代码之家  ›  专栏  ›  技术社区  ›  Harley

摧毁所有游戏对象,但不断产生新的游戏对象-Unity3DC#

  •  0
  • Harley  · 技术社区  · 2 年前

    当我的宇宙飞船死亡时,我会将其移动到屏幕顶部的一个新的随机位置。当我的宇宙飞船死了,我想摧毁所有的流星体,但不断产生新的流星体。

    当我环绕并摧毁所有的流星体时,没有新的流星体产生。

    我正在尝试复制我在2015年创建的以下Scratch游戏: Homesick Cody

    一些建议是,在删除最后一个流星体之前,先统计有多少流星体并产生一个。我有点不知所措。也许还有更好的方法。

    Meteoroid.cs

    using System.Collections.Generic;
    using UnityEngine;
    
    public class Meteoroid : MonoBehaviour
    {
        [SerializeField] 
        private GameObject spaceship;
    
        [SerializeField] 
        private float speed = 1.0f;
    
        [SerializeField] 
        private Rigidbody2D rb;
    
        // Start is called before the first frame update
        void Start()
        {
            rb = this.GetComponent<Rigidbody2D>();
        }
    
        // Update is called once per frame
        void Update()
        {
            transform.position = Vector2.MoveTowards(transform.position, spaceship.transform.position, speed * Time.deltaTime);
            transform.up = spaceship.transform.position - transform.position;
        }
    
        private void OnCollisionEnter2D(Collision2D collision)
        {
            foreach (var meteoroid in GameObject.FindGameObjectsWithTag("meteoroid"))
            {
                Destroy(meteoroid);
            }
        }
    
    }
    

    MeteoroidSpawn.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class MeteoroidSpawn : MonoBehaviour
    {
    
        // Start is called before the first frame update
        void Start()
        {
            StartCoroutine(SpawnRoutine());
        }
    
        // Update is called once per frame
        void Update()
        {
    
        }
    
        [SerializeField] 
        private GameObject meteoroid; //prefab
    
        [SerializeField]
        private bool spawn = true;
    
        IEnumerator SpawnRoutine()
        {
            Vector2 spawnPos = Vector2.zero;
            
            while (spawn == true)
            {
                yield return new WaitForSeconds(1f);
    
                // Spawn meteoroid
                spawnPos.x = Random.Range(-Screen.width/40, Screen.width/40);
                spawnPos.y = -Screen.height/40;
                meteoroid = Instantiate(meteoroid, spawnPos, Quaternion.identity);
            }
        }
    
    }
    
    0 回复  |  直到 2 年前
        1
  •  1
  •   crsmn    2 年前
    1. 似乎你正试图在任何碰撞中摧毁流星体。如果你也能检查一下流星体与实际宇宙飞船的碰撞,那就太好了。这现在可能没有必要,但如果任何两个流星体相互碰撞,或者与您稍后可能添加的其他游戏对象碰撞,可能会适得其反。

    假设你的飞船有标签 Player :

    private void OnCollisionEnter2D(Collision2D collision)
        {
            if (collision.gameObject.CompareTag("Player")
            {
                Debug.Log("collision");
                Destroy(gameObject);  // I want to destroy all meteoroids when spaceship dies
            }
        }
    
    1. 我想你是在摧毁离开游戏区域的流星体,这样它们就不会浪费资源。

    2. 你们的流星体不会一次全部被摧毁,因为每一个都有自己的例子 Meteoroid 脚本,每个脚本将分别运行自己的 OnCollisionEnter2D() 因此,如果流星体击中玩家,它只检查自己和另一个对撞机之间的碰撞。

    如果你想一次摧毁所有的流星体,你必须以某种方式存储它们,然后在任何流星体之间发生碰撞时,调用某种方法,可以访问所有存储的流星体并一次摧毁它们。 你的 MeteoroidSpawn 现在上课似乎是一个很好的地方:

    public class MeteoroidSpawn : MonoBehaviour
    {
        private List<Meteoroid> meteoroids; // Stores all currently spawned Meteoroids.
    
        [SerializeField] 
        private GameObject meteoroidPrefab;
    
        private bool spawn = true;
    
        private void Start()
        {
            meteoroids = new List<Meteoroid>();  // Create and initialize the list.
            StartCoroutine(SpawnRoutine());
        }
    
        IEnumerator SpawnRoutine()
        {
            Vector2 spawnPos = Vector2.zero;
            
            while (spawn == true)
            {
                yield return new WaitForSeconds(1f);
    
                // Spawn meteoroid
                spawnPos.x = Random.Range(-Screen.width/40, Screen.width/40);
                spawnPos.y = -Screen.height/40;
                var meteor = Instantiate(meteoroidPrefab, spawnPos, Quaternion.identity);
                
                // Add new meteoroid to the list.
                meteoroids.Add(meteor.GetComponent<Meteoroid>());
            }
        }
    }
    

    现在你已经参考了所有繁殖的流星体,你可以用任何你喜欢的方式摧毁它们,即在 MeteoroidSpawn.cs 它会摧毁流星体,并在碰撞时称之为Meteoroid.cs(记住,你也必须 使 List<Meteoroid> 静止的 ).

    这样的代码可能看起来像这样( MeteoroidSpawn.cs ):

    public static void DestroyAllMeteoroids()
    {
        foreach (var meteoroid in meteoroids)
            Destroy(meteoroid.gameObject);
    }
    

    可以这样称呼( Meteoroid.cs ):

    private void OnCollisionEnter2D(Collision2D collision)
        {
            if (collision.gameObject.CompareTag("Player")
            {
                Debug.Log("collision");
                MeteoroidSpawn.DestroyAllMeoroids();
            }
        }
    
    事件方法

    另一种方法是使用事件。Unity提供了自己的事件系统( UnityEvent )但我个人更喜欢用普通的 C# events 对于像这样轻的工作:

    Meteoroid.cs :

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Meteoroid : MonoBehaviour
    {
        public event EventHandler OnSpaceshipHit;
    
        [SerializeField] 
        private GameObject spaceship;
    
        [SerializeField] 
        private float speed = 1.0f;
    
        [SerializeField] 
        private Rigidbody2D rb;
    
        // Start is called before the first frame update
        void Start()
        {
            rb = this.GetComponent<Rigidbody2D>();
        }
    
        // Update is called once per frame
        void Update()
        {
            transform.position = Vector2.MoveTowards(transform.position, spaceship.transform.position, speed * Time.deltaTime);
            transform.up = spaceship.transform.position - transform.position;
        }
    
        private void OnCollisionEnter2D(Collision2D collision)
        {
            if (collision.gameObject.CompareTag("Player")
            {
                Debug.Log("collision");
                OnSpaceshipHit?.Invoke(this, null); // Raise event.
            }
        }
    
    }
    

    在里面 MeteoroidSpawn.cs :

    private List<Meteoroid> meteoroids; // Stores all currently spawned Meteoroids.
    
    private void Start()
    {
        meteoroids = new List<Meteoroid>();  // Create and initialize the list.
        StartCoroutine(SpawnRoutine());
    }
        
    // Rest of the code.
    
    IEnumerator SpawnRoutine()
    {
        Vector2 spawnPos = Vector2.zero;
        
        while (spawn == true)
        {
            yield return new WaitForSeconds(1f);
    
            // Spawn meteoroid
            spawnPos.x = Random.Range(-Screen.width/40, Screen.width/40);
            spawnPos.y = -Screen.height/40;
            Meteoroid meteor = Instantiate(meteoroidPrefab, spawnPos, Quaternion.identity).GetComponent<Meteoroid>();
            meteor.OnSpaceshipHit += DestroyAllMeteoroids; // Subscribe to the event.
                
            // Add new meteoroid to the list.
            meteoroids.Add(meteor);
        }
    }
    
    private void DestroyAllMeteoroids(object sender, EventArgs e)
    {
        foreach (var meteoroid in meteoroids)
            Destroy(meteoroid.gameObject);
    }
    

    使用此设置,您可以订阅 OnSpaceshipHit 每个新流星体的事件。现在,每个流星体都有办法让房主知道它击中了玩家。如果发生这种情况,将引发一个事件,并且 DestroyAllMeteoroids() 方法被称为,在这个过程中摧毁所有流星体。不需要静态方法或变量。也没有必要取消订阅该事件,因为你在碰撞发生的同一帧破坏游戏对象。

    如果您需要进一步解释代码,请告诉我。

        2
  •  0
  •   derHugo    2 年前

    使现代化

    在你用敏锐的代码编辑了你的问题后,很明显这一行

    meteoroid = Instantiate(meteoroid, spawnPos, Quaternion.identity);
    

    导致新产生的问题 meteoroid 是前一个的克隆。

    当然,只要你销毁了最后一个实例化的实例 流星体 成为无效对象,不能用作的预制件 Instantiate 不再

    我看不出你有什么理由需要更新字段

    =>

    Instantiate(meteoroid, spawnPos, Quaternion.identity);
    

    问题编辑前的原件

    这不是你的错误来源的答案,但我只想跟踪所有实例,例如将它们放在特定的父对象下:

    Instantiate(meteoroid, spawnPos, Quaternion.identity, transform);
    

    那么你可以简单地使用这样的方法

    public void DestroAllCurrenMeteroids()
    {
        foreach(Transform child in transform)
        {
            Destroy(child.gameObject);
        }
    }
    

    作为一种更奇特的选择,你可以在一个集合中的类型本身中进行,例如。

    public class Meteoroid : MonoBehaviour
    {
        private static readonly HashSet<Meteoroid> instances = new ();
    
        ...
    
        private void Awake()
        {
            instances.Add(this);
        }
    
        private void OnDestroy()
        {
            instances.Remove(this);
        }
    
        public static void DestroyAllInstances()
        {
            foreach(var instance in instances)
            {
                Destroy(instance.gameObject);
            }
        }
    }
    

    所以你可以从任何地方打电话

    Meteoroid.DestroyAllInstances();