File size: 7,456 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
using System;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;

namespace UnityEngine.InputSystem.Controls
{
    /// <summary>
    /// A control made up of four discrete, directional buttons. Forms a vector
    /// but can also be addressed as individual buttons.
    /// </summary>
    /// <remarks>
    /// 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 <c>(1,1)</c> for example).
    /// </remarks>
    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)]

        /// <summary>
        /// The button representing the vertical upwards state of the D-Pad.
        /// </summary>
        [InputControl(bit = (int)ButtonBits.Up, displayName = "Up")]
        public ButtonControl up { get; set; }

        /// <summary>
        /// The button representing the vertical downwards state of the D-Pad.
        /// </summary>
        [InputControl(bit = (int)ButtonBits.Down, displayName = "Down")]
        public ButtonControl down { get; set; }

        /// <summary>
        /// The button representing the horizontal left state of the D-Pad.
        /// </summary>
        [InputControl(bit = (int)ButtonBits.Left, displayName = "Left")]
        public ButtonControl left { get; set; }

        /// <summary>
        /// The button representing the horizontal right state of the D-Pad.
        /// </summary>
        [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<ButtonControl>("up");
            down = GetChildControl<ButtonControl>("down");
            left = GetChildControl<ButtonControl>("left");
            right = GetChildControl<ButtonControl>("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);
        }

        /// <summary>
        /// Create a direction vector from the given four button states.
        /// </summary>
        /// <param name="up">Whether button representing the up direction is pressed.</param>
        /// <param name="down">Whether button representing the down direction is pressed.</param>
        /// <param name="left">Whether button representing the left direction is pressed.</param>
        /// <param name="right">Whether button representing the right direction is pressed.</param>
        /// <param name="normalize">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).</param>
        /// <returns>A 2D direction vector.</returns>
        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;
        }

        /// <summary>
        /// Create a direction vector from the given axis states.
        /// </summary>
        /// <param name="up">Axis value representing the up direction.</param>
        /// <param name="down">Axis value representing the down direction.</param>
        /// <param name="left">Axis value representing the left direction.</param>
        /// <param name="right">Axis value representing the right direction.</param>
        /// <returns>A 2D direction vector.</returns>
        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,
        }
    }
}