From bb43c552b00209f77c02515c205491231d0d510c Mon Sep 17 00:00:00 2001 From: Michal Adam Pikulski Date: Tue, 21 Oct 2025 12:51:24 +0200 Subject: [PATCH] Block input on first loop --- Assets/Scripts/UI/Tutorial/DivingTutorial.cs | 123 ++++++++++++++++++- 1 file changed, 118 insertions(+), 5 deletions(-) diff --git a/Assets/Scripts/UI/Tutorial/DivingTutorial.cs b/Assets/Scripts/UI/Tutorial/DivingTutorial.cs index 16e9a8e6..3063a3ac 100644 --- a/Assets/Scripts/UI/Tutorial/DivingTutorial.cs +++ b/Assets/Scripts/UI/Tutorial/DivingTutorial.cs @@ -1,3 +1,5 @@ +using System.Collections; +using System.Linq; using UnityEngine; using Pixelplacement; using Minigames.DivingForPictures; @@ -10,17 +12,18 @@ public class DivingTutorial : MonoBehaviour, ITouchInputConsumer 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) + if (playTutorial == true) { InitializeTutorial(); } else { RemoveTutorial(); } - - - } void InitializeTutorial() @@ -30,19 +33,35 @@ public class DivingTutorial : MonoBehaviour, ITouchInputConsumer InputManager.Instance.RegisterOverrideConsumer(this); stateMachine.OnLastStateExited.AddListener(RemoveTutorial); + // prepare gating for the initial active state + SetupInputGateForCurrentState(); } void RemoveTutorial() { Debug.Log("Remove me!"); - Destroy(gameObject); + 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) @@ -59,4 +78,98 @@ public class DivingTutorial : MonoBehaviour, ITouchInputConsumer { 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; + } }