Unity MyFramework 用法说明(十四):使用 ComponentOwner 和 GameComponent 拆分对象能力
角色、界面和管理器的功能不断增加后所有逻辑都堆在一个类中会产生大量字段、生命周期函数和条件判断。MyFramework 提供了一套纯 C# 组件系统通过ComponentOwner和GameComponent把一个复杂对象拆分为多个相对独立的能力组件。项目地址https://github.com/ZHOURUIH/MyFramework一、为什么还需要一套组件系统Unity 已经提供了MonoBehaviour组件但并不是所有业务对象都适合挂在 GameObject 上。例如一个玩家角色可能包含模型管理 动作播放 状态机 技能数据 背包数据 装备数据 输入控制全部放进角色类后很容易变成public class CharacterPlayer { protected Animator mAnimator; protected ListSkill mSkillList; protected ListItem mPackItems; protected ListEquip mEquipList; protected CharacterState mState; protected bool mCanControl; protected bool mModelLoaded; public void update(float elapsedTime) { updateAnimation(elapsedTime); updateSkill(elapsedTime); updateState(elapsedTime); updateController(elapsedTime); } }随着功能增加这个类会不断扩大。使用组件后可以拆分为CharacterPlayer ├── COMCharacterAvatar ├── COMCharacterAnimation ├── COMCharacterStateMachine ├── COMCharacterSkill ├── COMCharacterPack ├── COMCharacterEquip └── COMCharacterController角色只负责组合这些能力不再直接实现所有细节。二、ComponentOwner 和 GameComponent框架中的核心关系是ComponentOwner └── 拥有多个 GameComponentComponentOwner负责创建和保存组件 控制组件启用状态 按照顺序更新组件 销毁并回收组件 根据类型查找组件GameComponent负责实现一个具体能力public abstract class GameComponent : ClassObject { protected ComponentOwner mComponentOwner; protected bool mIgnoreTimeScale; protected bool mDefaultActive; protected bool mActive; public virtual void init(ComponentOwner owner) { mComponentOwner owner; } public virtual void update(float elapsedTime) { } public virtual void fixedUpdate(float elapsedTime) { } public virtual void lateUpdate(float elapsedTime) { } }它不是 Unity 的Component也不会被挂到 GameObject 上。它是一个由框架创建、更新和回收的纯 C# 对象。三、创建一个角色生命值组件先创建一个简单的生命值组件public class COMCharacterHealth : GameComponent { protected int mHP; protected int mMaxHP; public void setHealth(int hp, int maxHP) { mHP hp; mMaxHP maxHP; } public void setHP(int hp) { mHP hp; } public int getHP() { return mHP; } public int getMaxHP() { return mMaxHP; } public bool isDead() { return mHP 0; } public override void resetProperty() { base.resetProperty(); mHP 0; mMaxHP 0; } }组件继承自ClassObject会由对象池复用所以必须在resetProperty()中清理全部字段。四、在角色中添加组件Character本身继承自ComponentOwner可以重写initComponents()public class CharacterPlayer : Character { protected COMCharacterHealth mCOMHealth; protected COMCharacterPack mCOMPack; protected COMCharacterEquip mCOMEquip; protected override void initComponents() { base.initComponents(); addInitComponent(out mCOMHealth, true); addInitComponent(out mCOMPack, true); addInitComponent(out mCOMEquip, true); } public override void resetProperty() { base.resetProperty(); mCOMHealth null; mCOMPack null; mCOMEquip null; } public COMCharacterHealth getCOMHealth() { return mCOMHealth; } }参数中的true表示组件创建后默认启用addInitComponent(out mCOMHealth, true);组件初始化顺序就是添加顺序基类组件 ↓ 生命值组件 ↓ 背包组件 ↓ 装备组件Character.init()会自动调用initComponents()不需要业务代码再次手动初始化每个组件。五、组件的自动更新只要拥有者每帧调用base.update(elapsedTime);ComponentOwner就会按照添加顺序更新全部启用组件Owner.update ↓ 组件1.update ↓ 组件2.update ↓ 组件3.update例如创建一个自动恢复生命值的组件public class COMCharacterHealthRecover : GameComponent { protected float mRecoverTimer; public override void update(float elapsedTime) { mRecoverTimer elapsedTime; if (mRecoverTimer 1.0f) { return; } mRecoverTimer 0.0f; CharacterPlayer character getOwner() as CharacterPlayer; COMCharacterHealth health character?.getCOMHealth(); if (health null || health.isDead() || health.getHP() health.getMaxHP()) { return; } health.setHP(health.getHP() 1); } public override void resetProperty() { base.resetProperty(); mRecoverTimer 0.0f; } }角色类不需要再增加updateHealthRecover(elapsedTime);只要把组件加入角色它就会自动获得更新。六、动态添加和获取组件并不是所有组件都需要初始化时创建。例如技能组件可以在角色第一次获得技能时再创建COMCharacterSkill skill character.getOrAddComponentCOMCharacterSkill();getOrAddComponentT()的规则是组件已经存在 返回已有组件 组件不存在 创建、初始化并返回新组件只查询、不自动创建COMCharacterSkill skill character.getComponentCOMCharacterSkill();只获取当前启用的组件COMCharacterSkill skill character.getActiveComponentCOMCharacterSkill();因此三种接口用途不同getComponent 只查询 getActiveComponent 只查询当前启用的组件 getOrAddComponent 没有时自动创建同一个拥有者不能添加两个完全相同类型的组件。七、启用和停用组件暂时不需要某个能力时可以停用组件character.activeComponentCOMCharacterHealthRecover(false);重新启用character.activeComponentCOMCharacterHealthRecover();停用后的组件仍然存在只是不再执行update fixedUpdate lateUpdate它保存的数据不会被销毁。例如进入安全区后停止自动战斗character.activeComponentCOMCharacterAutoBattle(false);离开安全区后重新启用character.activeComponentCOMCharacterAutoBattle();这比反复创建和销毁组件更适合短时间关闭某项能力。八、只暂停组件更新有时不希望改变组件自身的启用状态只想暂时跳过更新可以使用character.addDisableComponentCOMCharacterController();恢复更新character.removeDisableComponentCOMCharacterController();它和activeComponent(false)的区别是activeComponent(false) 真正修改组件的激活状态 addDisableComponent 只让拥有者暂时跳过该类型组件的更新例如播放一段不可操作的剧情时可以暂停控制器更新但不需要销毁或重新初始化控制器。九、让子类取消基类组件基类可能默认创建了一些组件但某个子类不需要。例如Character默认会创建COMCharacterAvatar COMCharacterAnimation COMCharacterStateMachine某种特殊角色不需要这些能力可以在调用基类初始化前声明public override void init() { addDontAutoCreateCOMCharacterStateMachine(); addDontAutoCreateCOMCharacterAnimation(); addDontAutoCreateCOMCharacterAvatar(); base.init(); }当基类执行addInitComponent(out mStateMachine, true);框架会检测该类型是否被标记为不自动创建。这种方式允许基类统一声明默认能力同时允许子类按需裁剪。十、组件的销毁与回收拥有者销毁时会自动销毁它拥有的全部组件ComponentOwner.destroy ↓ 逐个调用组件 destroy ↓ 调用 resetProperty ↓ 归还 ClassPool组件内部如果注册了事件或持有外部对象应在destroy()中清理public override void destroy() { mEventSystem?.unlistenEvent(this); base.destroy(); }普通字段则在resetProperty()中重置public override void resetProperty() { base.resetProperty(); mTarget null; mTimer 0.0f; }角色中缓存的组件引用也需要重置为null避免对象池复用后保留旧引用。十一、什么时候适合使用 GameComponent适合拆成组件的功能通常具备以下特点属于某个对象而不是全局系统 拥有相对独立的数据和生命周期 可能只在部分对象上存在 可以独立启用或停用 需要跟随拥有者自动更新和销毁典型例子包括角色状态机 角色动画 角色背包 角色技能 摄像机跟随 对象拖拽 自动旋转 透明度渐变 界面交互 场景音效如果功能是整个游戏唯一的全局管理器更适合使用FrameSystem。如果只是一个短暂的业务操作更适合封装成 Command。十二、总结MyFramework 的组件系统由两部分组成ComponentOwner 负责组合、更新和销毁组件 GameComponent 负责实现一项具体能力基本使用流程是1. 创建 GameComponent 子类 2. 在 resetProperty 中重置字段 3. 在拥有者的 initComponents 中添加 4. 由 ComponentOwner 自动更新 5. 按需启用、停用或动态创建 6. 拥有者销毁时自动回收它与 Unity 的MonoBehaviour组件思想相似但完全运行在框架的纯 C# 对象体系中。通过这种方式角色、界面、场景和管理器都可以由多个小组件组合而成避免一个类承担越来越多互不相关的职责。

