记录的一个简单课后作业...
1、分析需求文案策划
阅读Unity3D保龄球案例设计和积分规则,最后整理游戏规则:
2、搭建游戏场景
导入场景资源包,发现运行资源包的场景发现报错,原因大体是因为网格内陷导致网格碰撞器出现了些小问题,这通常出现在早期Unity版本资源包。
通过勾选每个保龄球的Collider中Convex选项,将不报错。
加载各种模型资源,一系列的操作,最后搭建将场景搭建完毕。把所有的保龄球放在一个AllBowls空物体下,把AllBowls和ball制作成一个预制体。
3、编写保龄球的控制脚本
新建一个BallController.cs脚本组件,用来管理球的发射方法。普通情况下,正常情况玩保龄球,会有以下几种操作:
- 捡球
- 带球到到发球线
- 给球施加一个方向的力
用射线的方式可以达到需求,但是具体如何实现还得需要分析:
我们可以通过射线找到两个关键点扔球点和发球点,两点确定发球方向。在扔球点捡起球,在发球点发射。
根据场景我选择世界坐标Z=8这个轴作为发球面,这有很大的瑕疵,以后会逐渐修正。
BallController.cs代码:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球的控制 6 /// </summary> 7 public class BallController : MonoBehaviour 8 { 9 10 #region 射线相关 11 12 /// <summary> 13 /// 射线碰撞信息 14 /// </summary> 15 private RaycastHit hit; 16 /// <summary> 17 /// 图层遮罩 18 /// </summary> 19 private LayerMask mask; 20 /// <summary> 21 /// 球的位置 22 /// </summary> 23 public Transform ballTrans; 24 25 #endregion 26 27 /// <summary> 28 /// 推力范围 29 /// </summary> 30 public Transform pushArea; 31 /// <summary> 32 /// 球的半径 33 /// </summary> 34 [SerializeField] 35 private float ballRadius; 36 /// <summary> 37 /// 扔球点 38 /// </summary> 39 private Vector3 ballPushOrgin; 40 /// <summary> 41 /// 球左右方向差量 42 /// </summary> 43 private Vector3 ballPushDir; 44 45 //是否推出去 46 static bool pushed = false; 47 private void Start() 48 { 49 //获取球的半径 50 ballRadius = ballTrans.GetComponent<SphereCollider>().radius; 51 GetComponent<Rigidbody>().isKinematic =true; 52 } 53 54 private void Update() 55 { 56 //发射一条射线 57 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 58 //如果还没有推出去这个球 59 if (!pushed) 60 { 61 //推球逻辑 62 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor"))) 63 { 64 //推出去 65 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 66 if (hit.transform == pushArea) 67 { 68 _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置 69 ballTrans.position = _ballPushTrans; 70 ballPushOrgin = hit.point; ;//记录球的起点 71 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z); 72 } 73 else 74 { 75 if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有) 76 { 77 Vector3 _ballPushDir = hit.point;//记录一下球的终点 78 Debug.Log(ballPushOrgin); 79 ballPushDir = _ballPushDir - ballPushOrgin; 80 pushed = true; 81 ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 82 GetComponent<Rigidbody>().isKinematic = false; 83 ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 20, ForceMode.Impulse); 84 85 } 86 } 87 } 88 } 89 } 90 }
BallController.cs
到现在为止发球功能可以实现。
4、每球得分计算
新建一个Bowl.cs脚本组件,检测得分情况。让每个保龄球均挂载该组件。
关于如何判断这个保龄球是否倒了,选择使用transform.localRotation中X和Z属性,如果他们的绝对值大于0.3f,我们就认为这个保龄球倒了。当然我们可以有其他更好的方法,比如碰撞器和触发器,不过通过计算网格的接触点,这会稍微增加一点小小的性能消耗,这个方法会更精准但是我们并不需要这么精准的操作。
Bowl.cs代码如下:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球 6 /// </summary> 7 public class Bowl : MonoBehaviour 8 { 9 /// <summary> 10 /// 是否得分 11 /// </summary> 12 public bool GetScore; 13 14 private void FixedUpdate() 15 { 16 if (!GetScore) 17 { 18 if (Mathf.Abs(transform.localRotation.x) > 0.3f || Mathf.Abs(transform.localRotation.z) > 0.3f) 19 { 20 21 GetScore = true; 22 Debug.LogWarning("分数加+1"); 23 } 24 } 25 } 26 }
Bowl.cs
最后运行测试一下:
5、UI统计分数面板
UI的搭建选择使用UGUI,首先建立一个新画布和相机,修改命名。
对Player_UI_Camera和Player_UI_Canvas进行调整,以适应场景。
接下来就是UI界面搭建。
新建一个脚本GameManage.cs组件,用来游戏管理。挂载到Player_UI_Panel对象上。可是如何进行游戏管理?把所有的UIText设置更新都写在GameManage.cs里?这无疑对GamManage.cs产生较大的功能负担,所以我们新建的GameManage.cs先不编辑,建一个ScoreItem.cs类,这个东西负责统计存储和修改每轮分数。然后让GameManage去管理它。
编写ScoreItem.cs:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 public class ScoreItem : MonoBehaviour 7 { 8 /// <summary> 9 /// 所有分数文字 10 /// </summary> 11 public Text[] SocreTexts; 12 /// <summary> 13 /// 每轮分数 14 /// </summary> 15 [SerializeField] 16 private int[] Scores; 17 /// <summary> 18 /// 本轮分数总和 19 /// </summary> 20 public int AllScoreItemNum; 21 /// <summary> 22 /// 本轮全中? 23 /// </summary> 24 public bool isStrike = false; 25 /// <summary> 26 /// 本轮补中? 27 /// </summary> 28 public bool isSpare = false; 29 30 public void Start() 31 { 32 Scores = new int[SocreTexts.Length - 1];//每轮分数初始化 33 } 34 /// <summary> 35 /// 每轮分数设置索引为_index的分数文本的值为_score 36 /// </summary> 37 /// <param name="_index">SocreTexts索引</param> 38 /// <param name="_score">分数</param> 39 public void SetScoreText(int _index, int _score) 40 { 41 Scores = new int[SocreTexts.Length];//更新设置分数 42 Scores[_index] = _score;//更新并存储分数 43 AllScoreItemNum = 0; 44 foreach (var item in Scores) 45 { 46 AllScoreItemNum += item;//累加本轮分数总和 47 } 48 } 49 /// <summary> 50 /// 显示所有分数 51 /// </summary> 52 public void ShowScore() 53 { 54 //设置每轮 55 for (int i = 0; i < SocreTexts.Length - 1; i++) 56 { 57 SocreTexts[i].text = Scores[i].ToString(); 58 } 59 SocreTexts[SocreTexts.Length - 1].text = AllScoreItemNum.ToString();//总分设置 60 61 } 62 }
ScoreItem.cs
我们为每个ScoreItem(前九局)和FinalScore(最后一局)这些对象都挂载ScoreItem.cs组件并绑定每个ScoreItem中的ScoreTexts。
其中ScoreItem(前九局)绑定每个子对象的文本对象如下:
最后一个FinalScore绑定如下:
绑定完毕后,我们只要获取每个ScoreItem就能直接更新每局数据了。所以我们要在GameManage内编写这些对象的引用,GameManage只有一个,所以我们可以把它单例,全局调用,SocreItems数组存储每局ScoreItem。我们先简单编写GameManage一下:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 public class GameManage : MonoBehaviour 7 { 8 #region 单例 9 10 private static GameManage _Instacne; 11 /// <summary> 12 /// 游戏管理单例 13 /// </summary> 14 public static GameManage Instacne 15 { 16 get 17 { 18 return _Instacne; 19 } 20 21 } 22 private void Awake() 23 { 24 _Instacne = this; 25 } 26 #endregion 27 /// <summary> 28 /// 每局分数块游戏对象 29 /// </summary> 30 public ScoreItem[] SocreItems; 31 32 /// <summary> 33 /// 每球的临时得分 34 /// </summary> 35 public int _tempScore; 36 37 /// <summary> 38 /// 保龄球瓶预制体 39 /// </summary> 40 public GameObject bowlsPrefabs; 41 42 /// <summary> 43 /// 保龄球控制预制体 44 /// </summary> 45 public BallController ballController; 46 47 48 }
GameManage.cs
把刚才的ScoreItem那些组件绑定,并顺手绑定一下BowlsPrefabs:
绑定结束。
修改重新修改一下Bowl然后再发射一球
1 public class Bowl : MonoBehaviour 2 { 3 /// <summary> 4 /// 是否得分 5 /// </summary> 6 public bool GetScore; 7 8 private void FixedUpdate() 9 { 10 if (!GetScore) 11 { 12 if (Mathf.Abs(transform.localRotation.x) > 0.3f || Mathf.Abs(transform.localRotation.z) > 0.3f) 13 { 14 15 GetScore = true; 16 GameManage.Instacne._tempScore++;//这轮分数 17 } 18 } 19 } 20 }
测试一下,已经清晰的看见GameManage的TempScore变量=6,也就是这一球得分:
6、刷新出球
发球得分已经可以获取到了,那么接下来要实现的功能就是如何结束此球结束,开始下一球,不断刷新出球。我们的思路如下:
所以我们重新修改一下GameManage.cs:
1 #region 单例 2 3 private static GameManage _Instacne; 4 /// <summary> 5 /// 游戏管理单例 6 /// </summary> 7 public static GameManage Instacne 8 { 9 get 10 { 11 return _Instacne; 12 } 13 14 } 15 private void Awake() 16 { 17 _Instacne = this; 18 } 19 #endregion 20 /// <summary> 21 /// 每局分数块游戏对象 22 /// </summary> 23 public ScoreItem[] SocreItems; 24 25 /// <summary> 26 /// 每球的临时得分 27 /// </summary> 28 public int _tempScore; 29 30 /// <summary> 31 /// 墙壁 32 /// </summary> 33 public Transform roomWall; 34 35 /// <summary> 36 /// 推力范围 37 /// </summary> 38 public Transform pushArea; 39 40 /// <summary> 41 /// 保龄球瓶预制体 42 /// </summary> 43 public GameObject bowlsPrefabs; 44 /// <summary> 45 /// 保龄球预制体 46 /// </summary> 47 public GameObject ballPrefab; 48 /// <summary> 49 /// 保龄球对象 50 /// </summary> 51 public GameObject ball; 52 public BallController _BallController; 53 /// <summary> 54 /// 出界等待时间 55 /// </summary> 56 [SerializeField] 57 private float outAreaWaitTime = 5f; 58 59 private float _temp; 60 /// <summary> 61 /// 每球声明时间 62 /// </summary> 63 [SerializeField] 64 private float BallLifeTime = 30f; 65 /// <summary> 66 /// 滚动时间 67 /// </summary> 68 private float _tempLife; 69 70 private void Start() 71 { 72 CreatBall(); 73 } 74 private void Update() 75 { 76 if (ball != null) 77 { 78 if (_BallController.pushed)//球已经被推出去 79 { 80 _tempLife += Time.deltaTime; 81 if (_tempLife>BallLifeTime&&!_BallController.isOutArea)//一直没出届 82 { 83 Debug.Log("此球分数:" + _tempScore); 84 _tempLife = 0; 85 _tempScore = 0;//清零 86 DestroyImmediate(ball); 87 CreatBall(); 88 } 89 if (_BallController.isOutArea)//球出界 90 { 91 _temp += Time.deltaTime; 92 if (_temp > outAreaWaitTime)//超过出界时间 93 { 94 Debug.Log("此球分数:" + _tempScore); 95 _tempLife = 0; 96 _temp = 0;//重新计时 97 _tempScore = 0;//清零 98 DestroyImmediate(ball); 99 CreatBall(); 100 } 101 } 102 } 103 } 104 } 105 public void CreatBall() 106 { 107 if (ball == null) 108 { 109 ball = Instantiate(ballPrefab) as GameObject; 110 _BallController = ball.GetComponent<BallController>(); 111 _BallController.Inits(); 112 } 113 }
再绑定一下各值。
稍微修改一下BallController.cs,添加越界的碰撞检测:
1 #region 射线相关 2 3 /// <summary> 4 /// 射线碰撞信息 5 /// </summary> 6 private RaycastHit hit; 7 /// <summary> 8 /// 图层遮罩 9 /// </summary> 10 private LayerMask mask; 11 /// <summary> 12 /// 球的位置 13 /// </summary> 14 public Transform ballTrans; 15 16 #endregion 17 18 19 /// <summary> 20 /// 球的半径 21 /// </summary> 22 [SerializeField] 23 private float ballRadius; 24 /// <summary> 25 /// 扔球点 26 /// </summary> 27 private Vector3 ballPushOrgin; 28 /// <summary> 29 /// 球左右方向差量 30 /// </summary> 31 private Vector3 ballPushDir; 32 33 /// <summary> 34 /// 是否推出去 35 /// </summary> 36 public bool pushed = false; 37 38 39 /// <summary> 40 /// 是否撞墙 41 /// </summary> 42 public bool ishitWall; 43 /// <summary> 44 /// 出界了 45 /// </summary> 46 public bool isOutArea; 47 48 /// <summary> 49 /// 保龄球初始化 50 /// </summary> 51 public void Inits() { 52 //获取球的半径 53 ballRadius = ballTrans.GetComponent<SphereCollider>().radius; 54 GetComponent<Rigidbody>().velocity = Vector3.zero; 55 GetComponent<Rigidbody>().isKinematic = true;//不受动力 56 isOutArea = false;//没有越界 57 pushed = false;//没有推出去 58 ishitWall =false;//没有撞墙 59 } 60 61 private void Update() 62 { 63 //发射一条射线 64 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 65 //如果还没有推出去这个球 66 if (!pushed) 67 { 68 //推球逻辑 69 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor"))) 70 { 71 //推出去 72 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 73 if (hit.transform == GameManage.Instacne.pushArea) 74 { 75 _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置 76 ballTrans.position = _ballPushTrans; 77 ballPushOrgin = hit.point; ;//记录球的起点 78 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z); 79 } 80 else 81 { 82 if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有) 83 { 84 Vector3 _ballPushDir = hit.point;//记录一下球的终点 85 Debug.Log(ballPushOrgin); 86 ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0); 87 pushed = true; 88 ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 89 GetComponent<Rigidbody>().isKinematic = false; 90 ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 25, ForceMode.Impulse); 91 92 } 93 } 94 } 95 } 96 } 97 /// <summary> 98 /// 碰撞器检测 99 /// </summary> 100 /// <param name="collision"></param> 101 private void OnCollisionEnter(Collision collision) 102 { 103 if (collision.gameObject.layer != 8 && pushed) 104 { 105 Debug.Log("球出界了!"); 106 isOutArea = true; 107 } 108 }
截止到目前为止刷新球的功能没问题了
7、游戏逻辑实现(显示统计每轮得分)
接下来的几部就实现最后游戏逻辑了,也是最麻烦的。由于前期已经做了较多的功能,所以现在已经减轻了不少代码负担。
在GameManage.cs定义一个indexItem,记录目前第几轮。
分析一下,如果你开始每一轮第一球打出Strike(10瓶全中),第一轮直接就结束了,但是这一轮的总分还没结束,因为Strike要加下两球的得分。
如果第一球并没有打出Strike,不管第二球打出什么都会结束,第二球如果打出Spare(补中),这一轮的总分也没结束,因为Spare要加下一球的得分;第二球如果没有打出Spare(补中),这一轮的总分就可以算出来了这两球得分之和。
我们先解决第一轮打球问题,我们在GameManage定义一个方法处理得分情况:
1 /// <summary> 2 /// 传递分数 3 /// </summary> 4 /// <param name="_score">分数值</param> 5 public void SetScore(int _score) 6 { 7 if (indexItem < 9)//前九轮 8 { 9 if (_tempNumBall == 1)//第一球 10 { 11 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球 12 SocreItems[indexItem].ShowScore(); 13 if (_score == 10)//分数为10 14 { 15 16 Debug.LogWarning("Strike全中!"); 17 SocreItems[indexItem].isStrike = true;//设置第一球 18 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 19 _tempNumBall = 1;//开始新的一轮 20 } 21 else//不为10 22 { 23 _tempNumBall = 2;//开始下一个球 24 } 25 } 26 else if (_tempNumBall == 2)//第二球 27 { 28 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球 29 SocreItems[indexItem].ShowScore(); 30 if (_score + SocreItems[indexItem].Scores[0] == 10)//yi 31 { 32 33 Debug.LogWarning("Spare补中!"); 34 SocreItems[indexItem].isSpare = true;//设置第一球 35 } 36 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 37 _tempNumBall = 1;//开始新的一轮 38 } 39 } 40 }
对到目前为止四个核心代码:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 public class GameManage : MonoBehaviour 7 { 8 #region 单例 9 10 private static GameManage _Instacne; 11 /// <summary> 12 /// 游戏管理单例 13 /// </summary> 14 public static GameManage Instacne 15 { 16 get 17 { 18 return _Instacne; 19 } 20 21 } 22 private void Awake() 23 { 24 _Instacne = this; 25 } 26 #endregion 27 /// <summary> 28 /// 每局分数块游戏对象 29 /// </summary> 30 public ScoreItem[] SocreItems; 31 32 /// <summary> 33 /// 每球的临时得分 34 /// </summary> 35 public int _tempScore; 36 37 /// <summary> 38 /// 墙壁 39 /// </summary> 40 public Transform roomWall; 41 42 /// <summary> 43 /// 推力范围 44 /// </summary> 45 public Transform pushArea; 46 47 /// <summary> 48 /// 保龄球瓶预制体 49 /// </summary> 50 public GameObject bowlsPrefabs; 51 /// <summary> 52 /// 保龄球预制体 53 /// </summary> 54 public GameObject ballPrefab; 55 /// <summary> 56 /// 保龄球对象 57 /// </summary> 58 public GameObject ball; 59 public BallController _BallController; 60 /// <summary> 61 /// 出界等待时间 62 /// </summary> 63 [SerializeField] 64 private float outAreaWaitTime = 5f; 65 66 private float _temp; 67 /// <summary> 68 /// 每球声明时间 69 /// </summary> 70 [SerializeField] 71 private float BallLifeTime = 30f; 72 /// <summary> 73 /// 滚动时间 74 /// </summary> 75 private float _tempLife; 76 /// <summary> 77 /// 轮数 78 /// </summary> 79 public int indexItem; 80 /// <summary> 81 /// 每轮第N球 82 /// </summary> 83 public int _tempNumBall; 84 85 private void Start() 86 { 87 indexItem = 0; 88 _tempNumBall = 1;//第1球 89 CreatBall(); 90 } 91 private void Update() 92 { 93 if (ball != null) 94 { 95 if (_BallController.pushed)//球已经被推出去 96 { 97 _tempLife += Time.deltaTime; 98 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届 99 { 100 Debug.Log("此球分数:" + _tempScore); 101 SetScore(_tempScore); 102 _tempLife = 0; 103 _tempScore = 0;//清零 104 DestroyImmediate(ball); 105 CreatBall(); 106 } 107 if (_BallController.isOutArea)//球出界 108 { 109 _temp += Time.deltaTime; 110 if (_temp > outAreaWaitTime)//超过出界时间 111 { 112 Debug.Log("球出界!此球得分:" + _tempScore); 113 SetScore(_tempScore); 114 _tempLife = 0; 115 _temp = 0;//重新计时 116 _tempScore = 0;//清零 117 DestroyImmediate(ball); 118 CreatBall(); 119 } 120 } 121 } 122 } 123 } 124 public void CreatBall() 125 { 126 if (ball == null) 127 { 128 ball = Instantiate(ballPrefab) as GameObject; 129 _BallController = ball.GetComponent<BallController>(); 130 _BallController.Inits(); 131 } 132 } 133 /// <summary> 134 /// 传递分数 135 /// </summary> 136 /// <param name="_score">分数值</param> 137 public void SetScore(int _score) 138 { 139 //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 140 if (indexItem < 9)//前九轮 141 { 142 if (_tempNumBall == 1)//第一球 143 { 144 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球 145 SocreItems[indexItem].ShowScore(); 146 if (_score == 10)//分数为10 147 { 148 149 Debug.LogWarning("Strike全中!"); 150 SocreItems[indexItem].isStrike = true;//设置第一球 151 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 152 _tempNumBall = 1;//开始新的一轮 153 } 154 else//不为10 155 { 156 _tempNumBall = 2;//开始下一个球 157 } 158 } 159 else if (_tempNumBall == 2)//第二球 160 { 161 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球 162 SocreItems[indexItem].ShowScore(); 163 if (_score + SocreItems[indexItem].Scores[0] == 10)//yi 164 { 165 166 Debug.LogWarning("Spare补中!"); 167 SocreItems[indexItem].isSpare = true;//设置第一球 168 } 169 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 170 _tempNumBall = 1;//开始新的一轮 171 } 172 } 173 } 174 }
GameManage.cs
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球 6 /// </summary> 7 public class Bowl : MonoBehaviour 8 { 9 /// <summary> 10 /// 是否得分 11 /// </summary> 12 public bool GetScore; 13 /// <summary> 14 /// 瓶号索引 15 /// </summary> 16 public int Index; 17 18 private void FixedUpdate() 19 { 20 if (!GetScore) 21 { 22 if (Mathf.Abs(transform.localRotation.x) > 0.3f || Mathf.Abs(transform.localRotation.z) > 0.3f) 23 { 24 25 GetScore = true; 26 GameManage.Instacne._tempScore++;//这轮分数 27 } 28 } 29 }
Bowl.cs
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球的控制 6 /// </summary> 7 public class BallController : MonoBehaviour 8 { 9 10 #region 射线相关 11 12 /// <summary> 13 /// 射线碰撞信息 14 /// </summary> 15 private RaycastHit hit; 16 /// <summary> 17 /// 图层遮罩 18 /// </summary> 19 private LayerMask mask; 20 /// <summary> 21 /// 球的位置 22 /// </summary> 23 public Transform ballTrans; 24 25 #endregion 26 27 28 /// <summary> 29 /// 球的半径 30 /// </summary> 31 [SerializeField] 32 private float ballRadius; 33 /// <summary> 34 /// 扔球点 35 /// </summary> 36 private Vector3 ballPushOrgin; 37 /// <summary> 38 /// 球左右方向差量 39 /// </summary> 40 private Vector3 ballPushDir; 41 42 /// <summary> 43 /// 是否推出去 44 /// </summary> 45 public bool pushed = false; 46 47 48 /// <summary> 49 /// 是否撞墙 50 /// </summary> 51 public bool ishitWall; 52 /// <summary> 53 /// 出界了 54 /// </summary> 55 public bool isOutArea; 56 57 /// <summary> 58 /// 保龄球初始化 59 /// </summary> 60 public void Inits() { 61 //获取球的半径 62 ballRadius = ballTrans.GetComponent<SphereCollider>().radius; 63 GetComponent<Rigidbody>().velocity = Vector3.zero; 64 GetComponent<Rigidbody>().isKinematic = true;//不受动力 65 isOutArea = false;//没有越界 66 pushed = false;//没有推出去 67 ishitWall =false;//没有撞墙 68 } 69 70 private void Update() 71 { 72 //发射一条射线 73 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 74 //如果还没有推出去这个球 75 if (!pushed) 76 { 77 //推球逻辑 78 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor"))) 79 { 80 //推出去 81 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 82 if (hit.transform == GameManage.Instacne.pushArea) 83 { 84 _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置 85 ballTrans.position = _ballPushTrans; 86 ballPushOrgin = hit.point; ;//记录球的起点 87 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z); 88 } 89 else 90 { 91 if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有) 92 { 93 Vector3 _ballPushDir = hit.point;//记录一下球的终点 94 Debug.Log(ballPushOrgin); 95 ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0); 96 pushed = true; 97 ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 98 GetComponent<Rigidbody>().isKinematic = false; 99 ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 25, ForceMode.Impulse); 100 101 } 102 } 103 } 104 } 105 } 106 /// <summary> 107 /// 碰撞器检测 108 /// </summary> 109 /// <param name="collision"></param> 110 private void OnCollisionEnter(Collision collision) 111 { 112 if (collision.gameObject.layer != 8 && pushed) 113 { 114 //Debug.Log("球出界了!"); 115 isOutArea = true; 116 } 117 } 118 }
BallController.cs
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球的控制 6 /// </summary> 7 public class BallController : MonoBehaviour 8 { 9 10 #region 射线相关 11 12 /// <summary> 13 /// 射线碰撞信息 14 /// </summary> 15 private RaycastHit hit; 16 /// <summary> 17 /// 图层遮罩 18 /// </summary> 19 private LayerMask mask; 20 /// <summary> 21 /// 球的位置 22 /// </summary> 23 public Transform ballTrans; 24 25 #endregion 26 27 28 /// <summary> 29 /// 球的半径 30 /// </summary> 31 [SerializeField] 32 private float ballRadius; 33 /// <summary> 34 /// 扔球点 35 /// </summary> 36 private Vector3 ballPushOrgin; 37 /// <summary> 38 /// 球左右方向差量 39 /// </summary> 40 private Vector3 ballPushDir; 41 42 /// <summary> 43 /// 是否推出去 44 /// </summary> 45 public bool pushed = false; 46 47 48 /// <summary> 49 /// 是否撞墙 50 /// </summary> 51 public bool ishitWall; 52 /// <summary> 53 /// 出界了 54 /// </summary> 55 public bool isOutArea; 56 57 /// <summary> 58 /// 保龄球初始化 59 /// </summary> 60 public void Inits() { 61 //获取球的半径 62 ballRadius = ballTrans.GetComponent<SphereCollider>().radius; 63 GetComponent<Rigidbody>().velocity = Vector3.zero; 64 GetComponent<Rigidbody>().isKinematic = true;//不受动力 65 isOutArea = false;//没有越界 66 pushed = false;//没有推出去 67 ishitWall =false;//没有撞墙 68 } 69 70 private void Update() 71 { 72 //发射一条射线 73 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 74 //如果还没有推出去这个球 75 if (!pushed) 76 { 77 //推球逻辑 78 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor"))) 79 { 80 //推出去 81 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 82 if (hit.transform == GameManage.Instacne.pushArea) 83 { 84 _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置 85 ballTrans.position = _ballPushTrans; 86 ballPushOrgin = hit.point; ;//记录球的起点 87 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z); 88 } 89 else 90 { 91 if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有) 92 { 93 Vector3 _ballPushDir = hit.point;//记录一下球的终点 94 Debug.Log(ballPushOrgin); 95 ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0); 96 pushed = true; 97 ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 98 GetComponent<Rigidbody>().isKinematic = false; 99 ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 25, ForceMode.Impulse); 100 101 } 102 } 103 } 104 } 105 } 106 /// <summary> 107 /// 碰撞器检测 108 /// </summary> 109 /// <param name="collision"></param> 110 private void OnCollisionEnter(Collision collision) 111 { 112 if (collision.gameObject.layer != 8 && pushed) 113 { 114 //Debug.Log("球出界了!"); 115 isOutArea = true; 116 } 117 } 118 }
BallController.cs
打第一轮测试一下效果,额,测试了八九次才打出Strike,非常理想的达到预期了的效果:
8、游戏逻辑实现(多次开局摆放瓶子)
保龄球每打完一局就应该把倒的瓶子清除,然后重新摆放。
其实摆放瓶子方法也很好分析,每轮结束销毁瓶子堆,不结束就把倒得那些隐藏掉SetActive(false)。
关于何时销毁,我选择把它放在分数统计里调用,因为只有经过分数统计你才知道是否所有瓶子都倒下了。
摆放的方法放在生成新球的后面:
而摆放瓶子的方法如下:
1 /// <summary> 2 /// 摆放瓶子 3 /// </summary> 4 public void SetBowls() 5 { 6 if (bowls == null)//没有瓶子 7 { 8 bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject; 9 AllBowls = new Bowl[10];//10个瓶 10 foreach (var item in bowls.GetComponentsInChildren<Bowl>()) 11 { 12 AllBowls[item.Index] = item;//把加载的瓶子放进数组里 13 } 14 } 15 else 16 { 17 foreach (var item in AllBowls) 18 { 19 if (item.GetScore)//没有倒下过显示他们 20 { 21 item.gameObject.SetActive(!item.GetScore); 22 } 23 } 24 } 25 }
关于对象的绑定:
GameManage.cs里面的代码也越来越多,整理以后的GameManage如下:
1 //------------------------------------------------------------------------------ 2 // Copyright 2017 21世纪少年. All rights reserved. 3 // 4 // Unity5.6.3f1 Editor make it 5 // BowlingHall_Project 6 // 7 // CreatTime: #10/7/2017# 8 // Author: Unity Guo 9 //------------------------------------------------------------------------------ 10 using System.Collections; 11 using System.Collections.Generic; 12 using UnityEngine; 13 using UnityEngine.UI; 14 15 public class GameManage : MonoBehaviour 16 { 17 #region 单例 18 19 private static GameManage _Instacne; 20 /// <summary> 21 /// 游戏管理单例 22 /// </summary> 23 public static GameManage Instacne 24 { 25 get 26 { 27 return _Instacne; 28 } 29 30 } 31 private void Awake() 32 { 33 _Instacne = this; 34 } 35 #endregion 36 37 38 #region 场景信息 39 40 /// <summary> 41 /// 墙壁 42 /// </summary> 43 public Transform roomWall; 44 /// <summary> 45 /// 推力范围 46 /// </summary> 47 public Transform pushArea; 48 /// <summary> 49 /// 保龄球瓶堆的父物体位置 50 /// </summary> 51 public Transform bowlsParentTrans; 52 53 #endregion 54 55 #region 游戏对象 56 57 /// <summary> 58 /// 保龄球瓶预制体 59 /// </summary> 60 public GameObject bowlsPrefabs; 61 /// <summary> 62 /// 保龄球瓶堆 63 /// </summary> 64 public GameObject bowls; 65 /// <summary> 66 /// 保龄球预制体 67 /// </summary> 68 public GameObject ballPrefab; 69 /// <summary> 70 /// 保龄球对象 71 /// </summary> 72 public GameObject ball; 73 /// <summary> 74 /// 保龄球上的控制器 75 /// </summary> 76 public BallController _BallController; 77 78 #endregion 79 80 #region 时间管理 81 /// <summary> 82 /// 出界等待时间 83 /// </summary> 84 [SerializeField] 85 private float outAreaWaitTime = 5f; 86 87 private float _temp; 88 /// <summary> 89 /// 每球声明时间 90 /// </summary> 91 [SerializeField] 92 private float BallLifeTime = 30f; 93 /// <summary> 94 /// 滚动时间 95 /// </summary> 96 private float _tempLife; 97 98 #endregion 99 100 /// <summary> 101 /// 每局分数块游戏对象 102 /// </summary> 103 public ScoreItem[] SocreItems; 104 /// <summary> 105 /// 管理所有瓶子的数组 106 /// </summary> 107 public Bowl[] AllBowls; 108 /// <summary> 109 /// 每球的临时得分 110 /// </summary> 111 public int _tempScore; 112 /// <summary> 113 /// 轮数 114 /// </summary> 115 public int indexItem; 116 /// <summary> 117 /// 每轮第N球 118 /// </summary> 119 public int _tempNumBall; 120 121 private void Start() 122 { 123 indexItem = 0; 124 _tempNumBall = 1;//第1球 125 SetBall(); 126 SetBowls(); 127 } 128 private void Update() 129 { 130 if (ball != null) 131 { 132 if (_BallController.pushed)//球已经被推出去 133 { 134 _tempLife += Time.deltaTime; 135 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届 136 { 137 Debug.Log("此球分数:" + _tempScore); 138 SetScore(_tempScore); 139 _tempLife = 0; 140 _tempScore = 0;//清零 141 DestroyImmediate(ball); 142 SetBall(); 143 SetBowls(); 144 } 145 if (_BallController.isOutArea)//球出界 146 { 147 _temp += Time.deltaTime; 148 if (_temp > outAreaWaitTime)//超过出界时间 149 { 150 Debug.Log("球出界!此球得分:" + _tempScore); 151 SetScore(_tempScore); 152 _tempLife = 0; 153 _temp = 0;//重新计时 154 _tempScore = 0;//清零 155 DestroyImmediate(ball); 156 SetBall(); 157 SetBowls(); 158 } 159 } 160 } 161 } 162 } 163 /// <summary> 164 /// 生成新球 165 /// </summary> 166 public void SetBall() 167 { 168 if (ball == null) 169 { 170 ball = Instantiate(ballPrefab) as GameObject; 171 _BallController = ball.GetComponent<BallController>(); 172 _BallController.Inits(); 173 } 174 } 175 /// <summary> 176 /// 传递分数 177 /// </summary> 178 /// <param name="_score">分数值</param> 179 public void SetScore(int _score) 180 { 181 //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 182 if (indexItem < 9)//前九轮 183 { 184 if (_tempNumBall == 1)//第一球 185 { 186 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球 187 SocreItems[indexItem].ShowScore(); 188 if (_score == 10)//分数为10 189 { 190 Debug.LogWarning("Strike全中!"); 191 SocreItems[indexItem].isStrike = true;//设置第一球 192 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 193 DestroyImmediate(bowls);//清空瓶子 194 _tempNumBall = 1;//开始新的一轮 195 } 196 else//不为10 197 { 198 _tempNumBall = 2;//开始下一个球 199 } 200 } 201 else if (_tempNumBall == 2)//第二球 202 { 203 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球 204 SocreItems[indexItem].ShowScore(); 205 if (_score + SocreItems[indexItem].Scores[0] == 10)//yi 206 { 207 Debug.LogWarning("Spare补中!"); 208 SocreItems[indexItem].isSpare = true;//设置第一球 209 } 210 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 211 DestroyImmediate(bowls);//清空瓶子 212 _tempNumBall = 1;//开始新的一轮 213 } 214 } 215 } 216 217 /// <summary> 218 /// 摆放瓶子 219 /// </summary> 220 public void SetBowls() 221 { 222 if (bowls == null)//没有瓶子 223 { 224 bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject; 225 AllBowls = new Bowl[10];//10个瓶 226 foreach (var item in bowls.GetComponentsInChildren<Bowl>()) 227 { 228 AllBowls[item.Index] = item;//把加载的瓶子放进数组里 229 } 230 } 231 else 232 { 233 foreach (var item in AllBowls) 234 { 235 if (item.GetScore)//没有倒下过显示他们 236 { 237 item.gameObject.SetActive(!item.GetScore); 238 } 239 } 240 } 241 } 242 }
GameManage
9、游戏逻辑实现(Strike全中和Spare补中计分实现)
好了到最麻烦的地方了,分数逻辑设置重新理清一下得分思路,在前面我们写了一个SetSocre()方法设置分数,但是没法处理补分和全中,而且也没有办法处理第十轮,所以我们稍加修改,完善并添加特殊加分方法AddScore(),在ScoreItem类中,每次设置分数都让他重新设置特殊分数:
而且之前分数我们直接用int[]的数组存储,我们改进一下在,ScoreItem中添加一个特殊类Score:
1 /// <summary> 2 /// 分数信息类 3 /// </summary> 4 public class Score 5 { 6 /// <summary> 7 /// Score构造参数 8 /// </summary> 9 /// <param name="_value"></param> 10 public Score(int _value) 11 { 12 value = _value; 13 } 14 /// <summary> 15 /// 分数值 16 /// </summary> 17 private int value; 18 /// <summary> 19 /// 获取分数值 20 /// </summary> 21 public int Value 22 { 23 get 24 { 25 return value; 26 } 27 } 28 }
当有分数时实例化一个Score,构造传值分数赋值,而且这个分数不可修改,封装起来只能获取,保证了外界不能修改你的分数。
理清思路逻辑,接下来进行编码:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 /// <summary> 7 /// 分数信息类 8 /// </summary> 9 public class Score 10 { 11 /// <summary> 12 /// Score构造参数 13 /// </summary> 14 /// <param name="_value"></param> 15 public Score(int _value) 16 { 17 value = _value; 18 } 19 /// <summary> 20 /// 分数值 21 /// </summary> 22 private int value; 23 /// <summary> 24 /// 获取分数值 25 /// </summary> 26 public int Value 27 { 28 get 29 { 30 return value; 31 } 32 } 33 } 34 public class ScoreItem : MonoBehaviour 35 { 36 /// <summary> 37 /// 索引 38 /// </summary> 39 public int index; 40 /// <summary> 41 /// 所有分数文字 42 /// </summary> 43 public Text[] SocreTexts; 44 /// <summary> 45 /// 每轮分数 46 /// </summary> 47 public Score[] Scores; 48 /// <summary> 49 /// 本轮分数临时总和 50 /// </summary> 51 public int _tempScoreItem; 52 /// <summary> 53 /// 本轮分数总和 54 /// </summary> 55 public int _Score; 56 /// <summary> 57 /// 本轮全中? 58 /// </summary> 59 public bool isStrike = false; 60 /// <summary> 61 /// 本轮补中? 62 /// </summary> 63 public bool isSpare = false; 64 /// <summary> 65 /// 初始化 66 /// </summary> 67 public void Inits() 68 { 69 Scores = new Score[SocreTexts.Length - 1];//每轮分数初始化 70 } 71 /// <summary> 72 /// 每轮分数设置索引为_index的分数文本的值为_score 73 /// </summary> 74 /// <param name="_index">索引</param> 75 /// <param name="_score">分数</param> 76 public void SetScoreText(int _index, int _score) 77 { 78 Scores[_index] = new Score(_score); 79 Debug.LogWarning("第"+index+"轮:"+_index+"球:分数"+_score); 80 _tempScoreItem = 0; 81 foreach (var item in Scores) 82 { 83 if (item != null) 84 { 85 _tempScoreItem += item.Value;//累加本轮分数总和 86 } 87 } 88 } 89 /// <summary> 90 /// 显示所有分数 91 /// </summary> 92 public void ShowScore() 93 { 94 //设置每轮 95 for (int i = 0; i < SocreTexts.Length - 1; i++) 96 { 97 if (Scores[i] != null) 98 { 99 SocreTexts[i].text = Scores[i].Value.ToString(); 100 } 101 else 102 { 103 SocreTexts[i].text = ""; 104 } 105 } 106 //AddScore(); 107 } 108 /// <summary> 109 /// 更新添加分数 110 /// </summary> 111 public void AddScore() 112 { 113 _Score = _tempScoreItem;//分数 114 int _tempTime = 0;//临时次数计数 115 if (isStrike)//全中 116 { 117 if (index < 9)//当前是前九个分数块 118 { 119 foreach (var item in GameManage.Instacne.SocreItems[index + 1].Scores)//遍历下一轮所有分数 120 { 121 if (item != null)//不为空加分 122 { 123 _Score += item.Value;//分数 124 _tempTime++; 125 if (_tempTime == 2) 126 { 127 break; 128 } 129 } 130 else 131 { 132 if (index <8)//前八个 133 { 134 if (GameManage.Instacne.SocreItems[index + 2].Scores[0] != null)//下一个球也是补中的情况 135 { 136 _Score += GameManage.Instacne.SocreItems[index + 2].Scores[0].Value; 137 _tempTime++; 138 break; 139 } 140 } 141 } 142 } 143 } 144 } 145 if (isSpare) 146 { 147 if (index <9)//前八个 148 { 149 if (GameManage.Instacne.SocreItems[index + 1].Scores[0] != null) 150 { 151 _Score += GameManage.Instacne.SocreItems[index + 1].Scores[0].Value; 152 _tempTime++;//一次 153 } 154 } 155 else if(index ==9)//第九个 156 { 157 } 158 } 159 SocreTexts[SocreTexts.Length - 1].text = _Score.ToString();//总分设置 160 } 161 }
ScoreItem.cs
对了再加一个IsOver,游戏结束设置为True,修改之后的GameManage:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 6 public class GameManage : MonoBehaviour 7 { 8 #region 单例 9 10 private static GameManage _Instacne; 11 /// <summary> 12 /// 游戏管理单例 13 /// </summary> 14 public static GameManage Instacne 15 { 16 get 17 { 18 return _Instacne; 19 } 20 21 } 22 private void Awake() 23 { 24 _Instacne = this; 25 } 26 #endregion 27 28 29 #region 场景信息 30 31 /// <summary> 32 /// 墙壁 33 /// </summary> 34 public Transform roomWall; 35 /// <summary> 36 /// 推力范围 37 /// </summary> 38 public Transform pushArea; 39 /// <summary> 40 /// 保龄球瓶堆的父物体位置 41 /// </summary> 42 public Transform bowlsParentTrans; 43 44 #endregion 45 46 #region 游戏对象 47 48 /// <summary> 49 /// 保龄球瓶预制体 50 /// </summary> 51 public GameObject bowlsPrefabs; 52 /// <summary> 53 /// 保龄球瓶堆 54 /// </summary> 55 public GameObject bowls; 56 /// <summary> 57 /// 保龄球预制体 58 /// </summary> 59 public GameObject ballPrefab; 60 /// <summary> 61 /// 保龄球对象 62 /// </summary> 63 public GameObject ball; 64 /// <summary> 65 /// 保龄球上的控制器 66 /// </summary> 67 public BallController _BallController; 68 /// <summary> 69 /// 管理所有瓶子的数组 70 /// </summary> 71 public Bowl[] AllBowls; 72 73 #endregion 74 75 #region 时间管理 76 /// <summary> 77 /// 出界等待时间 78 /// </summary> 79 [SerializeField] 80 private float outAreaWaitTime = 5f; 81 82 private float _temp; 83 /// <summary> 84 /// 每球声明时间 85 /// </summary> 86 [SerializeField] 87 private float BallLifeTime = 30f; 88 /// <summary> 89 /// 滚动时间 90 /// </summary> 91 private float _tempLife; 92 93 #endregion 94 95 #region 分数管理 96 97 /// <summary> 98 /// 每局分数块游戏对象 99 /// </summary> 100 public ScoreItem[] SocreItems; 101 /// <summary> 102 /// 每球的临时得分 103 /// </summary> 104 public int _tempScore; 105 /// <summary> 106 /// 所有总分 107 /// </summary> 108 public int AllScore; 109 /// <summary> 110 /// 所有分数 111 /// </summary> 112 public Text AllScoreText; 113 #endregion 114 /// <summary> 115 /// 轮数 116 /// </summary> 117 public int indexItem; 118 /// <summary> 119 /// 每轮第N球 120 /// </summary> 121 public int _tempNumBall; 122 /// <summary> 123 /// 游戏结束 124 /// </summary> 125 public bool IsOver; 126 127 private void Start() 128 { 129 indexItem = 0; 130 _tempNumBall = 1;//第1球 131 SetBall(); 132 SetBowls(); 133 //分数初始化 134 foreach (var item in SocreItems) 135 { 136 item.Inits();//初始化 137 item.ShowScore(); 138 } 139 } 140 private void Update() 141 { 142 if (ball != null)//游戏没有结束 143 { 144 145 if (_BallController.pushed)//球已经被推出去 146 { 147 _tempLife += Time.deltaTime; 148 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届 149 { 150 Debug.Log("此球分数:" + _tempScore); 151 _tempLife = 0; 152 DestroyImmediate(ball); 153 SetScore(_tempScore); 154 _tempScore = 0;//清零 155 SetBall(); 156 SetBowls(); 157 } 158 if (_BallController.isOutArea)//球出界 159 { 160 _temp += Time.deltaTime; 161 if (_temp > outAreaWaitTime)//超过出界时间 162 { 163 Debug.Log("球出界!此球得分:" + _tempScore); 164 _tempLife = 0; 165 _temp = 0;//重新计时 166 DestroyImmediate(ball); 167 SetScore(_tempScore); 168 _tempScore = 0;//清零 169 SetBall(); 170 SetBowls(); 171 } 172 } 173 } 174 175 176 } 177 } 178 /// <summary> 179 /// 生成新球 180 /// </summary> 181 public void SetBall() 182 { 183 if (ball == null) 184 { 185 ball = Instantiate(ballPrefab) as GameObject; 186 _BallController = ball.GetComponent<BallController>(); 187 _BallController.Inits(); 188 } 189 } 190 /// <summary> 191 /// 传递分数 192 /// </summary> 193 /// <param name="_score">分数值</param> 194 public void SetScore(int _score) 195 { 196 //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 197 if (indexItem < 9)//---------------------------------------------------前九轮 198 { 199 if (_tempNumBall == 1)//第一球 200 { 201 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球 202 SocreItems[indexItem].ShowScore(); 203 if (_score == 10)//分数为10 204 { 205 Debug.LogWarning("Strike全中!"); 206 SocreItems[indexItem].isStrike = true;//设置第一球 207 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 208 DestroyImmediate(bowls);//清空瓶子 209 _tempNumBall = 1;//开始新的一轮 210 } 211 else//不为10 212 { 213 _tempNumBall = 2;//开始下一个球 214 } 215 } 216 else if (_tempNumBall == 2)//第二球 217 { 218 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球 219 SocreItems[indexItem].ShowScore(); 220 if (_score + SocreItems[indexItem].Scores[0].Value == 10)//yi 221 { 222 Debug.LogWarning("Spare补中!"); 223 SocreItems[indexItem].isSpare = true;//设置第一球 224 } 225 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 226 DestroyImmediate(bowls);//清空瓶子 227 _tempNumBall = 1;//开始新的一轮 228 229 } 230 } 231 else if (indexItem == 9)//--------------------------------------------第十轮 232 { 233 if (_tempNumBall == 1)//第一球 234 { 235 if (_score == 10) 236 { 237 Debug.LogWarning("Strike全中!"); 238 SocreItems[indexItem].isStrike = true;//设置第一球 239 DestroyImmediate(bowls);//清空瓶子 240 } 241 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球 242 SocreItems[indexItem].ShowScore(); 243 _tempNumBall = 2;//开始新的一轮 244 } 245 else if (_tempNumBall == 2)//第二球 246 { 247 if (!SocreItems[indexItem].isStrike)//不是全中 248 { 249 if (SocreItems[indexItem].Scores[0].Value + _score == 10)//达成补中条件 250 { 251 Debug.LogWarning("Spare补中!"); 252 SocreItems[indexItem].isSpare = true;//全中 253 DestroyImmediate(bowls);//清空瓶子 254 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数 255 SocreItems[indexItem].ShowScore(); 256 _tempNumBall = 3;//开始新的一轮,第3球 257 } 258 else 259 { 260 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数 261 SocreItems[indexItem].ShowScore(); 262 IsOver = true; 263 } 264 } 265 else//全中下一球 266 { 267 DestroyImmediate(bowls);//清空瓶子 268 SocreItems[9].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数 269 SocreItems[9].ShowScore(); 270 _tempNumBall = 3;//开始新的一轮,第3球 271 } 272 } 273 else if (_tempNumBall == 3) 274 { 275 DestroyImmediate(bowls);//清空瓶子 276 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球分数 277 SocreItems[indexItem].ShowScore(); 278 279 IsOver = true; 280 } 281 } 282 AllScore = 0; 283 foreach (var item in SocreItems)//加分 284 { 285 item.AddScore(); 286 AllScore += item._Score; 287 } 288 AllScoreText.text = AllScore.ToString(); 289 } 290 291 /// <summary> 292 /// 摆放瓶子 293 /// </summary> 294 public void SetBowls() 295 { 296 if (bowls == null)//没有瓶子 297 { 298 bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject; 299 AllBowls = new Bowl[10];//10个瓶 300 foreach (var item in bowls.GetComponentsInChildren<Bowl>()) 301 { 302 AllBowls[item.Index] = item;//把加载的瓶子放进数组里 303 } 304 } 305 else 306 { 307 foreach (var item in AllBowls) 308 { 309 if (item.GetScore)//没有倒下过显示他们 310 { 311 item.gameObject.SetActive(!item.GetScore); 312 } 313 } 314 } 315 } 316 }
GameManage.cs
此外修改一下BallController.cs:
这样游戏结束就捡不起来球了。
到目前测试一下,基本上游戏大体逻辑已完全解决!但是这并不是最终代码,得分和现实已经没有问题:
10、开始界面的搭建
和Main场景搭建手法差不多,搭建一个Start场景。
接下来然后搭建Start的UI界面,我们发现这个按钮素材需要九宫切图,于是切一下:
经过一系列的UI布局:
关于场景的替换需要注意一下,不要用Application.loadedLevel(),导入UnityEngine.SceneManagement包,用SceneManager.LoadScene():
我们再编写一个游戏管理的脚本StartManage.cs,让它负责场景的切换和球的表面材质替换。
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.SceneManagement; 5 using UnityEngine.UI; 6 7 public class StartManage : MonoBehaviour 8 { 9 /// <summary> 10 /// 开始按键 11 /// </summary> 12 public Button PlayBtn; 13 /// <summary> 14 /// 上一个球 15 /// </summary> 16 public Button PrevBtn; 17 /// <summary> 18 /// 下一个球 19 /// </summary> 20 public Button NextBtn; 21 /// <summary> 22 /// 球的材质列表 23 /// </summary> 24 [SerializeField] 25 private Texture[] ballTextures; 26 /// <summary> 27 /// 当前材质索引 28 /// </summary> 29 [SerializeField] 30 private int _tempIndex; 31 /// <summary> 32 /// 球的材质贴图 33 /// </summary> 34 public Material ballMaterial; 35 36 void Start() 37 { 38 _tempIndex = 0; 39 PlayBtn.onClick.AddListener(playBtnClick); 40 PrevBtn.onClick.AddListener(UpdateRevTexture); 41 NextBtn.onClick.AddListener(UpdateNextTexture); 42 } 43 public void playBtnClick() 44 { 45 SceneManager.LoadScene("Main"); 46 } 47 /// <summary> 48 /// 更换上一个材质 49 /// </summary> 50 public void UpdateRevTexture() { 51 _tempIndex--; 52 if (_tempIndex==-1) 53 { 54 _tempIndex = ballTextures.Length - 1; 55 } 56 ballMaterial.SetTexture("_MainTex", ballTextures[_tempIndex]); 57 } 58 /// <summary> 59 /// 更换下一个材质 60 /// </summary> 61 public void UpdateNextTexture() 62 { 63 _tempIndex++; 64 if (_tempIndex == ballTextures.Length) 65 { 66 _tempIndex = 0; 67 } 68 ballMaterial.SetTexture("_MainTex", ballTextures[_tempIndex]); 69 } 70 }
StartManage.cs
绑定一下:
这样还没有完。我们还需要一个步骤,从游戏界面返回初始界面。打开Main场景,制作一个返回按键。
经过测试后,完成之后两个场景完美切换。
11、发球后的相机跟随
在GameManage上声明主相机Transform变量,绑定
在BallController添加以下代码:
相机跟随实现。
修改后的BallController代码:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球的控制 6 /// </summary> 7 public class BallController : MonoBehaviour 8 { 9 10 #region 射线相关 11 12 /// <summary> 13 /// 射线碰撞信息 14 /// </summary> 15 private RaycastHit hit; 16 /// <summary> 17 /// 图层遮罩 18 /// </summary> 19 private LayerMask mask; 20 /// <summary> 21 /// 球的位置 22 /// </summary> 23 public Transform ballTrans; 24 25 #endregion 26 27 28 /// <summary> 29 /// 球的半径 30 /// </summary> 31 [SerializeField] 32 private float ballRadius; 33 /// <summary> 34 /// 扔球点 35 /// </summary> 36 private Vector3 ballPushOrgin; 37 /// <summary> 38 /// 球左右方向差量 39 /// </summary> 40 private Vector3 ballPushDir; 41 42 /// <summary> 43 /// 是否推出去 44 /// </summary> 45 public bool pushed = false; 46 47 /// <summary> 48 /// 是否撞墙 49 /// </summary> 50 public bool ishitWall; 51 /// <summary> 52 /// 出界了 53 /// </summary> 54 public bool isOutArea; 55 /// <summary> 56 /// 与相机差距位置 57 /// </summary> 58 private Vector3 _tempPos; 59 /// <summary> 60 /// 保龄球初始化 61 /// </summary> 62 public void Inits() 63 { 64 //获取球的半径 65 ballRadius = ballTrans.GetComponent<SphereCollider>().radius; 66 GetComponent<Rigidbody>().velocity = Vector3.zero; 67 GetComponent<Rigidbody>().isKinematic = true;//不受动力 68 isOutArea = false;//没有越界 69 pushed = false;//没有推出去 70 ishitWall = false;//没有撞墙 71 GameManage.Instacne.CameraTrans.localPosition = Vector3.zero; 72 _tempPos = new Vector3(0, -0.5f, -2); 73 } 74 75 private void Update() 76 { 77 78 79 //发射一条射线 80 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 81 //如果还没有推出去这个球 82 if (!pushed && !GameManage.Instacne.IsOver) 83 { 84 //推球逻辑 85 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor"))) 86 { 87 //推出去 88 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 89 if (hit.transform == GameManage.Instacne.pushArea) 90 { 91 _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置 92 ballTrans.position = _ballPushTrans; 93 ballPushOrgin = hit.point; ;//记录球的起点 94 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z); 95 } 96 else 97 { 98 if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有) 99 { 100 Vector3 _ballPushDir = hit.point;//记录一下球的终点 101 //Debug.Log(ballPushOrgin); 102 ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0); 103 pushed = true; 104 ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 105 GetComponent<Rigidbody>().isKinematic = false; 106 ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 35, ForceMode.Impulse); 107 } 108 } 109 } 110 } 111 else if (pushed)//推出去了 112 { 113 if (GameManage.Instacne.CameraTrans.position.z > -7f) 114 { 115 GameManage.Instacne.CameraTrans.position = transform.position - _tempPos; 116 } 117 } 118 } 119 /// <summary> 120 /// 碰撞器检测 121 /// </summary> 122 /// <param name="collision"></param> 123 private void OnCollisionEnter(Collision collision) 124 { 125 if (collision.gameObject.layer != 8 && pushed) 126 { 127 //Debug.Log("球出界了!"); 128 isOutArea = true; 129 } 130 } 131 }
BallController.cs
12、音效的添加控制
Start场景中,初始BGM添加:
之后是球控制音效:
修改BallController:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 /// <summary> 5 /// 保龄球的控制 6 /// </summary> 7 public class BallController : MonoBehaviour 8 { 9 10 #region 射线相关 11 12 /// <summary> 13 /// 射线碰撞信息 14 /// </summary> 15 private RaycastHit hit; 16 /// <summary> 17 /// 图层遮罩 18 /// </summary> 19 private LayerMask mask; 20 /// <summary> 21 /// 球的位置 22 /// </summary> 23 public Transform ballTrans; 24 25 #endregion 26 27 #region 音效管理 28 /// <summary> 29 /// 鼓掌音源 30 /// </summary> 31 public AudioClip applausSource; 32 /// <summary> 33 /// 正在滚动的声音 34 /// </summary> 35 public AudioClip pushSource; 36 /// <summary> 37 /// BGM 38 /// </summary> 39 public AudioClip BGM; 40 /// <summary> 41 /// 音源 42 /// </summary> 43 public AudioSource AS; 44 /// <summary> 45 /// 播放一次 46 /// </summary> 47 private bool Once; 48 #endregion 49 50 /// <summary> 51 /// 球的半径 52 /// </summary> 53 [SerializeField] 54 private float ballRadius; 55 /// <summary> 56 /// 扔球点 57 /// </summary> 58 private Vector3 ballPushOrgin; 59 /// <summary> 60 /// 球左右方向差量 61 /// </summary> 62 private Vector3 ballPushDir; 63 64 /// <summary> 65 /// 是否推出去 66 /// </summary> 67 public bool pushed = false; 68 69 /// <summary> 70 /// 是否撞墙 71 /// </summary> 72 public bool ishitWall; 73 /// <summary> 74 /// 出界了 75 /// </summary> 76 public bool isOutArea; 77 /// <summary> 78 /// 与相机差距位置 79 /// </summary> 80 private Vector3 _tempPos; 81 /// <summary> 82 /// 保龄球初始化 83 /// </summary> 84 public void Inits() 85 { 86 //获取球的半径 87 ballRadius = ballTrans.GetComponent<SphereCollider>().radius; 88 GetComponent<Rigidbody>().velocity = Vector3.zero; 89 GetComponent<Rigidbody>().isKinematic = true;//不受动力 90 isOutArea = false;//没有越界 91 pushed = false;//没有推出去 92 ishitWall = false;//没有撞墙 93 GameManage.Instacne.CameraTrans.localPosition = Vector3.zero; 94 _tempPos = new Vector3(0, -0.5f, -2); 95 if (!AS.isPlaying) 96 { 97 if (AS.clip == null) 98 { 99 AS.clip = BGM; 100 AS.loop = true; 101 AS.Play(); 102 } 103 } 104 } 105 106 private void Update() 107 { 108 //发射一条射线 109 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 110 //如果还没有推出去这个球 111 if (!pushed && !GameManage.Instacne.IsOver) 112 { 113 //推球逻辑 114 if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor"))) 115 { 116 //推出去 117 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 118 if (hit.transform == GameManage.Instacne.pushArea) 119 { 120 _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置 121 ballTrans.position = _ballPushTrans; 122 ballPushOrgin = hit.point; ;//记录球的起点 123 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z); 124 } 125 else 126 { 127 if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有) 128 { 129 Vector3 _ballPushDir = hit.point;//记录一下球的终点 130 //Debug.Log(ballPushOrgin); 131 ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0); 132 pushed = true; 133 ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f); 134 GetComponent<Rigidbody>().isKinematic = false; 135 ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 35, ForceMode.Impulse); 136 } 137 } 138 } 139 } 140 else if (pushed)//推出去了 141 { 142 if (GameManage.Instacne.CameraTrans.position.z > -7f) 143 { 144 GameManage.Instacne.CameraTrans.position = transform.position - _tempPos; 145 } 146 } 147 } 148 /// <summary> 149 /// 碰撞器检测 150 /// </summary> 151 /// <param name="collision"></param> 152 private void OnCollisionEnter(Collision collision) 153 { 154 if (!Once) 155 { 156 //滚动声音播放 157 AS.Stop(); 158 AS.loop = false; 159 AS.clip = pushSource; 160 AS.Play(); 161 Once = true; 162 } 163 if (collision.gameObject.layer != 8 && pushed) 164 { 165 //Debug.Log("球出界了!"); 166 isOutArea = true; 167 } 168 } 169 }
BallController.cs
最后是Main场景中的掌声:
对GameManage修改:
1 //------------------------------------------------------------------------------ 2 // Copyright 2017 21世纪少年. All rights reserved. 3 // 4 // Unity5.6.3f1 Editor make it 5 // BowlingHall_Project 6 // 7 // CreatTime: #10/7/2017# 8 // Author: Unity Guo 9 //------------------------------------------------------------------------------ 10 using System.Collections; 11 using System.Collections.Generic; 12 using UnityEngine; 13 using UnityEngine.SceneManagement; 14 using UnityEngine.UI; 15 16 public class GameManage : MonoBehaviour 17 { 18 #region 单例 19 20 private static GameManage _Instacne; 21 /// <summary> 22 /// 游戏管理单例 23 /// </summary> 24 public static GameManage Instacne 25 { 26 get 27 { 28 return _Instacne; 29 } 30 31 } 32 private void Awake() 33 { 34 _Instacne = this; 35 } 36 #endregion 37 38 39 #region 场景信息 40 41 /// <summary> 42 /// 墙壁 43 /// </summary> 44 public Transform roomWall; 45 /// <summary> 46 /// 推力范围 47 /// </summary> 48 public Transform pushArea; 49 /// <summary> 50 /// 保龄球瓶堆的父物体位置 51 /// </summary> 52 public Transform bowlsParentTrans; 53 54 #endregion 55 56 #region 游戏对象 57 58 /// <summary> 59 /// 保龄球瓶预制体 60 /// </summary> 61 public GameObject bowlsPrefabs; 62 /// <summary> 63 /// 保龄球瓶堆 64 /// </summary> 65 public GameObject bowls; 66 /// <summary> 67 /// 保龄球预制体 68 /// </summary> 69 public GameObject ballPrefab; 70 /// <summary> 71 /// 保龄球对象 72 /// </summary> 73 public GameObject ball; 74 /// <summary> 75 /// 保龄球上的控制器 76 /// </summary> 77 public BallController _BallController; 78 /// <summary> 79 /// 管理所有瓶子的数组 80 /// </summary> 81 public Bowl[] AllBowls; 82 83 #endregion 84 85 #region 时间管理 86 /// <summary> 87 /// 出界等待时间 88 /// </summary> 89 [SerializeField] 90 private float outAreaWaitTime = 5f; 91 92 private float _temp; 93 /// <summary> 94 /// 每球声明时间 95 /// </summary> 96 [SerializeField] 97 private float BallLifeTime = 30f; 98 /// <summary> 99 /// 滚动时间 100 /// </summary> 101 private float _tempLife; 102 103 #endregion 104 105 #region 分数管理 106 107 /// <summary> 108 /// 每局分数块游戏对象 109 /// </summary> 110 public ScoreItem[] SocreItems; 111 /// <summary> 112 /// 每球的临时得分 113 /// </summary> 114 public int _tempScore; 115 /// <summary> 116 /// 所有总分 117 /// </summary> 118 public int AllScore; 119 /// <summary> 120 /// 所有分数 121 /// </summary> 122 public Text AllScoreText; 123 #endregion 124 125 126 /// <summary> 127 /// 轮数 128 /// </summary> 129 public int indexItem; 130 /// <summary> 131 /// 每轮第N球 132 /// </summary> 133 public int _tempNumBall; 134 /// <summary> 135 /// 相机位置 136 /// </summary> 137 public Transform CameraTrans; 138 /// <summary> 139 /// 返回餐单按键 140 /// </summary> 141 public Button RenturnBtn; 142 /// <summary> 143 /// 游戏结束 144 /// </summary> 145 public bool IsOver; 146 147 private void Start() 148 { 149 RenturnBtn.onClick.AddListener(ReturnMenu); 150 indexItem = 0; 151 _tempNumBall = 1;//第1球 152 SetBall(); 153 SetBowls(); 154 //分数初始化 155 foreach (var item in SocreItems) 156 { 157 item.Inits();//初始化 158 item.ShowScore(); 159 } 160 } 161 private void Update() 162 { 163 if (ball != null)//游戏没有结束 164 { 165 166 if (_BallController.pushed)//球已经被推出去 167 { 168 CameraTrans.GetComponent<AudioSource>().Stop(); 169 _tempLife += Time.deltaTime; 170 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届 171 { 172 Debug.Log("此球分数:" + _tempScore); 173 _tempLife = 0; 174 DestroyImmediate(ball); 175 SetScore(_tempScore); 176 _tempScore = 0;//清零 177 SetBall(); 178 SetBowls(); 179 } 180 if (_BallController.isOutArea)//球出界 181 { 182 _temp += Time.deltaTime; 183 if (_temp > outAreaWaitTime)//超过出界时间 184 { 185 Debug.Log("球出界!此球得分:" + _tempScore); 186 _tempLife = 0; 187 _temp = 0;//重新计时 188 DestroyImmediate(ball); 189 SetScore(_tempScore); 190 _tempScore = 0;//清零 191 SetBall(); 192 SetBowls(); 193 } 194 } 195 } 196 197 198 } 199 } 200 /// <summary> 201 /// 生成新球 202 /// </summary> 203 public void SetBall() 204 { 205 if (ball == null) 206 { 207 ball = Instantiate(ballPrefab) as GameObject; 208 _BallController = ball.GetComponent<BallController>(); 209 _BallController.Inits(); 210 } 211 } 212 /// <summary> 213 /// 传递分数 214 /// </summary> 215 /// <param name="_score">分数值</param> 216 public void SetScore(int _score) 217 { 218 //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 219 if (indexItem < 9)//---------------------------------------------------前九轮 220 { 221 if (_tempNumBall == 1)//第一球 222 { 223 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球 224 SocreItems[indexItem].ShowScore(); 225 if (_score == 10)//分数为10 226 { 227 Debug.LogWarning("Strike全中!"); 228 CameraTrans.GetComponent<AudioSource>().Play(); 229 SocreItems[indexItem].isStrike = true;//设置第一球 230 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 231 DestroyImmediate(bowls);//清空瓶子 232 _tempNumBall = 1;//开始新的一轮 233 } 234 else//不为10 235 { 236 _tempNumBall = 2;//开始下一个球 237 } 238 } 239 else if (_tempNumBall == 2)//第二球 240 { 241 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球 242 SocreItems[indexItem].ShowScore(); 243 if (_score + SocreItems[indexItem].Scores[0].Value == 10)//yi 244 { 245 Debug.LogWarning("Spare补中!"); 246 CameraTrans.GetComponent<AudioSource>().Play(); 247 SocreItems[indexItem].isSpare = true;//设置第一球 248 } 249 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引 250 DestroyImmediate(bowls);//清空瓶子 251 _tempNumBall = 1;//开始新的一轮 252 253 } 254 } 255 else if (indexItem == 9)//--------------------------------------------第十轮 256 { 257 if (_tempNumBall == 1)//第一球 258 { 259 if (_score == 10) 260 { 261 Debug.LogWarning("Strike全中!"); 262 CameraTrans.GetComponent<AudioSource>().Play(); 263 SocreItems[indexItem].isStrike = true;//设置第一球 264 DestroyImmediate(bowls);//清空瓶子 265 } 266 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球 267 SocreItems[indexItem].ShowScore(); 268 _tempNumBall = 2;//开始新的一轮 269 } 270 else if (_tempNumBall == 2)//第二球 271 { 272 if (!SocreItems[indexItem].isStrike)//不是全中 273 { 274 if (SocreItems[indexItem].Scores[0].Value + _score == 10)//达成补中条件 275 { 276 Debug.LogWarning("Spare补中!"); 277 CameraTrans.GetComponent<AudioSource>().Play(); 278 SocreItems[indexItem].isSpare = true;//全中 279 DestroyImmediate(bowls);//清空瓶子 280 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数 281 SocreItems[indexItem].ShowScore(); 282 _tempNumBall = 3;//开始新的一轮,第3球 283 } 284 else 285 { 286 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数 287 SocreItems[indexItem].ShowScore(); 288 IsOver = true; 289 } 290 } 291 else//全中下一球 292 { 293 DestroyImmediate(bowls);//清空瓶子 294 SocreItems[9].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数 295 SocreItems[9].ShowScore(); 296 _tempNumBall = 3;//开始新的一轮,第3球 297 } 298 } 299 else if (_tempNumBall == 3) 300 { 301 DestroyImmediate(bowls);//清空瓶子 302 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球分数 303 SocreItems[indexItem].ShowScore(); 304 305 IsOver = true; 306 } 307 } 308 AllScore = 0; 309 foreach (var item in SocreItems)//加分 310 { 311 item.AddScore(); 312 AllScore += item._Score; 313 } 314 AllScoreText.text = AllScore.ToString(); 315 } 316 317 /// <summary> 318 /// 摆放瓶子 319 /// </summary> 320 public void SetBowls() 321 { 322 if (bowls == null)//没有瓶子 323 { 324 bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject; 325 AllBowls = new Bowl[10];//10个瓶 326 foreach (var item in bowls.GetComponentsInChildren<Bowl>()) 327 { 328 AllBowls[item.Index] = item;//把加载的瓶子放进数组里 329 } 330 } 331 else 332 { 333 foreach (var item in AllBowls) 334 { 335 if (item.GetScore)//没有倒下过显示他们 336 { 337 item.gameObject.SetActive(!item.GetScore); 338 } 339 } 340 } 341 } 342 /// <summary> 343 /// 返回菜单 344 /// </summary> 345 public void ReturnMenu() { 346 SceneManager.LoadScene("Start"); 347 } 348 }
GameManage.cs
最后测试。。。。
就这样完了吗?并没有...现在基本功能已经实现,你可以继续扩展更多的功能。
如果有问题可以给我留言,谢大佬们指点= ̄ω ̄=。
原文地址:http://www.cnblogs.com/craft0625/p/7506289.html