#if !UNITY_2019_3_OR_NEWER #define CINEMACHINE_PHYSICS #define CINEMACHINE_PHYSICS_2D #endif using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.Playables; namespace Cinemachine { #if !(CINEMACHINE_PHYSICS || CINEMACHINE_PHYSICS_2D) // Workaround for Unity scripting bug /// <summary> /// A multi-purpose script which causes an action to occur when /// a trigger collider is entered and exited. /// </summary> [AddComponentMenu("")] // Hide in menu public class CinemachineTriggerAction : MonoBehaviour {} #else /// <summary> /// A multi-purpose script which causes an action to occur when /// a trigger collider is entered and exited. /// </summary> [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)] [SaveDuringPlay] [HelpURL(Documentation.BaseURL + "api/Cinemachine.CinemachineTriggerAction.html")] public class CinemachineTriggerAction : MonoBehaviour { /// <summary>Only triggers generated by objects on these layers will be considered.</summary> [Header("Trigger Object Filter")] [Tooltip("Only triggers generated by objects on these layers will be considered")] public LayerMask m_LayerMask = 1; /// <summary>If set, only triggers generated by objects with this tag will be considered</summary> [TagField] [Tooltip("If set, only triggers generated by objects with this tag will be considered")] public string m_WithTag = string.Empty; /// <summary>Triggers generated by objects with this tag will be ignored</summary> [TagField] [Tooltip("Triggers generated by objects with this tag will be ignored")] public string m_WithoutTag = string.Empty; /// <summary>Skip this many trigger entries before taking action</summary> [NoSaveDuringPlay] [Tooltip("Skip this many trigger entries before taking action")] public int m_SkipFirst = 0; /// <summary>Repeat the action for all subsequent trigger entries</summary> [Tooltip("Repeat the action for all subsequent trigger entries")] public bool m_Repeating = true; /// <summary>Defines what action to take on trigger enter/exit</summary> [Serializable] public struct ActionSettings { /// <summary>What action to take</summary> public enum Mode { /// <summary>Use the event only</summary> Custom, /// <summary>Boost priority of virtual camera target</summary> PriorityBoost, /// <summary>Activate the target GameObject</summary> Activate, /// <summary>Decativate target GameObject</summary> Deactivate, /// <summary>Enable a component</summary> Enable, /// <summary>Disable a component</summary> Disable, #if CINEMACHINE_TIMELINE /// <summary>Start animation on target</summary> Play, /// <summary>Stop animation on target</summary> Stop #endif } /// <summary>Serializable parameterless game event</summary> [Serializable] public class TriggerEvent : UnityEvent {} /// <summary>What action to take</summary> [Tooltip("What action to take")] public Mode m_Action; /// <summary>The target object on which to operate. If null, then the current behaviour/GameObject will be used</summary> [Tooltip("The target object on which to operate. If null, then the current behaviour/GameObject will be used")] public UnityEngine.Object m_Target; /// <summary>If PriorityBoost, this amount will be added to the virtual camera's priority</summary> [Tooltip("If PriorityBoost, this amount will be added to the virtual camera's priority")] public int m_BoostAmount; /// <summary>If playing a timeline, start at this time</summary> [Tooltip("If playing a timeline, start at this time")] public float m_StartTime; /// <summary>How to interpret the start time</summary> public enum TimeMode { /// <summary>Offset after the start of the timeline</summary> FromStart, /// <summary>Offset before the end of the timeline</summary> FromEnd, /// <summary>Offset before the current timeline time</summary> BeforeNow, /// <summary>Offset after the current timeline time</summary> AfterNow }; /// <summary>How to interpret the start time</summary> [Tooltip("How to interpret the start time")] public TimeMode m_Mode; /// <summary>This event will be invoked</summary> [Tooltip("This event will be invoked")] public TriggerEvent m_Event; /// <summary>Standard Constructor</summary> /// <param name="action">Action to set</param> public ActionSettings(Mode action) { m_Action = action; m_Target = null; m_BoostAmount = 0; m_StartTime = 0; m_Mode = TimeMode.FromStart; m_Event = new TriggerEvent(); } /// <summary>Invoke the action. Depending on the mode, different action will /// be performed. The embedded event will always be invoked, in addition to the /// action specified by the Mode.</summary> public void Invoke() { UnityEngine.Object currentTarget = m_Target; if (currentTarget != null) { GameObject targetGameObject = currentTarget as GameObject; Behaviour targetBehaviour = currentTarget as Behaviour; if (targetBehaviour != null) targetGameObject = targetBehaviour.gameObject; switch (m_Action) { case Mode.Custom: break; case Mode.PriorityBoost: { CinemachineVirtualCameraBase vcam = targetGameObject.GetComponent<CinemachineVirtualCameraBase>(); if (vcam != null) { vcam.Priority += m_BoostAmount; vcam.MoveToTopOfPrioritySubqueue(); } break; } case Mode.Activate: if (targetGameObject != null) { targetGameObject.SetActive(true); CinemachineVirtualCameraBase vcam = targetGameObject.GetComponent<CinemachineVirtualCameraBase>(); if (vcam != null) vcam.MoveToTopOfPrioritySubqueue(); } break; case Mode.Deactivate: if (targetGameObject != null) targetGameObject.SetActive(false); break; case Mode.Enable: { if (targetBehaviour != null) targetBehaviour.enabled = true; break; } case Mode.Disable: { if (targetBehaviour != null) targetBehaviour.enabled = false; break; } #if CINEMACHINE_TIMELINE case Mode.Play: { PlayableDirector playable = targetGameObject.GetComponent<PlayableDirector>(); if (playable != null) { double startTime = 0; double duration = playable.duration; double current = playable.time; switch (m_Mode) { default: case TimeMode.FromStart: startTime += m_StartTime; break; case TimeMode.FromEnd: startTime = duration - m_StartTime; break; case TimeMode.BeforeNow: startTime = current - m_StartTime; break; case TimeMode.AfterNow: startTime = current + m_StartTime; break; } playable.time = startTime; playable.Play(); } else { Animation ani = targetGameObject.GetComponent<Animation>(); if (ani != null) ani.Play(); } break; } case Mode.Stop: { PlayableDirector playable = targetGameObject.GetComponent<PlayableDirector>(); if (playable != null) playable.Stop(); else { Animation ani = targetGameObject.GetComponent<Animation>(); if (ani != null) ani.Stop(); } break; } #endif } } m_Event.Invoke(); } } /// <summary>What action to take when an eligible object enters the collider or trigger zone</summary> public ActionSettings m_OnObjectEnter = new ActionSettings(ActionSettings.Mode.Custom); /// <summary>What action to take when an eligible object exits the collider or trigger zone</summary> public ActionSettings m_OnObjectExit = new ActionSettings(ActionSettings.Mode.Custom); HashSet<GameObject> m_ActiveTriggerObjects = new HashSet<GameObject>(); private bool Filter(GameObject other) { if (!enabled) return false; if (((1 << other.layer) & m_LayerMask) == 0) return false; if (m_WithTag.Length != 0 && !other.CompareTag(m_WithTag)) return false; if (m_WithoutTag.Length != 0 && other.CompareTag(m_WithoutTag)) return false; return true; } void InternalDoTriggerEnter(GameObject other) { if (!Filter(other)) return; --m_SkipFirst; if (m_SkipFirst > -1) return; if (!m_Repeating && m_SkipFirst != -1) return; m_ActiveTriggerObjects.Add(other); m_OnObjectEnter.Invoke(); } void InternalDoTriggerExit(GameObject other) { if (!m_ActiveTriggerObjects.Contains(other)) return; m_ActiveTriggerObjects.Remove(other); if (enabled) m_OnObjectExit.Invoke(); } #if CINEMACHINE_PHYSICS void OnTriggerEnter(Collider other) { InternalDoTriggerEnter(other.gameObject); } void OnTriggerExit(Collider other) { InternalDoTriggerExit(other.gameObject); } void OnCollisionEnter(Collision other) { InternalDoTriggerEnter(other.gameObject); } void OnCollisionExit(Collision other) { InternalDoTriggerExit(other.gameObject); } #endif #if CINEMACHINE_PHYSICS_2D void OnTriggerEnter2D(Collider2D other) { InternalDoTriggerEnter(other.gameObject); } void OnTriggerExit2D(Collider2D other) { InternalDoTriggerExit(other.gameObject); } void OnCollisionEnter2D(Collision2D other) { InternalDoTriggerEnter(other.gameObject); } void OnCollisionExit2D(Collision2D other) { InternalDoTriggerExit(other.gameObject); } #endif void OnEnable() {} // For the Enabled checkbox } #endif }