保龄球游戏Demo

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

1、分析需求文案策划



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

2、搭建游戏场景



导入场景资源包,发现运行资源包的场景发现报错,原因大体是因为网格内陷导致网格碰撞器出现了些小问题,这通常出现在早期Unity版本资源包。

通过勾选每个保龄球的Collider中Convex选项,将不报错。

加载各种模型资源,一系列的操作,最后搭建将场景搭建完毕。把所有的保龄球放在一个AllBowls空物体下,把AllBowls和ball制作成一个预制体。

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



新建一个BallController.cs脚本组件,用来管理球的发射方法。普通情况下,正常情况玩保龄球,会有以下几种操作:

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

用射线的方式可以达到需求,但是具体如何实现还得需要分析:

我们可以通过射线找到两个关键点扔球点和发球点,两点确定发球方向。在扔球点捡起球,在发球点发射。

根据场景我选择世界坐标Z=8这个轴作为发球面,这有很大的瑕疵,以后会逐渐修正。

BallController.cs代码:

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 /// <summary>
 5 /// 保龄球的控制
 6 /// </summary>
 7 public class BallController : MonoBehaviour
 8 {
 9
10     #region 射线相关
11
12     /// <summary>
13     /// 射线碰撞信息
14     /// </summary>
15     private RaycastHit hit;
16     /// <summary>
17     /// 图层遮罩
18     /// </summary>
19     private LayerMask mask;
20     /// <summary>
21     /// 球的位置
22     /// </summary>
23     public Transform ballTrans;
24
25     #endregion
26
27     /// <summary>
28     /// 推力范围
29     /// </summary>
30     public Transform pushArea;
31     /// <summary>
32     /// 球的半径
33     /// </summary>
34     [SerializeField]
35     private float ballRadius;
36     /// <summary>
37     /// 扔球点
38     /// </summary>
39     private Vector3 ballPushOrgin;
40     /// <summary>
41     /// 球左右方向差量
42     /// </summary>
43     private Vector3 ballPushDir;
44
45     //是否推出去
46     static bool pushed = false;
47     private void Start()
48     {
49         //获取球的半径
50         ballRadius = ballTrans.GetComponent<SphereCollider>().radius;
51         GetComponent<Rigidbody>().isKinematic =true;
52     }
53
54     private void Update()
55     {
56         //发射一条射线
57         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
58         //如果还没有推出去这个球
59         if (!pushed)
60         {
61             //推球逻辑
62             if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor")))
63             {
64                 //推出去
65                 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
66                 if (hit.transform == pushArea)
67                 {
68                     _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置
69                     ballTrans.position = _ballPushTrans;
70                     ballPushOrgin = hit.point; ;//记录球的起点
71                     //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z);
72                 }
73                 else
74                 {
75                     if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有)
76                     {
77                         Vector3 _ballPushDir = hit.point;//记录一下球的终点
78                         Debug.Log(ballPushOrgin);
79                         ballPushDir = _ballPushDir - ballPushOrgin;
80                         pushed = true;
81                         ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
82                         GetComponent<Rigidbody>().isKinematic = false;
83                         ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 20, ForceMode.Impulse);
84
85                     }
86                 }
87             }
88         }
89     }
90 }

BallController.cs

到现在为止发球功能可以实现。

4、每球得分计算



新建一个Bowl.cs脚本组件,检测得分情况。让每个保龄球均挂载该组件。

        

关于如何判断这个保龄球是否倒了,选择使用transform.localRotation中X和Z属性,如果他们的绝对值大于0.3f,我们就认为这个保龄球倒了。当然我们可以有其他更好的方法,比如碰撞器和触发器,不过通过计算网格的接触点,这会稍微增加一点小小的性能消耗,这个方法会更精准但是我们并不需要这么精准的操作。

Bowl.cs代码如下:

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 /// <summary>
 5 /// 保龄球
 6 /// </summary>
 7 public class Bowl : MonoBehaviour
 8 {
 9     /// <summary>
10     /// 是否得分
11     /// </summary>
12     public bool GetScore;
13
14     private void FixedUpdate()
15     {
16         if (!GetScore)
17         {
18             if (Mathf.Abs(transform.localRotation.x) > 0.3f || Mathf.Abs(transform.localRotation.z) > 0.3f)
19             {
20
21                 GetScore = true;
22                 Debug.LogWarning("分数加+1");
23             }
24         }
25     }
26 }

Bowl.cs

最后运行测试一下:

5、UI统计分数面板



UI的搭建选择使用UGUI,首先建立一个新画布和相机,修改命名。

对Player_UI_Camera和Player_UI_Canvas进行调整,以适应场景。

    

接下来就是UI界面搭建。

新建一个脚本GameManage.cs组件,用来游戏管理。挂载到Player_UI_Panel对象上。可是如何进行游戏管理?把所有的UIText设置更新都写在GameManage.cs里?这无疑对GamManage.cs产生较大的功能负担,所以我们新建的GameManage.cs先不编辑,建一个ScoreItem.cs类,这个东西负责统计存储和修改每轮分数。然后让GameManage去管理它。

编写ScoreItem.cs:

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 using UnityEngine.UI;
 5
 6 public class ScoreItem : MonoBehaviour
 7 {
 8     /// <summary>
 9     /// 所有分数文字
10     /// </summary>
11     public Text[] SocreTexts;
12     /// <summary>
13     /// 每轮分数
14     /// </summary>
15     [SerializeField]
16     private int[] Scores;
17     /// <summary>
18     /// 本轮分数总和
19     /// </summary>
20     public int AllScoreItemNum;
21     /// <summary>
22     /// 本轮全中?
23     /// </summary>
24     public bool isStrike = false;
25     /// <summary>
26     /// 本轮补中?
27     /// </summary>
28     public bool isSpare = false;
29
30     public void Start()
31     {
32         Scores = new int[SocreTexts.Length - 1];//每轮分数初始化
33     }
34     /// <summary>
35     /// 每轮分数设置索引为_index的分数文本的值为_score
36     /// </summary>
37     /// <param name="_index">SocreTexts索引</param>
38     /// <param name="_score">分数</param>
39     public void SetScoreText(int _index, int _score)
40     {
41         Scores = new int[SocreTexts.Length];//更新设置分数
42         Scores[_index] = _score;//更新并存储分数
43         AllScoreItemNum = 0;
44         foreach (var item in Scores)
45         {
46             AllScoreItemNum += item;//累加本轮分数总和
47         }
48     }
49     /// <summary>
50     /// 显示所有分数
51     /// </summary>
52     public void ShowScore()
53     {
54         //设置每轮
55         for (int i = 0; i < SocreTexts.Length - 1; i++)
56         {
57             SocreTexts[i].text = Scores[i].ToString();
58         }
59         SocreTexts[SocreTexts.Length - 1].text = AllScoreItemNum.ToString();//总分设置
60
61     }
62 }

ScoreItem.cs

我们为每个ScoreItem(前九局)和FinalScore(最后一局)这些对象都挂载ScoreItem.cs组件并绑定每个ScoreItem中的ScoreTexts。

其中ScoreItem(前九局)绑定每个子对象的文本对象如下:

 

最后一个FinalScore绑定如下:

绑定完毕后,我们只要获取每个ScoreItem就能直接更新每局数据了。所以我们要在GameManage内编写这些对象的引用,GameManage只有一个,所以我们可以把它单例,全局调用,SocreItems数组存储每局ScoreItem。我们先简单编写GameManage一下:

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 using UnityEngine.UI;
 5
 6 public class GameManage : MonoBehaviour
 7 {
 8     #region 单例
 9
10     private static GameManage _Instacne;
11     /// <summary>
12     /// 游戏管理单例
13     /// </summary>
14     public static GameManage Instacne
15     {
16         get
17         {
18             return _Instacne;
19         }
20
21     }
22     private void Awake()
23     {
24         _Instacne = this;
25     }
26     #endregion
27     /// <summary>
28     /// 每局分数块游戏对象
29     /// </summary>
30     public ScoreItem[] SocreItems;
31
32     /// <summary>
33     /// 每球的临时得分
34     /// </summary>
35     public int _tempScore;
36
37     /// <summary>
38     /// 保龄球瓶预制体
39     /// </summary>
40     public GameObject bowlsPrefabs;
41
42     /// <summary>
43     /// 保龄球控制预制体
44     /// </summary>
45     public BallController ballController;
46
47
48 }

GameManage.cs

把刚才的ScoreItem那些组件绑定,并顺手绑定一下BowlsPrefabs:

绑定结束。

修改重新修改一下Bowl然后再发射一球

 1 public class Bowl : MonoBehaviour
 2 {
 3     /// <summary>
 4     /// 是否得分
 5     /// </summary>
 6     public bool GetScore;
 7
 8     private void FixedUpdate()
 9     {
10         if (!GetScore)
11         {
12             if (Mathf.Abs(transform.localRotation.x) > 0.3f || Mathf.Abs(transform.localRotation.z) > 0.3f)
13             {
14
15                 GetScore = true;
16                 GameManage.Instacne._tempScore++;//这轮分数
17             }
18         }
19     }
20 }

测试一下,已经清晰的看见GameManage的TempScore变量=6,也就是这一球得分:

6、刷新出球



发球得分已经可以获取到了,那么接下来要实现的功能就是如何结束此球结束,开始下一球,不断刷新出球。我们的思路如下:

