// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System.Linq; using UltimateXR.Animation.Interpolation; using UltimateXR.Audio; using UltimateXR.Avatar; using UltimateXR.Core.Components; using UnityEngine; namespace UltimateXR.Examples.FullScene.Doors { /// /// Component to model de behavior of a door that opens automatically when the user gets near and closes /// when the user moves away from it. /// public class AutomaticDoor : UxrComponent { #region Inspector Properties/Serialized Fields [SerializeField] private Transform _floorCenter; [SerializeField] private Transform _leftDoor; [SerializeField] private Transform _rightDoor; [SerializeField] private Vector3 _leftOpenLocalOffset; [SerializeField] private Vector3 _rightOpenLocalOffset; [SerializeField] private float _openDelaySeconds; [SerializeField] private float _openDurationSeconds = 0.8f; [SerializeField] private float _openDistance = 1.5f; [SerializeField] private float _closeDistance = 2.0f; [SerializeField] private UxrEasing _openEasing = UxrEasing.EaseOutCubic; [SerializeField] private UxrEasing _closeEasing = UxrEasing.EaseInCubic; [SerializeField] private UxrAudioSample _audioOpen; [SerializeField] private UxrAudioSample _audioClose; #endregion #region Public Types & Data /// /// Gets a value from 0.0 (completely closed) to 1.0 (completely open) telling how open the door currently is. /// public float OpenValue { get; private set; } /// /// Gets if the door is open or opening. /// public bool IsOpen { get; private set; } #endregion #region Public Methods /// /// Opens the door. This can be used in child implementations where opening can be disallowed under certain /// conditions. See for an example. /// /// Whether to play the open sound public void OpenDoor(bool playSound) { BeginSync(); IsOpen = true; if (playSound) { _audioOpen.Play(FloorCenter.position); } EndSyncMethod(new object[] { playSound }); } /// /// Closes the door. /// /// Whether to play the close sound public void CloseDoor(bool playSound) { BeginSync(); // Over closing distance and door completely open: close door IsOpen = false; _openDelayTimer = 0.0f; if (playSound) { _audioClose.Play(FloorCenter.position); } EndSyncMethod(new object[] { playSound }); } #endregion #region Unity /// /// Stores initial state. /// protected override void Awake() { base.Awake(); _leftStartLocalPosition = _leftDoor.localPosition; _rightStartLocalPosition = _rightDoor.localPosition; } /// /// Updates the door. /// private void Update() { if (UxrAvatar.LocalAvatar == null) { return; } // Check distance to door UxrAvatar closestAvatar = UxrAvatar.EnabledComponents.OrderBy(a => Vector3.Distance(a.CameraFloorPosition, FloorCenter.position)).FirstOrDefault(); if (closestAvatar == UxrAvatar.LocalAvatar) { // The closest avatar will determine the door state. float closestAvatarDistance = Vector3.Distance(closestAvatar.CameraFloorPosition, FloorCenter.position); if (closestAvatarDistance < _openDistance && Mathf.Approximately(OpenValue, 0.0f)) { _openDelayTimer += Time.deltaTime; if (_openDelayTimer > _openDelaySeconds && IsOpeningAllowed) { // Within opening distance, door completely closed and opening allowed: open door OpenDoor(true); } } else if (closestAvatarDistance > _closeDistance && Mathf.Approximately(OpenValue, 1.0f)) { CloseDoor(true); } } // Update timer and perform interpolation OpenValue = Mathf.Clamp01(OpenValue + Time.deltaTime * (1.0f / _openDurationSeconds) * (IsOpen ? 1.0f : -1.0f)); float t = UxrInterpolator.GetInterpolationFactor(OpenValue, IsOpen ? _openEasing : _closeEasing); _leftDoor.transform.localPosition = Vector3.Lerp(_leftStartLocalPosition, _leftStartLocalPosition + _leftOpenLocalOffset, t); _rightDoor.transform.localPosition = Vector3.Lerp(_rightStartLocalPosition, _rightStartLocalPosition + _rightOpenLocalOffset, t); } #endregion #region Protected Types & Data /// /// Gets if opening is allowed. Can be used by child implementations to disallow opening under certain conditions. See /// for an example. /// protected virtual bool IsOpeningAllowed => true; /// /// Gets the door center at floor level. /// protected Transform FloorCenter => _floorCenter != null ? _floorCenter : transform; #endregion #region Private Types & Data private float _openDelayTimer; private Vector3 _leftStartLocalPosition; private Vector3 _rightStartLocalPosition; #endregion } }