From 1c9d87ec8413fa9e9e77a3ac608c316f9e94b1a0 Mon Sep 17 00:00:00 2001
From: Victor Victor <victor.victor@tu-dresden.de>
Date: Thu, 3 Jul 2025 19:15:41 +0200
Subject: [PATCH] Update how prefabs handle coroutine

---
 CHANGELOG.md                                  |  7 +++
 .../Materials/CircularButton/ButtonBorder.mat |  2 +-
 .../CircularButton/CircularButtonIcon.mat     |  2 +-
 .../Art/Materials/Controller/DiConCircle.mat  |  2 +-
 .../Art/Materials/Controller/DiConCore.mat    |  3 +-
 .../Art/Materials/Controller/TriConBound.mat  |  3 +-
 .../Materials/Controller/TriConCircleA.mat    |  2 +-
 .../Materials/Controller/TriConCircleB.mat    |  2 +-
 .../Art/Materials/Controller/TriConCore.mat   |  3 +-
 .../Scripts/CircularButton/CircularButton.cs  | 58 +++++++++++--------
 .../CircularButton/CircularHoldButton.cs      | 39 +++++++------
 Runtime/Scripts/Controller/DiConCircle.cs     | 11 +++-
 Runtime/Scripts/Controller/TriConCircle.cs    | 11 +++-
 package.json                                  |  2 +-
 14 files changed, 91 insertions(+), 56 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 336f713..f461aa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+## 1.0.4 - 2025-07-03
+
+### Changed
+
+- Update how prefabs handle coroutine.
+- Update URP material version.
+
 ## 1.0.3 - 2025-07-02
 
 ### Fixed
diff --git a/Runtime/Art/Materials/CircularButton/ButtonBorder.mat b/Runtime/Art/Materials/CircularButton/ButtonBorder.mat
index 4bf5374..399ec99 100644
--- a/Runtime/Art/Materials/CircularButton/ButtonBorder.mat
+++ b/Runtime/Art/Materials/CircularButton/ButtonBorder.mat
@@ -140,4 +140,4 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
diff --git a/Runtime/Art/Materials/CircularButton/CircularButtonIcon.mat b/Runtime/Art/Materials/CircularButton/CircularButtonIcon.mat
index eb3cb15..8b2ea27 100644
--- a/Runtime/Art/Materials/CircularButton/CircularButtonIcon.mat
+++ b/Runtime/Art/Materials/CircularButton/CircularButtonIcon.mat
@@ -137,4 +137,4 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
diff --git a/Runtime/Art/Materials/Controller/DiConCircle.mat b/Runtime/Art/Materials/Controller/DiConCircle.mat
index 978ac4f..210e459 100644
--- a/Runtime/Art/Materials/Controller/DiConCircle.mat
+++ b/Runtime/Art/Materials/Controller/DiConCircle.mat
@@ -12,7 +12,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
 --- !u!21 &2100000
 Material:
   serializedVersion: 8
diff --git a/Runtime/Art/Materials/Controller/DiConCore.mat b/Runtime/Art/Materials/Controller/DiConCore.mat
index 308f913..926ad92 100644
--- a/Runtime/Art/Materials/Controller/DiConCore.mat
+++ b/Runtime/Art/Materials/Controller/DiConCore.mat
@@ -121,6 +121,7 @@ Material:
     - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
     - _Position: {r: 0, g: 0, b: 0, a: 0}
   m_BuildTextureStacks: []
+  m_AllowLocking: 1
 --- !u!114 &6463663647676341978
 MonoBehaviour:
   m_ObjectHideFlags: 11
@@ -133,4 +134,4 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
diff --git a/Runtime/Art/Materials/Controller/TriConBound.mat b/Runtime/Art/Materials/Controller/TriConBound.mat
index 8cde9b9..54fc5be 100644
--- a/Runtime/Art/Materials/Controller/TriConBound.mat
+++ b/Runtime/Art/Materials/Controller/TriConBound.mat
@@ -12,7 +12,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
 --- !u!21 &2100000
 Material:
   serializedVersion: 8
@@ -144,3 +144,4 @@ Material:
     - _FrontColor: {r: 5.5908804, g: 0, b: 3.9941745, a: 0.31764707}
     - _VertexAmount: {r: 0.02, g: 0.02, b: 0.02, a: 0}
   m_BuildTextureStacks: []
+  m_AllowLocking: 1
diff --git a/Runtime/Art/Materials/Controller/TriConCircleA.mat b/Runtime/Art/Materials/Controller/TriConCircleA.mat
index 0b3a690..0ee47a7 100644
--- a/Runtime/Art/Materials/Controller/TriConCircleA.mat
+++ b/Runtime/Art/Materials/Controller/TriConCircleA.mat
@@ -12,7 +12,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
 --- !u!21 &2100000
 Material:
   serializedVersion: 8