所以我们重新修改一下GameManage.cs:

  1    #region 单例
  2
  3     private static GameManage _Instacne;
  4     /// <summary>
  5     /// 游戏管理单例
  6     /// </summary>
  7     public static GameManage Instacne
  8     {
  9         get
 10         {
 11             return _Instacne;
 12         }
 13
 14     }
 15     private void Awake()
 16     {
 17         _Instacne = this;
 18     }
 19     #endregion
 20     /// <summary>
 21     /// 每局分数块游戏对象
 22     /// </summary>
 23     public ScoreItem[] SocreItems;
 24
 25     /// <summary>
 26     /// 每球的临时得分
 27     /// </summary>
 28     public int _tempScore;
 29
 30     /// <summary>
 31     /// 墙壁
 32     /// </summary>
 33     public Transform roomWall;
 34
 35     /// <summary>
 36     /// 推力范围
 37     /// </summary>
 38     public Transform pushArea;
 39
 40     /// <summary>
 41     /// 保龄球瓶预制体
 42     /// </summary>
 43     public GameObject bowlsPrefabs;
 44     /// <summary>
 45     /// 保龄球预制体
 46     /// </summary>
 47     public GameObject ballPrefab;
 48     /// <summary>
 49     /// 保龄球对象
 50     /// </summary>
 51     public GameObject ball;
 52     public BallController _BallController;
 53     /// <summary>
 54     /// 出界等待时间
 55     /// </summary>
 56     [SerializeField]
 57     private float outAreaWaitTime = 5f;
 58
 59     private float _temp;
 60     /// <summary>
 61     /// 每球声明时间
 62     /// </summary>
 63     [SerializeField]
 64     private float BallLifeTime = 30f;
 65     /// <summary>
 66     /// 滚动时间
 67     /// </summary>
 68     private float _tempLife;
 69
 70     private void Start()
 71     {
 72         CreatBall();
 73     }
 74     private void Update()
 75     {
 76         if (ball != null)
 77         {
 78             if (_BallController.pushed)//球已经被推出去
 79             {
 80                 _tempLife += Time.deltaTime;
 81                 if (_tempLife>BallLifeTime&&!_BallController.isOutArea)//一直没出届
 82                 {
 83                     Debug.Log("此球分数:" + _tempScore);
 84                     _tempLife = 0;
 85                     _tempScore = 0;//清零
 86                     DestroyImmediate(ball);
 87                     CreatBall();
 88                 }
 89                 if (_BallController.isOutArea)//球出界
 90                 {
 91                     _temp += Time.deltaTime;
 92                     if (_temp > outAreaWaitTime)//超过出界时间
 93                     {
 94                         Debug.Log("此球分数:" + _tempScore);
 95                         _tempLife = 0;
 96                         _temp = 0;//重新计时
 97                         _tempScore = 0;//清零
 98                         DestroyImmediate(ball);
 99                         CreatBall();
100                     }
101                 }
102             }
103         }
104     }
105     public void CreatBall()
106     {
107         if (ball == null)
108         {
109             ball = Instantiate(ballPrefab) as GameObject;
110             _BallController = ball.GetComponent<BallController>();
111             _BallController.Inits();
112         }
113     }

再绑定一下各值。

稍微修改一下BallController.cs,添加越界的碰撞检测:

  1     #region 射线相关
  2
  3     /// <summary>
  4     /// 射线碰撞信息
  5     /// </summary>
  6     private RaycastHit hit;
  7     /// <summary>
  8     /// 图层遮罩
  9     /// </summary>
 10     private LayerMask mask;
 11     /// <summary>
 12     /// 球的位置
 13     /// </summary>
 14     public Transform ballTrans;
 15
 16     #endregion
 17
 18
 19     /// <summary>
 20     /// 球的半径
 21     /// </summary>
 22     [SerializeField]
 23     private float ballRadius;
 24     /// <summary>
 25     /// 扔球点
 26     /// </summary>
 27     private Vector3 ballPushOrgin;
 28     /// <summary>
 29     /// 球左右方向差量
 30     /// </summary>
 31     private Vector3 ballPushDir;
 32
 33     /// <summary>
 34     /// 是否推出去
 35     /// </summary>
 36     public bool pushed = false;
 37
 38
 39     /// <summary>
 40     /// 是否撞墙
 41     /// </summary>
 42     public bool ishitWall;
 43     /// <summary>
 44     /// 出界了
 45     /// </summary>
 46     public bool isOutArea;
 47
 48     /// <summary>
 49     /// 保龄球初始化
 50     /// </summary>
 51     public void Inits() {
 52         //获取球的半径
 53         ballRadius = ballTrans.GetComponent<SphereCollider>().radius;
 54         GetComponent<Rigidbody>().velocity = Vector3.zero;
 55         GetComponent<Rigidbody>().isKinematic = true;//不受动力
 56         isOutArea = false;//没有越界
 57         pushed = false;//没有推出去
 58         ishitWall =false;//没有撞墙
 59     }
 60
 61     private void Update()
 62     {
 63         //发射一条射线
 64         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
 65         //如果还没有推出去这个球
 66         if (!pushed)
 67         {
 68             //推球逻辑
 69             if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor")))
 70             {
 71                 //推出去
 72                 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 73                 if (hit.transform == GameManage.Instacne.pushArea)
 74                 {
 75                     _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置
 76                     ballTrans.position = _ballPushTrans;
 77                     ballPushOrgin = hit.point; ;//记录球的起点
 78                     //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z);
 79                 }
 80                 else
 81                 {
 82                     if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有)
 83                     {
 84                         Vector3 _ballPushDir = hit.point;//记录一下球的终点
 85                         Debug.Log(ballPushOrgin);
 86                         ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0);
 87                         pushed = true;
 88                         ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 89                         GetComponent<Rigidbody>().isKinematic = false;
 90                         ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 25, ForceMode.Impulse);
 91
 92                     }
 93                 }
 94             }
 95         }
 96     }
 97     /// <summary>
 98     /// 碰撞器检测
 99     /// </summary>
100     /// <param name="collision"></param>
101     private void OnCollisionEnter(Collision collision)
102     {
103         if (collision.gameObject.layer != 8 && pushed)
104         {
105             Debug.Log("球出界了!");
106             isOutArea = true;
107         }
108     }

截止到目前为止刷新球的功能没问题了

7、游戏逻辑实现(显示统计每轮得分)



接下来的几部就实现最后游戏逻辑了,也是最麻烦的。由于前期已经做了较多的功能,所以现在已经减轻了不少代码负担。

在GameManage.cs定义一个indexItem,记录目前第几轮。

分析一下,如果你开始每一轮第一球打出Strike(10瓶全中),第一轮直接就结束了,但是这一轮的总分还没结束,因为Strike要加下两球的得分。

如果第一球并没有打出Strike,不管第二球打出什么都会结束,第二球如果打出Spare(补中),这一轮的总分也没结束,因为Spare要加下一球的得分;第二球如果没有打出Spare(补中),这一轮的总分就可以算出来了这两球得分之和。

我们先解决第一轮打球问题,我们在GameManage定义一个方法处理得分情况:

 1     /// <summary>
 2     /// 传递分数
 3     /// </summary>
 4     /// <param name="_score">分数值</param>
 5     public void SetScore(int _score)
 6     {
 7         if (indexItem < 9)//前九轮
 8         {
 9             if (_tempNumBall == 1)//第一球
10             {
11                 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球
12                 SocreItems[indexItem].ShowScore();
13                 if (_score == 10)//分数为10
14                 {
15
16                     Debug.LogWarning("Strike全中!");
17                     SocreItems[indexItem].isStrike = true;//设置第一球
18                     indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
19                     _tempNumBall = 1;//开始新的一轮
20                 }
21                 else//不为10
22                 {
23                     _tempNumBall = 2;//开始下一个球
24                 }
25             }
26             else if (_tempNumBall == 2)//第二球
27             {
28                 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球
29                 SocreItems[indexItem].ShowScore();
30                 if (_score + SocreItems[indexItem].Scores[0] == 10)//yi
31                 {
32
33                     Debug.LogWarning("Spare补中!");
34                     SocreItems[indexItem].isSpare = true;//设置第一球
35                 }
36                 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
37                 _tempNumBall = 1;//开始新的一轮
38             }
39         }
40     }

