基于 cocos2d-x 引擎的游戏框架设计
文 / 李成, 李成,郑鑫
移动互联网浪潮正在彻底改变人们日常的生活习惯和生活方式。 相应的, 基于移动终端和感 应交互的游戏, 也为人们带来了全新的游戏体验。 本文, 我们将结合目前流行的 cocos2d-x 引擎,使用 C++语言,基于 iOS 平台,和大家分享 iPhone、iPad 上游戏客户端的构架与
实现。
游戏架构与实现
目前,很多基于 cocos2d-x 的代码基本上仅是对引擎功能的使用,完全不能按照游戏项目
的标准来参考。作为游戏项目代码,不仅需要实现游戏的诸多功能,还需要从架构层面,从
模块设计的角度来思考和设计,使代码具有更好的复用性和拓展性。
对于游戏客户端,按照功能模块的区别可分为:引擎封装层模块、游戏数据管理模块、应用
程序配置模块、日志记录模块、网络管理模块、消息事件机制模块、输入输出控制模块、音 效管理模块、
UI 系统模块、逻辑系统处理模块、调试器控制模块等。针对不同类型的游戏,
通常只需要单独实现最上层的游戏逻辑系统, 而剩余的模块完全可以复用。 下面将详细讲解
各个模块的职能与实现(暂不包含游戏逻辑系统)。
引擎封装模块( 引擎封装模块(EngineSystem)
为了减少客户端代码对 cocos2d-x 引擎的依赖程度和降低耦合度,我们建立了引擎封装层
模块,将引擎必要的初始化、逻辑更新、渲染和资源管理等操作全部交给引擎封装层处理,
使客户端的其他模块不需要过于依赖引擎层。同时,为了避免客户端代码中频繁、直接地调
用平台相关的诸多功能,我们还将一些平台相关的功能全部封装在引擎封装层模块内。
cocos2d-x 功能很多很强大, 但在开发时, 需要根据项目需要有条件选择引擎功能 (当然, cocos2d-x 本身设计实现的很好) 。 例如, 在引擎封装层内部, 我们仅使用了一个 CCScene 对象, 在设计之初就刻意避免处理多个 CCScene 之间的初始化、 跳转、 销毁、 更新等操作,
极大地简化了逻辑层代码,降低了复杂度,且到目前为止,在表现效果上没有什么影响。
数据管理模块( 数据管理模块(DBSystem)
在开发过程中经常会存储和读取大量的静/动态数据,对比我们针对这部分设计了 DBSystem 模块, 专门进行游戏数据层的管理。 每种类型的游戏数据都会派生出一个具体的 类,如音效数据管理器、图片数据管理器等。这些数据管理器都在 DBSystem 内部统一进
行初始化、更新和销毁,并各自使用单例模式。外层使用时,直接通过其类进行数据读取即
可,无须关心其初始化、逻辑更新、销毁等操作。同时,为了时刻对游戏的静态数据进行监
控, 所有的数据模块都暴露了获取其所包含数据的接口, 这样我们就可以在游戏中随时获取游戏xml文件修改
数据层的信息,方便进行统计和监控。
应用配置系统( 应用配置系统(VariableSystem)
开发者一般需要对应用属性进行可配置化处理:一方面可以方便开发者快速开启/屏蔽某些
功能; 另一方面能更人性化地支持用户偏好设置。 目前, 我们根据类型的不同, 建立了账号、
网络、日志三种配置文件,分别对游戏账号、游戏功能信息、游戏网络配置信息、游戏日志 配置信息进
行动态设置,全部使用 XML 进行数据存储和读取。
在开发中通常需要保存大量临时数据, 这些数据往往被放在各个模块内部, 如果其他模块需
要使用,就造成两个模块间强行依赖,增加了耦合度。所以,我们将所有临时需要的数据统
一定位为内存配置数据,放在我们的应用配置系统中,其和账号、网络、日志配置文件的区
别在于: 基于文件配置的属性数据都需要在程序退出时强行写回文件, 而基于内存配置的属
性数据无须保存。
音效控制模块( 音效控制模块(SoundSystem)
iOS 平台并没有十分完善的音效引擎, 而一般自行实现的音效库都难以进行拓展和支持跨平 台,所以我们直接选取流行的 FmodEx 引擎,进行游戏音效的播放和管理。同时,结合 FmodEx 提供的强大接口, 可以很方便地实现声音大小设置、 暂停、 循环、 3D 音效等操作,
完全满足一般游戏的需求。
在游戏中,一般都需要频繁的播放多个音效,为了提高效率并节省内存,我们在逻辑层对每
一个音效文件都使用了引用计数技术, 对同一种音效文件仅需通过计数的方式维持一份实例
即可,同时播放的多个相同音效,实际上都是使用同样的一份实例而已,无须单独创建音效
实例;另外,通过引用计数,很好地解决了音效资源回收的问题,当音效资源计数为零时,
即表示其可以被回收,对应的资源,占用的内存也将被释放。
日志系统模块( 日志系统模块(LoggerSystem)
为了方便在开发、 运营期对出现的问题及时进行定位和排查, 对游戏中关键的处理流程都需 要进行日志记录。在客户端,我们仿照 Log4J 的方式,实现了分级(Trace 级、Info 级、 Error 级等)、分文件、分输出方式的强大日志管理。游戏的日志模块,结合了应用配置系
统,完全实现了动态化配置,通过对日志配置文件进行设置修改,开发者可以很方便地设置
日志的开启等级、输出方式、大小拆分、输出名称等。另外,对于客户端日志模块,无须过
多考虑其性能问题,所以我们的日志模块,完全是简单地在主线程里进行文件写入,没有多
开线程进行文件操作。
消息事件系统模块( 消息事件系统模块(EventSystem)
考虑到客户端框架总体的拓展性,我们完全使用事件驱动模型(Event-driven)来设计和
开发,将客户端中事件的触发时机和具体处理逻辑彻底分隔开。游戏的各个模块,仅需要注
册、监听和实现其关心的消息事件,而无须关心事件何时被触发,降低了总体耦合度。目前 游戏中所有 UI 面板的隐藏/显示、事件响应、音效的播放/停止、游戏流程的切换、游戏角
状态迁移等,完全通过事件驱动方式开发;同时这种基于事件的处理方式,为项目使用动
态脚本拓展提供了支持: 脚本层省去对逻辑代码的大量直接调用, 通过消息事件完成脚本层
和逻辑层的交互调度,大大简化了开发的复杂度。
UI 系统( 系统(GUISystem)
cocos2d、cocos2d-x 这两种引擎本身并没有提供太多 UI 控件,仅提供了按钮、进度条等 基础控件,如果想使用更多的 UI 控件,需要开发者借鉴或使用其他成熟的 GUI 引擎,如 CEGUI 等。
在我们的 UI 系统中,充分借鉴了 CEGUI 的设计思想。整体上,将游戏中有关联的 UI 控件 集中到一个
个单独的 CCLayer 上,组成多个独立的 Layout,也就是我们在代码中定义的 IWindow 类。每一个 IWindow 类,都包含其自身的根面板(CCLayer)和众多依附在其上