using System.Collections;
using System.Collections.Generic;
#if MM_CINEMACHINE
using Cinemachine;
#endif
using UnityEngine;
using UnityEngine.Events;
namespace MoreMountains.Tools
{
///
/// An abstract class that lets you define a zone that, when entered, enables a virtual camera, and takes care
/// of all the boilerplate setup
///
[AddComponentMenu("")]
[ExecuteAlways]
public abstract class MMCinemachineZone : MonoBehaviour
{
public enum Modes { Enable, Priority }
[Header("Virtual Camera")]
/// whether to enable/disable virtual cameras, or to play on their priority for transitions
[Tooltip("whether to enable/disable virtual cameras, or to play on their priority for transitions")]
public Modes Mode = Modes.Enable;
/// whether or not the camera in this zone should start active
[Tooltip("whether or not the camera in this zone should start active")]
public bool CameraStartsActive = false;
#if MM_CINEMACHINE
/// the virtual camera associated to this zone (will try to grab one in children if none is set)
[Tooltip("the virtual camera associated to this zone (will try to grab one in children if none is set)")]
public CinemachineVirtualCamera VirtualCamera;
#endif
/// when in priority mode, the priority this camera should have when the zone is active
[Tooltip("when in priority mode, the priority this camera should have when the zone is active")]
[MMEnumCondition("Mode", (int)Modes.Priority)]
public int EnabledPriority = 10;
/// when in priority mode, the priority this camera should have when the zone is inactive
[Tooltip("when in priority mode, the priority this camera should have when the zone is inactive")]
[MMEnumCondition("Mode", (int)Modes.Priority)]
public int DisabledPriority = 0;
[Header("Collisions")]
/// a layermask containing all the layers that should activate this zone
[Tooltip("a layermask containing all the layers that should activate this zone")]
public LayerMask TriggerMask;
[Header("Confiner Setup")]
/// whether or not the zone should auto setup its camera's confiner on start - alternative is to manually click the ManualSetupConfiner, or do your own setup
[Tooltip("whether or not the zone should auto setup its camera's confiner on start - alternative is to manually click the ManualSetupConfiner, or do your own setup")]
public bool SetupConfinerOnStart = false;
/// a debug button used to setup the confiner on click
[MMInspectorButton("ManualSetupConfiner")]
public bool GenerateConfinerSetup;
[Header("State")]
/// whether this room is the current room or not
[Tooltip("whether this room is the current room or not")]
[MMReadOnly]
public bool CurrentRoom = false;
/// whether this room has already been visited or not
[Tooltip("whether this room has already been visited or not")]
public bool RoomVisited = false;
[Header("Events")]
/// a UnityEvent to trigger when entering the zone for the first time
[Tooltip("a UnityEvent to trigger when entering the zone for the first time")]
public UnityEvent OnEnterZoneForTheFirstTimeEvent;
/// a UnityEvent to trigger when entering the zone
[Tooltip("a UnityEvent to trigger when entering the zone")]
public UnityEvent OnEnterZoneEvent;
/// a UnityEvent to trigger when exiting the zone
[Tooltip("a UnityEvent to trigger when exiting the zone")]
public UnityEvent OnExitZoneEvent;
[Header("Activation")]
/// a list of gameobjects to enable when entering the zone, and disable when exiting it
[Tooltip("a list of gameobjects to enable when entering the zone, and disable when exiting it")]
public List ActivationList;
[Header("Debug")]
/// whether or not to draw shape gizmos to help visualize the zone's bounds
[Tooltip("whether or not to draw shape gizmos to help visualize the zone's bounds")]
public bool DrawGizmos = true;
/// the color of the gizmos to draw in edit mode
[Tooltip("the color of the gizmos to draw in edit mode")]
public Color GizmosColor;
protected GameObject _confinerGameObject;
protected Vector3 _gizmoSize;
#if MM_CINEMACHINE
///
/// On Awake we proceed to init if app is playing
///
protected virtual void Awake()
{
AlwaysInitialization();
if (!Application.isPlaying)
{
return;
}
Initialization();
}
///
/// On Awake we initialize our collider
///
protected virtual void AlwaysInitialization()
{
InitializeCollider();
}
///
/// On init we grab our virtual camera
///
protected virtual void Initialization()
{
if (VirtualCamera == null)
{
VirtualCamera = GetComponentInChildren();
}
if (VirtualCamera == null)
{
Debug.LogWarning("[MMCinemachineZone2D] " + this.name + " : no virtual camera is attached to this zone. Set one in its inspector.");
}
if (SetupConfinerOnStart)
{
SetupConfinerGameObject();
}
foreach (GameObject go in ActivationList)
{
go.SetActive(false);
}
}
///
/// On Start we setup the confiner
///
protected virtual void Start()
{
if (!Application.isPlaying)
{
return;
}
if (SetupConfinerOnStart)
{
SetupConfiner();
}
StartCoroutine(EnableCamera(CameraStartsActive, 1));
}
///
/// Describes what happens when initializing the collider
///
protected abstract void InitializeCollider();
///
/// Describes what happens when setting up the confiner
///
protected abstract void SetupConfiner();
///
/// A method used to manually create a confiner
///
protected virtual void ManualSetupConfiner()
{
Initialization();
SetupConfiner();
}
///
/// Creates an object to host the confiner
///
protected virtual void SetupConfinerGameObject()
{
// we remove the object if needed
Transform child = this.transform.Find("Confiner");
if (child != null)
{
DestroyImmediate(child.gameObject);
}
// we create an empty child object
_confinerGameObject = new GameObject();
_confinerGameObject.transform.localPosition = Vector3.zero;
_confinerGameObject.transform.SetParent(this.transform);
_confinerGameObject.name = "Confiner";
}
///
/// An extra test you can override to add extra collider conditions
///
///
///
protected virtual bool TestCollidingGameObject(GameObject collider)
{
return true;
}
///
/// Enables the camera, either via enabled state or priority
///
///
///
///
protected virtual IEnumerator EnableCamera(bool state, int frames)
{
if (VirtualCamera == null)
{
yield break;
}
if (frames > 0)
{
yield return MMCoroutine.WaitForFrames(frames);
}
if (Mode == Modes.Enable)
{
VirtualCamera.enabled = state;
}
else if (Mode == Modes.Priority)
{
VirtualCamera.Priority = state ? EnabledPriority : DisabledPriority;
}
}
protected virtual void EnterZone()
{
if (!RoomVisited)
{
OnEnterZoneForTheFirstTimeEvent.Invoke();
}
CurrentRoom = true;
RoomVisited = true;
OnEnterZoneEvent.Invoke();
StartCoroutine(EnableCamera(true, 0));
foreach(GameObject go in ActivationList)
{
go.SetActive(true);
}
}
protected virtual void ExitZone()
{
CurrentRoom = false;
OnExitZoneEvent.Invoke();
StartCoroutine(EnableCamera(false, 0));
foreach (GameObject go in ActivationList)
{
go.SetActive(false);
}
}
///
/// On Reset we initialize our gizmo color
///
protected virtual void Reset()
{
GizmosColor = MMColors.RandomColor();
GizmosColor.a = 0.2f;
}
#endif
}
}