【问题标题】:Enemy spawner makes Unity freeze on play敌人生成器使 Unity 在游戏中冻结
【发布时间】:2021-04-19 03:33:03
【问题描述】:

我正在尝试为我的游戏制作一个敌人生成器,但每当我尝试在敌人生成脚本处于活动状态的情况下玩游戏时,Unity 就会冻结。

该脚本应该基本上只允许敌人在一定时间内生成。我假设有一些无限循环,但我不知道(我对 C# 仍然相当缺乏经验)。

这段代码到底有什么问题?

EnemySpawner.cs:

{
    public GameObject enemyPrefab;
    public Transform spawner;
    public float spawnRate = 1f;
    public float nextSpawn;
    public float nextSpawnAllow = 3f;
    public float spawnAllowRate;
    public float timer = 2f;
    GameObject player;
    bool ShouldSpawnEnemy = false;
    
    // Start is called before the first frame update
    void Start()
    {
        player = GameObject.Find("Player Ship");
    }

    public void SpawnCheck()
    {
        while (!ShouldSpawnEnemy)
        {
            nextSpawnAllow -= Time.deltaTime;

        }
        
            
        ShouldSpawnEnemy = true;
        timer -= Time.deltaTime;

        do 
        {
            ShouldSpawn();
        }
        while (timer !<= 0);

        timer = spawnAllowRate/1;
        nextSpawnAllow = spawnAllowRate/1;
        ShouldSpawnEnemy = false;
    }

    public void ShouldSpawn()
    {
        if(player != null)
        {
            if(ShouldSpawnEnemy == true)
            {
                nextSpawn -= Time.deltaTime;
                if(nextSpawn <= 0)
                {
                    nextSpawn = spawnRate/1;
                    
                    GameObject enemy = Instantiate(enemyPrefab, spawner.position, spawner.rotation);
                }
            }

        }
    }




    // Update is called once per frame
    void Update()
    {   
        SpawnCheck();            
    }

    
}

团结2020.3.4f1

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    你的问题是你有一个无限循环。在每帧运行的Update() 函数中,您正在调用函数SpawnCheck()。在这个函数里面你有线条

    public void SpawnCheck()
        {
            while (!ShouldSpawnEnemy)
            {
                nextSpawnAllow -= Time.deltaTime;
    
            }
    ...
    

    当您的布尔值 ShouldSpawnEnemy 以 false 开头时,它将无限期地运行此 while 循环。我建议使用带有 StartCoroutine 而不是 Update 循环的 IEnumerator。如果您需要使用这些函数的示例,我可以发布示例 sn-p。

    编辑:这是我将如何使用 IEnumerators 来处理您的情况。我不确定这是 100% 你想要的,但这是你可以采取的方向。调整您需要的内容。

    using System.Generic;   // needed for the IEnumerator
    
    public GameObject enemyPrefab;
    public Transform spawner;
    public float spawnRate = 1f;
    public float nextSpawnAllow = 3f;
    public float timer = 2f;
    GameObject player;
    
    private void Start()
    {
        player = GameObject.Find("Player Ship");
        StartCoroutine(AttemptToSpawnEnemies());
    }
    
    public IEnumerator AttemptToSpawnEnemies()
    {
        // IEnumerators to explain very simpily (not techincally correct) run parallel to your code
        // it is NOT multithreading, but will run snippets up until a yield and will jump back in the next frame
        
        // think of them almost like an Update method that does a little work every frame
        
        // as they are functions that do not repeat but run where they left off, we can have local variables
        float currentTime = 0.0f;
        
        // keep incrementing our timer until it exceeds when we want to spawn next 
        while(currentTime <= nextSpawnAllow)
        {
            currentTime += Time.deltaTime;
            
            // we use a yield return to stop the progress here until the condition in the while() is met
            yield return null;
        }
        
        // our spawn increment period is met, now lets start spawning enemies
        // you can do this by starting a new coroutine inside of this one
        yield return StartCoroutine(SpawnEnemies());
        
        // all of our enemies are now spawned, so start the loop again to keep spawning!
        StartCoroutine(AttemptToSpawnEnemies());
    }
    
    public IEnumerator SpawnEnemies()
    {
        // how long we have been spawning enemies
        float timeToSpawnEnemies = 0.0f;
        
        // how long it has been since we last spawned an individual enemy
        float currentEnemyTimer = spawnRate;
        
        if(player != null)
        {
            // until our enemy timer exceeds the timer that allows us to spawn enemies, keep trying to spawn enemies
            while(timeToSpawnEnemies <= timer)
            {
                timeToSpawnEnemies += Time.deltaTime;
                currentEnemyTimer += Time.deltaTime;
                
                // time to spawn an enemy as the timer is now off cooldown
                if(currentEnemyTimer >= spawnRate)
                {
                    // spawn our enemy - you only need to grab the reference if you need to edit the object you are spawning
                    Instantiate(enemyPrefab, spawner.position, spawner.rotation)
                    
                    // reset the timer
                    currentEnemyTimer = 0.0f;
                }
                
                // remember to yield or face another infinite loop
                yield return null;
            }
        }
        yield return null;
    }
    

    如果您有更多问题或部分代码未按预期工作,请告诉我。它目前未经测试,仅用作帮助教授 Coroutines / IEnumerators 的手段。

    【讨论】:

    • 我想是的。您的意思是将Update 函数更改为IEnumerator 还是您在谈论SpawnCheck
    • 好吧,现在你的整个Update 正在运行SpawnCheck。我会将SpawnCheck 设为IEnumerator 并在Start() 中调用StartCoroutine。
    • 只是一个公平的警告,只是用 IEnumerator 替换 void 是行不通的。您将需要稍微更改逻辑并添加收益回报。如果您不熟悉如何设置 IEnumerator,我会查看文档或者我可以编写一个示例。我需要你解释你想要实现的目标,因为我不完全理解你的代码的方向。例如,您希望每 X 秒生成一个敌人,并在 X 秒结束后每 Y 秒生成一个新敌人。然后重复。
    • 明白了。我会写一个例子然后扔一堆cmets。如果您不熟悉 IEnumerators 而不只是提供某种 sn-p,这有点难以解释。
    • @Squiddu 让我知道是否有任何不合理或无法按您的预期工作。
    猜你喜欢
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2012-11-28
    • 1970-01-01
    • 2021-12-11
    • 1970-01-01
    相关资源
    最近更新 更多