🔒 サンドボックス内で実行中 ⛶ 全画面で遊ぶ
HTML / CSS / JS
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>メカゴジラ風 3Dモデル (Three.js)</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #050510;
            font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', 'Hiragino Sans', Meiryo, sans-serif;
            color: #fff;
        }
        #canvas-container {
            width: 100vw;
            height: 100vh;
            display: block;
        }
        #ui {
            position: absolute;
            top: 20px;
            left: 20px;
            z-index: 10;
            pointer-events: none;
            background: rgba(0, 0, 0, 0.6);
            padding: 15px 20px;
            border-radius: 8px;
            border: 1px solid #333;
            box-shadow: 0 4px 6px rgba(0,0,0,0.5);
        }
        h1 {
            margin: 0 0 10px 0;
            font-size: 1.2rem;
            color: #00ffcc;
            letter-spacing: 1px;
        }
        p {
            margin: 5px 0;
            font-size: 0.9rem;
            color: #ccc;
        }
        #loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 1.5rem;
            font-weight: bold;
            color: #00ffcc;
            z-index: 20;
            animation: pulse 1.5s infinite;
        }
        @keyframes pulse {
            0% { opacity: 0.5; }
            50% { opacity: 1; }
            100% { opacity: 0.5; }
        }
    </style>
    
    <!-- Import Map を追加してモジュールパスを解決 -->
    <script type="importmap">
      {
        "imports": {
          "three": "https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.module.js",
          "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/"
        }
      }
    </script>
