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# 对象体系中。通过这种方式角色、界面、场景和管理器都可以由多个小组件组合而成避免一个类承担越来越多互不相关的职责。

相关新闻

Python计算机毕设之个人学术文献库智能整理管理系统 基于 Python 的文献检索与归档服务系统(完整前后端代码+说明文档+LW,调试定制等)

Python计算机毕设之个人学术文献库智能整理管理系统 基于 Python 的文献检索与归档服务系统(完整前后端代码+说明文档+LW,调试定制等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/22 21:41:57 阅读更多 →
2025年AI写小说完全指南:5种最佳工具对比与赚钱技巧详解

2025年AI写小说完全指南:5种最佳工具对比与赚钱技巧详解

说实话,2025年的今天,AI辅助创作早就不是什么秘密武器,而是行业标配了。但AI写小说和人类写作的根本区别在于:人类负责灵魂和创意,AI负责效率和执行。在用AI写小说之前最好先了解一下它的原理,知道了原理才…

2026/7/22 21:41:57 阅读更多 →
袁艺凡-FDE前沿工程师:AI Agent从需求拆解到生产交付的工程实践

袁艺凡-FDE前沿工程师:AI Agent从需求拆解到生产交付的工程实践

一、FDE解决的不是模型问题,而是交付问题 FDE的标准英文通常是Forward Deployed Engineer,业内常译为“前线部署工程师”或“一线交付工程师”。本文沿用“FDE前沿工程师”这一人物定位,强调该角色处于AI技术与业务落地的交叉前沿。 袁艺凡是…

2026/7/22 21:40:57 阅读更多 →

最新新闻

YUM包管理工具:原理、配置与实战技巧

YUM包管理工具:原理、配置与实战技巧

1. YUM包管理工具概述YUM(Yellowdog Updater Modified)是Linux系统中基于RPM包管理系统的命令行工具,主要用于自动化软件包的安装、更新和依赖管理。作为RPM生态中最主流的包管理工具之一,YUM通过软件仓库机制简化了Linux系统的软…

2026/7/22 22:24:12 阅读更多 →
Magisk深度解析:Android系统定制的革命性Root解决方案

Magisk深度解析:Android系统定制的革命性Root解决方案

Magisk深度解析:Android系统定制的革命性Root解决方案 【免费下载链接】Magisk The Magic Mask for Android 项目地址: https://gitcode.com/GitHub_Trending/ma/Magisk 在Android生态系统中,Magisk以其创新的系统级挂载技术彻底改变了Root权限的…

2026/7/22 22:24:12 阅读更多 →
索哲门窗用什么五金?一文看懂高端门窗常用十大五金品牌怎么选

索哲门窗用什么五金?一文看懂高端门窗常用十大五金品牌怎么选

装修选门窗,很多业主把注意力放在型材、玻璃和价格上,却往往忽略了门窗的"心脏"——五金配件。行业内有句老话:“门窗好不好,七分看五金”,五金件的质量直接决定了门窗的使用寿命、密封性、静音效果和使用手…

2026/7/22 22:24:12 阅读更多 →
Skills3:如何通过智能技能系统提升开发者300%的工作效率

Skills3:如何通过智能技能系统提升开发者300%的工作效率

Skills3:如何通过智能技能系统提升开发者300%的工作效率 【免费下载链接】skills Public repository for Agent Skills 项目地址: https://gitcode.com/GitHub_Trending/skills3/skills 在当今快速迭代的软件开发环境中,技术团队面临着一个核心挑…

2026/7/22 22:23:11 阅读更多 →
AI生成商业摄影级图片的7个致命误区:90%设计师踩坑的隐藏雷区,现在规避还来得及

AI生成商业摄影级图片的7个致命误区:90%设计师踩坑的隐藏雷区,现在规避还来得及

更多请点击: https://kaifayun.com 第一章:AI生成商业摄影级图片的致命误区全景图 在商业摄影级图像生成场景中,AI模型常因训练数据偏差、提示词工程失当与后处理缺失,导致输出结果陷入“高分辨率假象”陷阱——像素清晰但光影逻…

2026/7/22 22:23:11 阅读更多 →
5分钟深度解析:exifr如何成为JavaScript元数据提取的性能王者

5分钟深度解析:exifr如何成为JavaScript元数据提取的性能王者

5分钟深度解析:exifr如何成为JavaScript元数据提取的性能王者 【免费下载链接】exifr 📷 The fastest and most versatile JS EXIF reading library. 项目地址: https://gitcode.com/gh_mirrors/ex/exifr 在数字图像处理的世界里,每张…

2026/7/22 22:23:11 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/7/22 12:54:44 阅读更多 →

月新闻