マガジン

メトロノームだけのゲームを作る:拍に合わせて押すだけ

遊びをつくる

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

キーボード、ゲームパッド、タッチ画面を同じ机に置いた操作方法の比較。
記事のテーマをもとに制作したAI生成イメージ。実際のゲーム画面ではありません。

拍に合わせてボタンを押すだけのゲームには、遊びの要素がほとんどありません。ルールも画面もなく、あるのは一定間隔のクリック音と、押した瞬間のずれだけです。だからこそ、一つの数字が体験を大きく左右します。「何ミリ秒以内なら成功とみなすか」という判定幅です。 判定幅を狭くすると、成功したときの手応えは強くなりますが、失敗が続くと責められている気分になります。広くすると気持ちよく成功し続けられますが、そのうち「どう押しても通る」と感じて張り合いが消えます。気持ちよさが最大になる幅は、狭すぎず広すぎない、その間のどこかにあるはずです。 この記事は、その幅を自分の手と耳で探すための実験の設計書です。単独で動く最小のコードを示し、判定幅を変えながら記録を取り、結果を読み解く手順までをまとめます。どの幅が正解かは書きません。プレイヤー、端末、イヤホンの種類で答えが変わるため、読者自身が測って決める形にしています。

この記事の目次
  1. 最小の音ゲーの土台を作る
  2. 何を変え、何を測るか
  3. 手順
  4. 結果の読み方
  5. 起こりやすい誤り
  6. 遊びとして仕上げる方向
  7. 次の行動
  8. 参考資料

最小の音ゲーの土台を作る

実験に必要な部品は三つです。一定間隔で鳴るクリック音、押した瞬間の時刻、最寄りの拍とのずれの計算です。

時刻の扱いで最も重要なのは、タイマーで直接鳴らさないことです。JavaScriptのsetIntervalは処理の混み具合でぶれます。音の再生時刻は、Web Audio APIのAudioContextが持つ時計(currentTime)で予約します。タイマーは、近い未来の音を予約する見張り役として短い間隔で回すだけにします。

次のコードは、そのまま.htmlとして保存すれば動きます。外部ライブラリもサーバーも使いません。

html
<!doctype html>
<meta charset="utf-8">
<button id="b" style="font-size:2em;padding:1em">押す (Space)</button>
<p>判定幅 ±<span id="wv">60</span>ms
<input id="w" type="range" min="20" max="200" value="60"></p>
<pre id="o"></pre>
<script>
const T = 0.6; // 100BPM
let ctx, t0, next = 0, hits = [];
const W = () => +document.getElementById('w').value;
function click(at) {
  const osc = ctx.createOscillator(), g = ctx.createGain();
  osc.frequency.value = 880;
  g.gain.setValueAtTime(0.3, at);
  g.gain.exponentialRampToValueAtTime(0.001, at + 0.05);
  osc.connect(g).connect(ctx.destination);
  osc.start(at); osc.stop(at + 0.06);
}
function tick() {
  while (t0 + next * T < ctx.currentTime + 0.2) click(t0 + next++ * T);
}
function press() {
  if (!ctx) {
    ctx = new AudioContext(); t0 = ctx.currentTime + 0.5;
    setInterval(tick, 50); return;
  }
  const n = Math.round((ctx.currentTime - t0) / T);
  if (n < 0) return;
  const d = Math.round((ctx.currentTime - t0 - n * T) * 1000);
  hits.push(d);
  const m = hits.reduce((a, b) => a + b, 0) / hits.length;
  const sd = Math.sqrt(hits.reduce((a, b) => a + (b - m) ** 2, 0) / hits.length);
  const ok = hits.filter(x => Math.abs(x) <= W()).length;
  o.textContent = (Math.abs(d) <= W() ? 'ぴったり ' : 'ずれ ') + d + 'ms\n'
    + `回数${hits.length} 平均${m.toFixed(0)}ms ばらつき${sd.toFixed(0)}ms ぴったり${ok}回`;
}
b.onpointerdown = press;
addEventListener('keydown', e => { if (e.code === 'Space' && !e.repeat) { e.preventDefault(); press(); } });
w.oninput = () => { wv.textContent = W(); hits = []; };
</script>

