29 KiB
21 · 形态技能系统(Form Skill System)
命名空间
BaseGames.Skills
所属文档集 ← 返回索引 · 总览
依赖BaseGames.Combat·BaseGames.Player·BaseGames.Equipment·BaseGames.Core.Events·BaseGames.Animation
目录
- 系统总览
- FormSkillSO — 技能数据
- SkillManager — 技能执行器
- 九种形态技能实现
- 技能修改系统(装备系统联动)
- 技能初始化与 FormSO 绑定
- SaveData 扩展
- UI 集成
- 事件频道
- 编辑器工具
1. 系统总览
技能系统管理形态专属主动技能。每种形态(天魂/地魂/命魂)各拥有 3 个技能槽,消耗独立资源触发:
| 技能类型 | Input Action | 消耗资源 | 说明 |
|---|---|---|---|
| 魂技能(SoulSkill) | SoulSkill |
灵力(SoulPower) | 每种形态各 1 个,高爆发或特殊形态能力 |
| 魄技能 1(SpiritSkill1) | SpiritSkill1 |
魄元(SpiritPower) | 每种形态各 1 个,自动恢复资源驱动 |
| 魄技能 2(SpiritSkill2) | SpiritSkill2 |
魄元(SpiritPower) | 每种形态各 1 个,自动恢复资源驱动 |
技能系统职责:
├─ FormSkillSO → 技能数据(消耗/动画/效果/VFX/SFX)
├─ SkillManager → 玩家上的技能执行器(校验资源、执行效果)
├─ FormSO → 持有 3 个技能 SO 引用(见 03_PlayerSystem §6)
├─ SkillModifierSnapshot → 装备魅力修改器快照
└─ SkillHUD → 技能图标 + 资源条(灵力/魄元)
与资源系统的关系:
- 魂技能通过
PlayerStats.ConsumeSoulPower(cost)扣除灵力,不自动恢复 - 魄技能通过
PlayerStats.ConsumeSpiritPower(cost)扣除魄元,魄元随时间自动恢复
2. FormSkillSO — 技能数据
[CreateAssetMenu(menuName = "Skills/FormSkill")]
public class FormSkillSO : ScriptableObject
{
[Header("基础信息")]
public string skillId; // 唯一 ID(如 "sky_soul_skill")
public string displayName; // 如 "裂空掌"
[TextArea(1, 3)]
public string description;
public Sprite icon;
[Header("资源消耗")]
public SkillResourceType resourceType; // SoulPower / SpiritPower
public int baseCost; // 基础消耗量(装备魅力可降低)
public float cooldown; // 技能独立冷却(秒,0 = 无冷却)
[Header("动画")]
public ClipTransition castAnimation; // 施法动画(Layer[1] 上半身)
public float castLockDuration; // 前摇期间输入锁定时长(秒)
[Header("效果类型")]
public SkillEffectType effectType;
[Header("伤害配置(MeleeAoE / Projectile)")]
public DamageSourceSO damageSource;
public float knockbackForce; // 击退力(effectType = MeleeAoE 时)
[Header("弹射物配置(Projectile)")]
public ProjectileConfigSO projectilePrefab;
public bool isHoming; // 是否追踪(灵踪弹)
public bool holdForContinuous; // 长按连射(灵踪弹)
[Header("位移配置(GroundDive / WraithDash / DragonKick)")]
public float dashForce;
public float dashDuration;
public bool isInvincibleDuringDash;
public bool canPassMagicWalls; // 太虚斩穿越魔法障壁
[Header("延迟爆炸配置(DelayedExplosion)")]
public float explosionDelay;
public float explosionRadius;
public float explosionExpandDuration;
[Header("VFX/SFX")]
public FeedbackPresetSO castFeedback;
[Header("霸体配置")]
[Tooltip("技能施放期间的霸体窗口(None = 可被任何攻击打断),见 54_PoiseSystem §7.2")]
public PoiseWindowConfig poiseWindow; // 见 54_PoiseSystem §7.2
}
public enum SkillResourceType
{
SoulPower, // 灵力(近战积累,不自动恢复)
SpiritPower, // 魄元(时间自动恢复)
}
public enum SkillEffectType
{
MeleeAoE, // 近身范围伤害 + 击退(裂空掌、霸山拳)
Projectile, // 发射弹射物(灵踪弹)
BarrierAura, // 护体漩涡 + 阻弹(漩灵击)
GroundDive, // 遁入地面无敌移动(地行术)
DragonKick, // 垂直升龙踢 + 无敌(登龙蹴)
WraithDash, // 灵体冲刺 + 二次瞬移(太虚斩)
ShadowDecoy, // 留灵体残影模仿攻击(残阴术)
DelayedExplosion, // 留魂元球延迟爆炸(魂元爆)
}
3. SkillManager — 技能执行器
SkillManager 挂载于 PlayerController 同一 GameObject,从 FormController.CurrentForm 读取当前形态的技能 SO:
namespace BaseGames.Skills
{
public class SkillManager : MonoBehaviour
{
[Header("依赖(同 Prefab 内)")]
[SerializeField] PlayerStats _stats;
[SerializeField] FormController _formController;
[SerializeField] AnimancerComponent _animancer;
[SerializeField] InputReaderSO _input;
[SerializeField] SkillModifierRegistry _modRegistry; // 见 §5
[Header("事件频道")]
[SerializeField] VoidEventChannelSO _onSkillCast;
[SerializeField] VoidEventChannelSO _onSkillFailed;
// 各技能独立冷却计时器(key = skillId)
readonly Dictionary<string, float> _cooldowns = new();
bool _isCasting;
void OnEnable()
{
_input.SoulSkillEvent += CastSoulSkill;
_input.SpiritSkill1StartedEvent += CastSpiritSkill1;
_input.SpiritSkill2StartedEvent += CastSpiritSkill2;
}
void OnDisable()
{
_input.SoulSkillEvent -= CastSoulSkill;
_input.SpiritSkill1StartedEvent -= CastSpiritSkill1;
_input.SpiritSkill2StartedEvent -= CastSpiritSkill2;
}
public void CastSoulSkill() => TryCast(_formController.CurrentForm.soulSkill, SkillSlotType.SoulSkill);
public void CastSpiritSkill1() => TryCast(_formController.CurrentForm.spiritSkill1, SkillSlotType.SpiritSkill1);
public void CastSpiritSkill2() => TryCast(_formController.CurrentForm.spiritSkill2, SkillSlotType.SpiritSkill2);
void TryCast(FormSkillSO baseSkill, SkillSlotType slot)
{
if (baseSkill == null || _isCasting) return;
// 1. 查询插槽替换(护符可能完全替换此槽位的技能)
FormSkillSO skill = _modRegistry.GetEffectiveSkill(
_formController.CurrentForm, slot, baseSkill);
// 2. 计算数值修改器(伤害/范围/消耗/冷却/特效)
EffectiveSkillParams p = _modRegistry.GetEffectiveParams(skill);
if (IsOnCooldown(skill)) return;
bool consumed = skill.resourceType switch
{
SkillResourceType.SoulPower => _stats.ConsumeSoulPower(p.effectiveCost),
SkillResourceType.SpiritPower => _stats.ConsumeSpiritPower(p.effectiveCost),
_ => false,
};
if (!consumed) { _onSkillFailed.Raise(); return; }
StartCoroutine(CastRoutine(skill, p));
}
IEnumerator CastRoutine(FormSkillSO skill, EffectiveSkillParams p)
{
_isCasting = true;
SetCooldown(skill, p.effectiveCooldown);
// 前摇动画(Layer[1] 上半身叠加层);护符可替换为其他施法动画
_animancer.Layers[1].Play(p.effectiveAnimation ?? skill.castAnimation);
yield return new WaitForSeconds(skill.castLockDuration);
ExecuteEffect(skill, p);
// 特效预设:护符可替换为不同 VFX;null 则回退到原始预设
(p.effectiveFeedback ?? skill.castFeedback)?.PlayFeedbacks(gameObject);
_onSkillCast.Raise();
_isCasting = false;
}
// p.damageMult 和 p.rangeMult 传入各效果执行函数,用于缩放伤害和范围
void ExecuteEffect(FormSkillSO skill, EffectiveSkillParams p)
{
switch (skill.effectType)
{
case SkillEffectType.MeleeAoE: ExecuteMeleeAoE(skill, p); break;
case SkillEffectType.Projectile: FireProjectile(skill, p); break;
case SkillEffectType.BarrierAura: ExecuteBarrierAura(skill, p); break;
case SkillEffectType.GroundDive: ExecuteGroundDive(skill, p); break;
case SkillEffectType.DragonKick: ExecuteDragonKick(skill, p); break;
case SkillEffectType.WraithDash: ExecuteWraithDash(skill, p); break;
case SkillEffectType.ShadowDecoy: ExecuteShadowDecoy(skill, p); break;
case SkillEffectType.DelayedExplosion: ExecuteDelayedExplosion(skill, p); break;
}
}
bool IsOnCooldown(FormSkillSO skill)
=> _cooldowns.TryGetValue(skill.skillId, out float t) && t > 0f;
// overrideDuration < 0 = 使用 SO 内置冷却;>= 0 = 使用修改后冷却(EffectiveSkillParams 传入)
void SetCooldown(FormSkillSO skill, float overrideDuration = -1f)
{
float cd = overrideDuration >= 0f ? overrideDuration : skill.cooldown;
if (cd > 0f) _cooldowns[skill.skillId] = cd;
}
void Update()
{
foreach (var key in _cooldowns.Keys.ToList())
_cooldowns[key] = Mathf.Max(0f, _cooldowns[key] - Time.deltaTime);
}
}
}
SkillSlotType — 技能槽枚举
public enum SkillSlotType
{
SoulSkill, // 魂技能(灵力)
SpiritSkill1, // 魄技能 1
SpiritSkill2, // 魄技能 2
}
EffectiveSkillParams — 运行时最终参数
所有数值修改器叠加后生成的快照,由 SkillModifierRegistry.GetEffectiveParams 计算,传入 CastRoutine 和 ExecuteEffect:
public struct EffectiveSkillParams
{
public FormSkillSO baseSkill; // 原始 SO 引用(不变,供判断 effectType)
public int effectiveCost; // 修改后消耗量
public float effectiveCooldown; // 修改后冷却(秒)
public float damageMult; // 伤害倍率(1.0 = 无增益)
public float rangeMult; // 范围倍率(AoE 半径 / 爆炸半径 / 障壁半径等)
public FeedbackPresetSO effectiveFeedback; // 最终特效预设(可被护符替换,null = 回退原始)
public ClipTransition effectiveAnimation; // 最终施法动画(可被护符替换,null = 回退原始)
public static EffectiveSkillParams FromBase(FormSkillSO skill) => new()
{
baseSkill = skill,
effectiveCost = skill.baseCost,
effectiveCooldown = skill.cooldown,
damageMult = 1f,
rangeMult = 1f,
effectiveFeedback = null, // null = 使用 skill.castFeedback
effectiveAnimation = null, // null = 使用 skill.castAnimation
};
}
4. 九种形态技能实现
天魂形态(Sky Form)
4.1 裂空掌(SoulSkill,消耗灵力)
skillId: "sky_soul_skill"
displayName: "裂空掌"
resourceType: SoulPower
baseCost: 40
cooldown: 0s(受灵力储量天然限频)
effectType: MeleeAoE
castLockDuration: 0.2s
特效: 角色前方中距离气波(宽约 1.5u,高约 2u)
击退: Medium(水平方向)
说明: 地面空中均可,气波沿面向方向水平发射
4.2 漩灵击(SpiritSkill1,消耗魄元)
skillId: "sky_spirit_skill1"
displayName: "漩灵击"
resourceType: SpiritPower
baseCost: 30
cooldown: 6s
effectType: BarrierAura
castLockDuration: 0.15s
持续时间: 2.5s
特效: 角色周身旋转灵气漩涡,可阻挡弹道并反弹
空中效果: 持续期间重力减半(缓落)
说明: 护体漩涡激活期间可被攻击中断(扣血不中断护体)
4.3 灵踪弹(SpiritSkill2,消耗魄元)
skillId: "sky_spirit_skill2"
displayName: "灵踪弹"
resourceType: SpiritPower
baseCost: 20(按下一次消耗,长按连射每颗独立消耗)
cooldown: 0.5s(每颗之间)
effectType: Projectile
isHoming: true(灵球锁定最近敌人,有转弯半径上限)
holdForContinuous: true(长按持续发射)
每次发射数量: 2
castLockDuration: 0.1s
地魂形态(Earth Form)
4.4 地行术(SoulSkill,消耗灵力)
skillId: "earth_soul_skill"
displayName: "地行术"
resourceType: SoulPower
baseCost: 50
cooldown: 0s
effectType: GroundDive
castLockDuration: 0.3s(进入地面前摇)
移动速度: 4 units/s(地下)
无敌: 全程无敌
说明: 仅限地面发动;遁入地面后可用 Move 水平移动;
松开 SoulSkill 或碰触障碍物时自动浮出
4.5 霸山拳(SpiritSkill1,消耗魄元)
skillId: "earth_spirit_skill1"
displayName: "霸山拳"
resourceType: SpiritPower
baseCost: 35
cooldown: 8s
effectType: MeleeAoE
castLockDuration: 0.25s
范围: 以角色为中心 2u 半径圆形冲击
附加效果: 震起 1 块悬浮灵石(FloatingStonePrefab),灵石可挡弹道;
被裂空掌击中后飞出造成追加伤害
说明: 仅限地面
4.6 登龙蹴(SpiritSkill2,消耗魄元)
skillId: "earth_spirit_skill2"
displayName: "登龙蹴"
resourceType: SpiritPower
baseCost: 40
cooldown: 5s
effectType: DragonKick
castLockDuration: 0.1s
dashForce: 22(垂直向上)
dashDuration: 0.55s
isInvincibleDuringDash: true
说明: 类升龙踢,上升期间无敌;位移约等于 3 段跳高度,可突破障碍物上方平台
命魂形态(Death Form)
4.7 太虚斩(SoulSkill,消耗灵力)
skillId: "death_soul_skill"
displayName: "太虚斩"
resourceType: SoulPower
baseCost: 45
cooldown: 0s
effectType: WraithDash
castLockDuration: 0.05s
dashForce: 20
dashDuration: 0.35s
isInvincibleDuringDash: true
canPassMagicWalls: true(穿越带 MagicWall 标签的特定障壁)
二次触发: 冲刺期间再按 SoulSkill → 瞬移至灵体当前终点位置
说明: 冲刺路径上持续有斩击判定盒;可穿越带 MagicWall 标签的特定障碍物
4.8 残阴术(SpiritSkill1,消耗魄元)
skillId: "death_spirit_skill1"
displayName: "残阴术"
resourceType: SpiritPower
baseCost: 35
cooldown: 10s
effectType: ShadowDecoy
castLockDuration: 0.2s(后撤位移)
后撤距离: 3u(反方向 Dash,无敌)
灵体持续: 4s 或受到 3 次攻击后消失
灵体行为: 模仿角色最近一次攻击/技能动作(伤害减半)
机关触发: 灵体可触碰延迟机关(压板/感应器等)
说明: 灵体留于原地,角色后撤;灵体消失前可再按 SpiritSkill1 提前销毁
4.9 魂元爆(SpiritSkill2,消耗魄元)
skillId: "death_spirit_skill2"
displayName: "魂元爆"
resourceType: SpiritPower
baseCost: 50
cooldown: 8s
effectType: DelayedExplosion
castLockDuration: 0.15s
explosionDelay: 1.2s(布置后延迟)
explosionRadius: 4u(最终半径)
explosionExpandDuration: 0.6s(冲击波从 0 扩散到 explosionRadius 的时长)
伤害: 扩散范围内所有敌人,范围越大伤害越低(衰减曲线)
说明: 布置时在角色脚下生成魂元球;爆炸冲击波持续扩散,可引爆其他魂元爆
5. 技能修改系统(装备系统联动)
5.1 两种修改路径
护符通过 ICharmEffect.OnEquip/OnUnequip 向 SkillModifierRegistry 注册修改器,SkillManager.TryCast 在每次施放前实时查询:
| 修改路径 | 效果 | 数据结构 |
|---|---|---|
| 数值修改 | 改变技能的伤害/范围/消耗/冷却/VFX/动画 | SkillNumericModifier |
| 插槽替换 | 将某形态某槽位的技能 SO 整体替换为另一个 | SkillSlotOverride |
5.2 SkillNumericModifier — 数值修改器
[Serializable]
public struct SkillNumericModifier
{
[Header("目标过滤(留空/Any = 对所有技能生效)")]
[Tooltip("指定 skillId 则仅修改该技能;留空 = 匹配所有")]
public string targetSkillId; // 如 "sky_soul_skill";留空 = 全部
public SkillResourceFilter resourceFilter; // Any / SoulOnly / SpiritOnly
[Header("数值倍率(1.0 = 不变)")]
public float damageMult; // 伤害倍率
public float rangeMult; // 范围倍率(AoE 半径 / 爆炸半径 / 障壁半径)
public float costMult; // 消耗倍率(0.8 = 降低 20% 消耗)
public float cooldownMult; // 冷却倍率(0.8 = 降低 20% 冷却)
[Header("资产替换(null = 保留原始)")]
public FeedbackPresetSO vfxOverride; // 替换特效预设
public ClipTransition animOverride; // 替换施法动画
/// <summary>此修改器是否适用于给定技能</summary>
public bool Matches(FormSkillSO skill)
{
if (!string.IsNullOrEmpty(targetSkillId) && skill.skillId != targetSkillId)
return false;
if (resourceFilter == SkillResourceFilter.SoulOnly
&& skill.resourceType != SkillResourceType.SoulPower) return false;
if (resourceFilter == SkillResourceFilter.SpiritOnly
&& skill.resourceType != SkillResourceType.SpiritPower) return false;
return true;
}
public static SkillNumericModifier Identity => new()
{
damageMult = 1f, rangeMult = 1f, costMult = 1f, cooldownMult = 1f,
};
}
public enum SkillResourceFilter { Any, SoulOnly, SpiritOnly }
5.3 SkillSlotOverride — 插槽替换
[Serializable]
public struct SkillSlotOverride
{
[Header("目标槽位")]
[Tooltip("null = 对所有形态的该槽位生效")]
public FormSO targetForm; // 指定形态;null = 所有形态
public SkillSlotType targetSlot; // Soul / Spirit1 / Spirit2
[Header("替换技能")]
public FormSkillSO replacementSkill; // 替换后使用的技能 SO
[Header("优先级")]
[Tooltip("多个护符同时替换同一槽位时,priority 最高者胜出;相同则后装备的优先")]
public int priority; // 默认 0;特殊护符可设置更高值
}
5.4 SkillModifierRegistry — 聚合器(组件)
挂载于 Player GameObject,EquipmentManager.Awake 通过 GetComponent 获取并注入 EquipmentContext:
namespace BaseGames.Skills
{
public class SkillModifierRegistry : MonoBehaviour
{
readonly List<SkillNumericModifier> _numericMods = new();
readonly List<SkillSlotOverride> _slotOverrides = new();
// ── 注册 / 注销 ──
public void AddNumericMod(SkillNumericModifier mod) => _numericMods.Add(mod);
public void RemoveNumericMod(SkillNumericModifier mod) => _numericMods.Remove(mod);
public void AddSlotOverride(SkillSlotOverride ov) => _slotOverrides.Add(ov);
public void RemoveSlotOverride(SkillSlotOverride ov) => _slotOverrides.Remove(ov);
/// <summary>
/// 查询某形态某槽位的有效技能 SO。
/// 优先级规则:priority 高者胜;相同 priority 取列表最后注册;
/// 精确匹配 targetForm 比 targetForm=null(通配)额外 +1 隐式优先级。
/// </summary>
public FormSkillSO GetEffectiveSkill(FormSO form, SkillSlotType slot, FormSkillSO defaultSkill)
{
FormSkillSO result = defaultSkill;
int bestScore = int.MinValue; // score = priority*2 + (精确匹配?1:0)
foreach (var ov in _slotOverrides)
{
if (ov.targetSlot != slot) continue;
if (ov.targetForm != null && ov.targetForm != form) continue;
if (ov.replacementSkill == null) continue;
// 精确指定 targetForm 比 null 通配多 +1 分
int score = ov.priority * 2 + (ov.targetForm != null ? 1 : 0);
if (score >= bestScore)
{
bestScore = score;
result = ov.replacementSkill;
}
}
return result;
}
/// <summary>
/// 计算技能 SO 经所有数值修改器叠加后的最终参数。
/// 倍率字段乘法叠加;资产替换字段取最后一个非 null 覆盖。
/// </summary>
public EffectiveSkillParams GetEffectiveParams(FormSkillSO skill)
{
var p = EffectiveSkillParams.FromBase(skill);
foreach (var mod in _numericMods)
{
if (!mod.Matches(skill)) continue;
p.damageMult *= mod.damageMult;
p.rangeMult *= mod.rangeMult;
p.effectiveCost = Mathf.Max(1,
Mathf.RoundToInt(p.effectiveCost * mod.costMult));
p.effectiveCooldown *= mod.cooldownMult;
if (mod.vfxOverride != null) p.effectiveFeedback = mod.vfxOverride;
if (mod.animOverride != null) p.effectiveAnimation = mod.animOverride;
}
return p;
}
}
}
5.5 ICharmEffect 实现
以下两个效果类在 17_EquipmentSystem.md §4 中声明,此处给出完整实现参考:
// ── 数值修改护符 ──
[Serializable]
public class SkillNumericModifierEffect : ICharmEffect
{
[Tooltip("数值修改配置(目标技能 + 各倍率 + 可选资产替换)")]
public SkillNumericModifier modifier;
public void OnEquip(EquipmentContext ctx) => ctx.SkillMods.AddNumericMod(modifier);
public void OnUnequip(EquipmentContext ctx) => ctx.SkillMods.RemoveNumericMod(modifier);
public string GetEffectDescription()
{
var parts = new List<string>();
if (!Mathf.Approximately(modifier.damageMult, 1f))
parts.Add($"伤害 ×{modifier.damageMult:0.##}");
if (!Mathf.Approximately(modifier.rangeMult, 1f))
parts.Add($"范围 ×{modifier.rangeMult:0.##}");
if (!Mathf.Approximately(modifier.costMult, 1f))
parts.Add($"消耗 ×{modifier.costMult:0.##}");
if (!Mathf.Approximately(modifier.cooldownMult, 1f))
parts.Add($"冷却 ×{modifier.cooldownMult:0.##}");
if (modifier.vfxOverride != null) parts.Add("特效替换");
if (modifier.animOverride != null) parts.Add("动画替换");
string target = string.IsNullOrEmpty(modifier.targetSkillId)
? $"所有{modifier.resourceFilter}技能"
: modifier.targetSkillId;
return $"{target}:{string.Join(" / ", parts)}";
}
}
// ── 插槽替换护符 ──
[Serializable]
public class SkillSlotOverrideEffect : ICharmEffect
{
[Tooltip("插槽替换配置(目标形态 + 槽位 + 替换技能 + 优先级)")]
public SkillSlotOverride overrideData;
public void OnEquip(EquipmentContext ctx) => ctx.SkillMods.AddSlotOverride(overrideData);
public void OnUnequip(EquipmentContext ctx) => ctx.SkillMods.RemoveSlotOverride(overrideData);
public string GetEffectDescription()
{
string formStr = overrideData.targetForm != null
? overrideData.targetForm.name : "所有形态";
string skillName = overrideData.replacementSkill != null
? overrideData.replacementSkill.displayName : "null";
return $"{formStr} {overrideData.targetSlot} → [{skillName}]";
}
}
5.6 rangeMult 应用规则
各 SkillEffectType 的 rangeMult 含义:
| effectType | rangeMult 作用对象 |
|---|---|
MeleeAoE |
AoE 碰撞体宽度 / 高度 |
BarrierAura |
护体漩涡半径 |
DelayedExplosion |
explosionRadius |
Projectile |
弹射物存活距离上限 |
GroundDive |
地下移动速度(间接扩大覆盖范围) |
ShadowDecoy |
灵体感应触发半径 |
DragonKick / WraithDash |
位移距离(dashForce) |
执行函数中应用方式示例(MeleeAoE):
void ExecuteMeleeAoE(FormSkillSO skill, EffectiveSkillParams p)
{
// 基础 AoE box size 由 skill 的 collider 配置定义,rangeMult 等比缩放
Vector2 baseSize = skill.aoeBoxSize; // (width, height)
Vector2 finalSize = baseSize * p.rangeMult;
// DamageSourceSO 伤害乘以 damageMult
var dmgInfo = skill.damageSource.BuildDamageInfo(p.damageMult);
// 物理查询 + 施加伤害
var hits = Physics2D.OverlapBoxAll(aoeCenter, finalSize, 0f, enemyLayer);
foreach (var h in hits)
if (h.TryGetComponent<HurtBox>(out var hb)) hb.ReceiveDamage(dmgInfo);
}
6. 技能初始化与 FormSO 绑定
技能 SO 通过 FormSO 持有,在 Inspector 中配置(见 03_PlayerSystem §6)。SkillManager 不持有技能 SO 引用,始终从 FormController.CurrentForm 动态读取,形态切换后自动生效:
FormController.SwitchForm(newForm)
→ FormController.CurrentForm = newForm
→ SkillManager 下一次技能输入时,自动读取 newForm.soulSkill / spiritSkill1 / spiritSkill2
→ 无需主动通知 SkillManager
各形态技能 SO 资产路径(建议目录结构):
Assets/
└── Skills/
├── SkyForm/
│ ├── Sky_SoulSkill_LieKongZhang.asset
│ ├── Sky_SpiritSkill1_XuanLingJi.asset
│ └── Sky_SpiritSkill2_LingZongDan.asset
├── EarthForm/
│ ├── Earth_SoulSkill_DiXingShu.asset
│ ├── Earth_SpiritSkill1_BaShanQuan.asset
│ └── Earth_SpiritSkill2_DengLongCu.asset
└── DeathForm/
├── Death_SoulSkill_TaiXuZhan.asset
├── Death_SpiritSkill1_CanYinShu.asset
└── Death_SpiritSkill2_HunYuanBao.asset
7. SaveData 扩展
技能由 FormSO 固定绑定,无需存储解锁状态(无解锁机制)。仅持久化魄元当前值(灵力战斗结束归零,不持久化):
"skills": {
"currentSpiritPower": 75,
"currentSpringCharges": 3
}
8. UI 集成
技能与资源 HUD
Canvas_HUD
└── ResourceBar(左下角或角色头顶)
├── SoulPowerBar 灵力条(近战命中积累,橙色格子)
├── SpiritPowerBar 魄元条(自动恢复,蓝色渐变 + 恢复动画)
└── FormSkillIcons 当前形态技能图标(右侧)
├── SoulSkillIcon + CooldownRing(冷却为 0 时不显示)
├── SpiritSkill1Icon + CooldownRing
└── SpiritSkill2Icon + CooldownRing
FormController.OnFormChanged→ 切换图标组(天魂/地魂/命魂各自有独立图标集)PlayerStats.OnSoulPowerChanged→ 更新SoulPowerBar.fillAmountPlayerStats.OnSpiritPowerChanged→ 更新SpiritPowerBar.fillAmount
9. 事件频道
| 频道资产 | 类型 | 发布方 | 订阅方 |
|---|---|---|---|
OnSkillCast.asset |
VoidEventChannelSO |
SkillManager |
PlayerFeedback(技能音效) |
OnSkillFailed.asset |
VoidEventChannelSO |
SkillManager |
PlayerFeedback(资源不足提示音) |
OnFormChanged.asset |
FormEventChannelSO |
FormController |
SkillHUD(切换图标)、PlayerCombat(重置连击) |
OnSoulPowerChanged.asset |
IntEventChannelSO |
PlayerStats |
SoulPowerBar(UI 更新) |
OnSpiritPowerChanged.asset |
IntEventChannelSO |
PlayerStats |
SpiritPowerBar(UI 更新) |
10. 编辑器工具
FormSkillSO 预览 Inspector
- 技能 Inspector 底部显示模拟执行区域
- 下拉选择形态 → 自动显示关联 FormSO 的 3 个技能槽
- 填入当前资源值 → 点击 [模拟施放] → 控制台输出技能效果描述和资源扣除
- 修改器预览:拖入 SkillModifierSnapshot 资产 → 实时预览最终消耗/伤害参数