对到目前为止四个核心代码:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 using UnityEngine.UI;
  5
  6 public class GameManage : MonoBehaviour
  7 {
  8     #region 单例
  9
 10     private static GameManage _Instacne;
 11     /// <summary>
 12     /// 游戏管理单例
 13     /// </summary>
 14     public static GameManage Instacne
 15     {
 16         get
 17         {
 18             return _Instacne;
 19         }
 20
 21     }
 22     private void Awake()
 23     {
 24         _Instacne = this;
 25     }
 26     #endregion
 27     /// <summary>
 28     /// 每局分数块游戏对象
 29     /// </summary>
 30     public ScoreItem[] SocreItems;
 31
 32     /// <summary>
 33     /// 每球的临时得分
 34     /// </summary>
 35     public int _tempScore;
 36
 37     /// <summary>
 38     /// 墙壁
 39     /// </summary>
 40     public Transform roomWall;
 41
 42     /// <summary>
 43     /// 推力范围
 44     /// </summary>
 45     public Transform pushArea;
 46
 47     /// <summary>
 48     /// 保龄球瓶预制体
 49     /// </summary>
 50     public GameObject bowlsPrefabs;
 51     /// <summary>
 52     /// 保龄球预制体
 53     /// </summary>
 54     public GameObject ballPrefab;
 55     /// <summary>
 56     /// 保龄球对象
 57     /// </summary>
 58     public GameObject ball;
 59     public BallController _BallController;
 60     /// <summary>
 61     /// 出界等待时间
 62     /// </summary>
 63     [SerializeField]
 64     private float outAreaWaitTime = 5f;
 65
 66     private float _temp;
 67     /// <summary>
 68     /// 每球声明时间
 69     /// </summary>
 70     [SerializeField]
 71     private float BallLifeTime = 30f;
 72     /// <summary>
 73     /// 滚动时间
 74     /// </summary>
 75     private float _tempLife;
 76     /// <summary>
 77     /// 轮数
 78     /// </summary>
 79     public int indexItem;
 80     /// <summary>
 81     /// 每轮第N球
 82     /// </summary>
 83     public int _tempNumBall;
 84
 85     private void Start()
 86     {
 87         indexItem = 0;
 88         _tempNumBall = 1;//第1球
 89         CreatBall();
 90     }
 91     private void Update()
 92     {
 93         if (ball != null)
 94         {
 95             if (_BallController.pushed)//球已经被推出去
 96             {
 97                 _tempLife += Time.deltaTime;
 98                 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届
 99                 {
100                     Debug.Log("此球分数:" + _tempScore);
101                     SetScore(_tempScore);
102                     _tempLife = 0;
103                     _tempScore = 0;//清零
104                     DestroyImmediate(ball);
105                     CreatBall();
106                 }
107                 if (_BallController.isOutArea)//球出界
108                 {
109                     _temp += Time.deltaTime;
110                     if (_temp > outAreaWaitTime)//超过出界时间
111                     {
112                         Debug.Log("球出界!此球得分:" + _tempScore);
113                         SetScore(_tempScore);
114                         _tempLife = 0;
115                         _temp = 0;//重新计时
116                         _tempScore = 0;//清零
117                         DestroyImmediate(ball);
118                         CreatBall();
119                     }
120                 }
121             }
122         }
123     }
124     public void CreatBall()
125     {
126         if (ball == null)
127         {
128             ball = Instantiate(ballPrefab) as GameObject;
129             _BallController = ball.GetComponent<BallController>();
130             _BallController.Inits();
131         }
132     }
133     /// <summary>
134     /// 传递分数
135     /// </summary>
136     /// <param name="_score">分数值</param>
137     public void SetScore(int _score)
138     {
139         //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
140         if (indexItem < 9)//前九轮
141         {
142             if (_tempNumBall == 1)//第一球
143             {
144                 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球
145                 SocreItems[indexItem].ShowScore();
146                 if (_score == 10)//分数为10
147                 {
148
149                     Debug.LogWarning("Strike全中!");
150                     SocreItems[indexItem].isStrike = true;//设置第一球
151                     indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
152                     _tempNumBall = 1;//开始新的一轮
153                 }
154                 else//不为10
155                 {
156                     _tempNumBall = 2;//开始下一个球
157                 }
158             }
159             else if (_tempNumBall == 2)//第二球
160             {
161                 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球
162                 SocreItems[indexItem].ShowScore();
163                 if (_score + SocreItems[indexItem].Scores[0] == 10)//yi
164                 {
165
166                     Debug.LogWarning("Spare补中!");
167                     SocreItems[indexItem].isSpare = true;//设置第一球
168                 }
169                 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
170                 _tempNumBall = 1;//开始新的一轮
171             }
172         }
173     }
174 }

GameManage.cs

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 /// <summary>
 5 /// 保龄球
 6 /// </summary>
 7 public class Bowl : MonoBehaviour
 8 {
 9     /// <summary>
10     /// 是否得分
11     /// </summary>
12     public bool GetScore;
13     /// <summary>
14     /// 瓶号索引
15     /// </summary>
16     public int Index;
17
18     private void FixedUpdate()
19     {
20         if (!GetScore)
21         {
22             if (Mathf.Abs(transform.localRotation.x) > 0.3f || Mathf.Abs(transform.localRotation.z) > 0.3f)
23             {
24
25                 GetScore = true;
26                 GameManage.Instacne._tempScore++;//这轮分数
27             }
28         }
29     }

Bowl.cs

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 /// <summary>
  5 /// 保龄球的控制
  6 /// </summary>
  7 public class BallController : MonoBehaviour
  8 {
  9
 10     #region 射线相关
 11
 12     /// <summary>
 13     /// 射线碰撞信息
 14     /// </summary>
 15     private RaycastHit hit;
 16     /// <summary>
 17     /// 图层遮罩
 18     /// </summary>
 19     private LayerMask mask;
 20     /// <summary>
 21     /// 球的位置
 22     /// </summary>
 23     public Transform ballTrans;
 24
 25     #endregion
 26
 27
 28     /// <summary>
 29     /// 球的半径
 30     /// </summary>
 31     [SerializeField]
 32     private float ballRadius;
 33     /// <summary>
 34     /// 扔球点
 35     /// </summary>
 36     private Vector3 ballPushOrgin;
 37     /// <summary>
 38     /// 球左右方向差量
 39     /// </summary>
 40     private Vector3 ballPushDir;
 41
 42     /// <summary>
 43     /// 是否推出去
 44     /// </summary>
 45     public bool pushed = false;
 46
 47
 48     /// <summary>
 49     /// 是否撞墙
 50     /// </summary>
 51     public bool ishitWall;
 52     /// <summary>
 53     /// 出界了
 54     /// </summary>
 55     public bool isOutArea;
 56
 57     /// <summary>
 58     /// 保龄球初始化
 59     /// </summary>
 60     public void Inits() {
 61         //获取球的半径
 62         ballRadius = ballTrans.GetComponent<SphereCollider>().radius;
 63         GetComponent<Rigidbody>().velocity = Vector3.zero;
 64         GetComponent<Rigidbody>().isKinematic = true;//不受动力
 65         isOutArea = false;//没有越界
 66         pushed = false;//没有推出去
 67         ishitWall =false;//没有撞墙
 68     }
 69
 70     private void Update()
 71     {
 72         //发射一条射线
 73         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
 74         //如果还没有推出去这个球
 75         if (!pushed)
 76         {
 77             //推球逻辑
 78             if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor")))
 79             {
 80                 //推出去
 81                 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 82                 if (hit.transform == GameManage.Instacne.pushArea)
 83                 {
 84                     _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置
 85                     ballTrans.position = _ballPushTrans;
 86                     ballPushOrgin = hit.point; ;//记录球的起点
 87                     //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z);
 88                 }
 89                 else
 90                 {
 91                     if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有)
 92                     {
 93                         Vector3 _ballPushDir = hit.point;//记录一下球的终点
 94                         Debug.Log(ballPushOrgin);
 95                         ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0);
 96                         pushed = true;
 97                         ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 98                         GetComponent<Rigidbody>().isKinematic = false;
 99                         ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 25, ForceMode.Impulse);
100
101                     }
102                 }
103             }
104         }
105     }
106     /// <summary>
107     /// 碰撞器检测
108     /// </summary>
109     /// <param name="collision"></param>
110     private void OnCollisionEnter(Collision collision)
111     {
112         if (collision.gameObject.layer != 8 && pushed)
113         {
114             //Debug.Log("球出界了!");
115             isOutArea = true;
116         }
117     }
118 }

BallController.cs

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 /// <summary>
  5 /// 保龄球的控制
  6 /// </summary>
  7 public class BallController : MonoBehaviour
  8 {
  9
 10     #region 射线相关
 11
 12     /// <summary>
 13     /// 射线碰撞信息
 14     /// </summary>
 15     private RaycastHit hit;
 16     /// <summary>
 17     /// 图层遮罩
 18     /// </summary>
 19     private LayerMask mask;
 20     /// <summary>
 21     /// 球的位置
 22     /// </summary>
 23     public Transform ballTrans;
 24
 25     #endregion
 26
 27
 28     /// <summary>
 29     /// 球的半径
 30     /// </summary>
 31     [SerializeField]
 32     private float ballRadius;
 33     /// <summary>
 34     /// 扔球点
 35     /// </summary>
 36     private Vector3 ballPushOrgin;
 37     /// <summary>
 38     /// 球左右方向差量
 39     /// </summary>
 40     private Vector3 ballPushDir;
 41
 42     /// <summary>
 43     /// 是否推出去
 44     /// </summary>
 45     public bool pushed = false;
 46
 47
 48     /// <summary>
 49     /// 是否撞墙
 50     /// </summary>
 51     public bool ishitWall;
 52     /// <summary>
 53     /// 出界了
 54     /// </summary>
 55     public bool isOutArea;
 56
 57     /// <summary>
 58     /// 保龄球初始化
 59     /// </summary>
 60     public void Inits() {
 61         //获取球的半径
 62         ballRadius = ballTrans.GetComponent<SphereCollider>().radius;
 63         GetComponent<Rigidbody>().velocity = Vector3.zero;
 64         GetComponent<Rigidbody>().isKinematic = true;//不受动力
 65         isOutArea = false;//没有越界
 66         pushed = false;//没有推出去
 67         ishitWall =false;//没有撞墙
 68     }
 69
 70     private void Update()
 71     {
 72         //发射一条射线
 73         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
 74         //如果还没有推出去这个球
 75         if (!pushed)
 76         {
 77             //推球逻辑
 78             if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor")))
 79             {
 80                 //推出去
 81                 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 82                 if (hit.transform == GameManage.Instacne.pushArea)
 83                 {
 84                     _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置
 85                     ballTrans.position = _ballPushTrans;
 86                     ballPushOrgin = hit.point; ;//记录球的起点
 87                     //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z);
 88                 }
 89                 else
 90                 {
 91                     if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有)
 92                     {
 93                         Vector3 _ballPushDir = hit.point;//记录一下球的终点
 94                         Debug.Log(ballPushOrgin);
 95                         ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0);
 96                         pushed = true;
 97                         ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 98                         GetComponent<Rigidbody>().isKinematic = false;
 99                         ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 25, ForceMode.Impulse);
100
101                     }
102                 }
103             }
104         }
105     }
106     /// <summary>
107     /// 碰撞器检测
108     /// </summary>
109     /// <param name="collision"></param>
110     private void OnCollisionEnter(Collision collision)
111     {
112         if (collision.gameObject.layer != 8 && pushed)
113         {
114             //Debug.Log("球出界了!");
115             isOutArea = true;
116         }
117     }
118 }

BallController.cs

打第一轮测试一下效果,额,测试了八九次才打出Strike,非常理想的达到预期了的效果:

  

8、游戏逻辑实现(多次开局摆放瓶子)



保龄球每打完一局就应该把倒的瓶子清除,然后重新摆放。

其实摆放瓶子方法也很好分析,每轮结束销毁瓶子堆,不结束就把倒得那些隐藏掉SetActive(false)。

