
【ゲーム】JavaScript:52 倉庫番
「📦倉庫番(PushBox)」ゲームは、荷物(箱)を押してゴール(🎯)に運ぶパズルゲームです。プレイヤー(🧑🔧)は箱を押して移動できますが、「箱は引けない・複数同時に押せない」という制約があります。シンプルなルールながら頭を使う奥深いロジックパズルです。
遊び方と操作方法
- 移動操作:キーボードの矢印キー(↑↓←→)またはWASDキーでプレイヤーを動かします。
- 箱を押す:箱の隣に移動し、箱の方向に進むと箱を1マスだけ押せます。
- リタイア:画面右上の「リタイア」ボタンでタイトル画面に戻れます。
- クリア条件:すべての箱が緑色のゴール(🎯)に乗るとクリア!
ルール
- 箱(📦)を押してゴール(🎯)へ運ぶ。
- 1回に1つだけ箱を押せる。
- 箱は引けません。押すだけ。
- 箱をゴールに運ぶと色が変化します。
- すべての箱がゴール上に乗ればクリアです。
🎮ゲームプレイ
以下のリンク先から実際にプレイできます。
52 倉庫番
素材のダウンロード
以下のリンクから使用する素材をダウンロードできます。
warehouse-keeper_title.png | warehouse-keeper_bg.png |
---|---|
![]() | ![]() |
ゲーム画面イメージ

