using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using DuloGames.UI.Tweens; using System; namespace DuloGames.UI { [DisallowMultipleComponent, ExecuteInEditMode, RequireComponent(typeof(CanvasGroup)), AddComponentMenu("UI/UI Scene/Scene")] public class UIScene : MonoBehaviour { public enum Type { Preloaded, Prefab, Resource } public enum Transition { None, Animation, CrossFade, SlideFromRight, SlideFromLeft, SlideFromTop, SlideFromBottom } [Serializable] public class OnActivateEvent : UnityEvent { } [Serializable] public class OnDeactivateEvent : UnityEvent { } private UISceneRegistry m_SceneManager; private bool m_AnimationState = false; [SerializeField] private int m_Id = 0; [SerializeField] private bool m_IsActivated = true; [SerializeField] private Type m_Type = Type.Preloaded; [SerializeField] private Transform m_Content; [SerializeField] private GameObject m_Prefab; [SerializeField][ResourcePath] private string m_Resource; [SerializeField] private Transition m_Transition = Transition.None; [SerializeField] private float m_TransitionDuration = 0.2f; [SerializeField] private TweenEasing m_TransitionEasing = TweenEasing.InOutQuint; [SerializeField] private string m_AnimateInTrigger = "AnimateIn"; [SerializeField] private string m_AnimateOutTrigger = "AnimateOut"; [SerializeField] private GameObject m_FirstSelected; /// /// Gets the scene id. /// public int id { get { return this.m_Id; } } /// /// Gets or sets value indicating whether the scene is activated. /// public bool isActivated { get { return this.m_IsActivated; } set { if (value) { this.Activate(); } else { this.Deactivate(); } } } /// /// Gets the scene type. /// public Type type { get { return this.m_Type; } } /// /// Gets or sets the scene content holder. /// public Transform content { get { return this.m_Content; } set { this.m_Content = value; } } /// /// Gets or sets the scene transition. /// public Transition transition { get { return this.m_Transition; } set { this.m_Transition = value; } } /// /// Gets or sets the transition duration. /// public float transitionDuration { get { return this.m_TransitionDuration; } set { this.m_TransitionDuration = value; } } /// /// Gets or set the transition easing. /// public TweenEasing transitionEasing { get { return this.m_TransitionEasing; } set { this.m_TransitionEasing = value; } } /// /// Gets or sets the animate in trigger. /// public string animateInTrigger { get { return this.m_AnimateInTrigger; } set { this.m_AnimateInTrigger = value; } } /// /// Gets or sets the animate out trigger. /// public string animateOutTrigger { get { return this.m_AnimateOutTrigger; } set { this.m_AnimateOutTrigger = value; } } /// /// Invoked when the scene is activated. /// public OnActivateEvent onActivate = new OnActivateEvent(); /// /// Invoked when the scene is deactivated. /// public OnDeactivateEvent onDeactivate = new OnDeactivateEvent(); /// /// Gets the rect transform. /// public RectTransform rectTransform { get { return (this.transform as RectTransform); } } /// /// Gets the animator. /// /// The animator. public Animator animator { get { return this.gameObject.GetComponent(); } } // The canvas group private CanvasGroup m_CanvasGroup; // Tween controls [NonSerialized] private readonly TweenRunner m_FloatTweenRunner; // Called by Unity prior to deserialization, // should not be called by users protected UIScene() { if (this.m_FloatTweenRunner == null) this.m_FloatTweenRunner = new TweenRunner(); this.m_FloatTweenRunner.Init(this); } protected virtual void Awake() { // Get the scene mangaer this.m_SceneManager = UISceneRegistry.instance; if (this.m_SceneManager == null) { Debug.LogWarning("Scene registry does not exist."); this.enabled = false; return; } // Set the initial animation state this.m_AnimationState = this.m_IsActivated; // Get the canvas group this.m_CanvasGroup = this.gameObject.GetComponent(); // Set the first selected game object for the navigation if (Application.isPlaying && this.isActivated && this.isActiveAndEnabled && this.m_FirstSelected != null) { EventSystem.current.SetSelectedGameObject(this.m_FirstSelected); } } protected virtual void Start() { } protected virtual void OnEnable() { // Register the scene if (this.m_SceneManager != null) { // Register only if in the scene if (this.gameObject.activeInHierarchy) { this.m_SceneManager.RegisterScene(this); } } // Trigger the on activate event if (this.isActivated && this.onActivate != null) this.onActivate.Invoke(this); } protected virtual void OnDisable() { // Unregister the scene if (this.m_SceneManager != null) { this.m_SceneManager.UnregisterScene(this); } } #if UNITY_EDITOR protected virtual void OnValidate() { // Check for duplicate id if (this.m_SceneManager != null) { UIScene[] scenes = this.m_SceneManager.scenes; foreach (UIScene scene in scenes) { if (!scene.Equals(this) && scene.id == this.m_Id) { Debug.LogWarning("Duplicate scene ids, change the id of scene: " + this.gameObject.name); this.m_Id = this.m_SceneManager.GetAvailableSceneId(); break; } } } // Activate or deactivate the scene if (this.m_Type == Type.Preloaded) { if (this.m_IsActivated) { // Enable the game object if (this.m_Content != null) this.m_Content.gameObject.SetActive(true); } else { // Disable the game object if (this.m_Content != null) this.m_Content.gameObject.SetActive(false); } } this.m_TransitionDuration = Mathf.Max(this.m_TransitionDuration, 0f); } #endif protected void Update() { if (this.animator != null && !string.IsNullOrEmpty(this.m_AnimateInTrigger) && !string.IsNullOrEmpty(this.m_AnimateOutTrigger)) { AnimatorStateInfo state = this.animator.GetCurrentAnimatorStateInfo(0); // Check which is the current state if (state.IsName(this.m_AnimateInTrigger) && !this.m_AnimationState) { if (state.normalizedTime >= state.length) { // Flag as opened this.m_AnimationState = true; // On animation finished this.OnTransitionIn(); } } else if (state.IsName(this.m_AnimateOutTrigger) && this.m_AnimationState) { if (state.normalizedTime >= state.length) { // Flag as closed this.m_AnimationState = false; // On animation finished this.OnTransitionOut(); } } } } /// /// Activate the scene, no transition is used. /// public void Activate() { // Make sure the scene is active and enabled if (!this.isActiveAndEnabled || !this.gameObject.activeInHierarchy) return; // If it's prefab if (this.m_Type == Type.Prefab || this.m_Type == Type.Resource) { GameObject prefab = null; if (this.m_Type == Type.Prefab) { // Check the prefab if (this.m_Prefab == null) { Debug.LogWarning("You are activating a prefab scene and no prefab is specified."); return; } prefab = this.m_Prefab; } if (this.m_Type == Type.Resource) { // Try loading the resource if (string.IsNullOrEmpty(this.m_Resource)) { Debug.LogWarning("You are activating a resource scene and no resource path is specified."); return; } prefab = Resources.Load(this.m_Resource); } // Instantiate the prefab if (prefab != null) { // Instantiate the prefab GameObject obj = Instantiate(prefab); // Set the content variable this.m_Content = obj.transform; // Set parent this.m_Content.SetParent(this.transform); // Check if it's a rect transform if (this.m_Content is RectTransform) { // Get the rect transform RectTransform rectTransform = this.m_Content as RectTransform; // Prepare the rect rectTransform.localScale = Vector3.one; rectTransform.localPosition = Vector3.zero; // Set anchor and pivot rectTransform.anchorMin = new Vector2(0f, 0f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 0.5f); // Get the canvas size Canvas canvas = UIUtility.FindInParents(this.gameObject); if (canvas == null) { canvas = this.gameObject.GetComponentInChildren(); } if (canvas != null) { RectTransform crt = canvas.transform as RectTransform; rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, crt.sizeDelta.x); rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, crt.sizeDelta.y); } // Set position rectTransform.anchoredPosition3D = new Vector3(0f, 0f, 0f); } } } // Enable the game object if (this.m_Content != null) { this.m_Content.gameObject.SetActive(true); } // Set the first selected for the navigation if (this.isActiveAndEnabled && this.m_FirstSelected != null) { EventSystem.current.SetSelectedGameObject(this.m_FirstSelected); } // Set the active variable this.m_IsActivated = true; // Invoke the event if (this.onActivate != null) { this.onActivate.Invoke(this); } } /// /// Deactivate the scene, no transition is used. /// public void Deactivate() { // Disable the game object if (this.m_Content != null) { this.m_Content.gameObject.SetActive(false); } // If prefab destroy the object if (this.m_Type == Type.Prefab || this.m_Type == Type.Resource) { Destroy(this.m_Content.gameObject); } // Unload unused assets Resources.UnloadUnusedAssets(); // Set the active variable this.m_IsActivated = false; // Invoke the event if (this.onDeactivate != null) { this.onDeactivate.Invoke(this); } } /// /// Transition to the scene. /// public void TransitionTo() { // Make sure the scene is active and enabled if (!this.isActiveAndEnabled || !this.gameObject.activeInHierarchy) return; if (this.m_SceneManager != null) { this.m_SceneManager.TransitionToScene(this); } } /// /// Transition the scene in. /// public void TransitionIn() { this.TransitionIn(this.m_Transition, this.m_TransitionDuration, this.m_TransitionEasing); } /// /// Transition the scene in. /// /// The transition. /// The transition duration. /// The transition easing. public void TransitionIn(Transition transition, float duration, TweenEasing easing) { // Make sure the scene is active and enabled if (!this.isActiveAndEnabled || !this.gameObject.activeInHierarchy) return; if (this.m_CanvasGroup == null) return; // If no transition is used if (transition == Transition.None) { this.Activate(); return; } // If the transition is animation if (transition == Transition.Animation) { this.Activate(); this.TriggerAnimation(this.m_AnimateInTrigger); return; } // Make the scene non interactable //this.m_CanvasGroup.interactable = false; //this.m_CanvasGroup.blocksRaycasts = false; // Prepare some variable Vector2 rectSize = this.rectTransform.rect.size; // Prepare the rect transform if (transition == Transition.SlideFromLeft || transition == Transition.SlideFromRight || transition == Transition.SlideFromTop || transition == Transition.SlideFromBottom) { // Anchor and pivot top left this.rectTransform.pivot = new Vector2(0f, 1f); this.rectTransform.anchorMin = new Vector2(0f, 1f); this.rectTransform.anchorMax = new Vector2(0f, 1f); this.rectTransform.sizeDelta = rectSize; } // Prepare the tween FloatTween floatTween = new FloatTween(); floatTween.duration = duration; switch (transition) { case Transition.CrossFade: this.m_CanvasGroup.alpha = 0f; floatTween.startFloat = 0f; floatTween.targetFloat = 1f; floatTween.AddOnChangedCallback(SetCanvasAlpha); break; case Transition.SlideFromRight: this.rectTransform.anchoredPosition = new Vector2(rectSize.x, 0f); floatTween.startFloat = rectSize.x; floatTween.targetFloat = 0f; floatTween.AddOnChangedCallback(SetPositionX); break; case Transition.SlideFromLeft: this.rectTransform.anchoredPosition = new Vector2((rectSize.x * -1f), 0f); floatTween.startFloat = (rectSize.x * -1f); floatTween.targetFloat = 0f; floatTween.AddOnChangedCallback(SetPositionX); break; case Transition.SlideFromBottom: this.rectTransform.anchoredPosition = new Vector2(0f, (rectSize.y * -1f)); floatTween.startFloat = (rectSize.y * -1f); floatTween.targetFloat = 0f; floatTween.AddOnChangedCallback(SetPositionY); break; case Transition.SlideFromTop: this.rectTransform.anchoredPosition = new Vector2(0f, rectSize.y); floatTween.startFloat = rectSize.y; floatTween.targetFloat = 0f; floatTween.AddOnChangedCallback(SetPositionY); break; } // Activate the scene this.Activate(); // Start the transition floatTween.AddOnFinishCallback(OnTransitionIn); floatTween.ignoreTimeScale = true; floatTween.easing = easing; this.m_FloatTweenRunner.StartTween(floatTween); } /// /// Transition the scene out. /// public void TransitionOut() { this.TransitionOut(this.m_Transition, this.m_TransitionDuration, this.m_TransitionEasing); } /// /// Transition the scene out. /// /// The transition. /// The transition duration. /// The transition easing. public void TransitionOut(Transition transition, float duration, TweenEasing easing) { // Make sure the scene is active and enabled if (!this.isActiveAndEnabled || !this.gameObject.activeInHierarchy) return; if (this.m_CanvasGroup == null) return; // If no transition is used if (transition == Transition.None) { this.Deactivate(); return; } // If the transition is animation if (transition == Transition.Animation) { this.TriggerAnimation(this.m_AnimateOutTrigger); return; } // Make the scene non interactable //this.m_CanvasGroup.interactable = false; //this.m_CanvasGroup.blocksRaycasts = false; // Prepare some variable Vector2 rectSize = this.rectTransform.rect.size; // Prepare the rect transform if (transition == Transition.SlideFromLeft || transition == Transition.SlideFromRight || transition == Transition.SlideFromTop || transition == Transition.SlideFromBottom) { // Anchor and pivot top left this.rectTransform.pivot = new Vector2(0f, 1f); this.rectTransform.anchorMin = new Vector2(0f, 1f); this.rectTransform.anchorMax = new Vector2(0f, 1f); this.rectTransform.sizeDelta = rectSize; this.rectTransform.anchoredPosition = new Vector2(0f, 0f); } // Prepare the tween FloatTween floatTween = new FloatTween(); floatTween.duration = duration; switch (transition) { case Transition.CrossFade: this.m_CanvasGroup.alpha = 1f; // Start the tween floatTween.startFloat = this.m_CanvasGroup.alpha; floatTween.targetFloat = 0f; floatTween.AddOnChangedCallback(SetCanvasAlpha); break; case Transition.SlideFromRight: // Start the tween floatTween.startFloat = 0f; floatTween.targetFloat = (rectSize.x * -1f); floatTween.AddOnChangedCallback(SetPositionX); break; case Transition.SlideFromLeft: // Start the tween floatTween.startFloat = 0f; floatTween.targetFloat = rectSize.x; floatTween.AddOnChangedCallback(SetPositionX); break; case Transition.SlideFromBottom: // Start the tween floatTween.startFloat = 0f; floatTween.targetFloat = rectSize.y; floatTween.AddOnChangedCallback(SetPositionY); break; case Transition.SlideFromTop: // Start the tween floatTween.startFloat = 0f; floatTween.targetFloat = (rectSize.y * -1f); floatTween.AddOnChangedCallback(SetPositionY); break; } // Start the transition floatTween.AddOnFinishCallback(OnTransitionOut); floatTween.ignoreTimeScale = true; floatTween.easing = easing; this.m_FloatTweenRunner.StartTween(floatTween); } /// /// Starts alpha tween. /// /// Target alpha. /// Duration. /// Easing. /// If set to true ignore time scale. /// Event to be called on transition finish. public void StartAlphaTween(float targetAlpha, float duration, TweenEasing easing, bool ignoreTimeScale, UnityAction callback) { if (this.m_CanvasGroup == null) return; // Start the tween var floatTween = new FloatTween { duration = duration, startFloat = this.m_CanvasGroup.alpha, targetFloat = targetAlpha }; floatTween.AddOnChangedCallback(SetCanvasAlpha); floatTween.AddOnFinishCallback(callback); floatTween.ignoreTimeScale = ignoreTimeScale; floatTween.easing = easing; this.m_FloatTweenRunner.StartTween(floatTween); } /// /// Sets the canvas alpha. /// /// Alpha. public void SetCanvasAlpha(float alpha) { if (this.m_CanvasGroup == null) return; // Set the alpha this.m_CanvasGroup.alpha = alpha; } /// /// Sets the rect transform anchored position X. /// /// The X position. public void SetPositionX(float x) { this.rectTransform.anchoredPosition = new Vector2(x, this.rectTransform.anchoredPosition.y); } /// /// Sets the rect transform anchored position Y. /// /// The Y position. public void SetPositionY(float y) { this.rectTransform.anchoredPosition = new Vector2(this.rectTransform.anchoredPosition.x, y); } /// /// Triggers animation. /// /// private void TriggerAnimation(string triggername) { // Get the animator on the target game object Animator animator = this.gameObject.GetComponent(); if (animator == null || !animator.enabled || !animator.isActiveAndEnabled || animator.runtimeAnimatorController == null || !animator.hasBoundPlayables || string.IsNullOrEmpty(triggername)) return; animator.ResetTrigger(this.m_AnimateInTrigger); animator.ResetTrigger(this.m_AnimateOutTrigger); animator.SetTrigger(triggername); } /// /// Raises the transition in finished event. /// protected virtual void OnTransitionIn() { // Re-enable the canvas group interaction if (this.m_CanvasGroup != null) { //this.m_CanvasGroup.interactable = true; //this.m_CanvasGroup.blocksRaycasts = true; } } /// /// Raises the transition out finished event. /// protected virtual void OnTransitionOut() { // Deactivate the scene this.Deactivate(); // Re-enable the canvas group interaction if (this.m_CanvasGroup != null) { //this.m_CanvasGroup.interactable = true; //this.m_CanvasGroup.blocksRaycasts = true; } // Reset the alpha this.SetCanvasAlpha(1f); // Reset the position of the transform this.SetPositionX(0f); } } }