关于何时销毁,我选择把它放在分数统计里调用,因为只有经过分数统计你才知道是否所有瓶子都倒下了。

摆放的方法放在生成新球的后面:

而摆放瓶子的方法如下:

 1    /// <summary>
 2     /// 摆放瓶子
 3     /// </summary>
 4     public void SetBowls()
 5     {
 6         if (bowls == null)//没有瓶子
 7         {
 8             bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject;
 9             AllBowls = new Bowl[10];//10个瓶
10             foreach (var item in bowls.GetComponentsInChildren<Bowl>())
11             {
12                 AllBowls[item.Index] = item;//把加载的瓶子放进数组里
13             }
14         }
15         else
16         {
17             foreach (var item in AllBowls)
18             {
19                 if (item.GetScore)//没有倒下过显示他们
20                 {
21                     item.gameObject.SetActive(!item.GetScore);
22                 }
23             }
24         }
25     }

关于对象的绑定:

GameManage.cs里面的代码也越来越多,整理以后的GameManage如下:

  1 //------------------------------------------------------------------------------
  2 // Copyright 2017 21世纪少年. All rights reserved.
  3 //
  4 // Unity5.6.3f1 Editor make it
  5 // BowlingHall_Project
  6 //
  7 // CreatTime: #10/7/2017#
  8 // Author: Unity Guo
  9 //------------------------------------------------------------------------------
 10 using System.Collections;
 11 using System.Collections.Generic;
 12 using UnityEngine;
 13 using UnityEngine.UI;
 14
 15 public class GameManage : MonoBehaviour
 16 {
 17     #region 单例
 18
 19     private static GameManage _Instacne;
 20     /// <summary>
 21     /// 游戏管理单例
 22     /// </summary>
 23     public static GameManage Instacne
 24     {
 25         get
 26         {
 27             return _Instacne;
 28         }
 29
 30     }
 31     private void Awake()
 32     {
 33         _Instacne = this;
 34     }
 35     #endregion
 36
 37
 38     #region 场景信息
 39
 40     /// <summary>
 41     /// 墙壁
 42     /// </summary>
 43     public Transform roomWall;
 44     /// <summary>
 45     /// 推力范围
 46     /// </summary>
 47     public Transform pushArea;
 48     /// <summary>
 49     /// 保龄球瓶堆的父物体位置
 50     /// </summary>
 51     public Transform bowlsParentTrans;
 52
 53     #endregion
 54
 55     #region 游戏对象
 56
 57     /// <summary>
 58     /// 保龄球瓶预制体
 59     /// </summary>
 60     public GameObject bowlsPrefabs;
 61     /// <summary>
 62     /// 保龄球瓶堆
 63     /// </summary>
 64     public GameObject bowls;
 65     /// <summary>
 66     /// 保龄球预制体
 67     /// </summary>
 68     public GameObject ballPrefab;
 69     /// <summary>
 70     /// 保龄球对象
 71     /// </summary>
 72     public GameObject ball;
 73     /// <summary>
 74     /// 保龄球上的控制器
 75     /// </summary>
 76     public BallController _BallController;
 77
 78     #endregion
 79
 80     #region 时间管理
 81     /// <summary>
 82     /// 出界等待时间
 83     /// </summary>
 84     [SerializeField]
 85     private float outAreaWaitTime = 5f;
 86
 87     private float _temp;
 88     /// <summary>
 89     /// 每球声明时间
 90     /// </summary>
 91     [SerializeField]
 92     private float BallLifeTime = 30f;
 93     /// <summary>
 94     /// 滚动时间
 95     /// </summary>
 96     private float _tempLife;
 97
 98     #endregion
 99
100     /// <summary>
101     /// 每局分数块游戏对象
102     /// </summary>
103     public ScoreItem[] SocreItems;
104     /// <summary>
105     /// 管理所有瓶子的数组
106     /// </summary>
107     public Bowl[] AllBowls;
108     /// <summary>
109     /// 每球的临时得分
110     /// </summary>
111     public int _tempScore;
112     /// <summary>
113     /// 轮数
114     /// </summary>
115     public int indexItem;
116     /// <summary>
117     /// 每轮第N球
118     /// </summary>
119     public int _tempNumBall;
120
121     private void Start()
122     {
123         indexItem = 0;
124         _tempNumBall = 1;//第1球
125         SetBall();
126         SetBowls();
127     }
128     private void Update()
129     {
130         if (ball != null)
131         {
132             if (_BallController.pushed)//球已经被推出去
133             {
134                 _tempLife += Time.deltaTime;
135                 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届
136                 {
137                     Debug.Log("此球分数:" + _tempScore);
138                     SetScore(_tempScore);
139                     _tempLife = 0;
140                     _tempScore = 0;//清零
141                     DestroyImmediate(ball);
142                     SetBall();
143                     SetBowls();
144                 }
145                 if (_BallController.isOutArea)//球出界
146                 {
147                     _temp += Time.deltaTime;
148                     if (_temp > outAreaWaitTime)//超过出界时间
149                     {
150                         Debug.Log("球出界!此球得分:" + _tempScore);
151                         SetScore(_tempScore);
152                         _tempLife = 0;
153                         _temp = 0;//重新计时
154                         _tempScore = 0;//清零
155                         DestroyImmediate(ball);
156                         SetBall();
157                         SetBowls();
158                     }
159                 }
160             }
161         }
162     }
163     /// <summary>
164     /// 生成新球
165     /// </summary>
166     public void SetBall()
167     {
168         if (ball == null)
169         {
170             ball = Instantiate(ballPrefab) as GameObject;
171             _BallController = ball.GetComponent<BallController>();
172             _BallController.Inits();
173         }
174     }
175     /// <summary>
176     /// 传递分数
177     /// </summary>
178     /// <param name="_score">分数值</param>
179     public void SetScore(int _score)
180     {
181         //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
182         if (indexItem < 9)//前九轮
183         {
184             if (_tempNumBall == 1)//第一球
185             {
186                 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球
187                 SocreItems[indexItem].ShowScore();
188                 if (_score == 10)//分数为10
189                 {
190                     Debug.LogWarning("Strike全中!");
191                     SocreItems[indexItem].isStrike = true;//设置第一球
192                     indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
193                     DestroyImmediate(bowls);//清空瓶子
194                     _tempNumBall = 1;//开始新的一轮
195                 }
196                 else//不为10
197                 {
198                     _tempNumBall = 2;//开始下一个球
199                 }
200             }
201             else if (_tempNumBall == 2)//第二球
202             {
203                 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球
204                 SocreItems[indexItem].ShowScore();
205                 if (_score + SocreItems[indexItem].Scores[0] == 10)//yi
206                 {
207                     Debug.LogWarning("Spare补中!");
208                     SocreItems[indexItem].isSpare = true;//设置第一球
209                 }
210                 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
211                 DestroyImmediate(bowls);//清空瓶子
212                 _tempNumBall = 1;//开始新的一轮
213             }
214         }
215     }
216
217     /// <summary>
218     /// 摆放瓶子
219     /// </summary>
220     public void SetBowls()
221     {
222         if (bowls == null)//没有瓶子
223         {
224             bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject;
225             AllBowls = new Bowl[10];//10个瓶
226             foreach (var item in bowls.GetComponentsInChildren<Bowl>())
227             {
228                 AllBowls[item.Index] = item;//把加载的瓶子放进数组里
229             }
230         }
231         else
232         {
233             foreach (var item in AllBowls)
234             {
235                 if (item.GetScore)//没有倒下过显示他们
236                 {
237                     item.gameObject.SetActive(!item.GetScore);
238                 }
239             }
240         }
241     }
242 }

GameManage

9、游戏逻辑实现(Strike全中和Spare补中计分实现)



好了到最麻烦的地方了,分数逻辑设置重新理清一下得分思路,在前面我们写了一个SetSocre()方法设置分数,但是没法处理补分和全中,而且也没有办法处理第十轮,所以我们稍加修改,完善并添加特殊加分方法AddScore(),在ScoreItem类中,每次设置分数都让他重新设置特殊分数:

而且之前分数我们直接用int[]的数组存储,我们改进一下在,ScoreItem中添加一个特殊类Score:

 1 /// <summary>
 2 /// 分数信息类
 3 /// </summary>
 4 public class Score
 5 {
 6     /// <summary>
 7     /// Score构造参数
 8     /// </summary>
 9     /// <param name="_value"></param>
10     public Score(int _value)
11     {
12         value = _value;
13     }
14     /// <summary>
15     /// 分数值
16     /// </summary>
17     private int value;
18     /// <summary>
19     /// 获取分数值
20     /// </summary>
21     public int Value
22     {
23         get
24         {
25             return value;
26         }
27     }
28 }

当有分数时实例化一个Score,构造传值分数赋值,而且这个分数不可修改,封装起来只能获取,保证了外界不能修改你的分数。