diff --git a/Runtime/Art/Materials/Controller/TriConCircleB.mat b/Runtime/Art/Materials/Controller/TriConCircleB.mat
index 4a2b86e..7f31b34 100644
--- a/Runtime/Art/Materials/Controller/TriConCircleB.mat
+++ b/Runtime/Art/Materials/Controller/TriConCircleB.mat
@@ -12,7 +12,7 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
 --- !u!21 &2100000
 Material:
   serializedVersion: 8
diff --git a/Runtime/Art/Materials/Controller/TriConCore.mat b/Runtime/Art/Materials/Controller/TriConCore.mat
index 9367b4e..5624001 100644
--- a/Runtime/Art/Materials/Controller/TriConCore.mat
+++ b/Runtime/Art/Materials/Controller/TriConCore.mat
@@ -121,6 +121,7 @@ Material:
     - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
     - _Position: {r: -2.1472923e-10, g: 0.00000074505806, b: 0.00000074505806, a: 0}
   m_BuildTextureStacks: []
+  m_AllowLocking: 1
 --- !u!114 &6463663647676341978
 MonoBehaviour:
   m_ObjectHideFlags: 11
@@ -133,4 +134,4 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  version: 9
+  version: 10
diff --git a/Runtime/Scripts/CircularButton/CircularButton.cs b/Runtime/Scripts/CircularButton/CircularButton.cs
index 77b67c3..497d985 100644
--- a/Runtime/Scripts/CircularButton/CircularButton.cs
+++ b/Runtime/Scripts/CircularButton/CircularButton.cs
@@ -18,19 +18,21 @@ namespace Hyper.Interaction
         [SerializeField] protected Transform buttonVisualTransform;
         [SerializeField] protected PokeInteractable pokeInteractable;
 
-        protected bool IsHovered;
-        protected float StartingButtonVisualZPosition;
+        protected bool _isHovered;
+        protected float _startingButtonVisualZPosition;
+        protected Coroutine _animateActiveStateCoroutine;
+        private Coroutine _activateAfterDelayCoroutine;
 
         private void Awake()
         {
-            StartingButtonVisualZPosition = buttonVisualTransform.localPosition.z;
+            _startingButtonVisualZPosition = buttonVisualTransform.localPosition.z;
         }
 
         private void Update()
         {
-            if (!IsHovered) return;
+            if (!_isHovered) return;
 
-            var normalizedValue = Mathf.InverseLerp(0, StartingButtonVisualZPosition, buttonVisualTransform.localPosition.z);
+            var normalizedValue = Mathf.InverseLerp(0, _startingButtonVisualZPosition, buttonVisualTransform.localPosition.z);
             var newScaleValue = Mathf.Lerp(borderPressScale, borderDefaultScale, normalizedValue);
 
             borderTransform.localScale = new Vector3(newScaleValue, newScaleValue, 1f);
@@ -38,46 +40,46 @@ namespace Hyper.Interaction
 
         public void OnHover()
         {
-            IsHovered = true;
+            _isHovered = true;
         }
 
         public virtual void OnUnhover()
         {
-            IsHovered = false;
+            _isHovered = false;
             borderTransform.localScale = new Vector3(borderDefaultScale, borderDefaultScale, 1f);
         }
 
         public virtual void OnSelect()
         {
-            StartCoroutine(AnimateBorder());
+            StartCoroutine(AnimateBorderCoroutine());
         }
 
         public void Activate()
         {
-            gameObject.SetActive(true);
-            StartCoroutine(AnimateActiveState(-180f, 0, true));
+            if (_animateActiveStateCoroutine != null) StopCoroutine(_animateActiveStateCoroutine);
+            _animateActiveStateCoroutine = StartCoroutine(AnimateActiveStateCoroutine(-180f, 0, true));
         }
 
         public void Deactivate()
         {
-            if (gameObject.activeSelf)
-                StartCoroutine(AnimateActiveState(0, 180f, false));
+            if (_animateActiveStateCoroutine != null) StopCoroutine(_animateActiveStateCoroutine);
+            _animateActiveStateCoroutine = StartCoroutine(AnimateActiveStateCoroutine(0, 180f, false));
         }
 
-        public virtual void ActivateAfterDelay(float delay)
+        public void ActivateAfterDelay(float delay)
         {
-            transform.localRotation = Quaternion.Euler(-180f, 0, 0);
-            gameObject.SetActive(true);
-            StartCoroutine(ActivateAfterDelayCoroutine(delay));
+            if (_activateAfterDelayCoroutine != null) StopCoroutine(_activateAfterDelayCoroutine);
+            _activateAfterDelayCoroutine = StartCoroutine(ActivateAfterDelayCoroutine(delay));
         }
 
-        protected IEnumerator ActivateAfterDelayCoroutine(float delay)
+        private IEnumerator ActivateAfterDelayCoroutine(float delay)
         {
             yield return new WaitForSeconds(delay);
-            StartCoroutine(AnimateActiveState(-180f, 0, true));
+            Activate();
+            _activateAfterDelayCoroutine = null;
         }
 
-        protected IEnumerator AnimateBorder()
+        protected IEnumerator AnimateBorderCoroutine()
         {
             var elapsedTime = 0f;
 
@@ -93,15 +95,19 @@ namespace Hyper.Interaction
                 yield return null;
             }
 
-            if (IsHovered)
+            if (_isHovered)
                 borderTransform.localScale = new Vector3(borderPressScale, borderPressScale, 1f);
             else
                 borderTransform.localScale = new Vector3(borderDefaultScale, borderDefaultScale, 1f);
         }
 
