This commit is contained in:
2026-07-07 03:34:56 +08:00
parent e7891d7e00
commit ecb20cf370
81 changed files with 2108 additions and 293 deletions
+24 -6
View File
@@ -1,6 +1,7 @@
using UnityEngine;
using UnityEngine.UI;
using IndianOceanAssets.Engine2_5D;
using Architecture.Core;
namespace GameFramework
{
@@ -27,15 +28,32 @@ namespace GameFramework
private float _currentAlpha;
private bool _isFlashing;
[Header("SO 事件通道(替代 static HealthSystem.onPlayerDamaged 事件)")]
[SerializeField] private GameEvent onPlayerDamagedEvent;
private void OnEnable()
{
if (onPlayerDamagedEvent == null)
Debug.LogWarning("[DamageFlashOverlay] onPlayerDamagedEvent 未接线!请在场景中预置已接线的 DamageFlashOverlay 实例,否则受击不闪红。", this);
else
onPlayerDamagedEvent.Register(OnPlayerDamaged);
}
private void OnDisable()
{
if (onPlayerDamagedEvent != null)
onPlayerDamagedEvent.Unregister(OnPlayerDamaged);
}
private void OnValidate()
{
if (onPlayerDamagedEvent == null)
Debug.LogWarning($"[DamageFlashOverlay] On Player Damaged Event 未接线({gameObject.name})。受击泛红不会出现。", this);
}
private void Start()
{
CreateOverlay();
HealthSystem.onPlayerDamaged += OnPlayerDamaged;
}
private void OnDestroy()
{
HealthSystem.onPlayerDamaged -= OnPlayerDamaged;
}
private void Update()
+37 -75
View File
@@ -2,12 +2,14 @@ using UnityEngine;
using UnityEngine.UI;
using TMPro;
using IndianOceanAssets.Engine2_5D;
using Architecture.Core;
using Architecture.Variables;
namespace GameFramework
{
/// <summary>
/// 游戏内 HUD —— 魂灵计数 + 生命图标 + 技能CD。
///
///
/// 布局:
/// - 左上角:收集到的魂灵(icon + 数字)
/// - 左下角:5个生命图标
@@ -36,12 +38,14 @@ namespace GameFramework
[Header("暂停")]
[SerializeField] private Button pauseButton;
[Header("SO 事件通道 / 变量")]
[SerializeField] private IntEvent scoreChangedEvent;
[SerializeField] private IntVariable playerHealthVar;
private HealthSystem _playerHealth;
private SpiritLanternSystem _lanternSystem;
private EchoSystem _echoSystem;
private PlayerController _playerController;
private int _maxHealth = 5;
private bool _subscribedScoreEvent = false;
private int _lastSoulCount = -1;
void Start()
@@ -62,59 +66,34 @@ namespace GameFramework
if (soulIcon != null)
SoulIconRect = soulIcon.rectTransform;
// 订阅分数事件(魂灵计数复用分数系统)
if (ScoreManager.Instance != null)
// 订阅分数事件(魂灵计数复用分数系统):无条件注册,事件资产始终存在;
// 初始值若 ScoreManager 已就绪则取实时分,否则先显示 0,首个 ScoreChanged 会刷新。
scoreChangedEvent?.Register(UpdateSoulCount);
UpdateSoulCount(ScoreManager.Instance != null ? ScoreManager.Instance.CurrentScore : 0);
// 订阅玩家血量变量(替代反射读取私有字段 + Update 条件 Find 回退)
if (playerHealthVar != null)
{
ScoreManager.onScoreChanged += UpdateSoulCount;
_subscribedScoreEvent = true;
UpdateSoulCount(ScoreManager.Instance.CurrentScore);
playerHealthVar.OnValueChanged += UpdateLifeIcons;
UpdateLifeIcons(playerHealthVar.Value);
}
UpdateLifeIcons();
// 自动创建受击泛红特效(如果场景中没有)
// 受击泛红特效:场景需预置已接线的 DamageFlashOverlay 实例(不再自动创建,
// 否则会生成一个事件为 null 的实例,既无效果又掩盖「未接线」问题)
if (FindObjectOfType<DamageFlashOverlay>() == null)
{
var overlayObj = new GameObject("DamageFlashOverlay");
overlayObj.AddComponent<DamageFlashOverlay>();
}
Debug.LogWarning("[GameHUD] 场景中未找到 DamageFlashOverlay,受击泛红特效不会显示。请在场景中放置一个、并把它 On Player Damaged Event 字段接上 OnPlayerDamaged 资产。", this);
}
void OnDestroy()
{
if (_subscribedScoreEvent)
{
ScoreManager.onScoreChanged -= UpdateSoulCount;
_subscribedScoreEvent = false;
}
scoreChangedEvent?.Unregister(UpdateSoulCount);
if (playerHealthVar != null)
playerHealthVar.OnValueChanged -= UpdateLifeIcons;
}
void Update()
{
// 延迟订阅 ScoreManager(跨场景后 ScoreManager 可能才创建)
if (!_subscribedScoreEvent && ScoreManager.Instance != null)
{
ScoreManager.onScoreChanged += UpdateSoulCount;
_subscribedScoreEvent = true;
UpdateSoulCount(ScoreManager.Instance.CurrentScore);
Debug.Log("[GameHUD] 延迟订阅 ScoreManager.onScoreChanged 成功");
}
// 延迟查找玩家(GameSpawnManager 可能比 HUD 晚创建玩家)
if (_playerHealth == null)
{
var player = GameObject.FindWithTag("Player");
if (player != null)
{
_playerHealth = player.GetComponent<HealthSystem>();
_lanternSystem = player.GetComponent<SpiritLanternSystem>();
_echoSystem = player.GetComponent<EchoSystem>();
_playerController = player.GetComponent<PlayerController>();
Debug.Log("[GameHUD] 延迟找到玩家,已绑定组件");
}
}
UpdateLifeIcons();
// 事件已在 Start 中无条件订阅,无需逐帧延迟订阅(避免反模式)。
UpdateSkillCooldowns();
}
@@ -140,17 +119,14 @@ namespace GameFramework
/// <summary>
/// 更新生命图标(左下角)。
/// 根据当前血量显示/隐藏对应图标
/// 直接读取 PlayerHealth 共享变量,由 OnValueChanged 事件驱动,无反射、无逐帧 Find
/// </summary>
private void UpdateLifeIcons()
private void UpdateLifeIcons(int value)
{
if (_playerHealth == null || lifeIcons == null || lifeIcons.Length == 0) return;
// 获取当前血量
var healthField = typeof(HealthSystem).GetField("health",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
int currentHealth = healthField != null ? (int)healthField.GetValue(_playerHealth) : _maxHealth;
if (lifeIcons == null || lifeIcons.Length == 0) return;
if (playerHealthVar == null) return;
int currentHealth = value;
for (int i = 0; i < lifeIcons.Length; i++)
{
if (lifeIcons[i] != null)
@@ -160,43 +136,29 @@ namespace GameFramework
/// <summary>
/// 更新技能CD遮罩(右下角)。
/// 使用 Image.fillAmount 实现圆形CD效果
/// 通过各系统的公共只读属性读取冷却状态,替代反射读取私有字段
/// </summary>
private void UpdateSkillCooldowns()
{
// 冲刺 CD(从 PlayerController 读取 rollCooldown + lastRollTime
// 冲刺 CD(从 PlayerController.RollCooldown 读取
if (_playerController != null)
{
var cdField = typeof(PlayerController).GetField("rollCooldown",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var lastField = typeof(PlayerController).GetField("lastRollTime",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
float sprintCD = cdField != null ? (float)cdField.GetValue(_playerController) : 1f;
float lastRoll = lastField != null ? (float)lastField.GetValue(_playerController) : -999f;
float remaining = Mathf.Max(0f, (lastRoll + sprintCD) - Time.time);
UpdateSkillCD(skillSprintCD, skillSprintIcon, remaining, sprintCD);
var cd = _playerController.RollCooldown;
UpdateSkillCD(skillSprintCD, skillSprintIcon, cd.remaining, cd.total);
}
// 摇铃 CD(从 EchoSystem 读取 cooldown + _lastEchoTime
// 摇铃 CD(从 EchoSystem.BellCooldown 读取
if (_echoSystem != null)
{
var cdField = typeof(EchoSystem).GetField("cooldown",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var lastField = typeof(EchoSystem).GetField("_lastEchoTime",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
float bellCD = cdField != null ? (float)cdField.GetValue(_echoSystem) : 2f;
float lastTime = lastField != null ? (float)lastField.GetValue(_echoSystem) : -999f;
float remaining = Mathf.Max(0f, (lastTime + bellCD) - Time.time);
UpdateSkillCD(skillBellCD, skillBellIcon, remaining, bellCD);
var cd = _echoSystem.BellCooldown;
UpdateSkillCD(skillBellCD, skillBellIcon, cd.remaining, cd.total);
}
// 灵灯 CDSpiritLanternSystem 读取
// 灵灯 CDSpiritLanternSystem 已暴露公共 Cooldown / CooldownRemaining,无反射
if (_lanternSystem != null)
{
float remaining = _lanternSystem.CooldownRemaining;
var cdField = typeof(SpiritLanternSystem).GetField("cooldown",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
float maxCD = cdField != null ? (float)cdField.GetValue(_lanternSystem) : 3f;
float maxCD = _lanternSystem.Cooldown;
UpdateSkillCD(skillLanternCD, skillLanternIcon, remaining, maxCD);
}
}
+25 -40
View File
@@ -2,6 +2,7 @@ using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using Architecture.Core;
namespace GameFramework
{
@@ -12,14 +13,12 @@ namespace GameFramework
/// 1. 在 Gameplay 场景里创建一个空物体,挂上本脚本
/// 2. 运行一次游戏(或点 Editor 按钮),脚本会自动构建子 UI
/// 3. 停止运行后,子 UI 留在场景中,可自由编辑样式/位置/精灵
/// 4. 运行时由 GameManager 调用 GameLostOverlay.Show() 激活
/// 4. 运行时订阅 OnGameOver 事件自激活(不再由 GameManager 直接调用)
///
/// 所有 UI 元素均为 [SerializeField],可在 Inspector 中替换。
/// </summary>
public class GameLostOverlay : MonoBehaviour
{
private static GameLostOverlay _instance;
// ====== Canvas ======
[Header("Canvas(留空则自动创建)")]
[SerializeField] private Canvas lostCanvas;
@@ -43,6 +42,9 @@ namespace GameFramework
[Header("音效")]
[SerializeField] private AudioData lostSFX;
[Header("SO 事件通道(替代 GameManager 直接调用 Show,订阅 OnGameOver 自激活)")]
[SerializeField] private GameEvent onGameOverEvent;
[Header("标题渐现动效")]
[Tooltip("标题渐现总时长(秒)")]
[SerializeField] private float titleFadeDuration = 1.5f;
@@ -54,6 +56,9 @@ namespace GameFramework
[SerializeField] private float rippleFrequency = 15f;
[Tooltip("水波纹材质(留空则自动从 Shader 创建)")]
[SerializeField] private Material rippleMaterial;
[Tooltip("水波纹着色器(留空则尝试 Shader.Find 兜底;建议拖入 Assets/UI/shaders/WaterRippleFade.shader 以避免构建裁剪导致运行时找不到)")]
[SerializeField] private Shader waterRippleShader;
[Tooltip("主界面按钮延迟出现时间(秒)")]
[SerializeField] private float buttonFadeDelay = 2f;
[Tooltip("按钮渐现时长(秒)")]
@@ -65,46 +70,11 @@ namespace GameFramework
private Material _rippleMaterial;
// ====================================================================
// 静态入口
// ====================================================================
/// <summary>
/// 显示失败叠加层。优先使用场景中已有的实例,
/// 没有则动态创建一个。
/// </summary>
public static void Show()
{
// 场景中已有(隐藏的)实例?
if (_instance != null)
{
_instance.Activate();
return;
}
// 尝试在场景中找到
var found = FindObjectOfType<GameLostOverlay>();
if (found != null)
{
_instance = found;
found.Activate();
return;
}
// 都没有 → 动态创建
var obj = new GameObject("GameLostOverlay");
var overlay = obj.AddComponent<GameLostOverlay>();
overlay.BuildAll();
overlay.Activate();
}
// ====================================================================
// 生命周期
// 生命周期(激活由 OnGameOver 事件驱动,见 OnEnable
// ====================================================================
void Awake()
{
_instance = this;
// 如果 Canvas 还没赋值,尝试从自身获取
if (lostCanvas == null)
lostCanvas = GetComponent<Canvas>();
@@ -114,6 +84,21 @@ namespace GameFramework
lostCanvas.enabled = false;
}
void OnEnable()
{
if (onGameOverEvent != null)
onGameOverEvent.Register(OnGameOver);
}
void OnDisable()
{
if (onGameOverEvent != null)
onGameOverEvent.Unregister(OnGameOver);
}
/// <summary>收到 OnGameOver 事件后激活失败叠加层(替代 GameManager 直接调用 Show)。</summary>
private void OnGameOver() => Activate();
void Update()
{
if (!_isVisible) return;
@@ -355,7 +340,7 @@ namespace GameFramework
}
else
{
var shader = Shader.Find("GameFramework/UI/WaterRippleFade");
var shader = waterRippleShader != null ? waterRippleShader : Shader.Find("GameFramework/UI/WaterRippleFade");
if (shader == null)
{
Debug.LogWarning("[GameLostOverlay] 找不到 WaterRippleFade Shader!请确认 Assets/UI/shaders/WaterRippleFade.shader 存在且无编译错误。回退到抖动动效。");
+51 -16
View File
@@ -4,6 +4,7 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using IndianOceanAssets.Engine2_5D;
using Architecture.Core;
namespace GameFramework
{
@@ -13,11 +14,31 @@ namespace GameFramework
/// </summary>
public class GameManager : PersistentSingleton<GameManager>
{
/// <summary>游戏结束(失败)时触发。</summary>
public static Action onGameOver;
[Header("SO 事件通道(替代 static Action 事件,Inspector 拖入对应资产)")]
[SerializeField] private GameEvent onGameOverEvent;
[SerializeField] private GameEvent onGameWinEvent;
/// <summary>游戏胜利时触发。</summary>
public static Action onGameWin;
[Header("SO 事件通道(玩家死亡事件,替代 HealthSystem 直接调用 GameOver")]
[SerializeField] private GameEvent onPlayerDiedEvent;
[Header("运行时集合(替代 FindObjectsOfType<EnemyAI>,需敌人 Prefab 挂 RuntimeSetRegistrar 指向该集合)")]
[SerializeField] private TransformRuntimeSet enemiesSet;
[SerializeField] private EnemyManager enemyManagerRef;
private void OnEnable()
{
if (onPlayerDiedEvent != null)
onPlayerDiedEvent.Register(OnPlayerDied);
}
private void OnDisable()
{
if (onPlayerDiedEvent != null)
onPlayerDiedEvent.Unregister(OnPlayerDied);
}
/// <summary>玩家死亡事件回调:触发失败流程(设置状态 + 过场 + 广播 OnGameOver)。</summary>
private void OnPlayerDied() => GameOver();
public static GameState GameState
{
@@ -49,7 +70,7 @@ namespace GameFramework
{
if (Instance == null) return;
GameState = GameState.GameOver;
onGameOver?.Invoke();
Instance.onGameOverEvent?.Raise();
Instance.StartCoroutine(Instance.DeathTransition());
}
@@ -58,7 +79,7 @@ namespace GameFramework
{
if (Instance == null) return;
GameState = GameState.Victory;
onGameWin?.Invoke();
Instance.onGameWinEvent?.Raise();
Instance.StartCoroutine(Instance.VictoryTransition());
}
@@ -141,17 +162,32 @@ namespace GameFramework
/// </summary>
private void DisableAllEnemyAI()
{
// 禁用所有 EnemyAI
var enemyAIs = FindObjectsOfType<IndianOceanAssets.Engine2_5D.EnemyAI>();
foreach (var ai in enemyAIs)
ai.enabled = false;
int disabled = 0;
// 优先用 Enemies 运行时集合(敌人 Prefab 需挂 RuntimeSetRegistrar 并指向该集合,
// 在 OnEnable/OnDisable 时自动注册/注销自身 Transform)
if (enemiesSet != null)
{
foreach (var t in enemiesSet.Items)
{
if (t == null) continue;
var ai = t.GetComponent<IndianOceanAssets.Engine2_5D.EnemyAI>();
if (ai != null) { ai.enabled = false; disabled++; }
}
}
else
{
// 过渡期兜底:尚未接入 Enemies 集合时回退(Prefab 接入 RuntimeSetRegistrar 后可删除此分支)
var enemyAIs = FindObjectsOfType<IndianOceanAssets.Engine2_5D.EnemyAI>();
foreach (var ai in enemyAIs) ai.enabled = false;
disabled = enemyAIs.Length;
}
// 禁用 EnemyManager(停止生成/管理逻辑)
var enemyManager = FindObjectOfType<IndianOceanAssets.Engine2_5D.EnemyManager>();
if (enemyManager != null)
enemyManager.enabled = false;
if (enemyManagerRef != null)
enemyManagerRef.enabled = false;
Debug.Log($"[GameManager] 已禁用 {enemyAIs.Length} 个 EnemyAI + EnemyManager");
Debug.Log($"[GameManager] 已禁用 {disabled} 个 EnemyAI + EnemyManager");
}
/// <summary>
@@ -230,8 +266,7 @@ namespace GameFramework
yield return null;
// 在全黑之上显示失败叠加层("你已迷失……" + 主界面按钮)
GameLostOverlay.Show();
// 失败叠加层("你已迷失……")由 GameLostOverlay 订阅 OnGameOver 事件自显示,此处不再直接调用。
// 等待一帧让 UI 渲染
yield return null;
-75
View File
@@ -1,75 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
namespace GameFramework
{
/// <summary>
/// 游戏结束画面。
/// 监听 GameManager.onGameOver 事件,显示结束 Canvas 并暂停游戏。
/// 玩家按确认键或点击按钮后跳转到排行榜场景。
/// </summary>
public class GameOverScreen : MonoBehaviour
{
[Header("Canvas")]
[SerializeField] Canvas gameOverCanvas;
[Header("Button")]
[SerializeField] Button confirmButton;
[Header("Audio")]
[SerializeField] AudioData gameOverSFX;
[Header("Input")]
[SerializeField] KeyCode confirmKey = KeyCode.Return;
void OnEnable()
{
GameManager.onGameOver += ShowGameOver;
}
void OnDisable()
{
GameManager.onGameOver -= ShowGameOver;
}
void Start()
{
// 初始隐藏
if (gameOverCanvas != null)
gameOverCanvas.enabled = false;
if (confirmButton != null)
confirmButton.onClick.AddListener(OnConfirmClick);
}
void ShowGameOver()
{
// 失败时由 GameLostOverlay 接管 UI,这里不再显示旧 Canvas
// 暂停和鼠标由 GameManager + GameLostOverlay 统一处理
return;
}
void Update()
{
// 游戏结束画面可见时,按确认键跳转
if (gameOverCanvas != null && gameOverCanvas.enabled)
{
if (Input.GetKeyDown(confirmKey) || Input.GetKeyDown(KeyCode.Space))
{
OnConfirmClick();
}
}
}
void OnConfirmClick()
{
// 先解除暂停
if (TimeController.Instance != null)
TimeController.Instance.Unpause();
// 跳转到排行榜场景
if (SceneLoader.Instance != null)
SceneLoader.Instance.LoadScoringScene();
}
}
}
-11
View File
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: CClOsC78Vi9/cIog8UeuUzecgGTFlu5AQ5iyvdkeMcyG1n6ns9R5ds8=
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+9 -11
View File
@@ -1,6 +1,7 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using Architecture.Core;
namespace GameFramework
{
@@ -10,9 +11,9 @@ namespace GameFramework
public enum GameResult { Win, Lose }
/// <summary>
/// 游戏结果界面(支持胜利/失败)。
/// 监听 GameManager.onGameOver 和 onGameWin 事件,
/// 显示对应面板并暂停游戏。确认后跳转到排行榜场景
/// 游戏结果界面(仅负责「胜利」结算)。
/// 监听 OnGameWin 事件,显示胜利面板并暂停游戏,确认后跳转到排行榜场景。
/// 「失败」由 GameLostOverlay 接管(订阅 OnGameOver),本屏不处理失败,避免空响应
/// </summary>
public class GameResultScreen : MonoBehaviour
{
@@ -38,6 +39,9 @@ namespace GameFramework
[Header("Input")]
[SerializeField] private KeyCode confirmKey = KeyCode.Return;
[Header("SO 事件通道(胜利事件;失败由 GameLostOverlay 接管)")]
[SerializeField] private GameEvent onGameWinEvent;
private bool _isVisible;
/// <summary>
@@ -59,20 +63,14 @@ namespace GameFramework
void OnEnable()
{
GameManager.onGameOver += OnGameOver;
GameManager.onGameWin += OnGameWin;
onGameWinEvent?.Register(OnGameWin);
}
void OnDisable()
{
GameManager.onGameOver -= OnGameOver;
GameManager.onGameWin -= OnGameWin;
onGameWinEvent?.Unregister(OnGameWin);
}
void OnGameOver()
{
// 失败时由 GameLostOverlay 接管,不再显示旧失败面板
}
void OnGameWin() => ShowResult(GameResult.Win);
void Start()
@@ -59,10 +59,10 @@ Material:
- _DecalMeshDepthBias: 0
- _DecalMeshViewBias: 0
- _DrawOrder: 0
- _FadeAlpha: 0.96674436
- _FadeAlpha: 1
- _RippleAmplitude: 0.0412
- _RippleFrequency: 10.5
- _RippleIntensity: 0.64074624
- _RippleIntensity: -0
- _RippleSpeed: 3
- _Shininess: 0.2
- _Stencil: 0
+8 -6
View File
@@ -1,5 +1,6 @@
using UnityEngine;
using UnityEngine.UI;
using Architecture.Core;
namespace GameFramework
{
@@ -13,24 +14,25 @@ namespace GameFramework
[SerializeField] Text scoreText;
[SerializeField] string format = "{0}";
[Header("SO 事件通道(替代 static ScoreManager.onScoreChanged 事件)")]
[SerializeField] private IntEvent scoreChangedEvent;
void OnEnable()
{
ScoreManager.onScoreChanged += UpdateText;
scoreChangedEvent?.Register(UpdateText);
}
void OnDisable()
{
ScoreManager.onScoreChanged -= UpdateText;
scoreChangedEvent?.Unregister(UpdateText);
}
void Start()
{
if (scoreText == null)
scoreText = GetComponent<Text>();
if (ScoreManager.Instance != null)
UpdateText(ScoreManager.Instance.CurrentScore);
else
UpdateText(0);
// 初始值走事件:订阅后首个 ScoreChanged 会刷新显示;分数初始即为 0,先显示 0。
UpdateText(0);
}
void UpdateText(int score)
+12 -12
View File
@@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Architecture.Core;
namespace GameFramework
{
@@ -32,7 +33,7 @@ namespace GameFramework
/// <summary>
/// 得分管理器(持久单例)。
/// 维护当前局得分,通过事件通知 UI,并管理 Top-10 排行榜存档。
/// 维护当前局得分,通过 SO 事件通道通知 UI,并管理 Top-10 排行榜存档。
/// </summary>
public class ScoreManager : PersistentSingleton<ScoreManager>
{
@@ -42,10 +43,9 @@ namespace GameFramework
// 当前局得分
int currentScore = 0;
// 当得分变化时触发,参数为最新得分(动画过程中的中间值也会触发)
public static event Action<int> onScoreChanged;
// 当得分完成最终增加时触发,参数为最终得分
public static event Action<int> onScoreSettled;
[Header("SO 事件通道(替代 static onScoreChanged / onScoreSettled 事件,Inspector 拖入对应资产)")]
[SerializeField] private IntEvent scoreChangedEvent;
[SerializeField] private IntEvent scoreSettledEvent;
/// <summary>当前得分(只读)。</summary>
public int CurrentScore => currentScore;
@@ -75,8 +75,8 @@ namespace GameFramework
public void SetScore(int value)
{
currentScore = Mathf.Max(0, value);
onScoreChanged?.Invoke(currentScore);
onScoreSettled?.Invoke(currentScore);
scoreChangedEvent?.Raise(currentScore);
scoreSettledEvent?.Raise(currentScore);
}
/// <summary>
@@ -85,8 +85,8 @@ namespace GameFramework
public void ResetScore()
{
currentScore = 0;
onScoreChanged?.Invoke(0);
onScoreSettled?.Invoke(0);
scoreChangedEvent?.Raise(0);
scoreSettledEvent?.Raise(0);
}
IEnumerator ScoreCountUpCoroutine(int from, int to)
@@ -100,12 +100,12 @@ namespace GameFramework
elapsed += Time.unscaledDeltaTime;
float t = Mathf.Clamp01(elapsed / duration);
int display = Mathf.RoundToInt(Mathf.Lerp(from, to, t));
onScoreChanged?.Invoke(display);
scoreChangedEvent?.Raise(display);
yield return null;
}
onScoreChanged?.Invoke(to);
onScoreSettled?.Invoke(to);
scoreChangedEvent?.Raise(to);
scoreSettledEvent?.Raise(to);
}
#region