//==================================================================================== // // Purpose: Provide abstraction into projecting a raycast into the game world. // As this is an abstract class, it should never be used on it's own. // // Events Emitted: // // WorldPointerIn - is emitted when the pointer collides with an object // WorldPointerOut - is emitted when the pointer stops colliding with an object // WorldPointerDestinationSet - is emmited when the pointer is deactivated // // Event Payload: // // controllerIndex - The index of the controller the pointer is attached to // distance - The distance from the collided object the controller is // target - The Transform of the object the pointer has collided with // tipPosition - The world position of the beam tip // //==================================================================================== using UnityEngine; using System.Collections; public struct WorldPointerEventArgs { public uint controllerIndex; public float distance; public Transform target; public Vector3 destinationPosition; public bool enableTeleport; } public delegate void WorldPointerEventHandler(object sender, WorldPointerEventArgs e); public class SteamVR_PlayAreaCollider : MonoBehaviour { private GameObject parent; public void SetParent(GameObject setParent) { parent = setParent; } void OnTriggerStay(Collider collider) { if (parent.GetComponent().IsActive() && !collider.name.Contains("PlayerObject_")) { parent.GetComponent().setPlayAreaCursorCollision(true); } } void OnTriggerExit(Collider collider) { if (! collider.name.Contains("PlayerObject_")) { parent.GetComponent().setPlayAreaCursorCollision(false); } } } public abstract class SteamVR_WorldPointer : MonoBehaviour { public Color pointerHitColor = new Color(0f, 0.5f, 0f, 1f); public Color pointerMissColor = new Color(0.8f, 0f, 0f, 1f); public bool showPlayAreaCursor = false; public Vector2 playAreaCursorDimensions = Vector2.zero; public bool handlePlayAreaCursorCollisions = false; public bool enableTeleport = true; public bool beamAlwaysOn = false; public float activateDelay = 0f; protected Vector3 destinationPosition; protected float pointerContactDistance = 0f; protected Transform pointerContactTarget = null; protected uint controllerIndex; protected Material pointerMaterial; protected bool playAreaCursorCollided = false; private SteamVR_PlayArea playArea; private GameObject playAreaCursor; private GameObject[] playAreaCursorBoundaries; private bool isActive; private float activateDelayTimer = 0f; private float updatesPerSecond = 60f; public event WorldPointerEventHandler WorldPointerIn; public event WorldPointerEventHandler WorldPointerOut; public event WorldPointerEventHandler WorldPointerDestinationSet; public virtual void OnWorldPointerIn(WorldPointerEventArgs e) { if (WorldPointerIn != null) WorldPointerIn(this, e); } public virtual void OnWorldPointerOut(WorldPointerEventArgs e) { if (WorldPointerOut != null) WorldPointerOut(this, e); } public virtual void OnWorldPointerDestinationSet(WorldPointerEventArgs e) { if (WorldPointerDestinationSet != null) WorldPointerDestinationSet(this, e); } public virtual void setPlayAreaCursorCollision(bool state) { if (handlePlayAreaCursorCollisions) { playAreaCursorCollided = state; } } public virtual bool IsActive() { return isActive; } public virtual bool CanActivate() { return (activateDelayTimer <= 0); } protected WorldPointerEventArgs SetPointerEvent(uint controllerIndex, float distance, Transform target, Vector3 position) { WorldPointerEventArgs e; e.controllerIndex = controllerIndex; e.distance = distance; e.target = target; e.destinationPosition = position; e.enableTeleport = enableTeleport; return e; } protected virtual void Start() { if (GetComponent() == null) { Debug.LogError("SteamVR_WorldPointer is required to be attached to a SteamVR Controller that has the SteamVR_ControllerEvents script attached to it"); return; } this.name = "PlayerObject_" + this.name; //Setup controller event listeners GetComponent().AliasPointerOn += new ControllerClickedEventHandler(EnablePointerBeam); GetComponent().AliasPointerOff += new ControllerClickedEventHandler(DisablePointerBeam); playArea = GameObject.FindObjectOfType(); playAreaCursorBoundaries = new GameObject[4]; pointerMaterial = new Material(Shader.Find("Unlit/TransparentColor")); pointerMaterial.color = pointerMissColor; } protected virtual void Update() { if (activateDelayTimer > 0) { activateDelayTimer--; } } protected virtual void InitPointer() { InitPlayAreaCursor(); } protected virtual void SetPlayAreaCursorTransform(Vector3 destination) { playAreaCursor.transform.position = destination; } protected virtual void EnablePointerBeam(object sender, ControllerClickedEventArgs e) { if (!isActive && activateDelayTimer <= 0) { setPlayAreaCursorCollision(false); controllerIndex = e.controllerIndex; TogglePointer(true); isActive = true; } } protected virtual void DisablePointerBeam(object sender, ControllerClickedEventArgs e) { if (isActive && activateDelayTimer <= 0) { activateDelayTimer = activateDelay * updatesPerSecond; controllerIndex = e.controllerIndex; TogglePointer(false); isActive = false; } } protected virtual void PointerIn() { if (!pointerContactTarget) { return; } OnWorldPointerIn(SetPointerEvent(controllerIndex, pointerContactDistance, pointerContactTarget, destinationPosition)); SteamVR_InteractableObject interactableObject = pointerContactTarget.GetComponent(); if (interactableObject && interactableObject.pointerActivatesUseAction && interactableObject.holdButtonToUse) { interactableObject.StartUsing(this.gameObject); } } protected virtual void PointerOut() { if (!pointerContactTarget) { return; } OnWorldPointerOut(SetPointerEvent(controllerIndex, pointerContactDistance, pointerContactTarget, destinationPosition)); SteamVR_InteractableObject interactableObject = pointerContactTarget.GetComponent(); if (interactableObject && interactableObject.pointerActivatesUseAction && interactableObject.holdButtonToUse) { interactableObject.StopUsing(this.gameObject); } } protected virtual void PointerSet() { if (!isActive || !pointerContactTarget) { return; } SteamVR_InteractableObject interactableObject = pointerContactTarget.GetComponent(); if (interactableObject && interactableObject.pointerActivatesUseAction) { if (interactableObject.IsUsing()) { interactableObject.StopUsing(this.gameObject); } else if (!interactableObject.holdButtonToUse) { interactableObject.StartUsing(this.gameObject); } } if (!playAreaCursorCollided && (interactableObject == null || !interactableObject.pointerActivatesUseAction)) { OnWorldPointerDestinationSet(SetPointerEvent(controllerIndex, pointerContactDistance, pointerContactTarget, destinationPosition)); } } protected virtual void TogglePointer(bool state) { bool playAreaState = (showPlayAreaCursor ? state: false); playAreaCursor.gameObject.SetActive(playAreaState); } protected virtual void SetPointerMaterial() { foreach(GameObject playAreaCursorBoundary in playAreaCursorBoundaries) { playAreaCursorBoundary.GetComponent().material = pointerMaterial; } } protected void UpdatePointerMaterial(Color color) { if (playAreaCursorCollided) { color = pointerMissColor; } pointerMaterial.color = color; SetPointerMaterial(); } private void DrawPlayAreaCursorBoundary(int index, float left, float right, float top, float bottom, float thickness, Vector3 localPosition) { GameObject playAreaCursorBoundary = GameObject.CreatePrimitive(PrimitiveType.Cube); playAreaCursorBoundary.name = string.Format("[{0}]PlayerObject_WorldPointer_PlayAreaCursorBoundary_" + index, this.gameObject.name); float width = (right - left) / 1.065f; float length = (top - bottom) / 1.08f; float height = thickness; playAreaCursorBoundary.transform.localScale = new Vector3(width, height, length); Destroy(playAreaCursorBoundary.GetComponent()); playAreaCursorBoundary.layer = 2; playAreaCursorBoundary.transform.parent = playAreaCursor.transform; playAreaCursorBoundary.transform.localPosition = localPosition; playAreaCursorBoundaries[index] = playAreaCursorBoundary; } private void InitPlayAreaCursor() { int btmRightInner = 0; int btmLeftInner = 1; int topLeftInner = 2; int topRightInner = 3; int btmRightOuter = 4; int btmLeftOuter = 5; int topLeftOuter = 6; int topRightOuter = 7; Vector3[] cursorDrawVertices = playArea.vertices; if (playAreaCursorDimensions != Vector2.zero) { float customAreaPadding = playArea.borderThickness; cursorDrawVertices[btmRightOuter] = new Vector3(playAreaCursorDimensions.x / 2, 0f, (playAreaCursorDimensions.y / 2) * -1); cursorDrawVertices[btmLeftOuter] = new Vector3((playAreaCursorDimensions.x / 2) * -1, 0f, (playAreaCursorDimensions.y / 2) * -1); cursorDrawVertices[topLeftOuter] = new Vector3((playAreaCursorDimensions.x / 2) * -1, 0f, playAreaCursorDimensions.y / 2); cursorDrawVertices[topRightOuter] = new Vector3(playAreaCursorDimensions.x / 2, 0f, playAreaCursorDimensions.y / 2); cursorDrawVertices[btmRightInner] = cursorDrawVertices[btmRightOuter] + new Vector3(-customAreaPadding, 0f, customAreaPadding); cursorDrawVertices[btmLeftInner] = cursorDrawVertices[btmLeftOuter] + new Vector3(customAreaPadding, 0f, customAreaPadding); cursorDrawVertices[topLeftInner] = cursorDrawVertices[topLeftOuter] + new Vector3(customAreaPadding, 0f, -customAreaPadding); cursorDrawVertices[topRightInner] = cursorDrawVertices[topRightOuter] + new Vector3(-customAreaPadding, 0f, -customAreaPadding); } float width = cursorDrawVertices[btmRightOuter].x - cursorDrawVertices[topLeftOuter].x; float length = cursorDrawVertices[topLeftOuter].z - cursorDrawVertices[btmRightOuter].z; float height = 0.01f; playAreaCursor = GameObject.CreatePrimitive(PrimitiveType.Cube); playAreaCursor.name = string.Format("[{0}]PlayerObject_WorldPointer_PlayAreaCursor", this.gameObject.name); playAreaCursor.transform.parent = null; playAreaCursor.transform.localScale = new Vector3(width, height, length); playAreaCursor.SetActive(false); playAreaCursor.GetComponent().enabled = false; BoxCollider playAreaCursorCollider = playAreaCursor.GetComponent(); playAreaCursorCollider.isTrigger = true; playAreaCursorCollider.center = new Vector3(0f, 65f, 0f); playAreaCursorCollider.size = new Vector3(1f, 100f, 1f); playAreaCursor.AddComponent().isKinematic = true; SteamVR_PlayAreaCollider playAreaCursorScript = playAreaCursor.AddComponent(); playAreaCursorScript.SetParent(this.gameObject); playAreaCursor.layer = 2; float playAreaBoundaryX = playArea.transform.localScale.x / 2; float playAreaBoundaryZ = playArea.transform.localScale.z / 2; float heightOffset = 0f; DrawPlayAreaCursorBoundary(0, cursorDrawVertices[btmLeftOuter].x, cursorDrawVertices[btmRightOuter].x, cursorDrawVertices[btmRightInner].z, cursorDrawVertices[btmRightOuter].z, height, new Vector3(0f, heightOffset, playAreaBoundaryZ)); DrawPlayAreaCursorBoundary(1, cursorDrawVertices[btmLeftOuter].x, cursorDrawVertices[btmLeftInner].x, cursorDrawVertices[topLeftOuter].z, cursorDrawVertices[btmLeftOuter].z, height, new Vector3(playAreaBoundaryX, heightOffset, 0f)); DrawPlayAreaCursorBoundary(2, cursorDrawVertices[btmLeftOuter].x, cursorDrawVertices[btmRightOuter].x, cursorDrawVertices[btmRightInner].z, cursorDrawVertices[btmRightOuter].z, height, new Vector3(0f, heightOffset, -playAreaBoundaryZ)); DrawPlayAreaCursorBoundary(3, cursorDrawVertices[btmLeftOuter].x, cursorDrawVertices[btmLeftInner].x, cursorDrawVertices[topLeftOuter].z, cursorDrawVertices[btmLeftOuter].z, height, new Vector3(-playAreaBoundaryX, heightOffset, 0f)); } }