Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run-demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
Write-Output "Found executable: $($exePath.FullName)"
$startTime = Get-Date

Start-Process -FilePath $exePath -Wait -PassThru -NoNewWindow
Start-Process -FilePath $exePath -ArgumentList "-demo" -Wait -PassThru -NoNewWindow

$endTime = Get-Date
$duration = $endTime - $startTime
Expand Down
16 changes: 8 additions & 8 deletions Assets/Resources/DemoConfig.asset
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: dd9ba42fbcfea443dae0540ef0f44176, type: 3}
m_Name: DemoConfig
m_EditorClassIdentifier:
_enabled: 1
_apiUrl: https://sentaur-leaderboard-f7z2cjcdzq-uc.a.run.app
_enabled: 0
_apiUrl:
_user:
Username:
Password:
_autoPlay: 1
_notHotDogParticleEffect: 1
_fetchUpgradeFromServer: 1
_crashOnGameOver: 1
Username:
Password:
_autoPlay: 0
_notHotDogParticleEffect: 0
_fetchUpgradeFromServer: 0
_crashOnGameOver: 0
15 changes: 9 additions & 6 deletions Assets/Scripts/Characters/Arrow.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using UnityEngine;
using UnityEngine.InputSystem;

public class Arrow : MonoBehaviour
{
[SerializeField] private bool _forceEnable;

private void Awake()
{
if (_forceEnable)
{
return;
}

if (Application.platform != RuntimePlatform.Android &&
Application.platform != RuntimePlatform.IPhonePlayer)
{
gameObject.SetActive(false);
}

// Show arrow on mobile platforms or when a gamepad is connected
bool shouldShowArrow = Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer ||
Gamepad.current != null;

gameObject.SetActive(shouldShowArrow);
}
}
2 changes: 1 addition & 1 deletion Assets/Scripts/Characters/DemoPlayerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class DemoPlayerController : MonoBehaviour

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();
}

private void OnEnable()
Expand Down
11 changes: 11 additions & 0 deletions Assets/Scripts/Config/ArgumentReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,15 @@ public static string GetCommandLineArg(string name)
}
return null;
}

public static bool HasCommandLineFlag(string name)
{
var args = Environment.GetCommandLineArgs();
for (var i = 0; i < args.Length; i++)
{
if (args[i] == "-" + name)
return true;
}
return false;
}
}
34 changes: 32 additions & 2 deletions Assets/Scripts/Config/DemoConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,45 @@ public class DemoConfiguration : ScriptableObject
[SerializeField] private bool _notHotDogParticleEffect;
[SerializeField] private bool _fetchUpgradeFromServer;
[SerializeField] private bool _crashOnGameOver;


private bool _overridesApplied;

public bool Enabled => _enabled;
public string ApiUrl => _apiUrl;
public User User => _user;

public bool AutoPlay => _enabled && _autoPlay;
public bool NotHotDogParticleEffect => _enabled && _notHotDogParticleEffect;
public bool FetchUpgradeFromServer => _enabled && _fetchUpgradeFromServer;
public bool CrashOnGameOver => _enabled && _crashOnGameOver;

public void ApplyRuntimeOverrides()
{
if (_overridesApplied)
return;
_overridesApplied = true;

if (ArgumentReader.HasCommandLineFlag("demo"))
{
_enabled = true;
_autoPlay = true;
_crashOnGameOver = true;
_notHotDogParticleEffect = true;
_fetchUpgradeFromServer = true;
}
}

private static DemoConfiguration _instance;

public static DemoConfiguration Load()
{
if (_instance == null)
{
_instance = Resources.Load("DemoConfig") as DemoConfiguration;
_instance?.ApplyRuntimeOverrides();
}
return _instance;
}
Comment on lines +49 to +57
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The static DemoConfiguration singleton doesn't clear its _instance on destroy. Scene transitions using LoadSceneMode.Single can cause it to hold a dangling reference to a destroyed object.
Severity: HIGH

Suggested Fix

