Unity官方案例进阶--Roll a ball

经过上一次的学习,我又想改进一下这款 Roll a ball 游戏,首先这款游戏只有胜利没有失败,所以我想定义一个条件来控制它的输赢,因此我想到了如下的方案:

使我们的玩家控制 Player 在规定时间内达到一定的分数就胜利,反之则失败

任务目标

这里只是大概的目标内容,还有一些细节会在后面完成的时候体现出来。

  1. 完成 PickUp 的随机刷新出现,每隔3秒刷新一个
  2. 做一个倒计时器用于显示游戏剩余时间
  3. 控制分数和时间,若玩家在规定时间内达到分数则显示 You Win!字样,Player 禁止移动;若失败,则小球爆炸并显示 Game Over! 字样。

环境搭建

主体还是 Roll a ball 的内容,其中有一些小改动,我们在此完成一下。

删除场景中的所有 PickUp

因为我们要完成的是随机刷新 PickUp 物体,所以我们不需要在场景中添加 PickUp 物体,到时候会用到一个新的知识来完成我们的随机显示 PickUp 物体的操作。

添加倒计时文本

因为我们需要让玩家知道游戏的剩余时间,所以我们需要添加一个用来显示时间的文本,位置我选择在了中间顶部的位置,其他设置随意。

细节修改

因为最终我们显示的文本不再是只有胜利,所以将我们之前创建的 WinText 改名为 ResultText。

遗漏补充

在官方的案例中,PickUp 是有一个黄色的材质,而我在之前做的项目中忘记添加了,所以我现在添加了一个黄色材质球给我们的 PickUp。

诶呀,图片加载出了点问题!

以上就是我们的环境搭建相关的操作,下面就要开始我们的脚本编写,来实现游戏的运行了。

游戏运行

修改以及准备

我将 PlayerController 中除了将 Player 运动的脚本其余的都删除了。

脚本文件名:PlayerController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {
public float speed;

private float moveHorizontal;
private float moveVertical;
private Vector3 movement;
private void FixedUpdate()
{
    moveHorizontal = Input.GetAxis("Horizontal");
    moveVertical = Input.GetAxis("Vertical");
    movement = new Vector3(moveHorizontal, 0.0f, moveVertical);

    GetComponent<Rigidbody>().AddForce(movement * speed * Time.deltaTime);
}
}

我将剩下的代码贴在上方了,有不清楚的或者项目删除了的可以直接复制过去。

创建 GameController 脚本

还是在我们的 Player 物体上添加脚本取名为 GameController ,我定义它用来控制整个游戏的运行脚本,其实我们游戏的改变主要就是体现在此脚本的编写,所以当我们完成此脚本的编写也就意味着我们游戏的完成,废话不多说了下面我们就来完成它。

删除补回

因为我们将原来 PlayerController 脚本中除了控制小球运动的代码都删除了,所以需要先补充一下 Planyer 与 PickUp 的碰撞以及分数控制的相关代码,此时只是补回后面还会有更改。

脚本文件名:GameController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public Text countText;

    private int count;

    private void Awake()
    {
        count = 0;
    }

    private void Start()
    {
        SetCount();
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }
}

目标一:刷新 PickUp 物体

在此我们要完成的是让我们的 PickUp 物体在地面上的随机位置每3秒刷新一个

这其中我们会用到几个新的知识,有需要的话最好还是去网上找找资料了解一下或者翻阅官网的API。

  1. Clone 克隆物体
  2. Randam 随机类

脚本文件名:GameController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public Text countText;
    //用于添加 PickUp 物体,之后克隆要用
    public GameObject pickUp;

    private int count;
    //定义一个三维向量的值用于存储克隆物体的位置
    private Vector3 newPickUpPt;
    //定义一个需要等待克隆物体的时间,根据目标应该赋值为3
    private float fireRate;
    //定义一个下一次克隆物体的时间
    private float nextFire;

    private void Awake()
    {
        count = 0;
        //给 fireRate 赋值,控制克隆物体的间隔时间
        fireRate = 3f;
    }

    private void Start()
    {
        SetCount();
    }

    private void Update()
    {
        //判断游戏时间是否大于我们需要的它克隆的时间
        if (Time.time > nextFire)
        {
            //计算下一次克隆时的时间
            nextFire = Time.time + fireRate;
            //给我们的 PickUp 随机一个位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //这就是克隆的操作脚本了
            //Instantiate(需要克隆的物体,克隆物体的 Pothion 值,克隆物体的 Rotation值)
            //此处我们要克隆的物体是 PickUp,他的位置就是我们之前随机存储的那个位置,Quaternion.identity 的意思是与原物体的 Rotation 值保持不变
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }
}

