app.hypergames.hypersdk 1.3.4 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/Editor/{HyperSDK.Editor.asmdef → Hyper.Editor.asmdef} +1 -1
- package/Editor/Resources/ControlBarPreviews/Dark/Default.png +0 -0
- package/Editor/Resources/ControlBarPreviews/Dark/Default.png.meta +143 -0
- package/Editor/Resources/ControlBarPreviews/Dark/HealthBarLeft.png +0 -0
- package/Editor/Resources/ControlBarPreviews/Dark/HealthBarLeft.png.meta +143 -0
- package/Editor/Resources/ControlBarPreviews/Dark/HeartsLeft.png +0 -0
- package/Editor/Resources/ControlBarPreviews/Dark/HeartsLeft.png.meta +169 -0
- package/Editor/Resources/ControlBarPreviews/Dark.meta +8 -0
- package/Editor/Resources/ControlBarPreviews/Light/Default.png +0 -0
- package/Editor/Resources/ControlBarPreviews/Light/Default.png.meta +143 -0
- package/Editor/Resources/ControlBarPreviews/Light/HealthBarLeft.png +0 -0
- package/Editor/Resources/ControlBarPreviews/Light/HealthBarLeft.png.meta +143 -0
- package/Editor/Resources/ControlBarPreviews/Light/HeartsLeft.png +0 -0
- package/Editor/Resources/ControlBarPreviews/Light/HeartsLeft.png.meta +143 -0
- package/Editor/Resources/ControlBarPreviews/Light.meta +8 -0
- package/Editor/Resources/ControlBarPreviews.meta +8 -0
- package/Editor/Scripts/Inspectors/HyperSDKSettingsEditor.cs +206 -0
- package/Editor/Scripts/Inspectors/HyperSDKSettingsEditor.cs.meta +2 -0
- package/Editor/Scripts/Inspectors.meta +8 -0
- package/Editor/Scripts/Tools/HyperBuildAssistant.cs +53 -3
- package/Editor/Scripts/Windows/HyperCoreSettingsWindow.cs +0 -8
- package/Editor/Scripts/Windows/HyperGameContentWindow.cs +0 -8
- package/Editor/Scripts/Windows/HyperSettingsWindow.cs +3 -3
- package/HyperLoader.unity +4 -49
- package/Runtime/Internal/AssemblyInfo.cs +1 -1
- package/Runtime/Internal/Managers/EventManager.cs +4 -0
- package/Runtime/Internal/Managers/GameManager.cs +173 -5
- package/Runtime/Internal/ScriptableObjects/HyperSDKSettings.cs +16 -10
- package/Runtime/Internal/UI/Animation/BlinkingText.cs +1 -1
- package/Runtime/Internal/UI/Animation/HyperTween.cs +88 -0
- package/Runtime/Internal/UI/Components/ControlBar.cs +220 -40
- package/Runtime/Internal/UI/Components/HealthBarDisplay.cs +269 -0
- package/Runtime/Internal/UI/Components/HealthBarDisplay.cs.meta +2 -0
- package/Runtime/Internal/UI/Components/LivesDisplay.cs +327 -0
- package/Runtime/Internal/UI/Components/LivesDisplay.cs.meta +2 -0
- package/Runtime/Internal/UI/Modals/ExitGameMenuModal.cs +46 -0
- package/Runtime/Internal/UI/Modals/ExitGameMenuModal.cs.meta +11 -0
- package/Runtime/Internal/UI/Modals/StartingInstructionModal.cs +202 -3
- package/Runtime/Internal/Utils/HyperConstants.cs +27 -0
- package/Runtime/Public/Core/HyperSDK.cs +68 -0
- package/Runtime/Resources/Fonts/Wonder Boys/Wonder Boys + Hyper BIG.mat +1 -1
- package/Runtime/Resources/Fonts/Wonder Boys/Wonder Boys + Shadows.mat +1 -1
- package/Runtime/Resources/HyperSDKSettings.asset +3 -2
- package/Runtime/Resources/Sprites/Big Icons/Exit.png +0 -0
- package/Runtime/Resources/Sprites/Big Icons/Exit.png.meta +195 -0
- package/Runtime/Resources/Sprites/Control Bar/Ellipse 3.png +0 -0
- package/Runtime/Resources/Sprites/Control Bar/Ellipse 3.png.meta +169 -0
- package/Runtime/Resources/Sprites/Control Bar/Health Bar - Green Fill.png +0 -0
- package/Runtime/Resources/Sprites/Control Bar/Health Bar - Green Fill.png.meta +169 -0
- package/Runtime/Resources/Sprites/Control Bar/Health Bar - Underlay.png +0 -0
- package/Runtime/Resources/Sprites/Control Bar/Health Bar - Underlay.png.meta +169 -0
- package/Runtime/Resources/Sprites/Control Bar/Health Bar.png +0 -0
- package/Runtime/Resources/Sprites/Control Bar/Health Bar.png.meta +169 -0
- package/Runtime/Resources/Sprites/Control Bar/heart.empty.png +0 -0
- package/Runtime/Resources/Sprites/Control Bar/heart.empty.png.meta +169 -0
- package/Runtime/Resources/Sprites/Control Bar/heart.fill.png +0 -0
- package/Runtime/Resources/Sprites/Control Bar/heart.fill.png.meta +169 -0
- package/Runtime/Resources/Sprites/Frames/{Game Over P.png.meta → Game Over.png.meta } +4 -4
- package/Runtime/Resources/Sprites/HYPERGAMES_Logo.png.meta +3 -3
- package/Runtime/Resources/Sprites/Hyper Atlas.spriteatlasv2 +6 -0
- package/Runtime/Resources/Sprites/Start CTAs/Hand.PNG +0 -0
- package/Runtime/Resources/Sprites/Start CTAs/Hand.PNG.meta +169 -0
- package/Runtime/Resources/Sprites/Start CTAs/Line .PNG +0 -0
- package/Runtime/Resources/Sprites/Start CTAs/Line .PNG.meta +169 -0
- package/Runtime/Resources/Sprites/Start CTAs/Swipe Gesture GIF.controller +130 -0
- package/Runtime/Resources/Sprites/Start CTAs/Swipe Gesture GIF.controller.meta +8 -0
- package/Runtime/Resources/Sprites/Start CTAs/Swipe Gesture.anim +158 -0
- package/Runtime/Resources/Sprites/Start CTAs/Swipe Gesture.anim.meta +8 -0
- package/Runtime/Resources/Sprites/Start CTAs.meta +8 -0
- package/Runtime/Resources/UIPrefabs/Control Bar Objects/Health Bar Display.prefab +361 -0
- package/Runtime/Resources/UIPrefabs/Control Bar Objects/Health Bar Display.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/Control Bar Objects/Lives Display .prefab +246 -0
- package/Runtime/Resources/UIPrefabs/Control Bar Objects/Lives Display .prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/Control Bar Objects.meta +8 -0
- package/Runtime/Resources/UIPrefabs/ControlBar.prefab +131 -17
- package/Runtime/Resources/UIPrefabs/ExitGameMenuModal.prefab +1043 -0
- package/Runtime/Resources/UIPrefabs/ExitGameMenuModal.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/StartingInstructionModal.prefab +271 -57
- package/Runtime/Resources/UIPrefabs/TutorialLandscapeModal.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/TutorialPortraitModal.prefab +1 -1
- package/Runtime/Shared/DTOs/Enums.cs +22 -6
- package/package.json +1 -1
- /package/Editor/{HyperSDK.Editor.asmdef.meta → Hyper.Editor.asmdef.meta} +0 -0
- /package/Runtime/Resources/Sprites/Frames/{Game Over P.png → Game Over.png} +0 -0
|
@@ -4,32 +4,82 @@ using TMPro;
|
|
|
4
4
|
using UnityEngine;
|
|
5
5
|
using UnityEngine.UI;
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
namespace Hyper.Internal
|
|
9
8
|
{
|
|
10
9
|
internal class ControlBar : MonoBehaviour
|
|
11
10
|
{
|
|
11
|
+
[Header("UI Components")]
|
|
12
|
+
public Image roundedBG;
|
|
12
13
|
[SerializeField] TextMeshProUGUI scoreText;
|
|
13
14
|
[SerializeField] private RectTransform scoreTextRect;
|
|
14
15
|
[SerializeField] private RectTransform timerText;
|
|
15
16
|
[SerializeField] private Image safeAreaFillerBG;
|
|
16
|
-
public Image roundedBG;
|
|
17
17
|
[SerializeField] private Image pauseButtonImage;
|
|
18
18
|
[SerializeField] private Image pauseBarsImage;
|
|
19
|
+
[SerializeField] private Button connectionStatusIcon;
|
|
20
|
+
|
|
21
|
+
[Header("Sprites")]
|
|
19
22
|
[SerializeField] Sprite portraitBG;
|
|
20
23
|
[SerializeField] Sprite landscapeBG;
|
|
21
24
|
[SerializeField] Sprite pauseBarsLight;
|
|
22
25
|
[SerializeField] Sprite pauseBarsDark;
|
|
23
26
|
|
|
27
|
+
[Header("Lives Display")]
|
|
28
|
+
[SerializeField] private GameObject livesDisplayPrefab;
|
|
29
|
+
|
|
30
|
+
[Header("Health Bar Display")]
|
|
31
|
+
[SerializeField] private GameObject healthBarDisplayPrefab;
|
|
32
|
+
|
|
24
33
|
private GameManager _gameManager;
|
|
34
|
+
private TextMeshProUGUI _scoreTextComponent;
|
|
35
|
+
private TextMeshProUGUI _timerTextComponent;
|
|
36
|
+
private LivesDisplay _livesDisplay;
|
|
37
|
+
private HealthBarDisplay _healthBarDisplay;
|
|
38
|
+
|
|
39
|
+
// Layout constants
|
|
40
|
+
private const float CENTERED_LAYOUT_SCORE_Y = 31f;
|
|
41
|
+
private const float CENTERED_LAYOUT_TIMER_Y = -13f;
|
|
42
|
+
private const float CENTERED_LAYOUT_SCORE_FONT_SIZE = 100f;
|
|
43
|
+
private const float CENTERED_LAYOUT_TIMER_FONT_SIZE = 50f;
|
|
44
|
+
|
|
45
|
+
private const float DEFAULT_LAYOUT_SCORE_Y = 10f;
|
|
46
|
+
private const float DEFAULT_LAYOUT_TIMER_X = 35f;
|
|
47
|
+
private const float DEFAULT_LAYOUT_TIMER_Y = 10f;
|
|
48
|
+
private const float DEFAULT_LAYOUT_SCORE_FONT_SIZE = 100f;
|
|
49
|
+
private const float DEFAULT_LAYOUT_TIMER_FONT_SIZE = 60f;
|
|
50
|
+
|
|
51
|
+
// Theme constants
|
|
52
|
+
private const string LIGHT_THEME_BG_COLOR = "#F5F5F5";
|
|
53
|
+
private const string LIGHT_THEME_TEXT_COLOR = "#434343";
|
|
54
|
+
private const float DARK_THEME_PAUSE_BUTTON_ALPHA = 0.25f;
|
|
55
|
+
private const float LIGHT_THEME_PAUSE_BUTTON_ALPHA = 1f;
|
|
25
56
|
|
|
26
57
|
void Awake()
|
|
27
58
|
{
|
|
28
59
|
_gameManager = HyperRuntime.GameManager;
|
|
60
|
+
CacheTextComponents();
|
|
29
61
|
|
|
30
62
|
RectTransform rect = transform.GetComponent<RectTransform>();
|
|
63
|
+
ConfigureOrientation(rect);
|
|
64
|
+
ConfigureLayout();
|
|
65
|
+
ConfigureTheme();
|
|
66
|
+
|
|
67
|
+
connectionStatusIcon.onClick.AddListener(ShowConnectionLostStatus);
|
|
68
|
+
}
|
|
31
69
|
|
|
32
|
-
|
|
70
|
+
private void OnDestroy()
|
|
71
|
+
{
|
|
72
|
+
connectionStatusIcon.onClick.RemoveListener(ShowConnectionLostStatus);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private void CacheTextComponents()
|
|
76
|
+
{
|
|
77
|
+
_scoreTextComponent = scoreTextRect.GetComponent<TextMeshProUGUI>();
|
|
78
|
+
_timerTextComponent = timerText.GetComponent<TextMeshProUGUI>();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private void ConfigureOrientation(RectTransform rect)
|
|
82
|
+
{
|
|
33
83
|
if (_gameManager.IsPortrait())
|
|
34
84
|
{
|
|
35
85
|
roundedBG.sprite = portraitBG;
|
|
@@ -40,62 +90,174 @@ namespace Hyper.Internal
|
|
|
40
90
|
roundedBG.sprite = landscapeBG;
|
|
41
91
|
rect.sizeDelta = new Vector2(rect.sizeDelta.x, 180);
|
|
42
92
|
}
|
|
93
|
+
}
|
|
43
94
|
|
|
95
|
+
private void ConfigureLayout()
|
|
96
|
+
{
|
|
97
|
+
switch (_gameManager.ControlBarLayout)
|
|
98
|
+
{
|
|
99
|
+
case ControlBarLayout.HeartsLeft:
|
|
100
|
+
ApplyCenteredLayout();
|
|
101
|
+
SetupLivesDisplay();
|
|
102
|
+
break;
|
|
103
|
+
case ControlBarLayout.HealthBarLeft:
|
|
104
|
+
ApplyCenteredLayout();
|
|
105
|
+
HideLivesDisplay();
|
|
106
|
+
SetupHealthBarDisplay();
|
|
107
|
+
break;
|
|
108
|
+
case ControlBarLayout.Default:
|
|
109
|
+
ApplyDefaultLayout();
|
|
110
|
+
HideLivesDisplay();
|
|
111
|
+
HideHealthBarDisplay();
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
44
115
|
|
|
45
|
-
|
|
46
|
-
|
|
116
|
+
private void SetupLivesDisplay()
|
|
117
|
+
{
|
|
118
|
+
if (_livesDisplay == null && livesDisplayPrefab != null)
|
|
47
119
|
{
|
|
48
|
-
|
|
49
|
-
|
|
120
|
+
GameObject livesInstance = Instantiate(livesDisplayPrefab, roundedBG.transform);
|
|
121
|
+
_livesDisplay = livesInstance.GetComponent<LivesDisplay>();
|
|
50
122
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
123
|
+
if (_livesDisplay == null)
|
|
124
|
+
{
|
|
125
|
+
Debug.LogError("[ControlBar] LivesDisplay prefab does not have LivesDisplay component!");
|
|
126
|
+
Destroy(livesInstance);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Position on the left side where timer would be in default layout
|
|
131
|
+
RectTransform livesRect = livesInstance.GetComponent<RectTransform>();
|
|
132
|
+
if (livesRect != null)
|
|
133
|
+
{
|
|
134
|
+
livesRect.anchoredPosition = new Vector2(DEFAULT_LAYOUT_TIMER_X, DEFAULT_LAYOUT_TIMER_Y);
|
|
135
|
+
livesRect.anchorMin = new Vector2(0f, 0.5f);
|
|
136
|
+
livesRect.anchorMax = new Vector2(0f, 0.5f);
|
|
137
|
+
livesRect.pivot = new Vector2(0, 0.5f);
|
|
138
|
+
}
|
|
56
139
|
}
|
|
57
|
-
|
|
140
|
+
|
|
141
|
+
if (_livesDisplay != null)
|
|
58
142
|
{
|
|
59
|
-
|
|
60
|
-
|
|
143
|
+
_livesDisplay.gameObject.SetActive(true);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
61
146
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
timerText.GetComponent<TextMeshProUGUI>().alignment = TextAlignmentOptions.MidlineLeft;
|
|
147
|
+
private void HideLivesDisplay()
|
|
148
|
+
{
|
|
149
|
+
if (_livesDisplay != null)
|
|
150
|
+
{
|
|
151
|
+
_livesDisplay.gameObject.SetActive(false);
|
|
68
152
|
}
|
|
153
|
+
}
|
|
69
154
|
|
|
70
|
-
|
|
71
|
-
|
|
155
|
+
private void SetupHealthBarDisplay()
|
|
156
|
+
{
|
|
157
|
+
if (_healthBarDisplay == null && healthBarDisplayPrefab != null)
|
|
72
158
|
{
|
|
73
|
-
|
|
74
|
-
|
|
159
|
+
GameObject healthBarInstance = Instantiate(healthBarDisplayPrefab, roundedBG.transform);
|
|
160
|
+
_healthBarDisplay = healthBarInstance.GetComponent<HealthBarDisplay>();
|
|
75
161
|
|
|
76
|
-
|
|
77
|
-
|
|
162
|
+
if (_healthBarDisplay == null)
|
|
163
|
+
{
|
|
164
|
+
Debug.LogError("[ControlBar] HealthBarDisplay prefab does not have HealthBarDisplay component!");
|
|
165
|
+
Destroy(healthBarInstance);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
78
168
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
169
|
+
// Position on the left side where timer would be in default layout
|
|
170
|
+
RectTransform healthBarRect = healthBarInstance.GetComponent<RectTransform>();
|
|
171
|
+
if (healthBarRect != null)
|
|
172
|
+
{
|
|
173
|
+
healthBarRect.anchoredPosition = new Vector2(DEFAULT_LAYOUT_TIMER_X, DEFAULT_LAYOUT_TIMER_Y);
|
|
174
|
+
healthBarRect.anchorMin = new Vector2(0f, 0.5f);
|
|
175
|
+
healthBarRect.anchorMax = new Vector2(0f, 0.5f);
|
|
176
|
+
healthBarRect.pivot = new Vector2(0, 0.5f);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (_healthBarDisplay != null)
|
|
181
|
+
{
|
|
182
|
+
_healthBarDisplay.gameObject.SetActive(true);
|
|
82
183
|
}
|
|
83
|
-
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private void HideHealthBarDisplay()
|
|
187
|
+
{
|
|
188
|
+
if (_healthBarDisplay != null)
|
|
84
189
|
{
|
|
85
|
-
|
|
86
|
-
|
|
190
|
+
_healthBarDisplay.gameObject.SetActive(false);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private void ApplyCenteredLayout()
|
|
195
|
+
{
|
|
196
|
+
scoreTextRect.anchoredPosition = new Vector2(0, CENTERED_LAYOUT_SCORE_Y);
|
|
197
|
+
_scoreTextComponent.fontSize = CENTERED_LAYOUT_SCORE_FONT_SIZE;
|
|
87
198
|
|
|
88
|
-
|
|
89
|
-
|
|
199
|
+
timerText.anchoredPosition = new Vector2(0, CENTERED_LAYOUT_TIMER_Y);
|
|
200
|
+
timerText.anchorMin = new Vector2(0.5f, 0.5f);
|
|
201
|
+
timerText.anchorMax = new Vector2(0.5f, 0.5f);
|
|
202
|
+
timerText.pivot = new Vector2(0.5f, 0.5f);
|
|
203
|
+
_timerTextComponent.fontSize = CENTERED_LAYOUT_TIMER_FONT_SIZE;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private void ApplyDefaultLayout()
|
|
207
|
+
{
|
|
208
|
+
scoreTextRect.anchoredPosition = new Vector2(0, DEFAULT_LAYOUT_SCORE_Y);
|
|
209
|
+
_scoreTextComponent.fontSize = DEFAULT_LAYOUT_SCORE_FONT_SIZE;
|
|
90
210
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
211
|
+
timerText.anchoredPosition = new Vector2(DEFAULT_LAYOUT_TIMER_X, DEFAULT_LAYOUT_TIMER_Y);
|
|
212
|
+
timerText.anchorMin = new Vector2(0f, 0.5f);
|
|
213
|
+
timerText.anchorMax = new Vector2(0f, 0.5f);
|
|
214
|
+
timerText.pivot = new Vector2(0, 0.5f);
|
|
215
|
+
_timerTextComponent.fontSize = DEFAULT_LAYOUT_TIMER_FONT_SIZE;
|
|
216
|
+
_timerTextComponent.alignment = TextAlignmentOptions.MidlineLeft;
|
|
95
217
|
}
|
|
96
218
|
|
|
97
|
-
|
|
219
|
+
private void ConfigureTheme()
|
|
98
220
|
{
|
|
221
|
+
switch (_gameManager.ControlBarTheme)
|
|
222
|
+
{
|
|
223
|
+
case ControlBarTheme.Dark:
|
|
224
|
+
ApplyDarkTheme();
|
|
225
|
+
break;
|
|
226
|
+
case ControlBarTheme.Light:
|
|
227
|
+
ApplyLightTheme();
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private void ApplyDarkTheme()
|
|
233
|
+
{
|
|
234
|
+
roundedBG.color = Color.black;
|
|
235
|
+
safeAreaFillerBG.color = Color.black;
|
|
236
|
+
|
|
237
|
+
_scoreTextComponent.color = Color.white;
|
|
238
|
+
_timerTextComponent.color = Color.white;
|
|
239
|
+
|
|
240
|
+
pauseButtonImage.color = new Color(1, 1, 1, DARK_THEME_PAUSE_BUTTON_ALPHA);
|
|
241
|
+
pauseBarsImage.sprite = pauseBarsDark;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private void ApplyLightTheme()
|
|
245
|
+
{
|
|
246
|
+
Color bgColor = HexToColor(LIGHT_THEME_BG_COLOR);
|
|
247
|
+
Color textColor = HexToColor(LIGHT_THEME_TEXT_COLOR);
|
|
248
|
+
|
|
249
|
+
roundedBG.color = bgColor;
|
|
250
|
+
safeAreaFillerBG.color = bgColor;
|
|
251
|
+
|
|
252
|
+
_scoreTextComponent.color = textColor;
|
|
253
|
+
_timerTextComponent.color = textColor;
|
|
254
|
+
|
|
255
|
+
pauseButtonImage.color = new Color(1, 1, 1, LIGHT_THEME_PAUSE_BUTTON_ALPHA);
|
|
256
|
+
pauseBarsImage.sprite = pauseBarsLight;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private void ShowConnectionLostStatus()
|
|
260
|
+
{
|
|
99
261
|
// Show the connection lost status
|
|
100
262
|
_gameManager.ShowConnectionLost();
|
|
101
263
|
}
|
|
@@ -105,6 +267,24 @@ namespace Hyper.Internal
|
|
|
105
267
|
scoreText.SetText(HyperRuntime.DataManager.CurrentScore.ToString("N0"));
|
|
106
268
|
}
|
|
107
269
|
|
|
270
|
+
/// <summary>
|
|
271
|
+
/// Gets the LivesDisplay component if available.
|
|
272
|
+
/// Only available when ControlBarLayout is set to HeartsLeft.
|
|
273
|
+
/// </summary>
|
|
274
|
+
public LivesDisplay GetLivesDisplay()
|
|
275
|
+
{
|
|
276
|
+
return _livesDisplay;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/// <summary>
|
|
280
|
+
/// Gets the HealthBarDisplay component if available.
|
|
281
|
+
/// Only available when ControlBarLayout is set to HealthBarGreenLeft.
|
|
282
|
+
/// </summary>
|
|
283
|
+
public HealthBarDisplay GetHealthBarDisplay()
|
|
284
|
+
{
|
|
285
|
+
return _healthBarDisplay;
|
|
286
|
+
}
|
|
287
|
+
|
|
108
288
|
Color HexToColor(string hex)
|
|
109
289
|
{
|
|
110
290
|
if (ColorUtility.TryParseHtmlString(hex, out Color color))
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
using System.Collections;
|
|
2
|
+
using UnityEngine;
|
|
3
|
+
using UnityEngine.UI;
|
|
4
|
+
using Hyper.Internal.Animation;
|
|
5
|
+
using System;
|
|
6
|
+
|
|
7
|
+
namespace Hyper.Internal
|
|
8
|
+
{
|
|
9
|
+
/// <summary>
|
|
10
|
+
/// Manages the display of health bar in the control bar.
|
|
11
|
+
/// Uses Image.fillAmount to represent health from 0-1.
|
|
12
|
+
/// </summary>
|
|
13
|
+
internal class HealthBarDisplay : MonoBehaviour
|
|
14
|
+
{
|
|
15
|
+
[Header("UI Components")]
|
|
16
|
+
[SerializeField] private Image healthFillImage;
|
|
17
|
+
[SerializeField] private Image underlayFillImage;
|
|
18
|
+
|
|
19
|
+
[Header("Configuration")]
|
|
20
|
+
[SerializeField] private float lowHealthThreshold = 0.3f;
|
|
21
|
+
|
|
22
|
+
[Header("Animation")]
|
|
23
|
+
[SerializeField] private float fillDelay = 0.1f;
|
|
24
|
+
[SerializeField] private float fillAnimationDuration = 0.3f;
|
|
25
|
+
[SerializeField] private bool usePulseOnLowHealth = false;
|
|
26
|
+
[SerializeField] private float pulseDuration = 0.5f;
|
|
27
|
+
|
|
28
|
+
private float _currentHealth = 1f;
|
|
29
|
+
private float _maxHealth = 1f;
|
|
30
|
+
private HyperTween.TweenHandle _fillTweenHandle;
|
|
31
|
+
private Coroutine _pulseCoroutine;
|
|
32
|
+
private bool _healthExhaustedTriggered;
|
|
33
|
+
|
|
34
|
+
private void Awake()
|
|
35
|
+
{
|
|
36
|
+
if (healthFillImage == null)
|
|
37
|
+
{
|
|
38
|
+
Debug.LogError("[HealthBarDisplay] Health fill image is not assigned!");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Initialize to full health
|
|
42
|
+
if (healthFillImage != null && underlayFillImage != null)
|
|
43
|
+
{
|
|
44
|
+
healthFillImage.fillAmount = 1f;
|
|
45
|
+
underlayFillImage.fillAmount = 1f;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// <summary>
|
|
50
|
+
/// Initializes the health bar with a maximum health value.
|
|
51
|
+
/// </summary>
|
|
52
|
+
/// <param name="maxHealth">Maximum health value</param>
|
|
53
|
+
/// <param name="initialHealth">Initial health (defaults to max, or pass 0-1 for normalized)</param>
|
|
54
|
+
/// <param name="isNormalized">If true, initialHealth is treated as 0-1, otherwise as absolute value</param>
|
|
55
|
+
public void Initialize(float maxHealth, float initialHealth = -1f, bool isNormalized = false)
|
|
56
|
+
{
|
|
57
|
+
if (maxHealth <= 0)
|
|
58
|
+
{
|
|
59
|
+
Debug.LogWarning("[HealthBarDisplay] Max health must be greater than 0. Setting to 1.");
|
|
60
|
+
maxHealth = 1f;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
_maxHealth = maxHealth;
|
|
64
|
+
|
|
65
|
+
if (initialHealth < 0)
|
|
66
|
+
{
|
|
67
|
+
_currentHealth = maxHealth;
|
|
68
|
+
}
|
|
69
|
+
else if (isNormalized)
|
|
70
|
+
{
|
|
71
|
+
_currentHealth = Mathf.Clamp01(initialHealth) * maxHealth;
|
|
72
|
+
}
|
|
73
|
+
else
|
|
74
|
+
{
|
|
75
|
+
_currentHealth = Mathf.Clamp(initialHealth, 0f, maxHealth);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_healthExhaustedTriggered = false;
|
|
79
|
+
UpdateDisplay();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/// <summary>
|
|
83
|
+
/// Sets the current health value.
|
|
84
|
+
/// </summary>
|
|
85
|
+
/// <param name="health">Health value (absolute)</param>
|
|
86
|
+
public void SetHealth(float health)
|
|
87
|
+
{
|
|
88
|
+
float previousHealth = _currentHealth;
|
|
89
|
+
_currentHealth = Mathf.Clamp(health, 0f, _maxHealth);
|
|
90
|
+
AnimateHealthChange(previousHealth, _currentHealth);
|
|
91
|
+
|
|
92
|
+
// Check if health reached 0
|
|
93
|
+
if (previousHealth > 0 && _currentHealth <= 0)
|
|
94
|
+
{
|
|
95
|
+
CheckHealthExhausted();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// <summary>
|
|
100
|
+
/// Sets the current health as a normalized value (0-1).
|
|
101
|
+
/// </summary>
|
|
102
|
+
/// <param name="normalizedHealth">Health value from 0 to 1</param>
|
|
103
|
+
public void SetHealthNormalized(float normalizedHealth)
|
|
104
|
+
{
|
|
105
|
+
SetHealth(Mathf.Clamp01(normalizedHealth) * _maxHealth);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// <summary>
|
|
109
|
+
/// Adds health.
|
|
110
|
+
/// </summary>
|
|
111
|
+
/// <param name="amount">Amount of health to add</param>
|
|
112
|
+
public void AddHealth(float amount)
|
|
113
|
+
{
|
|
114
|
+
if (amount > 0)
|
|
115
|
+
{
|
|
116
|
+
SetHealth(_currentHealth + amount);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// <summary>
|
|
121
|
+
/// Removes health.
|
|
122
|
+
/// </summary>
|
|
123
|
+
/// <param name="amount">Amount of health to remove</param>
|
|
124
|
+
public void RemoveHealth(float amount)
|
|
125
|
+
{
|
|
126
|
+
if (amount > 0)
|
|
127
|
+
{
|
|
128
|
+
SetHealth(_currentHealth - amount);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/// <summary>
|
|
133
|
+
/// Gets the current health value.
|
|
134
|
+
/// </summary>
|
|
135
|
+
public float GetCurrentHealth() => _currentHealth;
|
|
136
|
+
|
|
137
|
+
/// <summary>
|
|
138
|
+
/// Gets the maximum health value.
|
|
139
|
+
/// </summary>
|
|
140
|
+
public float GetMaxHealth() => _maxHealth;
|
|
141
|
+
|
|
142
|
+
/// <summary>
|
|
143
|
+
/// Gets the current health as a normalized value (0-1).
|
|
144
|
+
/// </summary>
|
|
145
|
+
public float GetHealthNormalized() => _maxHealth > 0 ? _currentHealth / _maxHealth : 0f;
|
|
146
|
+
|
|
147
|
+
private void AnimateHealthChange(float fromHealth, float toHealth)
|
|
148
|
+
{
|
|
149
|
+
if (healthFillImage == null) return;
|
|
150
|
+
|
|
151
|
+
// Stop existing animation
|
|
152
|
+
if (_fillTweenHandle != null)
|
|
153
|
+
{
|
|
154
|
+
_fillTweenHandle.Kill();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
float fromFill = _maxHealth > 0 ? fromHealth / _maxHealth : 0f;
|
|
158
|
+
float toFill = _maxHealth > 0 ? toHealth / _maxHealth : 0f;
|
|
159
|
+
|
|
160
|
+
healthFillImage.fillAmount -= Math.Abs(fromHealth - toHealth) / _maxHealth;
|
|
161
|
+
|
|
162
|
+
HyperTween.DelayedCall(fillDelay, () =>
|
|
163
|
+
{
|
|
164
|
+
// Animate fill amount
|
|
165
|
+
_fillTweenHandle = underlayFillImage.DOFillAmount(toFill, fillAnimationDuration)
|
|
166
|
+
.SetEase(HyperTween.Ease.OutQuad)
|
|
167
|
+
.OnComplete(() =>
|
|
168
|
+
{
|
|
169
|
+
_fillTweenHandle = null;
|
|
170
|
+
|
|
171
|
+
// Start pulse if health is low
|
|
172
|
+
if (usePulseOnLowHealth && toFill <= lowHealthThreshold && toFill > 0)
|
|
173
|
+
{
|
|
174
|
+
StartPulseAnimation();
|
|
175
|
+
}
|
|
176
|
+
else
|
|
177
|
+
{
|
|
178
|
+
StopPulseAnimation();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private void StartPulseAnimation()
|
|
185
|
+
{
|
|
186
|
+
if (_pulseCoroutine != null) return;
|
|
187
|
+
_pulseCoroutine = StartCoroutine(PulseAnimation());
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private void StopPulseAnimation()
|
|
191
|
+
{
|
|
192
|
+
if (_pulseCoroutine != null)
|
|
193
|
+
{
|
|
194
|
+
StopCoroutine(_pulseCoroutine);
|
|
195
|
+
_pulseCoroutine = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (healthFillImage != null)
|
|
199
|
+
{
|
|
200
|
+
Color color = healthFillImage.color;
|
|
201
|
+
color.a = 1f;
|
|
202
|
+
healthFillImage.color = color;
|
|
203
|
+
underlayFillImage.color = color;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private IEnumerator PulseAnimation()
|
|
208
|
+
{
|
|
209
|
+
if (healthFillImage == null) yield break;
|
|
210
|
+
|
|
211
|
+
while (true)
|
|
212
|
+
{
|
|
213
|
+
if (healthFillImage == null) yield break;
|
|
214
|
+
|
|
215
|
+
float normalizedHealth = GetHealthNormalized();
|
|
216
|
+
if (normalizedHealth > lowHealthThreshold || normalizedHealth <= 0)
|
|
217
|
+
{
|
|
218
|
+
StopPulseAnimation();
|
|
219
|
+
yield break;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Pulse alpha
|
|
223
|
+
float elapsed = 0f;
|
|
224
|
+
Color startColor = healthFillImage.color;
|
|
225
|
+
Color pulseColor = new Color(startColor.r, startColor.g, startColor.b, 0.5f);
|
|
226
|
+
|
|
227
|
+
while (elapsed < pulseDuration)
|
|
228
|
+
{
|
|
229
|
+
if (healthFillImage == null) yield break;
|
|
230
|
+
elapsed += Time.deltaTime;
|
|
231
|
+
float t = Mathf.PingPong(elapsed / pulseDuration, 1f);
|
|
232
|
+
Color currentColor = Color.Lerp(startColor, pulseColor, t);
|
|
233
|
+
healthFillImage.color = currentColor;
|
|
234
|
+
underlayFillImage.color = currentColor;
|
|
235
|
+
yield return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private void UpdateDisplay()
|
|
241
|
+
{
|
|
242
|
+
if (healthFillImage == null) return;
|
|
243
|
+
|
|
244
|
+
float normalizedHealth = GetHealthNormalized();
|
|
245
|
+
healthFillImage.fillAmount = normalizedHealth;
|
|
246
|
+
underlayFillImage.fillAmount = normalizedHealth;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private void CheckHealthExhausted()
|
|
250
|
+
{
|
|
251
|
+
if (_currentHealth <= 0 && !_healthExhaustedTriggered)
|
|
252
|
+
{
|
|
253
|
+
_healthExhaustedTriggered = true;
|
|
254
|
+
HyperRuntime.EventManager?.InvokeHealthExhaustedEvent();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private void OnDestroy()
|
|
259
|
+
{
|
|
260
|
+
if (_fillTweenHandle != null)
|
|
261
|
+
{
|
|
262
|
+
_fillTweenHandle.Kill();
|
|
263
|
+
}
|
|
264
|
+
HyperTween.Kill(underlayFillImage);
|
|
265
|
+
StopPulseAnimation();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|