离散仿真引擎基础--井字棋
简答题
解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系。
解答:
- 游戏对象(GameObjects),游戏程序空间中的事物,可能是 Empty(空,最有用的事物)、2D、3D、光线、摄像机等
- 游戏资源(Assets),构造游戏对象、装饰游戏对象、配置游戏的物体和数据。即序列化的或存储格式的游戏对象或数据
游戏对象指的是创建出来的对象,以及初始化时的灯光和相机。而游戏资源可以是创建的游戏对象,也可以是从外部导入的图像。
二者也是有联系的,我们将游戏对象拖入进游戏资源中,便成为了一个游戏资源;当我们将游戏资源实例化之后便成为了游戏对象
下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
解答:
在 unity hub 中的学习目录中有很多游戏样例进行学习和下载,我选择了其中的两款进行分析。
通过 unity 打开后可以查看到它的 Assets 目录如下所示:
由图中可以看到Assets目录的结构,是采用的属性结构,父文件夹包含多个子文件夹,且相同或者类似的文件保存在同一个文件夹中,每个子文件夹,也有若干个子文件。可以根据这样的层次结构得到结论,资源文件夹将属性相似的资源放在同一个文件夹中。
找到该项目的场景BlankScene打开后,可以看到对象的层次结构,不难看出有多个对象有自己的子对象,这些子对象又具有自己的子对象。父对象由他们的子对象组成起来。所有对象以树形形式组织起来。
又下载了几个游戏案例,发现其对象的层次结构与Assets的层次结构与上面的案例类似,这里就不展开说明了,直接将他们的结构截图放在下面:
- 游戏介绍:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200921205417641.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjY3Nzcz,size_16,color_FFFFFF,t_70#pic_center)
- Assets的结构如下所示:
![](https://img-blog.csdnimg.cn/2020092120515657.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjY3Nzcz,size_16,color_FFFFFF,t_70#pic_center)
- 对象的层次结构如下所示:
![](https://img-blog.csdnimg.cn/2020092120523657.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjY3Nzcz,size_16,color_FFFFFF,t_70#pic_center)
编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
- 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
- 常用事件包括 OnGUI() OnDisable() OnEnable()
解答:
首先添加一个C#代码的资源,验证的代码内容如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Debug.Log("Start");
}
// Update is called once per frame
void Update()
{
Debug.Log("Update");
}
void FixedUpdate()
{
Debug.Log("FixedUpdate");
}
void LateUpdate()
{
Debug.Log("LateUpdate");
}
void Awake()
{
Debug.Log("Awake");
}
void OnGUI()
{
Debug.Log("OnGUI");
}
void OnDisable()
{
Debug.Log("OnDisable");
}
void OnEnable()
{
Debug.Log("OnEnable");
}
}
然后打开保存,在unity中点击play进行运行,得到的结果如下图所示:
下面是对行为或事件的解释:
- Start(): 在Update函数调用之前调用一遍
- Awake(): 用于脚本被实例装载时调用
- Update(): 每一帧都会调用一次
- FixedUpdate(): 每个固定时间片调用一次
- LateUpdate(): 在Update函数调用后被调用
- OnGUI(): 绘制GUI时会被调用
- OnDisable(): 对象变为不可用时函数被调用
- OnEnable(): 对象变为可用状态时函数被调用
查找脚本手册,了解 GameObject,Transform,Component 对象
- 分别翻译官方对三个对象的描述(Description)
描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
- 本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
- 例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
- 用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
解答:
- GameObject:Base class for all entities in Unity Scenes.(是所有在Unity场景中的实体的基类)
Transform:Position, rotation and scale of an object.(物体的位置、旋转、和大小)
Component:Base class for everything attached to GameObjects.(是所有附加到游戏物体上的基类)
table对象的属性:activeSelf属性,有物体的名字,和Static属性,下面是Tap,Layer,Prefab,Transform等属性Transform的属性:Position代表物体在XYZ轴上的位置,Rotation代表物体在XYZ方向旋转的角度,Scale表示物体在XYZ方向上的大小
table的部件:Transform,Mesh Filter,Box Colider,Mesh Renderer
三者的关系是,GameObject和component是一对多的关系,也就是一个GameObject可以有多个component;GameObject和Transform是一对一的关系,也就是说一个GameObject对应于一个Transform;component是Transform的父类,Transform只是component的一种
资源预设(Prefabs)与 对象克隆 (clone)
- 预设(Prefabs)有什么好处?
- 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
- 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
解答:
- 预设可以快速实例化很多相同的对象,可以减少工作量,并且对于预设的修改相当于是对所有由预设创建出来的对象的修改,这样不需要一一修改,也是减少了工作量
- 用预设创建一个对象,相当于是从预设克隆出来的一个对象,对预设的全部修改,由它创建出来的所有对象都会进行修改。但是如果是克隆对象的话,由于克隆对象是相对独立的,彼此之间没有联系,也不会互相影响,所以对一个克隆对象进行改变并不会造成别的克隆对象的改变
首先做好Table对象,做好的成果如下所示:
做好之后将Table拖入到Assets中成为一个预制资源
成为预制资源后将原先做好的游戏对象删除掉,使得场景中只有灯光和相机两个对象,然后创建一个C#资源进行编写代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { // Start is called before the first frame update public GameObject table; void Start() { Instantiate(table); } // Update is called once per frame void Update() { } }
将写好的C#代码加入到新创建的对象的部件中,然后点击 play 开始运行,结果如下所示:
可以看到成功将资源实例化成对象
编程实践,小游戏
- 游戏内容: 井字棋 或 贷款计算器 或 简单计算器 等等
- 技术限制: 仅允许使用 IMGUI 构建 UI
作业目的:
- 了解 OnGUI() 事件,提升 debug 能力
- 提升阅读 API 文档能力
我学习并制作了井字棋这个项目,实验的过程截屏如下所示:
- 游戏过程:分为两个玩家进行游戏,第一个玩家是'X',第二个玩家是'O',如果某个玩家的棋子3个连成一条线那么就会显示该玩家获胜,如果最终棋盘填满也没有玩家获胜,则会显示“Play even(平局)”。在游戏进行途中,或者游戏结束后都可以进行重置,只需点击左边的reset按钮即可,棋盘会重新清零,开始新的游戏
- 游戏框架:该游戏主要由9个按钮组成的棋盘,一个插入的背景图片,按钮组成的重置键,标签组成的游戏名称,以及最后游戏结束显示的结束语组成
- 代码说明:首先设置两个全局变量,int类型的board数组代表 3 * 3 的棋盘,如果数值为0代表该位置为空,如果数值为1代表玩家1在此下了棋,若为2代表玩家2在此下了棋,和int类型step变量,用于记录当前的棋盘有多少位置被下过了
然后是start()函数,包括了init()函数用来初始化棋盘,也就是清零board数组,并且将step设置为0
接下来是isWin(),函数用来判断是否有人获胜,并且返回获胜的人,如果没有人获胜则返回0
接下来就是OnGUI()函数,用来调用上面的初始化或者判断是否有人获胜,并且进行每一步下棋的操作,并且显示游戏的名称和背景,具体代码如下所示:
- 实验总结:通过本次实验学会了OnGUI函数的完成,并且学会了按钮,标签的定义过程,也更加熟悉了在游戏运行时,各个函数的运行顺序和运行次数。也发现了unity做游戏的方便之处。
- 完整实验代码:传送门
思考题【选做】
微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。
- 为什么是“模板方法”模式而不是“策略模式”呢?
解答:
模板方法模式:定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。是一种行为模式。
策略模式:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化。是一种对象行为模式。
策略模式的缺点:
- 策略类数量有可能会很多。
- 所有的策略类都需要对外暴露。
模板方法模式和策略模式都可以用来分离高层的算法和底层的具体实现细节。都允许高层的算法独立于它的具体实现细节重用。此外,策略模式也允许具体实现细节独立于高层的算法重用,不过要以一些额外的复杂性、内存以及运行时间作为代价。
将游戏对象组成树型结构,每个节点都是游戏对象(或数)。
- 尝试解释组合模式(Composite Pattern / 一种设计模式)。
- 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
解答:
- 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
// father void Start () { this.BroadcastMessage("Mes"); } // son void Mes(){ Debug.Log ("Son"); }
一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component)。
- 这是什么设计模式?
- 为什么不用继承设计特殊的游戏对象?
解答:
- 组合模式
- 直接添加不见,可以方便的调整对象的属性,否则可能会造成浪费现象
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。