为什么这个敌人的spawn方法导致Unity崩溃?

时间:2017-02-05 07:49:15

标签: c# loops unity3d spawning

我已经在我的Unity 2D项目的主要Gameplay.cs脚本中研究了这部分代码了很长一段时间,无论我改变它并改变它的方式,它要么导致Unity锁定在播放或经过一些调整之后,它将立即从Wave 0转到Wave 9.这是我最初的尝试,这会导致Unity在Play上冻结。我不明白为什么会发生这种情况,并希望了解我的逻辑出错的地方。

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

  • 在游戏中,游戏等待startWait秒。
  • 在startWait之后,游戏进入SpawnWaves()方法并等待 waveWait每个波之间的秒数,并增加波数 每次浪潮中都有一次。
  • 每一波都会产生zombieCount数量的敌人并等待每个僵尸之间产卵等待几秒钟。


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; } } }

我是初学者并且理解我的代码可能不遵循最佳实践。我还有一个使用Coroutine和产量回报的工作敌人产卵者,但我想让这个版本的我的敌人产卵。

2 个答案:

答案 0 :(得分:2)

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

Update()函数通常每秒调用几次,在你的代码中,SpawnWaves()应该在每次Update调用后执行一次startTimer&gt; = 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&gt; = waveWait

    内部的所有代码都将立即执行。 因此,如果它可以使用IncrementWave(),它将在毫秒内完成九次。然后,虽然是假的,但内部的代码再也没有执行过。

  • 案例:waveTimer&lt; 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
    }

答案 1 :(得分:1)

使用协程来调用spawn wave方法:

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指令将代码块分开,并指定在下一个代码块开始执行之前等待的时间量。

有几种类型的良率指令:

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

你可以想到

  • 以yield return null
  • 更新为协程
  • LateUpdate as coroutine with yield return new WaitForEndOfFrame()
  • FixedUpdate as coroutine with yield return new WaitForSeconds(.2f)