使用Unity创建塔防游戏(Part2),unitypart2

2019-05-08 作者:计算机教程   |   浏览(168)

How to Create a Tower Defense Game in Unity – Part 2

原文地址:https://www.raywenderlich.com/107529/unity-tower-defense-tutorial-part-2

  欢迎大家来查看,使用Unity创建塔防游戏(第二篇)。在第一篇的结尾,我们已经可以召唤和升级小怪兽,召唤一个敌人朝着饼干前进的敌人。

  但是这个敌人没有方向感,让人感觉怪怪的。接下来,我们要做的是召唤一波一波的敌人,然后令小怪兽能够消灭它们,都是为了保护你那块美味的饼干。

使用Unity创建塔防游戏(Part2),unitypart2

准备工作

  用Unity打开你之前完成的工程,但如果你没看过Part1,先下载starter project ,然后打开TowerDefense-Part2-Starter这个工程。打开Scenes文件夹下的GameScene。

How to Create a Tower Defense Game in Unity – Part 2

原文地址:https://www.raywenderlich.com/107529/unity-tower-defense-tutorial-part-2

  欢迎大家来查看,使用Unity创建塔防游戏(第二篇)。在第一篇的结尾,我们已经可以召唤和升级小怪兽,召唤一个敌人朝着饼干前进的敌人。

  但是这个敌人没有方向感,让人感觉怪怪的。接下来,我们要做的是召唤一波一波的敌人,然后令小怪兽能够消灭它们,都是为了保护你那块美味的饼干。

让敌人有方向感

  在Part1的结尾,我们可以令敌人沿着路线前进,但它们毫无方向感。

  用VS打开脚本MoveEnemy.cs,添加下面的代码来解决这个问题。

    private void RotateIntoMoveDirection() 
    {
        // 1 
        Vector3 newStartPosition = waypoints[currentWaypoint].transform.position;
        Vector3 newEndPosition = waypoints[currentWaypoint   1].transform.position;
        Vector3 newDirection = (newEndPosition - newStartPosition);
        // 2
        float x = newDirection.x;
        float y = newDirection.y;
        float rotationAngle = Mathf.Atan2(y, x) * 180 / Mathf.PI;
        // 3
        GameObject sprite = (GameObject)gameObject.transform.FindChild("Sprite").gameObject;
        sprite.transform.rotation = Quaternion.AngleAxis(rotationAngle, Vector3.forward);
    }

  RotateIntoMoveDirection 这个方法是将场景中敌人对象的角度进行旋转,让敌人看起来有方向感。我们一步一步地来看:

  1. 计算出下一个路标与当前路标之间的向量之差,敌人会沿着这个向量前往下一个路标。
  2. 计算敌人要旋转的角度,即敌人当前的方向与newDirection之间的夹角的度数。调用Mathf.Atan2 来计算,参数为newDirection的X坐标和Y坐标,但返回的结果是以弧度为单位的。因此,我们需要将结果乘以 180 / Math.PI ,将弧度转化为角度。 
  3. 最后,我们获取敌人对象的子对象——Sprite,令它围绕Z轴旋转的度数为rotationAngle。这里我们调用Quaternion.AngleAxis来完成旋转的工作,它的第一个参数就是我们之前计算出的角度。注意,为什么是将子对象旋转,而不是敌人对象?这是为了保证敌人的血条始终保持水平,接下来我们要为敌人添加血条了。

    www.2003.com 1

  将Update() 中的注释 // TODO: Rotate into move direction替换成调用我们刚写好的函数—— RotateIntoMoveDirection

                RotateIntoMoveDirection();

  保存好脚本,返回Unity,运行游戏,看敌人现在有方向感了。这样才算是朝着饼干前进。

  www.2003.com 2

  才一个小兵?这怎行,要来就来一大群。在一般的塔防游戏中,都是每一波敌人都是一大群。

准备工作

  用Unity打开你之前完成的工程,但如果你没看过Part1,先下载starter project ,然后打开TowerDefense-Part2-Starter这个工程。打开Scenes文件夹下的GameScene。

