// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using UltimateXR.Avatar; using UltimateXR.Core; using UltimateXR.Core.Components; using UltimateXR.Extensions.Unity; using UnityEngine; namespace UltimateXR.Animation.Transforms { /// /// Component that allows to continuously orientate an object looking at the local avatar camera. /// If there is no local avatar, it will use the first enabled camera. /// public class UxrLookAtLocalAvatar : UxrComponent { #region Inspector Properties/Serialized Fields [SerializeField] private bool _allowRotateAroundY = true; [SerializeField] private bool _allowRotateAroundX = true; [SerializeField] private bool _invertedForwardAxis; [SerializeField] private bool _onlyOnce; #endregion #region Public Types & Data /// /// Should the lookAt alter the rotation around the vertical axis? /// public bool AllowRotateAroundVerticalAxis { get => _allowRotateAroundY; set => _allowRotateAroundY = value; } /// /// Should the lookAt alter the rotation around the horizontal axis? /// public bool AllowRotateAroundHorizontalAxis { get => _allowRotateAroundX; set => _allowRotateAroundX = value; } /// /// If true, the target's forward axis will try to point at the opposite direction where the /// avatar is. By default this is false, meaning the forward vector will try to point at /// the avatar. /// public bool InvertedForwardAxis { get => _invertedForwardAxis; set => _invertedForwardAxis = value; } /// /// If true, will only perform the lookat the first time it is called. Useful for explosions /// or similar effects in VR. /// public bool OnlyOnce { get => _onlyOnce; set => _onlyOnce = true; } #endregion #region Public Methods /// /// Makes an object look at the local avatar continuously over time. /// /// The object that will look at the local avatar /// /// Should the lookAt alter the rotation around the vertical axis? /// /// /// Should the lookAt alter the rotation around the horizontal axis? /// /// /// If true, the target's forward axis will try to point at the opposite direction where the avatar is. By default this /// is false, meaning the forward vector will try to point at the avatar /// /// The look-at component public UxrLookAtLocalAvatar MakeLookAt(GameObject gameObject, bool allowRotateAroundVerticalAxis, bool allowRotateAroundHorizontalAxis, bool invertedForwardAxis) { UxrLookAtLocalAvatar lookAtComponent = gameObject.GetOrAddComponent(); lookAtComponent._allowRotateAroundY = allowRotateAroundVerticalAxis; lookAtComponent._allowRotateAroundX = allowRotateAroundHorizontalAxis; lookAtComponent._invertedForwardAxis = invertedForwardAxis; return lookAtComponent; } /// /// Makes an object look at the local avatar a single time. /// /// The object that will look at the local avatar /// /// Should the lookAt alter the rotation around the vertical axis? /// /// /// Should the lookAt alter the rotation around the horizontal axis? /// /// /// If true, the target's forward axis will try to point at the opposite direction where the avatar is. By default this /// is false, meaning the forward vector will try to point at the avatar /// public static void MakeLookAtOnlyOnce(GameObject gameObject, bool allowRotateAroundVerticalAxis, bool allowRotateAroundHorizontalAxis, bool invertedForwardAxis) { PerformLookAt(gameObject.transform, allowRotateAroundVerticalAxis, allowRotateAroundHorizontalAxis, invertedForwardAxis); } /// /// Removes an UxrLookAtLocalAvatar component if it exists. /// /// The GameObject to remove the component from public static void RemoveLookAt(GameObject gameObject) { if (gameObject) { UxrLookAtLocalAvatar lookAtComponent = gameObject.GetComponent(); if (lookAtComponent) { Destroy(lookAtComponent); } } } #endregion #region Unity /// /// Subscribes to events. /// protected override void OnEnable() { base.OnEnable(); UxrManager.AvatarsUpdated += UxrManager_AvatarsUpdated; } /// /// Unsubscribes from events. /// protected override void OnDisable() { base.OnDisable(); UxrManager.AvatarsUpdated -= UxrManager_AvatarsUpdated; } #endregion #region Event Handling Methods /// /// Called after avatars are updated. Performs look at. /// private void UxrManager_AvatarsUpdated() { if (_repeat) { PerformLookAt(transform, _allowRotateAroundY, _allowRotateAroundX, _invertedForwardAxis); if (_onlyOnce) { _repeat = false; } } } #endregion #region Private Methods /// /// Performs look at. /// /// The Transform that will look at the local avatar /// /// Should the lookAt alter the rotation around the vertical axis? /// /// /// Should the lookAt alter the rotation around the horizontal axis? /// /// /// If true, the target's forward axis will try to point at the opposite direction where the avatar is. By default this /// is false, meaning the forward vector will try to point at the avatar /// private static void PerformLookAt(Transform transform, bool allowRotateAroundVerticalAxis, bool allowRotateAroundHorizontalAxis, bool invertedForwardAxis) { Camera currentCamera = UxrAvatar.LocalOrFirstEnabledCamera; if (currentCamera == null) { return; } Vector3 lookAt = currentCamera.transform.position - transform.position; if (allowRotateAroundHorizontalAxis == false) { lookAt.y = 0.0f; } if (allowRotateAroundVerticalAxis == false) { lookAt = Vector3.ProjectOnPlane(lookAt, transform.right); } if (lookAt != Vector3.zero) { transform.rotation = Quaternion.LookRotation(invertedForwardAxis ? -lookAt : lookAt); } } #endregion #region Private Types & Data private bool _repeat = true; #endregion } }