如何生成游戏对象,然后在它们停止时重新生成它们

How to generate GameObjects, then respawn them when they are stopped

我正在编写一个在随机位置实例化云(我创建的预制件)的系统。当它们生成时,它们会通过 Vector2.MoveTowards 移动到一个地点。我想做的是检查他们是否停止了。如果他们停止了,创建新的。

我的系统:
脚本 1(程序云(我知道它们不是程序创建的,但我开始将脚本命名为这个,因为这是我想要在长 运行 中实现的目标)):

脚本 2 (MoveClouds):

这是我开始挣扎的地方。我首先尝试使用 .transform.position = Vector3.MoveTowards()。但我这里的问题是检查云是否已经停止。我试过 hasChanged,但没有用。因此,经过大量谷歌搜索并尝试使其工作后,我决定将 Rigidbody2D 附加到云中,因为我认为我可以检查 velocity.magnitude == 0,然后它们就会停止。我还应用了 rb.velocity 和 rb.addforce,因为有实际的力量来检查那个力量是否已经停止并且应该产生一个新的力量。我不想使用 RB,因为它们不会与任何物理相关,它们只是从一个随机点移动到另一个特定点,然后蒸发,当它们蒸发时,会产生一个新的。

我知道它很长 post,但我非常感谢您的帮助!我已经在 Unity 中工作了 3 个多月,所以我对此还是很陌生。

我的代码: 脚本 1

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ProceduralClouds : MonoBehaviour 
{
    public GameObject[] clouds;
    Vector3 startPos1, startPos2, startPos3, startPos4, startPos5, startPos6;
    [HideInInspector] public GameObject cloud1, cloud2, cloud3, cloud4, cloud5, cloud6;
    public bool cloud1Spawned, cloud2Spawned, cloud3Spawned, cloud4Spawned, cloud5Spawned, cloud6Spawned;

    private void Start()
    {
        
        /*InvokeRepeating(nameof(SpawnCloud1), 0, 20); 
        InvokeRepeating(nameof(SpawnCloud2), 1, 22); 
        InvokeRepeating(nameof(SpawnCloud3), 3, 25); 
        InvokeRepeating(nameof(SpawnCloud4), 0, 18); 
        InvokeRepeating(nameof(SpawnCloud5), 2, 20); 
        InvokeRepeating(nameof(SpawnCloud6), 5, 21); */
    }

    private void Update()
    {
        startPos1 = new Vector3(Random.Range(15, 25), Random.Range(0, 10), 0);
        startPos2 = new Vector3(Random.Range(15, 25), Random.Range(0, 10), 0);
        startPos3 = new Vector3(Random.Range(15, 25), Random.Range(0, 10), 0);
        startPos4 = new Vector3(Random.Range(15, 25), Random.Range(0, 10), 0);
        startPos5 = new Vector3(Random.Range(15, 25), Random.Range(0, 10), 0);
        startPos6 = new Vector3(Random.Range(15, 25), Random.Range(0, 10), 0);

        // test prints:

        print("Cloud 1 spawned: " + cloud1Spawned);

        SpawnCloud1();
        SpawnCloud2();
        SpawnCloud3();
        SpawnCloud4();
        SpawnCloud5();
        SpawnCloud6();
        
    }

    private void SpawnCloud1()
    {
        
        if (!cloud1Spawned)
        {
            cloud1 = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos1, Quaternion.identity);
            cloud1Spawned = true;
        } 
    }

    private void SpawnCloud2()
    {
        
        if (!cloud2Spawned)
        {
            cloud2Spawned = true;
            cloud2 = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos2, Quaternion.identity);
        }
    }

    private void SpawnCloud3()
    {
        
        if (!cloud3Spawned)
        {
            cloud3 = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos3, Quaternion.identity);
            cloud3Spawned = true;
        }
    }

    private void SpawnCloud4()
    {
        
        if (!cloud4Spawned)
        {
            cloud4 = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos4, Quaternion.identity);
            cloud4Spawned = true;
        }
    }

    private void SpawnCloud5()
    {
        
        if (!cloud5Spawned)
        {
            cloud5 = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos5, Quaternion.identity);
            cloud5Spawned = true;
        }
    }

    private void SpawnCloud6()
    {
        
        if (!cloud6Spawned)
        {
            cloud6 = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos6, Quaternion.identity);
            cloud6Spawned = true;
        }
    }
}

