// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using UltimateXR.Avatar; using UltimateXR.Core.Components; using UnityEngine; namespace UltimateXR.Locomotion { /// /// Component that, added to an object with colliders, allows to define volumes that force a fixed teleportation /// destination when they are hit with teleporting pointers (arc, ray, etc.). /// public class UxrTeleportSpawnCollider : UxrComponent { #region Inspector Properties/Serialized Fields [SerializeField] private GameObject _enableWhenSelected; [SerializeField] private Transform _spawnPosOneSide; [SerializeField] private Transform _spawnPosOptionalOtherSide; [SerializeField] private Transform _altTargetPosOneSide; [SerializeField] private Transform _altTargetPosOtherSide; [SerializeField] private float _heightDistanceFactor = 1.0f; #endregion #region Public Types & Data /// /// Event raised when the user was teleported by using the spawn collider. /// public event EventHandler Teleported; /// /// Gets or sets the that will be enabled while the component is being pointed at. This can /// be used to enable graphics that help identifying the interactivity and destination. /// public GameObject EnableWhenSelected { get => _enableWhenSelected; set => _enableWhenSelected = value; } /// /// Gets or sets the spawn point the avatar will be teleported to when using the component. /// In two-sided spawn setups -a ladder that will allow to climb up or down, for example- it represents one of the /// sides. In the ladder example, it will be either the ground or the top. /// public Transform SpawnPosOneSide { get => _spawnPosOneSide; set => _spawnPosOneSide = value; } /// /// Gets or sets the other spawn point the avatar can be teleported to when using the component. /// In single point spawn setups this should be left null. In two-sided spawn setups -a ladder that will allow to climb /// up or down, for example- it represents the other side where the avatar can be teleported to. /// In the ladder example it will be either the ground or the top. /// public Transform SpawnPosOptionalOtherSide { get => _spawnPosOptionalOtherSide; set => _spawnPosOptionalOtherSide = value; } /// /// Gets or sets the alternate target position for . /// When non-null it will override the preview teleport target position for . /// Overriding can be useful to draw the target position on top of a seat, even though the actual spawn position will /// be at the bottom of the seat. /// public Transform AltTargetPosOneSide { get => _altTargetPosOneSide; set => _altTargetPosOneSide = value; } /// /// Gets or sets the alternate target position for . /// Same as but for . /// public Transform AltTargetPosOtherSide { get => _altTargetPosOtherSide; set => _altTargetPosOtherSide = value; } /// /// Gets or sets the additional factor that the height difference between and /// will play in computing which spawn point is closer. /// In two-sided spawn setups, the collider will teleport the avatar to the farthest spawn point of the two. This /// factor helps give more weight to the spawn point that is at a farther ground level. /// public float HeightDistanceFactor { get => _heightDistanceFactor; set => _heightDistanceFactor = value; } #endregion #region Public Methods /// /// If there are two spawn positions (one side and other side) it will return the farthest one to the avatar. /// This is useful to simulate ladders and other spawn elements that will allow to go from one side to the other. /// Otherwise it will just return the single spawn position. /// /// Avatar /// /// Where the target position should be rendered. This is useful when you want /// a user to teleport to a chair, where the spawn position would be the base of the chair, but the target /// should be rendered on top of the seat instead. /// /// Farthest spawn position to the player available public Transform GetSpawnPos(UxrAvatar avatar, out Vector3 targetPosition) { if (SpawnPosOneSide != null && SpawnPosOptionalOtherSide != null) { Vector3 avatarPos = avatar.CameraFloorPosition; bool isToOtherSide = Distance(avatarPos, SpawnPosOneSide.position) < Distance(avatarPos, SpawnPosOptionalOtherSide.position); if (isToOtherSide) { targetPosition = AltTargetPosOtherSide != null ? AltTargetPosOtherSide.position : SpawnPosOptionalOtherSide.position; } else { targetPosition = AltTargetPosOneSide != null ? AltTargetPosOneSide.position : SpawnPosOneSide.position; } return isToOtherSide ? SpawnPosOptionalOtherSide : SpawnPosOneSide; } if (SpawnPosOneSide != null) { targetPosition = AltTargetPosOneSide != null ? AltTargetPosOneSide.position : SpawnPosOneSide.position; return SpawnPosOneSide; } if (SpawnPosOptionalOtherSide != null) { targetPosition = AltTargetPosOtherSide != null ? AltTargetPosOtherSide.position : SpawnPosOptionalOtherSide.position; return SpawnPosOptionalOtherSide; } targetPosition = transform.position; return transform; } #endregion #region Unity /// /// Initializes the _enableWhenSelected GameObject if there is one. /// protected override void Start() { base.Start(); if (_enableWhenSelected != null) { _enableWhenSelected.gameObject.SetActive(false); } } #endregion #region Event Trigger Methods /// /// Raises the event. /// /// Event parameters internal void RaiseTeleported(UxrAvatar avatar, UxrAvatarMoveEventArgs e) { Teleported?.Invoke(this, new UxrTeleportSpawnUsedEventArgs(avatar, e)); } #endregion #region Private Methods /// /// Computes a distance value between the avatar and a spawn position by adding up the linear distance at floor level /// and the vertical distance. The distance is used to check which spawn position is closer to the user when two spawn /// positions are available. /// /// Avatar position /// Spawn position /// Distance (horizontal + vertical). private float Distance(Vector3 avatarPosition, Vector3 spawnPosition) { float verticalDistance = Mathf.Abs(avatarPosition.y - spawnPosition.y) * HeightDistanceFactor; Vector3 a = avatarPosition; Vector3 b = spawnPosition; a.y = 0.0f; b.y = 0.0f; return Vector3.Distance(a, b) + verticalDistance; } #endregion } }