Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
BeaconTeleport for unity3d / VRTK
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;
// 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() {
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) {
// 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.destinationPosition) && e.enableTeleport) {
StartTeleport(sender, e);
// Quaternion updatedRotation = SetNewRotation(e.destinationRotation);
// Vector3 newPosition = e.destinationPosition; // GetNewPosition(e.destinationPosition,, e.forceDestinationPosition);
// Vector3 updatedPosition = SetNewPosition(newPosition,, 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
You can’t perform that action at this time.