脚本 2:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveClouds : MonoBehaviour
{
    public float step;
    private ProceduralClouds proCloudsScript;
    private GameObject cloud1, cloud2, cloud3, cloud4, cloud5, cloud6;
    private Vector3 endPosBottom, endPosMiddle, endPosTop;
    private void Start()
    {
        proCloudsScript = GameObject.Find("ScriptHandler").GetComponent<ProceduralClouds>();
        endPosBottom = new Vector3(-26, 8, 0);
        endPosMiddle = new Vector3(-26, 10, 0);
        endPosTop = new Vector3(-26, 12, 0);
    }

    private void Update()
    {
        cloud1 = proCloudsScript.cloud1;
        cloud2 = proCloudsScript.cloud2;
        cloud3 = proCloudsScript.cloud3;
        cloud4 = proCloudsScript.cloud4;
        cloud5 = proCloudsScript.cloud5;
        cloud6 = proCloudsScript.cloud6;

        float step = this.step * Time.deltaTime;
       /* cloud1.transform.position = Vector3.MoveTowards(cloud1.transform.position, endPosBottom, step);
        cloud2.transform.position = Vector3.MoveTowards(cloud2.transform.position, endPosTop, step);
        cloud3.transform.position = Vector3.MoveTowards(cloud3.transform.position, endPosMiddle, step);
        cloud4.transform.position = Vector3.MoveTowards(cloud4.transform.position, endPosTop, step);
        cloud5.transform.position = Vector3.MoveTowards(cloud5.transform.position, endPosBottom, step);
        cloud6.transform.position = Vector3.MoveTowards(cloud6.transform.position, endPosMiddle, step);*/

        cloud1.GetComponent<Rigidbody2D>().velocity = Vector2.MoveTowards(cloud1.transform.position, 
        endPosBottom, step);
        cloud2.GetComponent<Rigidbody2D>().velocity = Vector2.MoveTowards(cloud2.transform.position, 
        endPosMiddle, step);
        cloud3.GetComponent<Rigidbody2D>().velocity = Vector2.MoveTowards(cloud3.transform.position, 
        endPosTop, step);
        cloud4.GetComponent<Rigidbody2D>().velocity = Vector2.MoveTowards(cloud4.transform.position, 
        endPosTop, step);
        cloud5.GetComponent<Rigidbody2D>().velocity = Vector2.MoveTowards(cloud5.transform.position, 
        endPosBottom, step);
        cloud6.GetComponent<Rigidbody2D>().velocity = Vector2.MoveTowards(cloud6.transform.position, 
        endPosMiddle, step);



        if (cloud1.GetComponent<Rigidbody2D>().velocity.magnitude == 0 )
        {
            print("Cloud 1 is still");
            proCloudsScript.cloud1Spawned = false;
            print(cloud1.GetComponent<Rigidbody2D>().velocity.magnitude);
        }

        if (cloud2.GetComponent<Rigidbody2D>().velocity.magnitude == 0)
        {
            print("Cloud 2 is still");
            proCloudsScript.cloud2Spawned = false;
            print(cloud2.GetComponent<Rigidbody2D>().velocity.magnitude);
        }

        if (cloud3.GetComponent<Rigidbody2D>().velocity.magnitude == 0)
        {
            print("Cloud 3 is still");
            proCloudsScript.cloud3Spawned = false;
            print(cloud3.GetComponent<Rigidbody2D>().velocity.magnitude);
        }

        if (cloud4.GetComponent<Rigidbody2D>().velocity.magnitude == 0)
        {
            print("Cloud 4 is still");
            proCloudsScript.cloud4Spawned = false;
            print(cloud4.GetComponent<Rigidbody2D>().velocity.magnitude);
        }

        if (cloud5.GetComponent<Rigidbody2D>().velocity.magnitude == 0)
        {
            print("Cloud 5 is still");
            proCloudsScript.cloud5Spawned = false;
            print(cloud5.GetComponent<Rigidbody2D>().velocity.magnitude);
        }

        if (cloud6.GetComponent<Rigidbody2D>().velocity.magnitude == 0)
        {
            print("Cloud 6 is still");
            proCloudsScript.cloud6Spawned = false;
            print(cloud6.GetComponent<Rigidbody2D>().velocity.magnitude);
        }

    }
}