理清思路逻辑,接下来进行编码:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 using UnityEngine.UI;
  5
  6 /// <summary>
  7 /// 分数信息类
  8 /// </summary>
  9 public class Score
 10 {
 11     /// <summary>
 12     /// Score构造参数
 13     /// </summary>
 14     /// <param name="_value"></param>
 15     public Score(int _value)
 16     {
 17         value = _value;
 18     }
 19     /// <summary>
 20     /// 分数值
 21     /// </summary>
 22     private int value;
 23     /// <summary>
 24     /// 获取分数值
 25     /// </summary>
 26     public int Value
 27     {
 28         get
 29         {
 30             return value;
 31         }
 32     }
 33 }
 34 public class ScoreItem : MonoBehaviour
 35 {
 36     /// <summary>
 37     /// 索引
 38     /// </summary>
 39     public int index;
 40     /// <summary>
 41     /// 所有分数文字
 42     /// </summary>
 43     public Text[] SocreTexts;
 44     /// <summary>
 45     /// 每轮分数
 46     /// </summary>
 47     public Score[] Scores;
 48     /// <summary>
 49     /// 本轮分数临时总和
 50     /// </summary>
 51     public int _tempScoreItem;
 52     /// <summary>
 53     /// 本轮分数总和
 54     /// </summary>
 55     public int _Score;
 56     /// <summary>
 57     /// 本轮全中?
 58     /// </summary>
 59     public bool isStrike = false;
 60     /// <summary>
 61     /// 本轮补中?
 62     /// </summary>
 63     public bool isSpare = false;
 64     /// <summary>
 65     /// 初始化
 66     /// </summary>
 67     public void Inits()
 68     {
 69         Scores = new Score[SocreTexts.Length - 1];//每轮分数初始化
 70     }
 71     /// <summary>
 72     /// 每轮分数设置索引为_index的分数文本的值为_score
 73     /// </summary>
 74     /// <param name="_index">索引</param>
 75     /// <param name="_score">分数</param>
 76     public void SetScoreText(int _index, int _score)
 77     {
 78         Scores[_index] = new Score(_score);
 79         Debug.LogWarning("第"+index+"轮:"+_index+"球:分数"+_score);
 80         _tempScoreItem = 0;
 81         foreach (var item in Scores)
 82         {
 83             if (item != null)
 84             {
 85                 _tempScoreItem += item.Value;//累加本轮分数总和
 86             }
 87         }
 88     }
 89     /// <summary>
 90     /// 显示所有分数
 91     /// </summary>
 92     public void ShowScore()
 93     {
 94         //设置每轮
 95         for (int i = 0; i < SocreTexts.Length - 1; i++)
 96         {
 97             if (Scores[i] != null)
 98             {
 99                 SocreTexts[i].text = Scores[i].Value.ToString();
100             }
101             else
102             {
103                 SocreTexts[i].text = "";
104             }
105         }
106         //AddScore();
107     }
108     /// <summary>
109     /// 更新添加分数
110     /// </summary>
111     public void AddScore()
112     {
113         _Score = _tempScoreItem;//分数
114         int _tempTime = 0;//临时次数计数
115         if (isStrike)//全中
116         {
117             if (index < 9)//当前是前九个分数块
118             {
119                 foreach (var item in GameManage.Instacne.SocreItems[index + 1].Scores)//遍历下一轮所有分数
120                 {
121                     if (item != null)//不为空加分
122                     {
123                         _Score += item.Value;//分数
124                         _tempTime++;
125                         if (_tempTime == 2)
126                         {
127                             break;
128                         }
129                     }
130                     else
131                     {
132                         if (index <8)//前八个
133                         {
134                             if (GameManage.Instacne.SocreItems[index + 2].Scores[0] != null)//下一个球也是补中的情况
135                             {
136                                 _Score += GameManage.Instacne.SocreItems[index + 2].Scores[0].Value;
137                                 _tempTime++;
138                                 break;
139                             }
140                         }
141                     }
142                 }
143             }
144         }
145         if (isSpare)
146         {
147             if (index <9)//前八个
148             {
149                 if (GameManage.Instacne.SocreItems[index + 1].Scores[0] != null)
150                 {
151                     _Score += GameManage.Instacne.SocreItems[index + 1].Scores[0].Value;
152                     _tempTime++;//一次
153                 }
154             }
155             else if(index ==9)//第九个
156             {
157             }
158         }
159         SocreTexts[SocreTexts.Length - 1].text = _Score.ToString();//总分设置
160     }
161 }

ScoreItem.cs

对了再加一个IsOver,游戏结束设置为True,修改之后的GameManage:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 using UnityEngine.UI;
  5
  6 public class GameManage : MonoBehaviour
  7 {
  8     #region 单例
  9
 10     private static GameManage _Instacne;
 11     /// <summary>
 12     /// 游戏管理单例
 13     /// </summary>
 14     public static GameManage Instacne
 15     {
 16         get
 17         {
 18             return _Instacne;
 19         }
 20
 21     }
 22     private void Awake()
 23     {
 24         _Instacne = this;
 25     }
 26     #endregion
 27
 28
 29     #region 场景信息
 30
 31     /// <summary>
 32     /// 墙壁
 33     /// </summary>
 34     public Transform roomWall;
 35     /// <summary>
 36     /// 推力范围
 37     /// </summary>
 38     public Transform pushArea;
 39     /// <summary>
 40     /// 保龄球瓶堆的父物体位置
 41     /// </summary>
 42     public Transform bowlsParentTrans;
 43
 44     #endregion
 45
 46     #region 游戏对象
 47
 48     /// <summary>
 49     /// 保龄球瓶预制体
 50     /// </summary>
 51     public GameObject bowlsPrefabs;
 52     /// <summary>
 53     /// 保龄球瓶堆
 54     /// </summary>
 55     public GameObject bowls;
 56     /// <summary>
 57     /// 保龄球预制体
 58     /// </summary>
 59     public GameObject ballPrefab;
 60     /// <summary>
 61     /// 保龄球对象
 62     /// </summary>
 63     public GameObject ball;
 64     /// <summary>
 65     /// 保龄球上的控制器
 66     /// </summary>
 67     public BallController _BallController;
 68     /// <summary>
 69     /// 管理所有瓶子的数组
 70     /// </summary>
 71     public Bowl[] AllBowls;
 72
 73     #endregion
 74
 75     #region 时间管理
 76     /// <summary>
 77     /// 出界等待时间
 78     /// </summary>
 79     [SerializeField]
 80     private float outAreaWaitTime = 5f;
 81
 82     private float _temp;
 83     /// <summary>
 84     /// 每球声明时间
 85     /// </summary>
 86     [SerializeField]
 87     private float BallLifeTime = 30f;
 88     /// <summary>
 89     /// 滚动时间
 90     /// </summary>
 91     private float _tempLife;
 92
 93     #endregion
 94
 95     #region 分数管理
 96
 97     /// <summary>
 98     /// 每局分数块游戏对象
 99     /// </summary>
100     public ScoreItem[] SocreItems;
101     /// <summary>
102     /// 每球的临时得分
103     /// </summary>
104     public int _tempScore;
105     /// <summary>
106     /// 所有总分
107     /// </summary>
108     public int AllScore;
109     /// <summary>
110     /// 所有分数
111     /// </summary>
112     public Text AllScoreText;
113     #endregion
114     /// <summary>
115     /// 轮数
116     /// </summary>
117     public int indexItem;
118     /// <summary>
119     /// 每轮第N球
120     /// </summary>
121     public int _tempNumBall;
122     /// <summary>
123     /// 游戏结束
124     /// </summary>
125     public bool IsOver;
126
127     private void Start()
128     {
129         indexItem = 0;
130         _tempNumBall = 1;//第1球
131         SetBall();
132         SetBowls();
133         //分数初始化
134         foreach (var item in SocreItems)
135         {
136             item.Inits();//初始化
137             item.ShowScore();
138         }
139     }
140     private void Update()
141     {
142         if (ball != null)//游戏没有结束
143         {
144
145             if (_BallController.pushed)//球已经被推出去
146             {
147                 _tempLife += Time.deltaTime;
148                 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届
149                 {
150                     Debug.Log("此球分数:" + _tempScore);
151                     _tempLife = 0;
152                     DestroyImmediate(ball);
153                     SetScore(_tempScore);
154                     _tempScore = 0;//清零
155                     SetBall();
156                     SetBowls();
157                 }
158                 if (_BallController.isOutArea)//球出界
159                 {
160                     _temp += Time.deltaTime;
161                     if (_temp > outAreaWaitTime)//超过出界时间
162                     {
163                         Debug.Log("球出界!此球得分:" + _tempScore);
164                         _tempLife = 0;
165                         _temp = 0;//重新计时
166                         DestroyImmediate(ball);
167                         SetScore(_tempScore);
168                         _tempScore = 0;//清零
169                         SetBall();
170                         SetBowls();
171                     }
172                 }
173             }
174
175
176         }
177     }
178     /// <summary>
179     /// 生成新球
180     /// </summary>
181     public void SetBall()
182     {
183         if (ball == null)
184         {
185             ball = Instantiate(ballPrefab) as GameObject;
186             _BallController = ball.GetComponent<BallController>();
187             _BallController.Inits();
188         }
189     }
190     /// <summary>
191     /// 传递分数
192     /// </summary>
193     /// <param name="_score">分数值</param>
194     public void SetScore(int _score)
195     {
196         //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
197         if (indexItem < 9)//---------------------------------------------------前九轮
198         {
199             if (_tempNumBall == 1)//第一球
200             {
201                 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球
202                 SocreItems[indexItem].ShowScore();
203                 if (_score == 10)//分数为10
204                 {
205                     Debug.LogWarning("Strike全中!");
206                     SocreItems[indexItem].isStrike = true;//设置第一球
207                     indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
208                     DestroyImmediate(bowls);//清空瓶子
209                     _tempNumBall = 1;//开始新的一轮
210                 }
211                 else//不为10
212                 {
213                     _tempNumBall = 2;//开始下一个球
214                 }
215             }
216             else if (_tempNumBall == 2)//第二球
217             {
218                 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球
219                 SocreItems[indexItem].ShowScore();
220                 if (_score + SocreItems[indexItem].Scores[0].Value == 10)//yi
221                 {
222                     Debug.LogWarning("Spare补中!");
223                     SocreItems[indexItem].isSpare = true;//设置第一球
224                 }
225                 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
226                 DestroyImmediate(bowls);//清空瓶子
227                 _tempNumBall = 1;//开始新的一轮
228
229             }
230         }
231         else if (indexItem == 9)//--------------------------------------------第十轮
232         {
233             if (_tempNumBall == 1)//第一球
234             {
235                 if (_score == 10)
236                 {
237                     Debug.LogWarning("Strike全中!");
238                     SocreItems[indexItem].isStrike = true;//设置第一球
239                     DestroyImmediate(bowls);//清空瓶子
240                 }
241                 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球
242                 SocreItems[indexItem].ShowScore();
243                 _tempNumBall = 2;//开始新的一轮
244             }
245             else if (_tempNumBall == 2)//第二球
246             {
247                 if (!SocreItems[indexItem].isStrike)//不是全中
248                 {
249                     if (SocreItems[indexItem].Scores[0].Value + _score == 10)//达成补中条件
250                     {
251                         Debug.LogWarning("Spare补中!");
252                         SocreItems[indexItem].isSpare = true;//全中
253                         DestroyImmediate(bowls);//清空瓶子
254                         SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数
255                         SocreItems[indexItem].ShowScore();
256                         _tempNumBall = 3;//开始新的一轮,第3球
257                     }
258                     else
259                     {
260                         SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数
261                         SocreItems[indexItem].ShowScore();
262                         IsOver = true;
263                     }
264                 }
265                 else//全中下一球
266                 {
267                     DestroyImmediate(bowls);//清空瓶子
268                     SocreItems[9].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数
269                     SocreItems[9].ShowScore();
270                     _tempNumBall = 3;//开始新的一轮,第3球
271                 }
272             }
273             else if (_tempNumBall == 3)
274             {
275                 DestroyImmediate(bowls);//清空瓶子
276                 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球分数
277                 SocreItems[indexItem].ShowScore();
278
279                 IsOver = true;
280             }
281         }
282         AllScore = 0;
283         foreach (var item in SocreItems)//加分
284         {
285             item.AddScore();
286             AllScore += item._Score;
287         }
288         AllScoreText.text = AllScore.ToString();
289     }
290
291     /// <summary>
292     /// 摆放瓶子
293     /// </summary>
294     public void SetBowls()
295     {
296         if (bowls == null)//没有瓶子
297         {
298             bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject;
299             AllBowls = new Bowl[10];//10个瓶
300             foreach (var item in bowls.GetComponentsInChildren<Bowl>())
301             {
302                 AllBowls[item.Index] = item;//把加载的瓶子放进数组里
303             }
304         }
305         else
306         {
307             foreach (var item in AllBowls)
308             {
309                 if (item.GetScore)//没有倒下过显示他们
310                 {
311                     item.gameObject.SetActive(!item.GetScore);
312                 }
313             }
314         }
315     }
316 }

