【结构型之组合模式】游戏开发实战——Unity技能系统与UI架构的高效实现之道

文章目录

      • 组合模式(Composite Pattern)深度解析
        • 一、模式本质与核心价值
        • 二、经典UML结构
        • 三、Unity实战代码(技能系统)
          • 1. 定义组件接口
          • 2. 实现叶子节点与复合节点
          • 3. 客户端使用
        • 四、模式进阶技巧
          • 1. 延迟加载优化
          • 2. 组合迭代器
          • 3. 可视化调试工具
        • 五、游戏开发典型应用场景
        • 六、性能优化策略
        • 七、模式对比与选择
        • 八、最佳实践原则
        • 九、常见问题解决方案

组合模式(Composite Pattern)深度解析

——以Unity实现动态技能系统嵌套式UI界面为核心案例


一、模式本质与核心价值

核心目标
统一处理单个对象与组合对象,建立树形结构层次
递归组合实现复杂结构,简化客户端代码
✅ 支持动态扩展节点类型,符合开闭原则

关键术语

  • Component(组件):定义统一接口(如游戏技能基类)
  • Leaf(叶子节点):不可再分的基础元素(如基础技能)
  • Composite(复合节点):包含子组件的容器(如组合技能)

数学表达
设组合结构为树T,其中每个节点N满足:
N.Operation() = N自身操作 + Σ(子节点.Operation())


二、经典UML结构
contains
«interface»
ISkillComponent
+Execute()
+Add(ISkillComponent)
+Remove(ISkillComponent)
+GetChild(int) : ISkillComponent
FireballSkill
+Execute()
SkillCombo
-children: List
+Execute()
+Add(ISkillComponent)
+Remove(ISkillComponent)
+GetChild(int) : ISkillComponent

三、Unity实战代码(技能系统)
1. 定义组件接口
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();
}
2. 实现叶子节点与复合节点
// 叶子技能:火球术
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);
        }
    }
}
3. 客户端使用
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();
        }
    }
}

四、模式进阶技巧
1. 延迟加载优化
public class LazyComposite : SkillComponent {
    private List<ISkillComponent> _children;
    
    public override void Add(ISkillComponent component) {
        _children ??= new List<ISkillComponent>();
        _children.Add(component);
    }
}
2. 组合迭代器
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;
    }
}
3. 可视化调试工具
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);
            }
        }
    }
}

五、游戏开发典型应用场景
  1. 技能/天赋树系统

    public class TalentTree : CompositeSkill {
        public void UnlockPath(ISkillComponent target) {
            // 递归解锁前置天赋
        }
    }
    
  2. 动态UI系统

    public class UIWidget : MonoBehaviour {
        private List<UIWidget> _children = new();
        
        public void Render() {
            foreach(var child in _children) {
                child.Render();
            }
        }
    }
    
  3. 场景对象管理

    public class SceneObjectGroup : MonoBehaviour {
        private List<SceneObject> _children = new();
        
        public void SetVisibility(bool visible) {
            foreach(var child in _children) {
                child.gameObject.SetActive(visible);
            }
        }
    }
    
  4. 成就系统

    public class AchievementSystem : CompositeAchievement {
        public void CheckProgress() {
            foreach(var achievement in GetChildren()) {
                if(achievement is CompositeAchievement composite) {
                    composite.CheckProgress();
                }
                else {
                    achievement.UpdateStatus();
                }
            }
        }
    }
    

六、性能优化策略
策略 实现方式 适用场景
批处理渲染 合并Mesh/材质 UI系统
惰性求值 缓存计算结果 复杂技能树
空间分区 四叉树/八叉树 场景管理
对象池 复用叶子节点 频繁创建销毁

七、模式对比与选择
维度 组合模式 装饰器模式
目的 构建树形结构 动态添加职责
结构 父子关系 链式包装
扩展性 垂直扩展 水平扩展
典型应用 技能组合 装备强化系统

八、最佳实践原则
  1. 透明性原则:保持叶子节点与复合节点接口一致
  2. 安全访问控制:使用TryGetComponent进行类型检查
  3. 循环引用检测
    public void Add(ISkillComponent component) {
        if(component == this) 
            throw new ArgumentException("禁止循环引用");
        _children.Add(component);
    }
    
  4. 事件通知:实现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系统的架构奥秘

你可能感兴趣的:(设计模式,组合模式,unity,设计模式,c#)