多轮审查和修复
This commit is contained in:
70
Assets/Scripts/Combat/ClashResolver.cs
Normal file
70
Assets/Scripts/Combat/ClashResolver.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Combat
|
||||
{
|
||||
/// <summary>
|
||||
/// 拼刀系统单例服务(架构 06_CombatModule §15)。
|
||||
/// 常驻 Persistent 场景,由 GameManager 持有。
|
||||
/// 当玩家与敌人的近战 HitBox 同时激活并相互重叠时触发拼刀:双方均不扣血,各自弹开。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-500)]
|
||||
public class ClashResolver : MonoBehaviour, IClashService
|
||||
{
|
||||
[SerializeField] private VoidEventChannelSO _onNailClash; // EVT_NailClash
|
||||
[SerializeField] private ClashConfigSO _config;
|
||||
|
||||
// 防止同一碰撞在同帧被双方 HitBox 各触发一次(去重)
|
||||
// Key = (min(idA,idB), max(idA,idB)),顺序无关且无 XOR 哈希碰撞风险
|
||||
private readonly HashSet<(int, int)> _processedThisFrame = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (ServiceLocator.GetOrDefault<IClashService>() != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
ServiceLocator.Register<IClashService>(this);
|
||||
Debug.Assert(_config != null, "[ClashResolver] _config 未赋值,请在 Inspector 中指定 ClashConfigSO。", this);
|
||||
}
|
||||
|
||||
private void LateUpdate() => _processedThisFrame.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// 由 HitBox.OnTriggerEnter2D 调用。
|
||||
/// 对同一对 HitBox 每帧只处理一次(HashSet 去重)。
|
||||
/// </summary>
|
||||
public void ResolveClash(HitBox hitBoxA, HitBox hitBoxB)
|
||||
{
|
||||
int idA = hitBoxA.GetInstanceID();
|
||||
int idB = hitBoxB.GetInstanceID();
|
||||
(int, int) key = (System.Math.Min(idA, idB), System.Math.Max(idA, idB));
|
||||
if (!_processedThisFrame.Add(key)) return;
|
||||
|
||||
// 1. 拼刀冻帧(比普通命中的 2 帧更短,强化拼刀手感)
|
||||
ServiceLocator.GetOrDefault<IHitStopService>()?.FreezeFrames(_config.ClashFreezeFrames);
|
||||
|
||||
// 2. 双方弹开
|
||||
ApplyClashKnockback(hitBoxA.OwnerRigidbody, hitBoxB.transform.position);
|
||||
ApplyClashKnockback(hitBoxB.OwnerRigidbody, hitBoxA.transform.position);
|
||||
|
||||
// 3. 广播事件(VFX / Audio / CameraImpulse 订阅)
|
||||
_onNailClash?.Raise();
|
||||
}
|
||||
|
||||
private void ApplyClashKnockback(Rigidbody2D rb, Vector2 oppositePos)
|
||||
{
|
||||
if (rb == null) return;
|
||||
Vector2 dir = ((Vector2)rb.transform.position - oppositePos).normalized;
|
||||
rb.AddForce(dir * _config.ClashKnockbackForce, ForceMode2D.Impulse);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ServiceLocator.Unregister<IClashService>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user