3D游戏作业2
一.简答题
1.解释游戏对象(GameObjects)和资源(Assets)的区别与联系。
unity对资源和游戏对象的解释如下
资源:可以在游戏或项目中使用的任何媒体或数据。资源可能来自 Unity 外部创建的文件,例如 3D 模型、音频文件、图像。还可以在 Unity 中创建一些资源类型,例如 Animator Controller、混音器 (Audio Mixer) 或渲染纹理 (Render Texture)。
游戏对象:Unity 场景中的基础对象,可以表示角色、道具、风景、摄像机、路径点等。游戏对象的功能由附加到游戏对象的组件来定义。
从中我们可以总结出两者的区别和联系是:游戏对象是游戏里的独立单位,可以由多个资源构成,比如上述游戏对象里举例的风景就由3D模型,图像等资源构成。而资源可以说是构成游戏对象的模板,而且可以被多个游戏对象使用,比如游戏里的不同人物可能是在同一个3D模型上做修改所形成的。
2.下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
资源的目录组织结构如下,其中一般包括字体,材料,模型,场景等文件夹,而各种资源就分类存在这些文件夹里
游戏对象树包含了当前场景中所有游戏对象,且是树状结构,即父节点会分出子节点,子节点又有新的子节点,层层构成了对象树。
3.编写一个代码,使用debug 语句来验证MonoBehaviour基本行为或事件触发的条件
-
基本行为包括Awake() Start() Update() FixedUpdate() LateUpdate()
-
常用事件包括OnGUI() OnDisable() OnEnable()
首先执行一遍代码
由图可以看出,Start()和Awake()和OnEnable()只执行了一次,而FixedUpdate(),Update(),LateUpdate(),OnGUI(),一直在执行,其中FixedUpdate()执行的次数较少,而Update()和LateUpdate()执行的次数较多,OnGUI()执行的次数最多。查询官网对相关事件的解释,可以知道这些行为和事件触发的条件。
start()和awake():这两个行为有一些相似之处,容易混淆,比如他们都只调用一次且都在update()调用之前。但是他们却是不同的,awake()是在对象初始化后就立即调用,而start()只保证在update()之前调用,一般都在awake()之后调用,并且如果在awake()调用之后立即将enable设置为false,禁止update(),那么start()不会被调用。
OnEnable:在激活状态下(即awake()调用之后)对象启用后调用,与start()和awake()的区别是对象被取消激活并重新激活后,start()和awake()不会再次被调用,而OnEnable()会被再次调用。下图是重新激活后的显示。
Update()和LateUpdate():这两个基本行为都是每帧都调用,lateupdate()在update()完全执行完成之后调用,lateupdate() 的常见用途是跟随第三人称摄像机。如果在updape()内让角色移动和转向,可以在lateupdate()中执行所有摄像机移动和旋转计算。这样可以确保角色在摄像机跟踪其位置之前已完全移动。
OnGui():该函数每帧都会调用多次以响应GUI事件。
Fixedupdate():具有物理系统的频率,每个固定帧率帧调用该函数。
Ondisable():行为被禁用或处于非活动状态时,调用此函数。
4.查找脚本手册,了解 GameObject,Transform,Component对象
-
分别翻译官方对三个对象的描述(description)
Gameobject:Base class for all entities in Unity Scenes.1
GameObject:unity界面中所有实体类的基类
Transform:Position, rotation and scale of an object.2
Transfrom:用于存放游戏物体的位置,旋转角度,缩放。
Component:Base class for everything attached to GameObjects.3
Component:附加到GameObjects的所有内容的基类。
-
描述下图中table对象(实体)的属性、table的Transform的属性、table的部件
第一个勾选框表示该对象是否存在scene中,如果取消勾选在scene中该对象就会消失;右边的文本框是对象的名字,再右边是将对象设置为静态与否,如果打钩,对象就在游戏过程中不会移动。下边的tag可以设置是否给对象添加标签,layer可以给对象设置涂层,prefab是预制,可以给对象预制。overrides是覆盖。
transform中有三个属性,Position是对象的位置,Rotation是旋转角度,Scale是对象的大小。
table的部件有如下四个。
资源预设(Prefabs)与对象克隆(Clone)
-
预设(prefabs)有什么好处
预制就是将一个通用的游戏对象设置为预制体,好处是能够以这个通用的游戏对象为类模板,在其基础上做一些修改和拓展,从而不断得到新的对象,能够节省许多重复创建对象和设置属性的时间,大大提升了工作效率。
-
预设与对象克隆(clone or copy or Instantiate of Unity Object)关系?
预设本身不需要有实例化的游戏对象,而克隆需要复制实例化的游戏对象。而且如果要集中修改通过将预设实例化创建出来的对象,只需要修改预设就能全都修改,方便批量修改。而如果要修改克隆出来的对象只能一个一个修改。
-
制作table预制,写一段代码将table预制资源实例化成游戏对象
GameObject,Transform,Component对象三者的关系如下
二 计算器
运行效果:
代码如下
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class cccal : MonoBehaviour { public GUIStyle buttonstyle; string str=""; void OnGUI(){ //创建GUI GUI.Box(new Rect (Screen.width/2-100,Screen.height/2-160, 235, 50), str); if (GUI.Button (new Rect (Screen.width/2-100,Screen.height/2-45,55,55), "1")) { str += "1"; } if (GUI.Button (new Rect (Screen.width/2-40,Screen.height/2-45,55,55), "2")) { str += "2"; } if (GUI.Button (new Rect (Screen.width/2+20,Screen.height/2-45,55,55), "3")) { str += "3"; } if (GUI.Button (new Rect (Screen.width/2-100,Screen.height/2+10,55,55), "4")) { str += "4"; } if (GUI.Button (new Rect (Screen.width/2-40,Screen.height/2+10,55,55), "5")) { str += "5"; } if (GUI.Button (new Rect (Screen.width/2+20,Screen.height/2+10,55,55), "6")) { str += "6"; } if (GUI.Button (new Rect (Screen.width/2-100,Screen.height/2+65,55,55), "7")) { str += "7"; } if (GUI.Button (new Rect (Screen.width/2-40,Screen.height/2+65,55,55), "8")) { str += "8"; } if (GUI.Button (new Rect (Screen.width/2+20,Screen.height/2+65,55,55), "9")) { str += "9"; } if (GUI.Button (new Rect (Screen.width/2+80, Screen.height/2+10 , 55, 55), "C")) { str =""; } if (GUI.Button (new Rect (Screen.width/2-100,Screen.height/2+120,115,55), "0")) { str +="0"; } if (GUI.Button (new Rect (Screen.width/2+20,Screen.height/2+120,55,55), ".")) { str +="."; } if (GUI.Button (new Rect (Screen.width/2-100,Screen.height/2-100,55,55), "+")) { str +="+"; } if (GUI.Button (new Rect (Screen.width/2-40,Screen.height/2-100,55,55), "-")) { str +="-"; } if (GUI.Button (new Rect (Screen.width/2+20,Screen.height/2-100,55,55), "x")) { str +="*"; } if (GUI.Button (new Rect (Screen.width/2+80,Screen.height/2-100,55,55), "/")) { str +="/"; } if (GUI.Button (new Rect (Screen.width/2+80, Screen.height/2-45 , 55, 55), "back")) { if (str.Length > 0) str = str.Remove(str.Length-1,1) ; } if (GUI.Button (new Rect (Screen.width/2+80,Screen.height/2+65,55,115), "=")) { mycocluator c = new mycocluator (); str = c.calculate(str); } } public class mycocluator{ float[] num = new float[100]; int top1 = -1;//用于记录num中使用到的个数 Stack<double> num2=new Stack<double>(); Stack<char> cal2=new Stack<char>(); char[] cal = new char[100]; int top2 = -1;//用于记录cal中使用到的个数 bool flag=false;//用于标记第一个数字是否为负数 public string calculate(string s) { int i = 0; if(i==0 && s[i]=='-') { flag=true; i++; } //先遍历一遍字符串,找出其中的计算符 while (i < s.Length) { switch (s [i]) { case '+': cal [top2 + 1] = '+'; top2++; break; case '-': cal [top2 + 1] = '-'; top2++; break; case '*': cal [top2 + 1] = '*'; top2++; break; case '/': cal [top2 + 1] = '/'; top2++; break; } int j = 0; //用于记录当前遍历到的数字 string tmpnum = ""; //找出字符串中的数字 while (((i+j) < s.Length) && (s [i + j] == '.' || (s [i + j] <= '9' && s [i + j] >= '0'))) { tmpnum += s [i + j]; j++; } if (j != 0) { float temp = float.Parse (tmpnum); top1++; if(top1==0 && flag==true) { num [top1] = 0-temp; } else { num [top1] = temp; } i += j; } else { i++; } } //开始计算,如果遇到*或者/就先计算出乘积或商再入栈。 int l=0; num2.Push(num[0]); for(int k=1;k<=top1;k++) { if(cal[l]=='*') { double tmp=num2.Peek(); num2.Pop(); double tmp2=num[k]; num2.Push(tmp*tmp2); } if(cal[l]=='/') { double tmp=num2.Peek(); num2.Pop(); double tmp2=num[k]; num2.Push(tmp/tmp2); } else if(cal[l]=='+'||cal[l]=='-') { cal2.Push(cal[l]); num2.Push(num[k]); } l++; } //由于栈是反向的,所以可能出现1-3-3被当成1-(3-3)的情况,所以将栈倒置 ArrayList nnum=new ArrayList(); ArrayList ccal=new ArrayList(); while(num2.Count>0) { nnum.Add(num2.Peek()); num2.Pop(); } while(cal2.Count>0) { ccal.Add(cal2.Peek()); cal2.Pop(); } for(int k=0;k<nnum.Count;k++) { num2.Push((double)nnum[k]); } for(int k=0;k<ccal.Count;k++) { cal2.Push((char)ccal[k]); } //对只剩下+和-的进行栈计算 while(num2.Count>1) { double a=num2.Peek(); num2.Pop(); double b=num2.Peek(); num2.Pop(); char c=cal2.Peek(); cal2.Pop(); if(c=='+') { double d=a+b; num2.Push(d); } if(c=='-') { double d=a-b; num2.Push(d); } } flag=false; return num2.Peek().ToString(); } } // Use this for initialization void Start () { } // Update is called once per frame void Update () { } }
[1] Unity - Scripting API: GameObject