読者です 読者をやめる 読者になる 読者になる

あさちゅんのゲームブログ

UnityやSiv3Dに関するゲーム開発メモを残していきます

Siv3Dでキーボードとゲームパッドの両方に簡単に対応させる

PCゲームだとやっぱり開発はキーボードで操作しがちですよね。ゲームパッド対応なんてすぐできるから後回し、なんてやってるうちに進捗がずれ込んで明日展示なのにまだ対応してないっ!なんてことも

今回はそんな人のためにさくっとゲームパッドやXInputに対応させるクラスAscInputの実装の紹介です!

軸を管理するAxisクラスを作る

ボタンについてはSiv3Dの機能「複数のキー入力の組み合わせ」を使えば良さそうなのでゲームパッドやXInputのスティックをキーボードでも表現できるようにする「軸」があれば良さそうです。そこでこんなクラスを作ります。

コンストラクタが3つあってどれで軸を表現するかによって使い分けます。まず、キーボードを軸にする時はプラス方向のキーとマイナス方向のキーの2つで軸を作ります。Input::KeyRightとInput::KeyLeftを指定してあげれば十字キーの左右の軸になるってことですね。コントローラーの軸として使いたいときはGamepadやXInputのインスタンスとどの軸を使いたいかを列挙型で指定してやります。

軸の値を取り出したい時はdouble型のoperatorがあるので、double型を突っ込むところにこのAxis型を突っ込んでやればOKです。

それでは実装を見ていきます

先程、意味深に宣言されていた4つのインナークラス(基底クラス1つ+派生クラス3つ)が使用したコンストラクタによって使われ、中身は全く違えど同じように扱えるという仕組みです。

キーボードの場合はCKeyboardAxisが突っ込まれてキーが押されているかいないかで-1.0と1.0を足し合わせて{ -1.0, 0.0, 1.0 } の値を取るようになっています。XInputの場合はCXInputAxisが突っ込まれ、渡したインスタンスを自身のメンバに保存して、列挙型に対応したインスタンスメンバ関数をstd::functionに縛り、double型に変換する時は縛った関数を呼び出しています。メンバ関数なのでstd::bindを使っているのに注目ですかね。Gamepadも同様にしてこれで3つの入力方法を1つのAxis型として扱う準備ができました。

複数の軸を一つにまとめ上げる

せっかくKeyの方は「Input::KeyZ | XInput(0).buttonA」のようにまとめ上げれるので、Axisの方も「|」でまとめ上げれる方が見た目がきれいですよね。そこでSivKey.hppにあるKeyCombinationと同じようなoperatorを持つAxisCombinationを以下のように作ります。

s3d::Keyと同じように可変長テンプレートのコンストラクタと「|」でまとめ上げるためのoperatorを宣言してやります。これでぱっと見はs3d::Keyと同じように扱うことが可能になりました。次に実装です。

operatorでは受け取ったAxisを全てArrayに格納していきます。double型に変換するときは頭から見て行って、0以外の値が出たらそれを返します。そのせいでs3d::Keyと違ってまとめ上げる順番を考慮しなければならなくなりましたが、まぁ仕方ないでしょう、、、

ついでにコントローラーの軸はたいてい触っていなくても値が0にならないのでデッドゾーンの処理をするかキーボードを先に登録しないとうまく反応しません。

AscInputの機能

前置きが長くなりましたが、ついにAscInputに入ります。と言ってもこいつはほとんど何もしてないんですが、、、

そもそもs3d::Inputと名前が衝突しているので今まで作ったものすべてを名前空間ascに閉じ込めておきます。

KeyやAxisを名前を指定して登録、その名前を使って呼び出してやります。これだけじゃつまらないのであれば便利な機能を足してみました。

vec2Normalized関数は軸2つから単位ベクトルを返してくれます。プレイヤーの移動とかは大体これを使っていると思うので、そのまま足せるよう軸が零ベクトルの時は単位ベクトルではなく零ベクトルを返します。

enabledプロパティはfalseの間、入力を無効にします。イベント中とかUIのアニメーション中など一時的にプレイヤーに操作してほしくない時にあると便利です。

それでは実装を見ていきます。

内部ではunordered_mapを使って文字列(登録する名前)をキーとしたKeyCombinationとAxisCombinationを保持しています。enabledがfalseの時はKeyのデフォルトの値や0を返すようにしています。本当にそれだけです。基本頑張ってくれているのはAxisクラスの方ですね、、、

バグ等

長々と説明しておいて思いっきしバグがあります。デッドゾーンの設定は現在XInputにしかできません。なのでvec2Normalized関数をGamepadの軸で使うと触っていないのにとんでもない方向を向いた単位ベクトルが返ってきます。

また、DirectInput方式(s3d::Gamepadのこと)とXInput方式両方に対応したコントローラーを繋いでGamepadとXInputをまとめ上げるとXInputでキャンセルされるはずの値がGamepad側で取られてしまってうまく動作しません。

asc::Input側でデッドゾーン的な何かを実装すれば解決できますが、将来的にはGamepadにもデッドゾーンが適用できるようなので、本家頼みです(汗

まとめ

PCゲームをやっている人からすればZが攻撃でジャンプがSpaceなど常識のようですが、コンシューマーゲームしかしたことのない人はたくさんあるキーのうちどれを使うかなんてわかりません。(実際、そのせいでゲームの操作方法がわからないことがありました。)

コントローラーを使えばそういう人たちにも簡単に操作できるようになるので、よりたくさんのゲームがコントローラー対応してくれたらいいなぁと思います。

そして最後に時間がなかったらぜひAscInput使ってやってください~

使用したソフト
Windows 8.1
Siv3D June 2015 v2