using System.Collections; using System.Linq; using UnityEngine; using Pixelplacement; using Minigames.DivingForPictures; using Input; using UnityEngine.Events; public class DivingTutorial : MonoBehaviour, ITouchInputConsumer { private StateMachine stateMachine; public DivingGameManager divingGameManager; public bool playTutorial; // gating for input until current state's animation finishes first loop private bool canAcceptInput = false; private Coroutine waitLoopCoroutine; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { if (playTutorial == true) { InitializeTutorial(); } else { RemoveTutorial(); } } void InitializeTutorial() { stateMachine = GetComponentInChildren(); divingGameManager.Pause(); InputManager.Instance.RegisterOverrideConsumer(this); stateMachine.OnLastStateExited.AddListener(RemoveTutorial); // prepare gating for the initial active state SetupInputGateForCurrentState(); } void RemoveTutorial() { Debug.Log("Remove me!"); if (waitLoopCoroutine != null) { StopCoroutine(waitLoopCoroutine); waitLoopCoroutine = null; } InputManager.Instance.UnregisterOverrideConsumer(this); divingGameManager.DoResume(); Destroy(gameObject); } public void OnTap(Vector2 position) { if (!canAcceptInput) return; // block taps until allowed // consume this tap and immediately block further taps canAcceptInput = false; // move to next state stateMachine.Next(true); // after the state changes, set up gating for the new active state's animation SetupInputGateForCurrentState(); } public void OnHoldStart(Vector2 position) { return; } public void OnHoldMove(Vector2 position) { return; } public void OnHoldEnd(Vector2 position) { return; } private void SetupInputGateForCurrentState() { if (waitLoopCoroutine != null) { StopCoroutine(waitLoopCoroutine); waitLoopCoroutine = null; } waitLoopCoroutine = StartCoroutine(WaitForFirstLoopOnActiveState()); } private IEnumerator WaitForFirstLoopOnActiveState() { // wait a frame to ensure StateMachine has activated the correct state GameObject yield return null; // find the active child under the StateMachine (the current state) Transform smTransform = stateMachine != null ? stateMachine.transform : transform; Transform activeState = null; for (int i = 0; i < smTransform.childCount; i++) { var child = smTransform.GetChild(i); if (child.gameObject.activeInHierarchy) { activeState = child; break; } } if (activeState == null) { // if we can't find an active state, fail open: allow input canAcceptInput = true; yield break; } // look for a legacy Animation component on the active state var anim = activeState.GetComponent(); if (anim == null) { // no animation to wait for; allow input immediately canAcceptInput = true; yield break; } // determine a clip/state to observe string clipName = anim.clip != null ? anim.clip.name : null; AnimationState observedState = null; if (!string.IsNullOrEmpty(clipName)) { observedState = anim[clipName]; } else { // fallback: take the first enabled state in the Animation foreach (AnimationState st in anim) { observedState = st; break; } } if (observedState == null) { // nothing to observe; allow input canAcceptInput = true; yield break; } // wait until the animation starts playing the observed clip float safetyTimer = 0f; while (anim.isActiveAndEnabled && activeState.gameObject.activeInHierarchy && !anim.IsPlaying(observedState.name) && safetyTimer < 2f) { safetyTimer += Time.deltaTime; yield return null; } // wait until the first loop completes (normalizedTime >= 1) while (anim.isActiveAndEnabled && activeState.gameObject.activeInHierarchy) { // if state changed (not playing anymore), allow input to avoid deadlock if (!anim.IsPlaying(observedState.name)) break; if (observedState.normalizedTime >= 1f) { break; } yield return null; } canAcceptInput = true; waitLoopCoroutine = null; } }