相关新闻

Unity游戏IL2CPP逆向工程与安全防护实战

Unity游戏IL2CPP逆向工程与安全防护实战

1. IL2CPP技术背景与Unity游戏保护机制在移动游戏开发领域,Unity引擎的IL2CPP技术已经成为保护游戏逻辑安全的重要屏障。作为传统Mono编译方式的替代方案,IL2CPP通过将C#中间语言(IL)转换为C代码,再编译为本地机器码,显著提高了逆…

2026/7/22 21:39:51阅读更多 →
HelloGitHub开源项目推荐与贡献指南

HelloGitHub开源项目推荐与贡献指南

1. 项目概述《HelloGitHub》第96期是一期专注于发现和分享有趣、入门级开源项目的社区月刊。作为一个长期运营的开源项目推荐平台,HelloGitHub每月都会精选各类优质开源项目,涵盖编程语言、工具库、应用软件等多个领域,旨在帮助开发者发现有意…

2026/7/22 21:39:51阅读更多 →
提升开发效率:Roo Commander多技能协同工作流实战案例

提升开发效率:Roo Commander多技能协同工作流实战案例

提升开发效率:Roo Commander多技能协同工作流实战案例 【免费下载链接】roo-commander Think of it like having a virtual, specialized software development team right inside your editor, orchestrated by the 👑 Roo Commander, powered by Roo C…

