簡體   English   中英

銷毀所有游戲對象但繼續生成新游戲對象 - Unity3D C#

[英]Destroy all GameObjects but keep spawning new ones - Unity3D C#

當我的宇宙飛船死亡時,我將它移動到屏幕頂部的一個新的隨機 position。 當我的宇宙飛船死亡時,我想摧毀所有流星體,但不斷產生新的流星體。

當我循環並摧毀所有流星體時,不會產生新的流星體。

我正在嘗試復制我在 2015 年創建的以下 Scratch 游戲: Homesick Cody

一些建議是在刪除最后一個之前計算有多少流星並產生一個。 我對如何做到這一點有點迷茫。 也許有更好的方法。

流星體.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);
        }
    }

}
  1. 似乎您正試圖在任何碰撞中摧毀流星體。 如果您還可以檢查流星體的碰撞是否與實際的宇宙飛船相撞,那就太好了。 現在可能不需要這樣做,但如果任何 2 個流星體相互碰撞,或者與您稍后可能添加的其他游戲對象碰撞,它可能會適得其反。

假設你的飛船有一個標簽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 class 現在似乎是一個不錯的選擇:

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 中編寫 static 方法,該方法將銷毀MeteoroidSpawn.cs體並在碰撞時從 Meteoroid.cs 調用它(請記住您還必須制作List<Meteoroid> static )。

這樣的代碼可能看起來像這樣( 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# 事件來完成這樣的輕量級工作:

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事件。 現在每個流星都有辦法讓 Spawner 知道它已經擊中了玩家。 如果發生這種情況,則會引發一個事件並調用DestroyAllMeteoroids()方法,從而銷毀該過程中的所有流星體。 不需要 static 方法或變量。 也沒有必要取消訂閱該事件,因為您正在破壞游戲 object 發生碰撞的同一幀。

如果您需要進一步解釋代碼,請告訴我。

更新

在您使用實際代碼編輯問題后,很明顯這一行

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

導致新生成的meteoroid是前一個流星體的克隆的問題。

所以當然,一旦你銷毀最后一個實例化的meteoroid ,它就會變成一個無效的 object,它不能再用作Instantiate的預制件。

我認為您根本沒有理由需要更新該字段

=>

Instantiate(meteoroid, spawnPos, Quaternion.identity);

編輯問題之前的原件

不是您的錯誤來自何處的答案,但我會簡單地跟蹤所有實例,例如將它們放在特定的父 object 下:

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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM