离散仿真引擎基础作业与练习

作业内容



一、简答题

1. 解释 GameObjects 和 Assets 的区别与联系

  • 游戏对象(GameObjects) :游戏对象是unity场景中的所有实例的基类。GameObjects本身并不能完成很多工作,但他们通过作为组件的容器来实现真正的功能。
  • 资源:资源是可以在游戏或项目中使用的任何item的表示,例如3d模型、音频文件、图像或渲染纹理等。
  • 区别与联系:对象是一些资源的集合体,是资源整合的具体表现;资源可以被多个对象使用。游戏对象一般为玩家,敌人,环境等,而资源可组成游戏中所有的对象、被多个对象使用、作为模版实例化游戏中具体的对象。

2. 下载几个游戏案例,分别总结资源、对象组织的结构

  • 资源以文件夹(目录)的形式来组织结构,按照文件类型分类我们可以把他们放到不同的文件夹。资源的目录组织结构主要包括动画,文本,场景,素材,模型,预设以及使用说明等。
  • 游戏对象树通过层次结构来组织,通过整体-部分的关系构成层次结构,主要包括摄像机包,游戏开始位置,场景布局以及文本管理等。

3. 使用 debug 验证 MonoBehaviour 基本行为或事件触发条件

  • 基本行为: Awake() Start() Update() FixedUpdate() LateUpdate()
  • 常用事件: OnGUI() OnDisable() OnEnable()

测试代码如下:
离散仿真引擎基础作业与练习
把上面的脚本加入到一个对象中运行,控制台结果如下:
离散仿真引擎基础作业与练习
结合上面的运行结果以及官方文档中对这几个函数作用机理的解释,可以知道函数的运行顺序以及触发条件分别是:

  • awake: 当该脚本实例被载入时Awake被调用。(只会被调用一次)
  • OnEable():当对象被启用并激活状态时此函数被调用
  • Start():当且仅在当第一次脚本启用Update方法被调用之前调用。(只会被调用一次)
  • Update():当MonoBehaviour启用时,其Update在每一帧被调用
  • FixedUpdate():按照设置的时间固定频率来循环更新
  • LateUpdate():LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。
  • OnGUI():渲染和处理GUI事件时调用。
  • OnDisable():当对象变为不可用或非激活状态时此函数被调用。
  • OnEnable():当对象被启用并激活状态时此函数被调用

4. 了解 GameObject、Transform、Component 对象

  • 分别翻译官方对三个对象的描述

    • 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.
      所有附加gameobjects的内容的基类

  • 描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
    离散仿真引擎基础作业与练习
    如图所示

    • table 实体的属性:
      activeInHierarchy:定义游戏对象是否在场景中处于活动状态。
      activeSelf:此游戏对象的本地活动状态。(只读)
      isStatic:仅限编辑器的API,用于指定游戏对象是否是静态的。
      layer:游戏对象所在的层。
      scene:游戏对象所属的场景。
      tag:游戏对象的标签。
      transform:附加到此游戏对象的transform
    • table transform 的属性:从右边 transform 面板中可以看到,transform 显示对象的位置、旋转角度,以及大小。table此时位于位置(0,0,0),旋转角度(0,0,0),大小(长宽高)为(1,1,1)。更多属性可以在官方文档中查询。
    • table 的部件:Mesh Filter、Box Collider、Mesh Renderer

  • 用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
    离散仿真引擎基础作业与练习

5. 整理相关学习资料,编写简单代码验证以下技术

  • 查找对象

    通过对象名称(Find方法) static GameObject Find (string name)
    通过标签获取单个游戏对象 static GameObject FindWithTag (string tag)
    通过标签获取多个游戏对象 static GameObject[] FindGameObjectsWithTag (string tag)
    通过类型获取单个游戏对象 static Object FindObjectOfType(Type type)
    通过类型获取多个游戏对象 static Object FindObjectsOfType(Type type)

    使用名称和标签分别编写简单代码进行查找
    离散仿真引擎基础作业与练习
    结果如下,查找成功
    离散仿真引擎基础作业与练习

  • 添加子对象

    函数:static GameObect CreatePrimitive(PrimitiveTypetype)

    使用代码在table下新建一把椅子
    离散仿真引擎基础作业与练习
    可以看到此时table下多了一个子对象new_chair,同时图像上也多出了一把椅子
    离散仿真引擎基础作业与练习

  • 遍历对象树
    代码
    离散仿真引擎基础作业与练习
    运行,可以看到输出了table下的四个子对象的名字
    离散仿真引擎基础作业与练习

  • 清除所有子对象
    代码
    离散仿真引擎基础作业与练习
    执行,可以看到原先table下的四张椅子都已经被清除了
    离散仿真引擎基础作业与练习