Implement an OnDestroy() method in DemoConfiguration.cs. Inside this method, check if the current instance _instance is the one being destroyed, and if so, set the static _instance field to null. This ensures that the singleton is properly re-initialized after a scene unload.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: Assets/Scripts/Config/DemoConfiguration.cs#L49-L57

Potential issue: The static `DemoConfiguration` singleton loads a ScriptableObject into
a static `_instance` field but lacks an `OnDestroy` method to nullify this field. When a
scene transition occurs using `LoadSceneMode.Single`, Unity unloads unused assets,
including the `DemoConfiguration` object. Because the static `_instance` reference is
not cleared, subsequent calls to `DemoConfiguration.Load()` in a new scene will
incorrectly return the stale reference to the destroyed object. Accessing any properties
on this returned object will result in a `UnityEngine.MissingReferenceException`,
causing a crash.

Did we get this right? 👍 / 👎 to inform future reviews.

}

[Serializable]
Expand Down
2 changes: 1 addition & 1 deletion Assets/Scripts/Pickups/NotHotDogPickup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class NotHotDogPickup : PickupBase

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();
}

protected override void OnCollect(Player player)
Expand Down
8 changes: 7 additions & 1 deletion Assets/Scripts/SceneManagers/BattleSceneManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ private enum GameState

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();

InputSystem.actions.FindActionMap("Player").Enable();
InputSystem.actions.FindActionMap("UI").Disable();
Expand Down Expand Up @@ -463,6 +463,12 @@ private void SetCurrentLevel(int level)

public void OnPause()
{
// Don't allow pausing if the level up UI is active (it already pauses the game)
if (_levelUpUI.activeSelf)
{
return;
}

if (_gameState == GameState.Playing)
{
PauseGame();
Expand Down
32 changes: 23 additions & 9 deletions Assets/Scripts/SceneManagers/HUDManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ private void Awake()
_navigateAction = InputSystem.actions.FindAction("Navigate");
_submitAction = InputSystem.actions.FindAction("Submit");

// Subscribe to the performed callback only
// Subscribe to input events
_navigateAction.performed += OnNavigatePerformed;
_submitAction.performed += OnSubmitPerformed;

_tryAgainHighlighter = tryAgainButton.GetComponent<Highlighter>();
_quitHighlighter = quitButton.GetComponent<Highlighter>();
}

private void OnDestroy()
{
// Unsubscribe from input events
_navigateAction.performed -= OnNavigatePerformed;
_submitAction.performed -= OnSubmitPerformed;
}

private void OnSubmitPerformed(InputAction.CallbackContext context)
{
Expand All @@ -37,10 +45,14 @@ private void OnSubmitPerformed(InputAction.CallbackContext context)
return;
}

_highlightedButton?.GetComponent<Button>().onClick.Invoke();
// Only invoke if the highlighted button is actually active
if (_highlightedButton != null && _highlightedButton.activeSelf)
{
_highlightedButton.GetComponent<Button>().onClick.Invoke();
}
}

public void OnNavigate()
private void OnNavigatePerformed(InputAction.CallbackContext context)
{
if (!gameObject.activeSelf)
{
Expand All @@ -52,12 +64,7 @@ public void OnNavigate()
return;
}

if (!_navigateAction.WasPressedThisFrame())
{
return;
}

var direction = _navigateAction.ReadValue<Vector2>();
var direction = context.ReadValue<Vector2>();

// Simple navigation between try again and quit buttons
if (_highlightedButton == null)
Expand Down Expand Up @@ -92,5 +99,12 @@ public void SetHighlightedButton(Highlighter highlighted)
highlighted.Highlight();
_highlightedButton = highlighted.gameObject;
}

public void ClearHighlightedButton()
{
_tryAgainHighlighter.Highlight(false);
_quitHighlighter.Highlight(false);
_highlightedButton = null;
}
}
}
2 changes: 1 addition & 1 deletion Assets/Scripts/SceneManagers/TitleSceneManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class TitleSceneManager : MonoBehaviour

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();
_navigateAction = InputSystem.actions.FindAction("Navigate");
_startHighlighter = startButton.GetComponent<Highlighter>();
_quitHighlighter = quitButton.GetComponent<Highlighter>();
Expand Down
10 changes: 9 additions & 1 deletion Assets/Scripts/UI/HUD/HUD.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using SceneManagers;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
Expand All @@ -16,13 +17,14 @@ public class HUD : MonoBehaviour
[SerializeField] private ScorePoster _scorePoster;
[SerializeField] private GameObject _tryAgain;
[SerializeField] private GameObject _quit;
[SerializeField] private HUDManager _hudManager;

