概要
UnityのInputSystemはキーボード、マウス、ゲームパッドなどの入力を一括管理できる代物です。詳しくはマニュアルをご覧ください。
InputSystemは基本的に入力開始時と入力終了時、トリガーやスティックだったらfloat
やvector2
で押し具合を受け取れるものです。
ただアクションゲーム前提だともっと詳しく入力情報ほしいので、さらに入力回数、入力時間や入力開始、入力中、入力終了フレームを計算するクラスを作りました。
環境
Unity 6000.0.23f1
実装
まずは、InputActionAssetを作成してラッパークラスを生成します。
このクラスはActionで関数を登録することで入力時、入力終了時にコールバックを受け取れ、引数で入力値のクラスを受け取ることができます。
InputData
このクラスでは先ほどのコールバック時にInput
を呼び、引数のboolに入力時にはtrue、終了時にはfalseを渡します。
このコールバック時とは別にUpdateで毎フレーム、計算用のPostInput
を呼んで入力回数や入力時間などを計算します。
/// <summary>
/// インプット状態の列挙
/// </summary>
public enum InputState
{
None = 0, // 入力なし
Start = 1, // 入力開始時
Middle = 2, // 入力中
End = 3 // 入力終了時
}
/// <summary>
/// インプット情報を格納する構造体
/// </summary>
public struct InputInfo
{
public bool isInput; // 入力判定
public int inputCount; // 入力回数
public float inputTime; // 入力時間
public InputState inputState; //入力の状態
}
/// <summary>
/// インプット情報計算の抽象クラス
/// </summary>
public abstract class InputData
{
protected bool _isInput;
protected bool _isInputed;
protected InputInfo _inputInfo;
protected bool _isPool;
protected bool _poolInput;
/// <summary>
/// 入力計算のためUpdateで毎フレーム呼ぶ
/// </summary>
public void PostInput()
{
if (!(_isPool & _inputInfo.inputState == InputState.Start))
{
if (_isInput)
{
_inputInfo.inputTime += Time.deltaTime;
_inputInfo.inputState = InputState.Middle;
}
else
{
_inputInfo.inputTime = 0;
_inputInfo.inputState = InputState.None;
}
}
_isInputed = _isInput;
_inputInfo.isInput = _isInput | _isInputed;
}
/// <summary>
/// 先行入力用
/// </summary>
/// <param name="isPool"></param>
public void InputPool(bool isPool)
{
_isPool = isPool;
if (!isPool)
_poolInput = false;
}
public InputInfo inputInfo {get => _inputInfo;}
}
/// <summary>
/// スティック、トリガーなど入力値がある場合
/// </summary>
/// <typeparam name="T"><see cref="UnityEngine.InputSystem.InputAction.CallbackContext.ReadValue{TValue}()"/>の型に合わせる</typeparam>
public class InputValueData<T> : InputData where T : struct
{
private T _value; // 入力値
public void Input(bool isInput, T value)
{
if (isInput)
_value = value;
if (!_isInputed & isInput)
{
++ _inputInfo.inputCount;
_inputInfo.inputState = InputState.Start;
}
if (_isInputed & !isInput)
_inputInfo.inputState = InputState.End;
_isInput = isInput;
_inputInfo.isInput = _isInput | _isInputed;
}
public T value {get => _inputInfo.isInput ? _value : default;}
}
/// <summary>
/// ボタン用
/// </summary>
public class InputButtonData : InputData
{
public void Input(bool isInput)
{
if (!_isInputed & isInput)
{
++ _inputInfo.inputCount;
_inputInfo.inputState = InputState.Start;
}
if (_isInputed & !isInput)
_inputInfo.inputState = InputState.End;
_isInput = isInput;
_inputInfo.isInput = _isInput | _isInputed;
}
}
使い方は以下のようになります。
public class PlayableCharacterInputManager : SingletonMonoBehaviour<PlayableCharacterInputManager>
{
private PlayableCharacterInput _inputs;
private List<InputData> _inputDataList;
private InputValueData<Vector2> _move;
private InputValueData<Vector2> _moveCamera;
private InputButtonData _jump;
private InputButtonData _guard;
private InputButtonData _step;
private InputButtonData _attack;
private InputValueData<float> _shoot;
protected override void OnAwakeProcess()
{
//InputActionAssetのラッパークラスをインスタンス化
_inputs = new PlayableCharacterInput();
//それぞれの入力用のInputDataを作成
_move = new InputValueData<Vector2>();
_moveCamera = new InputValueData<Vector2>();
_jump = new InputButtonData();
_guard = new InputButtonData();
_step = new InputButtonData();
_attack = new InputButtonData();
_shoot = new InputValueData<float>();
_inputDataList = new List<InputData>()
{
_move,
_moveCamera,
_jump,
_guard,
_step,
_attack,
_shoot
};
//CallBackを登録
_inputs.Enable();
_inputs.Player.Move.performed += Move;
_inputs.Player.Move.canceled += Move;
_inputs.Player.MoveCamera.performed += MoveCamera;
_inputs.Player.MoveCamera.canceled += MoveCamera;
_inputs.Player.Jump.performed += Jump;
_inputs.Player.Jump.canceled += Jump;
_inputs.Player.Guard.performed += Guard;
_inputs.Player.Guard.canceled += Guard;
_inputs.Player.Step.performed += Step;
_inputs.Player.Step.canceled += Step;
_inputs.Player.Attack.performed += Attack;
_inputs.Player.Attack.canceled += Attack;
_inputs.Player.Shoot.performed += Shoot;
_inputs.Player.Shoot.canceled += Shoot;
}
protected override void OnDestroyProcess()
{
if (_inputs == null) return;
_inputs.Disable();
_inputs.Dispose();
_inputs.Player.Move.performed -= Move;
_inputs.Player.Move.canceled -= Move;
_inputs.Player.MoveCamera.performed -= MoveCamera;
_inputs.Player.MoveCamera.canceled -= MoveCamera;
_inputs.Player.Jump.performed -= Jump;
_inputs.Player.Jump.canceled -= Jump;
_inputs.Player.Guard.performed -= Guard;
_inputs.Player.Guard.canceled -= Guard;
_inputs.Player.Step.performed -= Step;
_inputs.Player.Step.canceled -= Step;
_inputs.Player.Attack.performed -= Attack;
_inputs.Player.Attack.canceled -= Attack;
_inputs.Player.Shoot.performed -= Shoot;
_inputs.Player.Shoot.canceled -= Shoot;
}
//それぞれのコールバックでInputを呼ぶ
private void Move(InputAction.CallbackContext context)
{
if (context.performed)
_move.Input(true, context.ReadValue<Vector2>());
if (context.canceled)
_move.Input(false, context.ReadValue<Vector2>());
}
private void MoveCamera(InputAction.CallbackContext context)
{
if (context.performed)
_moveCamera.Input(true, context.ReadValue<Vector2>());
if (context.canceled)
_moveCamera.Input(false, context.ReadValue<Vector2>());
}
private void Jump(InputAction.CallbackContext context)
{
if (context.performed)
_jump.Input(true);
if (context.canceled)
_jump.Input(false);
}
private void Guard(InputAction.CallbackContext context)
{
if (context.performed)
_guard.Input(true);
if (context.canceled)
_guard.Input(false);
}
private void Step(InputAction.CallbackContext context)
{
if (context.performed)
_step.Input(true);
if (context.canceled)
_step.Input(false);
}
private void Attack(InputAction.CallbackContext context)
{
if (context.performed)
_attack.Input(true);
if (context.canceled)
_attack.Input(false);
}
private void Shoot(InputAction.CallbackContext context)
{
if (context.performed)
_shoot.Input(true, context.ReadValue<float>());
if (context.canceled)
_shoot.Input(false, context.ReadValue<float>());
}
// Updateで毎フレーム呼ばれるとこでPostInputを呼ぶ
public void PlayableCharacterInputPostTick()
{
foreach (var inputData in _inputDataList)
{
inputData.PostInput();
}
}
public void PlayableCharacterInputPools(bool isInputPool)
{
foreach (var inputData in _inputDataList)
{
inputData.InputPool(isInputPool);
}
}
public InputValueData<Vector2> move {get => _move;}
public InputValueData<Vector2> moveCamera {get => _moveCamera;}
public InputButtonData jump {get => _jump;}
public InputButtonData guard {get => _guard;}
public InputButtonData step {get => _step;}
public InputButtonData attack {get => _attack;}
public InputValueData<float> shoot {get => _shoot;}
}