🔒 サンドボックス内で実行中 ⛶ 全画面で遊ぶ
HTML / CSS / JS
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>ガメラ:大怪獣空中決戦 - 究極造形・バニシング版</title>
    <!-- Three.js 安定版 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <style>
        :root {
            --bg-color: #010103;
            --ui-color: #00ffff;
            --danger-color: #ff3333;
            --heal-color: #00ff88;
            --magma-color: #ff4400;
        }

        body {
            margin: 0;
            padding: 0;
            background-color: var(--bg-color);
            color: white;
            font-family: 'Hiragino Kaku Gothic Pro', 'Meiryo', sans-serif;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            touch-action: none;
        }

        #game-container {
            position: relative;
            width: 100%;
            max-width: 1000px;
            aspect-ratio: 16 / 9;
            background: #000;
            border: 1px solid #222;
            box-shadow: 0 0 100px rgba(0,0,0,1);
            overflow: hidden;
        }

        canvas#gameCanvas {
            width: 100%;
            height: 100%;
            display: block;
        }

        #ui-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            padding: 25px;
            box-sizing: border-box;
            display: flex;
            justify-content: space-between;
            pointer-events: none;
            z-index: 10;
        }

        .hp-container {
            width: 40%;
            filter: drop-shadow(0 0 8px rgba(0,0,0,0.8));
        }

        .hp-bar-bg {
            background: rgba(255, 255, 255, 0.05);
            height: 6px;
            border: 1px solid #333;
            border-radius: 3px;
            overflow: hidden;
        }

        .hp-bar-fill {
            height: 100%;
            width: 100%;
            transition: width 0.2s ease-out;
        }

        #player-hp { background: linear-gradient(90deg, #00d2ff, #3a7bd5); box-shadow: 0 0 15px rgba(0,210,255,0.6); }
        #enemy-hp { background: linear-gradient(270deg, #ff4b2b, #ff416c); box-shadow: 0 0 15px rgba(255,75,43,0.6); }

        .name-label {
            font-size: 14px;
            font-weight: 900;
            letter-spacing: 4px;
            margin-bottom: 6px;
            text-shadow: 0 0 10px black;
            text-transform: uppercase;
        }

        .overlay-screen {
            position: absolute;
            inset: 0;
            background: radial-gradient(circle, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.95) 100%);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            z-index: 100;
            backdrop-filter: blur(10px);
        }

        h1 {
            font-size: 64px;
            margin: 0;
            letter-spacing: 12px;
            text-align: center;
            color: #fff;
        }

        button {
            background: transparent;
            color: #fff;
            border: 1px solid #fff;
            padding: 12px 40px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s;
            letter-spacing: 5px;
            margin: 10px;
            text-transform: uppercase;
        }

        button:hover {
            background: #fff;
            color: #000;
            box-shadow: 0 0 40px rgba(255,255,255,0.4);
        }

        #dictionary-screen {
            display: none;
            position: absolute;
            inset: 0;
            background: #000;
            z-index: 200;
        }

        #dictionary-3d-canvas {
            width: 100%;
            height: 100%;
            cursor: grab;
        }

        .dict-controls {
            position: absolute;
            bottom: 30px;
            width: 100%;
            display: flex;
            justify-content: center;
            gap: 20px;
        }

        .dict-info {
            position: absolute;
            top: 30px;
            left: 30px;
            pointer-events: none;
            text-shadow: 0 0 10px black;
            max-width: 400px;
        }

        #notify-box {
            position: absolute;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 255, 255, 0.2);
            border: 1px solid var(--ui-color);
            padding: 10px 20px;
            border-radius: 5px;
            color: var(--ui-color);
            font-weight: bold;
            display: none;
            z-index: 500;
        }

        #volume-btn {
            position: absolute;
            bottom: 20px;
            right: 20px;
            z-index: 1000;
            background: rgba(0,0,0,0.5);
            border: 1px solid #fff;
            color: white;
            padding: 5px 10px;
            cursor: pointer;
            font-size: 12px;
        }

        #mobile-controls {
            display: none;
            position: absolute;
            bottom: 15px;
            width: 100%;
            justify-content: space-between;
            padding: 0 10px;
            box-sizing: border-box;
            z-index: 20;
        }

        .btn-group { display: flex; gap: 5px; pointer-events: none; }
        .t-btn {
            width: 44px; height: 44px;
            background: rgba(255,255,255,0.05);
            border: 1px solid rgba(255,255,255,0.15);
            border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            font-size: 12px; pointer-events: auto; user-select: none;
            backdrop-filter: blur(4px);
        }

        @media (max-width: 768px) {
            #mobile-controls { display: flex; }
        }
    </style>