-        protected virtual IEnumerator AnimateActiveState(float startXRotation, float endXRotation, bool activeState)
+        protected virtual IEnumerator AnimateActiveStateCoroutine(float startXRotation, float endXRotation, bool activeState)
         {
-            if (!activeState)
+            if (activeState)
+            {
+                pokeInteractable.gameObject.SetActive(true);
+            }
+            else
             {
                 yield return new WaitForSeconds(borderAnimDuration);
                 pokeInteractable.enabled = false;
@@ -123,9 +129,15 @@ namespace Hyper.Interaction
             transform.localRotation = Quaternion.Euler(0, 0, 0);
 
             if (activeState)
+            {
                 pokeInteractable.enabled = true;
+            }
             else
-                gameObject.SetActive(false);
+            {
+                pokeInteractable.gameObject.SetActive(false);
+            }
+
+            _animateActiveStateCoroutine = null;
         }
     }
 }
diff --git a/Runtime/Scripts/CircularButton/CircularHoldButton.cs b/Runtime/Scripts/CircularButton/CircularHoldButton.cs
index eafaf97..7a2e035 100644
--- a/Runtime/Scripts/CircularButton/CircularHoldButton.cs
+++ b/Runtime/Scripts/CircularButton/CircularHoldButton.cs
@@ -23,7 +23,7 @@ namespace Hyper.Interaction
 
         private void Awake()
         {
-            StartingButtonVisualZPosition = buttonVisualTransform.localPosition.z;
+            _startingButtonVisualZPosition = buttonVisualTransform.localPosition.z;
             _greyCircleImage = greyCircleGameObject.GetComponent<Image>();
             _greenCircleImage = greenCircleGameObject.GetComponent<Image>();
             _greyCircleRectTransform = greyCircleGameObject.GetComponent<RectTransform>();
@@ -38,8 +38,8 @@ namespace Hyper.Interaction
                     _isHolding = false;
                     buttonBorderHoldGameObject.SetActive(false);
                     borderTransform.gameObject.SetActive(true);
-                    StartCoroutine(AnimateBorder());
-                    StartCoroutine(ResetButton());
+                    StartCoroutine(AnimateBorderCoroutine());
+                    StartCoroutine(ResetButtonCoroutine());
 
                     whenHoldDone?.Invoke();
                 }
@@ -59,9 +59,9 @@ namespace Hyper.Interaction
                 _greenCircleImage.fillAmount = 0;
             }
 
-            if (!IsHovered) return;
+            if (!_isHovered) return;
 
-            var normalizedValue = Mathf.InverseLerp(0, StartingButtonVisualZPosition, buttonVisualTransform.localPosition.z);
+            var normalizedValue = Mathf.InverseLerp(0, _startingButtonVisualZPosition, buttonVisualTransform.localPosition.z);
             var newScaleValue = Mathf.Lerp(borderPressScale, borderDefaultScale, normalizedValue);
 
             _greyCircleRectTransform.localScale = new Vector3(newScaleValue, newScaleValue, 1f);
@@ -69,7 +69,7 @@ namespace Hyper.Interaction
 
         public override void OnUnhover()
         {
-            IsHovered = false;
+            _isHovered = false;
             _greyCircleRectTransform.localScale = new Vector3(borderDefaultScale, borderDefaultScale, 1f);
         }
 
@@ -83,15 +83,7 @@ namespace Hyper.Interaction
             _isHolding = false;
         }
 
-        public override void ActivateAfterDelay(float delay)
-        {
-            _greyCircleImage.enabled = false;
-            transform.localRotation = Quaternion.Euler(-180f, 0, 0);
-            gameObject.SetActive(true);
-            StartCoroutine(ActivateAfterDelayCoroutine(delay));
-        }
-
-        private IEnumerator ResetButton()
+        private IEnumerator ResetButtonCoroutine()
         {
             yield return new WaitForSeconds(borderAnimDuration);
 
@@ -102,9 +94,14 @@ namespace Hyper.Interaction
             _isHoldDone = false;
         }
 
