在上一节中我们给球体添加了纹理,并且为场景设置了一个目的区域当球体处于目的区域内时,其将变色在这一节里,我们将完善球体在到达目的区域时游戏的反馈,增加音乐和UI显示,下面我们就来说一说关于unity的ui怎么用?我们一起去了解并探讨一下这个问题吧!

unity的ui怎么用(Unity游戏教程初步五)

unity的ui怎么用

前言

在上一节中我们给球体添加了纹理,并且为场景设置了一个目的区域。当球体处于目的区域内时,其将变色。在这一节里,我们将完善球体在到达目的区域时游戏的反馈,增加音乐和UI显示。

项目需求

增加小球进入目的区域的音效。

增加计分的UI界面。

增加音效

-本节相关内容请读者参考:

-https://docs.unity.cn/cn/current/ScriptReference/Resources.html,《Resources》

-https://docs.unity.cn/cn/current/Manual/class-AudioSource.html,《音频源》

我们可以用两个方法将音效素材导入到游戏。第一个就是按照上一节绑定变量的方法,将音效素材作为JudgeController的属性绑定到类中;第二个是使用Resources文件夹。同时,为了让JudgeController可以播放音乐,我们需要为其添加一个Audio Source组件。

Resources文件夹是一类文件夹,其文件夹名为Resources,位于工程目录下的Assets文件夹内(不需要位于根目录下,比如Assets\a\Resources也有效)。我们可以在脚本中使用Resources.Load来访问Resources文件夹下的文件。

第一种方法因为已经在前面使用过了,所以在这里我们不多赘述,来看第二种方法。由于unity预设建立的项目中没有Resources文件夹,我们先在Assets文件夹下建立一个Resources文件夹,然后将音效文件放进去。

然后码代码:

