You can’t connect the dots looking forward; you can only connect them looking backwards
— Steve Jobs, Stanford Report, June 14, 2005
游戏引擎
游戏引擎是一组游戏运行部件以及软件工具的集合。随着技术进步,多数现代游戏引擎都包含以下部件,游戏引擎架构如图所示:
一般,游戏引擎分为两个层次:
- 游戏内容层:一组工具管理游戏需要的数据
- 游戏引擎层:一组游戏运行部件,支撑游戏的运行与人机交互
而对于 Unity 3D 离散引擎,其核心内容为,游戏循环遍历所有游戏对象的所有部件,驱动游戏运行。
作业与练习
1 简答题
Question 1.1
解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系。
游戏对象(GameObjects):是对象的子类,是有些设计中绝大部分实例的基类,也可以说游戏对象是一种容器。它们是空盒,可以容纳各类组件 Component,游戏中的每个对象都是一个游戏对象。
资源(Assets):在游戏设计中可以被游戏对象所使用的游戏素材,资源也可以被实例化为游戏对象。
综上,游戏对象可以使用资源,资源也可以被预设实例化为游戏对象。
Question 1.2
下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
下载了几个游戏案例,下图由ChinesePoker举例说明,其资源(Assets)组织结构如下:
其中,Images 是图像文件,Prefabs 是预储存文件,Resours 是动态加载的资源文件,Scences 是场景文件,Scripts 是脚本代码文件。可知,资源的组织结构一般是根据文件种类、以文件夹的形式来进行构建的。更一般地,资源种类可以大致分为:模型、动画、材质、着色器、纹理、音频、脚本等类型。
对于游戏对象(GameObjects),其组织结构由具体游戏设计而定,一般有游戏玩家、环境、其他玩家(如AI)、摄像机等虚拟父类。
Question 1.3
编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
- 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
- 常用事件包括 OnGUI() OnDisable() OnEnable()
编写的代码内容如下:
1 | using System.Collections; |
验证结果如下:
Question 1.4
查找脚本手册,了解 GameObject,Transform,Component 对象
- 分别翻译官方对三个对象的描述(Description)
- 描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
- 本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
- 例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
- 用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
Question 1.4.1
根据脚本手册,有如下翻译:
GameObject: Base class for all entities in Unity Scenes.
游戏对象是 Unity 场景中所使用的所有实体的基类。
Transform: Position, rotation and scale of an object. Every object in a Scene has a Transform. It’s used to store and manipulate the position, rotation and scale of the object.
变换决定了场景中每个对象的位置、旋转和比例,每个 Object 都有一个变换。
Component: Base class for everything attached to GameObjects.
组件是每个游戏对象的功能部件,是附加于游戏对象上的一种基类。
Question 1.4.2
对象(实体)属性:GameObject,Cube类,第一个框中为 activeSelf 属性,第二个框中为对象名称,第三个框中为 static 属性。
Transform属性:Position: (0, 0, 0)、Rotation: (0, 0, 0)、Scale: (1, 1, 1),分别表示对象的位置、旋转角度和长宽高。
部件:包括 Transform、Box Collider、Mesh Renderer、Default-Material 等。
Question 1.4.3
UML 图如下:
Question 1.5
整理相关学习资料,编写简单代码验证以下技术的实现:
- 查找对象
- 添加子对象
- 遍历对象树
- 清除所有子对象
- 查找对象
1 | // 通过名称查找 |
- 添加子对象
1 | // 添加子对象 |
- 遍历对象树
1 | // 遍历对象树 |
- 清除所有子对象
1 | // 清除所有子对象 |
Question 1.6
资源预设(Prefabs)与 对象克隆 (clone)
- 预设(Prefabs)有什么好处?
- 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
- 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
预设的好处
- 使对象和资源能够重复利用,且相同的游戏对象可以用同一个预制来创建。
- 方便统一批量调整对象属性,对预设进行修改后,所有的游戏对象都会发生改变。
预设与对象克隆的关系
- 两者都能创建出相同的游戏对象。
- 预设创建的实体会因预设的变化而变化,而克隆不会。
table 预制的制作
一个简单的例子如下:
1 | using System.Collections; |
2 编程实践-小游戏
2.1 游戏内容
井字棋 Tic-Tac-Toe:一种在3*3格子上进行的连珠游戏,由分别代表O和X的两个游戏者轮流在格子里留下标记(一般来说先手者为X),任意三个标记形成一条直线,则为获胜。
2.2 技术限制
使用 IMGUI 构建 UI。
2.3 作业目的
- 了解 OnGUI() 事件,提升 debug 能力
- 提升阅读 API 文档能力
2.4 完成情况
核心代码如下:
1 | using System.Collections; |
游戏界面如下:
2.5 项目地址
代码地址:https://github.com/CUFMHeart/3D-Game-Programming-Design/tree/master/homework2
视频地址:https://www.bilibili.com/video/av67477674/
3 思考题
Question 3.1
微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。
- 为什么是“模板方法”模式而不是“策略模式”呢?
首先,要明确“模板方法模式”和“策略模式”的含义。
模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
策略模式:一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
两者相比,模版方法扩展性好,对不变的代码进行封装,对可变的进行扩展;可维护性好,因为将公共代码进行了提取,使用的时候直接调用即可;但另一方面,因为每一个不同的实现都需要一个子类来实现,导致类的个数增加,它会使系统变得复杂。而实际上,由于微软 XNA 引擎的游戏循环骨架是已经确定的,所以使用模板方法模式的设计更为合理。
Question 3.2
将游戏对象组成树型结构,每个节点都是游戏对象(或数)。
- 尝试解释组合模式(Composite Pattern / 一种设计模式)。
- 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
组合模式
用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
BroadcastMessage()
一个简单的例子如下:
1 | using System.Collections; |
Question 3.3
一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component)。
- 这是什么设计模式?
- 为什么不用继承设计特殊的游戏对象?
这是装饰器模式(Decorator Pattern)。
继承的设计,往往为类引入静态特征,而随着扩展功能的增多,子类很容易脱离设计者预期的掌控,变得笨重、无法封装,因此一般不用继承设计特殊的游戏对象。装饰器模式可以将具体功能职责划分,可以更方便地进行类的功能的修改。
参考资料
[1] 离散仿真引擎基础_教学讲义
[2] Maunal
[3] 官方案例