マガジン

30分で作るクリッカー:増える数字と買える強化

遊びをつくる

執筆にClaudeを使用しています。

一つのボールと短い足場だけで作られた小さなゲーム模型。
記事のテーマをもとに制作したAI生成イメージ。実際のゲーム画面ではありません。

クリッカーは「ボタンを押すと数字が増える」だけのゲームです。それでも画面を離れにくくなるのは、数字そのものが面白いからではありません。「あと少しで次の強化が買える」という見通しが、いつも手の届く距離にあるからです。この記事では、その見通しを作る最小の構成を30分で組みます。 問いは「増える数字と買える強化だけで、どこまで遊びが成り立つか」です。部品は、所持数、買うたびに高くなる価格、買うと増える毎秒の生産量の3つだけです。画像も音も保存も後回しにして、価格の上がり方と生産量の釣り合いに時間を使います。ここがクリッカーの遊び心地を決めます。 完成後の遊び心地は、読者自身が遊んで確かめる部分です。この記事は、確かめるための記録の取り方と、数字の直し方の判断基準までを手順にします。

この記事の目次
  1. 30分の時間配分と、決める3つの部品
  2. 手順1:動く土台を作る
  3. 手順2:価格と回収秒数を手計算で確かめる
  4. 手順3:自分で遊んで記録し、数値を直す
  5. 起こりやすい失敗
  6. 次の行動

30分の時間配分と、決める3つの部品

30分を次のように配ります。

  • 0〜5分:部品の数値を決めます。
  • 5〜15分:下のコードを写して動かします。
  • 15〜25分:自分で遊んで、数値を直します。
  • 25〜30分:公開用に整えます。

部品は3つです。

  • 所持数:増えていく数字です。名前は「個」でも「光」でも「貝がら」でも構いません。
  • 生産役:買うと毎秒の増加量が上がるものです。今回は3種類(小さなかご、手押し車、小さな畑)を用意します。
  • 価格の式:同じものを買うほど値段が上がる決まりです。今回は「基本価格 × 1.15 の所持個数乗」を切り捨てた値にします。

価格が一定のままだと、いちばん効率のよい強化を押し続けるだけになります。価格を少しずつ上げると、「次も同じものを買うか、別のものに替えるか」という選択が生まれます。

手順1:動く土台を作る

次のコードを index.html という名前で保存し、ブラウザで開いてください。外部ライブラリは使わず、このファイル1つで動きます。

html
<!doctype html>
<meta charset="utf-8">
<title>クリッカー</title>
<h1><span id="n">0</span> 個</h1>
<p>毎秒 <span id="ps">0</span> 個</p>
<button id="tap">集める</button>
<div id="shop"></div>
<script>
const items = [
  {name:"小さなかご", base:15, rate:0.1, own:0},
  {name:"手押し車", base:100, rate:1, own:0},
  {name:"小さな畑", base:1100, rate:8, own:0},
];
let n = 0, power = 1;
const cost = it => Math.floor(it.base * 1.15 ** it.own);
const perSec = () => items.reduce((s, it) => s + it.rate * it.own, 0);
const shop = document.getElementById("shop");
const btns = items.map(it => {
  const b = document.createElement("button");
  b.onclick = () => { if (n >= cost(it)) { n -= cost(it); it.own++; draw(); } };
  shop.append(b);
  return b;
});
document.getElementById("tap").onclick = () => { n += power; draw(); };
function draw() {
  document.getElementById("n").textContent = Math.floor(n);
  document.getElementById("ps").textContent = perSec().toFixed(1);
  items.forEach((it, i) => {
    btns[i].textContent = `${it.name} ×${it.own}(${cost(it)}個・毎秒+${it.rate})`;
    btns[i].disabled = n < cost(it);
  });
}
let last = performance.now();
setInterval(() => {
  const now = performance.now();
  n += perSec() * (now - last) / 1000;
  last = now;
  draw();
}, 100);
draw();
</script>

画面が開いたら、次の4点を確かめます。

  1. 「集める」を押すと、数字が1ずつ増えます。
  2. 15個たまるまで「小さなかご」のボタンが押せず、15個で押せるようになります。
  3. かごを買うと「毎秒 0.1 個」と表示されます。
  4. かごの価格表示が、買うたびに少しずつ上がります。

どれかが違う場合は、保存時の文字コードがUTF-8になっているか、コードを途中で切って写していないかを見直してください。

なお、時間の進みは「前回からの経過ミリ秒」で計算しています。タイマーが一定間隔で動くことに頼らないので、別のタブに切り替えて処理が間引かれても、戻ったときに増加量が合います。

手順2:価格と回収秒数を手計算で確かめる

数値を直す前に、強化ごとの「回収秒数」を計算します。回収秒数とは「その強化を買って元が取れるまでの秒数」で、価格 ÷ 毎秒の増加量で求めます。

