5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

UnityでInputの分岐器を作った話

Last updated at Posted at 2019-08-18

#概要
Unityあるある、「複数の操作可能なオブジェクトが同時に操作できてしまう」を抹殺するため、常に1か所にのみインプットを流す仕組みを作ったお話。
example.gif

#作成したもの
https://github.com/domasyake/InputTurnout

#経緯
 Unity、カスタマイズ前提で敢えて最低限の機能しか用意してないんだろうなーっていうもの幾つかあると個人的には思います、インプットなんかはそのうちの1つ。ゲームはプレイヤーキャラやUIなど、「入力によって操作できるもの」が多数必要になりますが、各自がバラバラにインプット情報を取得するとなると、操作対象の切り替えもBoolで判定したりとかGameObjectそのもののActiveで止めたりとかするしかなく、バグの温床になること請け合い。
 そこでインプット情報を常に1か所にのみ流すようにする分岐器(線路の切り替えするアレ)みたいなものを経由すれば、制御対象=インプット出力先となり各自で判定せずに済むだろうと考え作成しました。 
 ついでにインプットの依存先をInterfaceに強制できるのでインプット出力元の可変(アセット使ったりモック使ったり)にも対応できるようになります。

#仕組み
インプットの出力元をサプライヤー、インプットを利用する側をレシーバーとします。
サプライヤーとして、実際に出力を行うEntityと常に非入力値を返すEmptyの2つを用意します。レシーバーは分岐器を介してサプライヤーにアクセスしますが、1つのレシーバーにのみEntityを、それ以外にEmptyを常に返すことでレシーバーは自身が今操作対象なのかどうかを意識せずに実装することができます。

#使い方
GitHubのReleaseにあるUnityPackageをインポートしてください。サンプルも含まれているのでそちらも参考に。

##レシーバー


private void Start(){
   InputTt.ChangeReceiver(this);//本来はレシーバーが直接ChangeReceiverするのではなく別途Managerなどの制御クラスで行うべき
}
	
private void Update(){	
   if (InputTt.Input(this).XXXX()){
   //移動処理とか
   }
}

##サプライヤー
###Interface
標準ではUnityEngine.Inputの主要メソッドのラッパーとして定義しています

IInputSupplier.cs
public interface IInputSupplier{
   bool anyKey{ get; }
   //中略
}

###Entity

WrapUnityStandardInputEntity.cs
public class WrapUnityStandardInputEntity:IInputSupplier{
   public bool anyKey => Input.anyKey;
   //中略
}

###Empty

WrapUnityStandardInputEmpty.cs
public class WrapUnityStandardInputEmpty:IInputSupplier{
   public bool anyKey => false;	
   //中略
}

#Tips

  • あくまで本ライブラリはインプットを中継し分岐させるだけなので、サプライヤーはカスタマイズ前提です。 
  • EntityにMonobehaviour継承クラスを使用したい場合は、下記のようにInputTtクラスの初期化をコメントアウト等で無効にした上でセットを行ってください。
InputTt.cs
private static void SupplierInitialize(){
   //SetSupplier(new WrapUnityStandardInputEntity(),new WrapUnityStandardInputEmpty());
}

public static void SetSupplier(IInputSupplier entity,IInputSupplier empty){
   LogWrite("------see Input Supplier------");
   inputSupplier = entity;
   emptySupplier = empty;
}
  • あくまでInputTt.Input(object key)に渡されたインスタンスで判定を行っているだけなので複数のレシーバーで共通のインスタンスを持ってそれをキーとしてアクセスすれば複数クラスでインプットを受け取ることも可能です。
  • InputTt.IsActiveプロパティでインプットを停止できます。
  • インスタンスをキーにあらゆる変更を停止するFreeze()と解除用のUnFreeze()メソッドが用意されています。暗転時等、是が非でも操作させたくない時にIsActiveと合わせて使用したりなど。

#余談
 一元管理できない処理が死ぬほど嫌いなので今まで3個くらいインプット制御のアプローチ作ってきたのですが、これが一番使い勝手よくてカスタマイズも楽だったので公開してみました。

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?