告知玩家——敌人来了

  在一大群敌人出现之前,我们应该先告知玩家——敌人来了。同时,我们需要显示这是第几波敌人,在界面的右上角显示。

  在脚本中,有不少需要用到波数的地方,我们先在GameManager的脚本组件GameManagerBehavior中添加有关波数的代码。

  用VS打开GameManagerBehavior.cs,然后添加下面两个变量:

    public Text waveLable;
    public GameObject[] nextWaveLabels;

  显示在屏幕右上角的波数会存储在waveLabel 这个变量中。 nextWaveLabels 这个数组保存了两个游戏对象。在一波新的敌人到来之前,它们会构成一个文字合并的动画,如下图所示:

  www.2003.com 3

  保存好脚本,返回Unity。选中Hierarchy视图中的GameManager,在Inspector面板中,点击Wave Label右侧的小圆圈,然后从弹出的Text对话框中的Scene标签页下选择 WaveLabel

  将NextWave LabelsSize 设置为2。就像刚才设置WaveLabel那样,将Element0设置为NextWaveBottomLabel ,将Element1设置为NextWaveTopLabel

  www.2003.com 4

  这是设置好数据的结果。

  当玩家输掉游戏的时候,它无法看到有关下一波敌人的信息。回到GameManagerBehavior.cs中,添加一个变量:

    public bool gameOver = false;

  gameOver这个变量表示玩家是否输掉了游戏。

  同样的,我们也要为wave这个私有变量添加一个属性,让wave中的值与游戏当前波数保持一致,再向GameManagerBehavior.cs添加以下代码:

    private int wave;
    public int Wave 
    {
        get { return wave; }
        set {
            wave = value;
            if (!gameOver) 
            {
                for (int i = 0; i < nextWaveLabels.Length; i  )
                {
                    nextWaveLabels[i].GetComponent<Animator>().SetTrigger("nextWave");
                }
            }
            waveLable.text = "WAVE: "   (wave   1);
        }
    }

  在上面的代码中,我们创建了一个私有变量,一个属性。这个属性的getter方法,我们已经习以为常了,但它的setter方法看起来有些棘手。

  先是更新了wave的值。接下来,判断游戏是否未结束,如果是的话,遍历nextWaveLabels中元素,这些元素都带有一个Animator组件。调用SetTrigger来触发动画。

  最后,我们设置waveLabel上的数值为 wave 1。为什么呢?因为在程序中,变量的初始值可以是0,但是人们都是从1开始数数的。

  在Start()方法中设置这个属性的值:

        Wave = 0;

  将Wave的初始值设置为1。

  保存好脚本,返回Unity中,运行游戏。波数的确是从1开始的。

  www.2003.com 5

  对于玩家而言,首先要解决的是第一波敌人。 

让敌人有方向感

  在Part1的结尾,我们可以令敌人沿着路线前进,但它们毫无方向感。

  用VS打开脚本MoveEnemy.cs,添加下面的代码来解决这个问题。

    private void RotateIntoMoveDirection() 
    {
        // 1 
        Vector3 newStartPosition = waypoints[currentWaypoint].transform.position;
        Vector3 newEndPosition = waypoints[currentWaypoint   1].transform.position;
        Vector3 newDirection = (newEndPosition - newStartPosition);
        // 2
        float x = newDirection.x;
        float y = newDirection.y;
        float rotationAngle = Mathf.Atan2(y, x) * 180 / Mathf.PI;
        // 3
        GameObject sprite = (GameObject)gameObject.transform.FindChild("Sprite").gameObject;
        sprite.transform.rotation = Quaternion.AngleAxis(rotationAngle, Vector3.forward);
    }

  RotateIntoMoveDirection 这个方法是将场景中敌人对象的角度进行旋转,让敌人看起来有方向感。我们一步一步地来看:

    www.2003.com 6

  将Update() 中的注释 // TODO: Rotate into move direction替换成调用我们刚写好的函数—— RotateIntoMoveDirection

                RotateIntoMoveDirection();

  保存好脚本,返回Unity,运行游戏,看敌人现在有方向感了。这样才算是朝着饼干前进。

  www.2003.com 7

  才一个小兵?这怎行,要来就来一大群。在一般的塔防游戏中,都是每一波敌人都是一大群。

