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



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

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

我正在尝试复制我在2015年创建的以下Scratch游戏:思乡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);
}
}
}
  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#事件来完成这样的轻松工作:

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()方法,在这个过程中摧毁所有流星体。不需要静态方法或变量。也没有必要取消订阅该事件,因为你在碰撞发生的同一帧破坏游戏对象。

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

更新

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

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

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

因此,当然,一旦您销毁了最后一个实例化的实例,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();

最新更新