(1)解释游戏对象(GameObjects) 和 资源(Assets)的区别与联系。
游戏对象是在游戏中真实存在一个物体,是可以进行选中操作的对象,而资源则是对游戏对象的一个修饰,比如一个正方体是游戏对象,但是其表面的图案等具体信息是资源来设定的。
(2)下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
在unity中的游戏其资源的目录结构一般有脚本和各种材质包,对不同的对象使用的类型相似的放在一起,比如颜色放一起,材质放一起等等。
对于对象组织的目录结构一般为从属结构,对某一些对象的控制,对资源环境的控制等等。
(3)编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
常用事件包括 OnGUI() OnDisable() OnEnable()
首先使用C#语言编写脚本文件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Debug.Log("start\n");
}
// Update is called once per frame
void Update()
{
Debug.Log("update\n");
}
private void Awake()
{
Debug.Log("awake\n");
}
private void FixedUpdate()
{
Debug.Log("fixedupdate\n");
}
private void LateUpdate()
{
Debug.Log("lateupdate\n");
}
private void OnGUI()
{
Debug.Log("ongui\n");
}
private void OnDisable()
{
Debug.Log("ondisable\n");
}
private void OnEnable()
{
Debug.Log("onenable\n");
}
}
所以其是否执行只需要观察是否有相应输出即可。
程序运行结果如下:
可以看到在程序最开始运行时会调用Awake函数,然后调用Onenable,接着执行Start函数,然后FixedUpdate,这些执行完之后才开始Update函数。
所以总结一下,同时查阅相关资料:
Awake函数:当脚本被载入时调用。
Start函数:第一次进入游戏时调用。
FixedUpdate函数:游戏循环时调用。
Update函数:当start函数执行完后进入游戏循环调用。
LastUpdate函数:当所有Update函数调用完之后调用。
OnGUI函数:游戏循环在渲染过程中,场景渲染之后调用。
OnDdiable函数:当某物体被禁用时调用。
OnEnable函数:当取消禁用时调用。
(4)查找脚本手册,了解 GameObject,Transform,Component 对象。
a.分别翻译官方对三个对象的描述(Description)
GameObject:是unity编辑中最重要的概念,在游戏中的每一个对象都是GameObject,从对象到道具到灯光,相机等。但是它本身不能做任何事情,在它成为一个角色之前需要给他一个特征、环境或者特殊的影响。
Transform:被用来储存一个游戏对象的位置、旋转角度、大小。每个GameObject都会有一个Transform组件依附。
Component:组件是游戏中对象和行为的螺母和螺栓,它们是每个游戏对象的功能部件。
b.描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
第二个选择框是Transform,第三个选择框是MeshFilter,第四个是BoxCollider,第五个是MeshRenderer。
c.用 UML 图描述三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图。
(5)整理相关学习资料,编写简单代码验证以下技术的实现:
a.查找对象:
Debug.Log("start\n");
GameObject cube;
cube = GameObject.Find("cube");
if(cube!=null)
{
Debug.Log("find\n");
}
else
{
Debug.Log("did not find\n");
}
b.添加子对象
void Start()
{
Debug.Log("start\n");
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "cube1";
cube.transform.position = new Vector3(0, Random.Range(0, 5), 0);
cube.transform.parent = this.transform;
}
c.遍历对象树
void Start()
{
Debug.Log("start\n");
foreach (Transform child in transform)
{
Debug.Log(child.name);
}
}
d.清除所有子对象
void Start()
{
Debug.Log("start\n");
foreach (Transform child in transform)
{
Destroy(child.gameObject);
}
}
(6)资源预设(Prefabs)与 对象克隆 (clone)
a.预设(Prefabs)有什么好处?
其为一种可重复使用的游戏对象,可以理解为一个配置好了的对象,可以很方便的加入到一个游戏中去,节省工作。而且在游戏中使用同一个预设都是它的一个克隆,当它本身改变时,其所有克隆出来的都会被改变。
b.预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
预设与克隆都可以对同一个对象复制出大量相同的对象,但是预设出来的克隆对象与其本体有关,当预设对象改变时,其所有克隆对象都会改变,但是如果通过克隆方式创建对象,那么创建出来的对象就与被克隆的对象没有了关系,可以随意更改。
c.制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
这里首先需要在Assets文件夹下创建一个Resources文件夹
然后再把cube对象拖进去。
void Start()
{
Debug.Log("start\n");
GameObject cube1 = Instantiate(Resources.Load("cube") as GameObject);
cube1.transform.parent = this.transform;
}
游戏内容: 井字棋 或 贷款计算器 或 简单计算器 等等
技术限制: 仅允许使用 IMGUI 构建 UI
作业目的:
了解 OnGUI() 事件,提升 debug 能力
提升阅读 API 文档能力
下面是编写的井字棋游戏。
实现思路是主要在OnGUI函数中对整个游戏过程的控制,每次点击时都会判断当前该谁下了,当前位置是否可以下,是否有人获胜,获胜或者平局应该弹出什么,还有就是重新开始游戏。对于算法逻辑来说并不复杂,主要是对于一些函数的调用,实现代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
private int[,] map = new int[3, 3];//创建棋盘
private int turn = 0;
private int num = 0;
private int size = 50;
// Start is called before the first frame update
void Start()
{
num = 0;//下棋数
turn = 1 - turn;//轮流下第一个
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
map[i,j] = 0;
}
}
}
// Update is called once per frame
void Update()
{
//Debug.Log("update\n");
}
private int getwin()
{
for(int i=0;i<3;i++)
{
if (map[i, 0] == map[i, 1] && map[i, 1] == map[i, 2]) return map[i, 1];
if (map[0, i] == map[1, i] && map[1, i] == map[2, i]) return map[1, i];
}
if (map[1, 1] == map[0, 0] && map[1, 1] == map[2, 2]) return map[1, 1];
if (map[1, 1] == map[0, 2] && map[1, 1] == map[2, 0]) return map[1, 1];
if (num == 9) return 3;
return 0;
}
private void OnGUI()
{
GUI.skin.button.fontSize = 20;
GUI.skin.label.fontSize = 20;
if (GUI.Button(new Rect(300, 250, 150, 50), "Reset"))
{
num = 0;
turn = 1 - turn;//轮流下第一个
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
map[i, j] = 0;
}
}
}
int win = getwin();
if(win==1) GUI.Label(new Rect(350, 50, 100, 50), "O wins");
else if(win==2) GUI.Label(new Rect(350, 50, 100, 50), "X wins");
else if(win==3) GUI.Label(new Rect(350, 50, 100, 50), "Draw");
for (int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
if(map[i,j]==1)
{
GUI.Button(new Rect(i * size + 300, j * size + 100, size, size), "O");
}
else if(map[i,j]==2)
{
GUI.Button(new Rect(i * size + 300, j * size + 100, size, size), "X");
}
else if(GUI.Button(new Rect(i * size + 300, j * size + 100, size, size), ""))
{
if (win == 0)
{
if (turn == 0)
{
map[i, j] = 1;
}
else map[i, j] = 2;
turn = 1 - turn;
num++;
}
}
}
}
}
}