逐个创建敌人

  显然,我们现在要做的是创建一支敌军(由想吃掉你饼干的小虫子组成),但我们暂时无法做到。

  此外,当玩家刚消灭一波敌人的时候,先不要创建下一波敌人,至少现在是这样。

  于是,我们必须要知道游戏场景中是否还有敌人存在,我们为敌人对象添加Tags(标签)来区别于其他游戏对象。此外,在脚本中,可以通过标签名快速查找物体。

告知玩家——敌人来了

  在一大群敌人出现之前,我们应该先告知玩家——敌人来了。同时,我们需要显示这是第几波敌人,在界面的右上角显示。

  在脚本中,有不少需要用到波数的地方,我们先在GameManager的脚本组件GameManagerBehavior中添加有关波数的代码。

  用VS打开GameManagerBehavior.cs,然后添加下面两个变量:

    public Text waveLable;
    public GameObject[] nextWaveLabels;

  显示在屏幕右上角的波数会存储在waveLabel 这个变量中。 nextWaveLabels 这个数组保存了两个游戏对象。在一波新的敌人到来之前,它们会构成一个文字合并的动画,如下图所示:

  www.2003.com 8

  保存好脚本,返回Unity。选中Hierarchy视图中的GameManager,在Inspector面板中,点击Wave Label右侧的小圆圈,然后从弹出的Text对话框中的Scene标签页下选择 WaveLabel

  将NextWave LabelsSize 设置为2。就像刚才设置WaveLabel那样,将Element0设置为NextWaveBottomLabel ,将Element1设置为NextWaveTopLabel

  www.2003.com 9

  这是设置好数据的结果。

  当玩家输掉游戏的时候,它无法看到有关下一波敌人的信息。回到GameManagerBehavior.cs中,添加一个变量:

    public bool gameOver = false;

  gameOver这个变量表示玩家是否输掉了游戏。

  同样的,我们也要为wave这个私有变量添加一个属性,让wave中的值与游戏当前波数保持一致,再向GameManagerBehavior.cs添加以下代码:

    private int wave;
    public int Wave 
    {
        get { return wave; }
        set {
            wave = value;
            if (!gameOver) 
            {
                for (int i = 0; i < nextWaveLabels.Length; i  )
                {
                    nextWaveLabels[i].GetComponent<Animator>().SetTrigger("nextWave");
                }
            }
            waveLable.text = "WAVE: "   (wave   1);
        }
    }

  在上面的代码中,我们创建了一个私有变量,一个属性。这个属性的getter方法,我们已经习以为常了,但它的setter方法看起来有些棘手。

  先是更新了wave的值。接下来,判断游戏是否未结束,如果是的话,遍历nextWaveLabels中元素,这些元素都带有一个Animator组件。调用SetTrigger来触发动画。

  最后,我们设置waveLabel上的数值为 wave 1。为什么呢?因为在程序中,变量的初始值可以是0,但是人们都是从1开始数数的。

  在Start()方法中设置这个属性的值:

        Wave = 0;

  将Wave的初始值设置为1。

  保存好脚本,返回Unity中,运行游戏。波数的确是从1开始的。

  www.2003.com 10

  对于玩家而言,首先要解决的是第一波敌人。 

为敌人对象添加标签

  在Project视图中,选中名为Enemy的prefab。在Inspector面板的顶部,点击Tag右边的下拉框,从弹出的对话框中选择Add Tag

        www.2003.com 11

  新建一个标签,命名为Enemy

      www.2003.com 12

  选中名为Enemy的prefab,在Inspector中将它的标签设置为我们刚才创建的标签——Enemy

逐个创建敌人

  显然,我们现在要做的是创建一支敌军(由想吃掉你饼干的小虫子组成),但我们暂时无法做到。

  此外,当玩家刚消灭一波敌人的时候,先不要创建下一波敌人,至少现在是这样。

  于是,我们必须要知道游戏场景中是否还有敌人存在,我们为敌人对象添加Tags(标签)来区别于其他游戏对象。此外,在脚本中,可以通过标签名快速查找物体。

