本节书摘来异步社区《Unity 游戏案例开发大全》一书中的第6章,第6.4节,作者: 吴亚峰 , 杜化美 , 于复兴 责编: 张涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。
Unity 游戏案例开发大全前一小节介绍了游戏的整体架构,从本节开始将介绍本案例各个场景的开发,首先介绍本案例的主菜单场景,该场景在游戏开始时呈现,控制所有界面之间的跳转。本节将在前面介绍的基础上对此场景的开发细节进行进一步的介绍。
场景搭建主要是针对游戏地图、灯光、天空盒等环境因素的设置。通过本小节学习,读者将会了解到如何构建出一个基本的游戏世界,接下来将具体介绍场景的搭建步骤。
(1)新建一个场景作为主菜单场景,具体步骤为“File”→“New Scene”,如图6-9所示。单击File选项中的“Save Scene”选项,或者使用保存快捷键“Ctrl+C”,在保存对话框中将场景名重命名为“Zhujiemian”。
(2)设置游戏中的环境光。环境光指的是在游戏场景中除去灯光后环境的亮度或颜色,具体步骤为单击“Edit”→“Render Setting”,单击属性查看器中的“Ambient Light”设置环境光的颜色和亮度,在这里选择白色,其他参数设置如图6-10所示。
(3)创建主菜单背景。按步骤“GameObject”→“Creat Other”→“Plane”创建出一个平面并重命名为“beijing”作为主菜单的背景。调整其大小和位置,并将纹理图“zhucaidan.png”拖拉到此对象上。效果如图6-11所示。
(4)选中“beijing”游戏对象,按步骤单击工具栏的“Component”→“Physic”→“MeshCollider”为背景游戏对象添加网格碰撞器。并将该对象勾选为“Static”静态的,使该背景屏幕不参加运动。参数设置如图6-12所示。
(5)为该对象指定层,在游戏中通过层的概念可以轻松地管理一批相同性质的对象。选中“beijing”游戏对象,在属性面板中单击“Layer”下拉列表,选择“Add Layer…”新建层beijing,并将该游戏对象设置为“beijing”层,如图6-13所示。
(6)创建游戏名称板。将模型“GameName.fbx”导入并从资源列表中拖拉到游戏场景中,自动生成GameName游戏对象。调整其大小和位置并将图片“youximing.png”拖拉到该游戏对象上,如图6-14所示。
(7)选中“GameName”游戏对象,按步骤单击工具栏的“Component”→“Physic”→“Rigidbody”、“Component”→“Physic”→“BoxCollider”,分别为游戏名称板游戏对象添加刚体和盒子碰撞器。组件列表如图6-15所示。
(8)下面是游戏开始按钮的创建。将模型“GameStart.fbx”导入并拖曳到游戏场景中,生成“GameStart”游戏对象。调整其大小和位置后并将图片“youxikaishi.png”拖到该游戏对象上。最终效果如图6-16所示。
(9)为游戏开始按钮对象添加刚体和盒子碰撞器,选中“GameStart”游戏对象,按步骤单击,“Component”→“Physic”→“Rigidbody”、“Component”→“Physic”→“BoxCollider”,分别为游戏对象添加刚体和盒子碰撞器。并将其勾选为静态的,如图6-17所示。
(10)下面创建游戏中的设置按钮、退出按钮、静音按钮、左右箭头按钮。将模型“GameOption.fbx”拖拉到游戏场景中,调整大小后为其添加刚体和盒子碰撞器组件并将其设置为静态的。五种按钮使用相同的模型,贴图分别是“shezhi.png”、“tuichu.png”、“jingyin.png”、“jixu.png”。创建好后在资源列表中如图6-18所示。
(11)创建选关按钮,将“GameName.fbx”拖到场景中,并重命名为“xuanguan2”,调整大小和位置后将纹理图“bj1.png”拖拉到该游戏对象上,将其勾选为静态的,并添加刚体和盒子碰撞器,如图6-19所示。
(12)按步骤“GameObject”→“Create Other”→“Plane”创建一个平面,重命名为“Guanshu”,并使其成为xuanguan1的子对象。调整大小后将数字图片“num-03”拖拉到该对象上,并将其Shader选为带有透明通道的Transparent/Diffuse。将其勾选为静态的,效果如图6-20所示。
(13)下面创建选关按钮上的分数板,这个游戏对象的主要功能是在“Choose.cs”脚本的左右下在按钮上绘制出获得的星星。将模型“GameStart.fbx”拖到场景中并使其成为xuanguan1的子对象,调整大小后将“xingxingban.png”拖拉到该游戏对象上,并将其勾选为静态的。
(14)创建三个Plane,调整大小并对应放在星星板上的星星位置分别重命名为“Star1”、“Star2”、“Star3”,使其成为fenshuban游戏对象的子对象。为其附加贴图numnull.png并将其着色器设置为带有透明通道的Transparent/Diffuse。效果如图6-21所示。
(15)本场景中共有四个选关按钮“xuanguan1”、“xuanguan2”、“xuanguan3”、“xuanguan4”,制作方法相同,读者可以参照步骤(11)~步骤(14)的方法自行制作和摆放,并将其子对象“Guanshu”的贴图改为相应的关卡数字。
(16)开始层的创建,通过将相似的游戏对象分到同一层,可以在代码中方便地进行统一处理。创建一个层方法时单击属性面板中的“Layer”→“Add Layer…”,创建一个“jinshu”层。并将所有按钮以及游戏名称板都勾选为此层。
(17)创建光源。选择“GameObject”→“Create Other”→“Directional Light”后会自动创建一个定向光源,该种类型的光源类似太阳光,无论设置在什么位置都可以影响到游戏场景中的所有物体,如图6-22所示。调整其参数如图6-23所示。
(18)实现落叶效果,该效果由粒子系统实现,按步骤“GameObject”→“Create Other”→“ParticleSystem”,在组件窗口的粒子系统设计器中可以调整其参数,读者可以自行调整或参看项目中的设置。将粒子的纹理图替换为“shuye.png”,如图6-24所示。
(19)检查该场景中游戏对象的参数设置,除了主摄像机和粒子系统,其余游戏对象应当全部为静态的,所有的按钮应当加上刚体和相应的碰撞器(MeshCollider或BoxCollider),自行调整灯光和环境光使得光线合适。至此,基本的主菜单场景搭建完毕。
前一小节完成了主场景的搭建,本小节将在搭建好的场景基础上介绍主摄像机相关脚本的开发。实现相应玩家手指滑动屏幕操控摄像机移动和单击主菜单场景中的按钮实现按钮的事件监听的功能,具体步骤如下。
(1)按步骤在Assets文件夹中单击鼠标右键,选择“Create”→“Folder”新建“script”文件夹。并在该文件夹中单击鼠标右键,在弹出的菜单中选择“Create”→“C# Script”创建脚本,命名为“MyMainMenu.cs”,如图6-25所示。
图6-25 创建脚本
(2)双击脚本,进入“MonoDevelop”编辑器中,开始脚本的编写。本脚本主要功能为通过3D拾取技术判断玩家的操控,并根据结果移动摄像机或者实现单击按钮的事件监听、相应玩家的触摸操控。脚本代码如下。
1 using UnityEngine; 2 using System.Collections; 3 public class MyMainMenu : MonoBehaviour { 4 public GameObject mycamera; //摄像机游戏对象 5 public GameObject yanwu; //烟雾预制件对象 6 public GameObject huohua; //火花预制件对象 7 public GameObject yinyue; //音乐按钮 8 private float startX = 0; //手指按下X坐标 9 private int fyflag=1; //翻页标志位 0 1 2代表左中右 10 private bool kzflag = false; //是否正在控制标志位 11 private Vector3[] fypos = new Vector3[3]{new Vector3(-5.6f,0,-8), 12 new Vector3(0,0,-8),new Vector3(5.6f,0,-8)}; //摄像机的3个位置 13 void Start(){ 14 yinyue.renderer.enabled = !MyStaticClass.yinyue; //初始化当前静音按钮状态 15 } 16 void Update(){ 17 foreach (Touch t in Input.touches){ //遍历触摸事件 18 RaycastHit hit; //射线碰撞信息 19 Ray ray = Camera.main.ScreenPointToRay(t.position); //声明射线 20 if (t.phase == TouchPhase.Began) { //若碰撞开始 21 kzflag = true; //开始控制 22 if (Physics.Raycast(ray, out hit)){ //发生射线碰撞 23 if(hit.transform.gameObject.layer==8){ //碰到背景层 24 Instantiate(yanwu,hit.point,hit.transform.rotation); //实例化烟雾 25 } 26 if (hit.transform.gameObject.layer == 17) { //碰到金属层 27 Instantiate(huohua, hit.point, hit.transform.rotation); //实例化火花 28 } 29 if (hit.transform.gameObject.name == "GameStart"){ //碰到开始游戏 30 fyflag = 2; //修改翻页标志位 31 } 32 if (hit.transform.gameObject.name == "GameOption"){//碰到设置按钮 33 fyflag = 0; //修改翻页标志位 34 } 35 if (hit.transform.gameObject.name == "zuojiantou" 36 || hit.transform.gameObject.name == "youjiantou") { //碰到左右箭头 37 fyflag = 1; //修改翻页标志位 38 } 39 if (hit.transform.gameObject.name == "jingyin") { //单击静音按钮 40 bool tempsound = MyStaticClass.yinyue; //获取当前声音状态 41 tempsound = !tempsound; //将状态置反 42 MyStaticClass.yinyue = tempsound; //设置声音状态 43 hit.transform.gameObject.renderer.enabled = !tempsound; //设置显示静音反选图片 44 } 45 if(hit.transform.gameObject.name=="GameExit") { //单击退出游戏按钮 46 Application.Quit(); //游戏退出 47 } 48 if(hit.transform.gameObject.name=="xuanguan1"){ //第一关 49 Application.LoadLevel("Level1"); //加载第一关场景 50 } 51 //以下省略一些相似代码,其功能为单击其他的选关按钮加载不同的场景 52 } 53 startX = t.position.x; //记录下按下位置 54 …//以下省略部分代码,下面将详细介绍 55 }}}}第4行~第12行的主要功能为变量声明,在这里声明了粒子系统、摄像机、音乐按钮游戏对象的引用,以及摄像机移动时的标志位和位置坐标,方便下面的代码调用。
第13行~第15行是对Start方法的重写,在该场景加载时调用,用于根据判断当前是否静音状态来改变静音按钮前的挡板是否渲染,以此改变静音按钮的图案。第16行~第28行的主要功能为当发生触摸事件时,从触摸点声明一条垂直于主摄像机射出射线的射线,通过射线碰撞判断玩家的动作。当触摸相位为刚按下且玩家单击的是金属层或背景层,则生成相应的粒子系统。第29行~第38行的主要功能是当用户单击翻页箭头功能按钮、开始游戏按钮或者游戏设置按钮时,根据判断改变翻页标志位,使其准备跳转到相应的界面,下面有代码会根据最终标志位状态对摄像机进行移动实现翻页。第39行~第55行的主要功能为当玩家单击静音按钮时,将反选图标的显示方式置反,这样就做出了静音按钮和非静音按钮,若玩家单击的是退出按钮则退出游戏,若玩家单击的是选关按钮,就加载相应的关卡。(3)上面介绍了发生射线碰撞且触摸相位为开始触摸时的代码片段,下面介绍“MyMainMenu.cs”脚本中剩下的代码,包含触摸中相位和触摸结束相位执行的代码,以及最后根据当前翻页标志位的状态进行翻页,代码片段如下。
代码位置:见随书光盘中源代码/第 06 章目录下的 ColaCola/Assets/script/zhucaidan/ MyMainMenu.cs。
1 if (t.phase == TouchPhase.Moved){ //触摸中相位 2 float dx=t.deltaPosition.x; //当前触摸点X坐标 3 if(Mathf.Abs(this.transform.position.x)>7.2f){ //超出边界 4 return; //返回 5 } 6 this.transform.position = new Vector3(this.transform.position.x-0.01f*dx,0,-8); //屏幕随手指滑动 7 }else 8 if(t.phase==TouchPhase.Ended) { //触摸结束 9 float endX = t.position.x; //记录下手指停下位置 10 if(endX-startX>200) { //向左滑动 11 fyflag = fyflag - 1; //更改翻页标志位 12 if (fyflag < 0) { //当前在就在最左边 13 fyflag = 0; //翻页标志位不变 14 }} 15 else if (endX - startX < -200) { //去右 16 fyflag = fyflag + 1; //更改翻页标志位 17 if (fyflag > 2) { //当前在最右边 18 fyflag = 2; //更改翻页标志位 19 }} 20 kzflag = false; //开始控制标志位为否 21 }} 22 if (!kzflag) { //没有被控制 23 this.transform.position = new Vector3(Mathf.Lerp(this.transform.position.x, fypos[fyflag].x, 3 * Time.deltaTime),this.transform.position.y, this.transform. 24 position.z); //根据标志位差值滑动 25 }}}第1行~第7行的功能为当发生触摸且触摸相位为手指触摸移动时,记录下手指的位置,并使屏幕随手指滑动而移动,当滑动超出背景范围时停止移动。
第8行~第21行的主要功能是当触摸结束时,很据手指移动的幅度及方式判断怎样使屏幕滑动,并根据判断结果改变翻页标志位。最后将正在控制标志位设置为false。第22行~第25行的主要功能是当控制结束标志位为真时,根据翻页标志位判断摄像机应当向fypos3数组中具体某个位置移动,并对摄像机执行差值移动,实现翻页的效果。上一小节已经介绍了主菜单界面中主摄像机的开发过程,本小节将对主菜单界面中选关按钮进行脚本开发的详解,脚本实现了在每个选关按钮上绘制该关卡获得的最大星星数量,并控制星星和主背景的绘制顺序。其具体的讲解内容如下。
创建C#脚本“Choose.cs”并将其挂载到前面建立好的11个选关按钮“xuanguan1”、“xuanguan2”、“xuanguan3”、“xuanguan4”上。该脚本的主要功能是在选关按钮上绘制各关卡已获得的星星数量。脚本代码如下。
1 using UnityEngine; 2 using System.Collections; 3 public class Choose : MonoBehaviour { 4 public GameObject[] mystar; //星星板游戏对象 5 public GameObject fenshuban; //分数板游戏对象 6 public Material star; //星星材质 7 public Material picnull; //纯透明材质 8 void Start () { 9 int zhu = fenshuban.renderer.material.renderQueue; //获取分数板的渲染次序 10 int level1score = PlayerPrefs.GetInt("level1score"); //获得第一关得到的星星数 11 int level2score = PlayerPrefs.GetInt("level2score"); //获得第二关得到的星星数 12 int level3score = PlayerPrefs.GetInt("level3score"); //获得第三关得到的星星数 13 int level4score = PlayerPrefs.GetInt("level4score"); //获得第四关得到的星星数 14 if(this.gameObject.name=="xuanguan1"){ //第一关分数板 15 for (int i = 0; i < level1score; i++){ //遍历第一关的分数 16 mystar[i].renderer.material = star; //将得分板上的材质改为星星图案 17 mystar[i].renderer.material.renderQueue = zhu + 1; //放在主背景后渲染 18 } 19 }else 20 if (this.gameObject.name == "xuanguan2"){ //第二关分数板 21 for (int i = 0; i < level2score; i++){ //遍历第二关的分数 22 mystar[i].renderer.material = star; //将得分板上的材质改为星星图案 23 mystar[i].renderer.material.renderQueue = zhu + 1; //放在主背景后渲染 24 } 25 }else 26 if (this.gameObject.name == "xuanguan3"){ //第三关分数板 27 for (int i = 0; i < level3score; i++){ //遍历第三关的分数 28 mystar[i].renderer.material = star; //将得分板上的材质改为星星图案 29 mystar[i].renderer.material.renderQueue = zhu + 1; //放在主背景后渲染 30 } 31 }else 32 if (this.gameObject.name == "xuanguan4"){ //第四关分数板 33 for (int i = 0; i < level4score; i++){ //遍历第四关的分数 34 mystar[i].renderer.material = star; //将得分板上的材质改为星星图案 35 mystar[i].renderer.material.renderQueue = zhu + 1; //放在主背景后渲染 36 }}}} 第4行~第7行的主要功能为变量声明,在这里声明了分数板游戏对象和星星板游戏对象数组,以及星星图案的材质和透明材质,以便后面代码调用。第8行~第13行的主要功能为获取分数板游戏对象的渲染次序,因为某些相距很近的物体在自动进行深度检测时会失败,造成“后面的挡住前面的”效果,所以获取对象的渲染次序后可以代码控制渲染次序,避免这种错误。这里还获得了每一关所得到的星星数量。第14行~第36行的功能是在游戏场景加载时根据每关获得的星星数量在选关按钮下的星星板上绘制星星,并代码控制其在分数板渲染后渲染。 相关资源:Unity_3D_主菜单制作