最初の1回の押下は、音を開始するために使われ、記録されません。ブラウザは、利用者の操作より前に音を鳴らすことを制限しているためです。

画面に出る数字の意味は次のとおりです。

  • ずれ(ms):負なら拍より早く、正なら遅く押したことを表します。
  • 平均:ずれの平均です。符号つきで見ます。
  • ばらつき:ずれの標準偏差(平均からの散らばりの大きさ)です。
  • ぴったり:現在の判定幅の中に入った回数です。

スライダーを動かすと記録がリセットされます。条件ごとに数え直すための仕様です。

何を変え、何を測るか

この実験で答えたい問いは、次の一文です。

判定幅を変えると、成功率ではなく「押していて気持ちいいか」はどう動くか。

「気持ちいい」は測りにくい量です。そこで、数字で取れるものと感覚で取れるものを分けて記録します。

  • 変える、項目:判定幅、取り方:例として30・60・100・150msの4条件
  • 固定する、項目:テンポ、音色、ボタン、出力機器、取り方:実験中は変えない
  • 数字で測る、項目:ずれの平均、ばらつき、ぴったり回数、取り方:画面の表示を読み取る
  • 感覚で測る、項目:爽快感、納得感、取り方:条件ごとに1〜5点で自己評価

爽快感は「押して成功したときの気持ちよさ」、納得感は「成功と失敗の線引きが妥当だと感じたか」です。一つにまとめず二つに分けるのは、判定幅を広げたときに両者が逆方向へ動く可能性があるからです。

判定幅の候補は、自分のばらつきを測ってから決めても構いません。次節の「準備運動」で得た標準偏差を基準に、その半分・同程度・2倍・3倍といった幅を選ぶ方法もあります。

手順

準備

  1. 上のコードをmetronome.htmlとして保存し、ブラウザで開きます。
  2. 使う出力機器を一つに決めます。有線イヤホン、内蔵スピーカー、無線イヤホンのどれでも構いませんが、途中で替えないでください。
  3. 記録用の表を紙か表計算ソフトに用意します。列は、条件、押した回数、平均、ばらつき、ぴったり回数、爽快感、納得感、ひとことメモです。

準備運動(ずれの癖を知る)

  1. ボタンを1回押して音を始めます。
  2. 10回ほど拍に合わせて押し、画面の数字を見ずに感覚だけで慣らします。
  3. スライダーを一度動かして記録をリセットします。
  4. 改めて20回押し、平均とばらつきを表に控えます。

平均が0から大きく離れていたら、それは押し方の癖か、出力機器の遅れです。この値は、あとで結果を読むときに使います。

本番

  1. 判定幅の4条件の順番を、くじやサイコロでランダムに決めます。小さい順に並べると、慣れや疲れが「広い幅ほど良い」という結論に混ざるためです。
  2. 1条件につき、拍に合わせて20回押します。
  3. 20回押し終えたら、画面の数字を表に写します。
  4. すぐに爽快感と納得感を1〜5点で付け、ひとことメモを書きます。メモは「外れた理由が分からなかった」「全部通って退屈だった」のような短い言葉で十分です。
  5. 次の条件へ進む前に、スライダーを動かして記録をリセットします。
  6. 4条件が終わったら、できれば日を改めて、別の順番で同じことをもう一度行います。

画面の数字は、評価を付けたあとで見るのが理想です。数字を見てから評価すると、感覚が数字に引っ張られます。難しければ、画面を紙などで隠し、20回終えてから開く方法でも構いません。

結果の読み方

表が埋まったら、条件ごとに次の三つを確認します。

ぴったり率を出す

ぴったり回数を押した回数で割ります。20回なら、回数を20で割った値です。

  • 全条件でほぼ100%、またはほぼ0%の場合は、条件の差が体感に出にくい状態です。幅の候補が極端すぎた可能性があるので、候補を取り直します。
  • 条件を広げるほど率が上がるのは自然な動きです。注目するのは率の上がり方ではなく、そこに感覚の評価がどう重なるかです。

感覚の評価と並べる

