/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.Demo.Objects { using Opsive.Shared.Game; using Opsive.UltimateCharacterController.Character; using Opsive.UltimateCharacterController.Game; using Opsive.UltimateCharacterController.Utility; using UnityEngine; /// /// Controls the animation for the door. /// public class Door : MonoBehaviour { private const string c_ColorText = "_Color"; private const string c_EmissionColor = "_EmissionColor"; [Tooltip("The LayerMask of the character.")] [SerializeField] protected LayerMask m_LayerMask = 1 << LayerManager.Character; [Tooltip("Is the door locked?")] [SerializeField] protected bool m_Locked = false; [Tooltip("Should the door be permanently locked? A permanently locked door cannot be opened by the DemoManager.")] [UnityEngine.Serialization.FormerlySerializedAs("m_PermantlyLocked")] [SerializeField] protected bool m_PermanentlyLocked; [Tooltip("Should the door be opened at the start?")] [SerializeField] protected bool m_OpenAtStart; [Tooltip("Should the door close when the character leaves the trigger?")] [SerializeField] protected bool m_CloseOnTriggerExit = true; [Tooltip("The material that can change colors when the door is locked or unlcoked.")] [SerializeField] protected Material m_StatusMaterial; [Tooltip("The color of the closed door.")] [SerializeField] protected Color m_LockedColor = Color.red; [Tooltip("The color of the opened door.")] [SerializeField] protected Color m_UnlockedColor = Color.green; [Tooltip("The AudioClip that should play when the door is opened.")] [SerializeField] protected AudioClip m_OpenAudioClip; [Tooltip("The AudioClip that should play when the door is closed.")] [SerializeField] protected AudioClip m_CloseAudioClip; public bool Locked { get { return m_Locked; } set { if (m_Locked == value) { return; } m_Locked = value; UpdateDoorStatus(); if (m_Locked && m_Open) { OpenClose(false, true, true); } else if (!m_Locked && m_CharacterCount > 0) { m_Open = false; OpenClose(true, true, true); } } } public bool PermanentlyLocked { get { return m_PermanentlyLocked; } set { m_PermanentlyLocked = value; } } public bool CloseOnTriggerExit { get { return m_CloseOnTriggerExit; } set { m_CloseOnTriggerExit = value; } } private static int s_OpenHash = Animator.StringToHash("Open"); private Animator m_Animator; private AudioSource m_AudioSource; private bool m_ManagerOpen; private bool m_Open; private int m_CharacterCount; private Material[] m_StatusMaterials; /// /// Initializes the default values. /// private void Awake() { m_Animator = GetComponent(); m_AudioSource = GetComponent(); var demoManager = FindObjectOfType(); if (demoManager != null) { demoManager.RegisterDoor(this); } // Cache the light materials so they can be changed. var renderers = GetComponentsInChildren(); for (int i = 0; i < renderers.Length; ++i) { var materials = renderers[i].sharedMaterials; for (int j = 0; j < materials.Length; ++j) { // The shared material will allow for a valid comparison. The instance material should not be compared. if (materials[j] != m_StatusMaterial) { continue; } if (m_StatusMaterials == null) { m_StatusMaterials = new Material[1]; } else { System.Array.Resize(ref m_StatusMaterials, m_StatusMaterials.Length + 1); } // Cache the instance material so each status light operates independently. m_StatusMaterials[m_StatusMaterials.Length - 1] = renderers[i].materials[j]; } } // Permantly locked doors cannot be opened. if (m_PermanentlyLocked) { m_Locked = true; } if (m_OpenAtStart) { OpenClose(true, true, false); } UpdateDoorStatus(); } /// /// Updates the door status material. /// private void UpdateDoorStatus() { if (m_StatusMaterials != null) { for (int i = 0; i < m_StatusMaterials.Length; ++i) { var locked = m_Locked || m_PermanentlyLocked; m_StatusMaterials[i].SetColor(c_ColorText, locked ? m_LockedColor : m_UnlockedColor); m_StatusMaterials[i].SetColor(c_EmissionColor, locked ? m_LockedColor : m_UnlockedColor); } } } /// /// Closes the door. /// public void Close() { OpenClose(false, false, false); } /// /// Opens the door. /// public void Open() { OpenClose(true, false, false); } /// /// Opens or closes the door. /// /// Should the door be opened? /// Is the door being opened or closed from the DemoManager? /// Should the door open/close audio be played? public void OpenClose(bool open, bool fromManager, bool playAudio) { // Permanently locked doors cannot be opened. if (m_PermanentlyLocked && open) { return; } // Don't close the door if the manager opened the door. if (!open && !fromManager && m_ManagerOpen) { return; } // The door can't open if it is already open. if (m_Open == open) { return; } if (open) { // The door can't open if it's locked. if (m_Locked) { // The manager can unlock the door. if (fromManager) { m_Locked = false; } else { return; } } } m_Open = open; m_Animator.SetBool(s_OpenHash, open); if (playAudio && m_AudioSource != null) { if (open && m_OpenAudioClip != null) { m_AudioSource.clip = m_OpenAudioClip; m_AudioSource.Play(); } else if (!open && m_CloseAudioClip != null) { m_AudioSource.clip = m_CloseAudioClip; m_AudioSource.Play(); } } // The manager will override the trigger settings. if (fromManager) { m_ManagerOpen = open; } } /// /// Open the door if the character enters the trigger. /// /// The collider which entered the trigger. private void OnTriggerEnter(Collider other) { if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask) || other.gameObject.GetCachedParentComponent() == null) { return; } m_CharacterCount++; OpenClose(true, false, true); } /// /// Close the door if the character leaves the trigger. /// /// The collider which left the trigger. private void OnTriggerExit(Collider other) { if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask) || other.gameObject.GetCachedParentComponent() == null) { return; } // The door should only close when all characters are no longer within the trigger. m_CharacterCount--; if (m_CharacterCount == 0 && m_Animator.GetBool(s_OpenHash) && m_CloseOnTriggerExit) { OpenClose(false, false, true); } } } }