一:Unity脚本概述以及断点调试
二:Time等常见工具类的使用
三:Unity脚本生命周期函数
四:对象基本操作
五:本章总结
一:Unity脚本概述以及断点调试
与其它常用的平台有所不同,Unity中的脚本程序如果要起作用,主要途径为将脚本挂载到特定的对象上。这样,脚本中的方法在特定的情况下就会被回调,实现特定的功能。
其实上节所说的几个回调方法一般是位于MonoBehavior类的子类中的,也就是说开发脚本代码时,主要是继承自MonoBehavior类并重写其中特定的方法。
用于初始化脚本的代码必须置于Awake或者Start方法中。两者的不同之处在于,Awake方法是在加载时运行,Start方法是场景加载后第一次调用Update之前调用。因此Awake方法总是在Start方法前调用。不要在构造函数中初始化任何变量,要用Awake或者Start方法来实现。即便是在编辑模式,Unity仍然会自动调用构造函数,这通常是在一个脚本编译之后,因为需要调用脚本的构造函数来取回脚本的默认值。无法预计何时调用构造函数,它或许会被预制件或未激活的游戏对象所调用。
通过观察上节的代码可以看出,只有是public类型的变量才能在检视视图中通过拖拽来完成赋值操作,而private或者protected类型的成员变量不能查看。当然,后期编辑器扩展是特例。
Unity中C#代码的调试与传统的C#调试有所不同。Unity有一个控制台,Console,在这个控制台可以看到脚本中精确的错误,包括位置、行数。比如以下错误代码:
using UnityEngine;
using System.Collections;
public class TestDebug : MonoBehaviour {
public int num;
// Use this for initialization
void Start () {
num = 0.2f;
}
// Update is called once per frame
void Update () {
}
} print ("快速输出");
使用Print也能进行快速输出,但是输出的时候,只能在Mono类中。Debug.Log可以在所有类使用。
断点调试功能可谓是程序员必备的功能了。Unity3D支持编写js和c#脚本,但很多人可能不知道,其实Unity3D也能对程序进行断点调试的。不过这个断点调试功能只限于使用Unity3D自带的MonoDevelop编辑器。
使用UnityVS可以使用VS对Unity进行断点调试,我们这里首先讲解第一种调试方式。调试具体步骤:
(1)首先把编辑器指定为自带的MonoDeveloper。点击Preference—>ExternalTools,选择所需的编辑器即可。
(2)新建C#脚本,我们使用OnGUI新建两个按钮,点击按钮进行测试输出。代码如下所示:
public class TestBreak : MonoBehaviour {
private int sum=0;
void OnGUI()
{
if (GUILayout.Button ("add")) {
for (int i = 0; i < 10; i++) {
sum+=i;
Debug.Log(&#34;sum=&#34;+sum);
}
}
if (GUILayout.Button (&#34;Clear&#34;)) {
sum=0;
}
}
}(3)写好脚本之后,把脚本随便拖到场景的物体上面,我们准备调试了。在调试之前,我们需要把MonoDevelop和Unity3D进行连接。
连接方式如下图所示,点击运行—->AttachToProcess,选择需要连接的进程。如下图所示:
选择UnityEditor,点击Attach。这时候,MonoDevelop就连接上了Unity了。
当连接好了Unity3D之后,右边的调试按钮就变成可用了。我们在代码里面添加断点。
(4)回到Unity,点击Run按钮,此时程序开始运行。点击其中的Button。
(5)此时Unity会跳转到MonoDevelope界面,如下所示:
(6)上方的四个按钮分别表示继续执行,单步执行逐过程,单步执行逐语句,跳出循环。
(7)调试完之后,我建议最好把MonoDevelop和Unity3D的连接断开。假如你不断开的话,Unity会一直处于很卡的状态。
断开的方法也是有几种了,首先对应Debug按钮有一个断开的按钮,可以直接点击断开。或者点击运行——->Detach即可。
Application —— 应用程序类
Input —— 输入类
GUI —— 图形输出类
Physics —— 物理引擎类
Resources —— 资源类
Time —— 时间类
由于Unity是面向组件的开发模式(而非面向对象),所以从类的继承关系中也能看出,对于GameObject类没有下太多的笔墨,而是将更多的内容交给下面的组件类。
组件(Component)是用来绑定到游戏对象(Game Object)上的一组相关属性。本质上每个组件是一个类的实例。常见的组件有:MeshFilter、MeshCollider、Renderer、Animation等等。
其实不同的游戏对象,都可以看成是一个空的游戏对象,只是绑定了不同的组件。比如:Camera对象,就是一个空对象,加上Camera、GUILayer、FlareLayer、AudioListener等组件。其他对象绑定的组件,可自行观察。
开发的时候,我们使用最多的就是Unity组件类以及自定义的组件脚本。
Unity基础常见类关系图:
二:Time等常见工具类的使用
(1)Unity提供了Time类,这个类主要用来获取当前的系统时间。其中常见的几个属性如下所示:
Time.time:从游戏开始后开始计时,表示截止目前共运行的游戏时间。Time.deltaTime:获取Update()方法中完成上一帧所消耗的时间。Time.fixedTime:FixedUpdate()方法中固定消耗的时间总和。FixedUpdate()每一帧更新的时间可以通过导航菜单栏”Edit“——”Project Settings“——”Time“菜单项去设置。Time.fixedDeltaTime:固定更新上一帧所消耗的时间。Time.timeScale:时间比例,等于0表示暂停,=1表示正常,大于1表示加速,小于1表示减速。只影响FixedUpdate,不影响Update和LateUpdate。
问题:如何让一个对象匀速运动?匀速旋转?
(2)随机数:在开发中,有时需要获取程序中的随机数,这可以使用Random.Range()方法实现,其中该方法的第一个参数为随机数的起始位置,第二个参数为获取的随机数的结束位置。
(3)Unity开发中封装了一个数学类Mathf,使用它可以很轻松地帮我们解决复杂的数学公式。
三:Unity脚本生命周期函数
若是使用脚本函数,我们必须熟练的掌握脚本函数的执行顺序。我们通过下面的例子来学习脚本的生命周期函数。
public class LifeTest : MonoBehaviour {
void Reset()
{
print (&#34;Reset&#34;);
}
void Awake(){
print (&#34;Awake&#34;);
}
void OnEnable()
{
print (&#34;Enabled&#34;);
}
// Use this for initialization
void Start () {
print (&#34;Start&#34;);
}
// Update is called once per frame
void Update () {
print (&#34;update&#34;);
}
void FixedUpdate()
{
print (&#34;fixed&#34;);
}
void OnDisable()
{
print (&#34;disabled&#34;);
}
void OnDestroy()
{
print (&#34;destroy&#34;);
}
}
大体可以分为以下五个阶段:
编辑阶段Reset->开始阶段Awake-OnEnable-Start->迭代更新阶段FixedUpdate-Update-LateUpdate->显示阶段OnGUI->清除阶段OnDisable-OnDestroy
四:对象基本操作
4.1:对象查找操作
通过案例依次使用每个函数。掌握每个函数的具体用法。
何为Tag?
Tag是Unity为了标识一个对象而设计的,通过Tag值可以对具体的对象进行管理和筛选,比如碰撞检测的时候,可以通过Tag值过滤一些没必要发生碰撞的物体。如何修改Tag值呢?
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
// Use this for initialization
void Start () {
GameObject[] array = GameObject.
FindGameObjectsWithTag (&#34;MyTag&#34;);
print (array.Length);
GameObject one=GameObject.FindGameObjectWithTag (&#34;MyTag&#34;);
print (one.name);
SayHello sayHello= GameObject.FindObjectOfType<SayHello>();
Debug.LogFormat (&#34;{0}{1}&#34;,sayHello.gameObject.name,
sayHello.gameObject.tag);
SayHello[] says = GameObject.FindObjectsOfType<SayHello> ();
foreach (var item in says) {
Debug.LogFormat (&#34;{0}{1}&#34;,item.gameObject.name,
item.gameObject.tag);
}
}
何为Layer?
在Unity中用int32数据类型来表示32个Layer层。int32表示二进制一共有32位(0—31)。在Unity中每个GameObject都有Layer属性,默认的Layer都是Default。在Unity中可编辑的Layer共有24个(8—31层),官方已使用的是0—7层,默认不可编辑。
在Unity中使用LayerMask:
Layers通常被摄像机用来渲染部分场景,和灯光照射部分场景使用。但是它们也可以用来做射线检测时忽略一些collder或Collision使用。
如何编辑Layers.
在代码中使用时如何开启某个Layers?答案是正确使LayerMask。
LayerMask mask = 1 << 你需要开启的Layers层。
LayerMask mask = 0 << 你需要关闭的Layers层。
例如:
LayerMask mask = 1 << 2; 表示开启Layer2。
LayerMask mask = 0 << 5;表示关闭Layer5。
LayerMask mask = 1<<2|1<<8;表示开启Layer2和Layer8。
LayerMask mask = 0<<3|0<<7;表示关闭Layer3和Layer7。
void Update ()
{
if (Input.GetMouseButtonDown(0)) {
// 只检测第6层
LayerMask mask1 = 1 << 6;
// 只不检测第7层
LayerMask mask2 = ~(1 << 7);
// 只检测第8层和第9层
LayerMask mask3 = (1 << 8)|(1 << 9);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, mask1)) {
Debug.Log(&#34;test&#34;);
}
}
}
4.2:组件查找操作
public class ComponentTest : MonoBehaviour {
public GameObject obj;
// Use this for initialization
void Start () {
//this.GetComponent<SayHello> ();
//1
SayHello h = obj.GetComponent<SayHello> ();
h.Hello ();
//2
SayHello h1 = obj.GetComponentInChildren<SayHello> ();
h1.Hello ();
//3
SayHello[] h2 = obj.GetComponentsInChildren<SayHello> ();
print (h2.Length);
SayHello[] h3= obj.GetComponents<SayHello>();
print (h3.Length);
}
}
给物体添加组件的几种方式:
编辑器直接添加,不再多说通过代码AddComponent添加
if (!ball1.GetComponent<CharacterController> ()) {
ball1.AddComponent<CharacterController> ();
}
通过RequireComponent添加,在脚本添加下面一行代码即可,当把该脚本拖动到物体上时候,会自动为该物体添加刚体组件。
[RequireComponent(typeof(Rigidbody))]
在该脚本内部添加一句代码[AddComponentMenu(“BodyTest”)],那么此时,在Unity菜单栏下面会出现BodyTest菜单,点击即可添加该脚本。
Rigidbody[] body=GetComponentsInChildren<Rigidbody> ();
print (body.Length);
print (body[0].name);
4.3:自定义脚本查找操作
自定义一个脚本,提供一个public函数。
using UnityEngine;using System.Collections;public class ChangeMat : MonoBehaviour {
public void ChangeMatrial(GameObject obj,Color col){
obj.GetComponent<MeshRenderer> ().material.color = col;
}
}
public class TestChange : MonoBehaviour {
// Use this for initialization
void Start () {
ChangeMat c = this.GetComponent<ChangeMat> ();
if (c == null) {
c = this.gameObject.AddComponent<ChangeMat> ();
}
c.ChangeMatrial (this.gameObject,Color.blue);
}
}
4.4:层级关系查找游戏对象
在层级视图的对象列表中,存在有父子关系,在代码中可以通过获取Transform组件来找到子对象或者父对象。
using UnityEngine;
using System.Collections;
public class Cengci : MonoBehaviour {
private Transform objT;
// Use this for initialization
void Start () {
//transform.Find获取的是名称为Cube的子对象
objT = this.transform.Find(“Cube”);
}
// Update is called once per frame
void Update () {
objT.Rotate(20*Time.deltaTime,0,0);
objT.parent.Translate(1,0,0);//objT的父对象一直移动
}
}
一旦获取到”Cube”子对象,就可以通过GetComponent方法获取到”Cube”子对象的所有组件。另外,Unity中可以通过遍历transform对象来获取其子对象,遍历代码如下:
foreach(Transform child in transform){
child.Translate(0.1f,0.1f,0);
}
4.5:代码创建游戏对象
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.AddComponent<Rigidbody>();
cube.transform.position = new Vector3(1, 1, 0);
//我们将obj1初始化为一个Cube立方体,当然我们也可以初始化为其他的形状
GameObject obj1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
//设置物体的位置Vector3三个参数分别代表x,y,z的坐标数
obj1.transform.position = new Vector3(1,1,1);
//给这个创建出来的对象起个名字
obj1.name = &#34;cube&#34;;
//设置物体的tag值,在赋值之前要在Inspector面板中注册一个tag值
//注册tag值得方法,用鼠标选中摄像机对象在Inspector面板中找到tag,选addtag
p> obj1.tag = &#34;shui&#34;; </p> //设置物体贴图要图片文件放在(Resources)文件夹下,没有自己创建
obj1.renderer.material.mainTexture = (Texture)Resources.Load(&#34;psb20&#34;);
五:本章总结
本章我们主要对Unity中的脚本进行了一个系统的介绍,主要需要大家掌握脚本的生命周期、常见工具类的使用、Unity组件机制、Unity对象查找规则等。 |