GameManage.cs

此外修改一下BallController.cs:

这样游戏结束就捡不起来球了。

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

10、开始界面的搭建



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

接下来然后搭建Start的UI界面,我们发现这个按钮素材需要九宫切图,于是切一下:

经过一系列的UI布局:

关于场景的替换需要注意一下,不要用Application.loadedLevel(),导入UnityEngine.SceneManagement包,用SceneManager.LoadScene():

我们再编写一个游戏管理的脚本StartManage.cs,让它负责场景的切换和球的表面材质替换。

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4 using UnityEngine.SceneManagement;
 5 using UnityEngine.UI;
 6
 7 public class StartManage : MonoBehaviour
 8 {
 9     /// <summary>
10     /// 开始按键
11     /// </summary>
12     public Button PlayBtn;
13     /// <summary>
14     /// 上一个球
15     /// </summary>
16     public Button PrevBtn;
17     /// <summary>
18     /// 下一个球
19     /// </summary>
20     public Button NextBtn;
21     /// <summary>
22     /// 球的材质列表
23     /// </summary>
24     [SerializeField]
25     private Texture[] ballTextures;
26     /// <summary>
27     /// 当前材质索引
28     /// </summary>
29     [SerializeField]
30     private int _tempIndex;
31     /// <summary>
32     /// 球的材质贴图
33     /// </summary>
34     public Material ballMaterial;
35
36     void Start()
37     {
38         _tempIndex = 0;
39         PlayBtn.onClick.AddListener(playBtnClick);
40         PrevBtn.onClick.AddListener(UpdateRevTexture);
41         NextBtn.onClick.AddListener(UpdateNextTexture);
42     }
43     public void playBtnClick()
44     {
45         SceneManager.LoadScene("Main");
46     }
47     /// <summary>
48     /// 更换上一个材质
49     /// </summary>
50     public void UpdateRevTexture() {
51         _tempIndex--;
52         if (_tempIndex==-1)
53         {
54             _tempIndex = ballTextures.Length - 1;
55         }
56         ballMaterial.SetTexture("_MainTex", ballTextures[_tempIndex]);
57     }
58     /// <summary>
59     /// 更换下一个材质
60     /// </summary>
61     public void UpdateNextTexture()
62     {
63         _tempIndex++;
64         if (_tempIndex == ballTextures.Length)
65         {
66             _tempIndex = 0;
67         }
68         ballMaterial.SetTexture("_MainTex", ballTextures[_tempIndex]);
69     }
70 }

StartManage.cs

绑定一下:

这样还没有完。我们还需要一个步骤,从游戏界面返回初始界面。打开Main场景,制作一个返回按键。

经过测试后,完成之后两个场景完美切换。

11、发球后的相机跟随



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

在BallController添加以下代码:

相机跟随实现。

修改后的BallController代码:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 /// <summary>
  5 /// 保龄球的控制
  6 /// </summary>
  7 public class BallController : MonoBehaviour
  8 {
  9
 10     #region 射线相关
 11
 12     /// <summary>
 13     /// 射线碰撞信息
 14     /// </summary>
 15     private RaycastHit hit;
 16     /// <summary>
 17     /// 图层遮罩
 18     /// </summary>
 19     private LayerMask mask;
 20     /// <summary>
 21     /// 球的位置
 22     /// </summary>
 23     public Transform ballTrans;
 24
 25     #endregion
 26
 27
 28     /// <summary>
 29     /// 球的半径
 30     /// </summary>
 31     [SerializeField]
 32     private float ballRadius;
 33     /// <summary>
 34     /// 扔球点
 35     /// </summary>
 36     private Vector3 ballPushOrgin;
 37     /// <summary>
 38     /// 球左右方向差量
 39     /// </summary>
 40     private Vector3 ballPushDir;
 41
 42     /// <summary>
 43     /// 是否推出去
 44     /// </summary>
 45     public bool pushed = false;
 46
 47     /// <summary>
 48     /// 是否撞墙
 49     /// </summary>
 50     public bool ishitWall;
 51     /// <summary>
 52     /// 出界了
 53     /// </summary>
 54     public bool isOutArea;
 55     /// <summary>
 56     /// 与相机差距位置
 57     /// </summary>
 58     private Vector3 _tempPos;
 59     /// <summary>
 60     /// 保龄球初始化
 61     /// </summary>
 62     public void Inits()
 63     {
 64         //获取球的半径
 65         ballRadius = ballTrans.GetComponent<SphereCollider>().radius;
 66         GetComponent<Rigidbody>().velocity = Vector3.zero;
 67         GetComponent<Rigidbody>().isKinematic = true;//不受动力
 68         isOutArea = false;//没有越界
 69         pushed = false;//没有推出去
 70         ishitWall = false;//没有撞墙
 71         GameManage.Instacne.CameraTrans.localPosition = Vector3.zero;
 72         _tempPos = new Vector3(0, -0.5f, -2);
 73     }
 74
 75     private void Update()
 76     {
 77
 78
 79         //发射一条射线
 80         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
 81         //如果还没有推出去这个球
 82         if (!pushed && !GameManage.Instacne.IsOver)
 83         {
 84             //推球逻辑
 85             if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor")))
 86             {
 87                 //推出去
 88                 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
 89                 if (hit.transform == GameManage.Instacne.pushArea)
 90                 {
 91                     _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置
 92                     ballTrans.position = _ballPushTrans;
 93                     ballPushOrgin = hit.point; ;//记录球的起点
 94                                                 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z);
 95                 }
 96                 else
 97                 {
 98                     if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有)
 99                     {
100                         Vector3 _ballPushDir = hit.point;//记录一下球的终点
101                                                          //Debug.Log(ballPushOrgin);
102                         ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0);
103                         pushed = true;
104                         ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
105                         GetComponent<Rigidbody>().isKinematic = false;
106                         ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 35, ForceMode.Impulse);
107                     }
108                 }
109             }
110         }
111         else if (pushed)//推出去了
112         {
113             if (GameManage.Instacne.CameraTrans.position.z > -7f)
114             {
115                 GameManage.Instacne.CameraTrans.position = transform.position - _tempPos;
116             }
117         }
118     }
119     /// <summary>
120     /// 碰撞器检测
121     /// </summary>
122     /// <param name="collision"></param>
123     private void OnCollisionEnter(Collision collision)
124     {
125         if (collision.gameObject.layer != 8 && pushed)
126         {
127             //Debug.Log("球出界了!");
128             isOutArea = true;
129         }
130     }
131 }

BallController.cs

12、音效的添加控制



Start场景中,初始BGM添加:

之后是球控制音效:

