提示:以下是本篇文章正文内容,下面案例仅供参考
井字棋设计请移步另一篇博客:
https://blog.csdn.net/qq_43256905/article/details/108733412
完整 Assets 设计请移步 gitee:
https://gitee.com/beilineili/game3-d
一、简答题
1.解释游戏对象(GameObjects)和资源(Assets)的区别与联系
答:
① 游戏对象,从广义的游戏角度看是在游戏过程中承担一部分功能并有一定属性的组件;从Unity角度看就是所有实例的基类
② 资源,从广义的游戏角度看是由游戏开发者预先准备好的素材,可以在开发游戏时直接迁移给多个游戏对象使用,以及重复使用;从Unity角度看就是指修饰游戏对象的部件,比如Scripts,Scences这些
① 游戏对象是资源的实例化,可以被保存为资源来重复使用;
② 资源则可以作为游戏对象创建的模板或一部分,供不同的游戏对象使用
2.下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
答:
由于这学期我们下载的 unity 版本是 2020.x 的,而网上的游戏项目基本上只支持到 2019.x,所以这里我是借用了 unity 官方游戏项目 Platform 的资源和对象组织结构的图
![](https://i-blog.csdnimg.cn/blog_migrate/a710f219fd5c92b3d19d20c04f5147e0.png)
可见资源的组织是类似文件夹目录的结构,主要是根据类型来组织,比如 Scripts 文件和 Scenes 文件分别在不同的文件夹中。主要包括 Prefabs 预设, Resources 动态加载的资源文件,Scenes 场景文件,Scripts 脚本代码文件,Textures 贴图文件等
而游戏对象 GameObjects 则是以层次结构来组织,父对象可由子对象组合而成。
3.编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
- 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
- 常用事件包括 OnGUI() OnDisable() OnEnable()
答:
-
基本行为
① Awake:在游戏开始前初始化变量或游戏状态,在脚本整个生命周期内只被调用一次
② Start:第一次进入游戏循环时被调用
③ FixedUpdate:固定帧更新,被游戏循环调用
④ Update:正常帧更新,被游戏循环调用
⑤ LateUpdate:在所有Update函数调用后被调用 -
常用事件
① OnGUI:在渲染和处理GUI事件时调用
② OnDisable:当游戏对象被禁用时调用
③ OnEnable:在游戏对象被启用时调用
我们创建一个游戏对象,并为其添加TestBehavior脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestBehavior : MonoBehaviour
{
void Awake()
{
Debug.Log("Awake");
}
void Start()
{
Debug.Log("Start");
}
void FixedUpdate()
{
Debug.Log("FixedUpdate");
}
void Update()
{
Debug.Log("Update");
}
void LateUpdate()
{
Debug.Log("LateUpdate");
}
void OnGUI()
{
Debug.Log("OnGUI");
}
void OnEnable()
{
Debug.Log("OnEnable");
}
void OnDisable()
{
Debug.Log("OnDisable");
}
}
运行后终端会得到下列输出
4.查找脚本手册,了解 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 Scenes中所有实例的基类
- 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.
用来定义游戏对象的位置、旋转、以及缩放比例
- Component
Base class for everything attached to GameObjects.
是所有附加于GameObjects上内容的基类
②
- table的对象是GameObject。属性第一个选择框是activeSelf,用于确定是否启用该对象,能触发事件OnEnable、OnDisable;右边的是name属性;再往右边的勾选框是static属性,用于指定游戏对象是否为静态;位于第二行的是Tag和Layer属性,分别指对象的所属标签和所在的层;第三行的是Prefab属性,用于设置该对象的预设
- table的Transform由Position、Rotation、Scale三个属性构成。当前分别是位置 (-2,0,0),旋转角度 (0,0,0),长宽高 (1,0.2,1)
- table的Component部件有Transform、Cube(Mesh Filter)、Box Collider、Mesh Renderer、Default-Material、chair1、chair2、chair3、chair4
③
下图是 GameObject 、Component 和 Transform 的 UML 关系图。
5.资源预设(Prefabs)与 对象克隆 (clone)
- 预设(Prefabs)有什么好处?
- 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
- 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
答:
① 预设的好处
- 相同的游戏对象可以用同个预设来创建,提高对资源的复用率,从而提高开发效率
- 对预设进行修改后,其所创建的所有游戏对象都会发生改变
② 预设与对象克隆
- 预设创建的实体会根据预设的变化而变化,而对象克隆的实体不会因为原实体的变化而改变
③ 制作table预制,写一段代码将table预制资源实例化成游戏对象
用 Cube 创建名为 Table 的物品,并在其下新建四个 Cube 作为椅子,最终拖到 Assets 制作为 Prefab 预制件
取消勾选Table对象,新建一个空物品GameObject,并添加脚本CreateTable,之后将Table预制拖动到上面
CreateTable Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CreateTable : MonoBehaviour
{
public GameObject table;
void Start()
{
GameObject instance = Instantiate(table);
instance.transform.position = new Vector3(0, Random.Range(0, 3), 0);
instance.transform.parent = this.transform;
}
void Update() { }
}
点击运行,可在 Game 栏目观察到我们根据Table预制件初始化一个实例,并将其显示
二、 编程实践,小游戏
- 游戏内容: 井字棋 或 贷款计算器 或 简单计算器 等等
- 技术限制: 仅允许使用 IMGUI 构建 UI
- 作业目的:
了解 OnGUI() 事件,提升 debug 能力
提升阅读 API 文档能力
由于篇幅问题,井字棋设计请移步另一篇博客:
https://blog.csdn.net/qq_43256905/article/details/108733412
三、思考题【选做】
1.微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。
- 为什么是“模板方法”模式而不是“策略模式”呢?
答:
- 模板方法思想:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中具体实现。它使得子类可以在不改变一个算法的结构的前提下,重定义该算法的某些特定步骤。
- 策略模式思想:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,而不影响客户端的使用。
和策略模式相比,模板方法更强调算法流程是按照特定顺序被执行的,程序开发者对于流程上的可变点的访问是受限制的。
对于微软 XNA 引擎,它已经定义好了游戏循环,即算法流程的执行顺序,为了避免开发者修改算法流程顺序,所以应该采用“模板方法”,而不是“策略模式”。
2.将游戏对象组成树型结构,每个节点都是游戏对象(或数)。
- 尝试解释组合模式(Composite Pattern / 一种设计模式)。
- 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
答:
① 组合模式是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。它创建了对象组的树形结构。
② BroadcastMessage:我们借用上文的 table 游戏对象
实现这个方法,向子对象发送消息。
- 为 Table 添加
TableBehaviour
脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TableBehaviour : MonoBehaviour
{
void Update()
{
if (Input.anyKeyDown)
{
this.BroadcastMessage("BroadcastReceive", "Hello");
}
}
}
- 为chair 1-4 添加
ChairBehaviour
脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChairBehaviour : MonoBehaviour
{
private void BroadcastReceive(string message)
{
Debug.Log(message);
}
}
运行后观察日志,得到以下输出,可知实现了 BroadcastMessage() 向子对象发送信息
3.一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component)。
- 这是什么设计模式?
- 为什么不用继承设计特殊的游戏对象?
答:
① 设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component),这属于装饰器设计模式,动态地给一个对象添加一些额外的职责,用的是组合思想
② 之所以不用继承设计特殊的游戏对象,是因为继承模式相比于组合模式,有以下缺点:
- 继承的灵活性不如组合。
- 继承不利于进行测试。
- 继承不利于封装。如果子类依赖父类的行为,子类将变得脆弱。一旦父类行为发生变化,子类也将受到影响。