🔬 AI発明ギャラリー syunsuke ガメラvsギャオス
🔒 サンドボックス内で実行中 ⛶ 全画面で遊ぶ
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>Gyaos Hyper Flock Simulator</title>
    <!-- Tailwind CSS for UI styling -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Three.js and OrbitControls -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: #0c0f12;
            user-select: none;
            -webkit-user-select: none;
        }
        canvas {
            display: block;
            width: 100vw;
            height: 100vh;
        }
        /* ネオン調 of ライトエフェクト */
        .neon-text {
            text-shadow: 0 0 5px rgba(239, 68, 68, 0.5), 0 0 15px rgba(239, 68, 68, 0.7);
        }
        .neon-border {
            box-shadow: 0 0 10px rgba(239, 68, 68, 0.2), inset 0 0 10px rgba(239, 68, 68, 0.1);
            border: 1px solid rgba(239, 68, 68, 0.3);
        }
    </style>
</head>
<body class="relative">

    <!-- 3D表示用キャンバス -->
    <div id="canvas-container" class="absolute inset-0 w-full h-full z-0"></div>

    <!-- UIオーバーレイ -->
    <div class="absolute inset-0 flex flex-col justify-between p-4 md:p-6 pointer-events-none z-10">
        
        <!-- ヘッダー(世代によってテキストが動的に切り替わります) -->
        <header class="flex justify-between items-start w-full">
            <div class="bg-black/85 backdrop-blur-md p-4 rounded-lg neon-border pointer-events-auto flex flex-col gap-2">
                <h1 id="title-main" class="text-red-500 font-black text-xl md:text-2xl tracking-widest neon-text font-serif">
                    GAMERA vs GYAOS-HYPER <span id="title-sub" class="text-[10px] text-red-400 font-sans block tracking-wider font-bold">ガメラ 対 ギャオス・ハイパー 大群シミュレーター</span>
                </h1>
                <p id="title-desc" class="text-gray-400 text-[10px] mt-0.5">ガメラ3 邪神〈イリス〉覚醒 (1999) より</p>
                
                <!-- BGM コントローラー -->
                <div class="flex items-center gap-2 mt-2 pt-2 border-t border-red-950/40">
                    <button id="bgm-btn" class="px-3 py-1 bg-red-950 hover:bg-red-900 text-red-400 text-xs font-bold rounded border border-red-800/40 flex items-center gap-1.5 transition">
                        <span id="bgm-icon">🔇</span> <span id="bgm-text">BGM OFF</span>
                    </button>
                    <span class="text-[9px] text-gray-500 select-none">「裂ける夜空」 BGM 収録</span>
                </div>
            </div>
            
            <div class="bg-black/85 backdrop-blur-md p-3 rounded-lg text-right text-xs text-gray-400 neon-border hidden sm:block pointer-events-auto">
                <div>[ドラッグ] 視点回転 / [スクロール] ズーム</div>
                <div>[Space] 超音波メス射撃 / [Enter] プラズマ火球</div>
                <div class="text-orange-400 font-bold animate-pulse">両方同時に撃つと中央で激突!</div>
            </div>
        </header>

        <!-- フラッシュエフェクト用画面オーバーレイ -->
        <div id="flash-overlay" class="absolute inset-0 bg-red-600/0 pointer-events-none transition-all duration-75"></div>

        <!-- ボトムコントロール -->
        <footer class="flex flex-col md:flex-row justify-between items-end gap-4 w-full">
            
            <!-- ステータス・操作パネル -->
            <div class="bg-black/85 backdrop-blur-md p-4 rounded-lg neon-border w-full md:w-80 pointer-events-auto">
                <h3 class="text-red-400 font-bold text-sm mb-3 tracking-wider uppercase border-b border-red-900/50 pb-1">大群コントロールシステム</h3>
                
                <!-- 世代選択(平成版と昭和版の切り替え) -->
                <div class="mb-3">
                    <div class="flex items-center justify-between text-xs text-gray-300 mb-1">
                        <span>怪獣・世代設定</span>
                        <div class="flex gap-1" id="gen-controls">
                            <button id="gen-heisei-btn" class="px-2.5 py-1 bg-red-600 text-white rounded text-[11px] font-bold border border-red-500 transition">
                                平成 (1999)
                            </button>
                            <button id="gen-showa-btn" class="px-2.5 py-1 bg-zinc-800 hover:bg-zinc-700 text-gray-300 rounded text-[11px] border border-zinc-700 transition">
                                昭和 (1967)
                            </button>
                        </div>
                    </div>
                </div>

                <!-- スライダー:群れの数(平成モード時のみ表示) -->
                <div id="flock-control-container" class="mb-3">
                    <div class="flex justify-between text-xs text-gray-300 mb-1">
                        <span>ギャオス個体数</span>
                        <span id="flock-val">9 匹</span>
                    </div>
                    <input id="flock-slider" type="range" min="1" max="15" step="1" value="9" 
                        class="w-full h-1 bg-red-950 rounded-lg appearance-none cursor-pointer accent-red-600">
                </div>

                <!-- スライダー:編隊の広がり(平成モード時のみ表示) -->
                <div id="spread-control-container" class="mb-3">
                    <div class="flex justify-between text-xs text-gray-300 mb-1">
                        <span>編隊の間隔</span>
                        <span id="spread-val">1.0x</span>
                    </div>
                    <input id="spread-slider" type="range" min="0.5" max="2.0" step="0.1" value="1.0" 
                        class="w-full h-1 bg-red-950 rounded-lg appearance-none cursor-pointer accent-red-600">
                </div>

                <!-- 環境選択トグル -->
                <div class="flex items-center justify-between text-xs text-gray-300 mb-1">
                    <span>環境プリセット</span>
                    <div class="flex gap-1" id="env-controls">
                        <button id="env-day-btn" class="px-2 py-0.5 bg-red-600 text-white rounded text-[11px] font-bold border border-red-500 transition">
                            昼間
                        </button>
                        <button id="env-studio-btn" class="px-2 py-0.5 bg-zinc-800 hover:bg-zinc-700 text-gray-300 rounded text-[11px] border border-zinc-700 transition">
                            スタジオ
                        </button>
                        <button id="env-night-btn" class="px-2 py-0.5 bg-zinc-800 hover:bg-zinc-700 text-gray-300 rounded text-[11px] border border-zinc-700 transition">
                            夜間
                        </button>
                    </div>
                </div>
            </div>

            <!-- メイン攻撃ボタン -->
            <div class="w-full md:w-auto flex flex-col sm:flex-row gap-3 pointer-events-auto">
                <button id="beam-btn" class="w-full sm:w-48 py-3 px-4 bg-gradient-to-r from-red-800 to-red-600 hover:from-red-700 hover:to-red-500 text-white font-bold text-sm rounded-xl shadow-[0_0_15px_rgba(239,68,68,0.5)] border border-red-400 transition transform active:scale-95">
                    超音波メス 一斉掃射
                </button>
                <button id="gamera-btn" class="w-full sm:w-48 py-3 px-4 bg-gradient-to-r from-orange-800 to-yellow-600 hover:from-orange-700 hover:to-yellow-500 text-white font-bold text-sm rounded-xl shadow-[0_0_15px_rgba(245,158,11,0.5)] border border-orange-400 transition transform active:scale-95 animate-pulse">
                    ガメラ:火球・火炎
                </button>
            </div>
            
        </footer>
    </div>

    <!-- JavaScript ロジック -->
    <script>
        // --- STREAMING_CHUNK:Initializing the main scene, camera, and renderer... ---
        const container = document.getElementById('canvas-container');
        const scene = new THREE.Scene();
        
        const colorDay = new THREE.Color(0xa5c5f0);
        const colorStudio = new THREE.Color(0xe0e4eb);
        const colorNight = new THREE.Color(0x0c0303);

        scene.background = colorDay;
        scene.fog = new THREE.FogExp2(0xa5c5f0, 0.012);

        const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(-12, 10, 24); // 対決構図が見やすい斜めの高所アングル

        const renderer = new THREE.WebGLRenderer({ antialias: true, powerPreference: "high-performance" });
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        container.appendChild(renderer.domElement);

        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.enableDamping = true;
        controls.dampingFactor = 0.05;
        controls.maxPolarAngle = Math.PI / 2 + 0.15;
        controls.minDistance = 5;
        controls.maxDistance = 60;

        // --- STREAMING_CHUNK:Configuring the lighting systems... ---
        // ライティング
        const ambientLight = new THREE.AmbientLight(0xffffff, 1.0);
        scene.add(ambientLight);

        const mainLight = new THREE.DirectionalLight(0xffffff, 1.8);
        mainLight.position.set(20, 40, 10);
        mainLight.castShadow = true;
        mainLight.shadow.mapSize.width = 2048;
        mainLight.shadow.mapSize.height = 2048;
        mainLight.shadow.bias = -0.0005;
        scene.add(mainLight);

        const fillLight = new THREE.DirectionalLight(0xddf0ff, 0.8);
        fillLight.position.set(-15, -10, -10);
        scene.add(fillLight);

        const glowLight = new THREE.PointLight(0xff0000, 2.5, 12);
        glowLight.position.set(0, 3, 4);
        scene.add(glowLight);

        // ガメラの発光用ポイントライト
        const gameraGlowLight = new THREE.PointLight(0xff5500, 0, 15);
        scene.add(gameraGlowLight);

        // 世代変数(デフォルト:平成 'heisei')
        let currentGeneration = 'heisei'; 

        // --- STREAMING_CHUNK:Loading materials for monsters... ---
        // マテリアル定義(関数内で動的に切り替わりますが、基本定義)
        let bodyMat, wingMembraneMat, eyeMat, goldHornMat, toothMat, skinMat, shellMat, plastronMat, tuskMat, clawMat;

        function updateMaterials() {
            if (currentGeneration === 'heisei') {
                // 平成:G3 & ギャオス・ハイパー(暗く、重厚で金属的なシャープさ)
                bodyMat = new THREE.MeshStandardMaterial({
                    color: 0x221d21, roughness: 0.45, metalness: 0.55, flatShading: true
                });
                wingMembraneMat = new THREE.MeshStandardMaterial({
                    color: 0x5a1217, roughness: 0.4, metalness: 0.3, side: THREE.DoubleSide, flatShading: true
                });
                eyeMat = new THREE.MeshBasicMaterial({ color: 0xff0515 }); // 狂暴な赤い眼
                goldHornMat = new THREE.MeshStandardMaterial({ color: 0x5a3e22, roughness: 0.25, metalness: 0.85 });
                toothMat = new THREE.MeshStandardMaterial({ color: 0xeeeeee, roughness: 0.2 });

                skinMat = new THREE.MeshStandardMaterial({ color: 0x2b2e2a, roughness: 0.85, flatShading: true });
                shellMat = new THREE.MeshStandardMaterial({ color: 0x1c1916, roughness: 0.9, flatShading: true });
                plastronMat = new THREE.MeshStandardMaterial({ 
                    color: 0x473b30, emissive: 0xff3c00, emissiveIntensity: 0.25, roughness: 0.8, flatShading: true 
                });
                tuskMat = new THREE.MeshStandardMaterial({ color: 0xeae5d8, roughness: 0.3 });
                clawMat = new THREE.MeshStandardMaterial({ color: 0xeeeeee, roughness: 0.3 });
            } else {
                // 昭和:1967年「大怪獣空中戦」(茶褐色〜オリーブ、レトロな着ぐるみのゴム感)
                bodyMat = new THREE.MeshStandardMaterial({
                    color: 0x3d312d, roughness: 0.7, metalness: 0.1, flatShading: true // 昭和ギャオスの茶褐色スキン
                });
                wingMembraneMat = new THREE.MeshStandardMaterial({
                    color: 0x50282b, roughness: 0.6, metalness: 0.1, side: THREE.DoubleSide, flatShading: true // 紫がかった赤茶
                });
                eyeMat = new THREE.MeshBasicMaterial({ color: 0xffd000 }); // 昭和特有の光る黄色い眼
                goldHornMat = new THREE.MeshStandardMaterial({ color: 0x403328, roughness: 0.6, metalness: 0.2 });
                toothMat = new THREE.MeshStandardMaterial({ color: 0xf5f3ea, roughness: 0.4 });

                skinMat = new THREE.MeshStandardMaterial({ color: 0x423830, roughness: 0.9, flatShading: true }); // 昭和ガメラの土気色の皮膚
                shellMat = new THREE.MeshStandardMaterial({ color: 0x2b271d, roughness: 0.9, flatShading: true }); // ドーム状の平坦な緑褐色甲羅
                plastronMat = new THREE.MeshStandardMaterial({ 
                    color: 0x7c694b, emissive: 0x332200, emissiveIntensity: 0.1, roughness: 0.9, flatShading: true // 黄土色の平滑な腹甲
                });
                tuskMat = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.4 }); // 大きな白い牙
                clawMat = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.4 });
            }
        }

        // --- STREAMING_CHUNK:Creating Single Gyaos model... ---
        function createSingleGyaos() {
            const gyaosInstance = new THREE.Group();
            const isShowa = (currentGeneration === 'showa');

            // 胴体
            const torsoGeo = new THREE.CylinderGeometry(0.5, 0.15, 4.5, 5);
            const torso = new THREE.Mesh(torsoGeo, bodyMat);
            torso.rotation.x = Math.PI / 2.3;
            torso.position.set(0, 0, -0.5);
            torso.castShadow = true;
            torso.receiveShadow = true;
            gyaosInstance.add(torso);

            // 蛇腹首
            const neckGroup = new THREE.Group();
            neckGroup.position.set(0, 0.7, 1.5);
            
            const neckSegments = 4;
            const neckParts = [];
            for (let i = 0; i < neckSegments; i++) {
                const size = 0.45 - (i * 0.05);
                const neckSegGeo = new THREE.BoxGeometry(size * 1.2, size, 0.5);
                const seg = new THREE.Mesh(neckSegGeo, bodyMat);
                seg.position.set(0, i * 0.25, i * 0.35);
                seg.rotation.x = -0.15;
                seg.castShadow = true;
                seg.receiveShadow = true;
                neckGroup.add(seg);
                neckParts.push(seg);
            }
            gyaosInstance.add(neckGroup);

            // 頭部
            const headGroup = new THREE.Group();
            headGroup.position.set(0, neckSegments * 0.25 + 0.1, neckSegments * 0.35 + 0.2);
            headGroup.rotation.x = 0.35;

            // 上顎プレート
            const headShape = new THREE.Shape();
            if (isShowa) {
                // 昭和:まな板のように平たく、幅が一定で後ろがそこまで深く裂けていない
                headShape.moveTo(0, 1.8);
                headShape.lineTo(0.6, 1.3);
                headShape.lineTo(0.65, 0.4);
                headShape.lineTo(0.7, -1.3); // 後ろがあまり広がらない
                headShape.lineTo(0.3, -1.0);
                headShape.lineTo(0, -0.6);
                headShape.lineTo(-0.3, -1.0);
                headShape.lineTo(-0.7, -1.3);
                headShape.lineTo(-0.65, 0.4);
                headShape.lineTo(-0.6, 1.3);
                headShape.lineTo(0, 1.8);
            } else {
                // 平成:超鋭利で巨大なV字スリット型
                headShape.moveTo(0, 2.0);
                headShape.lineTo(0.5, 1.2);
                headShape.lineTo(0.8, 0.4);
                headShape.lineTo(1.2, -1.8);
                headShape.lineTo(0.5, -1.2);
                headShape.lineTo(0, -0.6);
                headShape.lineTo(-0.5, -1.2);
                headShape.lineTo(-1.2, -1.8);
                headShape.lineTo(-0.8, 0.4);
                headShape.lineTo(-0.5, 1.2);
                headShape.lineTo(0, 2.0);
            }

            const extrudeSettings = {
                steps: 1, depth: 0.15, bevelEnabled: true, bevelThickness: 0.04, bevelSize: 0.03, bevelSegments: 3
            };
            const headPlateGeo = new THREE.ExtrudeGeometry(headShape, extrudeSettings);
            headPlateGeo.center();
            headPlateGeo.rotateX(-Math.PI / 2);
            headPlateGeo.rotateY(Math.PI);
            
            const headPlate = new THREE.Mesh(headPlateGeo, bodyMat);
            headPlate.castShadow = true;
            headPlate.receiveShadow = true;
            headGroup.add(headPlate);

            // 下顎プレート
            const lowerJawShape = new THREE.Shape();
            lowerJawShape.moveTo(0, 1.95);
            lowerJawShape.lineTo(0.48, 1.15);
            lowerJawShape.lineTo(0.72, 0.35);
            lowerJawShape.lineTo(0.85, -0.8);
            lowerJawShape.lineTo(0, -0.5);
            lowerJawShape.lineTo(-0.85, -0.8);
            lowerJawShape.lineTo(-0.72, 0.35);
            lowerJawShape.lineTo(-0.48, 1.15);
            lowerJawShape.lineTo(0, 1.95);

            const lowerJawExtSettings = {
                steps: 1, depth: 0.12, bevelEnabled: true, bevelThickness: 0.03, bevelSize: 0.02, bevelSegments: 2
            };
            const lowerJawPlateGeo = new THREE.ExtrudeGeometry(lowerJawShape, lowerJawExtSettings);
            lowerJawPlateGeo.center();
            lowerJawPlateGeo.rotateX(-Math.PI / 2);
            lowerJawPlateGeo.rotateY(Math.PI);

            const lowerJawPlate = new THREE.Mesh(lowerJawPlateGeo, bodyMat);
            lowerJawPlate.position.set(0, -0.18, 0.05);
            lowerJawPlate.rotation.x = 0.12;
            lowerJawPlate.castShadow = true;
            lowerJawPlate.receiveShadow = true;
            headGroup.add(lowerJawPlate);

            // 口内(発光部)
            const mouthInsideGeo = new THREE.BoxGeometry(1.0, 0.1, 2.4);
            const mouthPos = mouthInsideGeo.attributes.position;
            for (let i = 0; i < mouthPos.count; i++) {
                let z = mouthPos.getZ(i);
                let x = mouthPos.getX(i);
                if (z > 0) mouthPos.setX(i, x * 0.4);
            }
            mouthInsideGeo.computeVertexNormals();
            
            const mouthInsideMat = new THREE.MeshStandardMaterial({
                color: 0x3a0305, emissive: isShowa ? 0xccaa00 : 0xff0000, emissiveIntensity: 1.6, roughness: 0.6
            });
            const mouthInside = new THREE.Mesh(mouthInsideGeo, mouthInsideMat);
            mouthInside.position.set(0, -0.09, 0.5);
            headGroup.add(mouthInside);

            // 牙
            const upperToothGeo = new THREE.ConeGeometry(0.04, 0.16, 4);
            upperToothGeo.rotateX(Math.PI);
            const upperToothPositions = [
                { x: 0.18, y: -0.09, z: 1.4 }, { x: -0.18, y: -0.09, z: 1.4 },
                { x: 0.35, y: -0.09, z: 0.9 }, { x: -0.35, y: -0.09, z: 0.9 }
            ];
            upperToothPositions.forEach(pos => {
                const tooth = new THREE.Mesh(upperToothGeo, toothMat);
                tooth.position.set(pos.x, pos.y, pos.z);
                headGroup.add(tooth);
            });

            // 目
            const eyeWidth = isShowa ? 0.18 : 0.08; 
            const eyeLength = isShowa ? 0.3 : 0.7;
            const eyeGeo = new THREE.BoxGeometry(eyeWidth, eyeWidth, eyeLength);
            const leftEye = new THREE.Mesh(eyeGeo, eyeMat);
            leftEye.position.set(0.65, 0.03, 0.7);
            leftEye.rotation.y = 0.2; leftEye.rotation.z = isShowa ? 0 : -0.12; 
            const rightEye = leftEye.clone();
            rightEye.position.x = -0.65;
            rightEye.rotation.y = -0.2; rightEye.rotation.z = isShowa ? 0 : 0.12;
            headGroup.add(leftEye, rightEye);

            // 側頭ブレード
            const hornGeo = new THREE.ConeGeometry(0.12, 1.8, 3);
            hornGeo.scale(1, 0.1, 1);
            const leftHorn = new THREE.Mesh(hornGeo, goldHornMat);
            leftHorn.rotation.set(Math.PI / 2.2, 0, -Math.PI / 6);
            leftHorn.position.set(0.9, 0.02, -1.0);
            leftHorn.castShadow = true;
            const rightHorn = leftHorn.clone();
            rightHorn.position.x = -0.9; rightHorn.rotation.z = Math.PI / 6;
            headGroup.add(leftHorn, rightHorn);

            neckGroup.add(headGroup);

            // --- STREAMING_CHUNK:Creating Ggyaos wings... ---
            // 左主翼
            const leftWingGroup = new THREE.Group();
            leftWingGroup.position.set(0.5, 0.4, 0.2);

            const leftWingBlade = new THREE.Group();
            leftWingGroup.add(leftWingBlade);

            const boneLength = isShowa ? 3.6 : 4.1;
            const wingBone1Geo = new THREE.BoxGeometry(boneLength, 0.18, 0.3);
            wingBone1Geo.translate(boneLength / 2, 0, 0);
            const wingBone1 = new THREE.Mesh(wingBone1Geo, bodyMat);
            wingBone1.rotation.y = 0.1;
            wingBone1.castShadow = true;
            leftWingBlade.add(wingBone1);

            const wingBone2Geo = new THREE.BoxGeometry(3.5, 0.13, 0.22);
            wingBone2Geo.translate(1.75, 0, 0);
            const wingBone2 = new THREE.Mesh(wingBone2Geo, bodyMat);
            wingBone2.position.set(boneLength - 0.2, 0, -0.4);
            wingBone2.rotation.y = isShowa ? -0.15 : -0.26;
            wingBone2.castShadow = true;
            leftWingBlade.add(wingBone2);

            const wingShape = new THREE.Shape();
            if (isShowa) {
                wingShape.moveTo(0, 0);
                wingShape.lineTo(3.5, 0.3);
                wingShape.lineTo(6.5, -0.4);
                wingShape.lineTo(5.0, -2.2);
                wingShape.lineTo(2.8, -1.6);
                wingShape.lineTo(1.0, -2.1);
                wingShape.lineTo(0, -0.6);
                wingShape.lineTo(0, 0);
            } else {
                wingShape.moveTo(0, 0);
                wingShape.lineTo(4.0, 0.4);
                wingShape.lineTo(7.5, -0.5);
                wingShape.lineTo(5.5, -2.8);
                wingShape.lineTo(2.8, -1.8);
                wingShape.lineTo(1.0, -2.5);
                wingShape.lineTo(0, -0.6);
                wingShape.lineTo(0, 0);
            }

            const wingMembraneGeo = new THREE.ShapeGeometry(wingShape);
            const leftWingMembrane = new THREE.Mesh(wingMembraneGeo, wingMembraneMat);
            leftWingMembrane.rotation.x = Math.PI / 2;
            leftWingMembrane.castShadow = true;
            leftWingMembrane.receiveShadow = true;
            leftWingBlade.add(leftWingMembrane);

            gyaosInstance.add(leftWingGroup);

            // 右主翼
            const rightWingGroup = new THREE.Group();
            rightWingGroup.position.set(-0.5, 0.4, 0.2);

            const rightWingBlade = new THREE.Group();
            rightWingGroup.add(rightWingBlade);

            const rightWingBone1 = new THREE.Mesh(wingBone1Geo, bodyMat);
            rightWingBone1.rotation.y = -0.1;
            rightWingBone1.scale.x = -1;
            rightWingBone1.castShadow = true;
            rightWingBlade.add(rightWingBone1);

            const rightWingBone2 = new THREE.Mesh(wingBone2Geo, bodyMat);
            rightWingBone2.position.set(-(boneLength - 0.2), 0, -0.4);
            rightWingBone2.rotation.y = isShowa ? 0.15 : 0.26;
            rightWingBone2.scale.x = -1;
            rightWingBone2.castShadow = true;
            rightWingBlade.add(rightWingBone2);

            const rightWingMembrane = leftWingMembrane.clone();
            rightWingMembrane.scale.x = -1;
            rightWingBlade.add(rightWingMembrane);

            gyaosInstance.add(rightWingGroup);

            // 後肢・尾
            const legL = new THREE.Group();
            legL.position.set(0.4, -0.3, -2.0);
            const thighGeo = new THREE.CylinderGeometry(0.2, 0.15, 1.5, 4);
            const thigh = new THREE.Mesh(thighGeo, bodyMat);
            thigh.rotation.x = Math.PI / 3;
            thigh.castShadow = true;
            legL.add(thigh);
            gyaosInstance.add(legL);

            const legR = legL.clone();
            legR.position.x = -0.4;
            gyaosInstance.add(legR);

            const tailGroup = new THREE.Group();
            tailGroup.position.set(0, -0.2, -2.4);
            const tailBaseGeo = new THREE.BoxGeometry(0.4, 0.15, 1.8);
            const tailBase = new THREE.Mesh(tailBaseGeo, bodyMat);
            tailBase.position.set(0, 0, -0.6);
            tailBase.rotation.x = -0.1;
            tailBase.castShadow = true;
            tailGroup.add(tailBase);
            gyaosInstance.add(tailGroup);

            gyaosInstance.scale.set(0.8, 0.8, 0.8);

            return {
                model: gyaosInstance,
                leftWingGroup: leftWingGroup,
                leftWingBlade: leftWingBlade,
                rightWingGroup: rightWingGroup,
                rightWingBlade: rightWingBlade,
                neckParts: neckParts,
                headGroup: headGroup,
                lowerJawPlate: lowerJawPlate,
                mouthInside: mouthInside,
                animOffset: Math.random() * Math.PI * 2
            };
        }

        // --- STREAMING_CHUNK:Creating Gamera model... ---
        function createGamera() {
            const gameraInstance = new THREE.Group();
            const isShowa = (currentGeneration === 'showa');

            // 1. 体幹
            const bodyGeo = new THREE.SphereGeometry(1.8, 8, 8);
            bodyGeo.scale(1.1, 1.3, 1.2);
            const body = new THREE.Mesh(bodyGeo, skinMat);
            body.position.set(0, 0, 0);
            body.castShadow = true;
            body.receiveShadow = true;
            gameraInstance.add(body);

            // 2. 腹甲
            const plastronGroup = new THREE.Group();
            for (let i = 0; i < 5; i++) {
                const depth = isShowa ? 0.3 : 0.4;
                const segmentGeo = new THREE.BoxGeometry(1.5 - i * 0.12, 0.3, depth);
                const segment = new THREE.Mesh(segmentGeo, plastronMat);
                segment.position.set(0, 0.7 - i * 0.35, 0.7 + i * 0.05);
                segment.rotation.x = -0.12;
                segment.castShadow = true;
                plastronGroup.add(segment);
            }
            gameraInstance.add(plastronGroup);

            // 3. 甲羅
            const shellGroup = new THREE.Group();
            shellGroup.position.set(0, 0.1, -0.6);
            
            const shellBaseGeo = new THREE.SphereGeometry(1.9, 8, 8);
            shellBaseGeo.scale(1.2, 1.4, 0.7);
            const shellBase = new THREE.Mesh(shellBaseGeo, shellMat);
            shellBase.castShadow = true;
            shellGroup.add(shellBase);

            // 甲羅の外縁
            const rimGeo = new THREE.TorusGeometry(1.5, 0.15, 4, 8);
            rimGeo.scale(1.15, 1.35, 0.5);
            const rim = new THREE.Mesh(rimGeo, shellMat);
            rim.position.set(0, 0, -0.2);
            shellGroup.add(rim);

            // 背骨の巨大トゲ
            const spikeHeight = isShowa ? 0.3 : 0.75;
            const spikeGeo = new THREE.ConeGeometry(0.2, spikeHeight, 4);
            spikeGeo.rotateX(Math.PI / 2);
            for (let i = 0; i < 6; i++) {
                const spike = new THREE.Mesh(spikeGeo, shellMat);
                const yPos = 1.2 - i * 0.48;
                const zPos = -0.8 - (Math.sin(i * 0.5) * 0.22);
                spike.position.set(0, yPos, zPos);
                spike.rotation.x = -0.2 - i * 0.15;
                spike.castShadow = true;
                shellGroup.add(spike);
            }
            
            // 左右のサブトゲ
            for (let i = 1; i < 4; i++) {
                const leftSpike = new THREE.Mesh(spikeGeo, shellMat);
                leftSpike.position.set(0.65, 0.8 - i * 0.45, -0.7);
                leftSpike.rotation.set(-0.2, 0.3, -0.2);
                const rightSpike = leftSpike.clone();
                rightSpike.position.x = -0.65;
                rightSpike.rotation.y = -0.3;
                shellGroup.add(leftSpike, rightSpike);
            }
            gameraInstance.add(shellGroup);

            // 手先足先
            const clawGeo = new THREE.ConeGeometry(0.08, 0.35, 4);
            clawGeo.rotateX(Math.PI / 2);

            // 左腕
            const armLGroup = new THREE.Group();
            armLGroup.position.set(1.4, 0.3, 0.2);
            const upperArmLGeo = new THREE.CylinderGeometry(0.4, 0.35, 1.2, 6);
            const upperArmL = new THREE.Mesh(upperArmLGeo, skinMat);
            upperArmL.rotation.z = -Math.PI / 6;
            upperArmL.castShadow = true;
            armLGroup.add(upperArmL);
            
            const forearmLGeo = new THREE.CylinderGeometry(0.35, 0.25, 1.0, 6);
            forearmLGeo.translate(0, -0.5, 0);
            const forearmL = new THREE.Mesh(forearmLGeo, skinMat);
            forearmL.position.set(0.3, -0.5, 0.1);
            forearmL.rotation.z = -Math.PI / 12;
            forearmL.castShadow = true;
            armLGroup.add(forearmL);

            for (let i = -1; i <= 1; i++) {
                const claw = new THREE.Mesh(clawGeo, clawMat);
                claw.position.set(0.3 + i * 0.12, -1.05, 0.25);
                claw.rotation.y = i * 0.15;
                claw.castShadow = true;
                armLGroup.add(claw);
            }
            gameraInstance.add(armLGroup);

            // 右腕
            const armRGroup = new THREE.Group();
            armRGroup.position.set(-1.4, 0.3, 0.2);
            const upperArmR = new THREE.Mesh(upperArmLGeo, skinMat);
            upperArmR.rotation.z = Math.PI / 6;
            upperArmR.castShadow = true;
            armRGroup.add(upperArmR);
            
            const forearmR = new THREE.Mesh(forearmLGeo, skinMat);
            forearmR.position.set(-0.3, -0.5, 0.1);
            forearmR.rotation.z = Math.PI / 12;
            forearmR.castShadow = true;
            armRGroup.add(forearmR);

            for (let i = -1; i <= 1; i++) {
                const claw = new THREE.Mesh(clawGeo, clawMat);
                claw.position.set(-0.3 + i * 0.12, -1.05, 0.25);
                claw.rotation.y = i * 0.15;
                claw.castShadow = true;
                armRGroup.add(claw);
            }
            gameraInstance.add(armRGroup);

            // 左脚
            const legLGroup = new THREE.Group();
            legLGroup.position.set(1.0, -0.8, -0.2);
            const thighLGeo = new THREE.CylinderGeometry(0.5, 0.45, 1.2, 6);
            const thighL = new THREE.Mesh(thighLGeo, skinMat);
            thighL.castShadow = true;
            legLGroup.add(thighL);
            
            const footLGeo = new THREE.BoxGeometry(0.6, 0.4, 1.0);
            const footL = new THREE.Mesh(footLGeo, skinMat);
            footL.position.set(0, -0.6, 0.2);
            footL.castShadow = true;
            legLGroup.add(footL);

            for (let i = -1; i <= 1; i++) {
                const claw = new THREE.Mesh(clawGeo, clawMat);
                claw.position.set(i * 0.15, -0.72, 0.7);
                claw.castShadow = true;
                legLGroup.add(claw);
            }
            gameraInstance.add(legLGroup);

            // 右脚
            const legRGroup = new THREE.Group();
            legRGroup.position.set(-1.0, -0.8, -0.2);
            const thighR = new THREE.Mesh(thighLGeo, skinMat);
            thighR.castShadow = true;
            legRGroup.add(thighR);
            
            const footR = footL.clone();
            legRGroup.add(footR);

            for (let i = -1; i <= 1; i++) {
                const claw = new THREE.Mesh(clawGeo, clawMat);
                claw.position.set(i * 0.15, -0.72, 0.7);
                claw.castShadow = true;
                legRGroup.add(claw);
            }
            gameraInstance.add(legRGroup);

            // 太い尻尾
            const tailGroup = new THREE.Group();
            tailGroup.position.set(0, -1.0, -1.2);
            const tailGeo = new THREE.ConeGeometry(0.45, 2.0, 5);
            tailGeo.rotateX(-Math.PI / 2.3);
            const tail = new THREE.Mesh(tailGeo, skinMat);
            tail.position.set(0, 0, -0.6);
            tail.castShadow = true;
            tailGroup.add(tail);
            gameraInstance.add(tailGroup);

            // 5. 頭部ユニット
            const gameraHeadGroup = new THREE.Group();
            gameraHeadGroup.position.set(0, 1.5, 0.6);

            const neckGeo = new THREE.CylinderGeometry(0.5, 0.65, 0.9, 6);
            neckGeo.rotateX(Math.PI / 6);
            const neck = new THREE.Mesh(neckGeo, skinMat);
            neck.position.set(0, -0.3, -0.1);
            neck.castShadow = true;
            gameraHeadGroup.add(neck);

            // 上顎
            const headMainGeo = new THREE.BoxGeometry(0.8, 0.5, 1.1);
            const headMain = new THREE.Mesh(headMainGeo, skinMat);
            headMain.position.set(0, 0.2, 0.3);
            headMain.castShadow = true;
            gameraHeadGroup.add(headMain);

            const snoutGeo = new THREE.ConeGeometry(0.4, 0.6, 4);
            snoutGeo.rotateX(Math.PI / 2);
            const snout = new THREE.Mesh(snoutGeo, skinMat);
            snout.position.set(0, 0.15, 0.85);
            gameraHeadGroup.add(snout);

            // 下顎
            const gameraLowerJaw = new THREE.Group();
            gameraLowerJaw.position.set(0, -0.1, 0.3);
            
            const lowerJawGeo = new THREE.BoxGeometry(0.76, 0.2, 0.9);
            const lowerJawMesh = new THREE.Mesh(lowerJawGeo, skinMat);
            lowerJawMesh.position.set(0, -0.05, 0.25);
            gameraLowerJaw.add(lowerJawMesh);

            // 双牙
            const tuskHeight = isShowa ? 0.7 : 0.6;
            const tuskGeo = new THREE.ConeGeometry(0.12, tuskHeight, 4);
            tuskGeo.rotateX(-Math.PI / 10);
            
            const tuskL = new THREE.Mesh(tuskGeo, tuskMat);
            tuskL.position.set(0.35, 0.18, 0.5);
            tuskL.rotation.z = -0.15;
            tuskL.castShadow = true;
            
            const tuskR = tuskL.clone();
            tuskR.position.x = -0.35;
            tuskR.rotation.z = 0.15;

            gameraLowerJaw.add(tuskL, tuskR);
            gameraLowerJaw.rotation.x = 0.15;
            gameraHeadGroup.add(gameraLowerJaw);

            // 眼
            if (isShowa) {
                // 昭和特有の白目・黒目を持つ円形眼
                const eyeOuterGeo = new THREE.SphereGeometry(0.13, 6, 6);
                eyeOuterGeo.scale(1.0, 1.2, 0.5);
                const eyeOuterL = new THREE.Mesh(eyeOuterGeo, new THREE.MeshBasicMaterial({ color: 0xeeeeaa })); 
                eyeOuterL.position.set(0.41, 0.25, 0.45);
                eyeOuterL.rotation.y = 0.15;

                const pupilGeo = new THREE.SphereGeometry(0.06, 5, 5);
                const pupilL = new THREE.Mesh(pupilGeo, new THREE.MeshBasicMaterial({ color: 0x111111 })); 
                pupilL.position.set(0.42, 0.25, 0.49);

                const eyeOuterR = eyeOuterL.clone();
                eyeOuterR.position.x = -0.41;
                eyeOuterR.rotation.y = -0.15;

                const pupilR = pupilL.clone();
                pupilR.position.x = -0.42;

                gameraHeadGroup.add(eyeOuterL, pupilL, eyeOuterR, pupilR);
            } else {
                // 平成G3の細く鋭い眼
                const gameraEyeMat = new THREE.MeshBasicMaterial({ color: 0xffd700 });
                const eyeGeo = new THREE.BoxGeometry(0.08, 0.08, 0.2);
                const eyeL = new THREE.Mesh(eyeGeo, gameraEyeMat);
                eyeL.position.set(0.41, 0.25, 0.45);
                eyeL.rotation.y = 0.15;
                const eyeR = eyeL.clone();
                eyeR.position.x = -0.41;
                eyeR.rotation.y = -0.15;
                gameraHeadGroup.add(eyeL, eyeR);
            }

            // 口内組織
            const fireInsideGeo = new THREE.SphereGeometry(0.2, 5, 5);
            const fireInsideMat = new THREE.MeshBasicMaterial({ color: 0xffaa00 });
            const fireInside = new THREE.Mesh(fireInsideGeo, fireInsideMat);
            fireInside.position.set(0, -0.05, 0.2);
            gameraHeadGroup.add(fireInside);

            gameraInstance.add(gameraHeadGroup);
            gameraInstance.scale.set(2.4, 2.4, 2.4);

            return {
                model: gameraInstance,
                headGroup: gameraHeadGroup,
                lowerJaw: gameraLowerJaw,
                plastron: plastronGroup,
                fireInside: fireInside,
                plastronMat: plastronMat,
                armLGroup: armLGroup,
                armRGroup: armRGroup
            };
        }

        // --- STREAMING_CHUNK:Initializing flock and combat state... ---
        let gyaosFlock = [];
        let currentFlockSize = 9;
        let baseSpread = 1.0;
        let gamera = null;

        // V字編隊の位置を計算
        function getChevronPosition(index, spread) {
            const isShowa = (currentGeneration === 'showa');
            if (index === 0) return new THREE.Vector3(0, isShowa ? 5.5 : 3.0, -12);
            
            const row = Math.floor((index + 1) / 2); 
            const isLeft = index % 2 === 1; 
            
            const xOffset = row * 3.8 * spread * (isLeft ? 1 : -1);
            const yOffset = 3 + (Math.sin(index * 1.5) * 0.5); 
            const zOffset = -12 - (row * 3.5 * spread); 
            
            return new THREE.Vector3(xOffset, yOffset, zOffset);
        }

        // 世代変更やスライダー変更を反映して怪獣たちを再配置
        function rebuildFlock() {
            gyaosFlock.forEach(item => {
                scene.remove(item.model);
            });
            gyaosFlock = [];

            if (gamera) {
                scene.remove(gamera.model);
            }

            updateMaterials();

            const isShowa = (currentGeneration === 'showa');

            if (isShowa) {
                // 昭和モード: 大きな1匹だけを対峙させる
                const gyaos = createSingleGyaos();
                const pos = getChevronPosition(0, baseSpread);
                gyaos.model.position.copy(pos);
                gyaos.model.rotation.y = 0; 
                gyaos.model.scale.set(2.1, 2.1, 2.1); // 巨大化
                scene.add(gyaos.model);
                gyaosFlock.push(gyaos);
            } else {
                // 平成モード: スライダーに準拠した大群編成
                for (let i = 0; i < currentFlockSize; i++) {
                    const gyaos = createSingleGyaos();
                    const pos = getChevronPosition(i, baseSpread);
                    gyaos.model.position.copy(pos);
                    gyaos.model.rotation.y = 0; 
                    scene.add(gyaos.model);
                    gyaosFlock.push(gyaos);
                }
            }

            gamera = createGamera();
            gamera.model.position.set(0, 0.8, 14); 
            gamera.model.rotation.y = Math.PI; // 正面(ギャオス側)を向かせる
            scene.add(gamera.model);
        }

        // --- STREAMING_CHUNK:Integrating background audio and audio controls... ---
        // BGMオーディオ統合
        const bgmUrl = "https://petitacode.web2050.jp/users/original/syunsuke/%E8%A3%82%E3%81%91%E3%82%8B%E5%A4%9C%E7%A9%BA.mp3";
        const bgmAudio = new Audio(bgmUrl);
        bgmAudio.loop = true;
        bgmAudio.volume = 0.45; // 適切な音量バランス

        let bgmPlaying = false;

        const bgmBtn = document.getElementById('bgm-btn');
        const bgmIcon = document.getElementById('bgm-icon');
        const bgmText = document.getElementById('bgm-text');

        function toggleBGM() {
            if (bgmPlaying) {
                bgmAudio.pause();
                bgmPlaying = false;
                bgmIcon.innerText = "🔇";
                bgmText.innerText = "BGM OFF";
                bgmBtn.classList.replace('bg-red-800', 'bg-red-950');
            } else {
                // 再生試行
                bgmAudio.play().then(() => {
                    bgmPlaying = true;
                    bgmIcon.innerText = "🔊";
                    bgmText.innerText = "BGM ON";
                    bgmBtn.classList.replace('bg-red-950', 'bg-red-800');
                }).catch(err => {
                    console.log("Audio play deferred due to browser interaction restrictions:", err);
                });
            }
        }

        bgmBtn.addEventListener('click', toggleBGM);

        // 最初の画面インタラクション(クリック)時に、BGMを自動的に再生開始するトリガーを追加(快適な体験のため)
        let initialInteractionDone = false;
        window.addEventListener('mousedown', () => {
            if (!initialInteractionDone) {
                initialInteractionDone = true;
                // 自動再生ポリシー対策:ONに初期設定して再生
                bgmAudio.play().then(() => {
                    bgmPlaying = true;
                    bgmIcon.innerText = "🔊";
                    bgmText.innerText = "BGM ON";
                    bgmBtn.classList.replace('bg-red-950', 'bg-red-800');
                }).catch(() => {});
            }
        });

        // 大気粒子
        const particleCount = 350;
        const positions = new Float32Array(particleCount * 3);
        const speeds = [];

        for (let i = 0; i < particleCount; i++) {
            positions[i * 3] = (Math.random() - 0.5) * 60;
            positions[i * 3 + 1] = (Math.random() - 0.2) * 20;
            positions[i * 3 + 2] = (Math.random() - 0.5) * 60;

            speeds.push({
                x: (Math.random() - 0.5) * 0.02,
                y: 0.015 + Math.random() * 0.03,
                z: (Math.random() - 0.5) * 0.02
            });
        }

        const particlesGeo = new THREE.BufferGeometry();
        particlesGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        const particleMat = new THREE.PointsMaterial({
            color: 0xffffff,
            size: 0.12,
            transparent: true,
            opacity: 0.4
        });

        const ambientParticles = new THREE.Points(particlesGeo, particleMat);
        scene.add(ambientParticles);

        const gridHelper = new THREE.GridHelper(100, 50, 0x7799bb, 0xccddee);
        gridHelper.position.y = -3.5;
        scene.add(gridHelper);

        const shadowFloorGeo = new THREE.PlaneGeometry(200, 200);
        const shadowFloorMat = new THREE.ShadowMaterial({ opacity: 0.15 });
        const shadowFloor = new THREE.Mesh(shadowFloorGeo, shadowFloorMat);
        shadowFloor.rotation.x = -Math.PI / 2;
        shadowFloor.position.y = -3.52;
        shadowFloor.receiveShadow = true;
        scene.add(shadowFloor);

        // --- STREAMING_CHUNK:Setting up combat effects... ---
        // 光線エフェクト
        let isBeaming = false; 
        let activeBeams = [];
        let beamProgress = 0;
        const maxBeamLength = 50;

        let isGameraBeaming = false; 
        let gameraBeamMesh = null;
        let gameraBeamProgress = 0;

        // 干渉爆発パーティクル
        let clashParticles = null;
        const clashCount = 65;
        const clashPositions = new Float32Array(clashCount * 3);
        const clashVelocities = [];

        const clashGeo = new THREE.BufferGeometry();
        clashGeo.setAttribute('position', new THREE.BufferAttribute(clashPositions, 3));
        
        const clashMat = new THREE.PointsMaterial({
            color: 0xffaa00,
            size: 0.45,
            transparent: true,
            opacity: 0,
            blending: THREE.AdditiveBlending
        });
        clashParticles = new THREE.Points(clashGeo, clashMat);
        scene.add(clashParticles);

        for (let i = 0; i < clashCount; i++) {
            clashVelocities.push({
                x: (Math.random() - 0.5) * 12,
                y: (Math.random() - 0.5) * 12,
                z: (Math.random() - 0.5) * 12
            });
        }

        function triggerClashExplosion() {
            clashMat.opacity = 1.0;
            const clashPosAttr = clashParticles.geometry.attributes.position;
            for (let i = 0; i < clashCount; i++) {
                clashPosAttr.setXYZ(i, (Math.random() - 0.5) * 2, 3 + (Math.random() - 0.5) * 2, 0);
            }
            clashPosAttr.needsUpdate = true;
        }

        // 1. ギャオス超音波メス射撃
        function createAllUltrasonicBeams() {
            if (isBeaming) return;
            isBeaming = true;

            const flash = document.getElementById('flash-overlay');
            flash.style.backgroundColor = currentGeneration === 'showa' ? 'rgba(255, 221, 51, 0.4)' : 'rgba(239, 68, 68, 0.4)';
            setTimeout(() => { flash.style.backgroundColor = 'rgba(0, 0, 0, 0)'; }, 150);

            activeBeams = [];

            const isShowa = (currentGeneration === 'showa');
            const beamColorHex = isShowa ? 0xffdd33 : 0xff1e56;

            gyaosFlock.forEach((gyaos, idx) => {
                // 昭和の巨大ギャオスは発射するビームも1.8倍太くする
                const scaleFactor = isShowa ? 1.8 : 1.0;
                const beamGeo = new THREE.CylinderGeometry(0.10 * scaleFactor, 0.025 * scaleFactor, 1, 8);
                beamGeo.rotateX(Math.PI / 2);
                beamGeo.translate(0, 0, 0.5);

                const beamMat = new THREE.MeshBasicMaterial({
                    color: beamColorHex,
                    transparent: true,
                    opacity: 0.95
                });

                const beamMesh = new THREE.Mesh(beamGeo, beamMat);
                
                const lightPower = idx === 0 ? 8 : 2;
                const beamLight = new THREE.PointLight(beamColorHex, lightPower, 15);
                beamMesh.add(beamLight);

                const muzzleWorldPos = new THREE.Vector3();
                gyaos.mouthInside.getWorldPosition(muzzleWorldPos);
                
                const forwardOffset = new THREE.Vector3(0, 0, 1.0);
                forwardOffset.applyQuaternion(gyaos.headGroup.getWorldQuaternion(new THREE.Quaternion()));
                muzzleWorldPos.add(forwardOffset);
                beamMesh.position.copy(muzzleWorldPos);

                const headDirection = new THREE.Vector3(0, -0.05, 1).normalize();
                headDirection.applyQuaternion(gyaos.headGroup.getWorldQuaternion(new THREE.Quaternion()));
                
                const targetPoint = new THREE.Vector3().copy(beamMesh.position).add(headDirection);
                beamMesh.lookAt(targetPoint);

                scene.add(beamMesh);
                
                activeBeams.push({
                    mesh: beamMesh,
                    gyaos: gyaos,
                    dir: headDirection,
                    startPos: muzzleWorldPos.clone()
                });
            });

            beamProgress = 0;
            if (isGameraBeaming) triggerClashExplosion();
        }

        // 2. ガメラの火球発射
        function createGameraFireball() {
            if (isGameraBeaming) return;
            isGameraBeaming = true;

            const flash = document.getElementById('flash-overlay');
            flash.style.backgroundColor = 'rgba(245, 158, 11, 0.3)';
            setTimeout(() => { flash.style.backgroundColor = 'rgba(0, 0, 0, 0)'; }, 150);

            const beamRadius = currentGeneration === 'showa' ? 0.55 : 0.35;
            const beamColorHex = currentGeneration === 'showa' ? 0xff8800 : 0xff5500;

            const beamGeo = new THREE.CylinderGeometry(beamRadius, 0.15, 1, 10);
            beamGeo.rotateX(Math.PI / 2);
            beamGeo.translate(0, 0, 0.5);

            const beamMat = new THREE.MeshBasicMaterial({
                color: beamColorHex,
                transparent: true,
                opacity: 0.98
            });

            gameraBeamMesh = new THREE.Mesh(beamGeo, beamMat);
            const gameraLight = new THREE.PointLight(beamColorHex, 10, 20);
            gameraBeamMesh.add(gameraLight);

            const mouthWorldPos = new THREE.Vector3();
            gamera.fireInside.getWorldPosition(mouthWorldPos);
            gameraBeamMesh.position.copy(mouthWorldPos);

            const targetDir = new THREE.Vector3(0, -0.05, 1).normalize(); 
            targetDir.applyQuaternion(gamera.headGroup.getWorldQuaternion(new THREE.Quaternion()));
            
            const targetPoint = new THREE.Vector3().copy(gameraBeamMesh.position).add(targetDir);
            gameraBeamMesh.lookAt(targetPoint);

            scene.add(gameraBeamMesh);
            gameraBeamProgress = 0;
            
            if (isBeaming) triggerClashExplosion();
        }

        // --- STREAMING_CHUNK:Binding UI controls and event listeners... ---
        document.getElementById('flock-slider').addEventListener('input', (e) => {
            currentFlockSize = parseInt(e.target.value);
            document.getElementById('flock-val').innerText = currentFlockSize + ' 匹';
            rebuildFlock();
        });

        document.getElementById('spread-slider').addEventListener('input', (e) => {
            baseSpread = parseFloat(e.target.value);
            document.getElementById('spread-val').innerText = baseSpread.toFixed(1) + 'x';
            gyaosFlock.forEach((gyaos, idx) => {
                const targetPos = getChevronPosition(idx, baseSpread);
                gyaos.model.position.x = targetPos.x;
                gyaos.model.position.z = targetPos.z;
            });
        });

        const genHeiseiBtn = document.getElementById('gen-heisei-btn');
        const genShowaBtn = document.getElementById('gen-showa-btn');
        const flockControl = document.getElementById('flock-control-container');
        const spreadControl = document.getElementById('spread-control-container');

        function setGeneration(gen) {
            currentGeneration = gen;
            if (gen === 'heisei') {
                genHeiseiBtn.classList.replace('bg-zinc-800', 'bg-red-600');
                genHeiseiBtn.classList.replace('text-gray-300', 'text-white');
                genHeiseiBtn.classList.remove('hover:bg-zinc-700');

                genShowaBtn.classList.replace('bg-red-600', 'bg-zinc-800');
                genShowaBtn.classList.replace('text-white', 'text-gray-300');
                genShowaBtn.classList.add('hover:bg-zinc-700');

                flockControl.classList.remove('hidden');
                spreadControl.classList.remove('hidden');

                document.getElementById('title-main').innerHTML = `GAMERA vs GYAOS-HYPER <span id="title-sub" class="text-[10px] text-red-400 font-sans block tracking-wider font-bold">ガメラ 対 ギャオス・ハイパー 大群シミュレーター</span>`;
                document.getElementById('title-desc').innerText = 'ガメラ3 邪神〈イリス〉覚醒 (1999) より';
                document.getElementById('beam-btn').innerText = "超音波メス 一斉掃射";
                
                clashMat.color.setHex(0xffaa00);
            } else {
                genShowaBtn.classList.replace('bg-zinc-800', 'bg-red-600');
                genShowaBtn.classList.replace('text-gray-300', 'text-white');
                genShowaBtn.classList.remove('hover:bg-zinc-700');

                genHeiseiBtn.classList.replace('bg-red-600', 'bg-zinc-800');
                genHeiseiBtn.classList.replace('text-white', 'text-gray-300');
                genHeiseiBtn.classList.add('hover:bg-zinc-700');

                flockControl.classList.add('hidden');
                spreadControl.classList.add('hidden');

                document.getElementById('title-main').innerHTML = `GAMERA vs GYAOS - SHOWA <span id="title-sub" class="text-[10px] text-yellow-400 font-sans block tracking-wider font-bold">大怪獣空中戦 ガメラ対ギャオス シミュレーター</span>`;
                document.getElementById('title-desc').innerText = '昭和大映特撮シリーズ (1967) より';
                document.getElementById('beam-btn').innerText = "超音波メス 発射";

                clashMat.color.setHex(0xffdd33); 
            }
            rebuildFlock();
        }

        genHeiseiBtn.addEventListener('click', () => setGeneration('heisei'));
        genShowaBtn.addEventListener('click', () => setGeneration('showa'));

        const dayBtn = document.getElementById('env-day-btn');
        const studioBtn = document.getElementById('env-studio-btn');
        const nightBtn = document.getElementById('env-night-btn');

        function setEnvironment(mode) {
            [dayBtn, studioBtn, nightBtn].forEach(b => {
                b.classList.replace('bg-red-600', 'bg-zinc-800');
                b.classList.replace('text-white', 'text-gray-300');
                b.classList.add('hover:bg-zinc-700');
            });

            if (mode === 'day') {
                dayBtn.classList.replace('bg-zinc-800', 'bg-red-600');
                dayBtn.classList.replace('text-gray-300', 'text-white');
                dayBtn.classList.remove('hover:bg-zinc-700');

                scene.background = colorDay;
                scene.fog.color = colorDay;
                scene.fog.density = 0.012;
                
                ambientLight.color.setHex(0xffffff);
                ambientLight.intensity = 1.2;
                
                mainLight.color.setHex(0xffffff);
                mainLight.intensity = 1.8;
                mainLight.position.set(20, 40, 10);
                
                fillLight.color.setHex(0xddf0ff);
                fillLight.intensity = 0.8;

                particleMat.color.setHex(0xffffff);
                particleMat.opacity = 0.4;
                particleMat.size = 0.12;

                gridHelper.material.color.setHex(0x7799bb);
            } 
            else if (mode === 'studio') {
                studioBtn.classList.replace('bg-zinc-800', 'bg-red-600');
                studioBtn.classList.replace('text-gray-300', 'text-white');
                studioBtn.classList.remove('hover:bg-zinc-700');

                scene.background = colorStudio;
                scene.fog.color = colorStudio;
                scene.fog.density = 0.015;

                ambientLight.color.setHex(0xffffff);
                ambientLight.intensity = 0.9;

                mainLight.color.setHex(0xffffff);
                mainLight.intensity = 2.0;
                mainLight.position.set(5, 40, 5);

                fillLight.color.setHex(0xaaaaaa);
                fillLight.intensity = 0.5;

                particleMat.color.setHex(0x888888);
                particleMat.opacity = 0.2;
                particleMat.size = 0.08;

                gridHelper.material.color.setHex(0xaaaaaa);
            }
            else if (mode === 'night') {
                nightBtn.classList.replace('bg-zinc-800', 'bg-red-600');
                nightBtn.classList.replace('text-gray-300', 'text-white');
                nightBtn.classList.remove('hover:bg-zinc-700');

                scene.background = colorNight;
                scene.fog.color = colorNight;
                scene.fog.density = 0.02;

                ambientLight.color.setHex(0x220c0c);
                ambientLight.intensity = 1.6;

                mainLight.color.setHex(0xaaccff);
                mainLight.intensity = 1.2;
                mainLight.position.set(10, 30, 10);

                fillLight.color.setHex(0xff3300);
                fillLight.intensity = 1.4;

                particleMat.color.setHex(0xff4400);
                particleMat.opacity = 0.8;
                particleMat.size = 0.15;

                gridHelper.material.color.setHex(0x550c0c);
            }
        }

        dayBtn.addEventListener('click', () => setEnvironment('day'));
        studioBtn.addEventListener('click', () => setEnvironment('studio'));
        nightBtn.addEventListener('click', () => setEnvironment('night'));

        document.getElementById('beam-btn').addEventListener('click', createAllUltrasonicBeams);
        document.getElementById('gamera-btn').addEventListener('click', createGameraFireball);
        
        window.addEventListener('keydown', (e) => {
            if (e.code === 'Space') {
                e.preventDefault();
                createAllUltrasonicBeams();
            }
            if (e.code === 'Enter') {
                e.preventDefault();
                createGameraFireball();
            }
        });

        // --- STREAMING_CHUNK:Animating characters and handling calculations... ---
        const clock = new THREE.Clock();
        let speedMultiplier = 1.0;

        function animate() {
            requestAnimationFrame(animate);

            const delta = clock.getDelta();
            const time = clock.getElapsedTime();

            // 1. ギャオスのアニメーション
            gyaosFlock.forEach((gyaos, idx) => {
                const individualTime = time + gyaos.animOffset;
                const chevronPos = getChevronPosition(idx, baseSpread);

                // 自律的なホバリング
                const hoverY = chevronPos.y + Math.sin(individualTime * 2.2) * 0.4;
                gyaos.model.position.y = THREE.MathUtils.lerp(gyaos.model.position.y, hoverY, 0.05);

                gyaos.model.position.z = THREE.MathUtils.lerp(
                    gyaos.model.position.z, 
                    chevronPos.z + Math.cos(individualTime * 1.5) * 0.15, 
                    0.05
                );

                gyaos.model.rotation.x = Math.sin(individualTime * 0.9) * 0.03 - 0.05;
                gyaos.model.rotation.z = Math.cos(individualTime * 0.5) * 0.05;

                // 羽ばたき
                const flapSpeed = individualTime * 4.5 * speedMultiplier;
                const flapAngle = Math.sin(flapSpeed) * 0.35;
                
                gyaos.leftWingGroup.rotation.z = flapAngle;
                gyaos.leftWingBlade.rotation.y = -0.2 + Math.sin(flapSpeed - 0.4) * 0.08;

                gyaos.rightWingGroup.rotation.z = -flapAngle;
                gyaos.rightWingBlade.rotation.y = 0.2 - Math.sin(flapSpeed - 0.4) * 0.08;

                // 首
                gyaos.neckParts.forEach((part, sIdx) => {
                    part.rotation.z = Math.sin(individualTime * 1.3 + sIdx * 0.3) * 0.035;
                    part.rotation.y = Math.cos(individualTime * 0.9 + sIdx * 0.2) * 0.02;
                });
                gyaos.headGroup.rotation.y = Math.sin(individualTime * 1.4) * 0.05;

                // 通常時の口
                if (!isBeaming) {
                    gyaos.lowerJawPlate.rotation.x = THREE.MathUtils.lerp(
                        gyaos.lowerJawPlate.rotation.x, 
                        0.10 + Math.sin(individualTime * 1.0) * 0.03, 
                        0.05
                    );
                }
            });

            // 2. ガメラのアニメーション(重厚な呼吸と巨獣感)
            if (gamera) {
                const pulseIntensity = currentGeneration === 'showa' 
                    ? 0.1 + Math.sin(time * 1.5) * 0.05
                    : 0.25 + Math.sin(time * 3.0) * 0.15;
                gamera.plastronMat.emissiveIntensity = isGameraBeaming ? 3.0 : pulseIntensity;

                gamera.model.position.y = 0.8 + Math.sin(time * 1.5) * 0.04; 

                if (gamera.armLGroup) gamera.armLGroup.rotation.z = -Math.PI / 6 + Math.sin(time * 1.5) * 0.025;
                if (gamera.armRGroup) gamera.armRGroup.rotation.z = Math.PI / 6 - Math.sin(time * 1.5) * 0.025;

                gamera.headGroup.rotation.y = Math.sin(time * 0.5) * 0.03;
                gamera.headGroup.rotation.x = 0.12 + Math.sin(time * 0.7) * 0.02; 
            }

            // --- STREAMING_CHUNK:Processing beam weapon updates and collisions... ---
            // 3. 環境パーティクル
            const particlePosAttr = ambientParticles.geometry.attributes.position;
            for (let i = 0; i < particleCount; i++) {
                let y = particlePosAttr.getY(i);
                let x = particlePosAttr.getX(i);
                let z = particlePosAttr.getZ(i);

                y += speeds[i].y;
                x += speeds[i].x;
                z += speeds[i].z;

                if (y > 20) {
                    y = -3.5;
                    x = (Math.random() - 0.5) * 60;
                    z = (Math.random() - 0.5) * 60;
                }

                particlePosAttr.setY(i, y);
                particlePosAttr.setX(i, x);
                particlePosAttr.setZ(i, z);
            }
            particlePosAttr.needsUpdate = true;

            // 4. 超音波メス照射
            const baseHeadZ = 4 * 0.35 + 0.2; // 首の接続点
            let targetHeadZ = baseHeadZ;

            if (isBeaming && activeBeams.length > 0) {
                beamProgress += delta * 125.0;

                if (beamProgress < maxBeamLength) {
                    activeBeams.forEach(beam => {
                        const currentMaxZ = isGameraBeaming ? Math.min(beamProgress, 14) : beamProgress;
                        beam.mesh.scale.set(1, 1, currentMaxZ);
                        
                        const currentMuzzlePos = new THREE.Vector3();
                        beam.gyaos.mouthInside.getWorldPosition(currentMuzzlePos);
                        const forwardOffset = new THREE.Vector3(0, 0, 1.0);
                        forwardOffset.applyQuaternion(beam.gyaos.headGroup.getWorldQuaternion(new THREE.Quaternion()));
                        currentMuzzlePos.add(forwardOffset);
                        beam.mesh.position.copy(currentMuzzlePos);

                        beam.gyaos.lowerJawPlate.rotation.x = THREE.MathUtils.lerp(beam.gyaos.lowerJawPlate.rotation.x, 0.48, 0.25);
                    });
                    
                    targetHeadZ = baseHeadZ - 0.4; // 反動
                    glowLight.intensity = 8;
                } else {
                    activeBeams.forEach(beam => {
                        scene.remove(beam.mesh);
                        beam.mesh.geometry.dispose();
                    });
                    activeBeams = [];
                    isBeaming = false;
                    glowLight.intensity = 2.5;
                }
            } else if (!isBeaming) {
                gyaosFlock.forEach(gyaos => {
                    gyaos.lowerJawPlate.rotation.x = THREE.MathUtils.lerp(gyaos.lowerJawPlate.rotation.x, 0.12, 0.1);
                });
            }

            gyaosFlock.forEach(gyaos => {
                gyaos.headGroup.position.z = THREE.MathUtils.lerp(gyaos.headGroup.position.z, targetHeadZ, 0.15);
            });

            // 5. ガメラ火球
            if (gamera && isGameraBeaming && gameraBeamMesh) {
                gameraBeamProgress += delta * 125.0;

                if (gameraBeamProgress < maxBeamLength) {
                    const currentMaxZ = isBeaming ? Math.min(gameraBeamProgress, 14) : gameraBeamProgress;
                    gameraBeamMesh.scale.set(1, 1, currentMaxZ);

                    const mouthPos = new THREE.Vector3();
                    gamera.fireInside.getWorldPosition(mouthPos);
                    gameraBeamMesh.position.copy(mouthPos);

                    const targetDir = new THREE.Vector3(0, -0.05, 1).normalize();
                    targetDir.applyQuaternion(gamera.headGroup.getWorldQuaternion(new THREE.Quaternion()));
                    const targetPoint = new THREE.Vector3().copy(mouthPos).add(targetDir);
                    gameraBeamMesh.lookAt(targetPoint);

                    gamera.lowerJaw.rotation.x = THREE.MathUtils.lerp(gamera.lowerJaw.rotation.x, 0.65, 0.25);
                    gameraGlowLight.intensity = 15;
                } else {
                    scene.remove(gameraBeamMesh);
                    gameraBeamMesh.geometry.dispose();
                    gameraBeamMesh = null;
                    isGameraBeaming = false;
                    gameraGlowLight.intensity = 0;
                }
            } else if (gamera) {
                gamera.lowerJaw.rotation.x = THREE.MathUtils.lerp(gamera.lowerJaw.rotation.x, 0.15, 0.1);
            }

            // 6. 空中激突
            if (isBeaming && isGameraBeaming) {
                clashMat.opacity = THREE.MathUtils.lerp(clashMat.opacity, 1.0, 0.1);
                const clashPosAttr = clashParticles.geometry.attributes.position;
                
                for (let i = 0; i < clashCount; i++) {
                    let cx = clashPosAttr.getX(i);
                    let cy = clashPosAttr.getY(i);
                    let cz = clashPosAttr.getZ(i);

                    cx += clashVelocities[i].x * delta;
                    cy += clashVelocities[i].y * delta;
                    cz += clashVelocities[i].z * delta;

                    if (Math.abs(cx) > 8 || Math.abs(cy) > 8) {
                        cx = (Math.random() - 0.5) * 1.5;
                        cy = 3 + (Math.random() - 0.5) * 1.5;
                        cz = (Math.random() - 0.5) * 1.5;
                    }

                    clashPosAttr.setXYZ(i, cx, cy, cz);
                }
                clashPosAttr.needsUpdate = true;
            } else {
                clashMat.opacity = THREE.MathUtils.lerp(clashMat.opacity, 0, 0.2);
            }

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

        window.addEventListener('resize', onWindowResize, false);

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }

        // 初期ロード
        setEnvironment('day');
        rebuildFlock();
        animate();

    </script>
</body>
</html>

ガメラvsギャオス

大迫力のバトルシーン

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