配置敌军的信息

  现在,我们需要定义有关敌军的类和变量。用VS打开SpawnEnemy.cs,在SpawnEnemy的上方添加一个新的类,如下面代码所示:

[System.Serializable]
public class Wave 
{
    public GameObject enemyPrefab;
    public float spawnInterval = 2;
    public int maxEnemies = 20;
}

  Wave这个类表示一支敌军,它有3个字段,enemyPrefab用于实例化敌人对象;每隔spawnInterval秒产生一个敌人,每波创建单个敌人的时间间隔可能是不同的;一波敌人的最大数量为maxEnemies

  这个类是序列化的,所以我们可以在Inspector面板中更改它的数据。

  接下来为SpawnEnemy这个类添加下列变量:

    public Wave[] waves;
    public int timeBetweenWaves = 5;

    private GameManagerBehavior gameManager;

    private float lastSpawnTime;
    private int enemiesSpawned = 0;

  这几个变量都是与创建敌人有关的。我们将各个级别的敌军存储在waves这个数组里;enemiesSpawned记录了已产生的敌人的数量;lastSpawnTime记录了还是上一个敌人产生的时间;

  玩家需要一些时间来消灭这些敌人,于是我们将timeBetweenWaves设置为5秒,即每隔5秒产生一波敌人。

  将Start()方法中的代码替换为以下代码:

        lastSpawnTime = Time.time;
        gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();

  我们将lastSpawnTime设置为当前时间,当场景加载完成后,Start()方法就会被执行。然后,我们获取了游戏对象GameManager的引用。

  向Update()方法中添加下列代码:

        // 1
        int currentWave = gameManager.Wave;
        if (currentWave < waves.Length)
        {   // 2
            float timeInterval = Time.time - lastSpawnTime;
            float spawnInterval = waves[currentWave].spawnInterval;
            if(((enemiesSpawned == 0 && timeInterval > timeBetweenWaves) ||
                timeInterval > spawnInterval) && 
                enemiesSpawned < waves[currentWave].maxEnemies)
            {   // 3
                lastSpawnTime = Time.time;
                GameObject newEnemy = (GameObject)Instantiate(waves[currentWave].enemyPrefab);
                enemiesSpawned  ;
            }
            // 4 
            if (enemiesSpawned == waves[currentWave].maxEnemies &&
                GameObject.FindGameObjectWithTag("Enemy") == null) 
            {
                gameManager.Wave  ;
                gameManager.Gold = Mathf.RoundToInt(gameManager.Gold * 1.1f);
                enemiesSpawned = 0;
                lastSpawnTime = Time.time;
            }
        }  // 5
        else
        {
            gameManager.gameOver = true;
            GameObject gameOverText = GameObject.FindGameObjectWithTag("GameWon");
            gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
        }

  让我们一步一步来理解这段代码:

  1. 获得当前波数,并判断是否未到最后一波。
  2. 如果是这样的话,先计算距离上一个敌人的创建过去了多少时间,并且判断是否到了创建下一个敌人的时间。这取决于两个条件:一、如果已创建的敌人数量为0,并且timeInterval大于timeBetweenWaves;二、判断timeInterval是否大于spawnInterval。无论如何,前提是这波敌人尚未被创建完毕。
  3. 假如符合2中的条件,就以enemyPrefab为拷贝,实例化一个敌人对象,赋予敌人对象有关路标的信息,并且将已创建的敌人数量加1。
  4. 若所有敌人都已被创建,但场景中找不到标签为Enemy的游戏对象,说明这波敌人都已玩家消灭。我们就要准备创建下一波敌人,并且给予玩家金币数量增加百分之十。
  5. 玩家消灭了最后一波敌人,播放游戏胜利的动画。

为敌人对象添加标签

  在Project视图中,选中名为Enemy的prefab。在Inspector面板的顶部,点击Tag右边的下拉框,从弹出的对话框中选择Add Tag

        www.2003.com 13

  新建一个标签,命名为Enemy

      www.2003.com 14

  选中名为Enemy的prefab,在Inspector中将它的标签设置为我们刚才创建的标签——Enemy