修改BallController:

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 /// <summary>
  5 /// 保龄球的控制
  6 /// </summary>
  7 public class BallController : MonoBehaviour
  8 {
  9
 10     #region 射线相关
 11
 12     /// <summary>
 13     /// 射线碰撞信息
 14     /// </summary>
 15     private RaycastHit hit;
 16     /// <summary>
 17     /// 图层遮罩
 18     /// </summary>
 19     private LayerMask mask;
 20     /// <summary>
 21     /// 球的位置
 22     /// </summary>
 23     public Transform ballTrans;
 24
 25     #endregion
 26
 27     #region 音效管理
 28     /// <summary>
 29     /// 鼓掌音源
 30     /// </summary>
 31     public AudioClip applausSource;
 32     /// <summary>
 33     /// 正在滚动的声音
 34     /// </summary>
 35     public AudioClip pushSource;
 36     /// <summary>
 37     /// BGM
 38     /// </summary>
 39     public AudioClip BGM;
 40     /// <summary>
 41     /// 音源
 42     /// </summary>
 43     public AudioSource AS;
 44     /// <summary>
 45     /// 播放一次
 46     /// </summary>
 47     private bool Once;
 48     #endregion
 49
 50     /// <summary>
 51     /// 球的半径
 52     /// </summary>
 53     [SerializeField]
 54     private float ballRadius;
 55     /// <summary>
 56     /// 扔球点
 57     /// </summary>
 58     private Vector3 ballPushOrgin;
 59     /// <summary>
 60     /// 球左右方向差量
 61     /// </summary>
 62     private Vector3 ballPushDir;
 63
 64     /// <summary>
 65     /// 是否推出去
 66     /// </summary>
 67     public bool pushed = false;
 68
 69     /// <summary>
 70     /// 是否撞墙
 71     /// </summary>
 72     public bool ishitWall;
 73     /// <summary>
 74     /// 出界了
 75     /// </summary>
 76     public bool isOutArea;
 77     /// <summary>
 78     /// 与相机差距位置
 79     /// </summary>
 80     private Vector3 _tempPos;
 81     /// <summary>
 82     /// 保龄球初始化
 83     /// </summary>
 84     public void Inits()
 85     {
 86         //获取球的半径
 87         ballRadius = ballTrans.GetComponent<SphereCollider>().radius;
 88         GetComponent<Rigidbody>().velocity = Vector3.zero;
 89         GetComponent<Rigidbody>().isKinematic = true;//不受动力
 90         isOutArea = false;//没有越界
 91         pushed = false;//没有推出去
 92         ishitWall = false;//没有撞墙
 93         GameManage.Instacne.CameraTrans.localPosition = Vector3.zero;
 94         _tempPos = new Vector3(0, -0.5f, -2);
 95         if (!AS.isPlaying)
 96         {
 97             if (AS.clip == null)
 98             {
 99                 AS.clip = BGM;
100                 AS.loop = true;
101                 AS.Play();
102             }
103         }
104     }
105
106     private void Update()
107     {
108         //发射一条射线
109         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
110         //如果还没有推出去这个球
111         if (!pushed && !GameManage.Instacne.IsOver)
112         {
113             //推球逻辑
114             if (Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("AreaFloor")))
115             {
116                 //推出去
117                 Vector3 _ballPushTrans = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
118                 if (hit.transform == GameManage.Instacne.pushArea)
119                 {
120                     _ballPushTrans = new Vector3(hit.point.x, hit.point.y + 0.3f, hit.point.z);//球的位置
121                     ballTrans.position = _ballPushTrans;
122                     ballPushOrgin = hit.point; ;//记录球的起点
123                                                 //Debug.Log(hit.point.x + "," + hit.point.y + "," + hit.point.z);
124                 }
125                 else
126                 {
127                     if (!pushed && hit.point.z < 8f && ballPushOrgin != Vector3.zero)//捡起保龄球并且它的发射距离超过世界坐标Z=8f(没有)
128                     {
129                         Vector3 _ballPushDir = hit.point;//记录一下球的终点
130                                                          //Debug.Log(ballPushOrgin);
131                         ballPushDir = new Vector3(_ballPushDir.x - ballPushOrgin.x, 0, 0);
132                         pushed = true;
133                         ballTrans.position = new Vector3(hit.point.x, 0.558f + ballRadius * 0.01f, 8.026f);
134                         GetComponent<Rigidbody>().isKinematic = false;
135                         ballTrans.GetComponent<Rigidbody>().AddForce((transform.forward + ballPushDir) * 35, ForceMode.Impulse);
136                     }
137                 }
138             }
139         }
140         else if (pushed)//推出去了
141         {
142             if (GameManage.Instacne.CameraTrans.position.z > -7f)
143             {
144                 GameManage.Instacne.CameraTrans.position = transform.position - _tempPos;
145             }
146         }
147     }
148     /// <summary>
149     /// 碰撞器检测
150     /// </summary>
151     /// <param name="collision"></param>
152     private void OnCollisionEnter(Collision collision)
153     {
154         if (!Once)
155         {
156             //滚动声音播放
157             AS.Stop();
158             AS.loop = false;
159             AS.clip = pushSource;
160             AS.Play();
161             Once = true;
162         }
163         if (collision.gameObject.layer != 8 && pushed)
164         {
165             //Debug.Log("球出界了!");
166             isOutArea = true;
167         }
168     }
169 }

BallController.cs

最后是Main场景中的掌声:

对GameManage修改:

  1 //------------------------------------------------------------------------------
  2 // Copyright 2017 21世纪少年. All rights reserved.
  3 //
  4 // Unity5.6.3f1 Editor make it
  5 // BowlingHall_Project
  6 //
  7 // CreatTime: #10/7/2017#
  8 // Author: Unity Guo
  9 //------------------------------------------------------------------------------
 10 using System.Collections;
 11 using System.Collections.Generic;
 12 using UnityEngine;
 13 using UnityEngine.SceneManagement;
 14 using UnityEngine.UI;
 15
 16 public class GameManage : MonoBehaviour
 17 {
 18     #region 单例
 19
 20     private static GameManage _Instacne;
 21     /// <summary>
 22     /// 游戏管理单例
 23     /// </summary>
 24     public static GameManage Instacne
 25     {
 26         get
 27         {
 28             return _Instacne;
 29         }
 30
 31     }
 32     private void Awake()
 33     {
 34         _Instacne = this;
 35     }
 36     #endregion
 37
 38
 39     #region 场景信息
 40
 41     /// <summary>
 42     /// 墙壁
 43     /// </summary>
 44     public Transform roomWall;
 45     /// <summary>
 46     /// 推力范围
 47     /// </summary>
 48     public Transform pushArea;
 49     /// <summary>
 50     /// 保龄球瓶堆的父物体位置
 51     /// </summary>
 52     public Transform bowlsParentTrans;
 53
 54     #endregion
 55
 56     #region 游戏对象
 57
 58     /// <summary>
 59     /// 保龄球瓶预制体
 60     /// </summary>
 61     public GameObject bowlsPrefabs;
 62     /// <summary>
 63     /// 保龄球瓶堆
 64     /// </summary>
 65     public GameObject bowls;
 66     /// <summary>
 67     /// 保龄球预制体
 68     /// </summary>
 69     public GameObject ballPrefab;
 70     /// <summary>
 71     /// 保龄球对象
 72     /// </summary>
 73     public GameObject ball;
 74     /// <summary>
 75     /// 保龄球上的控制器
 76     /// </summary>
 77     public BallController _BallController;
 78     /// <summary>
 79     /// 管理所有瓶子的数组
 80     /// </summary>
 81     public Bowl[] AllBowls;
 82
 83     #endregion
 84
 85     #region 时间管理
 86     /// <summary>
 87     /// 出界等待时间
 88     /// </summary>
 89     [SerializeField]
 90     private float outAreaWaitTime = 5f;
 91
 92     private float _temp;
 93     /// <summary>
 94     /// 每球声明时间
 95     /// </summary>
 96     [SerializeField]
 97     private float BallLifeTime = 30f;
 98     /// <summary>
 99     /// 滚动时间
100     /// </summary>
101     private float _tempLife;
102
103     #endregion
104
105     #region 分数管理
106
107     /// <summary>
108     /// 每局分数块游戏对象
109     /// </summary>
110     public ScoreItem[] SocreItems;
111     /// <summary>
112     /// 每球的临时得分
113     /// </summary>
114     public int _tempScore;
115     /// <summary>
116     /// 所有总分
117     /// </summary>
118     public int AllScore;
119     /// <summary>
120     /// 所有分数
121     /// </summary>
122     public Text AllScoreText;
123     #endregion
124
125
126     /// <summary>
127     /// 轮数
128     /// </summary>
129     public int indexItem;
130     /// <summary>
131     /// 每轮第N球
132     /// </summary>
133     public int _tempNumBall;
134     /// <summary>
135     /// 相机位置
136     /// </summary>
137     public Transform CameraTrans;
138     /// <summary>
139     /// 返回餐单按键
140     /// </summary>
141     public Button RenturnBtn;
142     /// <summary>
143     /// 游戏结束
144     /// </summary>
145     public bool IsOver;
146
147     private void Start()
148     {
149         RenturnBtn.onClick.AddListener(ReturnMenu);
150         indexItem = 0;
151         _tempNumBall = 1;//第1球
152         SetBall();
153         SetBowls();
154         //分数初始化
155         foreach (var item in SocreItems)
156         {
157             item.Inits();//初始化
158             item.ShowScore();
159         }
160     }
161     private void Update()
162     {
163         if (ball != null)//游戏没有结束
164         {
165
166             if (_BallController.pushed)//球已经被推出去
167             {
168                 CameraTrans.GetComponent<AudioSource>().Stop();
169                 _tempLife += Time.deltaTime;
170                 if (_tempLife > BallLifeTime && !_BallController.isOutArea)//一直没出届
171                 {
172                     Debug.Log("此球分数:" + _tempScore);
173                     _tempLife = 0;
174                     DestroyImmediate(ball);
175                     SetScore(_tempScore);
176                     _tempScore = 0;//清零
177                     SetBall();
178                     SetBowls();
179                 }
180                 if (_BallController.isOutArea)//球出界
181                 {
182                     _temp += Time.deltaTime;
183                     if (_temp > outAreaWaitTime)//超过出界时间
184                     {
185                         Debug.Log("球出界!此球得分:" + _tempScore);
186                         _tempLife = 0;
187                         _temp = 0;//重新计时
188                         DestroyImmediate(ball);
189                         SetScore(_tempScore);
190                         _tempScore = 0;//清零
191                         SetBall();
192                         SetBowls();
193                     }
194                 }
195             }
196
197
198         }
199     }
200     /// <summary>
201     /// 生成新球
202     /// </summary>
203     public void SetBall()
204     {
205         if (ball == null)
206         {
207             ball = Instantiate(ballPrefab) as GameObject;
208             _BallController = ball.GetComponent<BallController>();
209             _BallController.Inits();
210         }
211     }
212     /// <summary>
213     /// 传递分数
214     /// </summary>
215     /// <param name="_score">分数值</param>
216     public void SetScore(int _score)
217     {
218         //indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
219         if (indexItem < 9)//---------------------------------------------------前九轮
220         {
221             if (_tempNumBall == 1)//第一球
222             {
223                 SocreItems[indexItem].SetScoreText(0, _score);//设置第一球
224                 SocreItems[indexItem].ShowScore();
225                 if (_score == 10)//分数为10
226                 {
227                     Debug.LogWarning("Strike全中!");
228                     CameraTrans.GetComponent<AudioSource>().Play();
229                     SocreItems[indexItem].isStrike = true;//设置第一球
230                     indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
231                     DestroyImmediate(bowls);//清空瓶子
232                     _tempNumBall = 1;//开始新的一轮
233                 }
234                 else//不为10
235                 {
236                     _tempNumBall = 2;//开始下一个球
237                 }
238             }
239             else if (_tempNumBall == 2)//第二球
240             {
241                 SocreItems[indexItem].SetScoreText(1, _score);//设置第一球
242                 SocreItems[indexItem].ShowScore();
243                 if (_score + SocreItems[indexItem].Scores[0].Value == 10)//yi
244                 {
245                     Debug.LogWarning("Spare补中!");
246                     CameraTrans.GetComponent<AudioSource>().Play();
247                     SocreItems[indexItem].isSpare = true;//设置第一球
248                 }
249                 indexItem++;//轮数自加,(indexItem-1)为每局分数块游戏对象SocreItems索引
250                 DestroyImmediate(bowls);//清空瓶子
251                 _tempNumBall = 1;//开始新的一轮
252
253             }
254         }
255         else if (indexItem == 9)//--------------------------------------------第十轮
256         {
257             if (_tempNumBall == 1)//第一球
258             {
259                 if (_score == 10)
260                 {
261                     Debug.LogWarning("Strike全中!");
262                     CameraTrans.GetComponent<AudioSource>().Play();
263                     SocreItems[indexItem].isStrike = true;//设置第一球
264                     DestroyImmediate(bowls);//清空瓶子
265                 }
266                 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球
267                 SocreItems[indexItem].ShowScore();
268                 _tempNumBall = 2;//开始新的一轮
269             }
270             else if (_tempNumBall == 2)//第二球
271             {
272                 if (!SocreItems[indexItem].isStrike)//不是全中
273                 {
274                     if (SocreItems[indexItem].Scores[0].Value + _score == 10)//达成补中条件
275                     {
276                         Debug.LogWarning("Spare补中!");
277                         CameraTrans.GetComponent<AudioSource>().Play();
278                         SocreItems[indexItem].isSpare = true;//全中
279                         DestroyImmediate(bowls);//清空瓶子
280                         SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数
281                         SocreItems[indexItem].ShowScore();
282                         _tempNumBall = 3;//开始新的一轮,第3球
283                     }
284                     else
285                     {
286                         SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数
287                         SocreItems[indexItem].ShowScore();
288                         IsOver = true;
289                     }
290                 }
291                 else//全中下一球
292                 {
293                     DestroyImmediate(bowls);//清空瓶子
294                     SocreItems[9].SetScoreText(_tempNumBall - 1, _score);//设置第二球分数
295                     SocreItems[9].ShowScore();
296                     _tempNumBall = 3;//开始新的一轮,第3球
297                 }
298             }
299             else if (_tempNumBall == 3)
300             {
301                 DestroyImmediate(bowls);//清空瓶子
302                 SocreItems[indexItem].SetScoreText(_tempNumBall - 1, _score);//设置第一球分数
303                 SocreItems[indexItem].ShowScore();
304
305                 IsOver = true;
306             }
307         }
308         AllScore = 0;
309         foreach (var item in SocreItems)//加分
310         {
311             item.AddScore();
312             AllScore += item._Score;
313         }
314         AllScoreText.text = AllScore.ToString();
315     }
316
317     /// <summary>
318     /// 摆放瓶子
319     /// </summary>
320     public void SetBowls()
321     {
322         if (bowls == null)//没有瓶子
323         {
324             bowls = Instantiate(bowlsPrefabs, bowlsParentTrans) as GameObject;
325             AllBowls = new Bowl[10];//10个瓶
326             foreach (var item in bowls.GetComponentsInChildren<Bowl>())
327             {
328                 AllBowls[item.Index] = item;//把加载的瓶子放进数组里
329             }
330         }
331         else
332         {
333             foreach (var item in AllBowls)
334             {
335                 if (item.GetScore)//没有倒下过显示他们
336                 {
337                     item.gameObject.SetActive(!item.GetScore);
338                 }
339             }
340         }
341     }
342     /// <summary>
343     /// 返回菜单
344     /// </summary>
345     public void ReturnMenu() {
346         SceneManager.LoadScene("Start");
347     }
348 }