幅を横軸に、爽快感と納得感を縦軸にして、二本の折れ線を描きます。見る型はおおよそ次の三つです。

  • 爽快感が上がり、納得感が下がる:広げると気持ちよくなる一方で、線引きが曖昧に感じられている状態です。納得感が落ち始める手前の幅が、候補になります。
  • どちらも途中で頭打ちになる:頭打ちが始まる幅より先は、広げても得るものが少ないと読めます。始まる手前を候補にします。
  • 爽快感が狭い条件でも高い:失敗しても押すこと自体が楽しいタイプです。狭い幅でも成り立つので、難度を上げる余地を探せます。

どの型になるかは、実際に測ってみないと分かりません。二回目の測定でも似た形になれば、傾向として信頼できます。形が毎回違うなら、その幅の近辺では差が体感に出ていないと考えます。

自分のばらつきとの比を見る

準備運動で得たばらつきと、各条件の判定幅の比を計算します。たとえば、幅がばらつきの何倍にあたるかを条件ごとに書き出します。比の小さい条件は「自分の揺れに対してかなり厳しい」、大きい条件は「揺れの大半が成功に入る」条件です。評価の高かった条件がどの比に集まるかを見れば、「自分は揺れの何倍くらいの幅が心地よいのか」という、プレイヤー個人に依存しにくい目安が得られます。

他人に遊んでもらう場面では、各自のばらつきが違います。一律の幅より、ばらつきに対する比で考えるほうが、設計の議論に使いやすくなります。

起こりやすい誤り

次の点は、結果を歪めやすい代表例です。

  • 無線イヤホンを使う:音の出力が遅れ、ずれの平均が大きくプラスに偏ることがあります。遅れは機器ごとに違うため、固定した機器で測るか、有線に替えてください。
  • 平均のずれを放置する:平均が一定の方向に偏るのは、押し方や機器の遅れによる癖です。判定側で平均分を補正する方法もありますが、補正した場合は、補正前と後を別の実験として扱ってください。
  • テンポが速すぎる:拍の間隔が判定幅の2倍に近づくと、隣の拍との区別が曖昧になります。コードは最寄りの拍とのずれを取る作りなので、押す位置が大きくずれると、実際には別の拍を狙った押下が記録に紛れます。
  • 条件を小さい順に並べる:慣れの効果が幅の効果に見えます。順番はランダムにし、日を改めて逆順でも測るのが安全です。
  • サンプルが少ない:20回では偶然の影響が残ります。差が小さいと感じたら、回数を増やしてください。
  • 音の聞こえ方を変える:クリック音の高さや長さが変わると、体感も変わります。音色を変える実験は、判定幅の実験とは別に行ってください。
  • 一人だけの結果で決める:自分には最適でも、他人には厳しすぎる場合があります。公開する前に数人に遊んでもらい、同じ表で記録を取ってもらうと、幅の妥当性を確かめられます。

遊びとして仕上げる方向

最適な幅の見当がついたら、そこから遊びに寄せていけます。ここでは、実験の結果が使える方向だけを挙げます。

  • 段階判定にする:「ぴったり」「ほぼ」の二段にして、内側の幅と外側の幅を別々に調整できます。二段目の幅に実験の結果を使えます。
  • フィードバックを足す:押した瞬間に光や短い音を返すと、判定の納得感が変わる可能性があります。変更するときは、一度に一つだけ変えて、同じ表で比べてください。
  • テンポを変える:テンポを変えると、同じ判定幅でも難度が変わります。テンポごとに幅を測り直してください。

作ったHTMLは、そのままindex.htmlとしてアソバースに投稿できます。HTMLは直接アップロードできるので、サーバーは不要です。投稿画面では、入力、表示の確認、公開の順に進みます。公開前に表示の確認ができるので、他の人の環境でも同じ体感になるかを一度見てみてください。

次の行動

まず上のコードを保存し、準備運動で自分の平均とばらつきを測ってください。次に、判定幅の4条件をランダムな順で20回ずつ押し、爽快感と納得感を表に記録します。最後に、日を改めてもう一度測り、二回で同じ形が出るかを確かめてください。

参考資料

  • MDN Web Docs「Web Audio API」
  • W3C「Web Audio API」仕様