</head>
<body>
    <div id="ui">
        <h1>メカゴジラ風 3Dモデル</h1>
        <p>🖱️ 左ドラッグ:視点回転</p>
        <p>🖱️ 右ドラッグ:平行移動</p>
        <p>🖱️ スクロール:ズーム</p>
    </div>
    <div id="loading">システム起動中...</div>
    <div id="canvas-container"></div>

    <!-- Three.js と OrbitControls を CDN から読み込む -->
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

        // --- 基本設定 ---
        const container = document.getElementById('canvas-container');
        const loading = document.getElementById('loading');
        
        const scene = new THREE.Scene();
        // 背景とフォグを少し明るい青緑に変更し、霧の密度を薄くする
        const fogColor = new THREE.Color(0x4a7a8c);
        scene.background = fogColor;
        scene.fog = new THREE.FogExp2(fogColor, 0.0035);

        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
        camera.position.set(60, 5, 180); // 巨大化に合わせてカメラを大きく引き、見上げるアングルに

        const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        container.appendChild(renderer.domElement);

        const controls = new OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.target.set(0, 40, 0); // 視点の中心を巨大化したメカゴジラの胸の高さに変更
        controls.maxPolarAngle = Math.PI / 2 + 0.1; // 地面より少し下まで

        // --- 照明設定 ---
        const ambientLight = new THREE.AmbientLight(0x4a7b9c, 3.5); // 環境光をかなり明るく設定
        scene.add(ambientLight);

        const directionalLight = new THREE.DirectionalLight(0xddeeff, 2.8); // 主光源も明るく設定
        directionalLight.position.set(-50, 100, -100); // 奥から手前へ
        directionalLight.castShadow = true;
        directionalLight.shadow.mapSize.width = 2048;
        directionalLight.shadow.mapSize.height = 2048;
        directionalLight.shadow.camera.near = 0.5;
        directionalLight.shadow.camera.far = 500;
        directionalLight.shadow.camera.left = -100;
        directionalLight.shadow.camera.right = 100;
        directionalLight.shadow.camera.top = 100;
        directionalLight.shadow.camera.bottom = -100;
        scene.add(directionalLight);

        const blueLight = new THREE.PointLight(0x00ffff, 3, 100); // シティのエネルギー光
        blueLight.position.set(0, 10, 0);
        scene.add(blueLight);

        // --- マテリアル定義 ---
        const silverMaterial = new THREE.MeshStandardMaterial({
            color: 0x2277aa, // より鮮やかなキャンディブルー
            metalness: 1.0,
            roughness: 0.2,
        });

        const darkMetalMaterial = new THREE.MeshStandardMaterial({
            color: 0x05101a, // 深い影を作るためのダークブルー
            metalness: 0.9,
            roughness: 0.4,
        });

        const jointMaterial = new THREE.MeshStandardMaterial({
            color: 0x111111,
            metalness: 0.6,
            roughness: 0.7,
        });

        const eyeMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // 赤い発光

        // --- メカゴジラ構築 ---
        const mechaGroup = new THREE.Group();
        
        // 1. 胴体(大きく前傾)
        const torsoGroup = new THREE.Group();
        torsoGroup.position.set(0, 10, -2); // 姿勢に合わせて高さと位置を調整
        torsoGroup.rotation.x = Math.PI / 5; // 前傾角度を調整(過度な前傾による破綻を修正)
        
        // 胸部コアブロック
        const chestGeo = new THREE.BoxGeometry(8, 8, 7);
        const chest = new THREE.Mesh(chestGeo, darkMetalMaterial);
        chest.castShadow = true;
        torsoGroup.add(chest);

        // 胸部装甲(トゲトゲしく)
        for(let i=0; i<3; i++) {
            const width = 9 - i * 1.5;
            const armorGeo = new THREE.BoxGeometry(width, 3, 3);
            const armor = new THREE.Mesh(armorGeo, silverMaterial);
            armor.position.set(0, 3 - i*2.5, 4);
            armor.rotation.x = -Math.PI / 8;
            armor.castShadow = true;
            torsoGroup.add(armor);
        }

        // 腹部
        const absGeo = new THREE.BoxGeometry(6, 7, 6);
        const abs = new THREE.Mesh(absGeo, darkMetalMaterial);
        abs.position.set(0, -6, 0);
        abs.castShadow = true;
        torsoGroup.add(abs);
        
        // 腹部装甲
        for(let i=0; i<3; i++) {
            const armorGeo = new THREE.BoxGeometry(7, 2.5, 3);
            const armor = new THREE.Mesh(armorGeo, silverMaterial);
            armor.position.set(0, -3.5 - i*2.2, 3);
            armor.rotation.x = -Math.PI / 12;
            armor.castShadow = true;
            torsoGroup.add(armor);
        }

        mechaGroup.add(torsoGroup);

        // 2. 頭部・首
        const neckGroup = new THREE.Group();
        // 前傾した胴体から前上に首を伸ばす
        neckGroup.position.set(0, 6, 3);
        neckGroup.rotation.x = -Math.PI / 6; 
        
        // 首の装甲
        for(let i=0; i<3; i++) {
            const neckSegmentGeo = new THREE.BoxGeometry(3.5 - i*0.2, 2, 4 - i*0.2);
            const neckSegment = new THREE.Mesh(neckSegmentGeo, silverMaterial);
            neckSegment.position.set(0, i*1.5, 0);
            neckGroup.add(neckSegment);
        }
        torsoGroup.add(neckGroup);

        const headGroup = new THREE.Group();
        headGroup.position.set(0, 5, 0); 
        headGroup.rotation.x = -Math.PI / 6; // さらに顔を前に向ける
        
        // 頭蓋骨
        const skullGeo = new THREE.BoxGeometry(3.5, 2.5, 5);
        const skull = new THREE.Mesh(skullGeo, silverMaterial);
        skull.castShadow = true;
        headGroup.add(skull);

        // 鼻先・上顎(分厚く鋭く)
        const snoutGeo = new THREE.BoxGeometry(2, 1.5, 5);
        const snout = new THREE.Mesh(snoutGeo, silverMaterial);
        snout.position.set(0, -0.5, 4);
        
        const snoutTipGeo = new THREE.ConeGeometry(1, 3, 4);
        const snoutTip = new THREE.Mesh(snoutTipGeo, silverMaterial);
        snoutTip.position.set(0, 0, 3);
        snoutTip.rotation.x = Math.PI / 2;
        snoutTip.rotation.y = Math.PI / 4;
        snout.add(snoutTip);
        headGroup.add(snout);

        // 頬のトゲ(長く鋭く)
        for (let sign of [1, -1]) {
            const cheekSpike = new THREE.Mesh(new THREE.ConeGeometry(0.5, 4, 4), silverMaterial);
            cheekSpike.position.set(2.5 * sign, -1, 3);
            cheekSpike.rotation.z = (Math.PI / 3) * sign;
            cheekSpike.rotation.x = Math.PI / 4;
            headGroup.add(cheekSpike);
        }

        // 下顎
        const jawGeo = new THREE.BoxGeometry(2, 1, 5);
        const jaw = new THREE.Mesh(jawGeo, darkMetalMaterial);
        jaw.position.set(0, -1.8, 3.5);
        jaw.rotation.x = Math.PI / 16;
        
        // 下顎の牙
        const jawTipGeo = new THREE.ConeGeometry(0.8, 2, 4);
        const jawTip = new THREE.Mesh(jawTipGeo, silverMaterial);
        jawTip.position.set(0, -0.5, 2.5);
        jawTip.rotation.x = Math.PI / 2.5;
        jawTip.rotation.y = Math.PI / 4;
        jaw.add(jawTip);
        headGroup.add(jaw);

        // 目
        const eyeGeo = new THREE.BoxGeometry(0.2, 0.4, 1.5);
        const leftEye = new THREE.Mesh(eyeGeo, eyeMaterial);
        leftEye.position.set(1.8, 0, 2);
        leftEye.rotation.x = -Math.PI / 8;
        headGroup.add(leftEye);

        const rightEye = new THREE.Mesh(eyeGeo, eyeMaterial);
        rightEye.position.set(-1.8, 0, 2);
        rightEye.rotation.x = -Math.PI / 8;
        headGroup.add(rightEye);

        // 頭頂部の巨大な角/アンテナ(画像のように長く斜め前へ)
        const mainCrest = new THREE.Mesh(new THREE.ConeGeometry(0.5, 6, 4), silverMaterial);
        mainCrest.position.set(0, 3, -1);
        mainCrest.rotation.x = -Math.PI / 3;
        headGroup.add(mainCrest);
        
        const subCrest = new THREE.Mesh(new THREE.ConeGeometry(0.4, 4, 4), silverMaterial);
        subCrest.position.set(0, 2.5, -2.5);
        subCrest.rotation.x = -Math.PI / 2.5;
        headGroup.add(subCrest);

        neckGroup.add(headGroup);

        // 3. 背びれ(放射状に広がるトゲ)
        const createFin = (w, h, d) => {
            const geo = new THREE.ConeGeometry(w, h, 4); // 板状より錐体に戻してシャープに
            const mesh = new THREE.Mesh(geo, silverMaterial);
            mesh.castShadow = true;
            return mesh;
        };

        for (let i = 0; i < 7; i++) {
            const scale = i < 3 ? 1 + (i * 0.3) : 1.9 - ((i-2) * 0.4);
            const zPos = -1 - (i * 2);
            const yPos = 4 - (i * 1.5);
            
            // 中央列
            const finC = createFin(1, 7 * scale, 4);
            finC.position.set(0, yPos, zPos);
            finC.rotation.x = -Math.PI / 3; 
            torsoGroup.add(finC);

            // 左右列(外側に広げる)
            const finL = createFin(0.8, 5 * scale, 4);
            finL.position.set(2.5, yPos - 1, zPos + 1);
            finL.rotation.x = -Math.PI / 3;
            finL.rotation.z = -Math.PI / 6;
            torsoGroup.add(finL);

            const finR = createFin(0.8, 5 * scale, 4);
            finR.position.set(-2.5, yPos - 1, zPos + 1);
            finR.rotation.x = -Math.PI / 3;
            finR.rotation.z = Math.PI / 6;
            torsoGroup.add(finR);
        }

        // 4. 腕(巨大な前腕アーマー)
        function createArm(isLeft) {
            const armGroup = new THREE.Group();
            const sign = isLeft ? 1 : -1;

            // 肩
            const shoulderBase = new THREE.Mesh(new THREE.BoxGeometry(4, 6, 5), darkMetalMaterial);
            shoulderBase.position.set(6 * sign, 2, 0);
            armGroup.add(shoulderBase);

            // 肩アーマー
            const shoulderArmor = new THREE.Mesh(new THREE.BoxGeometry(6, 6, 6), silverMaterial);
            shoulderArmor.position.set(7 * sign, 4, 0);
            shoulderArmor.rotation.z = (-Math.PI / 8) * sign;
            armGroup.add(shoulderArmor);
            
            // 肩の巨大スパイク
            const sSpike = new THREE.Mesh(new THREE.ConeGeometry(1, 6, 4), silverMaterial);
            sSpike.position.set(8 * sign, 7, 0);
            sSpike.rotation.z = (-Math.PI / 4) * sign;
            armGroup.add(sSpike);

            // 上腕
            const upperArm = new THREE.Mesh(new THREE.BoxGeometry(2.5, 5, 2.5), darkMetalMaterial);
            upperArm.position.set(7 * sign, -2, 0);
            armGroup.add(upperArm);

            // 下腕(巨大な装甲)
            const lowerArmGroup = new THREE.Group();
            lowerArmGroup.position.set(7.5 * sign, -5, 2);
            lowerArmGroup.rotation.x = -Math.PI / 4; // 前腕を前方へ突き出す
            
            const lowerArmBase = new THREE.Mesh(new THREE.BoxGeometry(4, 7, 4), silverMaterial);
            lowerArmGroup.add(lowerArmBase);

            // 肘の丸い関節パーツ(画像に見られる特徴的な銀色の円形パーツ)
            const jointMat = new THREE.MeshStandardMaterial({color: 0xcccccc, metalness: 0.9, roughness: 0.2});
            const elbowJoint = new THREE.Mesh(new THREE.CylinderGeometry(1.8, 1.8, 4.5, 16), jointMat);
            elbowJoint.rotation.z = Math.PI / 2;
            elbowJoint.position.set(0, 3.5, 0);
            lowerArmGroup.add(elbowJoint);
            
            // 前腕の装甲ブレード(より巨大に)
            const blade = new THREE.Mesh(new THREE.ConeGeometry(1.2, 10, 4), silverMaterial);
            blade.position.set(0, -3, 2.5);
            blade.rotation.x = Math.PI / 8;
            lowerArmGroup.add(blade);
            
            // 肘のトゲ
            const elbowSpike = new THREE.Mesh(new THREE.ConeGeometry(0.8, 3, 4), silverMaterial);
            elbowSpike.position.set(0, 3, -2.5);
            elbowSpike.rotation.x = -Math.PI / 2;
            lowerArmGroup.add(elbowSpike);

            // 爪
            for(let i=0; i<3; i++) {
                const claw = new THREE.Mesh(new THREE.ConeGeometry(0.5, 3, 4), silverMaterial);
                claw.position.set(((i-1)*1.2) * sign, -4.5, 1);
                claw.rotation.x = Math.PI / 6;
                lowerArmGroup.add(claw);
            }
            
            armGroup.add(lowerArmGroup);
            return armGroup;
        }

        torsoGroup.add(createArm(true)); 
        torsoGroup.add(createArm(false));
        
        // ※胴体はここまで。完成した胴体をメカゴジラ全体に追加
        mechaGroup.add(torsoGroup);

        // 5. 脚(踏ん張った逆関節感)
        function createLeg(isLeft) {
            const legGroup = new THREE.Group();
            const sign = isLeft ? 1 : -1;

            // 胴体から切り離し、絶対座標で脚の付け根を配置(脚を細くした分少し内側に寄せる)
            legGroup.position.set(4.5 * sign, 1, -2);

            const thighGroup = new THREE.Group();
            thighGroup.position.set(0, 8, 0);
            thighGroup.rotation.x = -Math.PI / 6; // 太ももを少し前へ
            
            // 太ももベース(細く)
            const thighBase = new THREE.Mesh(new THREE.BoxGeometry(4, 10, 6), darkMetalMaterial);
            thighGroup.add(thighBase);
            
            // 太もも装甲(細く)
            for(let i=0; i<3; i++) {
                const tArmorFront = new THREE.Mesh(new THREE.BoxGeometry(4.5, 3.5, 2.5), silverMaterial);
                tArmorFront.position.set(0, 3 - i*3, 3.5);
                tArmorFront.rotation.x = -Math.PI / 12;
                thighGroup.add(tArmorFront);
            }
            legGroup.add(thighGroup); // legGroupに直接追加

            // 膝スパイク(細く)
            const kneeSpike = new THREE.Mesh(new THREE.ConeGeometry(0.8, 5, 4), silverMaterial);
            kneeSpike.position.set(0, -4.5, 4);
            kneeSpike.rotation.x = Math.PI / 4;
            thighGroup.add(kneeSpike);

            // すね
            const shinGroup = new THREE.Group();
            shinGroup.position.set(0, -6.5, 2);
            shinGroup.rotation.x = Math.PI / 4; // 膝を曲げて後ろへ
            
            // すねベース(細く)
            const shinBase = new THREE.Mesh(new THREE.BoxGeometry(3.5, 9, 5), silverMaterial);
            shinGroup.add(shinBase);
            
            // すね装甲(細く)
            const sArmorFront = new THREE.Mesh(new THREE.BoxGeometry(4, 8, 2.5), silverMaterial);
            sArmorFront.position.set(0, 0, 3);
            shinGroup.add(sArmorFront);

            thighGroup.add(shinGroup);

            // 足(接地面)
            const footGroup = new THREE.Group();
            footGroup.position.set(0, -5, 2);
            footGroup.rotation.x = -Math.PI / 12; // 地面に並行に
            
            // 足ベース(細く)
            const footBase = new THREE.Mesh(new THREE.BoxGeometry(5, 3, 10), silverMaterial);
            footGroup.add(footBase);
            
            // 足の爪(細く、間隔を狭める)
            for(let i=0; i<3; i++) {
                const toe = new THREE.Mesh(new THREE.ConeGeometry(0.8, 5, 4), silverMaterial);
                toe.position.set(((i-1)*1.5) * sign, -0.5, 6);
                toe.rotation.x = Math.PI / 2;
                footGroup.add(toe);
            }
            shinGroup.add(footGroup);

            return legGroup; 
        }

        // 脚をメカゴジラ全体に直接追加(胴体の傾きに影響されない)
        mechaGroup.add(createLeg(true)); 
        mechaGroup.add(createLeg(false));

        // 6. 尻尾(地面に這うように後方に長く)
        const tailGroup = new THREE.Group();
        tailGroup.position.set(0, 6, -5); // 腰の位置の絶対座標から生やす
        const numTailSegments = 16; 
        
        for (let i = 0; i < numTailSegments; i++) {
            const size = 5.5 * (1 - i * 0.05);
            const segmentGroup = new THREE.Group();
            
            const zPos = - (i * 4);
            const yPos = - (i * 0.8) - (i * i * 0.02); // 徐々に地面に下りていく
            
            segmentGroup.position.set(0, Math.max(yPos, -7), zPos); 
            segmentGroup.rotation.x = Math.max(-Math.PI / 8 + (i * 0.05), 0); // 根本は少し下向き、先は水平

            // 尻尾コア
            const core = new THREE.Mesh(new THREE.BoxGeometry(size*0.8, size*0.8, size), darkMetalMaterial);
            segmentGroup.add(core);

            // 尻尾装甲
            const topArmor = new THREE.Mesh(new THREE.BoxGeometry(size, size*0.4, size*1.2), silverMaterial);
            topArmor.position.set(0, size*0.4, 0);
            segmentGroup.add(topArmor);
            
            const sideArmorL = new THREE.Mesh(new THREE.BoxGeometry(size*0.4, size*0.8, size*1.2), silverMaterial);
            sideArmorL.position.set(size*0.4, 0, 0);
            segmentGroup.add(sideArmorL);
            
            const sideArmorR = new THREE.Mesh(new THREE.BoxGeometry(size*0.4, size*0.8, size*1.2), silverMaterial);
            sideArmorR.position.set(-size*0.4, 0, 0);
            segmentGroup.add(sideArmorR);

            // 上部スパイク
            const spike = new THREE.Mesh(new THREE.ConeGeometry(size*0.2, size*1.5, 4), silverMaterial);
            spike.position.set(0, size*0.8, 0);
            segmentGroup.add(spike);
            
            // 左右スパイク
            if (i > 0) {
                const lSpike = new THREE.Mesh(new THREE.ConeGeometry(size*0.15, size, 4), silverMaterial);
                lSpike.position.set(size*0.8, 0, 0);
                lSpike.rotation.z = -Math.PI / 2;
                segmentGroup.add(lSpike);
                
                const rSpike = new THREE.Mesh(new THREE.ConeGeometry(size*0.15, size, 4), silverMaterial);
                rSpike.position.set(-size*0.8, 0, 0);
                rSpike.rotation.z = Math.PI / 2;
                segmentGroup.add(rSpike);
            }

            tailGroup.add(segmentGroup);
        }
        // 尻尾もメカゴジラ全体に直接追加
        mechaGroup.add(tailGroup); 

        // --- メカゴジラ本体を巨大化 ---
        const scaleFactor = 4.0; // 4倍のサイズに拡大
        mechaGroup.scale.set(scaleFactor, scaleFactor, scaleFactor);
        // 巨大化した分、足が地面(y=-4)にぴったり接するようにY座標を計算して持ち上げる
        mechaGroup.position.y = 4 * (scaleFactor - 1);

        scene.add(mechaGroup);

        // --- 背景・地形(メカゴジラシティ風) ---
        
        // 1. 荒涼とした起伏のある地面(岩肌)
        const groundGeo = new THREE.PlaneGeometry(800, 800, 128, 128);
        const pos = groundGeo.attributes.position;
        for (let i = 0; i < pos.count; i++) {
            const x = pos.getX(i);
            const y = pos.getY(i);
            // 複数のサイン波を重ねて自然な起伏を作る
            let z = Math.sin(x * 0.02) * Math.cos(y * 0.02) * 15;
            z += Math.sin(x * 0.05) * Math.cos(y * 0.04) * 5;
            
            // 中央(メカゴジラの足元)はめり込まないように平らに均す
            const dist = Math.sqrt(x*x + y*y);
            if (dist < 40) {
                z = z * (dist / 40);
            }
            pos.setZ(i, z);
        }
        groundGeo.computeVertexNormals();

        const groundMat = new THREE.MeshStandardMaterial({ 
            color: 0x1a2b3c, // 暗い岩肌の色
            roughness: 0.9,
            metalness: 0.2,
            flatShading: true // ポリゴン感を出してゴツゴツさせる
        });
        const ground = new THREE.Mesh(groundGeo, groundMat);
        ground.rotation.x = -Math.PI / 2;
        ground.position.y = -4; // 足元の高さ調整
        ground.receiveShadow = true;
        scene.add(ground);

        // 2. 遠景のドーム型建造物群
        const cityMat = new THREE.MeshStandardMaterial({ 
            color: 0x223344, 
            roughness: 0.6, 
            metalness: 0.7,
            flatShading: true
        });

        function createDome(x, z, scale) {
            const domeGroup = new THREE.Group();
            domeGroup.position.set(x, -4, z); // 地面と同じ基準高さ

            // ドーム本体(半球)
            const domeGeo = new THREE.SphereGeometry(15 * scale, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2);
            const dome = new THREE.Mesh(domeGeo, cityMat);
            dome.castShadow = true;
            dome.receiveShadow = true;
            domeGroup.add(dome);
            
            // ドームのリング装飾
            const ringGeo = new THREE.TorusGeometry(15.2 * scale, 0.5 * scale, 8, 32);
            const ring = new THREE.Mesh(ringGeo, cityMat);
            ring.rotation.x = Math.PI / 2;
            ring.position.y = 5 * scale;
            domeGroup.add(ring);

            // 側面から伸びる巨大なパイプ
            for(let i=0; i<4; i++) {
                const pipeAngle = (Math.PI / 2) * i + Math.random();
                const pipeGeo = new THREE.CylinderGeometry(1.5 * scale, 1.5 * scale, 25 * scale, 8);
                const pipe = new THREE.Mesh(pipeGeo, cityMat);
                
                // ドームから斜め下へ伸びる角度
                pipe.rotation.z = Math.PI / 4;
                pipe.rotation.y = pipeAngle;
                
                // 位置調整
                pipe.position.x = Math.cos(pipeAngle) * (15 * scale);
                pipe.position.y = 8 * scale;
                pipe.position.z = -Math.sin(pipeAngle) * (15 * scale);
                
                domeGroup.add(pipe);
            }

            return domeGroup;
        }

        // 遠景にドームを複数配置
        scene.add(createDome(-80, -120, 3));
        scene.add(createDome(60, -180, 4.5));
        scene.add(createDome(140, -100, 2.5));
        scene.add(createDome(-160, -80, 2));
        scene.add(createDome(0, -250, 6));

        // --- ロード完了処理 ---
        loading.style.display = 'none';

        // --- アニメーションループ ---
        const clock = new THREE.Clock();

        function animate() {
            requestAnimationFrame(animate);

            const elapsedTime = clock.getElapsedTime();

            // 待機モーション(ゆっくりとした呼吸)
            // 胴体が上下すると脚と分離してしまうため、胴体の角度(胸の張り)だけで表現
            torsoGroup.rotation.x = Math.PI / 5 + Math.sin(elapsedTime * 1.5) * 0.02;
            
            // 尻尾をゆっくり揺らす
            tailGroup.children.forEach((segment, index) => {
                segment.position.x = Math.sin(elapsedTime * 2 - index * 0.5) * (index * 0.2);
            });

            // 目の明滅(呼吸に合わせて)
            eyeMaterial.color.setHSL(0, 1, 0.3 + Math.sin(elapsedTime * 4) * 0.2);

            controls.update();
            renderer.render(scene, camera);
        }

        animate();

        // --- ウィンドウリサイズ対応 ---
        window.addEventListener('resize', onWindowResize, false);

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }
    </script>
</body>
</html>

シティ

メカゴジラです

🤖 使用したAI
Gemini
👁 28 回閲覧
📅 投稿:2026年4月15日 🔄 更新:2026年8月13日
応援スタンプを押してみよう!