-        protected override IEnumerator AnimateActiveState(float startXRotation, float endXRotation, bool activeState)
+        protected override IEnumerator AnimateActiveStateCoroutine(float startXRotation, float endXRotation, bool activeState)
         {
-            if (!activeState)
+            if (activeState)
+            {
+                _greyCircleImage.enabled = false;
+                pokeInteractable.gameObject.SetActive(true);
+            }
+            else
             {
                 yield return new WaitForSeconds(borderAnimDuration);
                 pokeInteractable.enabled = false;
@@ -128,9 +125,15 @@ namespace Hyper.Interaction
             transform.localRotation = Quaternion.Euler(0, 0, 0);
 
             if (activeState)
+            {
                 pokeInteractable.enabled = true;
+            }
             else
-                gameObject.SetActive(false);
+            {
+                pokeInteractable.gameObject.SetActive(false);
+            }
+
+            _animateActiveStateCoroutine = null;
         }
     }
 }
diff --git a/Runtime/Scripts/Controller/DiConCircle.cs b/Runtime/Scripts/Controller/DiConCircle.cs
index 65633be..1aa145e 100644
--- a/Runtime/Scripts/Controller/DiConCircle.cs
+++ b/Runtime/Scripts/Controller/DiConCircle.cs
@@ -13,6 +13,7 @@ namespace Hyper.Interaction
 
         private Material _material;
         private Color _color;
+        private Coroutine _animateCoroutine;
 
         private void Start()
         {
@@ -22,15 +23,17 @@ namespace Hyper.Interaction
 
         public void OnHover()
         {
-            StartCoroutine(Animate(unhoverScale, hoverScale, 0, 1f, animDuration));
+            if (_animateCoroutine != null) StopCoroutine(_animateCoroutine);
+            _animateCoroutine = StartCoroutine(AnimateCoroutine(unhoverScale, hoverScale, 0, 1f, animDuration));
         }
 
         public void OnUnhover()
         {
-            StartCoroutine(Animate(hoverScale, unhoverScale, 1f, 0, animDuration));
+            if (_animateCoroutine != null) StopCoroutine(_animateCoroutine);
+            _animateCoroutine = StartCoroutine(AnimateCoroutine(hoverScale, unhoverScale, 1f, 0, animDuration));
         }
 
-        private IEnumerator Animate(
+        private IEnumerator AnimateCoroutine(
             float startScale, float endScale,
             float startAlpha, float endAlpha,
             float duration
@@ -52,6 +55,8 @@ namespace Hyper.Interaction
                 yield return null;
             }
             transform.localScale = new Vector3(endScale, endScale, 1f);
+
+            _animateCoroutine = null;
         }
     }
 }
diff --git a/Runtime/Scripts/Controller/TriConCircle.cs b/Runtime/Scripts/Controller/TriConCircle.cs
index f8994b8..d9f2b03 100644
--- a/Runtime/Scripts/Controller/TriConCircle.cs
+++ b/Runtime/Scripts/Controller/TriConCircle.cs
@@ -13,6 +13,7 @@ namespace Hyper.Interaction
         [SerializeField] private AnimationCurve animCurve;
 
         private Material _material;
+        private Coroutine _animateCoroutine;
 
         private void Start()
         {
@@ -21,15 +22,17 @@ namespace Hyper.Interaction
 
         public void OnHover()
         {
-            StartCoroutine(Animate(unhoverRotation, hoverRotation, 0, 1f, animDuration));
+            if (_animateCoroutine != null) StopCoroutine(_animateCoroutine);
+            _animateCoroutine = StartCoroutine(AnimateCoroutine(unhoverRotation, hoverRotation, 0, 1f, animDuration));
         }
 
         public void OnUnhover()
         {
-            StartCoroutine(Animate(hoverRotation, unhoverRotation, 1f, 0, animDuration));
+            if (_animateCoroutine != null) StopCoroutine(_animateCoroutine);
+            _animateCoroutine = StartCoroutine(AnimateCoroutine(hoverRotation, unhoverRotation, 1f, 0, animDuration));
         }
 
-        private IEnumerator Animate(
+        private IEnumerator AnimateCoroutine(
             Vector3 startRotation, Vector3 endRotation,
             float startAlpha, float endAlpha,
             float duration
@@ -52,6 +55,8 @@ namespace Hyper.Interaction
                 yield return null;
             }
             transform.rotation = Quaternion.Euler(endRotation);
+
+            _animateCoroutine = null;
         }
     }
 }
diff --git a/package.json b/package.json
index 295594b..74f1844 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "de.tu-dresden.hyper.interaction",
-  "version": "1.0.3",
+  "version": "1.0.4",
   "displayName": "HYPER Interaction",
   "description": "This package provides ready-to-use interaction components.",
   "unity": "6000.0",
-- 
GitLab