这节课介绍了Game Objects,以及游戏引擎中Objects与平时OOP编程中Objects的不同。
游戏对象Game Objects,GO:
- 动态物 Dynamic Game Objects
- 静态物 Static Game Objects
- 环境 Environments
- 地形系统 Terrain
- 天空 Sky
- 植被 Vegetation
- 空气墙 air wall / combat area / war zone
- Trigger Area
- 某些使用对象表达的规则Rules、寻路系统navigation mesh等
GO并不是最小单位。游戏对象的描述方法与OOP的常用方法并不一致。因为游戏中的对象很难单纯的用继承来描述,使用组件描述更方便,比如枪的各种模块,实际上抽象的内容比如方法等也应当组件化。所以游戏中对象的最小单位实际上是组件。
所有的组件都有Tick()方法(包括逻辑的渲染的,一般先逻辑再渲染),每一个服务器帧把所有组件的Tick()方法调用一次,就形成了动态的世界。调用也是以组件而不是GO为单位的,因为将所有同类的组件一并调用可以提高效率。
Tick()可以选择步长。如果一个服务器帧内Tick()不完,可以考虑下一次Tick()的时候Tick()两帧的步长,或者预计到该帧Tick()不完的话这一帧就只Tick()对游戏体验影响大的组件。
组件之间可以绑定。比如人进入载具之后人的位置等属性就与载具绑定了。
组件之间可以通信,即事件机制。当一个组件触发什么事件后,会广播消息,收到消息的组件判断是否与自己相关,有必要的话调用相关方法。这一模块要有好的可扩展性,鬼知道游戏开发人员想设计什么规则的事件。另外要注意时序,尤其是多处理机系统中,要做好同步。另外游戏中的很多情形颇有循环依赖的感觉,例如移动导致的碰撞又导致移动,以及布娃娃系统导致的碰撞反馈到动画系统等(可以先放动画,动画后半部分逐步引入物理碰撞带来的效果,这样既有动画的戏剧性效果有有物理系统的真实),这种情况下要特别注意同步。
组件应当以类似树的结构被管理,以便于上述通信发生时进行广播。即广播时只需向兄弟节点或满足什么什么要求的节点广播。也可以简单地根据坐标位置分块管理,但对较大的项目来说性能不理想,因为GO的分布是不均匀的。当动态物位置变动时,可能涉及到树的重构,需要高效的算法。游戏引擎可以提供多种管理组件的结构供游戏开发人员选择。
组件模式的缺点是,由于组件太多,其管理和通信很复杂,做得不好会很低效。