记录的一个简单课后作业...
1、分析需求文案策划
阅读Unity3D保龄球案例设计和积分规则,最后整理游戏规则:
2、搭建游戏场景
导入场景资源包,发现运行资源包的场景发现报错,原因大体是因为网格内陷导致网格碰撞器出现了些小问题,这通常出现在早期Unity版本资源包。
通过勾选每个保龄球的Collider中Convex选项,将不报错。
加载各种模型资源,一系列的操作,最后搭建将场景搭建完毕。把所有的保龄球放在一个AllBowls空物体下,把AllBowls和ball制作成一个预制体。
3、编写保龄球的控制脚本
新建一个BallController.cs脚本组件,用来管理球的发射方法。普通情况下,正常情况玩保龄球,会有以下几种操作:
- 捡球
- 带球到到发球线
- 给球施加一个方向的力
用射线的方式可以达到需求,但是具体如何实现还得需要分析:
我们可以通过射线找到两个关键点扔球点和发球点,两点确定发球方向。在扔球点捡起球,在发球点发射。
根据场景我选择世界坐标Z=8这个轴作为发球面,这有很大的瑕疵,以后会逐渐修正。
BallController.cs代码:
![](https://i-blog.csdnimg.cn/blog_migrate/d5f3ca27818ecf98285d643b3073f782.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/c81ac817b1657f40cfd225be7fd880ad.gif)
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 }
到现在为止发球功能可以实现。
4、每球得分计算
新建一个Bowl.cs脚本组件,检测得分情况。让每个保龄球均挂载该组件。
关于如何判断这个保龄球是否倒了,选择使用transform.localRotation中X和Z属性,如果他们的绝对值大于0.3f,我们就认为这个保龄球倒了。当然我们可以有其他更好的方法,比如碰撞器和触发器,不过通过计算网格的接触点,这会稍微增加一点小小的性能消耗,这个方法会更精准但是我们并不需要这么精准的操作。
Bowl.cs代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/e4d86e57c7fa9d39b188901f0a7800ca.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/298e5fe5337421c557170dec63f87324.gif)
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 }
最后运行测试一下:
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:
![](https://i-blog.csdnimg.cn/blog_migrate/d5c7e46d51177b3e2f5aa904b7d831ec.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3bf3f6be9ccabbc75fb27038a2cb1819.gif)
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(前九局)和FinalScore(最后一局)这些对象都挂载ScoreItem.cs组件并绑定每个ScoreItem中的ScoreTexts。
其中ScoreItem(前九局)绑定每个子对象的文本对象如下:
最后一个FinalScore绑定如下:
绑定完毕后,我们只要获取每个ScoreItem就能直接更新每局数据了。所以我们要在GameManage内编写这些对象的引用,GameManage只有一个,所以我们可以把它单例,全局调用,SocreItems数组存储每局ScoreItem。我们先简单编写GameManage一下:
![](https://i-blog.csdnimg.cn/blog_migrate/d6f7f7d061b0b9487c8ab8d9f0ec23dc.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/c1dd9ee1fc572d9867b495a32569f2ff.gif)
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 }
把刚才的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 }
对到目前为止四个核心代码:
![](https://i-blog.csdnimg.cn/blog_migrate/b90522bcef009f51b091ad2aace34f11.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0d9780941477fe195e633da5e99625d4.gif)
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 }
![](https://i-blog.csdnimg.cn/blog_migrate/59ade1514fb65ad513719710927b763d.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/90e12b57afce40fcd5384fef9a77c4ae.gif)
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 }
![](https://i-blog.csdnimg.cn/blog_migrate/04c3cb44fae5e495097c78f4bb9bd5ac.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0e493eb057cfd42f5918693ecee833b4.gif)
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 }
![](https://i-blog.csdnimg.cn/blog_migrate/9f4593deffa56f99fea27eb7d4648beb.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/77b6b9f8a187ea9e514da4f204893f3e.gif)
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 }
打第一轮测试一下效果,额,测试了八九次才打出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如下:
![](https://i-blog.csdnimg.cn/blog_migrate/4dd783579d9f2cc1c798b1f1c7c47a6a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/7679a33a88cdf754687cee242060ca6b.gif)
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 }
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,构造传值分数赋值,而且这个分数不可修改,封装起来只能获取,保证了外界不能修改你的分数。
理清思路逻辑,接下来进行编码:
![](https://i-blog.csdnimg.cn/blog_migrate/f908452f211ee74235579d7071d9e3d4.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/3878e898769f026e0602baf9a884d8aa.gif)
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 }
对了再加一个IsOver,游戏结束设置为True,修改之后的GameManage:
![](https://i-blog.csdnimg.cn/blog_migrate/6a2d1b608d490caab6c694ac6de96186.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/653eb627bd9d18e7c77eab8c5869c8b3.gif)
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 }
此外修改一下BallController.cs:
这样游戏结束就捡不起来球了。
到目前测试一下,基本上游戏大体逻辑已完全解决!但是这并不是最终代码,得分和现实已经没有问题:
10、开始界面的搭建
和Main场景搭建手法差不多,搭建一个Start场景。
接下来然后搭建Start的UI界面,我们发现这个按钮素材需要九宫切图,于是切一下:
经过一系列的UI布局:
关于场景的替换需要注意一下,不要用Application.loadedLevel(),导入UnityEngine.SceneManagement包,用SceneManager.LoadScene():
我们再编写一个游戏管理的脚本StartManage.cs,让它负责场景的切换和球的表面材质替换。
![](https://i-blog.csdnimg.cn/blog_migrate/f50d59f35e27d792390647d0e9e13d01.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/440d5c1bf946bf3748e63e212580aa1a.gif)
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 }
绑定一下:
这样还没有完。我们还需要一个步骤,从游戏界面返回初始界面。打开Main场景,制作一个返回按键。
经过测试后,完成之后两个场景完美切换。
11、发球后的相机跟随
在GameManage上声明主相机Transform变量,绑定
在BallController添加以下代码:
相机跟随实现。
修改后的BallController代码:
![](https://i-blog.csdnimg.cn/blog_migrate/73e4df4002566cb062baaa96e3d4ce80.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/8e45f5daa4142545c4749846fd0c8265.gif)
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 }
12、音效的添加控制
Start场景中,初始BGM添加:
之后是球控制音效:
修改BallController:
![](https://i-blog.csdnimg.cn/blog_migrate/c4853d10cf09c6b425d63e9bff8226df.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/748739e958f9535a6511894b44ccfeac.gif)
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 }
最后是Main场景中的掌声:
对GameManage修改:
![](https://i-blog.csdnimg.cn/blog_migrate/57bd4f505c369df097af307e04fc1191.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6828350f42687f559472e451b01acb29.gif)
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 }
最后测试。。。。
就这样完了吗?并没有...现在基本功能已经实现,你可以继续扩展更多的功能。
如果有问题可以给我留言,谢大佬们指点= ̄ω ̄=。