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

お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

UnityのInputが使いにくすぎるので、俺々ライブラリを書いて一人満足した

ダウンロード

まぁ君がこの記事を読んでいるのも何かの縁だ。
とりあえず下のページのcsファイルをダウンロードして君のUnityプロジェクトにインポートするんだ。
github.com

UnityのInput

Unityの入門をした人でInput.GetAxis()や、Input.GetKeyDown()とかの関数を使ったことのない人はいないと思う。
Unityでゲームを作るうえで、キーボードやゲームパッドなど、外部からの入力を受け取るには基本的にこれらの関数を使うことになる。

Inputの使いにくさ

はじめこそ、「おっ、これ簡単で使いやすいじゃん」って思うのだが、しばらく使っているとなんともかゆいところに手が届かないようになっているのである。

たとえば、アナログスティックのX軸やY軸の値をとるための、Axisに関する関数群がそうだ。
このAxisの値をとろうとすると、「Input.GetAxis("Horizontal")」という関数を使うことになるわけだが、見ての通りこの関数は引数としてAxisの名前の文字列を必要とする。
これは、Axisがその名前で管理されていることに起因するのだが、いちいちプログラム中にスペルミスを気にしながら文字列を打つのはかなりめんどくさいものがある。
それを避けるためには自分で列挙型を作ったり、文字列を変数に保存したりといろいろやり方があるのだろうが、いかんせんめんどくさい。
そもそも初めから引数に列挙型とかを使うことができればこんな苦労はしなくてもよいのだ。

また、Axisに対してはInput.GetAxisによって現在の値を取得するということしかできないため、たとえばメニュー画面で方向キーによって選択している項目を切り替えたいとき、つまり方向キーが押された瞬間を取得する、という行為でさえ少し煩雑な作業が必要になる。
「Input.GetButtonDown」はあるくせに、「Input.GetAxisDown」は無いのである。

とまぁこんな具合に日頃、Inputによって鬱憤を溜める羽目になっているUnityユーザーは多くいることだろう。
あるいは、そんなInputに嫌気がさして、もうすでに自分で汎用スクリプトを書いてある、という人もかなりいるに違いない。

さて、自分は後者の人間だったわけで、数か月前にUnityでInputを扱うための俺々ライブラリを書き、継ぎ足し継ぎ足し所々で使ってきたのだった。
ここではそんな俺々ライブラリについて紹介させてもらおうと思う。

InputController

っていう静的クラスを作った。
このクラスは「Controller」クラスの配列を持っており、このControllerはさらに「ButtonState」クラスや「AxisState」クラスの配列を持っている。InputControllerが何をしているのかというと、フレームごとにButtonクラスやAxisクラスのインスタンスに「更新しろ」という命令を送り、その結果を用いて種々の入力を返すメソッドを持っている、というわけだ。

といってもよくわからんので、InputControllerが持っている関数を下に紹介していく。

配下のいろいろ

Controller

複数のコントローラーに対応したかったので、Controllerというクラスを作ってそのインスタンスを配列で持つようにした。

ButtonState

Controllerのインスタンス連想配列で所持している。
InputControllerが何をしているかを簡単に説明すると、こいつを毎フレーム更新し、OnButtonDownとかを呼び出すことでボタンが押された瞬間などを取得する、という仕組みになっている。

AxisState

ButtonStateと大体いっしょ

Button

ボタンの列挙型。
Button.AとかButton.Startとかがある。

Axis

軸の列挙型
Axis.R_HorizontalやAxis.Cross_Verticalなどがある。

GamePad

ゲームパッドの列挙型。
1から4まである。

汎用関数

Update

