多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -0,0 +1,102 @@
using System.Collections;
using UnityEngine;
using BaseGames.Core;
namespace BaseGames.Combat
{
/// <summary>
/// 命中冻帧服务接口。
/// </summary>
public interface IHitStopService
{
/// <summary>冻帧 <paramref name="frames"/> 帧(以 fixedDeltaTime 换算为实际时长)。</summary>
void FreezeFrames(int frames);
/// <summary>冻帧指定时长Unscaled 秒)。</summary>
void FreezeDuration(float unscaledSeconds);
/// <summary>游戏正常时间缩放(默认 1子弹时间等功能修改此属性。</summary>
float BaseTimeScale { get; set; }
}
/// <summary>
/// 命中冻帧服务HitStop架构 06_CombatModule §16
/// 通过短暂将 Time.timeScale 设为 0 实现"冻帧"效果,强化打击感。
/// 常驻 Persistent 场景,由 GameManager 持有;通过 ServiceLocator 注册访问。
///
/// 设计说明:
/// - 多次并发请求取最长持续时间StopCoroutine + 重启)
/// - 使用 WaitForSecondsRealtime 确保 timeScale=0 时协程仍能恢复
/// - OnDestroy 强制还原 timeScale防止异常退出导致游戏卡死
/// </summary>
[DefaultExecutionOrder(-400)]
public class HitStopManager : MonoBehaviour, IHitStopService
{
/// <summary>游戏正常时间缩放(默认 1通过属性读取以便外部修改子弹时间时保留基准值。</summary>
public float BaseTimeScale
{
get => _baseTimeScale;
set => _baseTimeScale = Mathf.Clamp(value, 0.01f, 10f);
}
private float _baseTimeScale = 1f;
private Coroutine _activeRoutine;
private float _freezeEndTime;
// ── 生命周期 ──────────────────────────────────────────────────────
private void Awake()
{
if (ServiceLocator.GetOrDefault<IHitStopService>() != null) { Destroy(gameObject); return; }
ServiceLocator.Register<IHitStopService>(this);
}
private void OnDestroy()
{
// 安全恢复:防止场景卸载/异常退出时 timeScale 永久为 0
Time.timeScale = _baseTimeScale;
ServiceLocator.Unregister<IHitStopService>(this);
}
// ── 公共 API ──────────────────────────────────────────────────────
/// <summary>
/// 冻帧 <paramref name="frames"/> 帧(以 fixedDeltaTime 换算为实际时长)。
/// 若已有冻帧进行中,取两者中持续时间较长的(避免短请求截断较长的冻帧)。
/// </summary>
/// <param name="frames">冻帧帧数fixedDeltaTime 单位。0 或负数无效。</param>
public void FreezeFrames(int frames)
{
if (frames <= 0) return;
FreezeDuration(frames * Time.fixedDeltaTime);
}
/// <summary>
/// 冻帧指定时长Unscaled 实际秒数)。
/// 若已有冻帧进行中,取两者中较长的。
/// </summary>
/// <param name="unscaledSeconds">实际时长(秒),不受 timeScale 影响。</param>
public void FreezeDuration(float unscaledSeconds)
{
if (unscaledSeconds <= 0f) return;
float newEndTime = Time.unscaledTime + unscaledSeconds;
// 已有更长的冻帧进行中,放弃短请求,避免截断
if (_activeRoutine != null && newEndTime <= _freezeEndTime) return;
_freezeEndTime = newEndTime;
if (_activeRoutine != null)
StopCoroutine(_activeRoutine);
_activeRoutine = StartCoroutine(FreezeRoutine(unscaledSeconds));
}
// ── 内部实现 ──────────────────────────────────────────────────────
private IEnumerator FreezeRoutine(float unscaledSeconds)
{
Time.timeScale = 0f;
yield return new WaitForSecondsRealtime(unscaledSeconds);
Time.timeScale = _baseTimeScale;
_activeRoutine = null;
}
}
}