设置创建单个敌人的时间间隔

  保存好脚本,返回Unity,选中Hierarchy视图中的Road对象,在Inspector面板中,将数组WavesSize设置为4。

  接下来,依次为数组的4个元素赋值。将名为Enemy的prefab赋值给Enemy Prefab,分别设置Spawn IntervalMax Enemies的值如下:

  • Element 0: Spawn Interval: 2.5, Max Enemies: 5
  • Element 1: Spawn Interval: 2, Max Enemies: 10
  • Element 2: Spawn Interval: 2, Max Enemies: 15
  • Element 3: Spawn Interval: 1, Max Enemies: 5

  最终设置好的结果如下如图所示:

  www.2003.com 15

  我们可以通过上面的设置达到平衡游戏的目的。运行游戏,哈哈!那些小虫子正朝着你的饼干前进!

  www.2003.com 16

配置敌军的信息

  现在,我们需要定义有关敌军的类和变量。用VS打开SpawnEnemy.cs,在SpawnEnemy的上方添加一个新的类,如下面代码所示:

[System.Serializable]
public class Wave 
{
    public GameObject enemyPrefab;
    public float spawnInterval = 2;
    public int maxEnemies = 20;
}

  Wave这个类表示一支敌军,它有3个字段,enemyPrefab用于实例化敌人对象;每隔spawnInterval秒产生一个敌人,每波创建单个敌人的时间间隔可能是不同的;一波敌人的最大数量为maxEnemies

  这个类是序列化的,所以我们可以在Inspector面板中更改它的数据。

  接下来为SpawnEnemy这个类添加下列变量:

    public Wave[] waves;
    public int timeBetweenWaves = 5;

    private GameManagerBehavior gameManager;

    private float lastSpawnTime;
    private int enemiesSpawned = 0;

  这几个变量都是与创建敌人有关的。我们将各个级别的敌军存储在waves这个数组里;enemiesSpawned记录了已产生的敌人的数量;lastSpawnTime记录了还是上一个敌人产生的时间;

  玩家需要一些时间来消灭这些敌人,于是我们将timeBetweenWaves设置为5秒,即每隔5秒产生一波敌人。

  将Start()方法中的代码替换为以下代码:

        lastSpawnTime = Time.time;
        gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();

  我们将lastSpawnTime设置为当前时间,当场景加载完成后,Start()方法就会被执行。然后,我们获取了游戏对象GameManager的引用。

  向Update()方法中添加下列代码:

        // 1
        int currentWave = gameManager.Wave;
        if (currentWave < waves.Length)
        {   // 2
            float timeInterval = Time.time - lastSpawnTime;
            float spawnInterval = waves[currentWave].spawnInterval;
            if(((enemiesSpawned == 0 && timeInterval > timeBetweenWaves) ||
                timeInterval > spawnInterval) && 
                enemiesSpawned < waves[currentWave].maxEnemies)
            {   // 3
                lastSpawnTime = Time.time;
                GameObject newEnemy = (GameObject)Instantiate(waves[currentWave].enemyPrefab);
                enemiesSpawned  ;
            }
            // 4 
            if (enemiesSpawned == waves[currentWave].maxEnemies &&
                GameObject.FindGameObjectWithTag("Enemy") == null) 
            {
                gameManager.Wave  ;
                gameManager.Gold = Mathf.RoundToInt(gameManager.Gold * 1.1f);
                enemiesSpawned = 0;
                lastSpawnTime = Time.time;
            }
        }  // 5
        else
        {
            gameManager.gameOver = true;
            GameObject gameOverText = GameObject.FindGameObjectWithTag("GameWon");
            gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
        }

  让我们一步一步来理解这段代码:

可选项:添加不同种类的敌人

  塔防游戏里的敌人一般都不止一种。在我们工程的Prefab文件夹中还包含着另一种敌人的prefab,Enemy2

  选中Prefab文件夹中的Enemy2,在Inspector面板中,为它添加一个脚本组件,我们选择已有的MoveEnemy这个脚本。将Speed的值设置为3,将它的标签设置为Enemy。我们用这种快速前进的小虫子,让玩家保持警觉。

