3

我已经在我的 Unity 2D 项目的主要 Gameplay.cs 脚本中处理这部分代码很长一段时间了,无论我以哪种方式更改它并改变它,它要么导致 Unity 锁定在 Play 上,要么,经过一些调整,会立即从 Wave 0 转到 Wave 9。这是我最初的尝试,它导致 Unity 在 Play 上冻结。我不明白为什么会发生这种情况,并想了解我的逻辑哪里出了问题。

我正在努力实现以下目标:

  • 在 Play 上,游戏等待 startWait 秒。
  • 在 startWait 之后,游戏进入 SpawnWaves() 方法并在每个波之间等待 waveWait 秒,并在每个波期间将波计数增加一。
  • 每一波都会产生zombieCount 数量的敌人并在每个僵尸产生之间等待 spawnWait 秒。


private float startWait = 3f;   //Amount of time to wait before first wave
private float waveWait = 2f;    //Amount of time to wait between spawning each wave
private float spawnWait = 0.5f; //Amount of time to wait between spawning each enemy

private float startTimer = 0f; //Keep track of time that has elapsed since game launch private float waveTimer = 0f; //Keep track of time that has elapsed since last wave private float spawnTimer = 0f; //Keep track of time that has elapsed since last spawn private List<GameObject> zombies = new List<GameObject>(); [SerializeField] private Transform spawnPoints; //The parent object containing all of the spawn point objects void Start() { deathUI.SetActive(false); //Set the default number of zombies to spawn per wave zombieCount = 5; PopulateZombies(); } void Update() { startTimer += Time.deltaTime; if (startTimer >= startWait) { waveTimer += Time.deltaTime; spawnTimer += Time.deltaTime; SpawnWaves(); } //When the player dies "pause" the game and bring up the Death screen if (Player.isDead == true) { Time.timeScale = 0; deathUI.SetActive(true); } } void SpawnWaves() { while (!Player.isDead && ScoreCntl.wave < 9) { if (waveTimer >= waveWait) { IncrementWave(); for (int i = 0; i < zombieCount; i++) { if (spawnTimer >= spawnWait) { Vector3 spawnPosition = spawnPoints.GetChild(Random.Range(0, spawnPoints.childCount)).position; Quaternion spawnRotation = Quaternion.identity; GameObject created = Instantiate(zombies[0], spawnPosition, spawnRotation); TransformCreated(created); spawnTimer = 0f; } } waveTimer = 0f; } } }

我是初学者,并且了解我的代码可能不遵循最佳实践。我也有一个使用协程和收益回报的工作敌人产卵器,但想让这个版本的我的敌人产卵工作。

4

2 回答 2

2

我对 Unity 没有任何经验,但对于 XNA,我假设它们的主要游戏处理与使用 Update() 和 Draw() 函数相似。

Update() 函数通常每秒调用几次,在您的代码中 SpawnWaves()在每次更新调用后执行一次 startTimer >= startWait。

所以让我们看看 SpawnWaves()

void SpawnWaves()
{
    while (!Player.isDead && ScoreCntl.wave < 9)
    {
        if (waveTimer >= waveWait)
        {
            IncrementWave();
            for (int i = 0; i < zombieCount; i++)
            {
                if (spawnTimer >= spawnWait)
                {
                    [..]
                    spawnTimer = 0f;
                }
            }
            waveTimer = 0f;
        }
    }
}

这个while循环是个问题,因为它是游戏循环中的一种游戏循环。它只会在玩家死亡或您的波数超过 9 时退出。

调用 SpawnWaves() 后有两种可能,取决于 Time.deltaTime,它在程序启动后或多或少是随机的,waveTimer 可以大于或小于 waveWait。

  • 案例:waveTimer >= waveWait

    里面的所有代码都会立即执行。因此,如果它可以使用 IncrementWave(),它将在毫秒内完成九次。然后 while 是假的,里面的代码再也不会执行了。

  • 案例:waveTimer < waveWait

    在这种情况下,将永远不会调用 IncrementWave(),并且您的游戏可能会在此 while 循环中无限旋转。

