Files
zeling_v2/Assets/_Game/Scripts/Combat/ClashResolver.cs

71 lines
2.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}