
【ゲーム】JavaScript:71 タワースタッカー
「🏗️🧱 タワースタッカー」は、動くブロックをタイミング良く落として、タワーを高く積み上げていくアクションパズルゲームです。積み上げたブロックの重なった面積がスコアとなり、20段積み上げるとゲームクリアとなります。操作はシンプルですが、繰り返すほどに難易度が上昇し、反射神経と集中力が試されます。
遊び方・操作方法
- スタート:「スタート」ボタンをクリックでゲーム開始
- 操作方法:動いているブロックのタイミングを見てゲーム画面をクリックまたはタップすると、その位置にブロックが落下し、積み上げます。
- 繰り返し:上手く積み重ねるごとに次のブロックの幅が決まります(はみ出した部分は切り落とされ、どんどん幅が狭くなります)。
- クリア条件:20段積み上げるとゲームクリア。積み上げたブロックの総面積(小数点以下切り捨て)がスコアです。
- ゲームオーバー:ブロックが一切重ならなかった場合は、その時点でゲームオーバーです。
ルール
- 最初の土台の上に、左右に動くブロックを落として重ねていく。
- 落としたブロックは下の段と重なった部分だけが次の土台となる。
- 重なりが無いと即ゲームオーバー。
- 20段積むとクリア!積み上げたブロックの「総面積」がスコアとなります。
- クリア・ゲームオーバー問わず、スコアは**ランキング(TOP5)**に登録されます。
🎮ゲームプレイ
以下のリンク先から実際にプレイできます。
71 タワースタッカー
素材のダウンロード
以下のリンクから使用する素材をダウンロードできます。
tower_stacker_title.png | tower_stacker_bg.png |
---|---|
![]() | ![]() |
ゲーム画面イメージ

