Files
vstyler/Assets/SteamVR_Unity_Toolkit/Scripts/Abstractions/SteamVR_WorldPointer.cs
T
2016-05-26 16:11:13 +02:00

358 lines
14 KiB
C#
Executable File

//====================================================================================
//
// 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<SteamVR_WorldPointer>().IsActive() && !collider.name.Contains("PlayerObject_"))
{
parent.GetComponent<SteamVR_WorldPointer>().setPlayAreaCursorCollision(true);
}
}
void OnTriggerExit(Collider collider)
{
if (! collider.name.Contains("PlayerObject_")) {
parent.GetComponent<SteamVR_WorldPointer>().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<SteamVR_ControllerEvents>() == 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<SteamVR_ControllerEvents>().AliasPointerOn += new ControllerClickedEventHandler(EnablePointerBeam);
GetComponent<SteamVR_ControllerEvents>().AliasPointerOff += new ControllerClickedEventHandler(DisablePointerBeam);
playArea = GameObject.FindObjectOfType<SteamVR_PlayArea>();
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<SteamVR_InteractableObject>();
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<SteamVR_InteractableObject>();
if (interactableObject && interactableObject.pointerActivatesUseAction && interactableObject.holdButtonToUse)
{
interactableObject.StopUsing(this.gameObject);
}
}
protected virtual void PointerSet()
{
if (!isActive || !pointerContactTarget)
{
return;
}
SteamVR_InteractableObject interactableObject = pointerContactTarget.GetComponent<SteamVR_InteractableObject>();
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<MeshRenderer>().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<BoxCollider>());
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<MeshRenderer>().enabled = false;
BoxCollider playAreaCursorCollider = playAreaCursor.GetComponent<BoxCollider>();
playAreaCursorCollider.isTrigger = true;
playAreaCursorCollider.center = new Vector3(0f, 65f, 0f);
playAreaCursorCollider.size = new Vector3(1f, 100f, 1f);
playAreaCursor.AddComponent<Rigidbody>().isKinematic = true;
SteamVR_PlayAreaCollider playAreaCursorScript = playAreaCursor.AddComponent<SteamVR_PlayAreaCollider>();
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));
}
}