离散仿真引擎基础作业与练习(一)
一、简答题
解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系。
答:
区别:游戏对象指的是游戏中的对象比如一个npc,一幕场景。游戏对象本身什么都不做,但是它是是组件的容器,可以通过添加组件使游戏对象具有某些特性从而成为游戏中的对象。
资源相当于一种模板,可以在asset store中下载(或者自己另外添加)来使用,包括图片、音频、视频、代码文件等。
联系:资源可以作为模板创建对象;游戏对象可以作为资源保存。下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
答:
asset以文件夹(目录)的形式来组织,按照文件类型分类我们可以把他们放到不同的文件夹,在unity客户端的project选项卡中可以看到这种组织结构:
对象通过层次结构来组织,通过整体-部分的关系构成层次结构,在unity的hierarchy选项卡中可以看到这种组织结构:
编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
常用事件包括OnGUI() OnDisable() OnEnable()答:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MyMonoBehaviourTestScript : MonoBehaviour { void Start () { Debug.Log("test start"); } void Update () { Debug.Log("test update"); } private void Awake() { Debug.Log("test awake"); } private void FixedUpdate() { Debug.Log("test fixedupdate"); } private void LateUpdate() { Debug.Log("test lateupdate"); } private void OnGUI() { Debug.Log("test ongui"); } private void OnDisable() { Debug.Log("test ondisable"); } private void OnEnable() { Debug.Log("test enable"); } }
程序启动:
程序运行的每一帧:
程序结束:
分析:
启动时awake首先被调用,然后start在所有的update之前被调用,之后enable被调用保证object被渲染;
运行时的每一帧,都会调用FixedUpdate,Update,LateUpdate,OnGUI,其中当MonoBehaviour启用时,其Update在每一帧被调用。当MonoBehaviour启用时,其 FixedUpdate在每一帧被调用。区别是:update跟当前平台的帧数有关,而FixedUpdate是真实时间,Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关以及被渲染的物体(可以认为是三角形的数量)。这会导致同一个游戏在不同的机器上效果不一致(不同设定下也不同),有的快有的慢。因为Update的执行间隔不一样了。而FixedUpdate,是在固定的时间间隔执行,不受游戏帧率的影响。查找脚本手册,了解 GameObject,Transform,Component 对象
- 分别翻译官方对三个对象的描述(Description)
答:
GameObject 游戏物体,是Unity场景里面所有实体的基类;
Transform指的是一个组件(component),用来控制物体的位置、旋转和缩放。每一个GameObject都可以添加Transform用来储存并它的位置、旋转和缩放。每一个Transform可以有一个父级,它的变换是相对与它的父级的。可以在Hierarchy面板查看层次关系:
Component 组件,一切附加到游戏对象的基类,变换(transform)、脚本、音频都可以算是组件,可以添加到游戏对象中; 描述下图中 table 对象(实体)的属性、table 的 Transform的属性、 table 的部件
本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。答:
table对象的属性: static、layer、tag、prefab等;
table的transform属性: position:(0,0,0),rotation(0,0,0),scale(1,1,1);
table的组件: transform、mesh render、box colider、cube(mesh filter)等;- 用UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
- 分别翻译官方对三个对象的描述(Description)
整理相关学习资料,编写简单代码验证以下技术的实现:
查找对象
static GameObject Find (string name)
name指的是是单个的对象的名字,也可以是hierarchy中的一个路径名,如果找到会返回该对象,如果找不到就返回;
static GameObject FindWithTag (string tag)
根据tag属性查找单个对象,如果有某个对象以的tag属性是符合的返回该对象,没有则返回null。
static GameObject[] FindGameObjectsWithTag (string tag)
根据tag属性查找对象集合,如果有某个/多个对象以的tag属性是符合的返回这些对象的集合,没有则返回null。
static Object FindObjectOfType(Type type)
根据type查找单个游戏对象,如果有某个对象是type类型的返回该对象(该对象是查找到的第一个类型为type的对象),没有则返回null。
FindObjectsOfType方法:static Object FindObjectsOfType(Type type)
根据type查找游戏对象集合,返回所有类型为type的游戏对象的集合,没有则返回null。
简单代码验证:var cube1 = GameObject.Find("Cube/Cube1"); if (null != cube1) { Debug.Log("find cube1 by path"); } cube1 = GameObject.Find("Cube1"); if (null != cube1) { Debug.Log("find cube1 by name"); } cube1 = GameObject.FindWithTag("test"); if (null != cube1) { Debug.Log("find cube1 by tag"); }
运行结果:
从图片中可以看出我们分别通过路径、tag属性、name属性进行了查找而且都成功查找添加子对象
答:GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube); cube.name = "Cube5"; cube.transform.position = new Vector3(-2, -2, -2);
运行前:只有四个方块,目录下也只有四个
运行后:产生了第五个方块,目录新增Cube:
遍历对象树
foreach (Transform child in transform) { Debug.Log("childname:"+child.name); }
这一段代码打印所有的child的名字,运行结果如下:
清除所有子对象
foreach (Transform child in transform) { GameObject.Destroy(child); } int count = 0; foreach (Transform child in transform) { count++; } Debug.Log(count);
这一段代码清除子对象之后打印结果,结果表明所有子对象都被删除了:
资源预设(Prefabs)与对象克隆 (clone)
- 预设(Prefabs)有什么好处?
利用预设可以快速生成相同对象,而且修改预设就可以完成对生成对象的修改,就不需要在后期优化时在多个地方修改了,这样可以大大节省开发时间; - 预设与对象克隆 (clone or copy or Instantiateof Unity Object) 关系?
预设与克隆都能创建出相同的对象。预设创建出的对象与源预设还有联系,源预设修改了由预设创建的对象也会被修改(也就是说他们之间有某种东西链接着)。克隆出的对象与源对象不再有联系,克隆就仅仅是把原本对象复制了一份而已。 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
GameObject prefabTable = (GameObject)Instantiate(Resources.Load("table")); prefabTable.transform.position = new Vector3(3, 3, 3);
运行结果:在指定位置创建了新的“table”
注意这里一定要把预制的东西放到一个叫做resources的文件夹下才能被resource.load读到
- 预设(Prefabs)有什么好处?
尝试解释组合模式(Composite Pattern / 一种设计模式)。使用 BroadcastMessage() 方法向子对象发送消息
查阅相关资料得知:组合模式—-将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。通俗点讲就是在组合模式下,处理单个对象和组合对象的方法、思想都是一样的。这样使我们可以使用处理简单事件的方法和思想来解决复杂的事情,从而使事情得到简化。比如我们使用计算机但是并不需要自己手动编辑每一个指令,我们只需要从键盘鼠标输入,然后由下面一层完成它的操作(下面一层又把它的工作分给再下面一层,不断如此使得每一层需要完成的事情都比较简单)。
向子对象发送消息:
创建parent.cs和child.cs,绑定到同一个object
其中parent.cs:using System.Collections; using System.Collections.Generic; using UnityEngine; public class parent : MonoBehaviour { string message = "I'm your father"; // Use this for initialization void Start () { this.BroadcastMessage("MyChild", message); } // Update is called once per frame void Update () { } }
其中child.cs:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class child : MonoBehaviour { // Use this for initialization void Start () { } void MyChild(string message) { Debug.Log("My father said to me:\""+message+"\""); } // Update is called once per frame void Update () { } }
最后运行结果:即parent讲message传给了子类函数Mychild,并且由子类调用了这个函数