在Unity中创建一个游戏时,增加游戏的挑战性是吸引玩家的重要手段之一。目前,只有一个敌人,这使得游戏过于简单。
如果回想一下Unity生存射击游戏教程,要生成敌人,需要创建一个名为SpawnManager的类,它负责创建敌人的新实例。在SpawnManager中,除了想要添加的其他功能外,主要需要给它的有:
然而,与生存射击游戏不同的是,将限制敌人的生成数量,以便能够赢得游戏。波次系统的实现涉及很多工作,从Unity的Enemy Spawner示例中借鉴了很多想法。
首先,要创建一个新的空游戏对象,将其命名为EnemyManager。将这个对象作为GameManager的子对象。接下来,将为这个新游戏对象创建一个新的脚本,将其命名为EnemyManager。
管理器将做几件事情:
通过跟踪敌人的数量和波次,可以知道何时进入下一个波次,以及否获胜。以下是EnemyManager.cs的初始代码:
using System.Collections;
using UnityEngine;
[System.Serializable]
public class Wave
{
public int EnemiesPerWave;
public GameObject Enemy;
}
public class SpawnManager : MonoBehaviour
{
public Wave[] Waves;
public Transform[] SpawnPoints;
public float TimeBetweenEnemies = 2f;
private int _totalEnemiesInCurrentWave;
private int _enemiesInWaveLeft;
private int _spawnedEnemies;
private int _currentWave;
private int _totalWaves;
void Start()
{
_currentWave = -1;
_totalWaves = Waves.Length - 1;
StartNextWave();
}
void StartNextWave()
{
_currentWave++;
if (_currentWave > _totalWaves)
{
return;
}
_totalEnemiesInCurrentWave = Waves[_currentWave].EnemiesPerWave;
_enemiesInWaveLeft = 0;
_spawnedEnemies = 0;
StartCoroutine(SpawnEnemies());
}
IEnumerator SpawnEnemies()
{
GameObject enemy = Waves[_currentWave].Enemy;
while (_spawnedEnemies < _totalEnemiesInCurrentWave)
{
_spawnedEnemies++;
_enemiesInWaveLeft++;
int spawnPointIndex = Random.Range(0, SpawnPoints.Length);
Instantiate(enemy, SpawnPoints[spawnPointIndex].position, SpawnPoints[spawnPointIndex].rotation);
yield return new WaitForSeconds(TimeBetweenEnemies);
}
yield return null;
}
public void EnemyDefeated()
{
_enemiesInWaveLeft--;
if (_enemiesInWaveLeft == 0 && _spawnedEnemies == _totalEnemiesInCurrentWave)
{
StartNextWave();
}
}
}
代码中有很多内容需要理解,这就是为什么添加了注释的原因。然而,以下是代码的概述。
在讨论变量之前,想介绍一下Wave类。Wave是一个容器,用于保存将要面对的每个波次的数据。如果还记得Space Shooter教程,做了类似的事情。创建了一个新的类来保存信息,并且使其可序列化,这样Unity就知道如何在编辑器中显示它。
对于public变量,有:
对于private变量来跟踪敌人,有:
还跟踪处于哪个波次:
现在知道了使用的变量,可以继续讨论代码的其余部分。在Start()中,将所有变量初始化为0。注意将_currentWave设置为-1,_totalWaves是数组的长度-1。对于不熟悉的人来说,所有这些都是因为在数组中使用0基础索引(意味着一切都从0而不是1开始)。
从Start(),还调用StartNextWave(),这段代码处理清除波次时会发生什么。增加_currentWave,假设还没有完成游戏,将设置需要遇到的下一个波次的敌人,并调用SpawnEnemies()。
SpawnEnemies()是一个协程,用来创建敌人。该函数将根据设置的随机生成点之一为波次生成敌人。代码将在生成下一个敌人之前等待2秒钟,这样就不会一下子给玩家带来所有的敌人。
最后,有public EnemyDefeated(),这意味着这个函数是从其他地方调用的。在这种情况下,敌人在被杀死时会调用这个函数。然后将减少敌人计数器,如果击败了波次中的所有敌人,将调用StartNextWave()。
现在有了脚本并且正在运行,需要做的最后一件事是设置Wave并创建Spawn Points。
现在,让只为SpawnManager创建2个波次,这将创建骑士敌人。需要为Wave类提供2样东西:
让首先设置骑士游戏对象。可以简单地将骑士从层级拖放到位置,一切都会很好。然而,正确的方法是首先创建一个骑士的prefab。
可以将prefab视为可以拖放到不同场景和游戏中的游戏对象的模板。在这种情况下,也可以像在这里所做的那样实例化它们。主要的好处是,如果需要更改prefab,可以随时更改prefab,任何从代码实例化它的东西也会得到更改。
将要做的第一件事是:
注意:在这个例子中,Knight已经是一个prefab,但没关系,已经改变了它的一些内容,所以让从它创建另一个prefab。
顺便说一句:可以从层级中删除Knight游戏对象。他为服务得很好,但不再需要他的服务了。
现在有了需要的一切,可以开始创建波次了。回到SpawnManger脚本,展开Waves并将Size设置为2,这样就可以创建新的波次了。
现在已经设置了波次系统,需要创建生成点。就像生存射击游戏一样,需要添加一些游戏对象,将使用它们作为将要生成敌人的位置。
让开始吧!创建3个空游戏对象:称它们为SpawnPoint1 - SpawnPoint3。将它们作为SpawnManager的子对象,然而,把它们放在哪里并不重要。
目前,无法在游戏看到SpawnPoints的位置,然而可以给它们添加一个Label,以便可以看到对象的位置。只想将Spawn点设置在3个房子中:
以下是Position值,可能会有所不同:
最后,回到SpawnManager并添加SpawnPoints。展开Spawn Points,然后对于Size将其更改为3,然后将SpawnPoints拖放到空的游戏位置。