最初の1個目を買う場合は次のとおりです。

  • 小さなかご:15 ÷ 0.1 = 150秒
  • 手押し車:100 ÷ 1 = 100秒
  • 小さな畑:1100 ÷ 8 = 137.5秒

1個目だけ見ると、手押し車がいちばん得です。ところが、価格は買うたびに上がります。手押し車を3台持った状態で4台目の価格を求めると、100 × 1.15³ ≒ 152.09 を切り捨てて152個です。回収秒数は152 ÷ 1 = 152秒になります。このとき、かごの1個目は150秒のままです。つまり、手押し車の4台目より、かごの1個目のほうが先に元が取れます。

この「得な強化が入れ替わる地点」が、クリッカーの選択の面白さです。3種類の回収秒数が、遊びの進行に沿って追い越し合う形になっていれば、選ぶ意味が残ります。

確認するための手順は次のとおりです。

  1. 紙か表計算に、強化ごとの列を作ります。
  2. 所持数を0から10まで増やしながら、価格(切り捨て)と回収秒数を書き出します。
  3. 強化どうしの折れ線が交差する地点があるかを見ます。

ブラウザの開発者ツールのコンソールで、items[1].base * 1.15 ** 3 のような式を直接打って値を確かめることもできます。

手順3:自分で遊んで記録し、数値を直す

次に、遊び心地を確かめます。結果は読者自身の画面で出るものなので、記録の取り方だけ決めておきます。

記録するのは、次の3つです。

  • 最初の購入までの秒数:「集める」だけで15個ためて、かごを買うまでにかかった時間です。
  • 各強化の初購入までの秒数:ストップウォッチで測ります。
  • 手が止まった場所:「何を買うか迷った」「待つだけで退屈だった」と感じた時点の所持数と毎秒の増加量を、メモします。

測る回数は、まず1回で十分です。可能なら別の人にも一度遊んでもらい、同じ項目を記録して見比べます。自分は仕組みを知っているので、迷いどころに気づきにくいためです。

結果の読み方と直し方の目安は次のとおりです。

  • 最初の購入まで長く、押すのが苦痛、疑う値:最初の価格、直し方の例:かごの base を下げる
  • 押さなくても進み、クリックが不要に感じる、疑う値:生産量の割合、直し方の例:生産役の rate を下げる、または power を上げる
  • ずっと同じ強化を買い続ける、疑う値:価格の上昇率、直し方の例:1.15 を大きくする(例:1.2)
  • 強化を選ぶ意味がない、疑う値:回収秒数の差、直し方の例:3種類の回収秒数が近づくよう base と rate を調整する
  • 序盤は面白いが中盤で停滞する、疑う値:2種類目の価格、直し方の例:手押し車の base を下げ、間を埋める

直すときは、一度に1つの値だけ動かします。複数を同時に変えると、どの変更が効いたのかわからなくなるためです。

動作確認を早く進めたい場合は、コンソールで n = 5000 と打つと所持数を直接変えられます。中盤の遊び心地を確かめたいときに便利です。ただし、公開するファイルにこの種の近道は入れません。

起こりやすい失敗

最初のクリッカーで多いつまずきを挙げます。

  • 小数の見た目:内部の値は小数のまま持ち、表示のときだけ Math.floor や toFixed で整えます。内部の値を丸めてしまうと、毎秒0.1の増加が積み上がりません。
  • ボタンの押せる判定のずれ:所持数と価格は、同じ計算の結果を使って比べます。表示用と判定用で別々の式を書くと、押せるのに灰色のまま、といった食い違いが出ます。
  • 数字が大きくなったときの見づらさ:今回の数値では問題になりにくいものの、強化を足していくと桁が増えます。そのときは、1,000ごとにカンマを入れる、「万」「億」の単位を使うなどの工夫が必要になります。
  • 強化を増やしすぎる:種類が多いと、回収秒数の関係を管理しきれなくなります。まず3種類で釣り合いを取り、それから足します。
  • 保存を最初から入れる:保存は仕組みが増えるため、遊び心地が固まってからにします。このコードは、ページを閉じると進行が消えます。

次の行動

まず上のコードを動かし、回収秒数の表を紙に書いてみてください。表の線が交差する地点を作れたら、小さな核は完成です。

公開するときは、index.html を入口にしたHTMLかZIPをアソバースの投稿画面にアップロードします。入力、表示の確認、公開の3段階で進みます。公開にはメールアドレスの確認が必要です。

アップロードした作品内のlocalStorageは一時メモリーの互換処理にとどまります。進行を残したい場合は、作品ごとにJSONで128KiBまで使えるクラウドセーブを使います。具体的な使い方は、現在の案内で確認してください。