1、简答题
解释游戏对象(GameObjects)和资源(Assets)的区别与联系
游戏对象(GameObjects)
Unity 场景中的基础对象,可以表示角色、道具、风景、摄像机、路径点等。游戏对象的功能由附加到游戏对象的组件来定义。
资源(Assets)
可以在游戏或项目中使用的任何媒体或数据。资源可能来自 Unity 外部创建的文件,例如 3D 模型、音频文件、图像。还可以在 Unity 中创建一些资源类型,例如 Animator Controller、混音器 。(Audio Mixer) 或渲染纹理 (Render Texture)。
两者区别和联系
资源是可以被多个游戏对象使用的,本身也可以进行实例化。对比起游戏对象,资源更像是集成的可扩展的模板包。
下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织与游戏对象树的层次结构)
资源结构
主要包括动画,文本,场景和一些使用说明等。
游戏对象树的层次
包括摄像机,开始位置,场景布局以及文本等。
编写一个代码,使用debug语句来验证MonoBehavior基本行为或事件触发的条件
①基本行为包括Awake() Start() Update() FixedUpdate() LateUpdate()
②常用事件包括OnGUI() OnDisable() OnEnable()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonoBehaviourTest : MonoBehaviour
{
void Awake()
{
Debug.Log("Awake!");
}
// 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 OnGUI()
{
Debug.Log("OnGUI!");
}
void OnDisable()
{
Debug.Log("OnDisable!");
}
void OnEnable()
{
Debug.Log("OnEnable!");
}
}
效果:
总结
- Awake():始终在任何 Start 函数之前并在实例化预制件之后调用此函数。
- OnEnable():(仅在对象处于激活状态时调用)在启用对象后立即调用此函数。
- Start():仅当启用脚本实例后,才会在第一次帧更新之前调用 Start。
- OnGUI():每帧调用多次以响应 GUI 事件。首先处理布局和重新绘制事件,然后为每个输入事件处理布局和键盘/鼠标事件。
- FixedUpdate():在MonoBehaviour启用之后在每一个固定的framerate frame中被调用;
- LateUpdate():在Behaviour是enabled的情况下在每一frame被调用;
- OnDisable():行为被禁用或处于非活动状态时,调用此函数;
- Update():每帧调用一次。这是用于帧更新的主要函数。
查找脚本手册,了解GameObject,Transform, Component对象
①分别翻译官方对三个对象的描述(Description)
GameObject
Unity 的 GameObject 类用于表示任何可以存在于场景中的事物。GameObject 是 Unity 中场景的构建块,可充当用于确定 GameObject 外观以及 GameObject 作用的功能组件的容器。
Transform
Transform 类提供多种方式来通过脚本处理游戏对象的位置、旋转和缩放,以及与父和子游戏对象的层级关系。
Component
组件是每个游戏对象的功能部分。组件包含一些属性,可以编辑这些属性来定义游戏对象的行为。一个游戏对象可以有多个组件,但每个游戏对象都有一个转换组件。
②描述下图中table对象(实体)的属性、table的Transform的属性、table的部件
对象的属性是GameObject;
- 第一个选择框是activeSelf:可以定义对象的名称,动静态等属性
- 第二个选择框是Transform:可以定义对象的位置、面向方向、大小
- 第三个选择框是Box Collider:可以调整坐标系的位置、大小
- 第四个选择框是Component:可以给对象增加行为
③用UML图描述三者的关系
资源预设(Prefabs)与对象克隆(clone)
①预设(Prefabs)有什么好处?
- 使用预设可在多个组件和资源之间重用属性设置。
- 使用预设还可指定新组件的默认属性以及资源的导入设置。
- 预设可用于简化团队的工作流程。
②预设与对象克隆的关系?
- 克隆游戏对象需要场景中有被克隆对象,而创建预制只需事先创建预制即可,允许场景中一开始并不存在该游戏对象。
- 克隆出来的游戏对象并不会随着被克隆体的变化而发生变化,但是使用预制创建出来的对象会随着预制的改变而发生改变。
2、编程实践:简易计算器
using UnityEngine;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System;
using System.Linq;
public class calculate : MonoBehaviour
{
//利用正则表达式判断是否合法
public static bool IsNumeric(string value)
{
return Regex.IsMatch(value, @"^[+-]?\d*[.]?\d*$");
}
//用来显示结果
public string result = "";
//第一个操作数
public static string str1 = "";
//第二个操作数
public static bool haveDot = false;
//是否按了等于号
public static bool isCaclutate = false;
void OnGUI()
{
//对数字进行处理
if (GUI.Button(new Rect(100, 100, 100, 60), "CE"))
{
result = "";
str1 = "";
haveDot = false;
}
if (GUI.Button(new Rect(210, 100, 100, 60), "×") && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != ".")
{
if (IsNumeric(str1.Substring(str1.Length - 1, 1)))
{
Debug.Log(str1.Substring(str1.Length - 1, 1));
str1 += "*";
haveDot = false;
isCaclutate = false;
}
result = str1;
}
if (GUI.Button(new Rect(320, 100, 100, 60), "÷") && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != ".")
{
if (IsNumeric(str1.Substring(str1.Length - 1, 1)))
{
str1 += "/";
haveDot = false;
isCaclutate = false;
}
result = str1;
}
if (GUI.Button(new Rect(430, 100, 100, 60), "←"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
} else if (result.Length != 0)
{
str1 = str1.Substring(0, str1.Length - 1);
}
result = str1;
}
if (GUI.Button(new Rect(100, 170, 100, 60), "1"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "1";
result = str1;
}
if (GUI.Button(new Rect(210, 170, 100, 60), "2"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "2";
result = str1;
}
if (GUI.Button(new Rect(320, 170, 100, 60), "3"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "3";
result = str1;
}
if (GUI.Button(new Rect(430, 170, 100, 60), "-"))
{
if (IsNumeric(str1.Substring(str1.Length - 1, 1)) && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != ".")
{
str1 += "-";
haveDot = false;
isCaclutate = false;
}
result = str1;
}
if (GUI.Button(new Rect(100, 240, 100, 60), "4"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "4";
result = str1;
}
if (GUI.Button(new Rect(210, 240, 100, 60), "5"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "5";
result = str1;
}
if (GUI.Button(new Rect(320, 240, 100, 60), "6"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "6";
result = str1;
}
if (GUI.Button(new Rect(430, 240, 100, 60), "+") && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != ".")
{
if (IsNumeric(str1.Substring(str1.Length - 1, 1)))
{
str1 += "+";
haveDot = false;
isCaclutate = false;
}
result = str1;
}
if (GUI.Button(new Rect(100, 310, 100, 60), "7"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "7";
result = str1;
}
if (GUI.Button(new Rect(210, 310, 100, 60), "8"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "8";
result = str1;
}
if (GUI.Button(new Rect(320, 310, 100, 60), "9"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "9";
result = str1;
}
if (GUI.Button(new Rect(430, 310, 100, 130), "="))
{
var tmp = Evaluator.Eval(result);
Debug.Log(tmp.ToString());
result = tmp.ToString();
str1 = result;
isCaclutate = true;
if (result.Contains("."))
{
haveDot = true;
}
}
if (GUI.Button(new Rect(100, 380, 210, 60), "0"))
{
if (isCaclutate == true)
{
str1 = "";
isCaclutate = false;
haveDot = false;
}
str1 += "0";
result = str1;
}
if (GUI.Button(new Rect(320, 380, 100, 60), "."))
{
if (isCaclutate == true)
{
str1 = "0.";
isCaclutate = false;
}
if (IsNumeric(str1.Substring(str1.Length - 1, 1)) && str1.Substring(str1.Length - 1, 1) != "." && haveDot == false)
{
Debug.Log(str1.Substring(str1.Length - 1, 1));
str1 += ".";
haveDot = true;
isCaclutate = false;
}
result = str1;
}
GUI.TextArea(new Rect(100, 20, 430, 60), result);
}
// 动态求值
public class Evaluator
{
// 计算结果,如果表达式出错则抛出异常
// <param name="statement">表达式,如"1+2+3+4"</param>
// <returns>结果</returns>
public static object Eval(string statement)
{
if (statement.Trim() != string.Empty)
{
Evaluator evaluator = new Evaluator();
return evaluator.GetFormulaResult(statement);
}
else
{
return null;
}
}
private object GetFormulaResult(string s)
{
if (s == "")
{
return null;
}
string S = BuildingRPN(s);
string tmp = "";
System.Collections.Stack sk = new System.Collections.Stack();
char c = ' ';
System.Text.StringBuilder Operand = new System.Text.StringBuilder();
double x, y;
for (int i = 0;
i < S.Length;
i++)
{
c = S[i];
//added c==',' for germany culture
if (char.IsDigit(c) || c == '.' || c == ',')
{
//数据值收集.
Operand.Append(c);
}
else if (c == ' ' && Operand.Length > 0)
{
try
{
tmp = Operand.ToString();
if (tmp.StartsWith("-"))//负数的转换不被直接支持.
{
sk.Push(-((double)Convert.ToDouble(tmp.Substring(1, tmp.Length - 1))));
}
else
{
sk.Push(Convert.ToDouble(tmp));
}
}
catch
{
return null;
}
Operand = new System.Text.StringBuilder();
}
else if (c == '+'//运算符处理.双目运算处理.
|| c == '-'
|| c == '*'
|| c == '/'
|| c == '%'
|| c == '^')
{
if (sk.Count > 0)/*如果输入的表达式根本没有包含运算符.或是根本就是空串.这里的逻辑就有意义了.*/
{
y = (double)sk.Pop();
}
else
{
sk.Push(0);
break;
}
if (sk.Count > 0)
x = (double)sk.Pop();
else
{
sk.Push(y);
break;
}
switch (c)
{
case '+':
sk.Push(x + y);
break;
case '-':
sk.Push(x - y);
break;
case '*':
if (y == 0)
{
sk.Push(x * 1);
}
else
{
sk.Push(x * y);
}
break;
case '/':
if (y == 0)
{
sk.Push(x / 0);
}
else
{
sk.Push(x / y);
}
break;
case '%':
sk.Push(x % y);
break;
case '^':
if (x > 0)
{
sk.Push(System.Math.Pow(x, y));
}
else
{
double t = y;
string ts = "";
t = 1 / (2 * t);
ts = t.ToString();
}
break;
}
}
else if (c == '!')//单目取反. )
{
sk.Push(-((double)sk.Pop()));
}
}
if (sk.Count > 1)
{
return null;
}
if (sk.Count == 0)
{
return null;
}
return sk.Pop();
}
private string BuildingRPN(string s)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder(s);
System.Collections.Stack sk = new System.Collections.Stack();
System.Text.StringBuilder re = new System.Text.StringBuilder();
char c = ' ';
for (int i = 0;
i < sb.Length;
i++)
{
c = sb[i];
//added c==',' for german culture
if (char.IsDigit(c) || c == ',')//数字当然要了.
re.Append(c);
//if( char.IsWhiteSpace( c )||
char.IsLetter(c);//如果是空白,那么不要.现在字母也不要.
//continue;
switch (c)//如果是其它字符...列出的要,没有列出的不要.
{
case '+':
case '-':
case '*':
case '/':
case '%':
case '^':
case '!':
case '(':
case ')':
case '.':
re.Append(c);
break;
default:
continue;
}
}
sb = new System.Text.StringBuilder(re.ToString());
//对负号进行预转义处理.负号变单目运算符求反.
for (int i = 0; i < sb.Length - 1; i++)
if (sb[i] == '-' && (i == 0 || sb[i - 1] == '('))
sb[i] = '!';
//字符转义.
//将中缀表达式变为后缀表达式.
re = new System.Text.StringBuilder();
for (int i = 0;
i < sb.Length;
i++)
{
if (char.IsDigit(sb[i]) || sb[i] == '.')//如果是数值.
{
re.Append(sb[i]);
//加入后缀式
}
else if (sb[i] == '+'
|| sb[i] == '-'
|| sb[i] == '*'
|| sb[i] == '/'
|| sb[i] == '%'
|| sb[i] == '^'
|| sb[i] == '!')//.
{
//运算符处理
while (sk.Count > 0) //栈不为空时
{
c = (char)sk.Pop();
//将栈中的操作符弹出.
if (c == '(') //如果发现左括号.停.
{
sk.Push(c);
//将弹出的左括号压回.因为还有右括号要和它匹配.
break;
//中断.
}
else
{
if (Power(c) < Power(sb[i]))//如果优先级比上次的高,则压栈.
{
sk.Push(c);
break;
}
else
{
re.Append(' ');
re.Append(c);
}
//如果不是左括号,那么将操作符加入后缀式中.
}
}
sk.Push(sb[i]);
//把新操作符入栈.
re.Append(' ');
}
else if (sb[i] == '(')//基本优先级提升
{
sk.Push('(');
re.Append(' ');
}
else if (sb[i] == ')')//基本优先级下调
{
while (sk.Count > 0) //栈不为空时
{
c = (char)sk.Pop();
//pop Operator
if (c != '(')
{
re.Append(' ');
re.Append(c);
//加入空格主要是为了防止不相干的数据相临产生解析错误.
re.Append(' ');
}
else
break;
}
}
else
re.Append(sb[i]);
}
while (sk.Count > 0)//这是最后一个弹栈啦.
{
re.Append(' ');
re.Append(sk.Pop());
}
re.Append(' ');
return FormatSpace(re.ToString());
//在这里进行一次表达式格式化.这里就是后缀式了.
}
// 优先级别测试函数.
private static int Power(char opr)
{
switch (opr)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '%':
case '^':
case '!':
return 3;
default:
return 0;
}
}
// 规范化逆波兰表达式.
private static string FormatSpace(string s)
{
System.Text.StringBuilder ret = new System.Text.StringBuilder();
for (int i = 0;
i < s.Length;
i++)
{
if (!(s.Length > i + 1 && s[i] == ' ' && s[i + 1] == ' '))
ret.Append(s[i]);
else
ret.Append(s[i]);
}
return ret.ToString();
}
}
}