脚本我们现在就写完了,保存一下脚本,然后来到我们的 Unity 编辑器中,给我们 Player 物体中的 GameController 脚本中的 Player 物体添加一下,然后运行就可以看到 PickUp 开始有规律的创建了。

控制倒计时器

做倒计时器又需要用到一个新的知识--协程,这个知识还是很重要的,最好先去翻阅些资料了解一下它。

脚本文件名:GameController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public Text countText;
    //用于添加时间 Text 物体
    public Text timeText;
    //用于添加 PickUp 物体,之后克隆要用
    public GameObject pickUp;

    private int count;
    //定义一个三维向量的值用于存储克隆物体的位置
    private Vector3 newPickUpPt;
    //定义一个需要等待克隆物体的时间,根据目标应该赋值为3
    private float fireRate;
    //定义一个下一次克隆物体的时间
    private float nextFire;
    private float time;

    private void Awake()
    {
        count = 0;
        //给 fireRate 赋值,控制克隆物体的间隔时间
        fireRate = 3f;
        time = 8;
    }

    private void Start()
    {
        SetCount();
        //开始我们的协程
        StartCoroutine(Timer());
    }

    private IEnumerator Timer()
    {
        //定义一个循环,条件为要time>0
        while (time > 0)
        {
            //间隔1秒继续执行一下代码
            yield return new WaitForSeconds(1);
            //time自减1
            time--;
            //更新我们的时间文本
            SetTime();
        }
        //结束我们的协程
        StopCoroutine(Timer());
    }

    private void Update()
    {
        //判断游戏时间是否大于我们需要的它克隆的时间
        if (Time.time > nextFire)
        {
            //计算下一次克隆时的时间
            nextFire = Time.time + fireRate;
            //给我们的 PickUp 随机一个位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //这就是克隆的操作脚本了
            //Instantiate(需要克隆的物体,克隆物体的 Pothion 值,克隆物体的 Rotation值)
            //此处我们要克隆的物体是 PickUp,他的位置就是我们之前随机存储的那个位置,Quaternion.identity 的意思是与原物体的 Rotation 值保持不变
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }

    void SetTime()
    {
        timeText.text = time.ToString();
    }
}

控制结果

方法挺多的,可以自己想想怎么去完成。

脚本文件名:GameController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public Text countText;
    //用于添加时间 Text 物体
    public Text timeText;
    public Text resultText;
    //用于添加 PickUp 物体,之后克隆要用
    public GameObject pickUp;

    private int count;
    //定义一个三维向量的值用于存储克隆物体的位置
    private Vector3 newPickUpPt;
    //定义一个需要等待克隆物体的时间,根据目标应该赋值为3
    private float fireRate;
    //定义一个下一次克隆物体的时间
    private float nextFire;
    private float time;

    private void Awake()
    {
        count = 0;
        //给 fireRate 赋值,控制克隆物体的间隔时间
        fireRate = 3f;
        time = 8;
    }

    private void Start()
    {
        SetCount();
        //开始我们的协程
        StartCoroutine(Timer());
    }

    private IEnumerator Timer()
    {
        //定义一个循环,条件为要time>0
        while (time > 0)
        {
            //间隔1秒继续执行一下代码
            yield return new WaitForSeconds(1);
            //time自减1
            time--;
            //更新我们的时间文本
            SetTime();
            //调用我们创建的控制结果函数
            if (time <= 0 && count < 2)
            {
                resultText.text = "Game Over!";
            }
            else if (count >= 2)
            {
                resultText.text = "You Win!";
            }
        }
        //结束我们的协程
        StopCoroutine(Timer());
    }

    private void Update()
    {
        //判断游戏时间是否大于我们需要的它克隆的时间
        if (Time.time > nextFire && time > 0)
        {
            //计算下一次克隆时的时间
            nextFire = Time.time + fireRate;
            //给我们的 PickUp 随机一个位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //这就是克隆的操作脚本了
            //Instantiate(需要克隆的物体,克隆物体的 Pothion 值,克隆物体的 Rotation值)
            //此处我们要克隆的物体是 PickUp,他的位置就是我们之前随机存储的那个位置,Quaternion.identity 的意思是与原物体的 Rotation 值保持不变
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }

    void SetTime()
    {
        timeText.text = time.ToString();
    }
}