GameManage.cs

最后测试。。。。

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

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

原文地址:http://www.cnblogs.com/craft0625/p/7506289.html

时间: 2024-10-26 00:16:45

保龄球游戏Demo的相关文章

基于Netty和Raphael的塔防online游戏Demo(一)

这是一个出于兴趣而在业余时间写的项目,初版的目的在于了解游戏服务端的一些设计思路,所以基本是想到什么写下什么,并没有做过于细节的设计,和过早的优化. 在这个版本里面,主要要完成以下功能: 定制简单的通信协议 处理简单的游戏逻辑 可运行的Server和Client 一. 服务端设计 整个服务端暂且分成以下几个部分: 基础通信模块 逻辑处理模块 GM模块 服务器状态监控模块 对服务端的几个高级抽象: 1)Context Context代表了整个服务端的生命周期, 负责启动和停止所有的游戏 2)Gam

unity + win8.1 apps 小游戏demo

unity3d用的人挺多...本来想写个3d游戏试试..额..貌似挺麻烦.. .....先用unity写个简单的2d游戏吧.. (adsw回车  或者  触摸屏虚拟摇杆) 开发环境 unity4.5.1f3      vs2013 express       win8.1 实现方法如下 1.创建自己的坦克Sprite 2.创建敌方坦克Prefab 3.创建子弹Prefab 4.添加虚拟摇杆 5.碰撞检测 具体实现 1.控制自己坦克的代码 if (Input.GetKey (KeyCode.W))

【转】265行JavaScript代码的第一人称3D H5游戏Demo

译文:http://blog.jobbole.com/70956/ 原文:http://www.playfuljs.com/a-first-person-engine-in-265-lines/ 这是一篇关于利用 Canvas 实现3D 游戏场景绘制的文章,看完感觉很受启发,所以自己准备总结一下.我们先看下最终效果: 很酷是不是.原作者只使用了265行代码就实现了这么炫酷的效果.代码写的也很清晰明了,并且原文也对原理也进行了解释.本篇就直接将原作者的代码全部贴上来大家观赏观赏. 1 <!doct

Java Swing扫雷游戏demo分享

好多年前写过简略的扫雷游戏,模拟windows的. 后来由于多次搬迁环境,弄丢了,遗憾不已啊. 于是趁着这两年还在编程的道路上,趁热再次编写了一次,同时也扩展了功能,更接近windows的扫雷. 此次重写是用Javaswing实现的(eclipse开发),考虑到各位看客可能大部分是Android岗位,于是我着重注意了功能结构化的处理,使游戏核心算法与UI分离,使用回调交互,便于迁移到android环境. 本人对swing不是很熟练,一直以来用swing做项目的人很少,学习资料也少,所有的这些都是

265行JavaScript代码的第一人称3D H5游戏Demo【个人总结1】

本文目的是分解前面的代码.其实,它得逻辑很清楚,只是对于我这种只是用过 Canvas 画线的人来说,这个还是很复杂的.我研究这个背景天空也是搞了一天,下面就是只加载天空的代码. 在线效果点击:http://1.codemo2.sinaapp.com/3d_demo_265line/index.html   [可以用键盘“左右”键控制][手机浏览器触控有些异常] 原理大概就是: 1. 创建主循环 一个无限循环 2. 主循环内重复调用绘制方法 3. 绘制方法: 针对 Player 的位置和方向,绘制

游戏demo测试结果分析

游戏链接:http://home.ustc.edu.cn/~chrkeith/demo_game/ 第1题   您能打开画面吗      [单选题] 选项 小计 比例 能 31 100% 第2题   您能控制游戏人物移动吗(并说明您的手机型号)      [单选题]  选项 小计 比例 能 28 90.32% 不能 3 9.68% 第3题   您能在十分钟内走出该关卡吗      [单选题] 选项 小计 比例 能 24 77.42% 不能 7 22.58% 第4题   您觉得这个游戏好玩吗?并说

Html5最简单的游戏Demo——Canvas绘图的骰子

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>骰子游戏</title> <script type="text/javascript"> var crapsSize = 100;//骰子的width,height var crapsMargin = 50;//骰子的边距 var pointRadi

Android手机中的2048游戏Demo开发

Android中正火的2048游戏开发,赶紧开发一个自己的2048吧 1.游戏中的几个关键点 1)界面 2048游戏的操作界面就是一个4X4的方格.如下图所示: 游戏首先要绘制出该界面. @1 界面布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:

游戏Demo服务器开发总结

游戏背景:房间类多人联机对战游戏 服务器功能:非对战模块通信+战斗通信 开发环境:golang+protobuf+logrus+mongodb 项目地址:https://github.com/iwangyao/DemoServer 要点总结 一.服务端开启 1.使用go关键字每新连接一个客户端就开启一个goroutine 2.每个与客户端连接的协程里,循环读客户端消息,获取的消息使用一个消息处理中心处理 3.写一个简单的客户端进行连接测试 二.通信协议模型 1.协议分为三层:第一层消息类型,第二