设置创建单个敌人的时间间隔

  保存好脚本,返回Unity,选中Hierarchy视图中的Road对象,在Inspector面板中,将数组WavesSize设置为4。

  接下来,依次为数组的4个元素赋值。将名为Enemy的prefab赋值给Enemy Prefab,分别设置Spawn IntervalMax Enemies的值如下:

  • Element 0: Spawn Interval: 2.5, Max Enemies: 5
  • Element 1: Spawn Interval: 2, Max Enemies: 10
  • Element 2: Spawn Interval: 2, Max Enemies: 15
  • Element 3: Spawn Interval: 1, Max Enemies: 5

  最终设置好的结果如下如图所示:

  www.2003.com 17

  我们可以通过上面的设置达到平衡游戏的目的。运行游戏,哈哈!那些小虫子正朝着你的饼干前进!

  www.2003.com 18

更新玩家的血量——不要让我死的那么快

  现在,即使一大群小虫子抵达了你那美味的饼干,你的血量都丝毫未损。于是,当有小虫子碰了你那块饼干的时候,你就要受伤了。

  www.2003.com 19

  打开GameManagerBehavior.cs,添加下面两个变量。

    public Text healthLabel;
    public GameObject[] healthIndicator;

  我们用healthLabel来显示玩家当前的血量,healthIndicator用于表示5只正在啃你饼干的小虫子,比起一个简单的数字或血条,用它们来表示玩家的血量会更有趣一些。

可选项:添加不同种类的敌人

  塔防游戏里的敌人一般都不止一种。在我们工程的Prefab文件夹中还包含着另一种敌人的prefab,Enemy2

  选中Prefab文件夹中的Enemy2,在Inspector面板中,为它添加一个脚本组件,我们选择已有的MoveEnemy这个脚本。将Speed的值设置为3,将它的标签设置为Enemy。我们用这种快速前进的小虫子,让玩家保持警觉。

控制玩家的血量

  接下来,为 GameManagerBehavior 添加一个属性,用来管理玩家的血量。  

    private int health;
    public int Health 
    {
        get { return health; }
        set { 
            // 1
            if (value < health) {
                Camera.main.GetComponent<CameraShake>().Shake();
            }
            // 2
            health = value;
            healthLabel.text = "HEALTH: "   health;
            // 3
            if (health <= 0 && !gameOver) 
            {
                gameOver = true;
                GameObject gameOverText = GameObject.FindGameObjectWithTag("GameOver");
                gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
            }
            // 4
            for (int i = 0; i < healthIndicator.Length; i  )
            {
                if (i < Health)
                {
                    healthIndicator[i].SetActive(true);
                }
                else 
                {
                    healthIndicator[i].SetActive(false);
                }
            }
        }
    }

  以上代码块用于管理玩家的血量,同样的,setter方法是这段代码的主体。

  1. 当玩家掉血的时候,我们使用CameraShake这个组件来制造一个很棒的晃动效果。(这个晃动效果是为了警告玩家,小虫子正在吃掉你的饼干。)这个脚本也被包含在我们的工程内,但本文不作介绍。
  2. 更新私有字段health的值,以及屏幕左上角的血量显示。
  3. 当玩家血量被扣光的时候,且游戏未结束,先设置 gameOver 的值为true,再触发游戏失败的动画。
  4. 将一只绿色的小怪物从饼干上移除。就可以做得简单点的话,我们可以只是隐藏它们,当我们需要为玩家加血的时候,就可以将重新它们显示出来。

  在Start()中初始化Health

        Health = 5;

  在游戏开始的时候,玩家的血量为5。

  有了这个属性,当小虫子抵达饼干的时候,我们就可以更新玩家的血量了。保存好脚本,在VS中打开MoveEnemy.cs这个脚本。