プログラム全体(tower_stacker.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>🏗️🧱 タワースタッカー 🧱🏗️</title>
<style>
html, body {
width: 100%; height: 100%; margin: 0; padding: 0;
background: url('tower_stacker_bg.png') no-repeat center center fixed;
background-size: cover;
font-family: 'Yu Gothic', 'Meiryo', sans-serif;
overflow: auto;
}
#wrapper {
width: 800px;
height: calc(100% - 20px);
margin: 20px auto 0 auto;
position: relative;
box-sizing: border-box;
}
.screen {
display: none; width: 100%; height: 100%; box-sizing: border-box;
}
#titleScreen {
display: block; text-align: center; color: #fff; padding-top: 20px;
}
#titleScreen img {
display: block; margin: 0 auto 20px auto; max-width: 70%; height: auto;
}
.rule-panel {
background: rgba(0,0,0,0.6); padding: 20px; border-radius: 10px;
margin: 0 auto; max-width: 90%;
}
.rule-panel h2 { margin-top: 0; text-align: center; }
.rule-panel p { text-align: left; margin: 0 0 10px 0; line-height: 1.6; }
.btn {
display: inline-block; padding: 12px 24px; margin-top: 20px; font-size: 1.1rem;
font-weight: bold; color: #fff; background: #1e90ff; border: none;
border-radius: 8px; cursor: pointer; transition: opacity .2s;
}
.btn:hover { opacity: 0.8; }
#gameScreen { position: relative; color: #fff; padding-top: 20px; }
#gameCanvas {
display: block; margin: 0 auto; background: #111; border: 4px solid #fff;
border-radius: 4px; width: 800px; height: 680px;
}
#scoreBoard {
position: absolute; top: 30px; left: 50%; transform: translateX(-50%);
background: rgba(0,0,0,0.6); padding: 6px 12px; border-radius: 6px;
font-size: 1.1rem; z-index: 5;
}
#resultScreen { text-align: center; color: #fff; padding-top: 20px; }
#resultScreen .message { font-size: 2rem; font-weight: bold; margin-bottom: 20px; }
#rankingBoard {
background: rgba(0,0,0,0.6); display: inline-block; padding: 10px 20px;
border-radius: 10px; text-align: left;
}
#rankingBoard h3 { margin: 0 0 10px 0; text-align: center; }
#rankingBoard ol { margin: 0; padding-left: 20px; }
</style>
</head>
<body>
<div id="wrapper">
<!-- ==================== タイトル画面 ==================== -->
<div id="titleScreen" class="screen">
<img src="tower_stacker_title.png" alt="ゲームタイトル">
<h1>🏗️🧱 <span style="background:rgba(0,0,0,0.6); padding:6px 12px; border-radius:8px;">タワースタッカー</span> 🧱🏗️</h1>
<div class="rule-panel">
<h2>📜 ルール説明 📜</h2>
<p>・ブロックが左右に移動します。クリックやタップで落としてタワーを積み上げましょう。</p>
<p>・はみ出した部分は切り落とされ、次のブロックの幅が狭くなります。</p>
<p>・ブロックが重ならないとゲームオーバーです。</p>
<p>・<strong>20 段</strong> 積み上げるとゲームクリア!<br>
積み上げた<strong>ブロック総面積 (小数点以下切り捨て)</strong>がスコアになります。</p>
</div>
<button id="startBtn" class="btn">▶️ スタート</button>
</div>
<!-- ==================== ゲーム画面 ==================== -->
<div id="gameScreen" class="screen">
<div id="scoreBoard">
高さ: <span id="height">0</span> 段 | 面積: <span id="area">0</span>
</div>
<canvas id="gameCanvas" width="800" height="680"></canvas>
</div>
<!-- ==================== 結果画面 ==================== -->
<div id="resultScreen" class="screen">
<div class="message" id="resultMessage"></div>
<div id="rankingBoard">
<h3>🏆 ランキング TOP 5 🏆</h3>
<ol id="rankingList"></ol>
</div><br>
<button id="returnBtn" class="btn">🔙 タイトル画面に戻る</button>
</div>
</div>
<script>
(() => {
/* ===== 定数 ===== */
const BLOCK_H = 30; // ブロックの高さ
const INIT_W = 300; // 最初のブロックの幅
const START_SPEED= 2.5; // 最初のスピード
const CLEAR_LV = 20; // クリアとなる段数
const LS_KEY = 'ts_scores'; // ローカルストレージ用キー
const RANK_MAX = 5; // ランキング最大数
/* ===== DOM取得 ===== */
const $ = id => document.getElementById(id);
const title = $('titleScreen');
const game = $('gameScreen');
const result = $('resultScreen');
const startBtn= $('startBtn');
const backBtn = $('returnBtn');
const hSpan = $('height');
const aSpan = $('area');
const resMsg = $('resultMessage');
const rankList= $('rankingList');
const cvs = $('gameCanvas');
const ctx = cvs.getContext('2d');
/* ===== ゲーム用変数 ===== */
let blocks = []; // 配置された全ブロック
let moveBlk; // 現在移動中のブロック
let speed; // 現在の移動スピード
let area; // 積み上げた総面積
let run = false; // ゲームループ実行フラグ
let raf; // requestAnimationFrame ID
/* ===== ユーティリティ ===== */
// ブロック生成
const mkBlk = (x, y, w, h, d = 1) => ({ x, y, w, h, d });
const floor = Math.floor;
/* ===== ゲーム初期化 ===== */
function init() {
blocks.length = 0;
// 一番下の土台ブロック
const baseX = (cvs.width - INIT_W) / 2;
const baseY = cvs.height - BLOCK_H;
blocks.push(mkBlk(baseX, baseY, INIT_W, BLOCK_H, 0));
// 最初の移動ブロック
moveBlk = mkBlk(0, baseY - BLOCK_H, INIT_W, BLOCK_H, 1);
speed = START_SPEED;
area = floor(INIT_W * BLOCK_H);
updHUD();
run = true;
cancelAnimationFrame(raf);
raf = requestAnimationFrame(loop);
}
/* ===== HUD(スコア等の表示)更新 ===== */
function updHUD() {
hSpan.textContent = blocks.length - 1;
aSpan.textContent = area;
}
/* ===== メインループ ===== */
function loop() {
upd();
draw();
if (run) raf = requestAnimationFrame(loop);
}
// ブロックの移動処理
function upd() {
moveBlk.x += moveBlk.d * speed;
// 端で反転
if (moveBlk.x <= 0) {
moveBlk.x = 0;
moveBlk.d = 1;
} else if (moveBlk.x + moveBlk.w >= cvs.width) {
moveBlk.x = cvs.width - moveBlk.w;
moveBlk.d = -1;
}
}
// ブロック描画
function draw() {
ctx.clearRect(0, 0, cvs.width, cvs.height);
ctx.fillStyle = '#2ecc71'; // 積み上がったブロック
blocks.forEach(b => ctx.fillRect(b.x, b.y, b.w, b.h));
ctx.fillStyle = '#e67e22'; // 現在の移動ブロック
ctx.fillRect(moveBlk.x, moveBlk.y, moveBlk.w, moveBlk.h);
}
/* ===== ブロック配置(クリック時) ===== */
function place() {
const top = blocks[blocks.length - 1];
// オーバーラップ判定
const oS = Math.max(top.x, moveBlk.x);
const oE = Math.min(top.x + top.w, moveBlk.x + moveBlk.w);
const oW = oE - oS;
if (oW <= 0) { // 全く重ならなかったらゲームオーバー
finish(false);
return;
}
// 新しいブロック配置
moveBlk.x = oS;
moveBlk.w = oW;
blocks.push({ ...moveBlk, d: 0 });
area = floor(area + oW * BLOCK_H);
updHUD();
// クリア判定
if (blocks.length - 1 >= CLEAR_LV) {
finish(true);
return;
}
// 次の移動ブロック準備
moveBlk = mkBlk(0, moveBlk.y - BLOCK_H, oW, BLOCK_H, 1);
speed += 0.1;
}
/* ===== ゲーム終了 ===== */
function finish(clear) {
run = false;
cancelAnimationFrame(raf);
const txt = clear ? '🎉 ゲームクリア! 🎉' : '🏁 ゲームオーバー! 🏁';
resMsg.innerHTML = `${txt}<br>高さ: <strong>${blocks.length - 1}</strong> 段<br>面積スコア: <strong>${area}</strong>`;
saveRank(area);
showRank();
game.style.display = 'none';
result.style.display = 'block';
}
/* ===== ランキング処理 ===== */
// ランキングデータ取得
const loadRank = () => {
try {
const d = JSON.parse(localStorage.getItem(LS_KEY));
return Array.isArray(d) ? d : [];
} catch {
return [];
}
};
// ランキングデータ保存
const saveRankArr = a => localStorage.setItem(LS_KEY, JSON.stringify(a));
// スコア登録
function saveRank(score) {
const r = loadRank();
r.push(score);
r.sort((a, b) => b - a);
if (r.length > RANK_MAX) r.length = RANK_MAX;
saveRankArr(r);
}
// ランキング表示
function showRank() {
const r = loadRank();
rankList.innerHTML = '';
if (!r.length) {
rankList.innerHTML = '<li>ランキングなし</li>';
return;
}
r.forEach((s, i) => {
const li = document.createElement('li');
li.textContent = `${i + 1} 位 : ${s} 点`;
rankList.appendChild(li);
});
}
/* ===== イベントバインド ===== */
startBtn.onclick = () => {
title.style.display = 'none';
result.style.display = 'none';
game.style.display = 'block';
init();
};
backBtn.onclick = () => {
result.style.display = 'none';
title.style.display = 'block';
};
cvs.onclick = () => {
if (run) place();
};
})();
</script>
</body>
</html>
アルゴリズムの流れ
ステップ | 内容 |
---|---|
1 | タイトル画面・ルール説明を表示 |
2 | 「スタート」ボタンでゲーム開始 |
3 | 一番下に土台ブロックを生成 |
4 | その上に左右に動くブロックを生成 |
5 | 画面クリック/タップでブロックを落とし、下段と重なった部分だけ残す |
6 | 面積・段数を表示しながら繰り返す |
7 | 重なりがゼロになったらゲームオーバー |
8 | 20段積んだらゲームクリア |
9 | リザルト画面+ランキング表示 |
10 | タイトル画面に戻ってリトライ可能 |
主要な関数・命令の解説
関数・変数名 | 役割・内容 |
---|---|
init() | ゲーム状態初期化、土台と最初のブロック生成、面積計算リセット |
loop() | メインループ。ブロックの移動・描画・ゲーム進行 |
upd() | 動いているブロックの左右移動&端での反転処理 |
draw() | キャンバスに積み上げたブロック・移動ブロックを描画 |
place() | クリック時にブロックを落とし、重なった部分のみを残す。面積更新、クリア/ゲームオーバー判定 |
finish(clear) | ゲーム終了処理(クリア/ゲームオーバーのメッセージ、ランキング登録・表示) |
updHUD() | 段数と面積スコアの表示を更新 |
mkBlk() | ブロック生成用のユーティリティ |
loadRank() | ランキングデータをlocalStorageから読み出し |
saveRank(score) | 現在のスコアをランキングに追加・保存(上位5件まで) |
showRank() | 結果画面にランキングリストを描画 |
関数の詳細
関数名 | 主な役割 |
---|---|
init | ゲーム状態リセット・土台と移動ブロック初期化・面積リセット・ループ開始 |
loop | ゲーム進行ループ(ブロック移動・描画・進行管理) |
upd | ブロックの位置を更新、端で移動方向を反転 |
draw | ブロックをCanvasに描画 |
place | ブロック落下・重なり計算・面積更新・次の段用ブロック生成・クリア/終了判定 |
finish | ゲーム終了画面遷移・スコア表示・ランキング登録 |
loadRank | ランキングデータ取得 |
saveRank | スコアのランキング保存 |
showRank | 結果画面にランキングを表示 |
改造のポイント
◆ 難易度調整・アレンジ
- ブロックの速度や初期幅・高さを調整して難易度を変更可能。
- クリア段数や面積計算方法を工夫しても面白い。
- ランキング保存数を増やしたり、プレイヤー名入力機能も追加可能。
◆ ビジュアル・演出の強化
- ブロックや背景画像を自作素材に変更、アニメーションや効果音追加で見た目のリッチ化。
- ブロックごとに色を変えたり、物理的な落下感・揺れなど演出追加も◎
◆ モバイル対応・操作性UP
- スマホのタッチ操作対応や、レスポンシブデザインの強化。
- ボタン操作・バイブレーション連動などのUI改善。
★アドバイス
積み上げた時の達成感が楽しいゲームです。
処理のシンプルさゆえに「Canvas描画、タイミング、配列操作、ローカルストレージ」など
Webゲーム制作の基礎スキル練習にも最適!
自分だけの演出やギミックを追加して、世界に一つのタワースタッカーに進化させましょう!
あなたも高く積み上げて、ハイスコアとクリアを目指してください!