我在你的代码中看到了很多小问题!

  • 我会从 Vector3 的用法开始.. 如果你真的想要 Vector2 然后使用它 ;)

  • 一遍又一遍地实现相同的功能只是为了有不同的数字,这真的很糟糕!尝试将所有这些合并到一个方法中。这是 a) 更好的维护和 b) 可扩展性!

  • 您正在传递使用

    生成的位置
    Vector2.MoveTowards(cloud1.transform.position, endPosBottom, step);
    

    速度 -> 没有意义。

  • 尽管它在新版本中得到了很大改进,但多次使用 GetComponent over ad 并且 every frame 效率极低。

  • 每帧生成新的随机位置,然后在大多数情况下将它们扔掉,因为如果云仍在移动,您就不会生成它,这又是非常效率低下。

所以我会这样做

// Responsible for creating and initializing cloud instances
public class ProceduralClouds : MonoBehaviour 
{
    // As before here assign your prefabs
    // CloudMove is a dedicated script that will go onto your cloud prefabs
    // See next code snippet
    public CloudMove[] clouds;
    // How many clouds should be spawned?
    public int amountOfClouds = 6;

    // Configure your possible target position in the Inspector
    public Vector2[] targetPositions = { new Vector3(-26, 8, 0), new Vector3(-26, 10, 0), new Vector3(-26, 12, 0) };

    // This will keep track which target position chose next
    // (could make it random as well)
    private int currentTargetIndex;     

    private void Start()
    {
        // Spawn the first "wave" of clouds
        for(var i = 0; i < amountOfClouds; i++)
        {
            SpawnCloud();
        }
    }

    private void SpawnCloud()
    {
        // pick a random start position
        var startPos = new Vector2(Random.Range(15, 25), Random.Range(0, 10));
        // get the next target position
        var targetPos = targetPositions [currentTargetIndex];
        // Increase the index, wrap around if reaching end of array
        currentTargetIndex = (currentTargetIndex + 1) % targetPositions.Length;

        // instantiate the cloud
        var cloud = Instantiate(clouds[Random.Range(0, clouds.Length)], startPos, Quaternion.identity);
        // assign its target position
        // Could also pick a random speed here
        cloud.targetPosition = targetPos;
        // Assign a callback to an event we will implement
        // This is way better than poll check stuff in Update
        cloud.OnReachedTarget += SpawnCloud;
    }
}

然后另一个脚本直接在您的云预制件上:

// Responsible for moving one cloud instance
public class CloudMove : MonoBehaviour
{
    // Basically equals your step
    // But this way the clouds could even have individual speed values
    public float speed = 1;

    // Will be assigned by the spawner script
    public Vector2 targetPosition;
    // Whoever wants can be listening here
    public event Action OnReachedTarget;

    private void Update ()
    {
        // Move towards the target position with given speed 
        transform.position = Vector2.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);

        // Either use this check with a precision of 0.00001
        if((Vector2)transform.position == targetPosition)
        // Or alternatively if you need a more exact precision
        //if(Mathf.Approximately(Vector2.Distance(transform.position, targetPosition), 0))
        {
            // Invoke the event so whoever is listening is informed
            OnReachedTarget?.Invoke();

            // You might be interested in destroying this cloud once it reached its target
            //Destroy(gameObject):
        }
    }
}