コントローラーの操作でジャンプする
Aボタンの判定の追加
コントローラーとUnityの対応が分かったところで「Aボタンを押せばキャラクターがジャンプする」ようにしましょう。対応表を見るとAボタンに対応するのは「joystick button 0」です。GameManager.csのスペースキーでジャンプ処理を行っていた部分を以下のように変更します。
// スペースキーでジャンプ // ジャンプ中はスペースキーが押されてもさらに上昇しない // XBox 360コントローラー対応 if ((Input.GetKeyDown("space") || Input.GetKey(KeyCode.Joystick1Button0)) && this._charactorScript.isJumping == false) { this._moveCharactor(Vector2.up * _upForce); }
動作を確認する
実行して動作を確認すると、Aボタンを押すとキャラクターが想定した以上にジャンプを行い画面の上端を飛び越えてしまいました。キーボードからコントローラーに変えたことで発生した不具合です。今回はその流れを確認しながら解決していきます。
ジャンプの判定文をもう一度見直してみます。
if ((Input.GetKeyDown("space") || Input.GetKey(KeyCode.Joystick1Button0)) && this._charactorScript.isJumping == false)
この判定は以下の条件を満たすとジャンプを行います。
- スペースキーまたはAボタン(Joystick1Button0)が押された
- キャラクターのisJumpingの値がfalseである
isJumpingはジャンプ中かどうかを判定するフラグで、これが正常に動作しているならキャラクターは大きくジャンプしないはずです。このフラグが怪しいので以下のようにログを書き出してみます。
if ((Input.GetKeyDown("space") || Input.GetKey(KeyCode.Joystick1Button0)) && this._charactorScript.isJumping == false) { this._moveCharactor(Vector2.up * _upForce); Debug.Log("jump"); }
予想通り一度のAボタン押下で複数回「jump」というログが出力されました。ではisJumpingの値はどのタイミングで変更されるのでしょうか?
ジャンプ判定の見直し
isJumpingはCharactorManager.csのプライベート変数_isJumpingの値を取得するgetter的な変数でした。_isJumpingが書き替えられるタイミングは以下です。
/// <summary> /// 接触状態から離れた /// </summary> /// <param name="col"></param> public void OnCollisionExit2D(Collision2D col) { if (col.gameObject.name == "ground" || col.gameObject.name == "floor") { this._collisionList.Remove(col.gameObject); // 接触しているGameObjectが0の場合はジャンプしていると判定してアニメーションを開始する if (this._collisionList.Count == 0) { this._isJumping = true; } } }
OnCollisionExit2Dというキャラクターが接地状態から離れた場合に発生するイベント内で_isJumpingが書き替えられています。GameManager.csのUpdateメソッドがOnCollisionExit2Dの処理が終わる前に複数回呼ばれるために、今回のバグが発生していると考えられます。
試しにAボタンを押された直前に_isJumpingを書き替えてみます。_isJumpingはパブリックな状態ではgetterしか提供していないので、以下のように値を変更できるようにsetterを追加します。
public bool isJumping { get { return _isJumping; } set { this._isJumping = value; } }
GameManager.csのUpdateメソッドに戻り、以下のようにisJumpingを操作します。
// スペースキーでジャンプ // ジャンプ中はスペースキーが押されてもさらに上昇しない // XBox 360コントローラー対応 if ((Input.GetKeyDown("space") || Input.GetKey(KeyCode.Joystick1Button0)) && this._charactorScript.isJumping == false) { this._moveCharactor(Vector2.up * _upForce); this._charactorScript.isJumping = true; }
実行してみると、大きくジャンプする問題は解決していることが確認できます。
setterを追加した点や、プログラミングの設計には改善の余地があるものの、今回の件で2つのことが分かりました。
- OnCollisionExit2D発生までにUpdateメソッドが複数回呼ばれる(イベント開始にわずかながら時間がかかっている)
- Input.GetKeyは複数回処理発生への注意が必要。
ボタン操作に対するもう一つの対応
Input.GetKeyではボタンを押している間は複数回処理を実行してしまいましたが、「ボタンを押したタイミングで一度だけ」イベントが取得できればよりスマートに解決できそうです。すでにコードには答えが出ているのですが、スペースキーの判定同様にInput.GetKeyDownを利用すれば、押したタイミングで一度イベントを取得できます。
コードを以下のように書き替えます。
if ((Input.GetKeyDown("space") || Input.GetKeyDown(KeyCode.Joystick1Button0)) && this._charactorScript.isJumping == false) { this._moveCharactor(Vector2.up * _upForce); }
これでもキャラクターの想定外の高いジャンプは発生しなくなりました。isJumpingへの操作がなくなるので、setterも必要なくなりました。
少し回りくどくなりましたが、OnCollisionExit2DイベントのタイミングとInput.GetKey、Input.GetKeyDownが分かったと思います。
まとめ
ゲームにおけるコントローラーの対応について紹介しました。これからXBox OneやPlayStation 4のコントローラーがPCゲームの標準になっていくと、また別の操作への対応が必要になってくるかもしれませんが、今のところ、コントローラーはXBox 360をターゲットにするのが主流です。
当たり前ですが、iPhoneやAndroid(Windows Phoneも)といったスマートフォンではコントローラーではなくタッチ操作への対応が必要になります。この辺は次回以降で紹介していければと思います。