Created
December 5, 2017 16:00
-
-
Save tangentstorm/f7d98f506c1314c060db26988aaa4cb0 to your computer and use it in GitHub Desktop.
BeaconTeleport for unity3d / VRTK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using VRTK; | |
/* This class lets you leave a beacon in the scene and teleport to it later. | |
* | |
* It should be attached to an object that also has a VRTK_ControllerEvents | |
* component attached. | |
* | |
* You also have to supply a transform to use as the beacon. | |
*/ | |
public class BeaconTeleport : VRTK_BasicTeleport { | |
public Transform beacon; | |
// same as basicteleport, but move the beacon instead of the playArea | |
protected override Vector3 SetNewPosition(Vector3 position, Transform target, bool forceDestinationPosition) { | |
if (ValidRigObjects()) { | |
beacon.position = CheckTerrainCollision(position, target, forceDestinationPosition); | |
return beacon.position; | |
} | |
return Vector3.zero; | |
} | |
// same as basicteleport, but rotate the beacon instead of the playArea | |
// !! (I think this only actually does anything if you're using a rotated | |
// destination marker?? not sure. actual rotation is handled | |
// by ProcessOrientation, below) | |
protected override Quaternion SetNewRotation(Quaternion? rotation) { | |
if (ValidRigObjects()) { | |
if (rotation != null) { | |
beacon.rotation = (Quaternion)rotation; | |
} | |
return beacon.rotation; | |
} | |
return Quaternion.identity; | |
} | |
// this does the work of moving the player to the beacon | |
public void TeleportToBeacon() { | |
Blink(blinkTransitionSpeed); | |
playArea.SetPositionAndRotation(beacon.position, beacon.rotation); | |
} | |
public void Start() { | |
var ce = GetComponent<VRTK_ControllerEvents>(); | |
if (ce == null) Debug.LogError("You need to add a VRTK_ControllerEvents!"); | |
else { | |
ce.TriggerClicked += OnTeleportButton; | |
ce.GripPressed += OnResetBeaconRotationButton; | |
} | |
} | |
private void OnResetBeaconRotationButton(object sender, ControllerInteractionEventArgs e) { | |
print("resetting beacon rotation"); | |
beacon.rotation = Quaternion.identity; | |
} | |
private void OnTeleportButton(object sender, ControllerInteractionEventArgs e) { | |
TeleportToBeacon(); | |
} | |
// the beacon's new position is the exact point of contact from the raycast. | |
// the new rotation brings the beacon's "up" in line with the surface normal. | |
protected override void DoTeleport(object sender, DestinationMarkerEventArgs e) { | |
if (enableTeleport && ValidLocation(e.target, e.destinationPosition) && e.enableTeleport) { | |
StartTeleport(sender, e); | |
// Quaternion updatedRotation = SetNewRotation(e.destinationRotation); | |
// Vector3 newPosition = e.destinationPosition; // GetNewPosition(e.destinationPosition, e.target, e.forceDestinationPosition); | |
// Vector3 updatedPosition = SetNewPosition(newPosition, e.target, e.forceDestinationPosition); | |
// ProcessOrientation(sender, e, updatedPosition, updatedRotation); | |
// first, move beacon to right place: | |
beacon.position = e.destinationPosition; | |
// align the beacon to the new "floor" | |
// beacon.rotation = Quaternion.identity; | |
beacon.rotation = Quaternion.FromToRotation(Vector3.up, e.raycastHit.normal); | |
// beacon.rotation = Quaternion.FromToRotation(beacon.forward, headset.forward); | |
// align beacon with player's gaze | |
// beacon.rotation = Quaternion.LookRotation(headset.forward, e.raycastHit.normal); | |
// There is probably a simpler way to do this with trig or quaternions, but | |
// I can't figure it out right now, so let's just use geometry. | |
// | |
// Let's pretend that the headset and the controller are the same point, because | |
// that's close enough. This is point P, for player. | |
// | |
// We have a line segment from P to D (the teleport destination), and an imaginary | |
// ray starting at P and following the vector in headset.forward (F). | |
// | |
// Right now, the beacon is aligned so that its y axis (up) matches the | |
// destination's surface normal, and its x and z axes are parallel to the | |
// global x and z axes. We want to rotate the beacon around its y axis so that | |
// the z axis (forward.. or possibly backward?) is parallel to headset.forward. | |
// | |
// So we need to construct a point that starts at D and follows F, and then | |
// pivot the beacon around its y axis so that it looks at that point. | |
// | |
// Quaternion.LookRotation does this, but the problem is that it needs the | |
// up and forward vectors to be perpendicular. | |
// | |
// The cross product of the normal and headset.right should give a new | |
// forward vector. (it could also come up with a backward vector, but in | |
// practice, it doesn't seem to.. we might need to do a sign flip here | |
// or something. | |
Vector3 temp = Vector3.Cross(e.raycastHit.normal, headset.right); | |
beacon.rotation = Quaternion.LookRotation(temp, e.raycastHit.normal); | |
EndTeleport(sender, e); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment