IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
HierarchicalInputModule.cs
1 using UnityEngine;
2 using UnityEngine.EventSystems;
3 using UnityEngine.UI;
4 using System.Collections.Generic;
5 using UI;
6 
11 public class HierarchicalInputModule : BaseInputModule {
12 
13  private GameObject activeGameObject = null;
14  private GameObject previousActiveGameObject = null;
15 
16  private LineRenderer lineRenderer;
17 
18  private CustomEventData eventData;
19 
20  CustomEventData leftData;
21  CustomEventData rightData;
22  CustomEventData middleData;
23  CustomEventData triggerData;
24 
25  private LayerMask layerMask;
26 
27  private Vector2 lastTextureCoord;
28  private Vector3 lastHitWorldPos;
30  private Vector2 fakeUIScreenPosition;
31 
32  private RaycastHit raycastHit;
33  private RaycastResult raycastResult;
34 
35  private bool isPointerOverUI = false;
36  private bool isPointerOverPlatformUI = false;
37 
38  protected override void Start()
39  {
40  lineRenderer = this.GetComponent<LineRenderer>();
41 
42  eventData = new CustomEventData (eventSystem);
43  leftData = new CustomEventData (eventSystem);
44  rightData = new CustomEventData (eventSystem);
45  middleData = new CustomEventData (eventSystem);
46  triggerData = new CustomEventData (eventSystem);
47 
48  //set Layer Mask on Default
49  resetLayerMask ();
50  }
51 
52  public void resetLayerMask() {
53  //Sets every Layer to 1
54  //activates every Layer
55  layerMask = -1;
56 
57  }
58 
59  public void disableLayer(string layer) {
60  //Disables the Given Layer
61  int l = LayerMask.NameToLayer (layer);
62  layerMask.value &= ~(1 << l);
63  }
64 
65  public void enableLayer(string layer) {
66  //Enables the given Layer
67  int l = LayerMask.NameToLayer (layer);
68  layerMask.value |= (1 << l);
69  }
70 
72  public override void UpdateModule()
73  {
74  previousActiveGameObject = activeGameObject;
75  activeGameObject = null;
76 
77  Vector2 hitTextureCoord = Vector2.zero;
78  int hitTriangleIndex = -1;
79  Vector3 hitWorldPos = Vector3.zero;
80 
81  isPointerOverUI = false;
82  isPointerOverPlatformUI = false;
83 
84  InputDeviceManager idm = InputDeviceManager.instance;
85  if( idm.currentInputDevice != null )
86  {
87  // Get a ray from the current input device (mouse or controller):
88  Ray ray = idm.currentInputDevice.createRay ();
89  lineRenderer.SetPosition (0, ray.origin);
90 
91 
92  // 1. First, cast this ray into the scene:
93  int layerMaskLocal = ~(1 << LayerMask.NameToLayer ("UI")); // Everything but UI Elements (but UIMesh could be hit!)
94  //apply global layer changes to LayerMask used for raycast
95  layerMaskLocal &= layerMask.value;
96  if (Physics.Raycast (ray, out raycastHit, Mathf.Infinity, layerMaskLocal)) {
97 
98  activeGameObject = raycastHit.transform.gameObject;
99  hitTextureCoord = raycastHit.textureCoord;
100  hitWorldPos = raycastHit.point;
101  hitTriangleIndex = raycastHit.triangleIndex;
102 
103  lineRenderer.SetPosition (1, raycastHit.point);
104 
105  // 2. If the UI Mesh was hit, check if the mouse is actually over a UI element:
106  if (raycastHit.transform.gameObject == Platform.instance.UIMesh) {
107 
108  if (UIRaycast (raycastHit.textureCoord, out raycastResult)) {
109  activeGameObject = raycastResult.gameObject;
110  lineRenderer.SetPosition (1, raycastHit.point);
111  hitWorldPos = raycastResult.worldPosition;
112  isPointerOverUI = true;
113  isPointerOverPlatformUI = true;
114  Vector2 localPoint;
115  RectTransform rt = activeGameObject.GetComponent<RectTransform> ();
116  RectTransformUtility.ScreenPointToLocalPointInRectangle ( rt,
117  raycastResult.screenPosition, UI.Core.instance.UICamera, out localPoint);
118 
119  hitTextureCoord = new Vector2 ((localPoint.x - rt.rect.min.x) / rt.rect.width,
120  (localPoint.y - rt.rect.min.y) / rt.rect.height);
121  hitTriangleIndex = raycastHit.triangleIndex;
122 
123  } else {
124  // 3. If no UI element was hit, raycast again but ignore the UIMesh:
125  layerMaskLocal = ~(1 << LayerMask.NameToLayer ("UIMesh"));
126  layerMaskLocal &= layerMask.value;
127  if (Physics.Raycast (ray, out raycastHit, Mathf.Infinity, layerMaskLocal)) {
128  activeGameObject = raycastHit.transform.gameObject;
129  lineRenderer.SetPosition (1, raycastHit.point);
130 
131  hitTextureCoord = raycastHit.textureCoord;
132  hitTriangleIndex = raycastHit.triangleIndex;
133  hitWorldPos = raycastHit.point;
134  } else {
135  activeGameObject = null;
136  }
137  }
138  }
139 
140  if (raycastHit.transform != null) { // If any 3D object was hit
141  if (raycastHit.transform.gameObject.layer == LayerMask.NameToLayer ("UITool") ||
142  raycastHit.transform.gameObject.layer == LayerMask.NameToLayer ("UIOrgans") ||
143  raycastHit.transform.gameObject.layer == LayerMask.NameToLayer ("UIAnnotationEdit") ) { // If the hit Object was a UI element
144  if (raycastHit.transform.GetComponent<CanvasRaycaster> () != null) {
145  RectTransform tf = raycastHit.transform.GetComponent<RectTransform> ();
146  PointerEventData data = new PointerEventData (EventSystem.current);
147  data.position = new Vector2 (tf.InverseTransformPoint (raycastHit.point).x, tf.InverseTransformPoint (raycastHit.point).y);
148  List<RaycastResult> raycastResults = new List<RaycastResult> ();
149  raycastHit.transform.GetComponent<CanvasRaycaster> ().Raycast (data, raycastResults);
150  if (raycastResults.Count > 0) {
151  raycastResult = raycastResults [0];
152  activeGameObject = raycastResult.gameObject;
153  lineRenderer.SetPosition (1, raycastHit.point);
154  hitWorldPos = raycastResult.worldPosition;
155  data.pointerCurrentRaycast = raycastResult;
156  isPointerOverUI = true;
157  if (raycastHit.transform.gameObject.layer == LayerMask.NameToLayer ("UITool")) {
158  isPointerOverPlatformUI = true;
159  }
160  }
161  }
162  }
163  }
164  }
165 
166  // Send end point of line:
167  if (activeGameObject != null) {
168  lineRenderer.SetPosition (1, raycastHit.point);
169  } else {
170  // If nothing was hit at all, just show a very long ray:
171  lineRenderer.SetPosition (1, ray.origin + ray.direction*300f);
172  }
173 
174  if (activeGameObject == previousActiveGameObject) {
175  eventData.delta = hitTextureCoord - lastTextureCoord;
176  eventData.delta3D = hitWorldPos - lastHitWorldPos;
177  } else {
178  eventData.delta = Vector2.zero;
179  eventData.delta3D = Vector3.zero;
180  }
181  idm.currentInputDevice.setTexCoordDelta (eventData.delta);
182  idm.currentInputDevice.set3DDelta (eventData.delta3D);
183  }
184 
185  //Debug.Log ("Active: " + activeGameObject + " previous: " + previousActiveGameObject);
186 
187  lastTextureCoord = hitTextureCoord;
188  lastHitWorldPos = hitWorldPos;
189  eventData.textureCoord = hitTextureCoord;
190  eventData.hitTriangleIndex = hitTriangleIndex;
191  }
192 
194  private bool UIRaycast( Vector2 screenPoint, out RaycastResult result )
195  {
196  Camera uiCamera = UI.Core.instance.UICamera;
197  fakeUIScreenPosition = new Vector2 (
198  screenPoint.x * uiCamera.targetTexture.width,
199  screenPoint.y * uiCamera.targetTexture.height);
200  //Ray uiRay = UI.Core.instance.UICamera.ScreenPointToRay ( rayOrigin );
201 
202  //int uiLayer = LayerMask.NameToLayer ("UI");
203 
204  PointerEventData data = new PointerEventData (EventSystem.current);
205  data.position = new Vector2 (fakeUIScreenPosition.x, fakeUIScreenPosition.y);
206  List<RaycastResult> raycastResults = new List<RaycastResult> ();
207  EventSystem.current.RaycastAll( data, raycastResults );
208  if (raycastResults.Count > 0) {
209  int UILayer = LayerMask.NameToLayer ("UI");
210  foreach (RaycastResult r in raycastResults) {
211  if( r.gameObject != null && r.gameObject.layer == UILayer) {
212  result = r;
213  return true;
214  }
215  }
216  }
217  result = new RaycastResult ();
218  return false;
219  }
220 
222  public override void Process()
223  {
224  UI.Core.instance.setPointerIsOnUI (isPointerOverUI);
225  UI.Core.instance.setPointerIsOnPlatformUI (isPointerOverPlatformUI);
226 
227  SendUpdateEventToSelectedObject();
228 
229  InputDeviceManager idm = InputDeviceManager.instance;
230 
231  // Get a list of buttons from the input module, which tells me if buttons are pressed, released or not changed:
232  ButtonInfo buttonInfo;
233  if (idm.currentInputDevice != null) {
234  buttonInfo = idm.currentInputDevice.updateButtonInfo ();
235  } else {
236  buttonInfo = new ButtonInfo ();
237  }
238 
239 
240  // ----------------------------------
241  // Fill the EventData with current information from the last hit:
242  eventData.scrollDelta = idm.currentInputDevice.getScrollDelta();
243  if (InputDeviceManager.instance.currentInputDevice.getDeviceType () == InputDeviceManager.InputDeviceType.ViveController)
244  eventData.scrollDelta *= 100;
245  //eventData.position = fakeUIScreenPosition;
246  // Because we're interested in the 3D positions only, copy it over to the 2D UI hit result:
247  raycastResult.worldPosition = raycastHit.point;
248  raycastResult.worldNormal = raycastHit.normal;
249  eventData.pointerCurrentRaycast = raycastResult;
250  eventData.position = raycastResult.screenPosition;
251 
252  HandlePointerExitAndEnter (eventData, activeGameObject);
253  if (activeGameObject != null) {
254  ExecuteEvents.ExecuteHierarchy (activeGameObject, eventData, CustomEvents.pointerHoverHandler);
255  }
256 
257 
258  CopyFromTo (eventData, leftData);
259  CopyFromTo (eventData, rightData);
260  CopyFromTo (eventData, middleData);
261  CopyFromTo (eventData, triggerData);
262  leftData.button = PointerEventData.InputButton.Left;
263  rightData.button = PointerEventData.InputButton.Right;
264  middleData.button = PointerEventData.InputButton.Middle;
265  triggerData.button = PointerEventData.InputButton.Left; // Treat trigger like a left click!
266 
267 
268  // Stop any selection if anything was pressed:
269  //if( AnyPressed( buttonInfo ) )
270  //{
271  //EventSystem.current.SetSelectedGameObject( null, eventData );
272  //}
273 
274  // ----------------------------------
275  // Handle left click:
276  //if (activeGameObject) {
277  HandleButton (ButtonType.Left, buttonInfo.buttonStates [ButtonType.Left], leftData, true);
278  ProcessDrag (leftData);
279  //}
280 
281 
282  // ----------------------------------
283  // Handle right click:
284  //if (activeGameObject) {
285  HandleButton (ButtonType.Right, buttonInfo.buttonStates [ButtonType.Right], rightData, false);
286  ProcessDrag (rightData);
287  //}
288 
289  // ----------------------------------
290  // Handle middle click:
291  //if (activeGameObject) {
292  HandleButton (ButtonType.Middle, buttonInfo.buttonStates [ButtonType.Middle], middleData, false);
293  ProcessDrag (middleData);
294  //}
295 
296 
297  // ----------------------------------
298  // Handle trigger:
299  //if (activeGameObject) {
300  HandleButton (ButtonType.Trigger, buttonInfo.buttonStates [ButtonType.Trigger], triggerData, true);
301  ProcessDrag (triggerData);
302  //}
303 
304 
305  // ----------------------------------
306  // Handle scroll:
307 
308  if (!Mathf.Approximately(eventData.scrollDelta.sqrMagnitude, 0.0f)) {
309  //Debug.Log ("Scrolling: " + eventData.scrollDelta + " " + activeGameObject.name);
310  //ExecuteEvents.ExecuteHierarchy(activeGameObject, eventData, ExecuteEvents.scrollHandler);
311 
312  var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler> (activeGameObject);
313  ExecuteEvents.ExecuteHierarchy(scrollHandler, eventData, ExecuteEvents.scrollHandler);
314  //ExecuteEvents.ExecuteHierarchy (activeGameObject, eventData, ExecuteEvents.scrollHandler);
315  }
316 
317  }
318 
319  private void HandleButton( ButtonType buttonType, PointerEventData.FramePressState buttonState, CustomEventData eventData, bool allowDragging )
320  {
321  GameObject currentOverGo = activeGameObject;
322 
323  // PointerDown notification
324  if (buttonState == PointerEventData.FramePressState.Pressed || buttonState == PointerEventData.FramePressState.PressedAndReleased )
325  {
326  eventData.eligibleForClick = true;
327  eventData.delta = Vector2.zero;
328  eventData.dragging = false;
329  eventData.useDragThreshold = true;
330  eventData.pressPosition = eventData.position;
331  eventData.pointerPressRaycast = eventData.pointerCurrentRaycast;
332 
333  DeselectIfSelectionChanged(currentOverGo, eventData);
334 
335  // search for the control that will receive the press
336  // if we can't find a press handler set the press
337  // handler to be what would receive a click.
338  var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.pointerDownHandler);
339 
340  // didnt find a press handler... search for a click handler
341  if (newPressed == null)
342  newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
343 
344  // Debug.Log("Pressed: " + newPressed);
345 
346  float time = Time.unscaledTime;
347 
348  if (newPressed == eventData.lastPress)
349  {
350  var diffTime = time - eventData.clickTime;
351  if (diffTime < 0.3f)
352  ++eventData.clickCount;
353  else
354  eventData.clickCount = 1;
355 
356  eventData.clickTime = time;
357  }
358  else
359  {
360  eventData.clickCount = 1;
361  }
362 
363  eventData.pointerPress = newPressed;
364  eventData.rawPointerPress = currentOverGo;
365 
366  eventData.clickTime = time;
367 
368  // Save the drag handler as well
369  eventData.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
370 
371  if (eventData.pointerDrag != null)
372  ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.initializePotentialDrag);
373  }
374 
375  // PointerUp notification
376  if (buttonState == PointerEventData.FramePressState.Released || buttonState == PointerEventData.FramePressState.PressedAndReleased )
377  {
378  // Debug.Log("Executing pressup on: " + pointer.pointerPress);
379  ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
380 
381  // Debug.Log("KeyCode: " + pointer.eventData.keyCode);
382 
383  // see if we mouse up on the same element that we clicked on...
384  var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
385 
386  // PointerClick and Drop events
387  if (eventData.pointerPress == pointerUpHandler && eventData.eligibleForClick)
388  {
389  ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerClickHandler);
390  }
391  else if (eventData.pointerDrag != null && eventData.dragging)
392  {
393  ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.dropHandler);
394  }
395 
396  eventData.eligibleForClick = false;
397  eventData.pointerPress = null;
398  eventData.rawPointerPress = null;
399 
400  if (eventData.pointerDrag != null && eventData.dragging)
401  ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.endDragHandler);
402 
403  eventData.dragging = false;
404  eventData.pointerDrag = null;
405 
406  // redo pointer enter / exit to refresh state
407  // so that if we moused over somethign that ignored it before
408  // due to having pressed on something else
409  // it now gets it.
410  if (currentOverGo != eventData.pointerEnter)
411  {
412  HandlePointerExitAndEnter(eventData, null);
413  HandlePointerExitAndEnter(eventData, currentOverGo);
414  }
415  }
416  }
417 
418  protected virtual void ProcessDrag(PointerEventData pointerEvent)
419  {
420  bool moving = pointerEvent.IsPointerMoving();
421 
422  if (moving && pointerEvent.pointerDrag != null
423  && !pointerEvent.dragging
424  && ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.position, eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold))
425  {
426  ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler);
427  pointerEvent.dragging = true;
428  }
429 
430  // Drag notification
431  if (pointerEvent.dragging && moving && pointerEvent.pointerDrag != null)
432  {
433  // Before doing drag we should cancel any pointer down state
434  // And clear selection!
435  if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
436  {
437  ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
438 
439  pointerEvent.eligibleForClick = false;
440  pointerEvent.pointerPress = null;
441  pointerEvent.rawPointerPress = null;
442  }
443  ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.dragHandler);
444  }
445  }
446  private static bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold)
447  {
448  if (!useDragThreshold)
449  return true;
450 
451  return (pressPos - currentPos).sqrMagnitude >= threshold * threshold;
452  }
453 
454  private bool AnyPressed( ButtonInfo buttonInfo )
455  {
456  foreach (KeyValuePair<ButtonType, PointerEventData.FramePressState> entry in buttonInfo.buttonStates) {
457  if (entry.Value == PointerEventData.FramePressState.Pressed || entry.Value == PointerEventData.FramePressState.PressedAndReleased) {
458  return true;
459  }
460  }
461  return false;
462  }
463 
464  protected bool SendUpdateEventToSelectedObject()
465  {
466  if (eventSystem.currentSelectedGameObject == null)
467  return false;
468 
469  var data = GetBaseEventData();
470  ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);
471  return data.used;
472  }
473 
475  public override void ActivateModule()
476  {
477  }
478 
479  public override void DeactivateModule()
480  {
481  }
482 
483  public override bool IsPointerOverGameObject(int pointerId)
484  {
485  // Don't care about the pointer ID (we only have one pointer at the time being):
486  return (activeGameObject != null);
487  }
488 
489  public override bool IsModuleSupported()
490  {
491  // sure!
492  return true;
493  }
494 
495  public override bool ShouldActivateModule()
496  {
497  // sure!
498  return true;
499  }
500 
501  protected void CopyFromTo(CustomEventData @from, CustomEventData @to)
502  {
503  @to.position = @from.position;
504  @to.delta = @from.delta;
505  @to.scrollDelta = @from.scrollDelta;
506  @to.pointerCurrentRaycast = @from.pointerCurrentRaycast;
507  @to.pointerEnter = @from.pointerEnter;
508  @to.textureCoord = @from.textureCoord;
509  @to.hitTriangleIndex = @from.hitTriangleIndex;
510  }
511 
512  protected void DeselectIfSelectionChanged(GameObject currentOverGo, BaseEventData pointerEvent)
513  {
514  // Selection tracking
515  var selectHandlerGO = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo);
516  // if we have clicked something new, deselect the old thing
517  // leave 'selection handling' up to the press event though.
518  if (selectHandlerGO != eventSystem.currentSelectedGameObject)
519  eventSystem.SetSelectedGameObject(null, pointerEvent);
520  }
521 }
override void ActivateModule()
Called when this module is activated.
override void Process()
Called every frame after UpdateModule (but only if this module is active!)
override void UpdateModule()
Called every frame by the event system.