摄像机区域的架构改动
This commit is contained in:
17
Assets/_Game/Scripts/Parry/BaseGames.Parry.asmdef
Normal file
17
Assets/_Game/Scripts/Parry/BaseGames.Parry.asmdef
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"precompiledReferences": [],
|
||||
"name": "BaseGames.Parry",
|
||||
"defineConstraints": [],
|
||||
"noEngineReferences": false,
|
||||
"versionDefines": [],
|
||||
"rootNamespace": "BaseGames.Parry",
|
||||
"references": [
|
||||
"BaseGames.Input",
|
||||
"BaseGames.Core.Events"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"overrideReferences": false,
|
||||
"includePlatforms": []
|
||||
}
|
||||
7
Assets/_Game/Scripts/Parry/BaseGames.Parry.asmdef.meta
Normal file
7
Assets/_Game/Scripts/Parry/BaseGames.Parry.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87e177cce09e78a44b5122b4acfe5763
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
38
Assets/_Game/Scripts/Parry/ParryConfigSO.cs
Normal file
38
Assets/_Game/Scripts/Parry/ParryConfigSO.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Parry
|
||||
{
|
||||
/// <summary>
|
||||
/// 弹反系统配置资产(架构 06_CombatModule §9)。
|
||||
/// 可通过 Assets/Create/Combat/ParryConfig 创建。
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "BaseGames/Combat/ParryConfig", fileName = "ParryConfig")]
|
||||
public class ParryConfigSO : ScriptableObject
|
||||
{
|
||||
[Header("阶段时长(秒)")]
|
||||
public float StartupDuration = 0.05f; // 前摇:按键到弹反窗口开启的延迟
|
||||
public float WindowDuration = 0.28f; // Active:弹反有效窗口时长
|
||||
public float EndlagDuration = 0.10f; // 后摇:窗口结束到恢复的延迟
|
||||
public float CounterWindowDuration = 0.5f; // 弹反成功后的反击窗口时长
|
||||
|
||||
[Header("完美弹反判定")]
|
||||
public float PerfectParryThreshold = 0.05f; // Active 开始后的完美弹反窗口(秒)
|
||||
|
||||
[Header("冷却")]
|
||||
public float ParryCooldown = 0.3f; // 两次弹反之间的最短间隔(秒)
|
||||
|
||||
[Header("灵力奖励")]
|
||||
public int SoulGainOnParry = 33; // 普通弹反获得灵力
|
||||
public int SoulGainOnPerfect = 50; // 完美弹反额外获得灵力
|
||||
|
||||
[Header("反击伤害")]
|
||||
public float ParryCounterMultiplier = 3.0f; // 弹反反击伤害倍率
|
||||
|
||||
[Header("子弹时间(完美弹反)")]
|
||||
public float BulletTimeScale = 0.25f; // 触发时的时间缩放比例
|
||||
public float BulletTimeDuration = 0.2f; // 子弹时间持续时长(秒,实际时间)
|
||||
|
||||
[Header("硬直")]
|
||||
public float StaggerDuration = 0.8f; // 被弹反敌人的受击硬直时长(秒)
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Parry/ParryConfigSO.cs.meta
Normal file
11
Assets/_Game/Scripts/Parry/ParryConfigSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5bf3627df91a5f741b94c552a26c3ae2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
15
Assets/_Game/Scripts/Parry/ParryInfo.cs
Normal file
15
Assets/_Game/Scripts/Parry/ParryInfo.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace BaseGames.Parry
|
||||
{
|
||||
/// <summary>
|
||||
/// 弹反事件数据。
|
||||
/// 仅携带 Parry 程序集内可知的基础信息,避免与 Combat 程序集产生循环依赖。
|
||||
/// </summary>
|
||||
public struct ParryInfo
|
||||
{
|
||||
/// <summary>是否为完美弹反(在 PerfectParryThreshold 时间窗口内命中)。</summary>
|
||||
public bool IsPerfect;
|
||||
|
||||
/// <summary>本次弹反获得的灵力值(已由 ParrySystem 按配置计算好)。</summary>
|
||||
public int SoulGained;
|
||||
}
|
||||
}
|
||||
11
Assets/_Game/Scripts/Parry/ParryInfo.cs.meta
Normal file
11
Assets/_Game/Scripts/Parry/ParryInfo.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b787a1bb2c43b7947a9ce25117bc4363
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Assets/_Game/Scripts/Parry/ParryInfoEventChannelSO.cs
Normal file
13
Assets/_Game/Scripts/Parry/ParryInfoEventChannelSO.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using BaseGames.Core.Events;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Parry
|
||||
{
|
||||
/// <summary>
|
||||
/// 弹反成功事件频道(架构 06_CombatModule §9)。
|
||||
/// 可通过 Assets/Create/Events/ParryInfo 创建资产实例。
|
||||
/// 订阅方:PlayerController(灵力 + 护盾恢复)、FeedbackSystem(特效/音效)、UISystem(UI 提示)
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "BaseGames/Events/ParryInfo", fileName = "EVT_ParrySuccess")]
|
||||
public class ParryInfoEventChannelSO : BaseEventChannelSO<ParryInfo> { }
|
||||
}
|
||||
11
Assets/_Game/Scripts/Parry/ParryInfoEventChannelSO.cs.meta
Normal file
11
Assets/_Game/Scripts/Parry/ParryInfoEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 06e4216945d83a5439d3ef6a497a1413
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
214
Assets/_Game/Scripts/Parry/ParrySystem.cs
Normal file
214
Assets/_Game/Scripts/Parry/ParrySystem.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using BaseGames.Input;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Parry
|
||||
{
|
||||
/// <summary>
|
||||
/// 弹反阶段枚举(架构 06_CombatModule §9)。
|
||||
/// </summary>
|
||||
public enum ParryPhase
|
||||
{
|
||||
Inactive, // 待机
|
||||
Startup, // 前摇(按键 → 有效窗口开启)
|
||||
Active, // 弹反有效窗口
|
||||
EndLag, // 后摇(窗口关闭 → 动作恢复)
|
||||
CounterWindow, // 弹反成功后的反击窗口
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹反状态机(架构 06_CombatModule §9)。
|
||||
///
|
||||
/// 组件设计:
|
||||
/// - 订阅 InputReaderSO.ParryEvent → 触发 TryActivateParry
|
||||
/// - 触发 OnParryActivated C# 事件 → PlayerController 转换到 ParryState
|
||||
/// - HurtBox 调用 ConsumeParry() → 弹反成功时触发奖励并进入 CounterWindow
|
||||
/// - 触发 OnParryConsumed(ParryInfo) C# 事件 → PlayerController 发放灵力 / 恢复护盾
|
||||
/// - 可选触发 ParryInfoEventChannelSO SO 事件 → UI / 反馈系统
|
||||
///
|
||||
/// 程序集约束:BaseGames.Parry 不引用 BaseGames.Combat → ConsumeParry() 无 DamageInfo 参数。
|
||||
/// </summary>
|
||||
public class ParrySystem : MonoBehaviour
|
||||
{
|
||||
[Header("配置")]
|
||||
[SerializeField] private ParryConfigSO _config;
|
||||
// _inputReader 由 PlayerController.Awake 注入,无需在 Inspector 单独配置。
|
||||
private InputReaderSO _inputReader;
|
||||
|
||||
[Header("Event Channels - Raise(可选)")]
|
||||
[SerializeField] private ParryInfoEventChannelSO _onParrySuccess;
|
||||
|
||||
// ── 运行时状态 ────────────────────────────────────────────────────────
|
||||
private ParryPhase _phase = ParryPhase.Inactive;
|
||||
private float _phaseTimer;
|
||||
private float _cooldownTimer;
|
||||
|
||||
public ParryPhase CurrentPhase => _phase;
|
||||
/// <summary>是否处于弹反有效窗口(供外部检测)。</summary>
|
||||
public bool IsParrying => _phase == ParryPhase.Active;
|
||||
public bool IsInCounterWindow => _phase == ParryPhase.CounterWindow;
|
||||
|
||||
/// <summary>启用/禁用弹反输入(玩家能力解锁前设为 false)。</summary>
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
// ── C# 事件 ───────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 弹反成功时触发(ConsumeParry 返回 true 后)。
|
||||
/// PlayerController 订阅此事件以发放灵力并恢复护盾。
|
||||
/// </summary>
|
||||
public event Action<ParryInfo> OnParryConsumed;
|
||||
|
||||
/// <summary>
|
||||
/// 弹反输入激活时触发(进入 Startup 阶段时)。
|
||||
/// PlayerController 订阅此事件以转换到 ParryState。
|
||||
/// </summary>
|
||||
public event Action OnParryActivated;
|
||||
|
||||
// ── Unity 生命周期 ────────────────────────────────────────────────────
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Debug.Assert(_config != null, "[ParrySystem] _config 未赋值,请在 Inspector 中指定 ParryConfigSO。", this);
|
||||
}
|
||||
|
||||
/// <summary>由 PlayerController 在 Awake 中注入 InputReader,无需在 Inspector 单独指定。</summary>
|
||||
public void SetInputReader(InputReaderSO reader)
|
||||
{
|
||||
_inputReader = reader;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_inputReader != null)
|
||||
_inputReader.ParryEvent += TryActivateParry;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_inputReader != null)
|
||||
_inputReader.ParryEvent -= TryActivateParry;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 冷却计时(使用 unscaledDeltaTime,子弹时间期间也正常递减)
|
||||
if (_cooldownTimer > 0f)
|
||||
_cooldownTimer -= Time.unscaledDeltaTime;
|
||||
|
||||
if (_phase == ParryPhase.Inactive) return;
|
||||
|
||||
_phaseTimer -= Time.unscaledDeltaTime;
|
||||
if (_phaseTimer <= 0f) AdvancePhase();
|
||||
}
|
||||
|
||||
// ── 外部 API ──────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// HurtBox.ReceiveDamage 在步骤 2 调用此方法。
|
||||
/// 若处于 Active 阶段则触发弹反成功流程并返回 true;否则返回 false。
|
||||
/// </summary>
|
||||
public bool ConsumeParry()
|
||||
{
|
||||
if (_phase != ParryPhase.Active) return false;
|
||||
|
||||
bool isPerfect = IsInPerfectWindow();
|
||||
|
||||
int soulGain = isPerfect
|
||||
? _config.SoulGainOnParry + _config.SoulGainOnPerfect
|
||||
: _config.SoulGainOnParry;
|
||||
|
||||
// 完美弹反子弹时间
|
||||
if (isPerfect)
|
||||
StartCoroutine(ApplyBulletTime());
|
||||
|
||||
// 构造负载
|
||||
var info = new ParryInfo { IsPerfect = isPerfect, SoulGained = soulGain };
|
||||
|
||||
// SO 事件(UI / 特效 / 音效系统订阅)
|
||||
_onParrySuccess?.Raise(info);
|
||||
|
||||
// C# 事件(PlayerController 订阅以添加灵力 + 恢复护盾)
|
||||
OnParryConsumed?.Invoke(info);
|
||||
|
||||
// 进入反击窗口
|
||||
EnterPhase(ParryPhase.CounterWindow, _config.CounterWindowDuration);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 由状态机(ParryState.OnStateEnter)或动画事件直接触发,跳过 Startup 前摇直接进入 Active 窗口。
|
||||
/// 若弹反已处于任意活跃阶段则忽略。
|
||||
/// </summary>
|
||||
public void OpenParryWindow()
|
||||
{
|
||||
if (_phase != ParryPhase.Inactive) return;
|
||||
EnterPhase(ParryPhase.Active, _config.WindowDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制中断弹反(ParryState.OnStateExit 或被击中打断时调用)。
|
||||
/// </summary>
|
||||
public void CloseParryWindow()
|
||||
{
|
||||
if (_phase == ParryPhase.Inactive) return;
|
||||
_cooldownTimer = _config.ParryCooldown;
|
||||
EnterPhase(ParryPhase.Inactive, 0f);
|
||||
}
|
||||
|
||||
// ── 私有实现 ──────────────────────────────────────────────────────────
|
||||
|
||||
private void TryActivateParry()
|
||||
{
|
||||
if (!IsEnabled) return;
|
||||
if (_phase != ParryPhase.Inactive) return;
|
||||
if (_cooldownTimer > 0f) return;
|
||||
|
||||
EnterPhase(ParryPhase.Startup, _config.StartupDuration);
|
||||
OnParryActivated?.Invoke();
|
||||
}
|
||||
|
||||
private bool IsInPerfectWindow()
|
||||
{
|
||||
// Active 阶段计时器从 WindowDuration 倒计,elapsed = WindowDuration - _phaseTimer
|
||||
float elapsed = _config.WindowDuration - _phaseTimer;
|
||||
return elapsed <= _config.PerfectParryThreshold;
|
||||
}
|
||||
|
||||
private void AdvancePhase()
|
||||
{
|
||||
switch (_phase)
|
||||
{
|
||||
case ParryPhase.Startup:
|
||||
EnterPhase(ParryPhase.Active, _config.WindowDuration);
|
||||
break;
|
||||
|
||||
case ParryPhase.Active:
|
||||
EnterPhase(ParryPhase.EndLag, _config.EndlagDuration);
|
||||
break;
|
||||
|
||||
case ParryPhase.EndLag:
|
||||
case ParryPhase.CounterWindow:
|
||||
_cooldownTimer = _config.ParryCooldown;
|
||||
EnterPhase(ParryPhase.Inactive, 0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnterPhase(ParryPhase phase, float duration)
|
||||
{
|
||||
_phase = phase;
|
||||
_phaseTimer = duration;
|
||||
}
|
||||
|
||||
private IEnumerator ApplyBulletTime()
|
||||
{
|
||||
Time.timeScale = _config.BulletTimeScale;
|
||||
yield return new WaitForSecondsRealtime(_config.BulletTimeDuration);
|
||||
Time.timeScale = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
Assets/_Game/Scripts/Parry/ParrySystem.cs.meta
Normal file
11
Assets/_Game/Scripts/Parry/ParrySystem.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14872ec0b53eece49a121f13ca519009
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user