游戏的完善

这是我们此游戏的最后部分,用于完善一些游戏中缺乏的地方,也添加了一些比较有意思的东西,来使游戏更加具有趣味性。

完善物体的克隆创建

当游戏结束时,我们的克隆还在不停操作,这是我们不愿意看到的,所以接下来我们来完善它。

其实完善很简单,只需在我们克隆创建操作的 if 判断中添加条件就可以了。

脚本文件名:GameController

代码如下:

private void Update()
    {
        //判断游戏时间是否大于我们需要的它克隆的时间
        if (Time.time > nextFire && time > 0)
        {
            //计算下一次克隆时的时间
            nextFire = Time.time + fireRate;
            //给我们的 PickUp 随机一个位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //这就是克隆的操作脚本了
            //Instantiate(需要克隆的物体,克隆物体的 Pothion 值,克隆物体的 Rotation值)
            //此处我们要克隆的物体是 PickUp,他的位置就是我们之前随机存储的那个位置,Quaternion.identity 的意思是与原物体的 Rotation 值保持不变
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

完善倒计时器

当我们游戏胜利后会发现我们的倒计时器依旧还在运行,所以这是有问题的,因此接下来我们就来完善它。

来到我们打代码,会发现我们的游戏胜利放在了倒计时器中的循环中,因此只要当我们胜利之后添加一个 个 Break 语句跳出循环就可以完成我们的倒计时停止的操作了。

脚本文件名:GmaeController

代买如下:

private IEnumerator Timer()
    {
        //定义一个循环,条件为要time>0
        while (time > 0)
        {
            //间隔1秒继续执行一下代码
            yield return new WaitForSeconds(1);
            //time自减1
            time--;
            //更新我们的时间文本
            SetTime();
            //调用我们创建的控制结果函数
            if (time <= 0 && count < 2)
            {
                resultText.text = "Game Over!";
            }
            else if (count >= 2)
            {
                resultText.text = "You Win!";
                break;
            }
        }
        //结束我们的协程
        StopCoroutine(Timer());
    }

写完代码后运行,然后完成我们的两个小球的条件后,会发现我们的倒计时停止了,耶!完成啦!但是我们又会惊喜的发现刚完成的游戏结束后停止创建物体的任务并没有完善,我们只考虑到了游戏失败时停止创建,若当游戏胜利结束时并没有考虑到,所以我们又要回过头来重新完善我们之前的任务。

补:完善物体的克隆创建

要完善其实也很简单,只需要在我们创建的条件中再添加一个 Count 要小于我们的目标值就可以了。

脚本文件名: GameController

代码如下:

private void Update()
    {
        //判断游戏时间是否大于我们需要的它克隆的时间
        if (Time.time > nextFire && time > 0 && count < 2)
        {
            //计算下一次克隆时的时间
            nextFire = Time.time + fireRate;
            //给我们的 PickUp 随机一个位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //这就是克隆的操作脚本了
            //Instantiate(需要克隆的物体,克隆物体的 Pothion 值,克隆物体的 Rotation值)
            //此处我们要克隆的物体是 PickUp,他的位置就是我们之前随机存储的那个位置,Quaternion.identity 的意思是与原物体的 Rotation 值保持不变
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

趣味添加:游戏失败小球爆炸效果

爆炸效果是从网上找的,在此感谢TPMer博主提供,爆炸效果需要用到的素材请到他的CSDN博客最底处下载。

TPMerd的CSDN博客

素材下载完成后记住他的下载位置当我们导入时需要用到,再打开我们的 Unity 编辑器在菜单栏中的 Assets 中找到 Import Package 再选择 Custom Package 选项来添加我们的素材,找到我们之前下载的素材包打开,在 Import Unity Package 界面中,我们勾去第一个 000.unity 场景文件后选择右下角的 Import 选项添加我们的素材。

诶呀,图片加载出了点问题!

然后编辑我们的脚本文件来完成我们的小球爆炸效果。

脚本文件名:GameController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GameController : MonoBehaviour {

    public Text countText;
    //用于添加时间 Text 物体
    public Text timeText;
    public Text resultText;
    //用于添加 PickUp 物体,之后克隆要用
    public GameObject pickUp;
    //用于添加我们的爆炸物体
    public GameObject Bang;

    private int count;
    //定义一个三维向量的值用于存储克隆物体的位置
    private Vector3 newPickUpPt;
    //定义一个需要等待克隆物体的时间,根据目标应该赋值为3
    private float fireRate;
    //定义一个下一次克隆物体的时间
    private float nextFire;
    private float time;

    private void Awake()
    {
        count = 0;
        //给 fireRate 赋值,控制克隆物体的间隔时间
        fireRate = 3f;
        time = 8;
    }

    private void Start()
    {
        SetCount();
        //开始我们的协程
        StartCoroutine(Timer());
    }

    private IEnumerator Timer()
    {
        //定义一个循环,条件为要time>0
        while (time > 0)
        {
            //间隔1秒继续执行一下代码
            yield return new WaitForSeconds(1);
            //time自减1
            time--;
            //更新我们的时间文本
            SetTime();
            //调用我们创建的控制结果函数
            if (time <= 0 && count < 2)
            {
                resultText.text = "Game Over!";
                //将 Player 物体销毁
                Destroy(this.gameObject);
                //克隆我们的爆炸物体来代替我们的 Player 物体
                Instantiate(Bang, this.transform.position, this.transform.rotation);
            }
            else if (count >= 2)
            {
                resultText.text = "You Win!";
                break;
            }
        }
        //结束我们的协程
        StopCoroutine(Timer());
    }

    private void Update()
    {
        //判断游戏时间是否大于我们需要的它克隆的时间
        if (Time.time > nextFire && time > 0 && count < 2)
        {
            //计算下一次克隆时的时间
            nextFire = Time.time + fireRate;
            //给我们的 PickUp 随机一个位置
            newPickUpPt = new Vector3(Random.Range(-8, 8), 1f, Random.Range(-8, 8));
            //这就是克隆的操作脚本了
            //Instantiate(需要克隆的物体,克隆物体的 Pothion 值,克隆物体的 Rotation值)
            //此处我们要克隆的物体是 PickUp,他的位置就是我们之前随机存储的那个位置,Quaternion.identity 的意思是与原物体的 Rotation 值保持不变
            Instantiate(pickUp, newPickUpPt, Quaternion.identity);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "PickUp")
        {
            Destroy(other.gameObject);
            count++;
            SetCount();
        }
    }

    void SetCount()
    {
        countText.text = "Count:" + count;
    }

    void SetTime()
    {
        timeText.text = time.ToString();
    }
}

当我们将脚本写完保存后,打开 Unity 编辑器在我们的 Player 物体中 GameController 脚本下方的 Bang 处添加我们 Prefabs 文件下的爆炸文件后运行,当我们游戏结束时会发现我们的 Player 物体爆炸了,但是我们下方会有红色文字报错。

诶呀,图片加载出了点问题!

它的意思大体为 物体已经被销毁了,但是游戏依旧还在使用它 ,然后在找到下面的提示可以看到问题出现在了我们的 CameraController 文件中的第18行,我们顺着找过去看到的是我们用来使我们的相机位置为 Player 位置加上一个固定值的代码,找到了病因我们就去解决了。

脚本文件名:CameraController

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour {

    public GameObject player;

    private Vector3 offset;

    private void Awake()
    {
        offset = transform.position - player.transform.position;
    }

    private void Update()
    {
        if (null != player)
        {
            transform.position = player.transform.position + offset;
        }
    }
}

解决方法很简单就是将我们的相机位置代码放在一个 if 判断语句中就行了,判断的条件就是我们的 Player 物体存在就行了。

总结

以上就是这篇文章中的所有内容了,因为本人还是新手所以在很多内容上理解可能很不准确或者错误的,希望各位朋友能像我提出,我一定会悉心学习并改正的,我也会努力让自己与目标更加靠近的!!!

欢迎大家收藏我的博客,我会努力更新更多的作品的,给自己加个油!

posted @ 2019-01-11 13:44  NoahCode  阅读(434)  评论(0编辑  收藏  举报