最近对Unity3d进行学习的过程中,发现unity不仅入门教程做的丰富,而且Script API文档也是看过的所有API文档中最清晰易用的。不得不说,这极大降低了入门unity3d的门槛。
Unity3d的学习首先从官方tutorials入手,如图1所示。
Tutorial中最经典的demo便是Roll-A-Ball,直接看英文的视频讲解各种术语听起来有点头晕,有些东西也讲得太啰嗦。不过没关系,下面的这个unity圣典的中文教程绝对能满足你对unity开发入门的基本需求,作者逻辑清晰,讲解细致,不得不说入门视频首选:Unity5.2入门课程 - 进入Unity开发的奇幻世界。
每个tutorial中配套有相关的开发视频,并且能找到相关demo源码。在对unity3d的界面视窗和基本操作,以及对Scene、GameObject和component有基本了解之后,从demo源码入手学习是最快的,很容易加深对unity3d工程的理解,以及对Script API document的熟悉。
由于许多人有Android开发经验,Android与unity3d两者在设计上有诸多相似之处,下面以Android的角度来与unity3d进行类比学习,以加深对相关概念的理解。
一、坐标系:
Unity3d的三维坐标系,再加上不同的参考基准,相比Android的二维坐标系要复杂很多:
1.在处理GameObject的transform相关属性,如position、rotation和scale时,假如直接用 鼠标放缩移动工具,三维坐标系拖动起来太难控制了。最方便精准的控制方式为两种:第一种是直接设定属性值;第二种便是shift+点击单个坐标轴,将三维坐标转到两位坐标进行控制。
2.另外针对不同参考基准,unity坐标系划分为几种:
1)世界坐标系:以描述所有场景内所有物体作为基准
2) 局部坐标系:GameObject之间在位置上可构成父子关系,以父GameObject为基准
3) 相机坐标系:相机是最终Game视角的真正观察者,以相机为基准,观察整个场景中 GameObject的立体位置前后和遮挡关系。
4) 屏幕坐标系:很简单,就是把屏幕当作二维坐标,描述像素在屏幕中的位置。
其中Transform提供了各坐标系相互转换的接口。
3.向量和矩阵运算
二维空间中,单纯对x和y坐标进行操作,书写容易。但三维空间,向量和矩阵的运算形式的简洁性和高效性便体现出来。向量主要用于坐标轴的控制,矩阵对阵列数据的放缩、旋转和平移,以前学的《矩阵分析》理论便派上用场啦。
二、界面布局和控件的ID引用
下面从unity3d的GameObject引用和GameObject中的组件引用分别进行叙述。
1.unity3d的GameObject引用
在界面布局元素中,Android叫控件,unity3d叫GameObject(游戏对象)。Android的的布局文件中定义了所需要的控件,在Java代码中通过查找id(findViewById)进行引用和控制。
Unity3d的布局定义在scene文件中,并且在Hierarchy窗口中展现了所有GameObject列表。GameObject通过在inspector窗口中添加脚本组件来实现具体的逻辑控制。假如脚本中要对另外一个GameObject进行控制,比如roll-a-ball游戏中,球撞完所有立方体之后,需要触发一个文本UI的显示,显示为“You win!”,就需要球所在的游戏对象Player的脚本类中定义一个public变量:
public GUIText winText;
随后在player的inspector面板中指定wintext对应的 GUI text对象,如图2所示。便可在脚本代码中实现修改GUIText对象的显示逻辑:winText.text = “You win!”
随后在player的inspector面板中指定wintext对应的 GUI text对象,如图2所示。便可在脚本代码中实现修改GUIText对象的显示逻辑:winText.text = “You win!”
图2
另一种引用的方法是设置为GameObject设置tag,通过GameObject.FindWithTag("tag")获取到需要引用的GameObject。下一段内容中会对此方法进行介绍。
2.引用其他GameObject的Component(组件)
假如一个脚本组件想调用另一个GameObject的脚本组件中的public方法,怎么办呢?这种应用场景出现在roll-a-ball游戏中的球体撞击到立方体时,立方体需要调用球体绑定的脚本中的addScore函数,对分数显示GUI Text进行修改,实时更新分数。
绑定到GameObject的脚本组件,本身是一个类文件,随着Scene的周期开始和结束,创建和销毁自己的实例。在整个内存空间中,应该是属于单例的,但实际上并不写成单例模式,那怎么获取其中的实例对象呢?
其实原理很简单,直接通过GameObject来获取其绑定的脚本即可,下面便是具体方法:
1) GameObject设置tag,一个tag便能分类一个或一类的GameObject,名字可以自定义,如图3所示。
图3
2) 随后在脚本中通过tag获取到具体的GameObject
GameObject gameControllerObject = GameObject.FindWithTag("pickup");
if (gameControllerObject != null)
{
gameController = gameControllerObject.GetComponent ();
} else {
Debug.LogError("Cannot find 'GameController' script", transform);
}
3) 从步骤中获取的GameObject实例,获取其脚本组件,并对其Public方法进行调用:
PlayController gameController;
gameController = gameControllerObject.GetComponent <PlayController >();
gameController.AddScore(scoreValue);
三、生命周期
Android中Service、Activity都有自己的生命周期,也直接决定了不同函数的调用顺序。unity也是如此,unity脚本有自己固定的生命周期函数,常见函数如下:
执行顺序为:awake—>start—>update
更多函数的执行顺序如图4所示,欲知更多可参考这篇文章:Unity3D中自带事件函数的执行顺序。
图4
四、界面跳转
Android一个完整的界面称之为Activity,通过startActivity来启动一个新的Activity实现跳转。但在Unity中称之为Scene(场景或关卡),假如只有一个scene,直接切换相机视角便可实现画面变化。但是如何实现不同scene的切换和跳转呢?
在Scenes文件夹中创建不同的scene文件,File->Build Settings,可以看到图5界面。若创建的scene无法展现出来,首先确保scene文件是有内容的,然后点击“Add Open Scenes”,就可以实时加载进来。
图5
每一个scene文件都会对应一个level,要跳转到某个scene有两种方法:scene文件名和index level。下面是实现不同scene文件循环跳转的方法:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class SwitchScence : MonoBehaviour {
public int scence_level;
private int delay_cnt = 0;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
delay_cnt ++;
if (delay_cnt == 100) {
SceneManager.LoadScene(scence_level);
delay_cnt = 0;
}
}
}
在Unity5.3中只要调用:SceneManager.LoadScene(scence_level),便可跳转到指定level的scene。