Tanoda
VRControllerInputModule.cs
Go to the documentation of this file.
1using UnityEngine;
3#if DANA
4using ViveHandTracking;
5#endif
6#if UNITY_WEBGL
7public class VRControllerInputModule : MonoBehaviour
8{
9#endif
10#if !UNITY_WEBGL
11using Valve.VR;
12
13public class VRControllerInputModule : BaseInputModule
14{
15 [Tooltip("A camera mounted on the controller")]
16 public Camera uiCamera;
17
18 [Tooltip("The threshold is a square length at which the cursor will begin drag. Lenght is measured in world coordinates.")]
19 public float dragThreshold = 0.1f;
20
21 public SteamVR_Action_Boolean uiInteractAction = SteamVR_Input.GetAction<SteamVR_Action_Boolean>("InteractUI");
22 // debug
24 private bool m_useDebugText = false;
25 private string[] m_debugStrings = new string[5];
26 // debug
27
28 private bool m_isButtonPressed = false; // true if controller's button is currently pressed, false otherwise
29 private bool m_isButtonPressedChanged = false; // true if controller's button was pressed or released during the last frame
30 private float m_pressedDistance; // Distance the cursor travelled while pressed.
31
32 private Vector2 m_cameraCenter;
33 private Vector3 m_lastRaycastHitPoint;
34 private PointerEventData m_pointerEventData;
35 private bool m_isActive = false;
36 private bool IsLeft = false;
37
38#if DANA
39 public GestureType selectGesture = GestureType.Like;
40#endif
41
42 protected override void Start()
43 {
44 base.Start();
45 if (null != uiCamera)
46 {
47 m_isActive = true;
48 m_cameraCenter = new Vector2(uiCamera.pixelWidth / 2, uiCamera.pixelHeight / 2);
49
50 m_useDebugText = null != uiDebugText;
51 WriteDebug("Camera center: " + m_cameraCenter.ToString());
52 }
53 }
54
55 public override void Process()
56 {
57 if (m_isActive)
58 {
59 bool usedEvent = SendUpdateEventToSelectedObject();
60
61 MyUpdateControllerData();
62 ProcessControllerEvent();
63 }
64 }
65
66 private void MyUpdateControllerData()
67 {
68 m_isButtonPressedChanged = false;
69 if (m_isButtonPressed != VRInputManager.GetIsControllerButtonPressed())
70 {
71 m_isButtonPressedChanged = true;
72 m_isButtonPressed = VRInputManager.GetIsControllerButtonPressed();
73 }
74 }
75
76 private void ProcessControllerEvent()
77 {
78 PointerEventData eventData = GetPointerEventData();
79
80 ProcessPress(eventData);
81 ProcessMove(eventData);
82 ProcessDrag(eventData);
83 }
84
85 private PointerEventData GetPointerEventData()
86 {
87 // Currently the module is made for a single controller with one button.
88 // That means that we only have a single pointer.
89 if (null == m_pointerEventData)
90 m_pointerEventData = new PointerEventData(eventSystem);
91
92 if (VRInputManager.GetControllerActive())
93 {
94 m_pointerEventData.position = m_cameraCenter;
95
96 m_pointerEventData.scrollDelta = Vector2.zero;
97 m_pointerEventData.button = PointerEventData.InputButton.Left;
98 eventSystem.RaycastAll(m_pointerEventData, m_RaycastResultCache);
99 var raycast = FindFirstRaycast(m_RaycastResultCache);
100
101 // Delta is used to define if the cursor was moved.
102 // We will also use it for drag threshold calculation, for which we'll store world distance
103 // between the last and the current raycasts (will actually use sqrmagnitude for its speed).
104 Ray ray = new Ray(uiCamera.transform.position, uiCamera.transform.forward);
105 Vector3 hitPoint = ray.GetPoint(raycast.distance);
106 m_pointerEventData.delta = new Vector2((hitPoint - m_lastRaycastHitPoint).sqrMagnitude, 0);
107 m_lastRaycastHitPoint = hitPoint;
108
109 m_pointerEventData.pointerCurrentRaycast = raycast;
110
111 // Debug
112 if (m_RaycastResultCache.Count > 0)
113 WriteDebug("Raycast hit " + raycast.gameObject.name);
114
115 if (raycast.isValid && raycast.gameObject.gameObject && raycast.gameObject.GetComponent<PointerInteractable>())
116 {
117#if DANA
118 GestureResult result = IsLeft ? GestureProvider.LeftHand : GestureProvider.RightHand;
119 if (result.gesture == selectGesture)
120 {
121 raycast.gameObject.GetComponent<PointerInteractable>().OnPointerClick(m_pointerEventData);
122 }
123#else
124 if (uiInteractAction.stateDown)
125 {
126 raycast.gameObject.GetComponent<PointerInteractable>().OnPointerDown(m_pointerEventData);
127 }
128
129 if (uiInteractAction.stateUp)
130 {
131 raycast.gameObject.GetComponent<PointerInteractable>().OnPointerClick(m_pointerEventData);
132 }
133#endif
134 }
135
136 m_RaycastResultCache.Clear();
137 }
138
139 return m_pointerEventData;
140 }
141
142 // Copied from PointerInputModule
143 private void ProcessDrag(PointerEventData eventData)
144 {
145 WriteDebug(eventData.delta.sqrMagnitude.ToString());
146
147 // If pointer is not moving or if a button is not pressed (or pressed control did not return drag handler), do nothing
148 if (!eventData.IsPointerMoving() || eventData.pointerDrag == null)
149 return;
150
151 // We are eligible for drag. If drag did not start yet, add drag distance
152 if (!eventData.dragging)
153 {
154 m_pressedDistance += eventData.delta.x;
155
156 if (ShouldStartDrag(eventData))
157 {
158 ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.beginDragHandler);
159 eventData.dragging = true;
160 }
161 }
162
163 // Drag notification
164 if (eventData.dragging)
165 {
166 // Before doing drag we should cancel any pointer down state
167 // And clear selection!
168 if (eventData.pointerPress != eventData.pointerDrag)
169 {
170 ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
171
172 eventData.eligibleForClick = false;
173 eventData.pointerPress = null;
174 eventData.rawPointerPress = null;
175 }
176 ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.dragHandler);
177 }
178 }
179
180 private bool ShouldStartDrag(PointerEventData eventData)
181 {
182 return !m_isButtonPressedChanged && (m_pressedDistance > dragThreshold);
183 }
184
185 // Copied from PointerInputModule
186 private void ProcessMove(PointerEventData eventData)
187 {
188 var targetGO = eventData.pointerCurrentRaycast.gameObject;
189 HandlePointerExitAndEnter(eventData, targetGO);
190 }
191
192 // modified StandaloneInputModule
193 private void ProcessPress(PointerEventData eventData)
194 {
195 var currentOverGo = eventData.pointerCurrentRaycast.gameObject;
196
197 // PointerDown notification
198 if (MyIsButtonPressedThisFrame())
199 {
200 eventData.eligibleForClick = true;
201 eventData.delta = Vector2.zero;
202 eventData.useDragThreshold = true;
203 eventData.pressPosition = eventData.position;
204 eventData.pointerPressRaycast = eventData.pointerCurrentRaycast;
205
206 DeselectIfSelectionChanged(currentOverGo, eventData);
207
208 var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.pointerDownHandler);
209
210 // didnt find a press handler... search for a click handler
211 if (newPressed == null)
212 newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
213
214 eventData.pointerPress = newPressed; // TODO:remove?
215 m_pressedDistance = 0;
216 eventData.rawPointerPress = currentOverGo;
217
218 eventData.clickTime = Time.unscaledTime;
219
220 // Save the drag handler as well
221 eventData.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
222
223 if (eventData.pointerDrag != null)
224 ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.initializePotentialDrag);
225 }
226
227 // PointerUp notification
228 if (MyIsButtonReleasedThisFrame())
229 {
230 ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
231
232 // see if we button up on the same element that we clicked on...
233 var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
234
235 // PointerClick and Drop events
236 if (eventData.pointerPress == pointerUpHandler && eventData.eligibleForClick)
237 {
238 ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerClickHandler);
239 }
240 else if (eventData.pointerDrag != null && eventData.dragging)
241 {
242 ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.dropHandler);
243 }
244
245 eventData.eligibleForClick = false;
246 eventData.pointerPress = null;
247 m_pressedDistance = 0; // just in case
248 eventData.rawPointerPress = null;
249
250 if (eventData.pointerDrag != null && eventData.dragging)
251 ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.endDragHandler);
252
253 eventData.dragging = false;
254 eventData.pointerDrag = null;
255
256 // redo pointer enter / exit to refresh state
257 // so that if we hovered over something that ignored it before
258 // due to having pressed on something else
259 // it now gets it.
260 if (currentOverGo != eventData.pointerEnter)
261 {
262 HandlePointerExitAndEnter(eventData, null);
263 HandlePointerExitAndEnter(eventData, currentOverGo);
264 }
265 }
266 }
267
268 // Copied from PointerInputModule
269 private void DeselectIfSelectionChanged(GameObject currentOverGo, BaseEventData pointerEvent)
270 {
271 // Selection tracking
272 var selectHandlerGO = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo);
273 // if we have clicked something new, deselect the old thing
274 // leave 'selection handling' up to the press event though.
275 if (selectHandlerGO != eventSystem.currentSelectedGameObject)
276 eventSystem.SetSelectedGameObject(null, pointerEvent);
277 }
278
279 // Copied from StandaloneInputModule
280 private bool SendUpdateEventToSelectedObject()
281 {
282 if (eventSystem.currentSelectedGameObject == null)
283 return false;
284
285 var data = GetBaseEventData();
286 ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);
287 return data.used;
288 }
289
290 private bool MyIsButtonReleasedThisFrame()
291 {
292 return (m_isButtonPressedChanged && !m_isButtonPressed);
293 }
294
295 private bool MyIsButtonPressedThisFrame()
296 {
297 return (m_isButtonPressedChanged && m_isButtonPressed);
298 }
299
300 // Debug
301 private void WriteDebug(string text)
302 {
303 if (!m_useDebugText)
304 return;
305
306 m_debugStrings[4] = m_debugStrings[3];
307 m_debugStrings[3] = m_debugStrings[2];
308 m_debugStrings[2] = m_debugStrings[1];
309 m_debugStrings[1] = m_debugStrings[0];
310 m_debugStrings[0] = text;
311 uiDebugText.text = string.Format("{0}\n{1}\n{2}\n{3}\n{4}", m_debugStrings[0], m_debugStrings[1], m_debugStrings[2], m_debugStrings[3], m_debugStrings[4]);
312 }
313#endif
314}
UnityEngine.UI.Text uiDebugText
SteamVR_Action_Boolean uiInteractAction