見出し画像

ジョイスティックの基本制御

デジタルジョイスティックの入力状態を制御する基本的な方法をまとめました。デジタルジョイスティックを扱う前に、守るべき大切なルールが2点紹介します。

ひとつめ、毎フレームで入力状態を取得するということです。割り込みなどを使って非同期に入力状態を取得してもよいのですが、タイマー割り込みなどを使ってしまうと割り込みのタイミングとゲームの進行との同期を取るのが難しくなります。

もうひとつは、ジョイスティックの入力状態の値(以下、入力値)がビットマップで保持することです。これは演算を容易にするためです。

ここからは以上の2点が満たされている前提で話を進めていきます。

入力状態の変化を検知する

ジョイスティック入力の状態が変化したことを検知することは、他の色々なジョイスティック操作を実装していく基礎となります。

たとえば、レバーを一度倒してから一定時間以内にもう一度倒したとか、レバーが一定時間倒しっぱなっているなどを検知するためには、なによりもまず、レバーの状態が変化したことを検知する必要があるからです。

ジョイスティックの入力状態の変化を検知するのはとても簡単です。1フレーム前に得られた入力値と現フレームで得られた入力値を使って排他的論理和を取ります。

1フレーム前に得られた入力値をprev_st、現フレームで得られた入力値をcur_stとしましょう。このとき入力値の状態が変化した結果xor_stは次のように求めることができます。

xor_st = prev_st ^ cur_st

排他的論理和を取った結果、ビット値が1となるビットが入力状態に変化があった箇所です。倒していない状態から倒された、あるいは倒されている状態から離されたということです。逆に値が0となるビットは入力状態に変化がなかった箇所であることを意味します。

連続入力を検知する

一度レバーを入れて、一定時間以内に再びレバーを倒すというような少し複雑な操作も、ジョイスティックの入力状態の変化を捕らえることができれば対応するのにはそれほど苦労しません。

このような連続入力に対応させるためには、次のような処理を行います。

(1)はじめてレバーが倒されたのを検知する
(2)カウントダウンタイマーを初期化する
(3)一度レバーが離されるのを待つ
(4)再びレバーが倒されるのを待つ

3番目の処理が大切です。この処理を省くとレバーを倒したままでも連続入力だと検知されてまいます(意図的にそうしたい場合を除く)。

レバーを入れて一度離し、再びレバーを倒すという処理となるので、最低3フレーム掛かる操作です。60fpsのゲームであれば1フレームが16.67msとなるので、最低約50ms(0.05秒)掛かるということです。

もちろん、人間にはそのような高速な動きはできませんが、ここから連続入力待ち受けをするのに適切なカウンター値を算出していくことがだきるようになります。

たとえば、0.5秒以内に連続入力させれば良いのであれば、30フレーム待てばよいとわかるようになります。

セミオート連射機能を実装する

一定時間、ボタンが押されたままであることを検知するには、一度ボタンが押されてから入力状態に変化がないことを検知します。

これを応用するとセミオート連射機能を実装することができます。具体的にはボタンが押しっぱなしであるという時間をカウントする、カウントアップタイマーを併用します。

具体的な処理の流れはこうです。

(1)はじめてボタンが押されたことを検知する
(2)カウントアップタイマーを初期化する
(3)ボタンが押しっぱなしであればタイマーをカウントアップさせる
(4)タイマーが一定の値に達したら、新しく入力したことにし、カウントアップタイマーを初期化する

60fpsのゲームで秒間8連射させたい場合、大まかに計算して60/8≒7.5フレーム待てばよいということになります。

実際には0.5フレーム待つということはできませんので、7フレームの待ち合わせになるでしょう。8連射というのは近似値ということです。

60fpsのゲームであれば、秒間6連射とか10連射、12連射など割り切れる数にしておくとよいと思います。

リピート入力機能を実装する

セミオート連射機能を実装できれば、次の応用としてリピート入力機能を実装することができるようになります。

処理的にはセミオート連射とまったく変わらないからです。

(1)はじめてボタンが押されたことを検知する
(2)カウントアップタイマーを初期化する
(3)ボタンが押しっぱなしであればタイマーをカウントアップさせる
(4)タイマーが一定の値に達したら、新しく入力したことにし、カウントアップタイマーを初期化する

リピート入力させる場合はカウントアップタイマーを初期化する際に、その値を変化させます。たとえば、最初のカウントは0.5秒間を数えるための30フレームまでとしますが、次は0.25秒となる15フレームまでとするわけです。

ファイナルファンタジー的にする

ファイナルファンタジーのリピート入力機能では3段回以上のスピード制御が行われています。先ほど紹介したリピート入力機能でも数段回のスピード制御を行うことができますが、あっという間にトップスピードへ達してしまいます。

ファイナルファンタジーのリピート入力機能では、もうひと捻りした処理が加えられています。最初のリピート入力から、次のリピート入力までは一般的なカウントアップ方式を採っていますが、その次以降は、別系統で立ててあるカウントアップタイマーを併用してカウントアップしているのです。

一般的なカウントアップタイマーをA系統、もうひとつのカウントアップタイマーをB系統と呼ぶことにしましょう。

A系統を使ったリピート入力機能はそのままに、B系統はB系統で独立したカウントアップを始めます。そして、B系統のタイマー値をたとえば90フレームで割り、その商を使ってA系統のカウントアップタイマーの初期値を割ります(0除算しないように注意)。

このようにしておくと、180フレーム(3秒)以降から90フレーム(約1.5秒)ごとにリピート速度が加速していくようになります。

終わりに

以上、デジタルジョイスティックの基本的な実装方式をまとめました。

ストリートファイターなどでよく利用されるコマンド入力に対応させたい場合、ここに書いた基本的な実装が役に立つと思います。

機会があれば、コマンド入力機能についてもまとめたいと思います。

この記事が気に入ったらサポートをしてみませんか?