using Convai.Scripts.Runtime.Core; using UnityEngine; /// /// Handles the UI transformation for XR interactions, adjusting the UI position based on the player's camera distance from an NPC. /// public class XRNPCUIPositionHandler : MonoBehaviour { // Speed at which the UI lerps to its new position. [SerializeField] private float _lerpSpeed; // Offset from the NPC position. [SerializeField] private Vector3 _offset; // Distance threshold for switching offsets. [SerializeField] private float _cameraDistanceThreshold; // Reference to the main camera in the scene. private Camera _playerCamera; // Reference to the currently active NPC. private ConvaiNPC _currentNPC; /// /// Subscribes to the active NPC change event when the script is enabled. /// private void OnEnable() { if (ConvaiNPCManager.Instance != null) { ConvaiNPCManager.Instance.OnActiveNPCChanged += OnActiveNPCChanged; _currentNPC = ConvaiNPCManager.Instance.activeConvaiNPC; } } /// /// Unsubscribes from the active NPC change event when the script is disabled. /// private void OnDisable() { if (ConvaiNPCManager.Instance != null) { ConvaiNPCManager.Instance.OnActiveNPCChanged -= OnActiveNPCChanged; } } /// /// Gets the main camera reference. /// private void Start() { _playerCamera = Camera.main; } /// /// Updates the current NPC and sets the UI position when the active NPC changes. /// /// The new active NPC. private void OnActiveNPCChanged(ConvaiNPC newNPC) { _currentNPC = newNPC; if (_currentNPC != null && _playerCamera != null) { SetUIPosition(); } } /// /// Updates the UI position and rotation to face the camera in each frame. /// private void LateUpdate() { if (_currentNPC != null) { UpdateUIPosition(); FaceCamera(); } } /// /// Sets the UI position based on the distance from the player camera to the NPC. /// private void SetUIPosition() { Transform npcTransform = _currentNPC.transform; Vector3 targetPosition = CalculateTargetPosition(npcTransform); transform.position = targetPosition; } /// /// Smoothly updates the UI position using linear interpolation (Lerp). /// private void UpdateUIPosition() { Transform npcTransform = _currentNPC.transform; Vector3 targetPosition = CalculateTargetPosition(npcTransform); transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * _lerpSpeed); } /// /// Calculates the target position of the UI based on the NPC's position and the distance to the player camera. /// /// The transform of the NPC. /// The target position for the UI. private Vector3 CalculateTargetPosition(Transform npcTransform) { Vector3 leftOffset = new Vector3(-_offset.x, _offset.y, _offset.z); Vector3 rightOffset = new Vector3(_offset.x, _offset.y, _offset.z); Vector3 leftOffsetPosition = npcTransform.position + npcTransform.TransformDirection(leftOffset); Vector3 rightOffsetPosition = npcTransform.position + npcTransform.TransformDirection(rightOffset); float distanceToLeftOffset = Vector3.Distance(leftOffsetPosition, _playerCamera.transform.position); float distanceToRightOffset = Vector3.Distance(rightOffsetPosition, _playerCamera.transform.position); Vector3 dynamicOffset = DetermineDynamicOffset(distanceToLeftOffset, distanceToRightOffset); return npcTransform.position + npcTransform.TransformDirection(dynamicOffset); } /// /// Determines the appropriate dynamic offset based on the distances from the camera to the left and right offsets. /// /// Distance to the left offset position. /// Distance to the right offset position. /// The chosen offset vector. private Vector3 DetermineDynamicOffset(float distanceToLeftOffset, float distanceToRightOffset) { Vector3 leftOffset = new Vector3(-_offset.x, _offset.y, _offset.z); Vector3 rightOffset = new Vector3(_offset.x, _offset.y, _offset.z); float threshold = 0.5f; if (distanceToLeftOffset < _cameraDistanceThreshold && distanceToRightOffset < _cameraDistanceThreshold) { float difference = Mathf.Abs(distanceToLeftOffset - distanceToRightOffset); return difference > threshold ? (distanceToLeftOffset > distanceToRightOffset ? leftOffset : rightOffset) : leftOffset; } else { return distanceToLeftOffset >= _cameraDistanceThreshold ? leftOffset : rightOffset; } } /// /// Makes the UI face the camera. /// private void FaceCamera() { Vector3 direction = transform.position - _playerCamera.transform.position; transform.rotation = Quaternion.LookRotation(direction); } }