へびゲームは、エサを食べてどんどん長くなるへびを操作するクラシックなゲームです。このチュートリアルでは、JavaScriptでへびゲームを作る方法を学びましょう。配列をキュー(先頭追加・末尾削除)として使うテクニック、グリッドベースの移動、壁や自分自身との衝突判定など、ゲームプログラミングの重要な基礎が身につきます。
へびゲームはグリッド(マス目)上で動きます。へびの体は座標の配列で表し、配列の先頭が「頭」です。方向は{x, y}のオブジェクトで管理すると、移動の計算がとてもシンプルになります。
var GRID_SIZE = 20;
var INITIAL_SNAKE_LENGTH = 3;
var DIR = {
UP: { x: 0, y: -1 },
DOWN: { x: 0, y: 1 },
LEFT: { x: -1, y: 0 },
RIGHT: { x: 1, y: 0 }
};
var snake = [];
var direction = DIR.RIGHT;
var nextDirection = DIR.RIGHT;
function initSnake() {
snake = [];
var startX = Math.floor(GRID_SIZE / 2);
var startY = Math.floor(GRID_SIZE / 2);
for (var i = 0; i < INITIAL_SNAKE_LENGTH; i++) {
snake.push({ x: startX - i, y: startY });
}
direction = DIR.RIGHT;
nextDirection = DIR.RIGHT;
}
DIRオブジェクトで4方向を定義しています。nextDirectionとdirectionを分けているのは、1フレーム中に複数回キーを押したときに方向が2回変わるのを防ぐためです。へびの初期位置はグリッドの中央に置き、右方向に3マス分の体を作っています。
へびの移動は「頭の前に新しいマスを追加し、尻尾のマスを削除する」というキュー操作で実現します。エサを食べたときは尻尾を削除しないことで、へびが1マス長くなります。
function gameStep() {
direction = nextDirection;
// 新しい頭の位置を計算
var head = snake[0];
var newHead = {
x: head.x + direction.x,
y: head.y + direction.y
};
// 壁との衝突判定
if (newHead.x < 0 || newHead.x >= GRID_SIZE ||
newHead.y < 0 || newHead.y >= GRID_SIZE) {
gameOver();
return;
}
// 自分自身との衝突判定
for (var i = 0; i < snake.length - 1; i++) {
if (snake[i].x === newHead.x && snake[i].y === newHead.y) {
gameOver();
return;
}
}
// 頭を追加
snake.unshift(newHead);
// エサを食べたかチェック
if (food && newHead.x === food.x && newHead.y === food.y) {
score += 1;
spawnFood();
} else {
// 食べなかった → 尻尾を削除
snake.pop();
}
}
unshiftで配列の先頭に新しい頭を追加し、popで末尾(尻尾)を削除します。エサを食べたときはpopしないので、へびが1マス伸びるわけです。このシンプルなキュー操作が、へびの「頭が進んで尻尾が追いかける」動きを自然に表現しています。
へびゲームでは、現在の進行方向と逆方向に転換できないルールがあります。右に進んでいるときに左を押しても無視する、という処理を実装しましょう。
function changeDirection(newDir) {
// 逆方向のチェック
if (direction.x + newDir.x === 0 &&
direction.y + newDir.y === 0) {
return; // 逆方向は無視
}
nextDirection = newDir;
}
function spawnFood() {
var occupied = {};
for (var i = 0; i < snake.length; i++) {
occupied[snake[i].x + ',' + snake[i].y] = true;
}
var emptyCells = [];
for (var y = 0; y < GRID_SIZE; y++) {
for (var x = 0; x < GRID_SIZE; x++) {
if (!occupied[x + ',' + y]) {
emptyCells.push({ x: x, y: y });
}
}
}
food = emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
逆方向の判定はdirection.x + newDir.x === 0で行えます。右(1,0)と左(-1,0)を足すと(0,0)になるので逆方向とわかります。エサの配置では、へびが占めているマスを除外して空きマスだけをリストアップし、その中からランダムに選んでいます。へびが全マスを埋めたら勝利です。
このチュートリアルでは、キュー操作によるへびの移動、方向転換と逆走防止、壁と自己衝突の判定の3つを学びました。さらに発展させるなら、壁をすり抜けるワープモード、特殊なエサ(ボーナスポイントや一時的なスピードアップ)、へびの見た目のカスタマイズ機能に挑戦してみましょう。スコアに応じてスピードが上がる仕組みも良い練習になりますよ。
A: はい、大丈夫です。このチュートリアルではステップごとにコードを書いていくので、初めての方でも順番に進めれば完成できます。わからないところがあれば、ひなテックの教室で質問もできますよ。
A: 基本部分は約30分〜1時間で作れます。見た目をこだわったり機能を追加すると、さらに楽しく発展させられます。