Files
zeling_v2/Assets/_Game/Scripts/Player/FormController.cs
Joywayer 47bdc67cdf feat: Implement DownDash ability and related systems
- 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.
2026-05-22 00:09:50 +08:00

166 lines
6.6 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;
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);
}
}