食べて大きくなれ!スネークゲーム

katayamawp

スネークゲーム

スコア:
0
ハイスコア:
0

スネークゲーム

矢印キーで蛇を操作し、赤い餌を食べてスコアを稼ごう!

壁や自分の体に当たると終了です

操作方法: 矢印キー ↑←↓→ または W,A,S,D







コード公開

        
        



CSS

#snake-game * { box-sizing: border-box; } .game-container { font-family: "Hiragino Kaku Gothic Pro", "メイリオ", sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d); background-size: 400% 400%; animation: gradientBG 15s ease infinite; border-radius: 15px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25); } @keyframes gradientBG { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .game-header { text-align: center; margin-bottom: 20px; color: white; } .game-header h2 { margin-bottom: 15px; font-size: 32px; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); } .score-info { display: flex; justify-content: center; gap: 30px; } .score-container, .highscore-container { background-color: rgba(255, 255, 255, 0.9); padding: 8px 20px; border-radius: 50px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); } .score-label { font-weight: bold; color: #333; } .score-value { font-weight: bold; color: #e74c3c; font-size: 18px; } .game-board-container { position: relative; width: 100%; max-width: 400px; height: 400px; margin: 0 auto; border-radius: 5px; overflow: hidden; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); } #gameBoard { background-color: #f8f8f8; display: block; } .overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.7); display: flex; justify-content: center; align-items: center; z-index: 10; } .overlay-content { background-color: white; padding: 30px; border-radius: 10px; text-align: center; max-width: 80%; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3); } .overlay-content h3 { margin-bottom: 15px; color: #2c3e50; font-size: 24px; } .overlay-content p { margin-bottom: 15px; color: #34495e; font-size: 16px; } .btn { background-color: #2ecc71; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; font-weight: bold; transition: all 0.3s ease; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); } .btn:hover { background-color: #27ae60; transform: translateY(-2px); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); } .btn:active { transform: translateY(1px); } .hidden { display: none; } .game-controls { margin-top: 20px; padding: 15px; background-color: rgba(255, 255, 255, 0.9); border-radius: 10px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); } .control-info { text-align: center; margin-bottom: 15px; color: #34495e; font-weight: bold; } .mobile-controls { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-bottom: 15px; } .mobile-controls-row { display: flex; gap: 10px; justify-content: center; } .control-btn { width: 60px; height: 60px; background-color: #3498db; color: white; border: none; border-radius: 10px; font-size: 24px; cursor: pointer; display: flex; justify-content: center; align-items: center; box-shadow: 0 3px 6px rgba(0, 0, 0, 0.1); transition: all 0.2s ease; user-select: none; } .control-btn:active { background-color: #2980b9; transform: translateY(2px); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .game-options { display: flex; justify-content: center; flex-wrap: wrap; gap: 20px; margin-top: 15px; } .speed-control, .grid-control { display: flex; align-items: center; gap: 8px; } .select-css { padding: 6px 10px; border: 1px solid #bdc3c7; border-radius: 5px; font-size: 14px; background-color: white; } .select-css:focus { outline: none; border-color: #3498db; } /* レスポンシブデザイン */ @media (max-width: 600px) { .game-container { padding: 10px; } .game-header h2 { font-size: 24px; } .game-board-container { height: 300px; } #gameBoard { width: 300px; height: 300px; } .mobile-controls { display: flex; } .control-btn { width: 50px; height: 50px; font-size: 20px; } .score-info { flex-direction: column; gap: 10px; align-items: center; } }

JS

