[英]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);
}
}
}
假設你的飛船有一個標簽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
}
}
我假設您正在摧毀離開游戲區域的流星體,以免它們浪費資源。
您的流星體不會一次全部被摧毀,因為每個流星體都擁有自己的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.