ノート

軸方向ドリフト:モバイル対応物理迷路ゲーム

フリーのプロンプト百科事典 Wikiprompt より

loshu2000

2026年5月17日

軸方向ドリフト:モバイル対応物理迷路ゲーム 重力シフト機構、モバイル傾きコントロール、バウハウス視覚スタイルを備えた単一ファイルHTML5迷路ゲームを生成するための包括的なプロンプト。

プロンプト内容保存

🌐
<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>Axial Drift</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } html, body { width: 100%; height: 100%; overflow: hidden; background: #1a1a1a; font-family: 'Helvetica Neue', Arial, sans-serif; touch-action: none; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; } #gameCanvas { display: block; width: 100%; height: 100%; background: #f4f4f0; } #ui-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10; } #hud { position: absolute; top: 16px; left: 16px; color: #000; font-size: 14px; font-weight: 700; letter-spacing: 1px; background: #ffd500; padding: 8px 12px; border: 3px solid #000; box-shadow: 4px 4px 0 #000; } #status { position: absolute; bottom: 16px; left: 16px; color: #fff; font-size: 12px; font-weight: 700; background: #e63946; padding: 6px 10px; border: 2px solid #000; box-shadow: 3px 3px 0 #000; } #gravity-indicator { position: absolute; top: 16px; right: 16px; width: 60px; height: 60px; background: #fff; border: 3px solid #000; box-shadow: 4px 4px 0 #000; display: flex; align-items: center; justify-content: center; } #gravity-arrow { width: 0; height: 0; border-left: 12px solid transparent; border-right: 12px solid transparent; border-bottom: 24px solid #e63946; transform-origin: center; transition: transform 0.1s; } #win-screen { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); display: none; align-items: center; justify-content: center; flex-direction: column; z-index: 20; color: #fff; } #win-screen h1 { font-size: 48px; color: #ffd500; text-shadow: 4px 4px 0 #000; margin-bottom: 20px; letter-spacing: 4px; } #win-screen p { font-size: 18px; margin-bottom: 30px; color: #fff; } #restart-btn { pointer-events: auto; background: #e63946; color: #fff; border: 3px solid #000; padding: 12px 24px; font-size: 16px; font-weight: 700; cursor: pointer; box-shadow: 4px 4px 0 #000; transition: transform 0.1s; } #restart-btn:hover { transform: translate(2px, 2px); box-shadow: 2px 2px 0 #000; } #restart-btn:active { transform: translate(4px, 4px); box-shadow: 0 0 0 #000; } </style> </head> <body> <canvas id="gameCanvas"></canvas> <div id="ui-overlay"> <div id="hud">AXIAL DRIFT</div> <div id="status">VORTEX: 未到達</div> <div id="gravity-indicator"> <div id="gravity-arrow"></div> </div> </div> <div id="win-screen"> <h1>VORTEX 到達</h1> <p>軸のドリフトを制御しました</p> <button id="restart-btn">再起動</button> </div> <script> (function() { 'use strict'; // ===== キャンバス設定 ===== const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const winScreen = document.getElementById('win-screen'); const restartBtn = document.getElementById('restart-btn'); const statusEl = document.getElementById('status'); const gravityArrow = document.getElementById('gravity-arrow'); // ===== 物理定数 ===== const PHYSICS = { GRAVITY_MAX: 1500, // 最大重力加速度 (px/s^2) FRICTION: 0.985, // 摩擦減衰係数 ANGULAR_DAMPING: 0.95, // 角速度減衰 MOMENTUM_DAMPING: 0.99, // 運動量減衰 MARBLE_RADIUS: 12, BOUNCE: 0.4, // 跳ね返り係数 MAX_SPEED: 800 }; // ===== 迷路定義 ===== // 壁は [x1, y1, x2, y2] の線分 const mazeWalls = [ // 外枠 [50, 50, 750, 50], [750, 50, 750, 550], [750, 550, 50, 550], [50, 550, 50, 50], // 内部構造 (バウハウス風の幾何学) [150, 50, 150, 200], [150, 200, 300, 200], [300, 200, 300, 100], [300, 100, 450, 100], [450, 100, 450, 250], [450, 250, 250, 250], [250, 250, 250, 400], [250, 400, 400, 400], [400, 400, 400, 300], [400, 300, 550, 300], [550, 300, 550, 450], [550, 450, 650, 450], [650, 450, 650, 150], [650, 150, 500, 150], [500, 150, 500, 50], [100, 350, 100, 500], [100, 500, 200, 500], [200, 500, 200, 350], [200, 350, 350, 350], [350, 350, 350, 500], [600, 200, 700, 200], [700, 200, 700, 350], [700, 350, 600, 350], [600, 350, 600, 200] ]; // 動的トラップドア (時間で開閉) const trapDoors = [ { x: 200, y: 280, w: 80, h: 12, phase: 0, speed: 0.8, active: true }, { x: 450, y: 180, w: 80, h: 12, phase: 2, speed: 0.6, active: true }, { x: 320, y: 450, w: 60, h: 12, phase: 4, speed: 0.7, active: true }, { x: 580, y: 380, w: 70, h: 12, phase: 1, speed: 0.9, active: true } ]; // 中心のボルテックス (目標) const vortex = { x: 400, y: 300, radius: 35 }; // ===== マーブル状態 ===== let marble = { x: 100, y: 100, vx: 0, vy: 0, angle: 0, angularVel: 0, radius: PHYSICS.MARBLE_RADIUS }; // ===== 重力ベクトル ===== let gravity = { x: 0, y: 0 }; // 正規化 (-1 〜 1) let targetGravity = { x: 0, y: 0 }; // ===== 入力状態 ===== let isDragging = false; let lastMouseX = 0, lastMouseY = 0; let deviceOrientationSupported = false; // ===== ゲーム状態 ===== let gameWon = false; let lastTime = 0; let elapsedTime = 0; // ===== リサイズ処理 ===== function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); // ===== マウス入力 ===== canvas.addEventListener('mousedown', (e) => { if (gameWon) return; isDragging = true; lastMouseX = e.clientX; lastMouseY = e.clientY; }); window.addEventListener('mousemove', (e) => { if (!isDragging || gameWon) return; const dx = e.clientX - lastMouseX; const dy = e.clientY - lastMouseY; // ドラッグ方向と逆に重力を傾ける targetGravity.x = Math.max(-1, Math.min(1, targetGravity.x - dx / 150)); targetGravity.y = Math.max(-1, Math.min(1, targetGravity.y - dy / 150)); lastMouseX = e.clientX; lastMouseY = e.clientY; }); window.addEventListener('mouseup', () => { isDragging = false; }); // ===== タッチ入力 ===== canvas.addEventListener('touchstart', (e) => { if (gameWon) return; e.preventDefault(); const touch = e.touches[0]; isDragging = true; lastMouseX = touch.clientX; lastMouseY = touch.clientY; }); canvas.addEventListener('touchmove', (e) => { if (!isDragging || gameWon) return; e.preventDefault(); const touch = e.touches[0]; const dx = touch.clientX - lastMouseX; const dy = touch.clientY - lastMouseY; targetGravity.x = Math.max(-1, Math.min(1, targetGravity.x - dx / 150)); targetGravity.y = Math.max(-1, Math.min(1, targetGravity.y - dy / 150)); lastMouseX = touch.clientX; lastMouseY = touch.clientY; }); canvas.addEventListener('touchend', (e) => { e.preventDefault(); isDragging = false; }); // ===== デバイスオリエンテーション ===== function handleOrientation(e) { if (gameWon) return; if (e.gamma === null || e.beta === null) return; deviceOrientationSupported = true; // gamma: 左右 (-90 〜 90), beta: 前後 (-180 〜 180) const xTilt = Math.max(-1, Math.min(1, e.gamma / 45)); const yTilt = Math.max(-1, Math.min(1, e.beta / 45)); targetGravity.x = xTilt; targetGravity.y = yTilt; } if (window.DeviceOrientationEvent) { window.addEventListener('deviceorientation', handleOrientation); } // ===== 壁衝突判定 ===== function checkWallCollision() { for (let i = 0; i < mazeWalls.length; i++) { const wall = mazeWalls[i]; const x1 = wall[0], y1 = wall[1], x2 = wall[2], y2 = wall[3]; // 線分と円の最短距離を計算 const dx = x2 - x1; const dy = y2 - y1; const lenSq = dx * dx + dy * dy; if (lenSq === 0) continue; let t = ((marble.x - x1) * dx + (marble.y - y1) * dy) / lenSq; t = Math.max(0, Math.min(1, t)); const closestX = x1 + t * dx; const closestY = y1 + t * dy; const distX = marble.x - closestX; const distY = marble.y - closestY; const dist = Math.sqrt(distX * distX + distY * distY); if (dist < marble.radius) { // 衝突解決 const overlap = marble.radius - dist; const nx = distX / (dist || 1); const ny = distY / (dist || 1); marble.x += nx * overlap; marble.y += ny * overlap; // 反射 const dot = marble.vx * nx + marble.vy * ny; marble.vx -= (1 + PHYSICS.BOUNCE) * dot * nx; marble.vy -= (1 + PHYSICS.BOUNCE) * dot * ny; // 摩擦 marble.vx *= 0.8; marble.vy *= 0.8; // 角速度更新 marble.angularVel += (marble.vx * ny - marble.vy * nx) * 0.05; } } } // ===== トラップドア衝突 ===== function checkTrapCollision() { for (let i = 0; i < trapDoors.length; i++) { const door = trapDoors[i]; if (!door.active) continue; // ドアの矩形と円の衝突 const closestX = Math.max(door.x, Math.min(marble.x, door.x + door.w)); const closestY = Math.max(door.y, Math.min(marble.y, door.y + door.h)); const distX = marble.x - closestX; const distY = marble.y - closestY; const dist = Math.sqrt(distX * distX + distY * distY); if (dist < marble.radius) { // 衝突: ドアを押しのける const overlap = marble.radius - dist; const nx = distX / (dist || 1); const ny = distY / (dist || 1); marble.x += nx * overlap; marble.y += ny * overlap; const dot = marble.vx * nx + marble.vy * ny; marble.vx -= (1 + PHYSICS.BOUNCE) * dot * nx; marble.vy -= (1 + PHYSICS.BOUNCE) * dot * ny; } } } // ===== ボルテックス到達判定 ===== function checkVortexReached() { const dx = marble.x - vortex.x; const dy = marble.y - vortex.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < vortex.radius + marble.radius) { gameWon = true; winScreen.style.display = 'flex'; statusEl.textContent = 'VORTEX: 到達'; } } // ===== 物理更新 ===== function updatePhysics(dt) { if (gameWon) return; // 重力をスムーズに補間 gravity.x += (targetGravity.x - gravity.x) * 0.1; gravity.y += (targetGravity.y - gravity.y) * 0.1; // 重力加速度を適用 const gx = gravity.x * PHYSICS.GRAVITY_MAX; const gy = gravity.y * PHYSICS.GRAVITY_MAX; marble.vx += gx * dt; marble.vy += gy * dt; // 最大速度制限 const speed = Math.sqrt(marble.vx * marble.vx + marble.vy * marble.vy); if (speed > PHYSICS.MAX_SPEED) { marble.vx = (marble.vx / speed) * PHYSICS.MAX_SPEED; marble.vy = (marble.vy / speed) * PHYSICS.MAX_SPEED; } // 位置更新 marble.x += marble.vx * dt; marble.y += marble.vy * dt; // 摩擦と減衰 marble.vx *= PHYSICS.FRICTION; marble.vy *= PHYSICS.FRICTION; marble.angularVel *= PHYSICS.ANGULAR_DAMPING; // 角速度を位置に反映 (転がり) marble.angle += marble.angularVel * dt; // 衝突判定 checkWallCollision(); checkTrapCollision(); // トラップドアの開閉更新 for (let i = 0; i < trapDoors.length; i++) { const door = trapDoors[i]; door.phase += door.speed * dt; door.active = Math.sin(door.phase) > -0.3; } // ボルテックス到達チェック checkVortexReached(); } // ===== 描画 ===== function draw() { const w = canvas.width; const h = canvas.height; // 背景 ctx.fillStyle = '#f4f4f0'; ctx.fillRect(0, 0, w, h); // スケーリング (迷路を中央に配置) const scale = Math.min(w / 800, h / 600); const offsetX = (w - 800 * scale) / 2; const offsetY = (h - 600 * scale) / 2; ctx.save(); ctx.translate(offsetX, offsetY); ctx.scale(scale, scale); // 影の設定 ctx.shadowColor = 'rgba(0,0,0,0.3)'; ctx.shadowBlur = 10; ctx.shadowOffsetX = 5; ctx.shadowOffsetY = 5; // 迷路の床 (バウハウス風の色ブロック) ctx.fillStyle = '#ffd500'; ctx.fillRect(50, 50, 700, 500); ctx.fillStyle = '#e63946'; ctx.fillRect(100, 100, 150, 150); ctx.fillStyle = '#2a9d8f'; ctx.fillRect(450, 350, 200, 150); ctx.fillStyle = '#264653'; ctx.fillRect(300, 150, 100, 100); // 影をリセット (壁用) ctx.shadowColor = 'rgba(0,0,0,0.5)'; ctx.shadowBlur = 8; ctx.shadowOffsetX = 3; ctx.shadowOffsetY = 3; // 壁を描画 ctx.strokeStyle = '#000'; ctx.lineWidth = 6; ctx.lineCap = 'round'; ctx.beginPath(); for (let i = 0; i < mazeWalls.length; i++) { const wall = mazeWalls[i]; ctx.moveTo(wall[0], wall[1]); ctx.lineTo(wall[2], wall[3]); } ctx.stroke(); // トラップドアを描画 for (let i = 0; i < trapDoors.length; i++) { const door = trapDoors[i]; ctx.shadowColor = 'rgba(0,0,0,0.4)'; ctx.shadowBlur = 6; ctx.shadowOffsetX = 2; ctx.shadowOffsetY = 2; if (door.active) { ctx.fillStyle = '#e63946'; ctx.strokeStyle = '#000'; ctx.lineWidth = 3; ctx.fillRect(door.x, door.y, door.w, door.h); ctx.strokeRect(door.x, door.y, door.w, door.h); } else { ctx.fillStyle = 'rgba(230, 57, 70, 0.3)'; ctx.strokeStyle = 'rgba(0,0,0,0.3)'; ctx.lineWidth = 2; ctx.fillRect(door.x, door.y, door.w, door.h); ctx.strokeRect(door.x, door.y, door.w, door.h); } } // ボルテックスを描画 ctx.shadowColor = 'rgba(42, 157, 143, 0.8)'; ctx.shadowBlur = 20; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; const vortexPulse = 1 + Math.sin(elapsedTime * 3) * 0.1; ctx.fillStyle = '#2a9d8f'; ctx.beginPath(); ctx.arc(vortex.x, vortex.y, vortex.radius * vortexPulse, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#000'; ctx.lineWidth = 4; ctx.stroke(); // ボルテックス内側の渦 ctx.fillStyle = '#264653'; ctx.beginPath(); ctx.arc(vortex.x, vortex.y, vortex.radius * 0.5 * vortexPulse, 0, Math.PI * 2); ctx.fill(); // マーブルを描画 ctx.shadowColor = 'rgba(0,0,0,0.6)'; ctx.shadowBlur = 15; ctx.shadowOffsetX = 4; ctx.shadowOffsetY = 4; ctx.save(); ctx.translate(marble.x, marble.y); ctx.rotate(marble.angle); // マーブルのグラデーション const grad = ctx.createRadialGradient(-3, -3, 2, 0, 0, marble.radius); grad.addColorStop(0, '#ffffff'); grad.addColorStop(0.3, '#cccccc'); grad.addColorStop(1, '#333333'); ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(0, 0, marble.radius, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.stroke(); // マーブルの模様 (回転を可視化) ctx.strokeStyle = '#e63946'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(marble.radius * 0.7, 0); ctx.stroke(); ctx.restore(); ctx.restore(); // 重力インジケーター更新 const angle = Math.atan2(gravity.y, gravity.x) + Math.PI / 2; gravityArrow.style.transform = `rotate(${angle}rad)`; } // ===== メインループ ===== function gameLoop(timestamp) { const dt = Math.min((timestamp - lastTime) / 1000, 0.05); lastTime = timestamp; elapsedTime += dt; updatePhysics(dt); draw(); requestAnimationFrame(gameLoop); } // ===== リスタート ===== function restartGame() { marble.x = 100; marble.y = 100; marble.vx = 0; marble.vy = 0; marble.angle = 0; marble.angularVel = 0; gravity = { x: 0, y: 0 }; targetGravity = { x: 0, y: 0 }; gameWon = false; winScreen.style.display = 'none'; statusEl.textContent = 'VORTEX: 未到達'; // トラップドアをリセット for (let i = 0; i < trapDoors.length; i++) { trapDoors[i].phase = i * 1.5; trapDoors[i].active = true; } } restartBtn.addEventListener('click', restartGame); // ===== 初期化 ===== lastTime = performance.now(); requestAnimationFrame(gameLoop); })(); </script> </body> </html>

ログインして完全なプロンプトを表示

次で続行:

ログインすると、次に同意したことになります: 利用規約 と プライバシーポリシー

使い方

このプロンプトは coding 向けに設計されています。上の内容をコピーして、お好みの AI ツールに貼り付けてください。

最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。

参考資料

カテゴリ:coding| prompts.chat| game-development| html5

ノート