【Unity】ECSでの入力の最適な検出方法【DOTS】

# はじめに

「ECSを使ってゲームを作りたい！」ということで、とりあえずECSでプレイヤーキャラクターを移動させるシステムを作ることにした。

とりあえず、以下のコードを見てほしい。

```cs
public struct Player : IComponentData { }

public struct Mover : IComponentData {
	public float Speed;
}

// プレイヤーを移動させるためのシステム
public class PlayerMovementSystem : SystemBase {

	// 移動の入力情報
	InputAction m_MoveInput;

	protected override void OnUpdate () {
		// 移動の入力を取得する
		Vector2 input = m_MoveInput.ReadValue<Vector2>();
		float3 movement = new float3(input.x,0f,input.y);

		// 移動させる
		float deltaTime = UnityEngine.Time.deltaTime;
		Entities
			.WithAll<Player>()
			.ForEach((Mover mover,ref Translation translation) => {
				translation.Value += movement * mover.Speed * deltaTime;
			})
			.WithBurst()
			.ScheduleParallel();
	}
}
```

ただ、一見動きそうなこのコードには問題があって、移動の入力情報（`m_MoveInput`）には何も割り当てられていないのです。

しかし、単純にnewすればいいというものでもなく、`InputAction`のコンストラクタは文字列ベースのハードコーディングになるので普段使いには適さない。

`Keyboard.current`などのAPIを使う方法もあるが、これもしたくない。
とにかくハードコーディングはしたくない。

**では、`m_MoveInput`はどうやって設定すればいいのだろうか？**


# 結論： `Generate C# Class`オプションを使う

InputSystemには、`InputActionAsset`からC#コードを生成する機能があります。
そして、生成されたクラスを使用することで、`InputActionAsset`で定義された`InputAction`に対してコードから簡単にアプローチすることができるようになります。

この方法の利点として、

- UnityエディターでInputActionを編集可能
    - ハードコーディングせずに済む
- 外部からInputActionの参照を割り当てる必要が無い
    - そのための余計なコンポーネントなどを実装せずに済む

というところが挙げられます。

以下からは実際の手順の解説です。

## 1. `InputActionAsset`を作成する

1. `Assets > Create > Input Actions`メニューから、`InputActionAsset`を作成する。
2. 作成した`InputActionAsset`に、必要な`InputAction`を定義する。

今回は`Move`という`InputAction`を定義しました。

![スクリーンショット 2021-08-04 005219.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605701/d97cd8df-9c4a-d22b-80e0-09d0b5468dcf.jpeg)

## 2. `Generate C# Class`オプションを有効にする

作成した`InputActionAsset`の`Generate C# Class`を有効にする。


![スクリーンショット 2021-08-04 005250.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605701/e11ad61e-38f9-67b7-351a-36deb0392ebb.jpeg)

すると、以下のようなコードが自動で生成されます。

![スクリーンショット 2021-08-04 190154.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/605701/47d40ca1-a25d-132d-0f91-45fef799dd65.jpeg)


## 3. コードを書く

`InputActionAsset`から生成したクラスを使用し、最初のコードに少し手を加える。

```cs
public class PlayerMovementSystem : SystemBase {

	PlayerInputActions m_Input;
	InputAction m_MoveInput;

	protected override void OnCreate () {
		// PlayerInputActionsをインスタンス化し、有効にする
		m_Input = new PlayerInputActions();
		m_Input.Enable();

		// PlayerInputActionsに定義されているMoveをm_MoveInputに割り当てる
		m_MoveInput = m_Input.Player.Move;
	}

	protected override void OnUpdate () {
		// 移動の入力を取得する
		Vector2 input = m_MoveInput.ReadValue<Vector2>();
		float3 movement = new float3(input.x,0f,input.y);

		// 移動させる
		float deltaTime = UnityEngine.Time.deltaTime;
		Entities
			.WithAll<Player>()
			.ForEach((Mover mover,ref Translation translation) => {
				translation.Value += movement * mover.Speed * deltaTime;
			})
			.WithBurst()
			.ScheduleParallel();
	}

	protected override void OnDestroy () {
		m_Input.Disable();
		m_Input.Dispose();
	}

}
```


# おわりに



ECSでの入力について調べましたが、これがECSでの最適な入力の検出方法なんじゃないかなと思います。

もし「もっと良い方法があるよ」という人がいれば、教えていただけると幸いです。

# 参考

- [Input System Update](https://forum.unity.com/threads/input-system-update.508660/page-17#post-5768788)
- [New Input System implementation with DOTS (ECS, Jobs, Burst)](https://answers.unity.com/questions/1795606/new-input-system-implementation-with-dots-ecs-jobs.html)