6. 资源预设(Prefabs)与 对象克隆 (clone)

  • 预设(Prefabs)有什么好处?
    预设是组件的集合体 , 预设可以提前将设计游戏中所需要的游戏对象进行设计打包,成为一个模板。在设计的过程中,随时可以直接从资源当中加载,成为一个游戏对象。因此,创建预设体可以帮助我们很方便的重复创建具有相同结构的游戏对象。

  • 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 的关系?
    预设是一个非常容易复用的类模板,可以迅速方便创建大量相同属性的对象。一旦需要修改所有相同属性的对象,只需要修改预设即可,所有通过预设实例化的对象都会做出相应变化。克隆只是复制一个一模一样的对象,这个对象独立于原来的对象,在修改的过程中不会影响原有的对象。

  • 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
    离散仿真引擎基础作业与练习
    在执行前需要把预制的table拖拽到右边脚本对应的位置obj上。
    离散仿真引擎基础作业与练习
    运行,可以看到table被成功实例化了
    离散仿真引擎基础作业与练习



二、编程实践—简单计算器

游戏内容: 简单计算器
技术限制: 仅允许使用 IMGUI 构建 UI
作业目的:

  • 提升 debug 能力
  • 提升阅读 API 文档 能力

1. 计算器的预期功能

  • 对数字进行加减乘除四种基本运算
  • 可以回退及清零
  • 拥有小数处理的能力

2. 计算器的实现步骤(代码)

  • 构建计算器的UI界面
    离散仿真引擎基础作业与练习
    OnGUI函数在前面已经介绍过,函数中的代码可以帮助我们在unity上创建一个简易的计算器的UI,其中变量compute_string用来保存需要计算的表达式。函数具体使用以及参数可以通过上面的链接参阅API文档。

  • 计算器功能实现
    为了让计算器能够有我们想要的功能,还需要添加几个函数进行支持。

    • 计算的具体实现函数 compute
      compute 函数对要计算的表达式 str 做处理,并返回计算结果。计算的大致流程为:预处理(分离数字与操作符)–>对优先级高的操作符进行计算(第一个for循环)–>对优先级低的操作符进行计算(第二个for循环)。
      其中,预处理的结果为,将数字和操作符按照输入顺序分别保存在List<float> numbersList<char> operators上;对操作符进行运算包括:取对应操作符两边的数字(如操作符位置为0,那么对应的操作数为 numbers[0] 和 numbers[1] ),保存运算结果,将对应位置的操作数删除/取代为新的计算结果,删除已完成计算的操作符,取操作符的 index 前移一位。其中最后一步是为了在删除操作符后下一次循环执行时能够取到正确的值。
      离散仿真引擎基础作业与练习

    • 预处理函数 pre_treat
      函数比较简单,处理的结果为分别保存数字和操作符。
      离散仿真引擎基础作业与练习

    • 判断函数is_number & is_operator
      很简单的两个函数,主要是辅助之前的操作。
      离散仿真引擎基础作业与练习

3. 在unity中查看运行效果

嗯,是一个一点都不花里胡哨的计算器。
离散仿真引擎基础作业与练习
经过检验计算器的使用是没问题的,就是界面“简易”了点。不过本次作业比较多,所以就暂且做到这里啦~



三、思考题

1. 微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”

  • 为什么是“模板方法”模式而不是“策略模式”呢?
    模板方法模式的主要思想是定义一个算法流程,并将一些特定步骤的具体实现延迟到子类。这使得可以在不改变算法流程的情况下,通过不同的子类来实现“定制”流程中的特定步骤。
    策略模式的主要思想是使不同的算法可以被相互替换,不影响客户端的使用。
    将模板方法和策略模式对比可以发现,模板方法更加强调算法流程是按照特定顺序被执行的,只不过其中某些部分可以变化(具体实现在子类中完成)。其实现主要是通过定义受保护的虚函数来定义可变化的地方。
    这样来看,微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节并使用一组虚方法让继承者完成的方法是完全符合模板方法定义的。

2. 将游戏对象组成树型结构,每个节点都是游戏对象(或数)

  • 试解释组合模式(Composite Pattern / 一种设计模式)。
    组合模式(Composite Pattern),又叫部分整体模式,用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次,创建了对象组的树形结构。

  • 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
    离散仿真引擎基础作业与练习
    如图,控制台中输出了预期结果
    离散仿真引擎基础作业与练习

3. 一个游戏对象用许多部件描述不同特征。我们设计Tank游戏对象不是继承于GameObject对象,而是GameObject 添加一组Component。

  • 是什么设计模式?
    装饰器模式(Decorator Pattern)

  • 为什么不用继承设计特殊的游戏对象?
    继承是实现类复用的重要手段,但却不是唯一的手段,通过类的关联组合同样可以做到,而且如果使用得当,会比通过继承更富有弹性。“装饰器模式”就是通过组合来实现类似复用和包装,这就是OO设计的另一个原则“合成复用原则”:尽量使用合成/聚合的方式,而不是使用继承。
    装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。它作为现有的类的一个包装,创建了一个装饰类用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

上一篇:Unity学习日志


下一篇:Unity之C#学习笔记(17):对象池模式 Object Pooling