所以你要做的基本上是用 if 语句替换这个 while 循环,以允许程序继续。

If(!Player.isDead && ScoreCntl.wave < 9)

从那时起,您的计数器增量将在 Update() 上多次调用。

waveTimer += Time.deltaTime;
spawnTimer += Time.deltaTime;

随着时间的推移,你的波浪变化和产卵条件会变得真实。

if (waveTimer >= waveWait),
if (spawnTimer >= spawnWait)

我希望这可以指导您解决问题。

更新:

对于这种行为,最好将 IncrementWave 和生成条件分开。为此,您需要一个额外的 bool 变量,在本例中称为 haveToSpawn。

    if (waveTimer >= waveWait)
    {
      IncrementWave();
      waveTimer = 0f;
      haveToSpawn = true; //new variable
    }

    if (haveToSpawn && spawnTimer >= spawnWait)
    {
        for (int i = 0; i < zombieCount; i++)
        {
            [..] //spawn each zonbie
        }
        spawnTimer = 0f; //reset spawn delay
        haveToSpawn = false; //disallow spawing
    }
于 2017-02-05T12:28:47.857 回答
1

使用协程调用 spawn waves 方法:

private float startWait = 3f;   //Amount of time to wait before first wave
private float waveWait = 2f;    //Amount of time to wait between spawning each wave
private float spawnWait = 0.5f; //Amount of time to wait between spawning each enemy


private float startTimer = 0f; //Keep track of time that has elapsed since game launch
private float waveTimer = 0f;  //Keep track of time that has elapsed since last wave
private float spawnTimer = 0f; //Keep track of time that has elapsed since last spawn

private List<GameObject> zombies = new List<GameObject>();

[SerializeField]
private Transform spawnPoints; //The parent object containing all of the spawn point objects


void Start()
{
    deathUI.SetActive(false);

    //Set the default number of zombies to spawn per wave
    zombieCount = 5;

    PopulateZombies();

    StartCoroutine(SpawnWaves());
}

void Update()
{
    startTimer += Time.deltaTime;
    if (startTimer >= startWait)
    {
        waveTimer += Time.deltaTime;
        spawnTimer += Time.deltaTime;
    }


    //When the player dies "pause" the game and bring up the Death screen
    if (Player.isDead == true)
    {
        Time.timeScale = 0;
        deathUI.SetActive(true);
    }

}

IEnumerator SpawnWaves()
{

    //wait 3 seconds
    yield return new WaitForSeconds(startWait);



    //then:

    while (!Player.isDead && ScoreCntl.wave < 9)
    {
        if (waveTimer >= waveWait)
        {
            IncrementWave();
            for (int i = 0; i < zombieCount; i++)
            {
                if (spawnTimer >= spawnWait)
                {
                    Vector3 spawnPosition = spawnPoints.GetChild(Random.Range(0, spawnPoints.childCount)).position;
                    Quaternion spawnRotation = Quaternion.identity;
                    GameObject created = Instantiate(zombies[0], spawnPosition, spawnRotation);
                    TransformCreated(created);

                    spawnTimer = 0f;
                }
            }

            waveTimer = 0f;
        }

        //wait until the end of frame
        yield return null;
    }
}

为了更好地理解统一协程的工作原理:

协程是返回类型为 IEnumerator 的方法,其行为类似于异步执行的代码块的集合。yield 指令分隔代码块并指定在下一个代码块开始执行之前要等待的时间帧。

有几种类型的 yield 指令:

  • null :等到帧结束(在渲染之前)
  • WaitForEndOfFrame :等待帧结束(渲染后)
  • WaitForSeconds :等待指定的秒数(包括时间刻度)
  • WaitForSecondsRealtime :等待指定的秒数(忽略时间刻度)

你可以想到

  • 作为协程更新,yield return null
  • LateUpdate 作为协程与 yield return new WaitForEndOfFrame()
  • FixedUpdate 作为协程与 yield return new WaitForSeconds(.2f)
于 2017-02-05T09:54:25.777 回答