プログラム全文(warehouse-keeper.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>📦 倉庫番(PushBox)ゲーム 📦</title>
<style>
html, body {
width: 100vw; height: 100vh;
margin: 0; padding: 0;
background: url('warehouse-keeper_bg.png') no-repeat center center fixed;
background-size: cover;
font-family: 'Yu Gothic', 'Meiryo', sans-serif;
overflow: auto;
}
body {
display: flex; justify-content: center; align-items: center;
min-height: 100vh;
}
#container {
width: 800px;
background: rgba(30, 32, 40, 0.95);
border-radius: 18px;
box-shadow: 0 0 16px 4px #222a;
padding: 32px 0 32px 0;
margin: 20px 0;
position: relative;
z-index: 10;
}
.center { text-align: center; }
.title-img { display: block; margin: 0 auto 18px auto; max-width:600px; max-height:180px; width:auto; height:auto; }
.button {
display: inline-block;
padding: 10px 30px;
background: #f6c94d;
color: #322; font-size: 1.25rem; font-weight: bold;
border: none; border-radius: 8px;
box-shadow: 0 2px 8px #1118;
cursor: pointer;
margin: 12px auto 0 auto;
transition: filter 0.2s;
}
.button:hover { filter: brightness(1.1); }
.main-title {
font-size: 2.1rem;
font-weight: bold;
color: #ffe46c;
letter-spacing: .1em;
margin-bottom: 12px;
text-shadow: 0 2px 8px #322;
}
.rule-title {
font-size: 1.4rem;
font-weight: bold;
color: #fafafa;
margin: 32px 0 8px 0;
text-align: center;
text-shadow: 0 2px 8px #222;
}
.rule-desc {
background: rgba(60,60,60,0.8);
border-radius: 8px;
padding: 10px 32px;
font-size: 1.08rem;
color: #fff;
text-align: left;
margin: 0 auto 24px auto;
line-height: 1.8;
width: 600px;
text-shadow: 0 1px 4px #000b;
}
.msg-box {
background: rgba(40, 60, 180, 0.85);
color: #fff;
border-radius: 8px;
font-size: 1.5rem;
font-weight: bold;
padding: 24px 0;
text-align: center;
margin: 24px auto 0 auto;
width: 480px;
box-shadow: 0 0 16px #2228;
text-shadow: 0 2px 8px #000;
z-index: 100;
}
#game-board {
display: grid;
grid-template-rows: repeat(9, 42px); /* 9行 */
grid-template-columns: repeat(14, 42px); /* 14列 */
justify-content: center;
background: #252834dd;
padding: 14px;
margin: 0 auto 16px auto;
border-radius: 12px;
box-shadow: 0 4px 24px #000b;
width: min-content;
}
.cell {
width: 38px; height: 38px;
display: flex; align-items: center; justify-content: center;
font-size: 1.6rem;
border-radius: 6px;
background: #3e4356;
border: 1px solid #626478;
box-shadow: 0 1px 4px #0004;
transition: background 0.1s;
}
.wall { background: #888a9a; }
.goal { background: #3da65c; }
.box { background: #b58444; }
.box.goal { background: #e2d90c; color: #715b00; }
.player { background: #5688db; }
#retire-btn-area {
position: absolute;
top: 30px;
right: 32px;
z-index: 11;
}
#retire-btn-area .button {
padding: 6px 20px;
font-size: 1.07rem;
margin: 0;
}
#stage-select-area {
display: flex; gap: 24px; justify-content: center; margin: 14px auto 0 auto;
}
.stage-btn {
background: #82dc7e; color: #232; font-weight: bold;
border-radius: 8px; padding: 8px 28px;
border: 2px solid #6c6;
font-size: 1.12rem;
margin-top: 0; margin-bottom: 0;
cursor: pointer;
box-shadow: 0 2px 8px #1116;
transition: filter 0.2s;
}
.stage-btn:hover { filter: brightness(1.13); }
#end-buttons {
display: flex; justify-content: center; margin-top: 20px;
}
@media (max-width: 960px) {
#container { width: 99vw; min-width: 0; }
#game-board { max-width: 96vw; overflow-x: auto; }
.rule-desc { width: 94vw;}
}
</style>
</head>
<body>
<div id="container"></div>
<script>
// ==== 14×9マスでクリアできる練習マップ3面 ====
// 0:床 1:壁 2:箱 3:ゴール 4:プレイヤー 5:箱+ゴール 6:プレイヤー+ゴール
// --- ステージ1: 箱1個 ---
const STAGE_LIST = [
[
[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,3,1],
[1,0,1,1,1,1,1,1,1,1,1,1,0,1],
[1,0,0,0,0,0,2,0,0,0,0,0,0,1],
[1,1,1,1,1,1,0,1,1,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,1,1,1,1,1,1,1,1,0,1],
[1,4,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
],
// --- ステージ2: 箱2個 ---
[
[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,3,0,1],
[1,0,1,1,1,0,1,1,1,1,0,1,0,1],
[1,0,2,0,0,0,1,1,1,1,0,2,0,1],
[1,1,1,1,1,0,1,1,1,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,1,3,1],
[1,0,1,1,1,1,1,1,1,1,1,1,0,1],
[1,4,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
],
// --- ステージ3: 箱3個 ---
[
[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,3,0,0,0,0,3,0,0,1],
[1,0,1,1,1,0,1,0,1,1,1,1,0,1],
[1,0,2,0,0,0,0,0,2,0,0,2,0,1],
[1,1,1,1,1,0,1,1,1,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,1,3,1],
[1,0,1,1,1,1,1,1,1,1,1,1,0,1],
[1,4,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1],
]
];
// ==== グローバル変数 ====
let stageIndex = 0;
let board;
let playerPos;
let gameState = "title";
const container = document.getElementById('container');
// ==== 盤面初期化 ====
function initBoard(idx) {
board = STAGE_LIST[idx].map(row => [...row]);
playerPos = null;
for (let y = 0; y < board.length; y++) {
for (let x = 0; x < board[0].length; x++) {
if (board[y][x] === 4 || board[y][x] === 6) {
playerPos = {x, y};
}
}
}
// 万一プレイヤーいなければ、床に設置
if (!playerPos) {
for (let y = 0; y < board.length; y++) {
for (let x = 0; x < board[0].length; x++) {
if (board[y][x] === 0) {
board[y][x] = 4;
playerPos = {x, y};
return;
}
}
}
}
}
// ==== タイトル画面 ====
function showTitle() {
gameState = "title";
let html = `
<div class="main-title center">📦 倉庫番(PushBox)ゲーム 📦</div>
<img src="warehouse-keeper_title.png" class="title-img">
<div class="rule-title">📝 ゲームのルール 📝</div>
<div class="rule-desc">
・荷物(箱)をすべて緑のゴールへ<strong>押して</strong>運ぶパズルゲームです。<br>
・荷物は<strong>1つずつ</strong>しか押せません。<br>
・荷物を<strong>引く</strong>ことはできません。<br>
・荷物がゴールに載ると色が変わります。<br>
・すべての荷物をゴールへ運べばクリアです。<br>
・キーボードの「↑↓←→」または「WASD」キーで操作します。<br>
<span style="color:#e2d90c">頭を使ってすべての荷物をゴールへ運びましょう!</span>
</div>
<div id="stage-select-area">
<button class="stage-btn" onclick="startGame(0)">ステージ 1</button>
<button class="stage-btn" onclick="startGame(1)">ステージ 2</button>
<button class="stage-btn" onclick="startGame(2)">ステージ 3</button>
</div>
`;
container.innerHTML = html;
}
// ==== ゲーム画面描画 ====
function renderGame() {
let html = `<div class="main-title center" style="margin-bottom:12px;">📦 倉庫番 - ステージ${stageIndex + 1} 📦</div>`;
// リタイアボタン(右上固定)
html += `<div id="retire-btn-area"><button class="button" onclick="showTitle()">リタイア</button></div>`;
// 盤面
html += `<div id="game-board">`;
for (let y = 0; y < board.length; y++) {
for (let x = 0; x < board[0].length; x++) {
let cellClass = "cell";
let symbol = "";
switch (board[y][x]) {
case 1: cellClass += " wall"; symbol = "🧱"; break;
case 0: cellClass += ""; symbol = ""; break;
case 2: cellClass += " box"; symbol = "📦"; break;
case 3: cellClass += " goal"; symbol = "🎯"; break;
case 4: cellClass += " player"; symbol = "🧑🔧"; break;
case 5: cellClass += " box goal"; symbol = "📦"; break;
case 6: cellClass += " player goal"; symbol = "🧑🔧"; break;
}
html += `<div class="${cellClass}">${symbol}</div>`;
}
}
html += `</div>`;
// 操作説明
html += `
<div class="center" style="margin-top:8px;">
<span style="background:rgba(0,0,0,0.6);color:#ffe46c;padding:6px 18px;border-radius:6px;font-size:1.1rem;">
「↑↓←→」または「WASD」キーで移動。荷物は押すだけでOK!
</span>
</div>
`;
container.innerHTML = html;
}
// ==== ゲーム開始 ====
function startGame(idx) {
stageIndex = idx;
initBoard(stageIndex);
gameState = "play";
renderGame();
}
// ==== ゲームクリア判定 ====
function isClear() {
for (let y = 0; y < board.length; y++) {
for (let x = 0; x < board[0].length; x++) {
if (board[y][x] === 2) return false; // 箱が残っていれば未クリア
if (board[y][x] === 3) return false; // ゴール上に箱がないなら未クリア
}
}
return true;
}
// ==== プレイヤーの移動処理 ====
function move(dx, dy) {
if (gameState !== "play") return;
const {x, y} = playerPos;
const nx = x + dx, ny = y + dy;
const nnx = x + dx * 2, nny = y + dy * 2;
// 範囲外
if (ny < 0 || ny >= board.length || nx < 0 || nx >= board[0].length) return;
const dest = board[ny][nx];
// 壁やプレイヤー禁止
if (dest === 1 || dest === 4 || dest === 6) return;
// 箱または箱+ゴールを押す場合
if (dest === 2 || dest === 5) {
// その1マス先が床orゴールなら押せる
if (nny < 0 || nny >= board.length || nnx < 0 || nnx >= board[0].length) return;
const beyond = board[nny][nnx];
if (beyond === 0 || beyond === 3) {
board[nny][nnx] = (beyond === 0) ? 2 : 5;
board[ny][nx] = (dest === 5) ? 3 : 0;
} else {
return;
}
}
// プレイヤー移動
if (board[y][x] === 4) board[y][x] = 0;
if (board[y][x] === 6) board[y][x] = 3;
if (board[ny][nx] === 3) board[ny][nx] = 6;
else board[ny][nx] = 4;
playerPos = {x: nx, y: ny};
renderGame();
// クリア判定
if (isClear()) {
setTimeout(showClear, 300);
}
}
// ==== クリア画面 ====
function showClear() {
gameState = "clear";
let html = `
<div class="main-title center" style="margin-bottom:14px;">🎉 ステージクリア! 🎉</div>
<div class="msg-box" style="background:rgba(60,180,80,0.95);">
全ての荷物をゴールに運びました!<br>おめでとうございます!
</div>
<div id="end-buttons">
<button class="button" onclick="showTitle()">タイトル画面に戻る</button>
</div>
`;
container.innerHTML = html;
}
// ==== キー操作(矢印&wasd対応) ====
document.addEventListener("keydown", function(e) {
if (gameState !== "play") return;
switch (e.key) {
case "ArrowUp": case "w": case "W": move(0, -1); break;
case "ArrowDown": case "s": case "S": move(0, 1); break;
case "ArrowLeft": case "a": case "A": move(-1, 0); break;
case "ArrowRight": case "d": case "D": move(1, 0); break;
}
});
// ==== 初期表示 ====
showTitle();
// ==== ウィンドウリサイズ対応 ====
window.addEventListener('resize', () => {
container.style.minHeight = (window.innerHeight - 40) + 'px';
});
</script>
</body>
</html>
アルゴリズムの流れ
ステップ | 内容 |
---|---|
1. 初期表示 | タイトル画面を表示(ルール説明・画像・ステージ選択ボタン) |
2. ゲーム開始 | 選択されたステージの盤面を初期化 |
3. ゲーム画面 | 盤面描画・リタイアボタン描画・操作説明 |
4. 入力処理 | 矢印キー/WASDキーによるプレイヤーの移動・箱の押し出し処理 |
5. 描画更新 | プレイヤーや箱の位置、盤面の状態を再描画 |
6. クリア判定 | すべての箱がゴール上にあるかチェック |
7. ゲームクリア | クリア画面を表示(タイトルに戻るボタン) |
主要関数・命令の解説
関数名・変数 | 概要 |
---|---|
showTitle() | タイトル画面、ルール、画像、ステージ選択を表示 |
startGame(idx) | 選んだステージを初期化し、プレイ画面に遷移 |
initBoard(idx) | 盤面配列・プレイヤー座標を初期化 |
renderGame() | 盤面の状態をHTMLで再描画 |
move(dx, dy) | プレイヤーと箱の移動処理(方向指定) |
isClear() | クリア判定:すべての箱がゴール上ならtrue |
showClear() | クリア画面を描画 |
document.addEventListener("keydown", ...) | キーボード操作のイベント監視 |
関数の処理内容
関数名 | 主な処理内容 |
---|---|
showTitle | タイトル・画像・ルール・ステージ選択を描画 |
startGame | 盤面初期化・プレイ画面描画 |
initBoard | 盤面配列(2次元配列)の初期化・プレイヤー座標特定 |
renderGame | 盤面上のマス状態ごとにHTMLを生成・描画 |
move | プレイヤーの移動・箱を押す処理(衝突判定・盤面配列の状態更新・再描画) |
isClear | 全ての箱(2,5)がゴール上(5)のみならtrue |
showClear | クリア時のメッセージ・ボタン描画 |
改造のポイント
ステージ追加・難易度調整
STAGE_LIST
配列に新しい盤面を追加するだけでステージ追加が可能。- マスサイズや盤面サイズも調整できます。
操作性の向上
- Undo(1手戻し)機能やリトライボタンを追加すれば快適性UP。
- プレイヤー・箱のアニメーション追加で演出強化。
見た目・演出の工夫
- 箱やゴール、壁などを画像アイコンでリッチにカスタマイズ。
- 効果音やBGMを加えるとレトロな雰囲気を楽しめます。
アドバイス
本プログラムはシンプルな構造なので、JavaScript初心者でもカスタマイズしやすいのが特徴です。盤面データ(2次元配列)を編集してオリジナルステージを作るのがおすすめです。
Undo機能や手数カウントなどパズルゲームらしい機能追加にも挑戦してみてください!
自分だけの“倉庫番”パズルを作って、みんなで遊ぼう!