private DemoConfiguration _demoConfig;
private XpBar _xpBar;

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();

// get score text component from child
_scoreText = transform.Find("Score").GetComponent<TextMeshProUGUI>();
Expand Down Expand Up @@ -78,6 +80,12 @@ public void HidePause()
_gameOverText.enabled = false;
_tryAgain.SetActive(false);
_quit.SetActive(false);

// Clear the highlighted button to prevent accidental clicks
if (_hudManager != null)
{
_hudManager.ClearHighlightedButton();
}
}

public void ShowGameOver()
Expand Down
2 changes: 1 addition & 1 deletion Assets/Scripts/UI/ScorePoster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class ScorePoster : MonoBehaviour

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();
_gameManager = GameObject.Find("BattleSceneManager").GetComponent<BattleSceneManager>();
_buttonText = _submitButton.GetComponentInChildren<TextMeshProUGUI>();

Expand Down
31 changes: 16 additions & 15 deletions Assets/Scripts/Upgrades/LevelUpUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class LevelUpUI : MonoBehaviour

private void Awake()
{
_demoConfig = Resources.Load("DemoConfig") as DemoConfiguration;
_demoConfig = DemoConfiguration.Load();
_gameManager = GameObject.Find("BattleSceneManager").GetComponent<BattleSceneManager>();

_navigateAction = InputSystem.actions.FindAction("Navigate");
Expand All @@ -49,6 +49,10 @@ private void OnEnable()
InputSystem.actions.FindActionMap("Player").Disable();
InputSystem.actions.FindActionMap("UI").Enable();

// Subscribe to input events
_navigateAction.performed += OnNavigatePerformed;
_submitAction.performed += OnSubmitPerformed;

// Pause the game
Time.timeScale = 0;

Expand All @@ -75,6 +79,9 @@ private void OnEnable()

_option1Button.onClick.AddListener(() => SelectUpgrade(upgradeChoice1));
_option2Button.onClick.AddListener(() => SelectUpgrade(upgradeChoice2));

// Set initial highlighted button to option 1
SetHighlightedButton(_option1Button);

if (_demoConfig != null && _demoConfig.AutoPlay)
{
Expand Down Expand Up @@ -107,19 +114,14 @@ private IEnumerator SelectSomething()
_highlightedButton?.GetComponent<Button>().onClick.Invoke();
}

public void OnNavigate()
private void OnNavigatePerformed(InputAction.CallbackContext context)
{
if (!gameObject.activeSelf)
{
return;
}

if (!_navigateAction.WasPressedThisFrame())
{
return;
}

var direction = _navigateAction.ReadValue<Vector2>();
var direction = context.ReadValue<Vector2>();
if (direction.x < 0)
{
SetHighlightedButton(_option1Button);
Expand All @@ -130,19 +132,14 @@ public void OnNavigate()
}
}

public void OnSubmit()
private void OnSubmitPerformed(InputAction.CallbackContext context)
{
if (!gameObject.activeSelf)
{
return;
}

if (!_submitAction.IsPressed())
{
return;
}

_highlightedButton?.GetComponent<Button>().onClick.Invoke();
_highlightedButton?.onClick.Invoke();
}

public void SetHighlightedButton(Button button)
Expand All @@ -156,6 +153,10 @@ public void SetHighlightedButton(Button button)

private void OnDisable()
{
// Unsubscribe from input events
_navigateAction.performed -= OnNavigatePerformed;
_submitAction.performed -= OnSubmitPerformed;

_option1Button.onClick.RemoveAllListeners();
_option2Button.onClick.RemoveAllListeners();
}
Expand Down
Loading
Loading