using System.Collections; using System.Collections.Generic; using UnityEngine; public class JudgeController : MonoBehaviour { public GameObject sphere; //球体的引用 //近点和远点分别是目的区域离原点最近和最远的点 public Vector3 nearP=new Vector3(2.5f,0,2.5f); //近点 public Vector3 farP= new Vector3(5,0,5); //远点 private AudioClip m; bool hasClip=false; //标志,球体在一次进出是否播放过音效 // Start is called before the first frame update void Start() { m=Resources.Load<AudioClip>("001"); //加载Assets/.../Resources/001.* } // Update is called once per frame void Update() { Vector3 p=sphere.GetComponent<Transform>().position; //位置 if(p.x>nearP.x && p.x<farP.x && p.z>nearP.z &&p.z<farP.z){ //小球中心点在目的区域内 GetComponent<AudioSource>().clip=m; if(!hasClip){ GetComponent<AudioSource>().Play(); //播放 hasClip=true; } sphere.GetComponent<Sphere>().changeMaterial(true); //调用Sphere的函数 }else{ //不在区域内,变回来 hasClip=false; sphere.GetComponent<Sphere>().changeMaterial(false); } } }

增加UI

-本节相关内容请读者参考:

-https://docs.unity.cn/cn/current/Manual/UISystem.html,《UI》

-https://docs.unity.cn/cn/current/Manual/UICanvas.html,《画布》

UI是User Interface(用户界面)的缩写。大多数游戏都会有UI,用于记录游戏的得分情况等信息。在unity中,UI必须作为Canvas(画布)的子项存在。如果直接创建UI,unity会在此之前自动创建一个Canvas并将被创建的UI作为子项。

Hierarchy->UI->Text,创建一个TextUI,我们命名为Bonus。设定初始分数为0,球体进入目的区域时分数加1。改写JudgeController的代码:

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class JudgeController : MonoBehaviour { public GameObject sphere; //球体的引用 public GameObject bonusUI; //显示分数的UI //近点和远点分别是目的区域离原点最近和最远的点 public Vector3 nearP=new Vector3(2.5f,0,2.5f); //近点 public Vector3 farP= new Vector3(5,0,5); //远点 private AudioClip m; //音效文件 private int bonus; //分数 bool hasClip=false; //标志,球体在一次进出是否播放过音效 // Start is called before the first frame update void Start() { bonus=0; m=Resources.Load<AudioClip>("001"); //加载Assets/.../Resources/001.* } // Update is called once per frame void Update() { Vector3 p=sphere.GetComponent<Transform>().position; //位置 if(p.x>nearP.x && p.x<farP.x && p.z>nearP.z &&p.z<farP.z){ //小球中心点在目的区域内 GetComponent<AudioSource>().clip=m; if(!hasClip){ bonus ; bonusUI.GetComponent<Text>().text=bonus.ToString(); //更新分数 GetComponent<AudioSource>().Play(); //播放 hasClip=true; } sphere.GetComponent<Sphere>().changeMaterial(true); //调用Sphere的函数 }else{ //不在区域内,变回来 hasClip=false; sphere.GetComponent<Sphere>().changeMaterial(false); } } }

可以看到我们增加了一个引用bonusUI,将这个变量与Bonus绑定。然后调整Bonus的大小和其Text的内容(由于后续内容由JudgeController控制,这里只需要将Text置为0即可)。

# TextMeshPro-Text

同样是UI,TextMeshPro可以被看做是Text的升级版。因为文档里没有与其相关的资料,所以在这里笔者会简单描述一下与它有关的信息。

TextMeshPro又称为TMP,一开始是一个外部插件,在最近的版本中才被包含进unity本体中。TMP采用了SDF文字渲染技术,相比原生的Text组件它能保证文字在缩放数倍后仍然保持平滑(其实就是矢量绘图)。

但是保持文字的平滑自然需要代价,TMP会为字体创建一个纹理集,而此纹理集在字体所属语言为中文的情况下会占用较大的空间。

而TMP也不只有这一个长处,TMP还可以设置文字的描边颜色渐变等,并且可以图文混用。

增加得分点

-本节相关内容请读者参考:

-https://docs.unity.cn/cn/current/ScriptReference/Random.html,《Random》

既然已经有了一套基础的得分系统,不妨在之前的基础上增加一个得分点,比如说,让平面上的正方体在进入得分区域时重置位置并且得分。

按照之前的思路,修改JudgeController,并给Cube添加脚本:

JudgeController.cs(注:前文中对于近点/远点的定义有误,在这里更正)

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class JudgeController : MonoBehaviour { public GameObject sphere; //球体的引用 public GameObject bonusUI; //显示分数的UI public GameObject cube; //正方体的引用 //近点和远点分别是目的区域离xy最大的点和xy最小的点 public Vector3 nearP=new Vector3(2.5f,0,2.5f); //近点 public Vector3 farP= new Vector3(5,0,5); //远点 private AudioClip m; //音效文件 private int bonus; //分数 bool hasClip=false; //标志,球体在一次进出是否播放过音效 // Start is called before the first frame update void Start() { bonus=0; m=Resources.Load<AudioClip>("001"); //加载Assets/.../Resources/001.* } bool comPosition(Vector3 p) { //比较传入点与近点/远点的相对位置,内部函数 if(p.x>nearP.x && p.x<farP.x && p.z>nearP.z &&p.z<farP.z){ return true; }else{ return false; } } // Update is called once per frame void Update() { Vector3 p=sphere.GetComponent<Transform>().position; //位置 if(this.comPosition(p)){ //小球中心点在目的区域内 GetComponent<AudioSource>().clip=m; if(!hasClip){ bonus ; bonusUI.GetComponent<Text>().text=bonus.ToString(); //更新分数 GetComponent<AudioSource>().Play(); //播放 hasClip=true; } sphere.GetComponent<Sphere>().changeMaterial(true); //调用Sphere的函数 } else{ //不在区域内,变回来 hasClip=false; sphere.GetComponent<Sphere>().changeMaterial(false); } Vector3 c=cube.GetComponent<Transform>().position; if(this.comPosition(c)){ //一旦抵达目标地点,就开始传送,所以不需要额外标志,也没有else cube.GetComponent<Cube>().transmit(nearP,farP); bonus =2; bonusUI.GetComponent<Text>().text=bonus.ToString(); //更新分数 } } }

Cube.cs

using System.Collections; using System.Collections.Generic; using UnityEngine; using Random=UnityEngine.Random; public class Cube : MonoBehaviour { public GameObject plane; //平台,用于计算区域长度 // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } float comVxy(float far,float near,float width){ //封装内部函数 if(far<width){ //换成near>-width也是一样的,因为目的区域必然不大于整个区域,所以只要比较一个 if(Random.value>0.5){ return Random.Range(far,width); } else{ return Random.Range(-1*width,near); } } else{ return Random.Range(-1*width,near); } } public void transmit(Vector3 near,Vector3 far){ //以0,0,0为中心的情况下,给出:整个区域长度、目的区域近点和远点 将正方体传送到整个区域之内,目的区域之外 //设定平面长宽相等,生成在与目的区域相对的区域内 Vector3 v2=new Vector3(0,-4.5f,0); float myWidth=GetComponent<Transform>().localScale.x*0.5f; //由于算的是物体中心的位置,要减去到中心的距离 float width=plane.GetComponent<Transform>().localScale.x*5-myWidth; v2.x=this.comVxy(far.x,near.x,width); v2.z=this.comVxy(far.z,near.z,width); //笔者的实例里y轴朝上 transform.localPosition=v2; //这里是相对坐标 print(v2); } }

可以看到在Cube.cs中,我们传送物体使用的是transform.localPosition(GetComponent<Transform>().localPosition),而非position。这是因为笔者在之前建立了一个空游戏对象作为Cube的父对象,而这里需要对正方体的父对象定位(如果没有父对象localPosition就与position相同)。我们把position称为世界位置,而localPosition则是相对位置。position是游戏对象在绝对坐标系下(世界坐标系,也就是无父对象时Transform面板显示的坐标)的位置,而localPosition则是position对游戏对象的所有父对象的位置进行变换之后的位置。例如在场景下有一个根游戏对象A(1,1,1),其子游戏对象B在Transform面板里的坐标为(0,1,-1),则B在世界坐标系的坐标为A B(1,2,0)。

由于需要判断两个物体的位置,为了节省代码量我们把判断位置部分的代码封装为一个函数comPosition(其实也就是节省了一行不到,但是也省得写了)。同时,建议读者尽量使用unity的Random来生成随机数,而不使用C#自带的Random。

,