# 记录的一个简单课后作业...

## 1、分析需求文案策划

阅读Unity3D保龄球案例设计和积分规则，最后整理游戏规则：

## 3、编写保龄球的控制脚本

1. 捡球
2. 带球到到发球线
3. 给球施加一个方向的力

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>
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]
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         //获取球的半径
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             //推球逻辑
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代码如下：

``` 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，首先建立一个新画布和相机，修改命名。

``` 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。

``` 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：

绑定结束。

``` 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、刷新出球

```  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;
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     }```

再绑定一下各值。

```  1     #region 射线相关
2
3     /// <summary>
4     /// 射线碰撞信息
5     /// </summary>
6     private RaycastHit hit;
7     /// <summary>
8     /// 图层遮罩
9     /// </summary>
11     /// <summary>
12     /// 球的位置
13     /// </summary>
14     public Transform ballTrans;
15
16     #endregion
17
18
19     /// <summary>
20     /// 球的半径
21     /// </summary>
22     [SerializeField]
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         //获取球的半径
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             //推球逻辑
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定义一个方法处理得分情况：

``` 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>
20     /// <summary>
21     /// 球的位置
22     /// </summary>
23     public Transform ballTrans;
24
25     #endregion
26
27
28     /// <summary>
29     /// 球的半径
30     /// </summary>
31     [SerializeField]
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         //获取球的半径
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             //推球逻辑
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>
20     /// <summary>
21     /// 球的位置
22     /// </summary>
23     public Transform ballTrans;
24
25     #endregion
26
27
28     /// <summary>
29     /// 球的半径
30     /// </summary>
31     [SerializeField]
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         //获取球的半径
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             //推球逻辑
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

## 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 //------------------------------------------------------------------------------
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补中计分实现）

``` 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 }```

```  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         }
107     }
108     /// <summary>
109     /// 更新添加分数
110     /// </summary>
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         {
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

到目前测试一下，基本上游戏大体逻辑已完全解决！但是这并不是最终代码，得分和现实已经没有问题：

## 10、开始界面的搭建

和Main场景搭建手法差不多，搭建一个Start场景。

``` 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;
42     }
43     public void playBtnClick()
44     {
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

绑定一下：

## 11、发球后的相机跟随

在GameManage上声明主相机Transform变量，绑定

```  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>
20     /// <summary>
21     /// 球的位置
22     /// </summary>
23     public Transform ballTrans;
24
25     #endregion
26
27
28     /// <summary>
29     /// 球的半径
30     /// </summary>
31     [SerializeField]
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         //获取球的半径
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             //推球逻辑
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添加：

```  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>
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]
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         //获取球的半径
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             //推球逻辑
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场景中的掌声：

```  1 //------------------------------------------------------------------------------
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     {
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         {
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>
347     }
348 }```
GameManage.cs

最后测试。。。。

### 就这样完了吗？并没有...现在基本功能已经实现，你可以继续扩展更多的功能。

如果有问题可以给我留言,谢大佬们指点=￣ω￣=。

(0)
(0)

0条