- Added DownDash ability with cooldown and speed configuration. - Introduced DownDashState to handle down dashing mechanics, including gravity manipulation and animation playback. - Updated PlayerMovement to support DownDash functionality. - Enhanced PlayerStats to manage spring charge consumption and healing. - Modified PlayerCombat and WeaponHitBoxInstance to support new hit confirmation events. - Updated AbilityType to include new form types for character abilities. - Improved Gizmos for better visualization of enemy detection and attack ranges. - Added feedback systems for form switching in PlayerFeedback and IFeedbackPlayer. - Refactored combat and movement states to accommodate new abilities and ensure smooth transitions.
166 lines
6.6 KiB
C#
166 lines
6.6 KiB
C#
using System;
|
||
using UnityEngine;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Save;
|
||
using BaseGames.Core.Events;
|
||
using BaseGames.Feedback;
|
||
using BaseGames.Input;
|
||
|
||
namespace BaseGames.Player
|
||
{
|
||
/// <summary>
|
||
/// 形态控制器。
|
||
/// 管理天魂/地魂/命魂三形态切换,依次触发:
|
||
/// 1. _onFormChanged SO 事件(UI/Save 用)
|
||
/// 2. OnFormChanged C# 事件(WeaponManager 订阅)
|
||
/// 3. _onSkillSetChanged SO 事件(SkillHUD 刷新)
|
||
/// 架构 05_PlayerModule §6。
|
||
/// </summary>
|
||
public class FormController : MonoBehaviour, ISaveable
|
||
{
|
||
[Header("配置")]
|
||
[SerializeField] private FormConfigSO _config;
|
||
[SerializeField] private InputReaderSO _input;
|
||
|
||
[Header("依赖")]
|
||
[SerializeField] private PlayerStats _stats;
|
||
|
||
[Header("事件频道")]
|
||
[SerializeField] private IntEventChannelSO _onFormChanged; // 广播当前形态索引(UI/Save)
|
||
[SerializeField] private VoidEventChannelSO _onSkillSetChanged; // 通知 SkillHUD 刷新
|
||
|
||
// ── 运行时 ─────────────────────────────────────────────────────────────
|
||
public FormSO CurrentForm { get; private set; }
|
||
public FormSO[] AllForms => _config.forms;
|
||
|
||
/// <summary>当前切换 CD 剩余时间(秒),0 表示可切换。</summary>
|
||
public float SwitchCooldownRemaining => _switchCooldownTimer;
|
||
|
||
/// <summary>C# 事件,WeaponManager 在 OnEnable 自订阅(架构 05 §6)。</summary>
|
||
public event Action OnFormChanged;
|
||
|
||
private float _switchCooldownTimer;
|
||
private IFeedbackPlayer _feedback;
|
||
|
||
private void Awake()
|
||
{
|
||
Debug.Assert(_config != null, "[FormController] _config 未赋值,请在 Inspector 中指定 FormConfigSO。", this);
|
||
if (_stats == null)
|
||
_stats = GetComponent<PlayerStats>();
|
||
_feedback = GetComponentInChildren<IFeedbackPlayer>() ?? NullFeedbackPlayer.Instance;
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||
if (_input == null) return;
|
||
_input.SwitchSkyFormEvent += OnSwitchSky;
|
||
_input.SwitchEarthFormEvent += OnSwitchEarth;
|
||
_input.SwitchDeathFormEvent += OnSwitchDeath;
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||
if (_input == null) return;
|
||
_input.SwitchSkyFormEvent -= OnSwitchSky;
|
||
_input.SwitchEarthFormEvent -= OnSwitchEarth;
|
||
_input.SwitchDeathFormEvent -= OnSwitchDeath;
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
if (_config.forms != null && _config.forms.Length > 0)
|
||
CurrentForm = _config.forms[0];
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (_switchCooldownTimer > 0f)
|
||
_switchCooldownTimer -= Time.deltaTime;
|
||
}
|
||
|
||
// ── 公共 API ────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>切换到指定形态类型。若已在目标形态、尚未解锁或 CD 未结束则不操作。</summary>
|
||
public void SwitchForm(FormType newFormType)
|
||
{
|
||
// CD 检查
|
||
if (_switchCooldownTimer > 0f) return;
|
||
|
||
// 检查对应形态的解锁标志
|
||
AbilityType required = newFormType switch
|
||
{
|
||
FormType.TianHun => AbilityType.FormTianHun,
|
||
FormType.DiHun => AbilityType.FormDiHun,
|
||
FormType.MingHun => AbilityType.FormMingHun,
|
||
_ => AbilityType.None,
|
||
};
|
||
if (required != AbilityType.None && _stats != null && !_stats.HasAbility(required))
|
||
return;
|
||
|
||
FormSO newForm = _config.GetFormByType(newFormType);
|
||
if (newForm == null || newForm == CurrentForm) return;
|
||
|
||
CurrentForm = newForm;
|
||
|
||
// 启动切换 CD
|
||
if (_config.SwitchCooldown > 0f)
|
||
_switchCooldownTimer = _config.SwitchCooldown;
|
||
|
||
// 播放对应形态切换反馈
|
||
_feedback.PlayFormSwitch((int)newFormType);
|
||
|
||
// 1. SO 事件广播索引(UI/Save)
|
||
_onFormChanged?.Raise(_config.GetFormIndex(newForm));
|
||
|
||
// 2. C# 事件(WeaponManager 等订阅者)
|
||
OnFormChanged?.Invoke();
|
||
|
||
// 3. SkillHUD 刷新事件
|
||
_onSkillSetChanged?.Raise();
|
||
}
|
||
|
||
/// <summary>通过数组索引切换形态。</summary>
|
||
public void SwitchToFormByIndex(int index)
|
||
{
|
||
if (_config?.forms == null || index < 0 || index >= _config.forms.Length) return;
|
||
var form = _config.forms[index];
|
||
if (form != null)
|
||
SwitchForm(form.formType);
|
||
}
|
||
|
||
// ── ISaveable ────────────────────────────────────────────────────────────
|
||
public void OnSave(SaveData data)
|
||
{
|
||
data.Player.ActiveFormId = CurrentForm?.formId;
|
||
}
|
||
|
||
public void OnLoad(SaveData data)
|
||
{
|
||
if (string.IsNullOrEmpty(data.Player.ActiveFormId) || _config?.forms == null)
|
||
{
|
||
if (_config?.forms != null && _config.forms.Length > 0)
|
||
CurrentForm = _config.forms[0];
|
||
return;
|
||
}
|
||
|
||
for (int i = 0; i < _config.forms.Length; i++)
|
||
{
|
||
if (_config.forms[i]?.formId == data.Player.ActiveFormId)
|
||
{
|
||
// 直接赋值,不触发事件——加载时场景尚未完全就绪,订阅者(WeaponManager 等)
|
||
// 会在各自 OnEnable + Register → OnLoad 回调中自行恢复状态。
|
||
CurrentForm = _config.forms[i];
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── 内部输入处理 ────────────────────────────────────────────────────────
|
||
private void OnSwitchSky() => SwitchForm(FormType.TianHun);
|
||
private void OnSwitchEarth() => SwitchForm(FormType.DiHun);
|
||
private void OnSwitchDeath() => SwitchForm(FormType.MingHun);
|
||
}
|
||
}
|