• 保龄球游戏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

     最后测试。。。。

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

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

  • 相关阅读:
    Oracle OCP 11G 053(601-712)答案解析目录_20140304
    Oracle OCP 11G 053(201-400)答案解析目录_20140304
    Oracle OCP 11G 053(1-200)答案解析目录_20140304
    dojo实现表格数据无法展示
    dojo实现表格
    Matlab基本函数-menu函数
    Matlab基本函数-log10函数
    Matlab基本函数-log函数
    Matlab基本函数-length函数
    Matlab基本函数-imag函数
  • 原文地址:https://www.cnblogs.com/craft0625/p/7506289.html
Copyright © 2020-2023  润新知