Files
zeling_v2/Assets/_Game/Scripts/Skills/SkillManager.cs

136 lines
6.2 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 UnityEngine;
using Animancer;
using System.Collections.Generic;
using BaseGames.Player;
using BaseGames.Input;
using BaseGames.Combat;
namespace BaseGames.Skills
{
/// <summary>
/// 技能管理器(架构 09_ProgressionModule §9
/// 挂在 Player 上,订阅输入事件并处理所有技能施放逻辑。
/// 通过 UpdateSkillSet() 由 FormController 注入当前形态技能。
/// 直接使用 AnimancerComponent 播放动画,无需 SoulSkillState FSM。
/// </summary>
public class SkillManager : MonoBehaviour
{
[Header("依赖引用")]
[SerializeField] private PlayerStats _stats;
[SerializeField] private AnimancerComponent _animancer;
[SerializeField] private InputReaderSO _input;
[SerializeField] private SkillModifierRegistry _modifiers;
[Header("技能挂载点")]
[SerializeField] private Transform _skillSocket; // [SkillSocket] 子节点
// 当前形态技能集(绑定到对应输入槽)
private FormSkillSO _soulSkill;
private FormSkillSO _spirit1;
private FormSkillSO _spirit2;
// 冷却字典FormSkillSO → 剩余冷却秒数UpdateSkillSet 时重建
private readonly Dictionary<FormSkillSO, float> _cooldowns = new(3);
// 无分配 Update 遍历用的快照数组
private FormSkillSO[] _activeSkills = System.Array.Empty<FormSkillSO>();
// ── 生命周期 ──────────────────────────────────────────────────────────
private void OnEnable()
{
if (_input == null) return;
_input.SoulSkillEvent += TrySoulSkill;
_input.SpiritSkill1StartedEvent += TrySpiritSkill1;
_input.SpiritSkill2StartedEvent += TrySpiritSkill2;
}
private void OnDisable()
{
if (_input == null) return;
_input.SoulSkillEvent -= TrySoulSkill;
_input.SpiritSkill1StartedEvent -= TrySpiritSkill1;
_input.SpiritSkill2StartedEvent -= TrySpiritSkill2;
}
private void Update()
{
for (int i = 0; i < _activeSkills.Length; i++)
{
var s = _activeSkills[i];
if (_cooldowns.TryGetValue(s, out float cd) && cd > 0f)
_cooldowns[s] = cd - Time.deltaTime;
}
}
// ── 公共 API ─────────────────────────────────────────────────────────
/// <summary>切换形态时由 FormController 调用,注入当前形态的三个技能。</summary>
public void UpdateSkillSet(FormSkillSO soul, FormSkillSO spirit1, FormSkillSO spirit2)
{
_soulSkill = soul;
_spirit1 = spirit1;
_spirit2 = spirit2;
_cooldowns.Clear();
// 构建无分配遍历快照(固定大小数组,避免 List + ToArray GC
int count = (soul != null ? 1 : 0) + (spirit1 != null ? 1 : 0) + (spirit2 != null ? 1 : 0);
if (_activeSkills.Length != count)
_activeSkills = count > 0 ? new FormSkillSO[count] : System.Array.Empty<FormSkillSO>();
int idx = 0;
if (soul != null) { _cooldowns[soul] = 0f; _activeSkills[idx++] = soul; }
if (spirit1 != null) { _cooldowns[spirit1] = 0f; _activeSkills[idx++] = spirit1; }
if (spirit2 != null) { _cooldowns[spirit2] = 0f; _activeSkills[idx] = spirit2; }
}
// ── 内部施放逻辑 ─────────────────────────────────────────────────────
private void TrySoulSkill() => TryCastSkill(_soulSkill);
private void TrySpiritSkill1() => TryCastSkill(_spirit1);
private void TrySpiritSkill2() => TryCastSkill(_spirit2);
private void TryCastSkill(FormSkillSO skill)
{
if (skill == null) return;
var p = _modifiers != null
? _modifiers.GetEffectiveParams(skill)
: EffectiveSkillParams.FromBase(skill);
if (!_cooldowns.TryGetValue(skill, out float cooldown) || cooldown > 0f) return;
// 消耗资源
bool consumed = skill.resourceType == SkillResourceType.SoulPower
? _stats.ConsumeSoulPower(p.effectiveCost)
: _stats.ConsumeSpiritPower(p.effectiveCost);
if (!consumed) return;
_cooldowns[skill] = p.effectiveCooldown;
// 播放动画(优先修改器动画,回退技能默认动画)
var clip = p.effectiveAnimation.Clip != null
? p.effectiveAnimation
: skill.castAnimation;
if (clip.Clip != null && _animancer != null)
_animancer.Play(clip);
// 生成 HitBox Prefab近战/爆炸类技能)
if (skill.SkillHitBoxPrefab != null)
{
var socket = _skillSocket != null ? _skillSocket : transform;
var go = Object.Instantiate(skill.SkillHitBoxPrefab, socket.position,
socket.rotation, socket);
var inst = go.GetComponent<SkillHitBoxInstance>();
inst?.Activate(skill.damageSource, transform);
inst?.AutoDestroyAfter(skill.castLockDuration > 0f ? skill.castLockDuration : 0.5f);
}
}
// ── 属性查询 ─────────────────────────────────────────────────────────
public FormSkillSO SoulSkill => _soulSkill;
public FormSkillSO Spirit1 => _spirit1;
public FormSkillSO Spirit2 => _spirit2;
public float SoulCooldownRatio =>
(_soulSkill != null && _soulSkill.cooldown > 0 &&
_cooldowns.TryGetValue(_soulSkill, out float cd))
? Mathf.Clamp01(cd / _soulSkill.cooldown) : 0f;
}
}