2026/7/22 21:39:51阅读更多 →
Custom Lightning Types 实战:航班预订示例

Custom Lightning Types 实战:航班预订示例

航班预订示例:自定义 Lightning Types 输入和输出 本示例展示如何使用自定义 Lightning Types 来覆盖 Agent Action 的默认输入和输出 UI。我们将使用 FlightAgent Apex 类查找可用航班,创建两个自定义 Lightning Type:flightResponse&#…

2026/7/22 22:38:01阅读更多 →
ComfyUI-LTXVideo完整教程:3步快速掌握AI视频生成核心技术

ComfyUI-LTXVideo完整教程:3步快速掌握AI视频生成核心技术

ComfyUI-LTXVideo完整教程:3步快速掌握AI视频生成核心技术 【免费下载链接】ComfyUI-LTXVideo LTX-Video Support for ComfyUI 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-LTXVideo ComfyUI-LTXVideo是ComfyUI平台上最强大的AI视频生成插件之…

2026/7/22 22:38:01阅读更多 →
FreeCAD源码分析:引用依赖管理

FreeCAD源码分析:引用依赖管理

本文分析FreeCAD中对象引用依赖管理。 限于研究水平,分析难免不当,欢迎批评指正; 文档会不定期更新。 一、背景 二、原理 三、核心组件 四、关键流程 文献 网络

2026/7/22 22:38:01阅读更多 →
深度解析LLM编码规范:提升开发效率的4大核心策略

深度解析LLM编码规范:提升开发效率的4大核心策略

深度解析LLM编码规范:提升开发效率的4大核心策略 【免费下载链接】andrej-karpathy-skills A single CLAUDE.md file to improve Claude Code behavior, derived from Andrej Karpathys observations on LLM coding pitfalls. 项目地址: https://gitcode.com/GitH…

2026/7/22 22:38:01阅读更多 →
30天标准化康复方案!速康济南中心,专治久治无效疑难康复问题

30天标准化康复方案!速康济南中心,专治久治无效疑难康复问题

康复的核心,从来不是盲目治疗,而是精准对症、科学施策、高效见效。很多康复患者久治不愈、迟迟不见改善,核心原因在于康复方案碎片化、针对性弱、缺乏标准化体系,无法从根源修复受损神经与肢体功能。针对行业普遍存在的康复效果参…

2026/7/22 22:38:01阅读更多 →
Java 开发者 Agent 框架:AgentScope Java 2.0 GA 发布

Java 开发者 Agent 框架:AgentScope Java 2.0 GA 发布

Java 开发者也能用 Agent 框架了!AgentScope Java 2.0 GA 发布,三大核心优势拆解2026 年 7 月 7 日,AgentScope Java 2.0 正式发布。不是又一个"把 Python 翻译成 Java"的框架——它从第一天起就在解决同一个问题:怎么让…

2026/7/22 22:36:01阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 0:53:59阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/22 0:53:59阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 0:53:59阅读更多 →
中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业小程序开发公司怎么选:预算、上手和售后避坑指南

中小企业做小程序,最常见的矛盾是预算有限,但又不希望功能太单薄;没有技术团队,但又希望后续能自己运营;想快速上线,又担心隐性收费和售后失联。选型时如果只看“低价套餐”或“案例数量”,很容…

2026/7/22 0:01:17阅读更多 →
GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

GEO优化如何沉淀长期内容资产?广拓时代谈AI搜索时代的内容ROI

企业做营销,最怕钱花完了,资产没有留下。 效果广告能带来一段时间的曝光,但预算停止后,流量往往也随之停止。短视频内容可能在几天内冲高,也可能很快沉下去。AI搜索时代,企业需要重新思考一个问题&#xff…

2026/7/22 0:01:17阅读更多 →
Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复

Agent 终态判定:何时该停止思考、给出最终回复 一、你的 Agent 在"再想想"的循环里绕了 12 轮,用户已经关窗口了 Agent 与人最大的区别是:人知道什么时候该停下来给答案,Agent 会一直"想"下去。你给 Agent 接…

2026/7/22 0:01:17阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/21 22:53:50阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/22 18:55:50阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/22 18:55:50阅读更多 →