InputControllerの要となる関数だ。
この関数を毎フレーム呼び出さないとこのライブラリは動かない。
したがって、どっかのコンポーネントのUpdateで一行「InputController.Update()」と空く必要がある。
う~ん、たぶんこの運用方法はダメなんだろうなぁ(Updateを呼び出すのがめんどくさい)と思いつつ、Singletonにしてもあまり手間は変わらない(いちいちGameObjectにアタッチしないといけない)ような気がするし、この使用のままにしている。
staticメソッドを毎フレーム呼び出せる、「ExucuteEveryFrame」みたいな属性があったらいいんだろうけど、ないものは仕方がない。
そういえばUniRxとかはどんな方法使ってるんだろうか。
ちなみに同じフレームに間違えて二度三度呼び出してしまっても大丈夫なようにはなっている。

GetAxisDown(Axis axis, GamePad pad = GamePad.One)

第二引数で指定したコントローラーの、第一引数で指定した軸が押された瞬間は1(or-1)を返す。それ以外は0。
GamePad.Allとかいうのもあって、これを指定するとすべてのコントローラーのいずれかの指定された軸が押されると1(or-1)を返すとかができる。

GetAnyButtonDown(GamePad pad =GamePad.One)

いずれかのボタンが押されたらその瞬間だけtrueを返す。

GetButtonStay

ボタンが押されている間はtrue、それ以外はfalseを返す。

GetAxisStayPeriodicly

軸がが押された瞬間、あるいは押されてから0.2秒以降の0.1秒ごとに1(or-1)が返される。
つまり、方向キーを押しっぱなしにしたときの「ピッ、ピピピピピ……」というカーソルの移動が容易に実装できるのだ!!

GetButtonStayTime

ボタンが押されている時間を取得する。

初期化関数

どのボタンをどこに割り当てるか、などの設定をする。
なにもしなかったら勝手に「SetForKeyboard」が呼び出される。

SetDefault

UnityでデフォルトでInputManagerに設定されてある内容を使えるようにする。
例えば、「InputController.GetButton(Button.A)」と「Input.GetButton("Jump")」は同じ動きをするようになる。
詳しくはコードを見ろ(ぶん投げ)

SetForKeyboard

キーボードを入力に使えるようにする。
例えば、「InputController.GetButtonStay(Button.A)」が「Input.GetKey(KeyCode.Space)」と同じ動作をするようになる。
詳しく書くのもめんどうなので、中で何をしてるか書くと、

        controller.Buttons[Button.A].SetKeyCode(KeyCode.Space);
        controller.Buttons[Button.B].SetKeyCode(KeyCode.Z);
        controller.Buttons[Button.X].SetKeyCode(KeyCode.X);
        controller.Buttons[Button.Y].SetKeyCode(KeyCode.C);

        controller.Buttons[Button.Start].SetKeyCode(KeyCode.Return);
        controller.Buttons[Button.Select].SetKeyCode(KeyCode.Escape);

        controller.Axes[Axis.R_Horizontal].SetKeyCodes(KeyCode.RightArrow, KeyCode.LeftArrow);
        controller.Axes[Axis.R_Vertical].SetKeyCodes(KeyCode.UpArrow, KeyCode.DownArrow);
        controller.Axes[Axis.L_Horizontal].SetKeyCodes(KeyCode.D, KeyCode.A);
        controller.Axes[Axis.L_Vertical].SetKeyCodes(KeyCode.W, KeyCode.S);
        controller.Axes[Axis.Cross_Horizontal].SetKeyCodes(KeyCode.RightArrow, KeyCode.LeftArrow);
        controller.Axes[Axis.Cross_Vertical].SetKeyCodes(KeyCode.UpArrow, KeyCode.DownArrow);
        controller.Axes[Axis.Side].SetKeyCodes(KeyCode.E, KeyCode.Q);

というわけで、Button.BがKeyCode.Zに対応してるとかいうことがわかるはず……。
自分が使いやすいように対応させているので、あまり一般的な対応ではなさそう。。。



とまぁ、こんな具合である。結局やりたかったことは何かと聞かれると、「GetAxisDown」関数がほしかったというだけのことなんだけど。。。
あ、あと「GetAxisStayPeriodicly」関数もお気に入りなので使ってみてね。
とりあえず、なるべく簡便に使えるように作ったつもりなので使い方さえわかればUnityでゲーム作るのがちょっとは楽になる(はず……?)