I also have a wrapper around the unity Input system. I call it a âInputDeviceâ and it is updated on every tick where it stores a struct representation of the current state. I always keep a copy of the current and last state in the InputDevice.
There is a method to get either the current or last state.
I also have an InputManager that all input devices get put into. The multiple instances can represent various players, or various modes, or what not.
The InputDevice has a collection attached to it of all the inputs available. There is a special interface for this called âIInputSignatureâ, of which there is the simple forwarding signature that wraps directly around the unity Input class. But there is others such as âAnalogButtonSimulatorâ that makes an analog trigger or stick act like a on/off dual state button. Others like ComboInputSignature that represent a combo that needs to be pressed to register an input. Such as 2 buttons at the same time, or a sequence of buttons in some order.
Here you can see the specific implementation for the player controls in a game Iâm currently working on. Not in the constructor how I set up all the input signatures. And at the bottom I have to custom IInputSignatures specific to this controller.
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using com.spacepuppy;
using com.spacepuppy.Ui;
using com.spacepuppy.Utils;
namespace com.apoc.Ui
{
public class ApocPlayerInputDevice : PlayerInputDevice
{
#region Fields
public const float AXLE_BUTTON_DEADZONE = 0.5f;
public const float MOVEMENT_AXLE_DEADZONE = 0.2f;
public const float MOVEMENT_RADIAL_DEADZONE = 0.2f;
public const float SWORD_AXLE_DEADZONE = 0.2f;
public const float SWORD_RADIAL_DEADZONE = 0.2f;
//InputManager ids
public const string HORIZONTAL = "Horizontal";
public const string VERTICAL = "Vertical";
public const string MOVEMENT = "Movement";
public const string JUMP = "Jump";
public const string GRAB = "Grab";
public const string KICK = "Kick";
public const string SWORDX = "SwordX";
public const string SWORDY = "SwordY";
public const string SWORDSWING = "SwordSwing";
public const string VAULTAIM = "VaultAim";
public enum ApocInputs
{
Movement = 0,
Jump = 1,
Grab = 2,
Kick = 3,
SwordDirection = 4,
SwordSwing = 5,
VaultAim = 6
}
public static string GetInputId(ApocInputs ei)
{
return ei.ToString();
}
private ApocPlayerInputInfo _currentState;
private ApocPlayerInputInfo _lastState;
#endregion
#region CONSTRUCTOR
public ApocPlayerInputDevice(string playerId)
: base(playerId)
{
this.InputSignatures.Add(new DualAxleInputSignature(GetInputId(ApocInputs.Movement), (int)ApocInputs.Movement, HORIZONTAL, VERTICAL)
{
AxleDeadZone = MOVEMENT_AXLE_DEADZONE,
AxleCutoff = DeadZoneCutoff.Scaled,
RadialDeadZone = MOVEMENT_RADIAL_DEADZONE,
RadialCutoff = DeadZoneCutoff.Scaled
});
this.InputSignatures.Add(new ButtonInputSignature(GetInputId(ApocInputs.Jump), (int)ApocInputs.Jump));
this.InputSignatures.Add(new AxleButtonInputSignature(GetInputId(ApocInputs.Grab), (int)ApocInputs.Grab) { AxisButtonDeadZone = AXLE_BUTTON_DEADZONE });
this.InputSignatures.Add(new ButtonInputSignature(GetInputId(ApocInputs.SwordSwing), (int)ApocInputs.SwordSwing));
this.InputSignatures.Add(new SwordInputSignature(ApocInputs.SwordDirection));
this.InputSignatures.Add(new AxleButtonInputSignature(GetInputId(ApocInputs.VaultAim), (int)ApocInputs.VaultAim) { AxisButtonDeadZone = AXLE_BUTTON_DEADZONE });
}
#endregion
#region Properties
#endregion
#region IPlayerInputDevice Interface
public override void Update()
{
base.Update();
_lastState = _currentState;
var info = new ApocPlayerInputInfo();
var mv = this.GetCurrentDualAxleState((int)ApocInputs.Movement);
var swrd = this.GetCurrentDualAxleState((int)ApocInputs.SwordDirection);
info.Movement = mv;
info.SwordPosition = swrd;
info.Jump = this.GetCurrentButtonState((int)ApocInputs.Jump);
info.Grab = this.GetCurrentButtonState((int)ApocInputs.Grab);
info.Kick = this.GetCurrentButtonState((int)ApocInputs.Kick);
info.SwordSwing = this.GetCurrentButtonState((int)ApocInputs.SwordSwing);
info.VaultAim = this.GetCurrentButtonState((int)ApocInputs.VaultAim);
//done
_currentState = info;
}
public ApocPlayerInputInfo GetCurrentState()
{
return _currentState;
}
public ApocPlayerInputInfo GetLastState()
{
return _lastState;
}
#endregion
#region Special Types
private class SwordInputSignature : AbstractInputSignature, IDualAxleInputSignature
{
#region Fields
private Vector2 _current;
#endregion
#region CONSTRUCTOR
public SwordInputSignature(ApocInputs hash)
:base(GetInputId(hash), (int)hash)
{
}
#endregion
#region IDualAxleInputSignature Interface
public Vector2 CurrentState
{
get { return _current; }
}
#endregion
#region IInputSignature Interface
public override void Update(PlayerInputDevice device)
{
var bSwordSwingButton = Input.GetButton(SWORDSWING);
var x = (bSwordSwingButton) ? Input.GetAxis(HORIZONTAL) : Input.GetAxis(SWORDX);
var y = (bSwordSwingButton) ? Input.GetAxis(VERTICAL) : Input.GetAxis(SWORDY);
_current = DeadZoneCutoffUtil.CutoffDualAxis(new Vector2(x, y), SWORD_AXLE_DEADZONE, DeadZoneCutoff.Scaled, SWORD_RADIAL_DEADZONE, DeadZoneCutoff.Scaled).normalized;
}
#endregion
}
[System.Obsolete()]
private class GrappleInputSignature : AbstractInputSignature, IButtonInputSignature
{
#region Fields
private ButtonState _current;
#endregion
#region CONSTRUCTOR
public GrappleInputSignature(ApocInputs hash)
:base(GetInputId(hash), (int)hash)
{
}
#endregion
#region IButtonInputSignature Interface
public ButtonState CurrentState
{
get { return _current; }
}
#endregion
#region IInputSignature Interface
public override void Update(PlayerInputDevice device)
{
var s = _current; //last state
_current = ButtonState.None;
var d = Input.GetAxis(GRAB);
if (d == 0)
{
d = Input.GetAxis("GrabAlt");
if (d < 0) d = 0;
}
if (d > AXLE_BUTTON_DEADZONE)
{
switch (s)
{
case ButtonState.None:
case ButtonState.Released:
_current = ButtonState.Down;
break;
case ButtonState.Down:
case ButtonState.Held:
_current = ButtonState.Held;
break;
}
}
else
{
switch (s)
{
case ButtonState.None:
case ButtonState.Released:
_current = ButtonState.None;
break;
case ButtonState.Down:
case ButtonState.Held:
_current = ButtonState.Released;
break;
}
}
}
#endregion
}
#endregion
}
}
And the struct it feeds out for representing the input state:
using UnityEngine;
using com.spacepuppy;
using com.spacepuppy.Ui;
using com.spacepuppy.Utils;
namespace com.apoc.Ui
{
public struct ApocPlayerInputInfo
{
public Vector2 Movement;
public Vector2 SwordPosition;
public ButtonState Jump;
public ButtonState Grab;
public ButtonState Kick;
public ButtonState SwordSwing;
public ButtonState VaultAim;
public static ApocPlayerInputInfo Empty
{
get { return new ApocPlayerInputInfo(); }
}
public ButtonState GetButtonState(string sId)
{
switch (sId)
{
case ApocPlayerInputDevice.JUMP:
return this.Jump;
case ApocPlayerInputDevice.GRAB:
return this.Grab;
case ApocPlayerInputDevice.KICK:
return this.Kick;
case ApocPlayerInputDevice.SWORDSWING:
return this.SwordSwing;
default:
return ButtonState.None;
}
}
public ButtonState GetButtonState(ApocPlayerInputDevice.ApocInputs input)
{
switch(input)
{
case ApocPlayerInputDevice.ApocInputs.Jump:
return this.Jump;
case ApocPlayerInputDevice.ApocInputs.Grab:
return this.Grab;
case ApocPlayerInputDevice.ApocInputs.Kick:
return this.Kick;
case ApocPlayerInputDevice.ApocInputs.SwordSwing:
return this.SwordSwing;
case ApocPlayerInputDevice.ApocInputs.VaultAim:
return this.VaultAim;
default:
return ButtonState.None;
}
}
}
}