From bccf0ecb10a5df9d42040ba6f2bf7c494bbee4ab Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:38:50 +0100 Subject: [PATCH 01/16] add switch pause button mapping --- .../Settings/InputSystem_Actions.inputactions | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Assets/Settings/InputSystem_Actions.inputactions b/Assets/Settings/InputSystem_Actions.inputactions index 6cd2c2f..f84be2a 100644 --- a/Assets/Settings/InputSystem_Actions.inputactions +++ b/Assets/Settings/InputSystem_Actions.inputactions @@ -262,6 +262,17 @@ "isComposite": false, "isPartOfComposite": false }, + { + "name": "", + "id": "cce22881-2de4-4491-943c-625113e873ac", + "path": "/start", + "interactions": "", + "processors": "", + "groups": "", + "action": "Pause", + "isComposite": false, + "isPartOfComposite": false + }, { "name": "", "id": "0328a504-56c9-413d-b17e-f54c8515dee2", @@ -798,6 +809,17 @@ "isComposite": false, "isPartOfComposite": false }, + { + "name": "", + "id": "9efea79e-9850-4bad-ba34-be1851dabca5", + "path": "/start", + "interactions": "", + "processors": "", + "groups": "", + "action": "Pause", + "isComposite": false, + "isPartOfComposite": false + }, { "name": "", "id": "9e92bb26-7e3b-4ec4-b06b-3c8f8e498ddc", From 9befe70c115dd26b60716791def22e0b7d4163de Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:38:51 +0100 Subject: [PATCH 02/16] fix levelupui bugging out when holding joystick in a direction --- Assets/Scripts/Upgrades/LevelUpUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/Upgrades/LevelUpUI.cs b/Assets/Scripts/Upgrades/LevelUpUI.cs index a0ec5f7..aaae22e 100644 --- a/Assets/Scripts/Upgrades/LevelUpUI.cs +++ b/Assets/Scripts/Upgrades/LevelUpUI.cs @@ -50,7 +50,7 @@ private void OnEnable() InputSystem.actions.FindActionMap("UI").Enable(); // Subscribe to input events - _navigateAction.performed += OnNavigatePerformed; + _navigateAction.started += OnNavigatePerformed; _submitAction.performed += OnSubmitPerformed; // Pause the game @@ -154,7 +154,7 @@ public void SetHighlightedButton(Button button) private void OnDisable() { // Unsubscribe from input events - _navigateAction.performed -= OnNavigatePerformed; + _navigateAction.started -= OnNavigatePerformed; _submitAction.performed -= OnSubmitPerformed; _option1Button.onClick.RemoveAllListeners(); From a7e014322af8fdd974d464d391bad1cbacbba78e Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:38:51 +0100 Subject: [PATCH 03/16] fix switch http request (using UnityWebRequest instead of httpClient) + add manual spans/breadcrumbs surrounding this --- Assets/Scripts/UI/ScorePoster.cs | 388 ++++++++++++++++++++++++++++++- 1 file changed, 382 insertions(+), 6 deletions(-) diff --git a/Assets/Scripts/UI/ScorePoster.cs b/Assets/Scripts/UI/ScorePoster.cs index a406353..83fbda3 100644 --- a/Assets/Scripts/UI/ScorePoster.cs +++ b/Assets/Scripts/UI/ScorePoster.cs @@ -1,10 +1,17 @@ using Sentry; using Sentry.Unity; using System; +using System.Collections.Generic; +#if !UNITY_SWITCH using System.Net.Http; +#endif +#if UNITY_STANDALONE_WIN +using System.Runtime.InteropServices; +#endif using System.Threading.Tasks; using TMPro; using UnityEngine; +using UnityEngine.Networking; using UnityEngine.UI; [Serializable] @@ -29,7 +36,18 @@ public class ScorePoster : MonoBehaviour private TextMeshProUGUI _buttonText; private string _jwtToken; +#if !UNITY_SWITCH private HttpClient _httpClient; +#endif + + private TouchScreenKeyboard _keyboard; + private bool _keyboardWasActive = false; + +#if UNITY_STANDALONE_WIN + [DllImport("HandheldHelper")] private static extern bool ShowVirtualKeyboard(); + [DllImport("HandheldHelper")] private static extern bool HideVirtualKeyboard(); + [DllImport("HandheldHelper")] private static extern bool IsDeviceHandheld(); +#endif private void Awake() { @@ -38,14 +56,58 @@ private void Awake() _buttonText = _submitButton.GetComponentInChildren(); _submitButton.onClick.AddListener(OnSubmit); - + + // Configure input field for touch devices + // For devices with touch keyboards (mobile, Switch, Windows tablets): + // - shouldHideMobileInput = false (default): Shows both Unity input field and native keyboard + // - shouldHideMobileInput = true: Hides Unity input field, shows only native keyboard (saves screen space) + // We use false to let Unity handle keyboard automatically on supported platforms + _nameField.shouldHideMobileInput = false; + + // On some platforms (like Nintendo Switch), we may need to manually trigger the keyboard + // Add listener as a fallback for platforms that need explicit keyboard management + if (TouchScreenKeyboard.isSupported) + { + _nameField.onSelect.AddListener(OnInputFieldSelected); + } +#if UNITY_STANDALONE_WIN + else + { + try + { + if (IsDeviceHandheld()) + { + _nameField.onSelect.AddListener(OnInputFieldSelected); + _nameField.onDeselect.AddListener(OnInputFieldDeselected); + } + } + catch (Exception ex) + { + Debug.LogWarning($"HandheldHelper unavailable: {ex.Message}"); + } + } +#endif + } + + private void OnDestroy() + { + // Clean up the listener to avoid memory leaks + if (_nameField != null) + { + _nameField.onSelect.RemoveListener(OnInputFieldSelected); +#if UNITY_STANDALONE_WIN + _nameField.onDeselect.RemoveListener(OnInputFieldDeselected); +#endif + } } public void Start() { if (_demoConfig != null && _demoConfig.Enabled && !string.IsNullOrEmpty(_demoConfig.ApiUrl)) { +#if !UNITY_SWITCH _httpClient = new HttpClient(new SentryHttpMessageHandler()); +#endif _ = LoginAsync(); } } @@ -59,17 +121,154 @@ public void Enable() } } + private void Update() + { + // Update the input field with keyboard text if the keyboard is active + if (_keyboard != null && _keyboard.active) + { + _nameField.text = _keyboard.text; + _keyboardWasActive = true; + } + else if (_keyboardWasActive) + { + // Keyboard was just closed, ensure final text is synchronized + if (_keyboard != null) + { + _nameField.text = _keyboard.text; + } + _keyboardWasActive = false; + } + } + + private void OnInputFieldSelected(string text) + { + // Open native keyboard on devices without physical keyboards + // This works on mobile (iOS/Android), Nintendo Switch, and Windows touch devices + if (TouchScreenKeyboard.isSupported) + { + _keyboard = TouchScreenKeyboard.Open( + _nameField.text, + TouchScreenKeyboardType.Default, + false, // autocorrection + false, // multiline + false, // secure + false, // alert + _nameField.placeholder.GetComponent().text // placeholder text + ); + } +#if UNITY_STANDALONE_WIN + else + { + // Windows handheld (e.g. ROG Ally X) — show the WinRT gamepad keyboard. + // Only reachable when IsDeviceHandheld() was true at startup (listener not added otherwise). + try + { + ShowVirtualKeyboard(); + } + catch (Exception ex) + { + Debug.LogWarning($"Failed to show virtual keyboard: {ex.Message}"); + } + } +#endif + } + +#if UNITY_STANDALONE_WIN + private void OnInputFieldDeselected(string text) + { + try + { + HideVirtualKeyboard(); + } + catch (Exception ex) + { + Debug.LogWarning($"Failed to hide virtual keyboard: {ex.Message}"); + } + } +#endif + private async Task LoginAsync() { var transaction = SentrySdk.StartTransaction("scoreposter", "login"); SentrySdk.ConfigureScope(scope => scope.Transaction = transaction); - + try { var json = JsonUtility.ToJson(_demoConfig.User); + var url = _demoConfig.ApiUrl + "/token"; + var method = "POST"; + +#if UNITY_SWITCH + // Start a child span for this HTTP request (mimics SentryHttpMessageHandler) + var span = transaction.StartChild("http.client", $"{method} {url}"); + span?.SetExtra("http.request.method", method); + + var uri = new Uri(url); + if (!string.IsNullOrWhiteSpace(uri.Host)) + { + span?.SetExtra("server.address", uri.Host); + } + + using (UnityWebRequest request = new UnityWebRequest(url, method)) + { + byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json); + request.uploadHandler = new UploadHandlerRaw(bodyRaw); + request.downloadHandler = new DownloadHandlerBuffer(); + request.SetRequestHeader("Content-Type", "application/json"); + + // Propagate trace headers for distributed tracing + PropagateTraceHeaders(request, span); + + await request.SendWebRequest(); + + var statusCode = (int)request.responseCode; + + // Add breadcrumb (mimics SentryHttpMessageHandler) + SentrySdk.AddBreadcrumb( + message: string.Empty, + category: "http", + type: "http", + data: new Dictionary + { + {"url", url}, + {"method", method}, + {"status_code", statusCode.ToString()} + } + ); + + if (request.result == UnityWebRequest.Result.Success) + { + Debug.Log("Login to leaderboard successful."); + _jwtToken = request.downloadHandler.text.Replace("\"", ""); + + // Finish span with status code info + span?.SetExtra("http.response.status_code", statusCode); + span?.Finish(GetSpanStatusFromHttpCode(statusCode)); + transaction.Finish(SpanStatus.Ok); + } + else + { + Debug.Log("Login to leaderboard failed."); + _jwtToken = null; + + // Finish span with error status + span?.SetExtra("http.response.status_code", statusCode); + var spanStatus = GetSpanStatusFromHttpCode(statusCode); + span?.Finish(spanStatus); + + // Capture failed request as event (mimics SentryHttpFailedRequestHandler) + if (statusCode >= 400) + { + CaptureFailedRequest(method, url, statusCode, request.error); + } + + transaction.Finish(SpanStatus.Unavailable); + } + } +#else var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); - - var response = await _httpClient.PostAsync(_demoConfig.ApiUrl + "/token", content); + + var response = await _httpClient.PostAsync(url, content); if (response.IsSuccessStatusCode) { Debug.Log("Login to leaderboard successful."); @@ -82,6 +281,7 @@ private async Task LoginAsync() transaction.Finish(SpanStatus.Unavailable); _jwtToken = null; } +#endif } catch (Exception ex) { @@ -108,15 +308,89 @@ private async Task UploadScoreAsync() }; var json = JsonUtility.ToJson(score); - var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); var uploadTransaction = SentrySdk.StartTransaction("scoreposter", "upload"); SentrySdk.ConfigureScope(scope => scope.Transaction = uploadTransaction); try { + var url = _demoConfig.ApiUrl + "/score"; + var method = "POST"; + +#if UNITY_SWITCH + // Start a child span for this HTTP request (mimics SentryHttpMessageHandler) + var span = uploadTransaction.StartChild("http.client", $"{method} {url}"); + span?.SetExtra("http.request.method", method); + + var uri = new Uri(url); + if (!string.IsNullOrWhiteSpace(uri.Host)) + { + span?.SetExtra("server.address", uri.Host); + } + + using (UnityWebRequest request = new UnityWebRequest(url, method)) + { + byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(json); + request.uploadHandler = new UploadHandlerRaw(bodyRaw); + request.downloadHandler = new DownloadHandlerBuffer(); + request.SetRequestHeader("Content-Type", "application/json"); + request.SetRequestHeader("Authorization", "Bearer " + _jwtToken); + + // Propagate trace headers for distributed tracing + PropagateTraceHeaders(request, span); + + await request.SendWebRequest(); + + var statusCode = (int)request.responseCode; + + // Add breadcrumb (mimics SentryHttpMessageHandler) + SentrySdk.AddBreadcrumb( + message: string.Empty, + category: "http", + type: "http", + data: new Dictionary + { + {"url", url}, + {"method", method}, + {"status_code", statusCode.ToString()} + } + ); + + if (request.result != UnityWebRequest.Result.Success) + { + Debug.Log("Uploading score to leaderboard failed."); + _buttonText.text = "Retry"; + + // Finish span with error status + span?.SetExtra("http.response.status_code", statusCode); + var spanStatus = GetSpanStatusFromHttpCode(statusCode); + span?.Finish(spanStatus); + + // Capture failed request as event (mimics SentryHttpFailedRequestHandler) + if (statusCode >= 400) + { + CaptureFailedRequest(method, url, statusCode, request.error); + } + + uploadTransaction.Finish(SpanStatus.Unavailable); + } + else + { + Debug.Log("Uploading score to leaderboard was successful."); + _submitButton.interactable = false; + _buttonText.text = "Posted!"; + + // Finish span with success status + span?.SetExtra("http.response.status_code", statusCode); + span?.Finish(GetSpanStatusFromHttpCode(statusCode)); + + uploadTransaction.Finish(SpanStatus.Ok); + } + } +#else + var content = new StringContent(json, System.Text.Encoding.UTF8, "application/json"); _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _jwtToken); - var response = await _httpClient.PostAsync(_demoConfig.ApiUrl + "/score", content); + var response = await _httpClient.PostAsync(url, content); if (!response.IsSuccessStatusCode) { @@ -132,6 +406,7 @@ private async Task UploadScoreAsync() _buttonText.text = "Posted!"; uploadTransaction.Finish(SpanStatus.Ok); } +#endif } catch (Exception ex) { @@ -140,4 +415,105 @@ private async Task UploadScoreAsync() uploadTransaction.Finish(SpanStatus.InternalError); } } + +#if UNITY_SWITCH + /// + /// Propagates Sentry trace headers to the UnityWebRequest for distributed tracing. + /// Mimics the behavior of SentryMessageHandler.PropagateTraceHeaders. + /// + private void PropagateTraceHeaders(UnityWebRequest request, ISpan span) + { + // Add sentry-trace header + var traceHeader = span?.GetTraceHeader() ?? SentrySdk.GetTraceHeader(); + if (traceHeader != null) + { + request.SetRequestHeader("sentry-trace", traceHeader.ToString()); + } + + // Add baggage header + var baggage = SentrySdk.GetBaggage(); + if (baggage != null) + { + request.SetRequestHeader("baggage", baggage.ToString()); + } + } + + /// + /// Maps HTTP status codes to Sentry span statuses. + /// Mimics the behavior of SpanStatusConverter.FromHttpStatusCode. + /// + private SpanStatus GetSpanStatusFromHttpCode(int code) + { + return code switch + { + < 400 => SpanStatus.Ok, + 400 => SpanStatus.FailedPrecondition, + 401 => SpanStatus.Unauthenticated, + 403 => SpanStatus.PermissionDenied, + 404 => SpanStatus.NotFound, + 409 => SpanStatus.AlreadyExists, + 429 => SpanStatus.ResourceExhausted, + 499 => SpanStatus.Cancelled, + < 500 => SpanStatus.FailedPrecondition, + 500 => SpanStatus.InternalError, + 501 => SpanStatus.Unimplemented, + 503 => SpanStatus.Unavailable, + 504 => SpanStatus.DeadlineExceeded, + < 600 => SpanStatus.InternalError, + _ => SpanStatus.UnknownError + }; + } + + /// + /// Captures failed HTTP requests as Sentry events. + /// Mimics the behavior of SentryHttpFailedRequestHandler. + /// + private void CaptureFailedRequest(string method, string url, int statusCode, string error) + { + // Only capture 4xx and 5xx errors + if (statusCode < 400) + { + return; + } + + // Create an exception for the failed request + var exception = new System.Net.Http.HttpRequestException( + $"Response status code does not indicate success: {statusCode} ({error})" + ); + + // Create a Sentry event + var sentryEvent = new SentryEvent(exception); + + // Add request context + var uri = new Uri(url); + sentryEvent.Request = new SentryRequest + { + Url = url, + Method = method, + QueryString = uri.Query + }; + + // Add response context + sentryEvent.Contexts["response"] = new Dictionary + { + {"status_code", statusCode} + }; + + // Capture the event + SentrySdk.CaptureEvent(sentryEvent); + } +#endif +} + +#if UNITY_SWITCH +// Extension method to make UnityWebRequest awaitable +public static class UnityWebRequestExtensions +{ + public static Task SendWebRequest(this UnityWebRequest request) + { + var tcs = new TaskCompletionSource(); + request.SendWebRequest().completed += _ => tcs.SetResult(true); + return tcs.Task; + } } +#endif From d973bd956dd010a9753212e9a79685ef1be7fd9b Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:38:52 +0100 Subject: [PATCH 04/16] fix LevelUpUI.cs again --- Assets/Scripts/Upgrades/LevelUpUI.cs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Assets/Scripts/Upgrades/LevelUpUI.cs b/Assets/Scripts/Upgrades/LevelUpUI.cs index aaae22e..a9dd61e 100644 --- a/Assets/Scripts/Upgrades/LevelUpUI.cs +++ b/Assets/Scripts/Upgrades/LevelUpUI.cs @@ -31,6 +31,7 @@ public class LevelUpUI : MonoBehaviour private Button _option1Button; private Button _option2Button; private Button _highlightedButton; + private int _lastNavZone = 0; // -1 = left, 0 = neutral, 1 = right private void Awake() { @@ -50,7 +51,8 @@ private void OnEnable() InputSystem.actions.FindActionMap("UI").Enable(); // Subscribe to input events - _navigateAction.started += OnNavigatePerformed; + _lastNavZone = 0; + _navigateAction.performed += OnNavigatePerformed; _submitAction.performed += OnSubmitPerformed; // Pause the game @@ -116,20 +118,18 @@ private IEnumerator SelectSomething() private void OnNavigatePerformed(InputAction.CallbackContext context) { - if (!gameObject.activeSelf) - { - return; - } - + if (!gameObject.activeSelf) return; + var direction = context.ReadValue(); - if (direction.x < 0) - { + int zone = direction.x < -0.5f ? -1 : direction.x > 0.5f ? 1 : 0; + + if (zone == _lastNavZone) return; // no zone change, skip + _lastNavZone = zone; + + if (zone == -1) SetHighlightedButton(_option1Button); - } - else if (direction.x > 0) - { + else if (zone == 1) SetHighlightedButton(_option2Button); - } } private void OnSubmitPerformed(InputAction.CallbackContext context) @@ -154,7 +154,7 @@ public void SetHighlightedButton(Button button) private void OnDisable() { // Unsubscribe from input events - _navigateAction.started -= OnNavigatePerformed; + _navigateAction.performed -= OnNavigatePerformed; _submitAction.performed -= OnSubmitPerformed; _option1Button.onClick.RemoveAllListeners(); From 5a24944a96017eda5f1516fb55515dece78ff193 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:38:54 +0100 Subject: [PATCH 05/16] remove right-joystick UI navigation --- .../Settings/InputSystem_Actions.inputactions | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/Assets/Settings/InputSystem_Actions.inputactions b/Assets/Settings/InputSystem_Actions.inputactions index f84be2a..921b487 100644 --- a/Assets/Settings/InputSystem_Actions.inputactions +++ b/Assets/Settings/InputSystem_Actions.inputactions @@ -413,17 +413,6 @@ "isComposite": false, "isPartOfComposite": true }, - { - "name": "up", - "id": "9144cbe6-05e1-4687-a6d7-24f99d23dd81", - "path": "/rightStick/up", - "interactions": "", - "processors": "", - "groups": ";Gamepad", - "action": "Navigate", - "isComposite": false, - "isPartOfComposite": true - }, { "name": "down", "id": "2db08d65-c5fb-421b-983f-c71163608d67", @@ -435,17 +424,6 @@ "isComposite": false, "isPartOfComposite": true }, - { - "name": "down", - "id": "58748904-2ea9-4a80-8579-b500e6a76df8", - "path": "/rightStick/down", - "interactions": "", - "processors": "", - "groups": ";Gamepad", - "action": "Navigate", - "isComposite": false, - "isPartOfComposite": true - }, { "name": "left", "id": "8ba04515-75aa-45de-966d-393d9bbd1c14", @@ -457,17 +435,6 @@ "isComposite": false, "isPartOfComposite": true }, - { - "name": "left", - "id": "712e721c-bdfb-4b23-a86c-a0d9fcfea921", - "path": "/rightStick/left", - "interactions": "", - "processors": "", - "groups": ";Gamepad", - "action": "Navigate", - "isComposite": false, - "isPartOfComposite": true - }, { "name": "right", "id": "fcd248ae-a788-4676-a12e-f4d81205600b", @@ -479,17 +446,6 @@ "isComposite": false, "isPartOfComposite": true }, - { - "name": "right", - "id": "1f04d9bc-c50b-41a1-bfcc-afb75475ec20", - "path": "/rightStick/right", - "interactions": "", - "processors": "", - "groups": ";Gamepad", - "action": "Navigate", - "isComposite": false, - "isPartOfComposite": true - }, { "name": "", "id": "fb8277d4-c5cd-4663-9dc7-ee3f0b506d90", From 2d622afe18711fb52a25305bbbbf82e65353bbe3 Mon Sep 17 00:00:00 2001 From: JoshuaMoelans <60878493+JoshuaMoelans@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:38:56 +0100 Subject: [PATCH 06/16] fix ScorePoster UI interactions - auto-select 'Enter Name' on game-over - navigate to the correct fields if deselected - fix double-post possibility --- Assets/Scenes/BattleScene.unity | 49 +++- Assets/Scripts/SceneManagers/HUDManager.cs | 248 +++++++++++++++++- .../SceneManagers/TitleSceneManager.cs | 2 + Assets/Scripts/UI/HUD/HUD.cs | 8 +- Assets/Scripts/UI/ScorePoster.cs | 41 +++ 5 files changed, 331 insertions(+), 17 deletions(-) diff --git a/Assets/Scenes/BattleScene.unity b/Assets/Scenes/BattleScene.unity index 3ffd5af..4109539 100644 --- a/Assets/Scenes/BattleScene.unity +++ b/Assets/Scenes/BattleScene.unity @@ -870,6 +870,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -1364,6 +1365,7 @@ TilemapCollider2D: m_UseDelaunayMesh: 0 --- !u!483693784 &527090879 TilemapRenderer: + serializedVersion: 2 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -1382,6 +1384,8 @@ TilemapRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -1403,9 +1407,11 @@ TilemapRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: -21974027 m_SortingLayer: -3 m_SortingOrder: 0 + m_MaskInteraction: 0 m_ChunkSize: {x: 32, y: 32, z: 32} m_ChunkCullingBounds: {x: 0, y: 0, z: 0} m_MaxChunkCount: 16 @@ -1413,7 +1419,6 @@ TilemapRenderer: m_SortOrder: 0 m_Mode: 0 m_DetectChunkCullingBounds: 0 - m_MaskInteraction: 0 --- !u!1839735485 &527090880 Tilemap: m_ObjectHideFlags: 0 @@ -8005,6 +8010,7 @@ MonoBehaviour: _scorePoster: {fileID: 1394716927} _tryAgain: {fileID: 1730555519} _quit: {fileID: 170557571} + _hudManager: {fileID: 0} --- !u!114 &715298360 MonoBehaviour: m_ObjectHideFlags: 0 @@ -8019,6 +8025,7 @@ MonoBehaviour: m_EditorClassIdentifier: tryAgainButton: {fileID: 1730555519} quitButton: {fileID: 170557571} + _scorePoster: {fileID: 1394716927} --- !u!1001 &719918155 PrefabInstance: m_ObjectHideFlags: 0 @@ -8126,6 +8133,7 @@ Transform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!483693784 &739222992 TilemapRenderer: + serializedVersion: 2 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -8144,6 +8152,8 @@ TilemapRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -8165,9 +8175,11 @@ TilemapRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 719373989 m_SortingLayer: -2 m_SortingOrder: 0 + m_MaskInteraction: 0 m_ChunkSize: {x: 32, y: 32, z: 32} m_ChunkCullingBounds: {x: 0, y: 0, z: 0} m_MaxChunkCount: 16 @@ -8175,7 +8187,6 @@ TilemapRenderer: m_SortOrder: 0 m_Mode: 0 m_DetectChunkCullingBounds: 0 - m_MaskInteraction: 0 --- !u!1839735485 &739222993 Tilemap: m_ObjectHideFlags: 0 @@ -10475,6 +10486,10 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 2050277062} m_Modifications: + - target: {fileID: 1872956838238847735, guid: 6cde6a2c228dc40d0b42ba0b19ca2fc3, type: 3} + propertyPath: _damageAmount + value: 25 + objectReference: {fileID: 0} - target: {fileID: 3306419623360918530, guid: 6cde6a2c228dc40d0b42ba0b19ca2fc3, type: 3} propertyPath: m_Name value: NotHotDogPickup @@ -10694,6 +10709,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -11135,6 +11151,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -11657,7 +11674,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: post your score! + m_text: submit your score! m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: be23bdf943a1d4c8aa3639183c48b7b5, type: 2} m_sharedMaterial: {fileID: -2666825150013189303, guid: be23bdf943a1d4c8aa3639183c48b7b5, type: 2} @@ -11695,6 +11712,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -11900,6 +11918,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -12118,6 +12137,7 @@ Transform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!483693784 &1455703351 TilemapRenderer: + serializedVersion: 2 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -12136,6 +12156,8 @@ TilemapRenderer: m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -12157,9 +12179,11 @@ TilemapRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: -1363271971 m_SortingLayer: -4 m_SortingOrder: 0 + m_MaskInteraction: 0 m_ChunkSize: {x: 32, y: 32, z: 32} m_ChunkCullingBounds: {x: 0, y: 0, z: 0} m_MaxChunkCount: 16 @@ -12167,7 +12191,6 @@ TilemapRenderer: m_SortOrder: 0 m_Mode: 0 m_DetectChunkCullingBounds: 0 - m_MaskInteraction: 0 --- !u!1839735485 &1455703352 Tilemap: m_ObjectHideFlags: 0 @@ -32618,6 +32641,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -33092,6 +33116,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -33973,6 +33998,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -34254,6 +34280,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -34435,6 +34462,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -34571,6 +34599,7 @@ MonoBehaviour: m_VerticalAlignment: 256 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -34927,6 +34956,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -35163,6 +35193,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -35327,6 +35358,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -35598,6 +35630,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 6 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -35782,6 +35815,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 6 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -36033,6 +36067,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 6 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -36537,7 +36572,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 1106abb26731449ba9105911f47bd350, type: 3} m_Name: m_EditorClassIdentifier: - _moveSpeed: 5 --- !u!224 &4107652790464639195 RectTransform: m_ObjectHideFlags: 0 @@ -36676,6 +36710,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 6 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -36922,6 +36957,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -37103,6 +37139,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -37538,6 +37575,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 @@ -37685,6 +37723,7 @@ MonoBehaviour: m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 4 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 diff --git a/Assets/Scripts/SceneManagers/HUDManager.cs b/Assets/Scripts/SceneManagers/HUDManager.cs index f8f09cf..62d2561 100644 --- a/Assets/Scripts/SceneManagers/HUDManager.cs +++ b/Assets/Scripts/SceneManagers/HUDManager.cs @@ -1,5 +1,8 @@ +using DG.Tweening; +using TMPro; using UI; using UnityEngine; +using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.UI; @@ -9,33 +12,74 @@ public class HUDManager : MonoBehaviour { private InputAction _navigateAction; private InputAction _submitAction; - + [SerializeField] private GameObject tryAgainButton; [SerializeField] private GameObject quitButton; - + [SerializeField] private ScorePoster _scorePoster; + private Highlighter _tryAgainHighlighter; private Highlighter _quitHighlighter; + private Highlighter _submitHighlighter; + private TMP_InputField _nameField; + private Button _submitButton; + + // DOTween animation for the name field (avoids calling OnPointerEnter(null) on + // TMP_InputField, which crashes on Switch due to the NintendoSDK TMP integration) + private Tween _nameFieldTween; + + // Direct color tween for the submit button — mirrors the name field approach because + // OnPointerEnter(null) on a Highlighter added at runtime doesn't reliably trigger + // DoStateTransition when the button's parent was inactive at AddComponent time. + private Tween _submitColorTween; + private Graphic _submitGraphic; + private Color _submitHighlightColor; + private Color _submitNormalColor; + private bool _submitNormalColorCaptured; private GameObject _highlightedButton; + private bool _nameFieldFocused; + + // Prevent rapid double-fire from analog sticks or composite bindings + private float _lastNavTime; + private const float NavCooldown = 0.2f; + private void Awake() { _navigateAction = InputSystem.actions.FindAction("Navigate"); _submitAction = InputSystem.actions.FindAction("Submit"); - + // Subscribe to input events _navigateAction.performed += OnNavigatePerformed; _submitAction.performed += OnSubmitPerformed; - + _tryAgainHighlighter = tryAgainButton.GetComponent(); _quitHighlighter = quitButton.GetComponent(); + + if (_scorePoster != null) + { + _nameField = _scorePoster.NameField; + _submitButton = _scorePoster.SubmitButton; + // Capture the highlight color from the navigation buttons for direct tweening. + _submitHighlightColor = tryAgainButton.GetComponent