——以Unity实现动态技能系统与嵌套式UI界面为核心案例
核心目标:
✅ 统一处理单个对象与组合对象,建立树形结构层次
✅ 递归组合实现复杂结构,简化客户端代码
✅ 支持动态扩展节点类型,符合开闭原则
关键术语:
数学表达:
设组合结构为树T,其中每个节点N满足:
N.Operation() = N自身操作 + Σ(子节点.Operation())
public interface ISkillComponent {
void Execute();
void Add(ISkillComponent component);
void Remove(ISkillComponent component);
ISkillComponent GetChild(int index);
}
public abstract class SkillComponent : MonoBehaviour, ISkillComponent {
public virtual void Add(ISkillComponent component) {
throw new System.NotSupportedException("叶子技能不能添加子技能");
}
public virtual void Remove(ISkillComponent component) {
throw new System.NotSupportedException("叶子技能不能移除子技能");
}
public virtual ISkillComponent GetChild(int index) {
throw new System.NotSupportedException("叶子技能没有子技能");
}
public abstract void Execute();
}
// 叶子技能:火球术
public class FireballSkill : SkillComponent {
[SerializeField] private ParticleSystem castEffect;
public override void Execute() {
StartCoroutine(CastRoutine());
}
private IEnumerator CastRoutine() {
castEffect.Play();
yield return new WaitForSeconds(0.5f);
Debug.Log("发射火球!造成100点伤害");
}
}
// 复合技能:三连击
public class ComboSkill : SkillComponent {
private readonly List<ISkillComponent> _children = new();
public override void Add(ISkillComponent component) {
_children.Add(component);
}
public override void Execute() {
StartCoroutine(ExecuteCombo());
}
private IEnumerator ExecuteCombo() {
foreach(var skill in _children) {
skill.Execute();
yield return new WaitForSeconds(0.3f);
}
}
}
public class SkillManager : MonoBehaviour {
void Start() {
// 构建组合技能
var combo = new ComboSkill();
combo.Add(new BasicAttack());
combo.Add(new UpperCut());
combo.Add(new FinalSmash());
// 执行组合技能
if(Input.GetKeyDown(KeyCode.Q)) {
combo.Execute();
}
}
}
public class LazyComposite : SkillComponent {
private List<ISkillComponent> _children;
public override void Add(ISkillComponent component) {
_children ??= new List<ISkillComponent>();
_children.Add(component);
}
}
public class SkillIterator {
private readonly Stack<ISkillComponent> _stack = new();
public SkillIterator(ISkillComponent root) {
_stack.Push(root);
}
public bool HasNext() => _stack.Count > 0;
public ISkillComponent Next() {
var current = _stack.Pop();
if(current is CompositeSkill composite) {
foreach(var child in composite.GetChildren().Reverse()) {
_stack.Push(child);
}
}
return current;
}
}
public class SkillTreeDebugger : MonoBehaviour {
void OnDrawGizmos() {
DrawSkillTree(_rootSkill);
}
private void DrawSkillTree(ISkillComponent node, Vector3 position, int depth = 0) {
Gizmos.DrawSphere(position, 0.2f);
if(node is CompositeSkill composite) {
var childPos = position + Vector3.right * (1f / depth);
foreach(var child in composite.GetChildren()) {
DrawSkillTree(child, childPos, depth + 1);
Gizmos.DrawLine(position, childPos);
}
}
}
}
技能/天赋树系统
public class TalentTree : CompositeSkill {
public void UnlockPath(ISkillComponent target) {
// 递归解锁前置天赋
}
}
动态UI系统
public class UIWidget : MonoBehaviour {
private List<UIWidget> _children = new();
public void Render() {
foreach(var child in _children) {
child.Render();
}
}
}
场景对象管理
public class SceneObjectGroup : MonoBehaviour {
private List<SceneObject> _children = new();
public void SetVisibility(bool visible) {
foreach(var child in _children) {
child.gameObject.SetActive(visible);
}
}
}
成就系统
public class AchievementSystem : CompositeAchievement {
public void CheckProgress() {
foreach(var achievement in GetChildren()) {
if(achievement is CompositeAchievement composite) {
composite.CheckProgress();
}
else {
achievement.UpdateStatus();
}
}
}
}
策略 | 实现方式 | 适用场景 |
---|---|---|
批处理渲染 | 合并Mesh/材质 | UI系统 |
惰性求值 | 缓存计算结果 | 复杂技能树 |
空间分区 | 四叉树/八叉树 | 场景管理 |
对象池 | 复用叶子节点 | 频繁创建销毁 |
维度 | 组合模式 | 装饰器模式 |
---|---|---|
目的 | 构建树形结构 | 动态添加职责 |
结构 | 父子关系 | 链式包装 |
扩展性 | 垂直扩展 | 水平扩展 |
典型应用 | 技能组合 | 装备强化系统 |
TryGetComponent
进行类型检查public void Add(ISkillComponent component) {
if(component == this)
throw new ArgumentException("禁止循环引用");
_children.Add(component);
}
INotifyCollectionChanged
接口Q1:如何处理叶子节点的空方法?
→ 使用空对象模式提供默认实现
public class NullSkill : ISkillComponent {
public void Execute() { /* 静默失败 */ }
public void Add(ISkillComponent c) {}
// 其他方法...
}
Q2:如何优化深层次遍历性能?
→ 实现备忘录模式缓存遍历结果
public class SkillCache {
private Dictionary<ISkillComponent, List<ISkillComponent>> _cache = new();
public List<ISkillComponent> GetFlattenedList(ISkillComponent root) {
if(!_cache.ContainsKey(root)) {
_cache[root] = FlattenTree(root);
}
return _cache[root];
}
}
Q3:如何实现撤销/重做功能?
→ 使用命令模式封装操作
public class AddSkillCommand : ICommand {
private CompositeSkill _parent;
private ISkillComponent _child;
public void Execute() => _parent.Add(_child);
public void Undo() => _parent.Remove(_child);
}
上一篇 【结构型之桥接模式】终极指南——Unity游戏多维度扩展的架构艺术
下一篇 【结构型之装饰器模式】终极指南——Unity动态能力扩展与BUFF系统的架构奥秘