</head>
<body>

    <div id="game-container">
        <canvas id="gameCanvas"></canvas>
        <button id="volume-btn">MUSIC: OFF</button>
        
        <div id="ui-overlay">
            <div class="hp-container">
                <div class="name-label">GAMERA</div>
                <div class="hp-bar-bg"><div id="player-hp" class="hp-bar-fill"></div></div>
            </div>
            <div class="hp-container" style="text-align: right;">
                <div id="enemy-name" class="name-label">GYAOS</div>
                <div class="hp-bar-bg"><div id="enemy-hp" class="hp-bar-fill"></div></div>
            </div>
        </div>

        <div id="notify-box">怪獣が図鑑に登録されました!</div>

        <div id="start-screen" class="overlay-screen">
            <h1>GAMERA</h1>
            <p style="margin-top: 10px; color: #666; letter-spacing: 5px;">THE LAST HOPE</p>
            <p style="margin-top: 20px; font-size: 12px; color: #888;">[WASD] 移動 [F] 飛行 [Q] 防御 [E] 回復 [Space] 攻撃 [Shift] 回転</p>
            <div style="display: flex;">
                <button id="start-btn">戦闘開始</button>
                <button id="view-dict-btn">3D怪獣図鑑</button>
            </div>
        </div>

        <div id="end-screen" class="overlay-screen" style="display: none;">
            <h1 id="result-title"></h1>
            <p id="result-text" style="margin-top: 20px; color: #888;"></p>
            <button id="restart-btn">最初から</button>
            <button id="to-dict-btn">図鑑へ</button>
        </div>

        <div id="dictionary-screen">
            <div class="dict-info">
                <h2 id="dict-monster-name">GAMERA</h2>
                <p id="dict-monster-desc">地球の守護神。</p>
            </div>
            <div id="dictionary-3d-canvas"></div>
            <div class="dict-controls">
                <button id="dict-prev">◀</button>
                <button id="dict-back">戻る</button>
                <button id="dict-next">▶</button>
            </div>
        </div>

        <div id="mobile-controls">
            <div class="btn-group">
                <div class="t-btn" id="m-left">←</div>
                <div class="t-btn" id="m-up">↑</div>
                <div class="t-btn" id="m-down">↓</div>
                <div class="t-btn" id="m-right">→</div>
            </div>
            <div class="btn-group">
                <div class="t-btn" id="m-guard" style="color: #ffff00;">盾</div>
                <div class="t-btn" id="m-heal" style="color: #00ff88;">癒</div>
                <div class="t-btn" id="m-fly" style="color: #00ffcc;">飛</div>
                <div class="t-btn" id="m-fire" style="color: #ffaa00;">🔥</div>
                <div class="t-btn" id="m-spin" style="color: #00ccff;">🌀</div>
            </div>
        </div>
    </div>

    <script>
        // --- データ定義 ---
        const monstersData = [
            { id: "GAMERA", name: "GAMERA", desc: "地球の守護神。鱗状に重なり合う装甲板の甲羅と、鋭い鉤爪を備える。究極の咆哮を放つ。", color: 0x1a211c },
            { id: "GYAOS", name: "GYAOS", desc: "超音波怪獣。染色体操作生物。禍々しい赤黒い体色が特徴。", color: 0x4b0a0a },
            { id: "JAIGER", name: "JAIGER", desc: "大魔獣。巨大な背びれとシアンの角、そして紫の皮膚を持つ古代の驚異。地を這うような低い姿勢で獲物を追う。", color: 0x5a005a },
            { id: "GUIRON", name: "GUIRON", desc: "大悪獣。出刃包丁のような巨大な頭部を持ち、頭部側面から超振動手裏剣を射出する。テラの守護獣。", color: 0x666666 }
        ];
        const unlockedMonsters = { GAMERA: true, GYAOS: false, JAIGER: false, GUIRON: false };
        let dictIndex = 0;

        const canvas = document.getElementById('gameCanvas');
        const ctx = canvas.getContext('2d');
        const CANVAS_W = 1600; 
        const CANVAS_H = 900;
        canvas.width = CANVAS_W;
        canvas.height = CANVAS_H;

        // オーディオ設定
        const battleMusic = new Audio('https://ia802206.us.archive.org/21/items/epic-orchestra-music/Epic%20Orchestra%20-%20The%20Legend.mp3');
        battleMusic.loop = true;
        let isMusicEnabled = false;
        let playPromise = null;

        async function playMusic() {
            if (!isMusicEnabled) return;
            try { playPromise = battleMusic.play(); await playPromise; } catch (err) {}
        }
        async function pauseMusic() {
            if (playPromise !== null) { try { await playPromise; battleMusic.pause(); } catch (err) {} }
            else { battleMusic.pause(); }
        }

        const GROUND_Y = CANVAS_H - 120;
        const GRAVITY = 1.2;

        let gameState = 'START';
        let currentLevel = 'GYAOS';
        let shakeAmount = 0;
        let frameCount = 0;
        const particles = [];
        const bullets = [];
        const enemyBullets = [];

        const input = { left: false, right: false, up: false, down: false, fire: false, spin: false, fly: false, guard: false, heal: false };
        window.addEventListener('keydown', e => updateInput(e.key, true));
        window.addEventListener('keyup', e => updateInput(e.key, false));

        function updateInput(key, state) {
            const k = key.toLowerCase();
            if (k === 'a' || k === 'arrowleft') input.left = state;
            if (k === 'd' || k === 'arrowright') input.right = state;
            if (k === 'w' || k === 'arrowup') input.up = state;
            if (k === 's' || k === 'arrowdown') input.down = state;
            if (k === 'k' || k === 'shift') input.spin = state;
            if (k === 'q') input.guard = state;
            if (k === 'e') input.heal = state;
            if (k === 'j' || k === ' ' || k === 'space') input.fire = state;
            if (k === 'f' && state) gamera.toggleFly();
        }

        function showNotify(t) {
            const b = document.getElementById('notify-box');
            if (b) {
                b.innerText = t;
                b.style.display = 'block';
                setTimeout(() => b.style.display = 'none', 3000);
            }
        }

        function createEffect(x, y, type, color = '#ff5500') {
            const count = (type === 'explode') ? 40 : 10;
            for(let i=0; i<count; i++) {
                particles.push({
                    x, y, vx: (Math.random()-0.5)*30, vy: (Math.random()-0.5)*30,
                    life: 1.0, size: Math.random()*20 + 5, color: color, type: type
                });
            }
            if(type === 'explode') shakeAmount = 20;
        }

        const gamera = {
            x: 200, y: GROUND_Y - 320, w: 300, h: 320, 
            vx: 0, vy: 0, hp: 200, maxHp: 200,
            isJumping: false, isSpinning: false, isFlying: false, isGuarding: false, isHealing: false,
            spinTimer: 0, facing: 1, cooldown: 0, animFrame: 0, chargeLevel: 0,
            transformationProgress: 0, guardProgress: 0, healProgress: 0,
            toggleFly() {
                if (this.isGuarding || this.isHealing) return;
                this.isFlying = !this.isFlying;
                createEffect(this.x + this.w/2, this.y + this.h/2, 'smoke', '#00ffff');
            },
            update() {
                this.animFrame += 0.08;
                this.isGuarding = input.guard && !this.isSpinning && !this.isHealing;
                this.isHealing = input.heal && !this.isSpinning && !this.isGuarding && !this.isFlying;
                if (this.isGuarding && this.guardProgress < 1) this.guardProgress += 0.15;
                if (!this.isGuarding && this.guardProgress > 0) this.guardProgress -= 0.15;
                if (this.isHealing && this.hp < this.maxHp) {
                    this.hp += 0.3;
                    if (frameCount % 10 === 0) createEffect(this.x + this.w/2, this.y + this.h/2, 'heal', '#00ff88');
                }
                if (this.isFlying && this.transformationProgress < 1) this.transformationProgress += 0.08;
                if (!this.isFlying && this.transformationProgress > 0) this.transformationProgress -= 0.08;
                if (!this.isSpinning && !this.isGuarding && !this.isHealing) {
                    if (input.left) { this.vx = -12; this.facing = -1; }
                    else if (input.right) { this.vx = 12; this.facing = 1; }
                    else { this.vx *= 0.85; }
                    if (this.isFlying) { if (input.up) { this.vy = -12; } else if (input.down) { this.vy = 12; } else { this.vy *= 0.85; } }
                    else { if (input.up && !this.isJumping) { this.vy = -34; this.isJumping = true; createEffect(this.x + this.w/2, GROUND_Y, 'dust', '#111'); } }
                } else { this.vx *= 0.7; if (!this.isFlying) this.vy += GRAVITY; }
                if (input.spin && this.cooldown <= 0 && !this.isSpinning && !this.isGuarding && !this.isHealing) {
                    this.isSpinning = true; this.spinTimer = 110; this.cooldown = 220; this.vy = -15; createEffect(this.x + this.w/2, this.y + this.h/2, 'explode', '#00f2fe');
                }
                if (this.isSpinning) {
                    this.vx = this.facing * 24; this.spinTimer--;
                    if (this.spinTimer <= 0) this.isSpinning = false;
                    if(frameCount % 3 === 0) createEffect(this.x + this.w/2, this.y + this.h/2, 'smoke', '#00d2ff');
                }
                this.y += this.vy; this.x += this.vx;
                if (!this.isFlying && !this.isSpinning) { if (this.y + this.h < GROUND_Y) this.vy += GRAVITY; else { this.y = GROUND_Y - this.h; this.vy = 0; this.isJumping = false; } }
                else { this.y = Math.max(0, Math.min(GROUND_Y - this.h, this.y)); }
                this.x = Math.max(0, Math.min(CANVAS_W - this.w, this.x));
                if (input.fire && this.cooldown <= 0 && !this.isSpinning && !this.isGuarding && !this.isHealing) {
                    this.chargeLevel += 0.15;
                    if(this.chargeLevel >= 1.0) {
                        const spawnY = this.y + 60; 
                        bullets.push({ x: this.x + (this.facing === 1 ? this.w : 0), y: spawnY, vx: this.facing*35, vy: 0, size: 85, life: 150, color: '#ff6600' });
                        this.cooldown = 15; this.chargeLevel = 0; shakeAmount = 20;
                    }
                } else this.chargeLevel = 0;
                if (this.cooldown > 0) this.cooldown--;
                document.getElementById('player-hp').style.width = (this.hp / this.maxHp * 100) + '%';
            },
            draw() {
                ctx.save(); ctx.translate(this.x + this.w/2, this.y + this.h/2);
                if (this.isSpinning) {
                    ctx.rotate(this.animFrame * 15);
                    const grad = ctx.createRadialGradient(0, 0, 10, 0, 0, this.w/0.8); grad.addColorStop(0, '#fff'); grad.addColorStop(0.3, '#00d2ff'); grad.addColorStop(1, 'transparent');
                    ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(0, 0, this.w/0.8, 0, Math.PI*2); ctx.fill();
                    ctx.fillStyle = '#0a0a0a'; ctx.beginPath(); ctx.ellipse(0, 0, this.w/2.2, this.h/2.2, 0, 0, Math.PI*2); ctx.fill();
                } else {
                    if (this.facing === -1) ctx.scale(-1, 1);
                    const breath = Math.sin(this.animFrame) * 5;
                    const p = this.transformationProgress; const g = this.guardProgress; const h = this.healProgress;
                    const shrink = 1 - (g * 0.4) - (h * 0.2); if (this.isFlying) ctx.rotate(-0.05);
                    ctx.fillStyle = '#1a1f1a'; ctx.beginPath(); ctx.ellipse(0, 20 + breath, (120+30*p)*(1+g*0.2), (140-40*p)*(1+g*0.1), 0, 0, Math.PI*2); ctx.fill();
                    const pColor = h > 0.5 ? '#00ff88' : '#00ffff'; ctx.strokeStyle = pColor; ctx.lineWidth = 3; ctx.shadowBlur = 15*(1-g*0.8); ctx.shadowColor = pColor;
                    for(let i=0; i<3; i++) { ctx.beginPath(); ctx.moveTo((-40+i*40)*shrink, -60+breath); ctx.lineTo((-40+i*40)*shrink, 80+breath); ctx.stroke(); }
                    const magmaInt = this.chargeLevel > 0.5 ? this.chargeLevel : 0.2;
                    ctx.save(); ctx.translate(70*shrink, 40*shrink+breath); ctx.rotate(0.3); ctx.fillStyle = `rgba(255, 68, 0, ${magmaInt})`; ctx.shadowBlur = 20 * magmaInt; ctx.shadowColor = '#ff4400'; ctx.beginPath(); ctx.ellipse(0, 0, 50, 70, 0.4, 0, Math.PI*2); ctx.fill(); ctx.restore();
                    ctx.fillStyle = '#141814';
                    for(let i=0; i<7; i++) {
                        const y = -120 + i*40 + breath; const spikeX = (-100 - (i*5)) * (1+g*0.1);
                        ctx.beginPath(); ctx.moveTo(-100 * shrink, y); ctx.quadraticCurveTo(spikeX-40, y+5, spikeX-70, y+25); ctx.quadraticCurveTo(spikeX-40, y+45, -100*shrink, y+50); ctx.fill();
                    }
                    ctx.save(); ctx.translate((100+30*p)*shrink, (-110+breath+40*p)*shrink); ctx.rotate(0.2*(1-g-h));
                    ctx.fillStyle = '#0a0d0a'; ctx.beginPath(); ctx.ellipse(25, -10, 65*shrink, 40*shrink, -0.1, 0, Math.PI*2); ctx.fill();
                    if (g < 0.8 && h < 0.8) { ctx.fillStyle = '#00ff00'; ctx.shadowBlur = 20; ctx.shadowColor = '#00ff00'; ctx.beginPath(); ctx.arc(40, -25, 12, 0, Math.PI*2); ctx.fill(); }
                    ctx.restore();
                }
                ctx.restore();
            }
        };

        const gyaos = {
            active: true, x: 1100, y: 150, w: 550, h: 300, hp: 300, maxHp: 300,
            angle: 0, attackTimer: 0, animFrame: 0, hitFlash: 0,
            update() {
                if (!this.active) return;
                this.animFrame += 0.15; this.angle += 0.035;
                this.y += (180 + Math.sin(this.angle)*160 - this.y)*0.045; this.x += (gamera.x + 600 - this.x)*0.015;
                this.attackTimer++;
                if (this.attackTimer > 100) { enemyBullets.push({ x: this.x, y: this.y + 40, vx: -28, vy: (gamera.y+120-this.y)/15, life: 80, type: 'sonic' }); this.attackTimer = 0; }
                if (this.hitFlash > 0) this.hitFlash--;
                const hpEl = document.getElementById('enemy-hp');
                if (hpEl) hpEl.style.width = (this.hp / this.maxHp * 100) + '%';
            },
            draw() {
                if (!this.active) return;
                ctx.save(); ctx.translate(this.x + this.w/2, this.y + this.h/2);
                if (this.hitFlash > 0) ctx.filter = 'brightness(3) sepia(1) hue-rotate(-50deg)';
                const ws = Math.sin(this.animFrame)*100; ctx.strokeStyle = '#3d0a0a'; ctx.lineWidth = 12; ctx.beginPath(); ctx.moveTo(0, 20); ctx.quadraticCurveTo(120, 160, 260, 230); ctx.stroke();
                const dw = (s) => { ctx.save(); ctx.scale(s, 1); ctx.fillStyle = '#5e0d0d'; ctx.beginPath(); ctx.moveTo(0, 0); ctx.quadraticCurveTo(180, ws-200, 350, ws); ctx.quadraticCurveTo(200, ws+120, 0, 40); ctx.fill(); ctx.restore(); };
                dw(1); dw(-1);
                ctx.fillStyle = '#3d0a0a'; ctx.beginPath(); ctx.ellipse(0, 10, 100, 55, 0, 0, Math.PI*2); ctx.fill();
                const na = Math.sin(this.animFrame * 0.5) * 12; ctx.fillStyle = '#4a1010'; ctx.beginPath(); ctx.moveTo(-40, 5); ctx.lineTo(-150, -65+na); ctx.lineTo(-160, 15+na); ctx.lineTo(-40, 30); ctx.fill();
                ctx.save(); ctx.translate(-140, -40+na); ctx.rotate(-0.15); ctx.fillStyle = '#3d0a0a'; ctx.fillRect(-90, -30, 110, 25);
                ctx.fillStyle = '#ff0033'; ctx.shadowBlur = 25; ctx.shadowColor = 'red'; ctx.beginPath(); ctx.arc(-95, -5, 12, 0, Math.PI*2); ctx.fill();
                ctx.restore(); ctx.restore();
            }
        };

        const jaiger = {
            active: false, x: 1400, y: GROUND_Y - 220, w: 550, h: 320, hp: 300, maxHp: 300,
            animFrame: 0, attackTimer: 0, state: 'IDLE', hitFlash: 0,
            update() {
                if (!this.active) return;
                this.animFrame += 0.12;
                if (this.state === 'IDLE') { this.x += (gamera.x + 500 - this.x) * 0.02; this.attackTimer++; if (this.attackTimer > 120) { this.state = Math.random() > 0.5 ? 'CHARGE' : 'NEEDLE'; this.attackTimer = 0; } }
                else if (this.state === 'CHARGE') { this.x -= 18; if (this.x < -400) { this.x = 1600; this.state = 'IDLE'; } }
                else if (this.state === 'NEEDLE') { if (this.attackTimer % 30 === 0) { enemyBullets.push({ x: this.x, y: this.y + 40, vx: -35, vy: (Math.random()-0.5)*8, life: 60, type: 'needle' }); } this.attackTimer++; if (this.attackTimer > 80) { this.state = 'IDLE'; this.attackTimer = 0; } }
                if (this.hitFlash > 0) this.hitFlash--;
                const hpEl = document.getElementById('enemy-hp');
                if (hpEl) hpEl.style.width = (this.hp / this.maxHp * 100) + '%';
            },
            draw() {
                if (!this.active) return;
                ctx.save(); ctx.translate(this.x + this.w/2, this.y + this.h/2);
                if (this.hitFlash > 0) ctx.filter = 'brightness(3) sepia(1) hue-rotate(-50deg)';
                const walk = Math.sin(this.animFrame*2)*20; ctx.fillStyle = '#4b0082'; ctx.beginPath(); ctx.ellipse(0, 0, 250, 110, 0, 0, Math.PI*2); ctx.fill();
                ctx.fillStyle = '#4b0082'; ctx.fillRect(-180, 50+walk, 80, 120); ctx.fillRect(100, 50-walk, 80, 120);
                ctx.fillStyle = '#9932cc'; ctx.beginPath(); ctx.ellipse(-200, -25, 120, 95, 0.1, 0, Math.PI*2); ctx.fill();
                ctx.fillStyle = '#ff0000'; ctx.beginPath(); ctx.arc(-240, -40, 15, 0, Math.PI*2); ctx.fill();
                ctx.restore();
            }
        };

        const guiron = {
            active: false, x: 1400, y: GROUND_Y - 250, w: 600, h: 250, hp: 400, maxHp: 400,
            animFrame: 0, attackTimer: 0, hitFlash: 0,
            update() {
                if (!this.active) return;
                this.animFrame += 0.15;
                this.x += (gamera.x + 450 - this.x) * 0.03;
                this.attackTimer++;
                if (this.attackTimer > 90) {
                    for(let i=0; i<3; i++) {
                        enemyBullets.push({ x: this.x + 50, y: this.y + 80, vx: -25, vy: (i-1)*5, life: 100, type: 'shuriken' });
                    }
                    this.attackTimer = 0;
                }
                if (this.hitFlash > 0) this.hitFlash--;
                const hpEl = document.getElementById('enemy-hp');
                if (hpEl) hpEl.style.width = (this.hp / this.maxHp * 100) + '%';
            },
            draw() {
                if (!this.active) return;
                ctx.save(); ctx.translate(this.x + this.w/2, this.y + this.h/2);
                if (this.hitFlash > 0) ctx.filter = 'brightness(3) contrast(1.5)';
                ctx.fillStyle = '#444'; ctx.beginPath(); ctx.ellipse(0, 30, 200, 80, 0, 0, Math.PI*2); ctx.fill();
                ctx.fillStyle = '#777'; ctx.beginPath(); ctx.moveTo(-50, 60); ctx.lineTo(-350, -40); ctx.lineTo(-50, -120); ctx.closePath(); ctx.fill();
                ctx.strokeStyle = '#fff'; ctx.lineWidth = 4; ctx.stroke();
                const walk = Math.sin(this.animFrame*2)*20;
                ctx.fillStyle = '#333'; ctx.fillRect(-100, 80+walk, 60, 90); ctx.fillRect(80, 80-walk, 60, 90);
                ctx.fillStyle = 'red'; ctx.beginPath(); ctx.arc(-80, -20, 10, 0, Math.PI*2); ctx.fill();
                ctx.restore();
            }
        };

        /**
         * 3D 怪獣図鑑
         */
        let dictScene, dictCamera, dictRenderer, dictMonster;
        let monsterBones = { head: null, body: null, tail: [], glowLines: [], arms: [], legs: [], shellGroup: [], jaw: null, wingsL: [], wingsR: [], jaigerSpikes: [] };

        function createCapsule(radius, height, mat) {
            const group = new THREE.Group();
            const cyl = new THREE.Mesh(new THREE.CylinderGeometry(radius, radius, height, 16), mat); group.add(cyl);
            const t = new THREE.Mesh(new THREE.SphereGeometry(radius, 16, 16), mat); t.position.y = height/2; group.add(t);
            const b = new THREE.Mesh(new THREE.SphereGeometry(radius, 16, 16), mat); b.position.y = -height/2; group.add(b);
            return group;
        }

        function init3DDictionary() {
            if (typeof THREE === 'undefined') return;
            const c = document.getElementById('dictionary-3d-canvas');
            if (dictRenderer) return;
            dictScene = new THREE.Scene();
            dictCamera = new THREE.PerspectiveCamera(45, c.clientWidth/c.clientHeight, 0.1, 1000);
            dictRenderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            dictRenderer.setPixelRatio(window.devicePixelRatio);
            dictRenderer.setSize(c.clientWidth, c.clientHeight);
            c.appendChild(dictRenderer.domElement);
            dictScene.add(new THREE.AmbientLight(0xffffff, 0.5));
            const dl = new THREE.DirectionalLight(0xffffff, 0.8); dl.position.set(5, 5, 5); dictScene.add(dl);
            const pl = new THREE.PointLight(0x00ffff, 2, 10); pl.position.set(0, 1, 4); dictScene.add(pl);
            dictCamera.position.set(0, 2, 11);
            c.addEventListener('pointerdown', e => { isDragging = true; previousMouseX = e.clientX; });
            window.addEventListener('pointermove', e => { if (isDragging && dictMonster) { dictMonster.rotation.y += (e.clientX - previousMouseX)*0.01; previousMouseX = e.clientX; } });
            window.addEventListener('pointerup', () => isDragging = false);
            animateDict();
        }

        function updateDictModel() {
            if (!dictScene || typeof THREE === 'undefined') return;
            if (dictMonster) dictScene.remove(dictMonster);
            const m = monstersData[dictIndex];
            const group = new THREE.Group();
            const isUnlocked = unlockedMonsters[m.id];
            monsterBones = { head: null, body: null, tail: [], glowLines: [], arms: [], legs: [], shellGroup: [], jaw: null, wingsL: [], wingsR: [], jaigerSpikes: [] };

            const nameEl = document.getElementById('dict-monster-name');
            const descEl = document.getElementById('dict-monster-desc');
            if (nameEl) nameEl.innerText = isUnlocked ? m.name : "??? (未登録)";
            if (descEl) descEl.innerText = isUnlocked ? m.desc : "撃破してデータを解放せよ。";

            const mat = new THREE.MeshPhongMaterial({ color: isUnlocked ? m.color : 0x111111, shininess: 15 });
            const shellMat = new THREE.MeshPhongMaterial({ color: isUnlocked ? 0x141814 : 0x050505, shininess: 40, flatShading: true });
            const emissiveMat = new THREE.MeshPhongMaterial({ color: 0x00ffff, emissive: 0x00ffff, emissiveIntensity: 2.5 });
            const magmaMat = new THREE.MeshPhongMaterial({ color: 0xff4400, emissive: 0xff1100, emissiveIntensity: 3 });
            const gyaosMat = new THREE.MeshPhongMaterial({ color: isUnlocked ? 0x4b0a0a : 0x111111, shininess: 10 });
            const jaigerMat = new THREE.MeshPhongMaterial({ color: isUnlocked ? 0x6a0d6a : 0x111111, shininess: 20 });
            const jaigerSpikeMat = new THREE.MeshPhongMaterial({ color: isUnlocked ? 0xfff4e0 : 0x050505, shininess: 30 });
            const guironMat = new THREE.MeshPhongMaterial({ color: isUnlocked ? 0x555555 : 0x111111, shininess: 30 });
            const bladeMat = new THREE.MeshPhongMaterial({ color: isUnlocked ? 0xaaaaaa : 0x222222, shininess: 100 });
            const toothMat = new THREE.MeshPhongMaterial({ color: 0xffffff });

            if (m.id === "GAMERA") {
                const bodyGroup = new THREE.Group();
                const mainBody = createCapsule(1.0, 2.2, mat); mainBody.position.y = 0.4; bodyGroup.add(mainBody);
                monsterBones.body = bodyGroup; group.add(bodyGroup);
                const shellNode = new THREE.Group(); shellNode.position.set(0, 0.4, -0.6); bodyGroup.add(shellNode);
                const scuteGeom = new THREE.ConeGeometry(0.4, 0.6, 4);
                for(let row = 0; row < 8; row++) {
                    const rowY = 1.9 - row * 0.45; const count = 3 + (row < 5 ? row : 8 - row);
                    for(let i = 0; i < count; i++) {
                        const scute = new THREE.Mesh(scuteGeom, shellMat); const ang = (i / count - 0.5) * 2.4;
                        scute.position.set(Math.sin(ang)*1.2, rowY, -0.3 - Math.cos(ang)*0.4); scute.rotation.set(-Math.PI/2.2, 0, ang); scute.scale.set(1.2, 1.4, 0.3); shellNode.add(scute);
                    }
                }
                const baseS = new THREE.Mesh(new THREE.SphereGeometry(1.6, 8, 8), shellMat); baseS.scale.set(1.1, 1.6, 0.5); shellNode.add(baseS);
                const veins = [{y:1.1,w:0.5},{y:0.8,w:1.0},{y:0.5,w:1.3},{y:0.2,w:1.1},{y:-0.1,w:0.7}];
                veins.forEach(v => { const glow = new THREE.Mesh(new THREE.BoxGeometry(v.w*0.8, 0.06, 0.15), emissiveMat); glow.position.set(0, v.y, 0.95); bodyGroup.add(glow); monsterBones.glowLines.push(glow); });
                const headNode = new THREE.Group(); headNode.position.set(0, 2.2, 0.4); group.add(headNode); monsterBones.head = headNode;
                const upperHead = new THREE.Mesh(new THREE.BoxGeometry(0.85, 0.5, 1.2), mat); upperHead.position.set(0, 0.35, 0); headNode.add(upperHead);
                const beak = new THREE.Mesh(new THREE.ConeGeometry(0.5, 0.8, 4), mat); beak.position.set(0, 0.1, 0.85); beak.rotation.x = Math.PI/2; upperHead.add(beak);
                const jawGroup = new THREE.Group(); jawGroup.position.set(0, -0.1, 0.1); jawGroup.rotation.x = 0.9; headNode.add(jawGroup); monsterBones.jaw = jawGroup;
                const jaw = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.35, 1.1), mat); jaw.position.set(0, 0, 0.5); jawGroup.add(jaw);
                const tuskL = new THREE.Mesh(new THREE.ConeGeometry(0.12, 0.6, 4), toothMat); tuskL.position.set(0.3, 0.25, 0.8); tuskL.rotation.x = -Math.PI/1.2; jaw.add(tuskL);
                const tuskR = tuskL.clone(); tuskR.position.x = -0.3; jaw.add(tuskR);
                for(let i=0; i<30; i++) { const t = new THREE.Mesh(new THREE.ConeGeometry(0.05, 0.22, 4), toothMat); const a = (i/15)*Math.PI; if(i<15){ t.position.set(Math.sin(a)*0.35, -0.25, 0.6+Math.cos(a)*0.4); upperHead.add(t); } else { t.position.set(Math.sin(a)*0.3, 0.2, 0.4+Math.cos(a)*0.4); t.rotation.x=Math.PI; jaw.add(t); } }
                const createMonsterArm = (side, armMat) => {
                    const armRoot = new THREE.Group(); const humerus = createCapsule(0.45, 0.9, armMat); humerus.position.y = -0.4; armRoot.add(humerus);
                    const palm = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.5, 0.25), armMat); palm.position.set(0, -0.9, 0.2); humerus.add(palm);
                    for(let i = 0; i < 5; i++) {
                        const fNode = new THREE.Group(); const ang = (i / 4 - 0.5) * 2.2; fNode.position.set(Math.sin(ang)*0.2, -0.2, 0.1); fNode.rotation.z = ang; fNode.rotation.x = 0.6;
                        const f = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.08, 0.4), armMat); f.position.y = -0.2; fNode.add(f);
                        const cl = new THREE.Mesh(new THREE.ConeGeometry(0.12, 0.55, 4), new THREE.MeshPhongMaterial({color:0x050505, shininess:80})); cl.position.y = -0.45; cl.rotation.x = -0.6; f.add(cl); palm.add(fNode);
                    }
                    return armRoot;
                };
                const aL = createMonsterArm(-1, mat); aL.position.set(-1.2, 0.9, 0.6); aL.rotation.set(0.4, 0.2, 0.4); bodyGroup.add(aL);
                const aR = createMonsterArm(1, magmaMat); aR.position.set(1.2, 0.9, 0.6); aR.rotation.set(0.4, -0.2, -0.4); bodyGroup.add(aR);
                monsterBones.arms.push(aL, aR);
                const lL = createCapsule(0.5, 1.4, mat); lL.position.set(-0.8, -1.0, 0); lL.rotation.z = 0.15; bodyGroup.add(lL);
                const lR = createCapsule(0.5, 1.4, mat); lR.position.set(0.8, -1.0, 0); lR.rotation.z = -0.15; bodyGroup.add(lR);
                for(let i=0; i<14; i++) { const sRad = 0.75 - i*0.05; const seg = new THREE.Mesh(new THREE.SphereGeometry(sRad, 8, 8), mat); seg.position.set(0, -0.8, -1.1-i*0.48); const tSpike = new THREE.Mesh(new THREE.ConeGeometry(0.16, 0.35, 4), mat); tSpike.position.y = sRad; seg.add(tSpike); group.add(seg); monsterBones.tail.push(seg); }
            } else if (m.id === "GYAOS") {
                const bodyGroup = new THREE.Group(); const torso = createCapsule(0.5, 1.8, gyaosMat); torso.position.y = 0; bodyGroup.add(torso); monsterBones.body = bodyGroup; group.add(bodyGroup);
                const headNode = new THREE.Group(); headNode.position.set(0, 1.3, 0.5); group.add(headNode); monsterBones.head = headNode;
                const skull = new THREE.Mesh(new THREE.BoxGeometry(0.7, 0.3, 1.2), gyaosMat); skull.position.set(0, 0, 0); headNode.add(skull);
                const crest = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.1, 0.8), new THREE.MeshPhongMaterial({color:0x7a1f7a, emissive:0x3d0a3d})); crest.position.set(0, 0.2, -0.2); headNode.add(crest);
                const beak = new THREE.Mesh(new THREE.ConeGeometry(0.35, 0.6, 4), gyaosMat); beak.position.set(0, -0.1, 0.7); beak.rotation.x = Math.PI/2; headNode.add(beak);
                const createWing = (side) => {
                    const wRoot = new THREE.Group(); const b1 = createCapsule(0.12, 4, gyaosMat); b1.rotation.z = side * Math.PI/2.5; b1.position.set(side * 2, 0.5, 0); wRoot.add(b1);
                    const mem = new THREE.Mesh(new THREE.BoxGeometry(4.5, 0.02, 2.5), gyaosMat); mem.position.set(side * 2.5, 0, -0.5); mem.rotation.y = side * 0.2; wRoot.add(mem); return wRoot;
                };
                const wingL = createWing(-1); bodyGroup.add(wingL); monsterBones.wingsL.push(wingL);
                const wingR = createWing(1); bodyGroup.add(wingR); monsterBones.wingsR.push(wingR);
                const createGyaosLeg = (x) => {
                    const leg = new THREE.Group(); const thigh = createCapsule(0.2, 1, gyaosMat); thigh.position.set(x, -0.8, 0); thigh.rotation.x = 0.4; leg.add(thigh);
                    const shin = createCapsule(0.15, 1, gyaosMat); shin.position.set(0, -0.8, 0.3); shin.rotation.x = -0.8; thigh.add(shin);
                    const foot = new THREE.Mesh(new THREE.BoxGeometry(0.3, 0.1, 0.5), gyaosMat); foot.position.set(0, -0.5, 0.2); shin.add(foot); return leg;
                };
                bodyGroup.add(createGyaosLeg(-0.4)); bodyGroup.add(createGyaosLeg(0.4));
                const tail = createCapsule(0.1, 2, gyaosMat); tail.position.set(0, -0.5, -1.2); tail.rotation.x = -0.5; bodyGroup.add(tail);
            } else if (m.id === "JAIGER") {
                const bodyGroup = new THREE.Group(); const coreBody = createCapsule(0.8, 2.8, jaigerMat); coreBody.rotation.x = 0.3; bodyGroup.add(coreBody); monsterBones.body = bodyGroup; group.add(bodyGroup);
                for(let i=0; i<16; i++) { const spikeH = 1.0 + Math.sin(i*0.4) * 0.8; const spike = new THREE.Mesh(new THREE.ConeGeometry(0.15, spikeH, 4), jaigerSpikeMat); spike.position.set(0, 0.8 + spikeH/2, 1.2 - i*0.25); spike.rotation.x = 0.2; bodyGroup.add(spike); monsterBones.jaigerSpikes.push(spike); }
                const headNode = new THREE.Group(); headNode.position.set(0, 0.2, 1.6); group.add(headNode); monsterBones.head = headNode;
                const headPlate = new THREE.Mesh(new THREE.BoxGeometry(1.0, 0.6, 1.2), jaigerMat); headNode.add(headPlate);
                const horn = new THREE.Mesh(new THREE.ConeGeometry(0.2, 1.0, 4), jaigerSpikeMat); horn.position.set(0, 0.5, 0.4); horn.rotation.x = -Math.PI/2.5; headNode.add(horn);
                const tuskL = new THREE.Mesh(new THREE.ConeGeometry(0.15, 0.8, 4), jaigerSpikeMat); tuskL.position.set(0.4, -0.4, 0.5); tuskL.rotation.x = -Math.PI/4; headNode.add(tuskL);
                const tuskR = tuskL.clone(); tuskR.position.x = -0.4; headNode.add(tuskR);
                const createJaigerLeg = (x, z, rZ) => { const leg = new THREE.Group(); const humerus = createCapsule(0.3, 1.2, jaigerMat); humerus.position.set(x, -0.5, z); humerus.rotation.z = rZ; leg.add(humerus); const foot = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.15, 0.6), jaigerMat); foot.position.y = -0.6; humerus.add(foot); return leg; };
                bodyGroup.add(createJaigerLeg(-0.9, 1.0, 0.3)); bodyGroup.add(createJaigerLeg(0.9, 1.0, -0.3)); bodyGroup.add(createJaigerLeg(-0.9, -1.0, 0.2)); bodyGroup.add(createJaigerLeg(0.9, -1.0, -0.2));
                const tail = createCapsule(0.2, 2.5, jaigerMat); tail.position.set(0, -0.2, -1.8); tail.rotation.x = -0.3; bodyGroup.add(tail);
            } else if (m.id === "GUIRON") {
                const bodyGroup = new THREE.Group(); const torso = createCapsule(0.8, 2.5, guironMat); torso.position.y = 0; bodyGroup.add(torso); monsterBones.body = bodyGroup; group.add(bodyGroup);
                const headNode = new THREE.Group(); headNode.position.set(0, 0.2, 1.5); group.add(headNode); monsterBones.head = headNode;
                const blade = new THREE.Mesh(new THREE.BoxGeometry(0.2, 1.8, 3.5), bladeMat); blade.position.set(0, 0.5, 1.0); blade.rotation.x = -0.2; headNode.add(blade);
                const edge = new THREE.Mesh(new THREE.BoxGeometry(0.05, 1.9, 3.6), new THREE.MeshPhongMaterial({color:0xffffff, emissive:0x666666})); edge.position.set(0, 0, 0); blade.add(edge);
                const holeL = new THREE.Mesh(new THREE.CircleGeometry(0.3, 16), new THREE.MeshBasicMaterial({color:0x000000})); holeL.position.set(0.12, 0.4, 0.5); holeL.rotation.y = Math.PI/2; headNode.add(holeL);
                const holeR = holeL.clone(); holeR.position.x = -0.12; holeR.rotation.y = -Math.PI/2; headNode.add(holeR);
                const eyeL = new THREE.Mesh(new THREE.SphereGeometry(0.1, 8, 8), new THREE.MeshBasicMaterial({color:0xff0000})); eyeL.position.set(0.3, 0.0, 0.8); headNode.add(eyeL);
                const eyeR = eyeL.clone(); eyeR.position.x = -0.3; headNode.add(eyeR);
                const createGuironLeg = (x, z) => { const l = new THREE.Group(); const u = createCapsule(0.35, 1, guironMat); u.position.set(x, -0.6, z); l.add(u); const f = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.2, 0.6), guironMat); f.position.y = -0.6; u.add(f); return l; };
                bodyGroup.add(createGuironLeg(0.8, 0.8)); bodyGroup.add(createGuironLeg(-0.8, 0.8)); bodyGroup.add(createGuironLeg(0.8, -0.8)); bodyGroup.add(createGuironLeg(-0.8, -0.8));
            }
            dictMonster = group; dictScene.add(dictMonster);
        }

        function animateDict() {
            requestAnimationFrame(animateDict);
            const t = Date.now() * 0.001;
            if (dictMonster && !isDragging) {
                dictMonster.rotation.y += 0.005;
                const mId = monstersData[dictIndex].id;
                if (mId === "GAMERA" && monsterBones.body) {
                    const b = Math.sin(t * 1.5); monsterBones.body.scale.set(1 + b*0.02, 1 + b*0.01, 1 + b*0.02);
                    monsterBones.glowLines.forEach(g => { if(g.material) g.material.emissiveIntensity = 2.0 + b * 1.5; });
                    monsterBones.arms.forEach(a => { a.traverse(node => { if (node.isMesh && node.material && node.material.emissive && node.material.color.r > 0.7) node.material.emissiveIntensity = 3.5 + b * 2.5; }); });
                    if (monsterBones.jaw) monsterBones.jaw.rotation.x = 0.9 + Math.sin(t * 3) * 0.15;
                    if (monsterBones.head) monsterBones.head.rotation.x = Math.sin(t*0.7)*0.08;
                    monsterBones.tail.forEach((s, i) => { s.position.x = Math.sin(t*2.5 - i*0.4) * (0.15 + i*0.03); s.position.y = -0.8 + Math.cos(t*1.5 - i*0.25) * 0.08; });
                } else if (mId === "GYAOS" && monsterBones.body) {
                    const flap = Math.sin(t * 2.5); monsterBones.wingsL.forEach(w => w.rotation.z = -0.5 + flap * 0.3); monsterBones.wingsR.forEach(w => w.rotation.z = 0.5 - flap * 0.3);
                    if (monsterBones.head) { monsterBones.head.rotation.y = Math.sin(t) * 0.2; monsterBones.head.rotation.x = Math.cos(t * 1.5) * 0.1; }
                    monsterBones.body.position.y = Math.sin(t * 2) * 0.2;
                } else if (mId === "JAIGER" && monsterBones.body) {
                    const b = Math.sin(t * 1.5); if(monsterBones.head) monsterBones.head.rotation.y = Math.sin(t * 0.8) * 0.2;
                    monsterBones.jaigerSpikes.forEach((s, i) => { s.scale.y = 1 + Math.sin(t * 2 + i * 0.5) * 0.1; });
                } else if (mId === "GUIRON" && monsterBones.body) {
                    if(monsterBones.head) monsterBones.head.rotation.x = Math.sin(t * 2) * 0.1;
                    monsterBones.body.position.y = Math.cos(t * 3) * 0.05;
                }
            }
            if (dictRenderer && dictScene && dictCamera) dictRenderer.render(dictScene, dictCamera);
        }

        // --- 衝突判定システム ---
        function updateBattle() {
            let target;
            if (currentLevel === 'GYAOS') target = gyaos;
            else if (currentLevel === 'JAIGER') target = jaiger;
            else target = guiron;

            const targetCenterX = target.x + target.w / 2;
            const targetCenterY = target.y + target.h / 2;

            for(let i = bullets.length - 1; i >= 0; i--) {
                const b = bullets[i]; 
                const angleToTarget = Math.atan2(targetCenterY - b.y, targetCenterX - b.x);
                b.vx += Math.cos(angleToTarget) * 6;
                b.vy += Math.sin(angleToTarget) * 6;
                const speed = Math.sqrt(b.vx*b.vx + b.vy*b.vy);
                if (speed > 55) { b.vx = (b.vx/speed)*55; b.vy = (b.vy/speed)*55; }
                b.x += b.vx; b.y += b.vy; b.life--;
                
                const bSize = b.size * 0.8;
                const hit = b.x + bSize > target.x && b.x - bSize < target.x + target.w && b.y + bSize > target.y && b.y - bSize < target.y + target.h;
                if (hit) { target.hp -= 40; target.hitFlash = 15; createEffect(b.x, b.y, 'explode', '#ffaa00'); bullets.splice(i, 1); continue; }
                if (b.life <= 0) bullets.splice(i, 1);
            }

            for(let i = enemyBullets.length - 1; i >= 0; i--) {
                const b = enemyBullets[i]; b.x += b.vx; b.y += b.vy; b.life--;
                if (b.x > gamera.x && b.x < gamera.x + gamera.w && b.y > gamera.y && b.y < gamera.y + gamera.h) {
                    const dmg = gamera.isGuarding ? 0.2 : 15; gamera.hp -= dmg;
                    createEffect(b.x, b.y, gamera.isGuarding?'smoke':'explode', gamera.isGuarding?'#00ffff':'#ffffff');
                    enemyBullets.splice(i, 1); continue;
                }
                if (b.life <= 0) enemyBullets.splice(i, 1);
            }
        }

        function loop() {
            frameCount++; ctx.clearRect(0, 0, CANVAS_W, CANVAS_H);
            ctx.save(); if (shakeAmount > 0) { ctx.translate((Math.random()-0.5)*shakeAmount, (Math.random()-0.5)*shakeAmount); shakeAmount *= 0.93; }
            drawScene();
            if (gameState === 'PLAYING') {
                gamera.update(); if (currentLevel === 'GYAOS') gyaos.update(); else if (currentLevel === 'JAIGER') jaiger.update(); else guiron.update();
                updateBattle();
                if (gamera.hp <= 0) endGame(false);
                if (currentLevel === 'GYAOS' && gyaos.hp <= 0) startNextLevel();
                else if (currentLevel === 'JAIGER' && jaiger.hp <= 0) startNextLevel();
                else if (currentLevel === 'GUIRON' && guiron.hp <= 0) endGame(true);
            }
            gamera.draw(); gyaos.draw(); jaiger.draw(); guiron.draw(); drawBattle(); drawParticles();
            ctx.restore(); requestAnimationFrame(loop);
        }

        function drawScene() { 
            const sky = ctx.createLinearGradient(0, 0, 0, CANVAS_H); 
            if (currentLevel === 'GUIRON') { sky.addColorStop(0, '#111'); sky.addColorStop(1, '#333'); } 
            else { sky.addColorStop(0, '#010105'); sky.addColorStop(1, '#050a1f'); }
            ctx.fillStyle = sky; ctx.fillRect(0, 0, CANVAS_W, CANVAS_H); 
            ctx.fillStyle = '#030303'; ctx.fillRect(0, GROUND_Y, CANVAS_W, 200); 
        }

        function drawBattle() { bullets.forEach(b => { ctx.fillStyle = b.color; ctx.shadowBlur = 40; ctx.shadowColor = b.color; ctx.beginPath(); ctx.arc(b.x, b.y, b.size/2, 0, Math.PI*2); ctx.fill(); ctx.shadowBlur = 0; }); enemyBullets.forEach(b => { ctx.strokeStyle = (b.type === 'shuriken') ? '#ccc' : (b.type === 'needle' ? '#00ffff' : '#fff'); ctx.lineWidth = 7; ctx.beginPath(); if(b.type === 'shuriken') { ctx.arc(b.x, b.y, 20, 0, Math.PI*2); } else { ctx.moveTo(b.x, b.y); ctx.lineTo(b.x+80, b.y+b.vy*3); } ctx.stroke(); }); }
        function drawParticles() { for(let i = particles.length - 1; i >= 0; i--) { const p = particles[i]; p.x += p.vx; p.y += p.vy; p.life -= 0.025; if (p.life <= 0) { particles.splice(i, 1); continue; } ctx.globalAlpha = p.life; ctx.fillStyle = p.color; ctx.beginPath(); ctx.arc(p.x, p.y, p.size*p.life, 0, Math.PI*2); ctx.fill(); } ctx.globalAlpha = 1.0; }
        
        function endGame(win) { 
            gameState = 'GAMEOVER'; if (win) unlockedMonsters.GUIRON = true; 
            document.getElementById('end-screen').style.display = 'flex';
            const t = document.getElementById('result-title');
            if (win) { t.innerText = "VICTORY"; t.style.color = "#00ffff"; showNotify("全怪獣 撃破完了!"); }
            else { t.innerText = "DEFEAT"; t.style.color = "#ff3333"; }
            pauseMusic(); 
        }

        function startNextLevel() { 
            if (currentLevel === 'GYAOS') {
                unlockedMonsters.GYAOS = true; showNotify("GYAOS 解析完了!次はJAIGERだ!"); 
                gyaos.active = false; currentLevel = 'JAIGER'; jaiger.active = true;
                document.getElementById('enemy-name').innerText = 'JAIGER';
                createEffect(CANVAS_W-200, GROUND_Y, 'explode', '#9932cc');
            } else if (currentLevel === 'JAIGER') {
                unlockedMonsters.JAIGER = true; showNotify("JAIGER 解析完了!最終兵器GUIRON出現!"); 
                jaiger.active = false; currentLevel = 'GUIRON'; guiron.active = true;
                document.getElementById('enemy-name').innerText = 'GUIRON';
                createEffect(CANVAS_W-200, GROUND_Y, 'explode', '#666666');
            }
        }

        document.getElementById('volume-btn').onclick = () => {
            isMusicEnabled = !isMusicEnabled; document.getElementById('volume-btn').innerText = `MUSIC: ${isMusicEnabled ? 'ON' : 'OFF'}`;
            if (!isMusicEnabled) pauseMusic(); else if (gameState === 'PLAYING') playMusic();
        };

        document.getElementById('start-btn').onclick = () => { document.getElementById('start-screen').style.display = 'none'; gameState = 'PLAYING'; isMusicEnabled = true; document.getElementById('volume-btn').innerText = "MUSIC: ON"; battleMusic.currentTime = 0; playMusic(); };
        document.getElementById('restart-btn').onclick = () => location.reload();
        function openDictionary() {
            document.getElementById('dictionary-screen').style.display = 'block'; 
            const c = document.getElementById('dictionary-3d-canvas');
            if (dictRenderer) { dictRenderer.setSize(c.clientWidth, c.clientHeight); dictCamera.aspect = c.clientWidth/c.clientHeight; dictCamera.updateProjectionMatrix(); updateDictModel(); } else { init3DDictionary(); updateDictModel(); }
        }
        document.getElementById('view-dict-btn').onclick = openDictionary;
        document.getElementById('to-dict-btn').onclick = () => { document.getElementById('end-screen').style.display = 'none'; openDictionary(); };
        document.getElementById('dict-back').onclick = () => document.getElementById('dictionary-screen').style.display = 'none';
        document.getElementById('dict-next').onclick = () => { dictIndex = (dictIndex+1)%monstersData.length; updateDictModel(); };
        document.getElementById('dict-prev').onclick = () => { dictIndex = (dictIndex-1+monstersData.length)%monstersData.length; updateDictModel(); };
        window.onload = () => { const checkThree = setInterval(() => { if (typeof THREE !== 'undefined') { init3DDictionary(); clearInterval(checkThree); } }, 100); loop(); };
    </script>
</body>
</html>

GAMERA

ガメラリバースをモデルとしたバトルゲームです。 とても簡単です! なおスマホ対応タブレット対応はしていません・・・ スミマセン・・・

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