更新玩家的血量——不要让我死的那么快

  现在,即使一大群小虫子抵达了你那美味的饼干,你的血量都丝毫未损。于是,当有小虫子碰了你那块饼干的时候,你就要受伤了。

  www.2003.com 20

  打开GameManagerBehavior.cs,添加下面两个变量。

    public Text healthLabel;
    public GameObject[] healthIndicator;

  我们用healthLabel来显示玩家当前的血量,healthIndicator用于表示5只正在啃你饼干的小虫子,比起一个简单的数字或血条,用它们来表示玩家的血量会更有趣一些。

更新玩家的血量

  将MoveEnemy.csUpdate()方法内部的注释:// TODO: deduct health ,替换成以下代码:

                GameManagerBehavior gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
                gameManager.Health -= 1;

  这段代码是为了获取GameManagerBehavior对象,然后将Health的值减1。

  保存好脚本,返回Unity。

  选中Hierarchy视图中的GameManager对象,为Health Label 赋值,选择HealthLabel

www.2003.com,  在Hierarchy视图中展开Cookie对象,注意不要选中它,我们只要让它下面的5个子对象显示出来即可。将这5个子对象拖拽赋值给GameManagerHealth Indicator数组。我们用5只正在开心地啃着饼干的青色小虫子来表示玩家的血量。玩家受到一次伤害,就减少一只青色的小虫子。

  运行游戏,让那些小虫子冲向饼干,什么都别做,直到游戏结束。

  www.2003.com 21

控制玩家的血量

  接下来,为 GameManagerBehavior 添加一个属性,用来管理玩家的血量。  

    private int health;
    public int Health 
    {
        get { return health; }
        set { 
            // 1
            if (value < health) {
                Camera.main.GetComponent<CameraShake>().Shake();
            }
            // 2
            health = value;
            healthLabel.text = "HEALTH: "   health;
            // 3
            if (health <= 0 && !gameOver) 
            {
                gameOver = true;
                GameObject gameOverText = GameObject.FindGameObjectWithTag("GameOver");
                gameOverText.GetComponent<Animator>().SetBool("gameOver", true);
            }
            // 4
            for (int i = 0; i < healthIndicator.Length; i  )
            {
                if (i < Health)
                {
                    healthIndicator[i].SetActive(true);
                }
                else 
                {
                    healthIndicator[i].SetActive(false);
                }
            }
        }
    }

  以上代码块用于管理玩家的血量,同样的,setter方法是这段代码的主体。

  在Start()中初始化Health

        Health = 5;

  在游戏开始的时候,玩家的血量为5。

  有了这个属性,当小虫子抵达饼干的时候,我们就可以更新玩家的血量了。保存好脚本,在VS中打开MoveEnemy.cs这个脚本。

小怪兽的战斗:消灭那些小虫子

  该召唤小怪兽?还是让小虫子前进?现在我们的小怪兽还是纸老虎,我们要做的是让小怪兽们能够消灭那些小虫子。

  我们先要把以下几件事情做好:

  • 给小虫子一个血条,让玩家能看出敌人的强弱。
  • 让小怪兽能够发现它攻击范围内的敌人们
  • 决定朝那个敌人开火
  • 无尽的子弹

更新玩家的血量

  将MoveEnemy.csUpdate()方法内部的注释:// TODO: deduct health ,替换成以下代码:

                GameManagerBehavior gameManager = GameObject.Find("GameManager").GetComponent<GameManagerBehavior>();
                gameManager.Health -= 1;

  这段代码是为了获取GameManagerBehavior对象,然后将Health的值减1。

  保存好脚本,返回Unity。

  选中Hierarchy视图中的GameManager对象,为Health Label 赋值,选择HealthLabel

  在Hierarchy视图中展开Cookie对象,注意不要选中它,我们只要让它下面的5个子对象显示出来即可。将这5个子对象拖拽赋值给GameManagerHealth Indicator数组。我们用5只正在开心地啃着饼干的青色小虫子来表示玩家的血量。玩家受到一次伤害,就减少一只青色的小虫子。

  运行游戏,让那些小虫子冲向饼干,什么都别做,直到游戏结束。

  www.2003.com 22

本文由www.2003.com发布于计算机教程,转载请注明出处:使用Unity创建塔防游戏(Part2),unitypart2

关键词: