using System;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
namespace UnityEngine.InputSystem.Controls
{
///
/// A control made up of four discrete, directional buttons. Forms a vector
/// but can also be addressed as individual buttons.
///
///
/// Is stored as four bits by default.
///
/// The vector that is aggregated from the button states is normalized. I.e.
/// even if pressing diagonally, the vector will have a length of 1 (instead
/// of reading something like (1,1) for example).
///
public class DpadControl : Vector2Control
{
[InputControlLayout(hideInUI = true)]
public class DpadAxisControl : AxisControl
{
public int component { get; set; }
protected override void FinishSetup()
{
base.FinishSetup();
component = name == "x" ? 0 : 1;
// Set the state block to be the parent's state block. We don't use that to read
// the axis directly (we call the parent control to do that), but we need to set
// it up the actions know to monitor this memory for changes to the control.
m_StateBlock = m_Parent.m_StateBlock;
}
public override unsafe float ReadUnprocessedValueFromState(void* statePtr)
{
var value = ((DpadControl)m_Parent).ReadUnprocessedValueFromState(statePtr);
return value[component];
}
}
// The DpadAxisControl has it's own logic to read state from the parent dpad.
// The useStateFrom argument here is not actually used by that. The only reason
// it is set up here is to avoid any state bytes being reserved for the DpadAxisControl.
[InputControl(name = "x", layout = "DpadAxis", useStateFrom = "right", synthetic = true)]
[InputControl(name = "y", layout = "DpadAxis", useStateFrom = "up", synthetic = true)]
///
/// The button representing the vertical upwards state of the D-Pad.
///
[InputControl(bit = (int)ButtonBits.Up, displayName = "Up")]
public ButtonControl up { get; set; }
///
/// The button representing the vertical downwards state of the D-Pad.
///
[InputControl(bit = (int)ButtonBits.Down, displayName = "Down")]
public ButtonControl down { get; set; }
///
/// The button representing the horizontal left state of the D-Pad.
///
[InputControl(bit = (int)ButtonBits.Left, displayName = "Left")]
public ButtonControl left { get; set; }
///
/// The button representing the horizontal right state of the D-Pad.
///
[InputControl(bit = (int)ButtonBits.Right, displayName = "Right")]
public ButtonControl right { get; set; }
////TODO: should have X and Y child controls as well
public DpadControl()
{
m_StateBlock.sizeInBits = 4;
m_StateBlock.format = InputStateBlock.FormatBit;
}
protected override void FinishSetup()
{
up = GetChildControl("up");
down = GetChildControl("down");
left = GetChildControl("left");
right = GetChildControl("right");
base.FinishSetup();
}
public override unsafe Vector2 ReadUnprocessedValueFromState(void* statePtr)
{
var upIsPressed = up.ReadValueFromStateWithCaching(statePtr) >= up.pressPointOrDefault;
var downIsPressed = down.ReadValueFromStateWithCaching(statePtr) >= down.pressPointOrDefault;
var leftIsPressed = left.ReadValueFromStateWithCaching(statePtr) >= left.pressPointOrDefault;
var rightIsPressed = right.ReadValueFromStateWithCaching(statePtr) >= right.pressPointOrDefault;
return MakeDpadVector(upIsPressed, downIsPressed, leftIsPressed, rightIsPressed);
}
public override unsafe void WriteValueIntoState(Vector2 value, void* statePtr)
{
var upIsPressed = up.IsValueConsideredPressed(value.y);
var downIsPressed = down.IsValueConsideredPressed(value.y * -1f);
var leftIsPressed = left.IsValueConsideredPressed(value.x * -1f);
var rightIsPressed = right.IsValueConsideredPressed(value.x);
up.WriteValueIntoState(upIsPressed && !downIsPressed ? value.y : 0f, statePtr);
down.WriteValueIntoState(downIsPressed && !upIsPressed ? value.y * -1f : 0f, statePtr);
left.WriteValueIntoState(leftIsPressed && !rightIsPressed ? value.x * -1f : 0f, statePtr);
right.WriteValueIntoState(rightIsPressed && !leftIsPressed ? value.x : 0f, statePtr);
}
///
/// Create a direction vector from the given four button states.
///
/// Whether button representing the up direction is pressed.
/// Whether button representing the down direction is pressed.
/// Whether button representing the left direction is pressed.
/// Whether button representing the right direction is pressed.
/// Whether to normalize the resulting vector. If this is false, vectors in the diagonal
/// directions will have a magnitude of greater than 1. For example, up-left will be (-1,1).
/// A 2D direction vector.
public static Vector2 MakeDpadVector(bool up, bool down, bool left, bool right, bool normalize = true)
{
var upValue = up ? 1.0f : 0.0f;
var downValue = down ? -1.0f : 0.0f;
var leftValue = left ? -1.0f : 0.0f;
var rightValue = right ? 1.0f : 0.0f;
var result = new Vector2(leftValue + rightValue, upValue + downValue);
if (normalize)
{
// If press is diagonal, adjust coordinates to produce vector of length 1.
// pow(0.707107) is roughly 0.5 so sqrt(pow(0.707107)+pow(0.707107)) is ~1.
const float diagonal = 0.707107f;
if (result.x != 0 && result.y != 0)
result = new Vector2(result.x * diagonal, result.y * diagonal);
}
return result;
}
///
/// Create a direction vector from the given axis states.
///
/// Axis value representing the up direction.
/// Axis value representing the down direction.
/// Axis value representing the left direction.
/// Axis value representing the right direction.
/// A 2D direction vector.
public static Vector2 MakeDpadVector(float up, float down, float left, float right)
{
return new Vector2(-left + right, up - down);
}
internal enum ButtonBits
{
Up,
Down,
Left,
Right,
}
}
}