《Unity 游戏案例开发大全》一6.5 游戏主场景

    xiaoxiao2024-05-06  8

    本节书摘来异步社区《Unity 游戏案例开发大全》一书中的第6章,第6.1节,作者: 吴亚峰 , 杜化美 , 于复兴 责编: 张涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。

    6.5 游戏主场景

    Unity 游戏案例开发大全前面的小节详细介绍了游戏主菜单场景,本小节将介绍游戏主场景的脚本开发,游戏主场景是本案例最重要的场景,也是游戏的关键场景。本游戏共有四个关卡,对应四个不同的场景,其开发方法基本相同,这里以关卡一为例进行详解。

    6.5.1 场景搭建

    首先进行游戏界面场景的搭建,这里步骤比较繁琐,包括模型的摆放、组件的添加、参数的设置等。通过此小节的开发,读者可以熟练掌握这些知识,同时也会积累一些开发技巧和细节。接下来对游戏场景的搭建进行详细的介绍。

    (1)首先新建一个“Level1”场景,具体步骤参考主菜单界面开发的相应步骤,此处不再赘述。需要的图片资源已经放在对应文件夹下,读者可参看本章6.2.2小节的相关内容。

    (2)按照步骤“GameObject”→“Create Other”→“Plane”创建四个平面,并分别重命名为“BackGround”、“LeftGround”、“RightGround”、“TopGround”。调整大小和位置使其围成一个前方和底部无盖的盒子的形状。资源列表如图6-26所示。

    (3)将步骤(2)创建的四个平面勾选为静态的“Satic”,使其不参加运动。并分别添加刚体和盒子碰撞器,在刚体组件中勾选“IsKinematic”并取消“UseGravity”属性的选择。为“BackGround”游戏对象赋予纹理贴图“beijing.png”。

    (4)下面创建放球台子,将模型“TaiZi.fbx”拖拉到场景中,调整大小和位置后赋予其纹理图“taizi1.png”,将其勾选为静态的“Static”。为其添加刚体,并勾选“IsKinematic”属性,取消“UseGravity”,如图6-27所示。

    (5)创建放可乐罐台子,其创建方法、组件添加、参数设置方法同步骤(4),使用模型为“TaiZi2.fbx”,纹理图为“Taizi2.png”,调整大小后合理摆放即可。

    (6)接下来创建广告板,按照步骤“GameObject”→“Create Other”→“Cube”创建一个立方体,并重命名为“GuanGao”,调整大小后赋予其纹理图“guanggao.png”。为其添加刚体属性和盒子碰撞器组件。在刚体属性中勾选“IsKinematic”和“UseGravity”,如图6-28所示。

    (7)下面创建层,对相似的游戏对象划分为同一层可以在代码中方便地进行统一管理。单击属性面板中的“Layer”→“Add Layer…”,创建一个“guanggao”层。并将广告板勾选为此层。用相似方法创建“beijing”层。并将该场景中放罐子台子、四个背景平面勾选为此层。

    (8)接下来创建三个球,将模型“muqiu.fbx”拖拉到场景中并重命名为“ball1”、“ball2”、“ball3”,调整大小并分别赋予纹理图“muqiu.png”。调整位置将创建好的三个球放到步骤(4)中创建的放球台子上。效果如图6-29所示。

    (9)为每个球添加刚体“Rigidbody”和球体碰撞器“Sphere Collider”组件。添加一个层“ball”并将三个球都选为此层。接下来在Layer下拉选框左边有Tag下拉选框,单击“Tag”→“Add Tag…”,添加一个名为“ball”的标签,并将三个球都选为此标签,如图6-30所示。

    (10)之后为每个球分别添加声音源使球在特定的情况下可以发出声音。选中球对象,按步骤“Component”→“Audio”→“Audio Source”即创建了一个声音源,将音乐资源“qiuzhuangguan. wav”拖拉到AudioClip属相框中,添加好后如图6-31所示。

    (11)下面创建可乐罐,将模型“kele.fbx”拖拉到场景中,调整大小后添加刚体属性和网格碰撞器“Mash Collider”。在本游戏中有三种可乐罐子“普通雪碧”、“爆炸可乐”、“加球雪碧”,分别对应纹理图“languanzi.png”、“hongguanzi.png”、“lvguanzi.png”和层“guanzi”、“guanzi2”、“guanzi3”。

    (12)创建好罐子后可以根据关卡设计进行自行摆放,不同种类的罐子只有纹理图和所属层不同。红色的爆炸罐子使用纹理图“hongguanzi.png”,所属层为guanzi2。绿色的加球罐子使用纹理图“lvguanzi.png”,所属层为guanzi3。摆放好后如图6-32所示。

    (13)下面创建地板,当球或罐子掉下平台撞击到该游戏对象时会进行计数。按步骤“GameObject”→“Create Other”→“Plane”创建一个平面,并重命名为“Diban”。为其添加刚体组件和盒子碰撞器,在盒子碰撞器中勾选“IsTrigger”使其变为触发器。属性设置如图6-33所示。

    (14)接下来创建游戏的控制板。按步骤“GameObject”→“Create Other”→“Cube”创建一个立方体并重命名为“Ban”,调整大小后为其添加纹理图“kongzhiban.png”。并将着色器改为带有透明通道的Transparent/Diffuse。效果如图6-34所示。

    (15)下面创建控制板的铁链部分,将模型“liantiao.fbx”拖拉到场景中,调整为灰色并使其成为Ban游戏对象的子物体。按步骤“Component”→“Physic”→“Rigidbody”为其添加刚体组件并勾选“IsKinematic”选项。效果如图6-35所示。

    (16)在链条的顶端创建一个球体“Sphere”为其添加刚体并勾选“IsKinematic”。选中Ban游戏对象,按步骤“Component”→“Physic”→“Hinge Joint”为Ban对象添加铰链关节,并把刚才创建好的Sphere对象拖拉到铰链关节属性中的Connected Body上。关于铰链关节的参数设置如图6-36所示。

    (17)接下来制作控制板的按钮部分,在Ban游戏对象纹理图上的五个按钮位置处创建五个Cube,分别调整大小后从上到下重命名为“kaishi”、“tuichu”、“jingyin”、“chongkaishi”、“zanting”。为五个立方体添加刚体和盒子碰撞器,效果如图6-37所示。

    (18)按步骤“Component”→“Physic”→“Fixed Joint”给五个立方体都添加上固定关节,将Connected Body都选为游戏对象“Ban”,给“jingyin”立方体游戏对象添加纹理图“fanxuan.png”,再将步骤(17)中创建的五个立方体组件列表中的“Mesh Renderer”关闭,使其不进行渲染,如图6-38所示。

    图6-38 按钮的设置

    (19)下面调整摄像机参数,调整摄像机中“Camera”组件中的“Near”和“Far”参数控制视口范围,“Field of View”参数控制摄像机视口的张角,使游戏中“近大远小”的效果不会过于明显影响到游戏控制。参数调整如图6-39所示。

    (20)最后创建灯光,按步骤“GameObject”→“Create Other”→“SpotLight”创建一个区域光,区域光仅在一个锥形区域内有光照,类似有灯罩的台灯。在游戏中使用不同的灯光加以配合会得到非常好的效果。调整参数后,效果如图6-40所示。

    6.5.2 主摄像机设置及脚本开发

    前一小节介绍了主场景的搭建过程,本小节将要介绍游戏场景主要游戏对象上挂载的脚本开发过程。首先介绍游戏主场景中的主摄像机相关脚本的开发与设置。具体步骤如下。

    (1)新建脚本,命名为“GameRule.cs”并拖曳到“Main Camera”对象上,该脚本用于实现游戏的规则,判定胜负。当球发射完毕而罐子还有剩余的时候游戏失败,当罐子全部被砸落时游戏胜利,并根据游戏的胜利情况生成相应的分数板。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public class GameRule : MonoBehaviour { 4 public GameObject GuanZi; //罐子游戏对象 5 public GameObject fenshuban; //分数板游戏对象 6 private GameObject scoreBoard; //实例化后分设板游戏对象 7 public GameObject fenshuban2; //失败分数板游戏对象 8 public Camera mycamera; //主摄像机 9 public AudioSource win; //游戏胜利音效 10 private bool jfflag = true; //加分标志位 只加一次 11 private float temptime; //加分时间 12 public GUIStyle myStyle; //GUI风格 13 public Texture2D jia1000; //加1000分图片 14 void Update(){ 15 if (Input.GetKeyUp(KeyCode.Escape)) { //安卓手机返回按键监听 16 Application.LoadLevel("Zhujiemian"); //返回游戏主界面 17 } 18 int gzshengyu=0; //声明罐子剩余数量变量 19 foreach (Transform childs in GuanZi.transform){ //罐子子对象 20 gzshengyu++; //统计罐子数量 21 } 22 GameObject[] ball=GameObject.FindGameObjectsWithTag("ball"); //所有标签为ball的物体数组 23 if (gzshengyu == 0){ //剩余罐子为0 24 MyStaticClass.shengfu=1; //游戏胜利 25 StartCoroutine(Waitforsecond1(2.0f,1.0f)); //启动协同程序等2秒游戏结束,1秒生成分数板 26 if (scoreBoard != null) { //若已经生产分数板 27 scoreBoard.transform.position = new Vector3(scoreBoard.transform.position.x, 28 Mathf.Lerp(scoreBoard.transform.position.y, 4f, Time.deltaTime), scoreBoard.transform.position.z); 29 }} 30 else if (ball.Length == 0){ //罐子不为0,球射完 31 MyStaticClass.shengfu = 2; //游戏失败 32 StartCoroutine(Waitforsecond2(3.0f)); //启动协同程序两秒后游戏结束 33 if (scoreBoard != null){ //若没有生产分数板 34 scoreBoard.transform.position = new Vector3(scoreBoard.transform. position.x, Mathf.Lerp(scoreBoard.transform.position.y, 4f, Time.deltaTime), scoreBoard. 35 transform.position.z); 36 }} 37 }IEnumerator Waitforsecond1(float waitTime1,float waitTime2){ //协同程序1 38 yield return new WaitForSeconds(waitTime1); //延迟waitTime1秒 39 MyStaticClass.gameoverflag = true; //游戏结束 40 yield return new WaitForSeconds(waitTime2); //等待加分(剩余球) 41 if (scoreBoard == null){ //若没有生产分数板 42 if (MyStaticClass.yinyue){ //不是静音状态 43 win.Play(); //播放胜利音效 44 } 45 scoreBoard = Instantiate(fenshuban, new Vector3(0.04f, 16f, -10f), 46 this.transform.rotation) as GameObject; //实例化分数板并取得其对象引用 47 }} 48 IEnumerator Waitforsecond2(float waitTime1){ //协同程序2 49 yield return new WaitForSeconds(waitTime1); //延迟waitTime1秒 50 MyStaticClass.gameoverflag = true; //游戏结束 51 if (scoreBoard == null) { //若没有生产分数板 52 scoreBoard = Instantiate(fenshuban2, new Vector3(0.04f, 16f, -10f), 53 this.transform.rotation) as GameObject; //实例化分数板并取得其对象引用 54 }}} 第4行~第13行的主要功能为变量的声明。在这里声明了分数板游戏对象、主摄像机、胜利音效、加分标志位、加分计时器和图片等对象,方便下面的代码使用。第14行~第17行的主要功能是对手机硬件上的返回按键进行监听,当玩家在游戏关卡中按下返回按键时,会跳转到主菜单场景。第18行~第29行首先遍历GuanZi游戏对象的子对象,统计出当前剩余罐子数量,并根据查找标签得到当前剩余球的数量。若是可乐罐已经全部销毁,更改标志位shengfu为游戏胜利,启动协同程序,生成分数板并播放音效。第30行~第36行的主要功能为若是罐子还有剩余但球已经全部使用完并被销毁,则更改游戏胜负标志位shengfu为游戏失败,启动协同程序生成失败分数板,并将失败分数板差值滑动到屏幕中指定位置。第37行~第54行是两个协同程序,协同程序是在主程序运行的同时开启另一条逻辑处理来协同当前程序执行。换句话说,协同程序就是开启一个进程。这里被用于等待一段时间后改变游戏状态标志位,并生成分数板赋予对象“ScoreBoard”,便于主程序使用。(2)下面是该脚本中对OnGUI方法的重写。OnGUI方法每帧调用若干次,掌管游戏中的绘制,如绘制按钮、图片等。在该方法中实现了当游戏结束后,根据罐子位置在其上方绘制罐子的加分动画。脚本片段代码如下。 1 void OnGUI() //绘制剩余球加分{ 2 if (MyStaticClass.gameoverflag){ //若是游戏结束 3 GameObject[] ball = GameObject.FindGameObjectsWithTag("ball"); //所有标签为ball的物体数组 4 Vector3 [] ballpos=new Vector3[ball.Length]; //声明用于保存球位置的数组 5 for (int i = 0; i < ball.Length; i++){ //遍历每个球 6 ballpos[i] = mycamera.WorldToScreenPoint(ball[i].transform.position); //取出当前球的位置 7 temptime += Time.deltaTime; //计时 8 if (jfflag){ //若可以加分 9 MyStaticClass.shengqiu++; //记录剩余球 10 MyStaticClass.setzongfen(MyStaticClass.getzongfen() + 1000); //加分 11 } 12 if (temptime <= 3){ //加分时间小于3秒 13 GUI.Label(new Rect(ballpos[i].x, Screen.height * 4 / 5, 14 40 / temptime, 10 / temptime), jia1000, myStyle); //绘制加分图片,大小随时间变小 15 }} 16 jfflag = false; //只加分一次,关闭加分标志位 17 }} 第1行~第4行的主要功能是判断当前是否游戏结束,若是已经结束,就通过标签找到所有剩余的球,并声明一个三维坐标数组用于储存球的位置。第5行~第11行开始遍历剩余球的游戏对象数组,每找到一个剩余的球就记录下位置,若是第一次加分,就给总分加1000分,并最后统计出台子上剩余球的数量。第12行~第17行的主要功能为绘制加分动画,根据加分时间改变jia1000.png的大小,使其达到加分图案由大缓缓变小的视觉效果,形成加分动画。最后关闭加分标志位,防止重复加分。

    (3)在本游戏的某些关卡具有特殊的罐子,如红色的爆炸罐子、绿色的加球罐子。若是在这些关卡开始的时候显示针对本关特色的提示板,会大大加强游戏体验。下面介绍这个提示板的实现方法。新建脚本“TiShi.cs”并挂载到主摄像机上。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public class TiShi : MonoBehaviour { 4 public GameObject level2Tishi; //第二关提示板 5 public GameObject level3Tishi; //第三关提示板 6 public GameObject ball1; //球1对象 7 public GameObject ball2; //球2对象 8 public GameObject ball3; //球3对象 9 public GameObject huohua; //火花粒子 10 private bool ssflag=false; //上升提示板标志位 11 private bool xjflag = true; //下降提示板标志位 12 private GameObject tiShi; //提示板 13 void Start () { 14 if (Application.loadedLevelName=="Level1"){ //当前是第一关 15 MyStaticClass.gamestart = true; //游戏直接开始,没有提示板 16 }else 17 if (Application.loadedLevelName == "Level2"){ //第二关 18 MyStaticClass.gamestart = false; //游戏暂时不开始 19 Ball b1 = (Ball)ball1.GetComponent("Ball"); //显示提示板的时候让球1不受控制 20 b1.enabled = false; //关闭Ball脚本 21 Ball b2 = (Ball)ball2.GetComponent("Ball"); //显示提示板的时候让球2不受控制 22 b2.enabled = false; //关闭Ball脚本 23 Ball b3 = (Ball)ball3.GetComponent("Ball"); //显示提示板的时候让球3不受控制 24 b3.enabled = false; //关闭Ball脚本 25 tiShi = Instantiate(level2Tishi, new Vector3(0.04f, 16f, -10f), 26 this.transform.rotation) as GameObject; //实例化分数板并取得其对象引用 27 } else 28 if (Application.loadedLevelName == "Level3"){ //第三关 29 MyStaticClass.gamestart = false; //游戏暂时不开始 30 Ball b1 = (Ball)ball1.GetComponent("Ball"); //显示提示板的时候让球不受控制 31 b1.enabled = false; //关闭Ball脚本 32 Ball b2 = (Ball)ball2.GetComponent("Ball"); //显示提示板的时候让球不受控制 33 b2.enabled = false; //关闭Ball脚本 34 Ball b3 = (Ball)ball3.GetComponent("Ball"); //显示提示板的时候让球不受控制 35 b3.enabled = false; //关闭Ball脚本 36 tiShi = Instantiate(level3Tishi, new Vector3(0.46f, 16f, -10f), 37 this.transform.rotation) as GameObject; //实例化分数板并取得其对象引用 38 }} 39 void Update(){ 40 ...//以下省略对UpDate方法的重写,下面将详细介绍 41 }} 第1行~第12行为变量声明,在这里声明了两种特殊罐子对应的提示板预制件对象、三个木球游戏对象、火花粒子系统对象、上升下降标志位等变量、生成后的提示板对象等,方便下面代码使用或更改其状态。第13行~第16行是对Start方法的重写,该方法在本脚本第一次加载时调用。在这里的主要功能是取出当前的关卡名,判断当前是否为游戏的第一关。若是第一关,没有提示板,游戏直接开始。否则先生成游戏提示板,之后再开始。第17行~第37行的主要功能是根据当前运行的场景名判断,若当前是第二关或者第三关,首先关闭球上的脚本“Ball.cs”锁定球,使得球暂时不受玩家控制。并在指定位置生成对应的提示板,并通过差值移动将提示板移动到屏幕中央。

    (4)下面介绍“Tishi.cs”脚本中对Update方法的重写,该方法在游戏的每帧都调用一次,在本脚本中的具体功能为,判断用户的触摸事件改变提示板上升或下降标志位,之后根据标志位对提示板进行移动,在显示提示板的时间里使球不能发射。代码片段如下。

    1 void Update () { 2 if (tiShi != null) { //已经生成提示板 3 if (xjflag){ //下降标志位 4 tiShi.transform.position = new Vector3(tiShi.transform.position.x, 5 Mathf.Lerp(tiShi.transform.position.y, 5f, Time.deltaTime), tiShi.transform. position.z); //下移提示板动画 6 if (tiShi.transform.position.y < 5.5f){ //到达最低位置 7 xjflag = false; //停止下降 8 } 9 return; //跳过下面的代码 10 } 11 if (ssflag) { //上升 12 tiShi.transform.position = new Vector3(tiShi.transform.position.x,Mathf. Lerp(tiShi.transform.position.y, 17f, Time.deltaTime), tiShi.transform.position.z); //上移提示板动画 13 14 if (tiShi.transform.position.y > 16.5f){ //到达最高位置 15 MyStaticClass.gamestart = true; //游戏开始 16 Ball b1 = (Ball)ball1.GetComponent("Ball"); //让球受玩家控制 17 b1.enabled = true; //开启Ball脚本 18 Ball b2 = (Ball)ball2.GetComponent("Ball"); //让球受玩家控制 19 b2.enabled = true; //开启Ball脚本 20 Ball b3 = (Ball)ball3.GetComponent("Ball"); //让球受玩家控制 21 b3.enabled = true; //开启Ball脚本 22 ssflag = false; //上升标志位改为false 23 Destroy(tiShi); //删除提示板对象 24 } 25 return; 26 } 27 foreach (Touch t in Input.touches){ //遍历触控事件 28 RaycastHit hit; //声明射线碰撞 29 Ray ray = Camera.main.ScreenPointToRay(t.position); //从摄像机位置发射一条射线 30 if (t.phase == TouchPhase.Began){ //触碰开始 31 if (Physics.Raycast(ray, out hit)){ //发生射线碰撞 32 if (hit.transform.gameObject.name == "jixu"){ //单击到继续按钮 33 ssflag = true; //上升标志位设为true 34 } 35 if (hit.transform.gameObject.name == "kongzhiban"){ //点到控制板 36 hit.transform.gameObject.rigidbody.AddForce(new Vector3(0, 0, 3), ForceMode.Impulse); //加力 37 Instantiate(huohua, hit.point, hit.transform.rotation); //显示火花 38 } 39 if (hit.transform.gameObject.name == "kele"){ //点到可乐罐子 40 hit.transform.gameObject.rigidbody.AddForce(new Vector3(0, 0, 5), ForceMode.Impulse); //加力 41 Instantiate(huohua, hit.point, hit.transform.rotation); //显示火花 42 }}}}} 第1行~第10行的主要功能为当提示板已经生成后,若当前下降标志位为真就开始使提示板差值下降,当高度小于5.5时停止下降,移动完毕后跳过下面代码。第11行~第26行的主要功能是当上升标志位为真时,匀速上升提示板,当摄像机看不到提示板时,开启Ball.cs脚本使球可以进行发射,将游戏开始标志位改为“true”,并销毁提示板。第27行~第42行的主要功能为辨别触摸事件。当发生触控时,从触控点发射一条射线,若是碰到继续按钮就把上升标志位改为真,若是碰到板子或者罐子就在碰撞点加一个瞬间力,并实例化火花粒子系统。

    6.5.3 球的设置及脚本开发

    上一小节介绍了游戏主场景中主摄像机的开发过程,本小节将详细介绍球类的开发方法和其对应的脚本开发,具体实现了玩家触摸滑动屏幕进行发球以及球在飞行途中碰到特殊物体的特殊处理办法,如广告版、爆炸球等。具体步骤如下。

    (1)新建脚本“Ball.cs”并挂载到每个球上,该脚本的主要功能为判断玩家手指的滑动方式,在其上添加相应的力,实现发球或调整球位置。同时当球在落下时碰到位于屏幕以下的地板触发器,删除球以节省资源。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public class Ball : MonoBehaviour { 4 public GameObject ball; //球对象 5 bool fsflag=false; //发射标志位 6 public LayerMask mask=-1; //层数 7 public Camera mycamera; //主摄像机 8 public Texture2D jia1000; //加1000分图片 9 private float startY = 0; //触控开始时Y坐标 10 private float startX = 0; //触控开始时X坐标 11 private float zsp=10f; //给球施加在Z轴的力 12 private float dx=0; //手指帧X坐标位移 13 private float dy=0; //手指帧Y坐标位移 14 private bool scflag = false; //删除标志位 15 private float sctime=0; //删除时间 16 void Start () { 17 scflag = false; //删除该球标志位置为false 18 } 19 void FixedUpdate () { 20 if (scflag){ //碰到地板触发器 21 sctime += Time.deltaTime; //计时 22 if (sctime > 3){ //3秒后删除 23 Destroy(this.gameObject); //删除球 24 } 25 return; //返回 26 } 27 if(this.transform.position.z>-11&&this.transform.position.z<-3){ //球在不可控范围内 28 this.rigidbody.AddForce(new Vector3(0,0,3*zsp),ForceMode.Force); //施加一个瞬间力 29 return; 30 } 31 foreach(Touch t in Input.touches) { //遍历触摸事件 32 RaycastHit hit; //射线碰撞信息 33 if(t.phase==TouchPhase.Began) { //若碰撞开始 34 fsflag=false; //关闭发射标志位 35 Ray ray=Camera.main.ScreenPointToRay(t.position); //定义射线 36 if(Physics.Raycast(ray,out hit,Mathf.Infinity,mask.value)) { //发生射线碰撞 37 if(hit.transform.root.transform==this.transform //碰到该对象 38 &&transform.position.y<0.5) { //球在可触摸位置 39 fsflag=true; //可以发射 40 startX = t.position.x; //触碰起始X位置 41 startY = t.position.y; //触碰起始Y位置 42 }}} 43 else if(t.phase==TouchPhase.Moved&&fsflag) { //滑动过程中 44 rigidbody.WakeUp(); //唤醒刚体 45 dy=t.deltaPosition.y; //记录手指Y帧位移 46 dx=t.deltaPosition.x; //记录手指Y帧位移 47 float moveX=t.position.x; //记录手指X坐标位置 48 float moveY=t.position.y; //记录手指Y坐标位置 49 if(moveY-startY<50) { //手指水平滑动 50 this.rigidbody.AddForce(new Vector3(dx,0,0),ForceMode.Force);//使球左右滑动 51 }else 52 if(moveY-startY>=50) { //手指向上滑动 53 this.rigidbody.AddForce(new Vector3(3*dx+(startX-Screen.width/2)/16, 54 dy * 2, 8 * zsp), ForceMode.Force); //给球添加一个力 55 }}}} 56 void OnTriggerEnter(Collider target){ //碰到触发器 57 if (target.gameObject.layer == 11) { //球撞到地板 58 scflag = true; //删除标志位改为true 59 }}} 第1行~第15行为变量声明,在本脚本中使用到较多变量,在这里进行声明并初始化。有球游戏对象、摄像机、发球标志位、加分图片、删除标志位、计时器以及各类位置坐标用于记录手指的位置,以便下面代码调用。第16行~第18行是对Start方法的重写,在本场景加载时调用。在这里的主要功能是初始化删除标志位为“false”。当该标志位为“true”时会对本球进行删除,所以在游戏开始时对其进行初始化,避免错误删除。第19行~第30行的主要功能是判断删除标志位是否为真,若满足,就开始计时,并在3秒后删除该球。若是球在不可控制范围内,就添加一个向前的力,这样的射击效果会相较于让球按照抛物线飞行可控性更高,降低了游戏的难度。第31行~第42行的主要功能为当发生触摸事件时,从主摄像机声明射线判断触摸到哪个游戏对象,若是碰到球并且触摸相位为开始触摸时,将球设置为可以发射状态并记录下手指位置。第43行~第55行是当触摸相位为触摸中时,记录下手指位置和手指滑动帧位移,并用这些变量计算出一个修正量,最后给球施加一个带有修正量的力,实现发球或者水平拖动球。该修正量的作用是消除由于视锥口产生的“近大远小”的视觉误差影响射击的问题。第56行~第59行是对“OnTriggerEnter”方法的重写。当该游戏对象碰到触发器时调用,若是碰到地板触发器,说明球已经掉出屏幕,所以将删除标志位改为true。在Update中3秒后会对该对象进行删除,避免后台对该球做多余的运动计算。(2)下面开发球的碰撞脚本,新建脚本“LiZi.cs”并将其挂载到每个球游戏对象上,该脚本处理球的碰撞事件,如碰撞到爆炸罐子发生的爆炸事件、碰到加球罐子会增加一个球、碰到其他物体播放粒子系统同时播放音效等。脚本代码如下。 1 using UnityEngine; 2 using System.Collections; 3 public class LiZi : MonoBehaviour { 4 public GameObject yanwu; //烟雾粒子系统 5 public GameObject huohua; //火花粒子系统 6 public GameObject baozha1; //爆炸特效1 7 public GameObject baozha2; //爆炸特效2 8 private float mytime; //计时器 9 public GameObject ball; //球游戏对象 10 public GameObject baozhaguanzi; //炸碎的罐子预制件 11 public AudioSource qiuzhuangguan; //球撞罐子音效 12 void OnCollisionEnter(Collision target){ 13 if (target.gameObject.layer == 12){ //爆炸罐子 14 Vector3 explosionPos = target.gameObject.transform.position; //爆炸点 15 Destroy(target.gameObject); //销毁爆炸罐子 16 Instantiate(baozhaguanzi,target.gameObject.transform. 17 position,target.gameObject.transform.rotation);//在爆炸罐子的地方实例化炸碎的罐子 18 float radius =4; //爆炸半径 19 float power = 5; //爆炸力 20 Collider[] colliders = Physics.OverlapSphere (explosionPos, radius);//爆炸相交球 21 foreach (Collider hit in colliders){ //遍历爆炸相交球碰到的物体 22 if(hit.rigidbody){ //碰到的是刚体 23 hit.rigidbody.AddExplosionForce(power, explosionPos, radius, 3.0f, ForceMode. Impulse); //爆炸力 24 }} 25 Instantiate(baozha1, this.transform.position, this.transform.rotation); //实例化爆炸火花1 26 Instantiate(baozha2, this.transform.position, this.transform.rotation); //实例化爆炸火花2 27 } 28 if (target.gameObject.layer == 16){ //加球罐子 29 Instantiate(ball,new Vector3(-4.6f,-2.7f,-14.8f),this.transform.rotation); //添加一个球 30 target.gameObject.layer = 9; //将加球管子变成普通罐子 31 } 32 if(target.gameObject.layer==10) { //广告板 33 target.rigidbody.isKinematic = false; //取消对广告板的锁定 34 instantiate(huohua,this.transform.position,this.transform.rotation); //火花粒子 35 } 36 if(target.gameObject.layer==8){ //背景 37 Instantiate(yanwu,this.transform.position,this.transform.rotation); //实例化烟雾粒子系统 38 }else 39 if(target.gameObject.layer==9) { //罐子 40 if (MyStaticClass.yinyue){ //如果不是静音状态 41 qiuzhuangguan.Play(); //播放球撞罐子音效 42 } 43 instantiate(huohua,this.transform.position,this.transform.rotation); //火花粒子系统 44 }}} 第1行~第11行是变量声明,在这里声明了许多变量如各种爆炸火花的粒子系统、炸碎后罐子的预制件、计时器、游戏中的碰撞音效等,供下面代码使用。第12行~第27行的主要功能为当球碰撞到爆炸罐子时,先销毁爆炸罐子,并在该位置上实例化炸碎罐子的预制件,之后在碰撞点生成一个相交球,对相交球接触到的刚体都施加一个来自爆炸点的力,以此来实现炸飞的效果。第28行~第31行为球撞到加球的罐子,在放球台子的最左边实例化一个球,球会在重力的作用下慢慢滚入屏幕,最后将撞到的加球罐子的层“guanzi3”改为普通罐子所处的层“guanzi”,避免下次再次碰到该球时进行重复加球。第32行~第35行的主要功能为当球碰到广告板的时候,激活广告板的刚体属性中的isKinematic组件,解除其锁定状态使其可以在重力的作用下自由下降,实现将广告板撞落的效果。并在碰撞点实例化火花粒子系统。第36行~第44行的主要功能是当球碰到背景或罐子时,实例化烟雾粒子系统或火花粒子系统,并从静态类中取出“yinyue”标志位判断当前是否为静音状态,判断是否播放音效。

    6.5.4 罐子的设置及脚本开发

    在上一小节中介绍了球的脚本开发,本小节将详细介绍罐子上所挂载脚本“GuanZi.cs”的开发,该脚本实现了当罐子落下平台后播放加分动画的功能。具体步骤如下。

    新建脚本“GuanZi.cs”并挂载到每个罐子游戏对象上。该脚本主要用于当罐子落下平台时播放加分动画。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public class GuanZi : MonoBehaviour { 4 rivate int flag=0; //播放加分标志位 5 public Texture2D jia100; //加100图片 6 public GUIStyle myStyle; //GUI风格 7 public Camera mycamera; //主摄像机 8 Vector3 wtos; //球掉落的位置 9 private float temptime; //显示加分的时间 10 void Update () { 11 if(flag==1){ //开始加分 12 temptime += Time.deltaTime; //开始计时 13 }} 14 void OnGUI(){ 15 if(flag==1&&temptime<2){ //加分且播放时间小于2秒 16 GUI.Label(new Rect(wtos.x,Screen.height/2, 17 30/temptime,10/temptime),jia100,myStyle); //显示加分2秒 18 }else 19 if(temptime>=2){ //两秒后 20 flag = 0; //重置标志位 21 temptime = 0.1f; //重置时间 22 Destroy(this.gameObject); //销毁罐子 23 }} 24 void OnTriggerEnter(Collider target){ //碰到触发器 25 if(target.gameObject.layer==11){ //罐子撞到地板 26 MyStaticClass.setzongfen(MyStaticClass.getzongfen() + 100); //加分 27 wtos = mycamera.WorldToScreenPoint(this.transform.position);//罐子掉下的位置 28 flag=1; //播放加分 29 }}} 第1行~第9行的主要功能为变量声明,在这里声明了加分标志位、加分图片、摄像机游戏对象、球位置坐标等,以便下面的代码使用。第10行~第13行是对Update方法的重写,该方法每帧调用一次,这里的作用是当加分标志位为1时,使计时器temptime开始计时。第14行~第23行是对OnGUI方法的重写,该方法用于在屏幕上绘制图片,每帧调用若干次。这里的作用是当加分标志位为1时,开始绘制加100分图片,同时使该图片在播放的过程中进行缩放。2秒后停止绘制并删除罐子游戏对象。第24行~第29行是对OnTriggerEnter方法的重写,当游戏对象撞击到触发器时调用。当罐子碰撞到地板层(触发器)时加分,并记录下掉落位置。将加分标志位改为可加分状态,后面将根据该标志位进行加分。

    6.5.5 控制板的设置及脚本开发

    上一个小节介绍了游戏中罐子的开发,下面将详细介绍游戏场景中控制板的脚本开发,控制板处于游戏的左上角,上面有一系列功能按钮如暂停按钮、重新开始按钮、静音按钮、继续按钮、退出游戏按钮,下面将介绍这些功能的实现。具体步骤如下。

    (1)新建脚本“GameControlPanel.cs”并挂载在游戏控制板游戏对象上。该脚本用于实现手指滑动控制控制板的上升和下降,以及单击控制板上各个按钮所实现的功能。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public class GameControlPanel : MonoBehaviour { 4 private bool gnflag=false; //false简要功能 true全部功能 5 private bool cmflag = false; //触摸到控制板标志位 6 private int donghua =0; //1上移 2下移 7 public LayerMask mask; //层数 8 private float startY = 0; //手指按下位置 9 public GameObject joint; //控制板连接关节点 10 public GameObject huohua; //粒子系统 11 public GameObject yinyue; //音效按钮 12 public bool ksflag = true; //游戏开始标志位 13 void Start () { 14 MyStaticClass.gameoverflag = false; //初始化游戏结束标志位 15 MyStaticClass.shengqiu = 0; //初始化剩余球数 16 yinyue.renderer.enabled = !MyStaticClass.yinyue; //根据是否静音确定静音按钮的图案 17 } 18 void FixedUpdate(){ 19 if (MyStaticClass.gamestart&&ksflag){ //游戏开始 20 this.transform.position = new Vector3(this.transform.position.x, //下降控制板动画 21 Mathf.Lerp(this.transform.position.y, 8f, Time.deltaTime), this.transform. position.z); 22 joint.transform.position = new Vector3(joint.transform.position.x, //下降关节点动画 23 Mathf.Lerp(joint.transform.position.y, 13.66f, Time.deltaTime), joint. transform.position.z); 24 if(this.transform.position.y<8.1f){ //下降到最低点 25 ksflag = false; //将开始标志位置为false 26 }} 27 if (MyStaticClass.gameoverflag){ //若是游戏结束 28 this.transform.position = new Vector3(this.transform.position.x,//上升控制板动画 29 Mathf.Lerp(this.transform.position.y, 11.9f, Time.deltaTime), this.transform. position.z); 30 joint.transform.position = new Vector3(joint.transform.position.x, //上升关节点动画 31 Mathf.Lerp(joint.transform.position.y, 17.5f, Time.deltaTime), joint. transform.position.z); 32 return;//返回 33 } 34 ...//这里省略了一些代码,下面将详细介绍 35 }} 第1行~第12行的主要功能是变量声明,在这里声明了功能标志位、触摸标志位、粒子系统等游戏变量,以便下面调用。第13行~第17行是对Start方法的重写,在本场景加载时调用。在这里的功能是初始化游戏结束标志位、剩余球数并且根据静态类中静音标志位的状态决定静音按钮的图案。第18行~第35行是对“FixedUpdate”方法的重写,该方法每过固定时间调用一次。在这里的主要功能是当游戏开始标志位为真时下降控制板和控制板关节点,对应当游戏结束标志位为真时上升控制板和关节点。(2)下面讲解实现控制板上升或者下降动画的代码片段,主要思路是根据之前修改过的标志位对控制板对象进行差值滑动。实现代码如下。 1 if (donghua == 1){ //上移动画 2 if (this.transform.position.y > 8.0f){ //到达指定位置 3 this.transform.position = new Vector3(this.transform.position.x, 8.07f, this. transform.position.z);//改变阀值 4 donghua = 0; //停止播放动画 5 GameObject[] ball = GameObject.FindGameObjectsWithTag("ball"); //所有标签为ball的物体数组 6 for (int i = 0; i < ball.Length; i++){ //遍历球对象数组 7 Ball b = (Ball)ball[i].GetComponent("Ball"); //找到球上的Ball.cs脚本组件 8 b.enabled = true; //开启脚本,使球受控制 9 } 10 return; //返回 11 } 12 this.transform.position = new Vector3(this.transform.position.x,//上升控制板动画 13 Mathf.Lerp(this.transform.position.y, 8.07f, Time.deltaTime), this.transform.position.z); 14 joint.transform.position = new Vector3(joint.transform.position.x,//上升关节点动画 15 Mathf.Lerp(joint.transform.position.y, 13.65051f, Time.deltaTime), joint.transform.position.z); 16 gnflag = false; //改为简易功能标志位 17 } 18 else if (donghua == 2){ //下移动画 19 GameObject[] ball = GameObject.FindGameObjectsWithTag("ball");//所有标签为ball的物体数组 20 for (int i = 0; i < ball.Length; i++){ //遍历球对象数组 21 Ball b = (Ball)ball[i].GetComponent("Ball"); //找到球上的Ball.cs脚本组件 22 b.enabled = false; //关闭脚本,使球不受控制 23 } 24 if (this.transform.position.y < 4.6f){ //到达指定位置 25 this.transform.position = new Vector3(this.transform.position.x,4.58f,this. transform.position.z);//改变阀值 26 donghua = 0; //停止播放动画 27 return; //返回 28 } 29 this.transform.position=new Vector3(this.transform.position.x,//下降控制板动画 30 Mathf.Lerp(this.transform.position.y, 4.58f, Time.deltaTime), this.transform. position.z); 31 joint.transform.position = new Vector3(joint.transform.position.x,//下降关节点动画 32 Mathf.Lerp(joint.transform.position.y, 10.16f, Time.deltaTime), joint.transform. position.z); 33 gnflag = true; //当前为全部功能 34 } 第1行~第17行的主要功能为实现了控制板上移动画,使控制板和控制板上挂载的铰链关节点同步差值向上滑动,到达指定地点后遍历所有球对象,并将上面挂载的“Ball”脚本逐个开启,使球受玩家控制。第18行~第34行的主要功能为实现控制板下移动画,使控制板和控制板上挂载的铰链关节点同步差值向下滑动,到达指定地点后遍历所有球对象,并将上面挂载的“Ball”脚本逐个关闭,使球不受玩家控制。(3)下面介绍如何实现通过判断玩家的触摸行为,使控制板做相应的运动,同时包含控制板上暂停按钮、重新开始按钮、继续按钮、退出按钮、静音按钮的按键监听的代码,代码片段如下。 1 foreach (Touch t in Input.touches) { //遍历触控事件 2 RaycastHit hit; //声明射线碰撞 3 if (t.phase == TouchPhase.Began) { //触碰开始 4 cmflag = false; //初始化触摸到控制板标志位 5 Ray ray = Camera.main.ScreenPointToRay(t.position); //发射一条射线 6 if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask.value)){//射线碰撞 7 if (hit.transform.root.transform == this.transform){ //碰到该对象 8 cmflag = true; //触摸标志位为true 9 startY = t.position.y; //记录触摸起始位置 10 }}} 11 else if (t.phase == TouchPhase.Moved&&cmflag){ //手指移动中 12 float moveY = t.position.y; //记录当前移动位置 13 if (moveY - startY > 10 && gnflag){ //向上滑且当前是全部功能 14 donghua = 1; //动画上移 15 } 16 else if (moveY - startY < -10 && !gnflag){ //向下滑且当前是简要功能 17 donghua = 2; //动画下移 18 }} 19 else if (t.phase == TouchPhase.Ended&&cmflag){ //触摸事件结束 20 Ray ray = Camera.main.ScreenPointToRay(t.position); //声明射线 21 if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask.value)){ //射线碰撞 22 if (hit.transform.gameObject.name == "Ban"){ //触摸到板 23 this.rigidbody.AddForce(new Vector3(0, 0, 3), ForceMode.Impulse); //加一个瞬间力 24 Instantiate(huohua, hit.point, hit.transform.rotation); //显示火花 25 } 26 if (hit.transform.gameObject.name == "zanting"){ //暂停按钮 27 if(!gnflag){ //功能为简易版 28 donghua = 2; //开始下降动画 29 }} 30 if (hit.transform.gameObject.name == "chongkaishi") { //重新开始按钮 31 MyStaticClass.setzongfen(0); //重置分数为0 32 MyStaticClass.gameoverflag = false; //初始化游戏结束标志位为否 33 Application.LoadLevel(Application.loadedLevelName); //重新加载当前场景 34 } 35 if (hit.transform.gameObject.name == "kaishi"){ //开始按钮 36 donghua = 1; //转换为简要按钮模式 37 } 38 if (hit.transform.gameObject.name == "jingyin"){ //静音按钮 39 bool tempflag = MyStaticClass.yinyue; //获取当前是否静音 40 tempflag = !tempflag; //置反 41 MyStaticClass.yinyue = tempflag; //重置标志位 42 hit.transform.gameObject.renderer.enabled = !tempflag;//更改静音按钮图案 43 } 44 if (hit.transform.gameObject.name == "tuichu"){ //退出按钮 45 Application.LoadLevel("Zhujiemian"); //加载主菜单界面 46 }}}} 第1行~第10行的主要功能是当手指放下时,声明一条从摄像机发出的垂直于摄像机的射线。若是该射线碰到控制板就将触摸标志位cmflag改为true,并记录下手指当前的位置startY。第11行~第18行是触摸相位为手指移动中,记录手指的位移,若是手指为向下滑动并且当前控制板的功能为简易功能,就改变动画标志位donghua为2,播放下移动画。若是手指向上滑动,则改变标志位donghua为1,播放上移动画。第19行~第46行为触摸结束时执行的代码,主要功能为判断手指单击到的是什么按钮,若是碰到重新开始按钮就重新加载本场景,若是碰到静音按钮就更改静态类中的静音标志位yinyue以及静音按钮的图案,若单击的是退出按钮就回到游戏的主菜单界面。

    6.5.6 分数板的设置及脚本开发

    上一小节介绍了游戏中控制板的开发,本小节将详细介绍游戏结束后显示的分数板相关脚本的开发过程,以实现分数板上显示分数、星星数的效果,以及分数板下的“进入下一关”按钮的按键监听。具体步骤如下。

    (1)新建脚本“ScoreBoard.cs”并挂载到分数板预制件上。该脚本的主要功能是在分数板上呈现本局游戏所获得的分数,以及分数板上的按钮监听。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public class ScoreBoard : MonoBehaviour { 4 public GameObject gewei; //总分 5 public GameObject shiwei; //十位分数 6 public GameObject baiwei; //百位分数 7 public GameObject qianwei; //千位分数 8 public GameObject[] mystar; //星星板 9 public GameObject zhubeijing; //主背景 10 public Material[] mat; //数字材质 11 public Material star; //星星材质 12 public Material picnull; //透明图片 13 void Start () { 14 int zhu = zhubeijing.renderer.material.renderQueue; //主背景的渲染排序 15 int score = MyStaticClass.getzongfen(); //取出总分数 16 gewei.renderer.material = mat[score]; //设置分数 17 shiwei.renderer.material = mat[score0-score]; //赋予十位数材质 18 baiwei.renderer.material = mat[(int)(score00/100)]; //赋予百位数材质 19 if ((int)score / 1000 == 0) { //最大不到千位 20 qianwei.renderer.material = picnull; //将千位设置为透明材质 21 } 22 else{ 23 qianwei.renderer.material = mat[(int)score / 1000]; //赋予千位材质 24 } 25 if (MyStaticClass.shengfu == 1) { //游戏胜利 26 int xingxingshu = MyStaticClass.shengqiu + 1; //不剩1星 剩一个2星 两个3星 27 if (xingxingshu >= 3){ //星星数超过3为 28 xingxingshu = 3; //星星数 29 } 30 if (Application.loadedLevelName == "Level1") { //按照不同关卡分别存分数 31 PlayerPrefs.SetInt("level1score", Mathf.Max(PlayerPrefs.GetInt("level1score"), xingxingshu)); 32 } 33 else if (Application.loadedLevelName == "Level2") { //按照不同关卡分别存分数 34 PlayerPrefs.SetInt("level2score", Mathf.Max(PlayerPrefs.GetInt("level2score"), xingxingshu)); 35 } 36 else if (Application.loadedLevelName == "Level3"){ //按照不同关卡分别存分数 37 PlayerPrefs.SetInt("level3score", Mathf.Max(PlayerPrefs.GetInt("level3score"), xingxingshu)); 38 } 39 else if (Application.loadedLevelName == "Level4") { //按照不同关卡分别存分数 40 PlayerPrefs.SetInt("level4score", Mathf.Max(PlayerPrefs.GetInt("level4score"), xingxingshu)); 41 } 42 mystar[0].renderer.material = star; //为首个星星板赋予星星材质 43 mystar[0].renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染星星 44 if (MyStaticClass.shengqiu >= 2) { //剩余球大于两个 45 for (int i = 1; i < 3; i++){ //遍历星星数组 46 mystar[i].renderer.material = star; //为星星板赋予材质 47 mystar[i].renderer.material.renderQueue = zhu + 1;//在主背景渲染后渲染星星 48 }} 49 else if (MyStaticClass.shengqiu >= 1){ //剩余球的数量大于1 50 mystar[1].renderer.material = star; //为首个星星板赋予星星材质 51 mystar[1].renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染星星 52 }}else if(MyStaticClass.shengfu==2) { //游戏失败 53 for (int i = 0; i < mystar.Length;i++){ //遍历星星板 54 mystar[i].renderer.material=picnull; //为星星板赋予透明材质 55 mystar[i].renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染星星 56 }} 57 gewei.renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染分数 58 shiwei.renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染分数 59 baiwei.renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染分数 60 qianwei.renderer.material.renderQueue = zhu + 1; //在主背景渲染后渲染分数 61 }} 第1行~第12行为变量声明,在这里声明了分数数字材质以及星星材质,同时还声明了分数板上用于显示分数的四个平面的游戏对象,分别代表个位、十位、百位、千位;以及用于显示星星的三个平面游戏对象,方便下面代码调用。第13行~第24行的主要功能为在游戏结束后并且已经生成分数板时,将本局得分的每一位拆分出来,并分别赋予在前面生成的四个代表个位、十位、百位、千位的平面上。第25行~第41行的功能是当游戏胜利结束时,若是获得的分数大于历史最高分,则根据当前的关卡名,将分数储存入数据库。U第42行~第51行的主要功能为游戏胜利结束后,根据当前剩余球的数量计算出应该获得的星星数量,并在星星板上呈现。为了避免星星板和绘制星星的屏幕深度检测失败,这里控制星星在星星板绘制后再进行绘制。第52行~第61行的主要功能为当游戏失败时,在分数板上绘制分数,同样的放在分数板后绘制,避免深度检测失败,但不绘制星星。

    (2)上面了介绍生成分数板后绘制分数与星星的方法,下面将介绍分数板触摸事件的实现,主要包含单击分数板上的进入下一关按钮进行跳转场景、单击退出按钮回到主界面、单击重新开始按钮重新加载本关。代码片段如下。

    1 void Update(){ 2 foreach (Touch t in Input.touches) { //遍历触控事件 3 RaycastHit hit; //声明射线碰撞 4 Ray ray = Camera.main.ScreenPointToRay(t.position); //声明一条射线 5 if (t.phase == TouchPhase.Began){ //触碰开始 6 if (Physics.Raycast(ray, out hit)){ //若是发生射线碰撞 7 if (hit.transform.gameObject.name == "tuichu") { //单击退出按钮 8 Application.LoadLevel("Zhujiemian"); //加载主菜单界面 9 } 10 if (hit.transform.gameObject.name == "chongkaish"){ //单击重新开始按钮 11 MyStaticClass.gameoverflag = false; //将游戏开始标志位改为否 12 MyStaticClass.setzongfen(0); //重置得分 13 Application.LoadLevel(Application.loadedLevelName);//重新加载当前场景 14 } 15 if (hit.transform.gameObject.name == "xiayiguan"){ //单击下一关按钮 16 if (Application.loadedLevelName == "Level1"){ //如果当前是关卡1 17 MyStaticClass.gameoverflag = false; //将游戏结束标志位改为否 18 MyStaticClass.setzongfen(0); //重置总分 19 Application.LoadLevel("Level2"); //加载关卡2 20 }else if (Application.loadedLevelName == "Level2"){ //当前是关卡2 21 MyStaticClass.gameoverflag = false; //将游戏结束标志位改为否 22 MyStaticClass.setzongfen(0); //重置总分 23 Application.LoadLevel("Level3"); //加载关卡3 24 } 25 else if (Application.loadedLevelName == "Level3"){ //如果当前是关卡3 26 MyStaticClass.gameoverflag = false; //将游戏结束标志位改为否 27 MyStaticClass.setzongfen(0); //重置总分 28 Application.LoadLevel("Level4"); //加载关卡4 29 } 30 else if (Application.loadedLevelName == "Level4"){ //当前是关卡4 31 Application.LoadLevel("Zhujiemian"); //加载主菜单界面 32 }}}}}} 第1行~第6行的主要功能为当发生触摸事件时,在触摸点声明一条垂直于摄像机射出射线的射线ray,若是手指触摸相位touch.phase为开始触摸且发生了射线碰撞Raycast,就证明单击到某物体,执行相应代码。第7行~第14行为若是单击退出游戏按钮,就跳转到游戏主菜单界面;若单击的是重新开始按钮,将静态类中的游戏结束标志位gameoverflag改为否,用setzongfen()方法重置静态类中的关卡得分,重新加载当前关卡。第15行~第32行的主要功能为当玩家单击的是进入下一关按钮时根据玩家当前的关卡名,加载下一关,并初始化静态类中的总分和游戏结束标志位gameoverflag为false。若当前已经是最后一关就回到主菜单场景。

    6.5.7 静态类的开发

    在游戏开发中时常要用到静态类,静态类不能被实例化,可以直接使用其属性与方法,静态类的最大特点是共享,不论是在哪一个场景都可以获得其属性,而且调用速度快,不会影响游戏的性能。所以本游戏的静态类中储存了一些设置相关的标志位、总分等。开发步骤如下。

    新建一个C#脚本,重命名为“MyStaticClass.cs”,该脚本为静态类脚本,所以不用挂载到任何对象上,其他脚本要使用静态类中的成员时可以直接用“类名.方法”或“类名.变量”的语法访问。脚本代码如下。

    1 using UnityEngine; 2 using System.Collections; 3 public static class MyStaticClass { 4 private static int zongfen=0; //总分 5 public static int shengqiu=0; //剩余球 6 public static int shengfu = 0; //0未完成 1游戏胜利 2游戏失败 7 public static bool gameoverflag = false; //游戏结束 8 public static bool gamestart = true; //游戏开始标志位 9 public static bool yinyue=true; //是否开启音效 10 public static int getzongfen(){ //获得总分方法 11 return zongfen; //返回总分 12 } 13 public static void setzongfen(int x){ //设置总分方法 14 zongfen = x; //设置总分 15 }}

    说明

    该脚本是静态类,不需要像其他脚本继承MonoBehaviour类,里面的成员变量和成员方法都应当是静态的,在该脚本中储存了与游戏设置、计分、状态相关的标志位与变量,在游戏运行中可以随时取出这些变量,非常方便。

    相关资源:Unity3D游戏场景设计实例教程
    最新回复(0)