// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}