VR模拟枪支打靶,消灭鬼怪,换弹以及上弦等等硬核枪支操作。 使用HTCVive设备,开启SteamVR进行游玩。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

651 lines
24 KiB

3 years ago
//========= Copyright 2016-2018, HTC Corporation. All rights reserved. ===========
using HTC.UnityPlugin.Utility;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
namespace HTC.UnityPlugin.Pointer3D
{
public class Pointer3DInputModule : BaseInputModule
{
private static Pointer3DInputModule instance;
private static bool isApplicationQuitting = false;
private static readonly IndexedSet<Pointer3DRaycaster> raycasters = new IndexedSet<Pointer3DRaycaster>();
private static IndexedSet<Pointer3DRaycaster> processingRaycasters = new IndexedSet<Pointer3DRaycaster>();
private static int validEventDataId = PointerInputModule.kFakeTouchesId - 1;
#if UNITY_5_5_OR_NEWER
private bool m_hasFocus;
#endif
private int m_processedFrame;
// Pointer3DInputModule has it's own RaycasterManager and Pointer3DRaycaster doesn't share with other input modules.
// So coexist with other input modules is by default and reasonable?
public bool coexist = true;
[NonSerialized]
[Obsolete("Use Pointer3DRaycaster.dragThreshold instead")]
public float dragThreshold = 0.02f;
[NonSerialized]
[Obsolete("Use Pointer3DRaycaster.clickInterval instead")]
public float clickInterval = 0.3f;
public static Vector2 ScreenCenterPoint { get { return new Vector2(Screen.width * 0.5f, Screen.height * 0.5f); } }
public static bool Active { get { return instance != null; } }
public static Pointer3DInputModule Instance
{
get
{
Initialize();
return instance;
}
}
protected virtual void OnApplicationQuit()
{
isApplicationQuitting = true;
}
public override bool ShouldActivateModule()
{
if (!base.ShouldActivateModule()) { return false; }
// if coexist with other inputmodule is enabled, tell EventSystem not to active and let other module active first
return !coexist;
}
#if UNITY_5_5_OR_NEWER
protected virtual void OnApplicationFocus(bool hasFocus)
{
m_hasFocus = hasFocus;
}
protected virtual void Update()
{
// EventSystem is paused when application lost focus, so force ProcessRaycast here
if (isActiveAndEnabled && !m_hasFocus)
{
if (EventSystem.current.currentInputModule == this || coexist)
{
ProcessRaycast();
}
}
}
#endif
public override void UpdateModule()
{
Initialize();
if (isActiveAndEnabled && EventSystem.current.currentInputModule != this && coexist)
{
ProcessRaycast();
}
}
public static void Initialize()
{
if (Active || isApplicationQuitting) { return; }
var instances = FindObjectsOfType<Pointer3DInputModule>();
if (instances.Length > 0)
{
instance = instances[0];
if (instances.Length > 1) { Debug.LogWarning("Multiple Pointer3DInputModule not supported!"); }
}
if (!Active)
{
EventSystem eventSystem = EventSystem.current;
if (eventSystem == null)
{
eventSystem = FindObjectOfType<EventSystem>();
}
if (eventSystem == null)
{
eventSystem = new GameObject("[EventSystem]").AddComponent<EventSystem>();
}
if (eventSystem == null)
{
Debug.LogWarning("EventSystem not found or create fail!");
return;
}
instance = eventSystem.gameObject.AddComponent<Pointer3DInputModule>();
}
if (Active)
{
DontDestroyOnLoad(instance.gameObject);
}
}
public static void AssignPointerId(Pointer3DEventData eventData)
{
eventData.pointerId = validEventDataId--;
}
public override void Process()
{
Initialize();
if (isActiveAndEnabled)
{
ProcessRaycast();
}
}
protected override void OnDisable()
{
base.OnDisable();
if (Active && processingRaycasters.Count == 0)
{
for (var i = raycasters.Count - 1; i >= 0; --i)
{
instance.CleanUpRaycaster(raycasters[i]);
}
}
}
public static readonly Comparison<RaycastResult> defaultRaycastComparer = RaycastComparer;
private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
if (lhs.module != rhs.module)
{
if (lhs.module.eventCamera != null && rhs.module.eventCamera != null && lhs.module.eventCamera.depth != rhs.module.eventCamera.depth)
{
// need to reverse the standard compareTo
if (lhs.module.eventCamera.depth < rhs.module.eventCamera.depth) { return 1; }
if (lhs.module.eventCamera.depth == rhs.module.eventCamera.depth) { return 0; }
return -1;
}
if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
{
return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);
}
if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
{
return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
}
}
if (lhs.sortingLayer != rhs.sortingLayer)
{
// Uses the layer value to properly compare the relative order of the layers.
var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
return rid.CompareTo(lid);
}
if (lhs.sortingOrder != rhs.sortingOrder)
{
return rhs.sortingOrder.CompareTo(lhs.sortingOrder);
}
if (!Mathf.Approximately(lhs.distance, rhs.distance))
{
return lhs.distance.CompareTo(rhs.distance);
}
if (lhs.depth != rhs.depth)
{
return rhs.depth.CompareTo(lhs.depth);
}
return lhs.index.CompareTo(rhs.index);
}
public static void AddRaycaster(Pointer3DRaycaster raycaster)
{
if (raycaster == null) { return; }
Initialize();
raycasters.AddUnique(raycaster);
}
public static void RemoveRaycaster(Pointer3DRaycaster raycaster)
{
if (!raycasters.Remove(raycaster)) { return; }
if (!processingRaycasters.Contains(raycaster) && Active)
{
Instance.CleanUpRaycaster(raycaster);
}
}
[Obsolete("Use RemoveRaycaster instead")]
public static void RemoveRaycasters(Pointer3DRaycaster raycaster) { RemoveRaycaster(raycaster); }
protected void CleanUpRaycaster(Pointer3DRaycaster raycaster)
{
if (raycaster == null) { return; }
var hoverEventData = raycaster.HoverEventData;
// buttons event
for (int i = 0, imax = raycaster.ButtonEventDataList.Count; i < imax; ++i)
{
var buttonEventData = raycaster.ButtonEventDataList[i];
if (buttonEventData == null) { continue; }
buttonEventData.Reset();
if (buttonEventData.pressPrecessed)
{
ProcessPressUp(buttonEventData);
HandlePressExitAndEnter(buttonEventData, null);
}
if (buttonEventData.pointerEnter != null)
{
if (buttonEventData == hoverEventData)
{
// perform exit event only for hover event data
HandlePointerExitAndEnter(buttonEventData, null);
}
else
{
buttonEventData.pointerEnter = null;
}
}
}
raycaster.CleanUpRaycast();
for (int i = 0, imax = raycaster.ButtonEventDataList.Count; i < imax; ++i)
{
raycaster.ButtonEventDataList[i].pointerPressRaycast = default(RaycastResult);
raycaster.ButtonEventDataList[i].pointerCurrentRaycast = default(RaycastResult);
}
}
protected virtual void ProcessRaycast()
{
if (m_processedFrame == Time.frameCount) { return; }
m_processedFrame = Time.frameCount;
// use another list to iterate raycasters
// incase that raycasters may changed during this process cycle
for (int i = 0, imax = raycasters.Count; i < imax; ++i)
{
var r = raycasters[i];
if (r != null)
{
processingRaycasters.Add(r);
}
}
for (var i = processingRaycasters.Count - 1; i >= 0; --i)
{
var raycaster = processingRaycasters[i];
if (raycaster == null) { continue; }
raycaster.Raycast();
var result = raycaster.FirstRaycastResult();
// prepare raycaster value
var scrollDelta = raycaster.GetScrollDelta();
var raycasterPos = raycaster.transform.position;
var raycasterRot = raycaster.transform.rotation;
var hoverEventData = raycaster.HoverEventData;
if (hoverEventData == null) { continue; }
// gen shared data and put in hover event
hoverEventData.Reset();
hoverEventData.delta = Vector2.zero;
hoverEventData.scrollDelta = scrollDelta;
hoverEventData.position = ScreenCenterPoint;
hoverEventData.pointerCurrentRaycast = result;
hoverEventData.position3DDelta = raycasterPos - hoverEventData.position3D;
hoverEventData.position3D = raycasterPos;
hoverEventData.rotationDelta = Quaternion.Inverse(hoverEventData.rotation) * raycasterRot;
hoverEventData.rotation = raycasterRot;
// copy data to other button event
for (int j = 0, jmax = raycaster.ButtonEventDataList.Count; j < jmax; ++j)
{
var buttonEventData = raycaster.ButtonEventDataList[j];
if (buttonEventData == null || buttonEventData == hoverEventData) { continue; }
buttonEventData.Reset();
buttonEventData.delta = Vector2.zero;
buttonEventData.scrollDelta = scrollDelta;
buttonEventData.position = ScreenCenterPoint;
buttonEventData.pointerCurrentRaycast = result;
buttonEventData.position3DDelta = hoverEventData.position3DDelta;
buttonEventData.position3D = hoverEventData.position3D;
buttonEventData.rotationDelta = hoverEventData.rotationDelta;
buttonEventData.rotation = hoverEventData.rotation;
}
ProcessPress(hoverEventData);
ProcessMove(hoverEventData);
ProcessDrag(hoverEventData);
// other buttons event
for (int j = 1, jmax = raycaster.ButtonEventDataList.Count; j < jmax; ++j)
{
var buttonEventData = raycaster.ButtonEventDataList[j];
if (buttonEventData == null || buttonEventData == hoverEventData) { continue; }
buttonEventData.pointerEnter = hoverEventData.pointerEnter;
ProcessPress(buttonEventData);
ProcessDrag(buttonEventData);
}
// scroll event
if (result.isValid && !Mathf.Approximately(scrollDelta.sqrMagnitude, 0.0f))
{
var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(result.gameObject);
ExecuteEvents.ExecuteHierarchy(scrollHandler, hoverEventData, ExecuteEvents.scrollHandler);
}
}
if (isActiveAndEnabled)
{
for (var i = processingRaycasters.Count - 1; i >= 0; --i)
{
var r = processingRaycasters[i];
if (!raycasters.Contains(r))
{
CleanUpRaycaster(r);
}
}
}
else
{
for (var i = processingRaycasters.Count - 1; i >= 0; --i)
{
CleanUpRaycaster(processingRaycasters[i]);
}
}
processingRaycasters.Clear();
}
protected virtual void ProcessMove(PointerEventData eventData)
{
var hoverGO = eventData.pointerCurrentRaycast.gameObject;
if (eventData.pointerEnter != hoverGO)
{
HandlePointerExitAndEnter(eventData, hoverGO);
}
}
protected virtual void ProcessPress(Pointer3DEventData eventData)
{
if (eventData.GetPress())
{
if (!eventData.pressPrecessed)
{
ProcessPressDown(eventData);
}
HandlePressExitAndEnter(eventData, eventData.pointerCurrentRaycast.gameObject);
}
else if (eventData.pressPrecessed)
{
ProcessPressUp(eventData);
HandlePressExitAndEnter(eventData, null);
}
}
protected void ProcessPressDown(Pointer3DEventData eventData)
{
var currentOverGo = eventData.pointerCurrentRaycast.gameObject;
eventData.pressPrecessed = true;
eventData.eligibleForClick = true;
eventData.delta = Vector2.zero;
eventData.dragging = false;
eventData.useDragThreshold = true;
eventData.pressPosition = eventData.position;
eventData.pressPosition3D = eventData.position3D;
eventData.pressRotation = eventData.rotation;
eventData.pressDistance = eventData.pointerCurrentRaycast.distance;
eventData.pointerPressRaycast = eventData.pointerCurrentRaycast;
DeselectIfSelectionChanged(currentOverGo, eventData);
// search for the control that will receive the press
// if we can't find a press handler set the press
// handler to be what would receive a click.
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.pointerDownHandler);
// didnt find a press handler... search for a click handler
if (newPressed == null)
{
newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
}
var time = Time.unscaledTime;
if (newPressed == eventData.lastPress)
{
if (eventData.raycaster != null && time < (eventData.clickTime + eventData.raycaster.clickInterval))
{
++eventData.clickCount;
}
else
{
eventData.clickCount = 1;
}
eventData.clickTime = time;
}
else
{
eventData.clickCount = 1;
}
eventData.pointerPress = newPressed;
eventData.rawPointerPress = currentOverGo;
eventData.clickTime = time;
// Save the drag handler as well
eventData.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
if (eventData.pointerDrag != null)
{
ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.initializePotentialDrag);
}
}
protected void ProcessPressUp(Pointer3DEventData eventData)
{
var currentOverGo = eventData.pointerCurrentRaycast.gameObject;
ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
// see if we mouse up on the same element that we clicked on...
var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
// PointerClick and Drop events
if (eventData.pointerPress == pointerUpHandler && eventData.eligibleForClick)
{
ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerClickHandler);
}
else if (eventData.pointerDrag != null && eventData.dragging)
{
ExecuteEvents.ExecuteHierarchy(currentOverGo, eventData, ExecuteEvents.dropHandler);
}
eventData.pressPrecessed = false;
eventData.eligibleForClick = false;
eventData.pointerPress = null;
eventData.rawPointerPress = null;
if (eventData.pointerDrag != null && eventData.dragging)
{
ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.endDragHandler);
}
eventData.dragging = false;
eventData.pointerDrag = null;
// redo pointer enter / exit to refresh state
// so that if we moused over something that ignored it before
// due to having pressed on something else
// it now gets it.
if (currentOverGo != eventData.pointerEnter)
{
HandlePointerExitAndEnter(eventData, null);
HandlePointerExitAndEnter(eventData, currentOverGo);
}
}
protected bool ShouldStartDrag(Pointer3DEventData eventData)
{
if (!eventData.useDragThreshold || eventData.raycaster == null) { return true; }
var currentPos = eventData.position3D + (eventData.rotation * Vector3.forward) * eventData.pressDistance;
var pressPos = eventData.pressPosition3D + (eventData.pressRotation * Vector3.forward) * eventData.pressDistance;
var threshold = eventData.raycaster.dragThreshold;
return (currentPos - pressPos).sqrMagnitude >= threshold * threshold;
}
protected void ProcessDrag(Pointer3DEventData eventData)
{
var moving = !Mathf.Approximately(eventData.position3DDelta.sqrMagnitude, 0f) || !Mathf.Approximately(Quaternion.Angle(Quaternion.identity, eventData.rotationDelta), 0f);
if (moving && eventData.pointerDrag != null && !eventData.dragging && ShouldStartDrag(eventData))
{
ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.beginDragHandler);
eventData.dragging = true;
}
// Drag notification
if (eventData.dragging && moving && eventData.pointerDrag != null)
{
// Before doing drag we should cancel any pointer down state
// And clear selection!
if (eventData.pointerPress != eventData.pointerDrag)
{
ExecuteEvents.Execute(eventData.pointerPress, eventData, ExecuteEvents.pointerUpHandler);
eventData.eligibleForClick = false;
eventData.pointerPress = null;
eventData.rawPointerPress = null;
}
ExecuteEvents.Execute(eventData.pointerDrag, eventData, ExecuteEvents.dragHandler);
}
}
protected static void HandlePressExitAndEnter(Pointer3DEventData eventData, GameObject newEnterTarget)
{
if (eventData.pressEnter == newEnterTarget) { return; }
var oldTarget = eventData.pressEnter == null ? null : eventData.pressEnter.transform;
var newTarget = newEnterTarget == null ? null : newEnterTarget.transform;
var commonRoot = default(Transform);
for (var t = oldTarget; t != null; t = t.parent)
{
if (newTarget != null && newTarget.IsChildOf(t))
{
commonRoot = t;
break;
}
else
{
ExecuteEvents.Execute(t.gameObject, eventData, ExecutePointer3DEvents.PressExitHandler);
}
}
eventData.pressEnter = newEnterTarget;
for (var t = newTarget; t != commonRoot; t = t.parent)
{
ExecuteEvents.Execute(t.gameObject, eventData, ExecutePointer3DEvents.PressEnterHandler);
}
}
protected void DeselectIfSelectionChanged(GameObject currentOverGo, BaseEventData pointerEvent)
{
// Selection tracking
var selectHandlerGO = ExecuteEvents.GetEventHandler<ISelectHandler>(currentOverGo);
// if we have clicked something new, deselect the old thing
// leave 'selection handling' up to the press event though.
if (eventSystem != null && selectHandlerGO != eventSystem.currentSelectedGameObject)
{
eventSystem.SetSelectedGameObject(null, pointerEvent);
}
}
public bool SendUpdateEventToSelectedObject()
{
var selected = EventSystem.current.currentSelectedGameObject;
if (selected == null) { return false; }
var data = GetBaseEventData();
ExecuteEvents.Execute(selected, data, ExecuteEvents.updateSelectedHandler);
return data.used;
}
public bool SendSubmitEventToSelectedObject(bool submit, bool cencel)
{
var selected = EventSystem.current.currentSelectedGameObject;
if (selected == null) { return false; }
var data = GetBaseEventData();
if (submit) { ExecuteEvents.Execute(selected, data, ExecuteEvents.submitHandler); }
if (cencel) { ExecuteEvents.Execute(selected, data, ExecuteEvents.cancelHandler); }
return data.used;
}
public bool SendMoveEventToSelectedObject(float x, float y, float moveDeadZone)
{
var selected = EventSystem.current.currentSelectedGameObject;
if (selected == null) { return false; }
var data = GetAxisEventData(x, y, moveDeadZone);
ExecuteEvents.Execute(selected, data, ExecuteEvents.moveHandler);
return data.used;
}
public static string PrintGOPath(GameObject go)
{
var str = string.Empty;
if (go != null)
{
for (var t = go.transform; t != null; t = t.parent)
{
if (!string.IsNullOrEmpty(str)) { str = "." + str; }
str = t.name + str;
}
}
return str;
}
public override string ToString()
{
var str = string.Empty;
if (raycasters.Count == 0)
{
str += "No raycaster registered";
}
else
{
for (int i = 0, imax = raycasters.Count; i < imax; ++i)
{
var raycaster = raycasters[i];
if (raycaster == null) { continue; }
str += "<b>Raycaster: [" + i + "]</b>\n";
str += raycaster.ToString() + "\n";
}
}
return str;
}
}
}