<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>かわいいスネークゲーム</title>
<style>
html, body {
margin: 0; padding: 0;
background: #ffeef2; /* かわいいピンク系背景 */
font-family: sans-serif;
user-select: none;
overflow: hidden;
}
#gameContainer {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
}
#gameCanvas {
background: #fffafc; /* 少し明るい背景色 */
border: 10px solid #ffcce0;
border-radius: 20px;
box-shadow: 0px 0px 20px rgba(255,0,100,0.3);
margin-top: 20px;
}
#info {
color: #ff66a3;
font-weight: bold;
margin-top: 10px;
text-align: center;
}
#score {
position: absolute;
top: 10px;
right: 20px;
color: #ff66a3;
font-size: 24px;
font-weight: bold;
text-shadow: 1px 1px #ffffff;
}
#message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ff66a3;
font-size: 32px;
font-weight: bold;
text-shadow: 1px 1px #ffffff;
pointer-events: none;
text-align: center;
white-space: pre;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div id="score"></div>
<div id="message"></div>
<div id="info">
<p>スペースキーでスタート / ポーズ</p>
<p>操作: W↑ A← S↓ D→</p>
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const messageEl = document.getElementById('message');
// ゲーム設定
const cellSize = 20;
const cols = Math.floor(canvas.width / cellSize);
const rows = Math.floor(canvas.height / cellSize);
// ゲーム状態
let snake = [];
let snakeDir = { x: 0, y: 0 };
let apple = { x: 0, y: 0 };
let score = 0;
let running = false;
let gameLoop = null;
let paused = true;
// 初期化関数
function init() {
score = 0;
snake = [];
// スネーク初期位置を中央付近に
const startX = Math.floor(cols/2);
const startY = Math.floor(rows/2);
for (let i = 0; i < 5; i++) {
snake.push({ x: startX - i, y: startY });
}
// ランダム初期方向
const dirs = [{x:0,y:-1},{x:1,y:0},{x:0,y:1},{x:-1,y:0}];
snakeDir = dirs[Math.floor(Math.random()*dirs.length)];
placeApple();
updateScore();
drawAll();
paused = true;
showMessage("スペースキーでスタート");
}
function placeApple() {
let valid = false;
while (!valid) {
const x = Math.floor(Math.random()*cols);
const y = Math.floor(Math.random()*rows);
if (!snake.some(part => part.x === x && part.y === y)) {
apple.x = x; apple.y = y;
valid = true;
}
}
}
function updateScore() {
scoreEl.textContent = "スコア: " + score;
}
function showMessage(msg) {
messageEl.textContent = msg;
}
function hideMessage() {
messageEl.textContent = '';
}
// キー操作
document.addEventListener('keydown', function(e) {
if (e.code === 'Space') {
if (!running) {
startGame();
} else {
togglePause();
}
}
if (!paused) {
if (e.key === 'w' && snakeDir.y === 0) {
snakeDir = {x:0,y:-1};
} else if (e.key === 's' && snakeDir.y === 0) {
snakeDir = {x:0,y:1};
} else if (e.key === 'a' && snakeDir.x === 0) {
snakeDir = {x:-1,y:0};
} else if (e.key === 'd' && snakeDir.x === 0) {
snakeDir = {x:1,y:0};
}
}
});
function startGame() {
if (running) return;
running = true;
paused = false;
hideMessage();
gameLoop = setInterval(gameTick, 100);
}
function togglePause() {
if (!running) return;
paused = !paused;
if (paused) {
clearInterval(gameLoop);
gameLoop = null;
showMessage("一時停止中\nスペースキーで再開");
} else {
hideMessage();
gameLoop = setInterval(gameTick, 100);
}
}
function gameTick() {
update();
drawAll();
}
function update() {
// 次のヘッド位置
const head = snake[0];
const newHead = {
x: head.x + snakeDir.x,
y: head.y + snakeDir.y
};
// 壁判定(反対側から出るかどうか)
// 通常はゲームオーバーだが、今回はゲームオーバーにする
if (newHead.x < 0 || newHead.y < 0 || newHead.x >= cols || newHead.y >= rows) {
gameOver();
return;
}
// 自分自身に衝突
if (snake.some((part, index) => index !== 0 && part.x === newHead.x && part.y === newHead.y)) {
gameOver();
return;
}
snake.unshift(newHead);
// リンゴを食べたか
if (newHead.x === apple.x && newHead.y === apple.y) {
score++;
updateScore();
placeApple();
} else {
snake.pop();
}
}
function gameOver() {
clearInterval(gameLoop);
gameLoop = null;
running = false;
paused = true;
showMessage("ゲームオーバー\nスコア: " + score + "\nスペースキーで再スタート");
// 再初期化待ち
init();
}
function drawAll() {
ctx.clearRect(0,0,canvas.width,canvas.height);
// 背景グリッド(淡い色で)
ctx.strokeStyle = "#ffe6ee";
for (let i = 0; i < cols; i++) {
ctx.beginPath();
ctx.moveTo(i*cellSize,0);
ctx.lineTo(i*cellSize,canvas.height);
ctx.stroke();
}
for (let j = 0; j < rows; j++) {
ctx.beginPath();
ctx.moveTo(0,j*cellSize);
ctx.lineTo(canvas.width,j*cellSize);
ctx.stroke();
}
// リンゴ
// リンゴは赤い丸に
const appleX = apple.x * cellSize + cellSize/2;
const appleY = apple.y * cellSize + cellSize/2;
ctx.fillStyle = "#ff3366";
ctx.beginPath();
ctx.arc(appleX, appleY, cellSize/2, 0, Math.PI*2);
ctx.fill();
// スネーク
ctx.fillStyle = "#66cc99";
for (let i = 0; i < snake.length; i++) {
const part = snake[i];
const px = part.x * cellSize;
const py = part.y * cellSize;
// 頭は少し濃い色に
if (i === 0) {
ctx.fillStyle = "#55bb88";
} else {
ctx.fillStyle = "#66cc99";
}
ctx.fillRect(px, py, cellSize, cellSize);
// かわいい装飾(目)
if (i === 0) {
ctx.fillStyle = "#000000";
// 頭向きによって目の位置変える
let eyeOffsetX1 = 0, eyeOffsetY1 = 0;
let eyeOffsetX2 = 0, eyeOffsetY2 = 0;
if (snakeDir.x === 1) { // 右
eyeOffsetX1 = 5; eyeOffsetY1 = 5;
eyeOffsetX2 = 5; eyeOffsetY2 = 15;
} else if (snakeDir.x === -1) { // 左
eyeOffsetX1 = 15; eyeOffsetY1 = 5;
eyeOffsetX2 = 15; eyeOffsetY2 = 15;
} else if (snakeDir.y === 1) { // 下
eyeOffsetX1 = 5; eyeOffsetY1 = 5;
eyeOffsetX2 = 15; eyeOffsetY2 = 5;
} else if (snakeDir.y === -1) { // 上
eyeOffsetX1 = 5; eyeOffsetY1 = 15;
eyeOffsetX2 = 15; eyeOffsetY2 = 15;
} else {
// 初期方向不明時のデフォルト
eyeOffsetX1 = 5; eyeOffsetY1 = 5;
eyeOffsetX2 = 15; eyeOffsetY2 = 5;
}
ctx.fillRect(px + eyeOffsetX1, py + eyeOffsetY1, 3, 3);
ctx.fillRect(px + eyeOffsetX2, py + eyeOffsetY2, 3, 3);
}
}
}
init();
})();
</script>
</body>
</html>