document.addEventListener('DOMContentLoaded', function() { // キャンバスの設定 const canvas = document.getElementById('gameBoard'); const ctx = canvas.getContext('2d'); // DOM要素 const startButton = document.getElementById('startButton'); const restartButton = document.getElementById('restartButton'); const overlay = document.getElementById('overlay'); const gameOverOverlay = document.getElementById('gameOverOverlay'); const scoreElement = document.getElementById('score'); const highScoreElement = document.getElementById('highScore'); const finalScoreElement = document.getElementById('finalScore'); const gameSpeedSelect = document.getElementById('gameSpeed'); const showGridCheckbox = document.getElementById('showGrid'); // モバイルコントロール const upButton = document.getElementById('upButton'); const leftButton = document.getElementById('leftButton'); const downButton = document.getElementById('downButton'); const rightButton = document.getElementById('rightButton'); // ゲーム設定 const gameConfig = { gridSize: 20, // グリッドの一区画のサイズ gridWidth: canvas.width / 20, gridHeight: canvas.height / 20, initialSnakeLength: 3, speeds: { slow: 150, normal: 100, fast: 70 } }; // ゲーム状態 let gameState = { snake: [], direction: 'right', nextDirection: 'right', food: {}, gameActive: false, score: 0, highScore: localStorage.getItem('snakeHighScore') || 0, speed: gameConfig.speeds.normal, showGrid: true, gameLoopTimeout: null }; // 高得点を初期表示 highScoreElement.textContent = gameState.highScore; // ゲーム初期化 function initGame() { // 蛇の初期位置 const centerX = Math.floor(gameConfig.gridWidth / 2); const centerY = Math.floor(gameConfig.gridHeight / 2); gameState.snake = []; for (let i = 0; i < gameConfig.initialSnakeLength; i++) { gameState.snake.push({ x: centerX - i, y: centerY }); } // 方向の初期化 gameState.direction = 'right'; gameState.nextDirection = 'right'; // スコアの初期化 gameState.score = 0; scoreElement.textContent = '0'; // 最初の餌の配置 placeFood(); // スピード設定の適用 gameState.speed = gameConfig.speeds[gameSpeedSelect.value]; // グリッド表示設定の適用 gameState.showGrid = showGridCheckbox.checked; // ゲーム状態の更新 gameState.gameActive = true; // メインゲームループの開始 if (gameState.gameLoopTimeout) { clearTimeout(gameState.gameLoopTimeout); } gameLoop(); } // 餌の配置 function placeFood() { const validPosition = false; let foodPos; // 蛇の体と重ならない位置に餌を配置 while (!validPosition) { foodPos = { x: Math.floor(Math.random() * gameConfig.gridWidth), y: Math.floor(Math.random() * gameConfig.gridHeight) }; // 蛇の体と重なっていないかチェック if (!gameState.snake.some(segment => segment.x === foodPos.x && segment.y === foodPos.y)) { break; } } gameState.food = foodPos; } // ゲームのメインループ function gameLoop() { if (!gameState.gameActive) return; // 蛇の移動 moveSnake(); // 衝突のチェック if (checkCollision()) { gameOver(); return; } // 餌を食べたかチェック checkFood(); // 画面の描画 drawGame(); // 次のフレーム gameState.gameLoopTimeout = setTimeout(gameLoop, gameState.speed); } // 蛇の移動 function moveSnake() { // 次のフレームで向かう方向を現在の方向に設定 gameState.direction = gameState.nextDirection; // 頭の現在位置をコピー const head = { ...gameState.snake[0] }; // 方向に応じて頭の位置を更新 switch (gameState.direction) { case 'up': head.y--; break; case 'down': head.y++; break; case 'left': head.x--; break; case 'right': head.x++; break; } // 新しい頭の位置を追加 gameState.snake.unshift(head); // 餌を食べていない場合、尻尾を取り除く const foodEaten = head.x === gameState.food.x && head.y === gameState.food.y; if (!foodEaten) { gameState.snake.pop(); } } // 衝突のチェック function checkCollision() { const head = gameState.snake[0]; // 壁との衝突チェック if (head.x < 0 || head.x >= gameConfig.gridWidth || head.y < 0 || head.y >= gameConfig.gridHeight) { return true; } // 自分の体との衝突チェック for (let i = 1; i < gameState.snake.length; i++) { if (head.x === gameState.snake[i].x && head.y === gameState.snake[i].y) { return true; } } return false; } // 餌のチェック function checkFood() { const head = gameState.snake[0]; // 頭が餌の位置と一致したら if (head.x === gameState.food.x && head.y === gameState.food.y) { // スコアを増やす gameState.score += 10; scoreElement.textContent = gameState.score; // 新しい餌を配置 placeFood(); } } // ゲームオーバー function gameOver() { gameState.gameActive = false; // ハイスコアの更新 if (gameState.score > gameState.highScore) { gameState.highScore = gameState.score; localStorage.setItem('snakeHighScore', gameState.highScore); highScoreElement.textContent = gameState.highScore; } // ゲームオーバー画面の表示 finalScoreElement.textContent = gameState.score; gameOverOverlay.classList.remove('hidden'); } // ゲームの描画 function drawGame() { // キャンバスをクリア ctx.clearRect(0, 0, canvas.width, canvas.height); // グリッドの描画(オプション) if (gameState.showGrid) { drawGrid(); } // 蛇の描画 drawSnake(); // 餌の描画 drawFood(); } // グリッドの描画 function drawGrid() { ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)'; for (let x = 0; x <= canvas.width; x += gameConfig.gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, canvas.height); ctx.stroke(); } for (let y = 0; y <= canvas.height; y += gameConfig.gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(canvas.width, y); ctx.stroke(); } } // 蛇の描画 function drawSnake() { ctx.fillStyle = '#2ecc71'; for (const segment of gameState.snake) { ctx.fillRect( segment.x * gameConfig.gridSize, segment.y * gameConfig.gridSize, gameConfig.gridSize, gameConfig.gridSize ); } } // 餌の描画 function drawFood() { ctx.fillStyle = '#e74c3c'; ctx.beginPath(); const x = gameState.food.x * gameConfig.gridSize + gameConfig.gridSize / 2; const y = gameState.food.y * gameConfig.gridSize + gameConfig.gridSize / 2; const radius = gameConfig.gridSize / 2 - 2; ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); } // キーボード操作 document.addEventListener('keydown', e => { const key = e.key.toLowerCase(); const newDir = { 'arrowup': 'up', 'w': 'up', 'arrowdown': 'down', 's': 'down', 'arrowleft': 'left', 'a': 'left', 'arrowright': 'right', 'd': 'right' } [key]; if (newDir && isValidDirection(newDir)) { gameState.nextDirection = newDir; } }); // 有効な方向かどうか(逆走防止) function isValidDirection(newDir) { const opposite = { 'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left' }; return gameState.direction !== opposite[newDir]; } // モバイルボタン操作 upButton.addEventListener('click', () => updateDirection('up')); downButton.addEventListener('click', () => updateDirection('down')); leftButton.addEventListener('click', () => updateDirection('left')); rightButton.addEventListener('click', () => updateDirection('right')); function updateDirection(dir) { if (isValidDirection(dir)) { gameState.nextDirection = dir; } } // スタートボタンと再スタートボタンのイベント startButton.addEventListener('click', () => { overlay.classList.add('hidden'); initGame(); }); restartButton.addEventListener('click', () => { gameOverOverlay.classList.add('hidden'); initGame(); }); document.addEventListener('keydown', function(e) { if (!gameState.gameActive) return; const keysToPrevent = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'a', 's', 'd', 'W', 'A', 'S', 'D']; if (keysToPrevent.includes(e.key)) { e.preventDefault(); } }, { passive: false }); });
記事URLをコピーしました