Unity - 如何让两个相同的游戏对象发生碰撞、相互破坏并在目标位置生成一个新的游戏对象?
Unity - How to have two of the same GameObjects collide, destroy each other, and spawn a new GameObject at the target position?
所以我想让两个球碰撞,自我毁灭,然后在它们的位置产生另一个球(最好以特定的速度)。但是,当我尝试将脚本附加到球上时,球的两个实例在接触时都被破坏,然后立即产生两个球的预制件,因为它们都有代码。这会导致两个球一遍又一遍地产生并相互摧毁。我在球上附加了这个脚本:
private Vector3 ballPosition;
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Ball") {
ballPosition = new Vector3 ((transform.position.x + other.transform.position.x) / 2, (transform.position.y + other.transform.position.y) / 2, 0.0f);
StartCoroutine ("RespawnBall");
}
}
IEnumerator RespawnBall () {
Instantiate (gameObject, ballPosition, Quaternion.identity);
Destroy (gameObject);
yield return null;
}
如何让这段代码销毁两个球,然后只生成一个预制件实例?
你可以做的是创建另一个脚本,它只破坏球但不生成并将它附加到第二个球。第一个球将同时具有销毁和重生功能。所以当两个球互相摧毁时,一个会重生而另一个不会,因为你没有将重生功能附加到第二个球
您还可以在脚本中使用一个布尔值,这样只有第一个调用了 OnTriggerEnter2D()
方法的球才会产生一个新球:
private Vector3 ballPosition;
public bool spawnNewBall;
void Start() {
spawnNewBall = true;
}
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Ball") {
if (spawnNewBall) {
other.GetComponent</*YourScriptName*/>().spawnNewBall = false;
}
ballPosition = new Vector3 ((transform.position.x + other.transform.position.x) / 2, (transform.position.y + other.transform.position.y) / 2, 0.0f);
StartCoroutine ("RespawnBall");
}
}
IEnumerator RespawnBall () {
if (spawnNewBall) {
Instantiate (gameObject, ballPosition, Quaternion.identity);
}
Destroy (gameObject);
yield return null;
}
更简单,只需这样做:
public class TwoToOne : MonoBehaviour {
public bool doNothing;
void OnCollisionEnter (Collision col)
{
if (doNothing) return;
col.gameObject.GetComponent<TwoToOne>().doNothing = true;
Destroy(col.gameObject);
GameObject newCube = Instantiate(gameObject);
Destroy(gameObject);
}
}
这是 Unity 中完全常见的脚本或模式。
因此,从概念上讲,您只是 "destroy the other one";由于其中一个脚本必须首先 运行,因此效果很好。在与 Unity 不同的系统中,您可以执行以下操作之一:
A,"destroy" 其他游戏对象以某种方式。在 Unity 中就是 DestroyImmediate(col.gameObject)
。
B, "destroy" 或以某种方式禁用其他脚本。在 Unity
中是 col.gameObject.GetComponent<TwoToOne>().enabled = false
C,"flag"(设置布尔值)以某种方式在另一个游戏对象上 - 如此处所示。
碰巧,在 Unity 中你不能做 A 或 B,所以你只做 C。B 是最优雅的解决方案,但它在 Unity5 中不起作用,所以现在做 C! =15=]
您可以使用事件管理器中的 delegates OR as @JoeBlow suggested, the use of 解决此问题。
在下面的示例中,我选择使用委托。
有2个脚本。一个位于球体上(您已经拥有),第二个是静态的 class,可以从应用程序中的任何其他脚本引用它而无需实例化。
EventManager.cs
using UnityEngine;
using System.Collections;
public static class EventManager{
// Create our delegate with expected params.
// NOTE params must match SphereScript.PostCollision declaration
public delegate void CollideEvent(string message);
// Create the delegate instance. This is the one we will invoke.
public static event CollideEvent PostCollision;
// Called whenever an object has collided with another
public static void Collision(GameObject obj1, GameObject obj2, Vector3 collisionPoint){
if (obj1.GetComponent<sphereScript>().isAlive && obj2.GetComponent<sphereScript>().isAlive) {
//Kill the 2 objects which haev collided.
obj1.GetComponent<sphereScript> ().Kill ();
obj2.GetComponent<sphereScript> ().Kill ();
//Create a cube.
GameObject cube = GameObject.CreatePrimitive (PrimitiveType.Cube);
cube.transform.position = collisionPoint;
// Invoke delegate invocation list
PostCollision("Something is dead");
}
}
}
SphereScript.cs
using UnityEngine;
using System.Collections;
public class sphereScript : MonoBehaviour {
// Am I alive?
public bool isAlive;
// Use this for initialization
void Start () {
// Add a function we want to be called when the EventManager invokes PostCollision
EventManager.PostCollision += PostCollision;
isAlive = true;
}
// Update is called once per frame
void Update () {
}
//Invoked from EventManager.PostCollision delegate
void PostCollision(string message){
if(isAlive)
Debug.Log (this.name + " message received: " + message);
}
// Called when it is time to destroy this gameobject
public void Kill(){
isAlive = false;
Destroy (this.gameObject);
}
//On collision with another object
void OnCollisionEnter2D(Collision2D collision){
if (collision.gameObject.GetComponent<sphereScript>()) {
EventManager.Collision (this.gameObject, collision.gameObject, collision.contacts [0].point);
}
}
// Called after this object has been destroyed
void OnDestroy(){
// cleanup events for performance.
EventManager.PostCollision -= PostCollision;
}
}
那为什么要用这个方法呢?
此方法非常适合控制从单个位置释放多个 'happenings'。在这种情况下,我们在每个碰撞球的每个球上触发一个函数。然后 EventManager 在它们的尾迹中创建一个单独的立方体,然后通知所有剩余的立方体在场景的其他地方发生了碰撞。
所以我想让两个球碰撞,自我毁灭,然后在它们的位置产生另一个球(最好以特定的速度)。但是,当我尝试将脚本附加到球上时,球的两个实例在接触时都被破坏,然后立即产生两个球的预制件,因为它们都有代码。这会导致两个球一遍又一遍地产生并相互摧毁。我在球上附加了这个脚本:
private Vector3 ballPosition;
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Ball") {
ballPosition = new Vector3 ((transform.position.x + other.transform.position.x) / 2, (transform.position.y + other.transform.position.y) / 2, 0.0f);
StartCoroutine ("RespawnBall");
}
}
IEnumerator RespawnBall () {
Instantiate (gameObject, ballPosition, Quaternion.identity);
Destroy (gameObject);
yield return null;
}
如何让这段代码销毁两个球,然后只生成一个预制件实例?
你可以做的是创建另一个脚本,它只破坏球但不生成并将它附加到第二个球。第一个球将同时具有销毁和重生功能。所以当两个球互相摧毁时,一个会重生而另一个不会,因为你没有将重生功能附加到第二个球
您还可以在脚本中使用一个布尔值,这样只有第一个调用了 OnTriggerEnter2D()
方法的球才会产生一个新球:
private Vector3 ballPosition;
public bool spawnNewBall;
void Start() {
spawnNewBall = true;
}
void OnTriggerEnter2D (Collider2D other) {
if (other.gameObject.tag == "Ball") {
if (spawnNewBall) {
other.GetComponent</*YourScriptName*/>().spawnNewBall = false;
}
ballPosition = new Vector3 ((transform.position.x + other.transform.position.x) / 2, (transform.position.y + other.transform.position.y) / 2, 0.0f);
StartCoroutine ("RespawnBall");
}
}
IEnumerator RespawnBall () {
if (spawnNewBall) {
Instantiate (gameObject, ballPosition, Quaternion.identity);
}
Destroy (gameObject);
yield return null;
}
更简单,只需这样做:
public class TwoToOne : MonoBehaviour {
public bool doNothing;
void OnCollisionEnter (Collision col)
{
if (doNothing) return;
col.gameObject.GetComponent<TwoToOne>().doNothing = true;
Destroy(col.gameObject);
GameObject newCube = Instantiate(gameObject);
Destroy(gameObject);
}
}
这是 Unity 中完全常见的脚本或模式。
因此,从概念上讲,您只是 "destroy the other one";由于其中一个脚本必须首先 运行,因此效果很好。在与 Unity 不同的系统中,您可以执行以下操作之一:
A,"destroy" 其他游戏对象以某种方式。在 Unity 中就是
DestroyImmediate(col.gameObject)
。B, "destroy" 或以某种方式禁用其他脚本。在 Unity
中是 C,"flag"(设置布尔值)以某种方式在另一个游戏对象上 - 如此处所示。
col.gameObject.GetComponent<TwoToOne>().enabled = false
碰巧,在 Unity 中你不能做 A 或 B,所以你只做 C。B 是最优雅的解决方案,但它在 Unity5 中不起作用,所以现在做 C! =15=]
您可以使用事件管理器中的 delegates OR as @JoeBlow suggested, the use of
在下面的示例中,我选择使用委托。
有2个脚本。一个位于球体上(您已经拥有),第二个是静态的 class,可以从应用程序中的任何其他脚本引用它而无需实例化。
EventManager.cs
using UnityEngine;
using System.Collections;
public static class EventManager{
// Create our delegate with expected params.
// NOTE params must match SphereScript.PostCollision declaration
public delegate void CollideEvent(string message);
// Create the delegate instance. This is the one we will invoke.
public static event CollideEvent PostCollision;
// Called whenever an object has collided with another
public static void Collision(GameObject obj1, GameObject obj2, Vector3 collisionPoint){
if (obj1.GetComponent<sphereScript>().isAlive && obj2.GetComponent<sphereScript>().isAlive) {
//Kill the 2 objects which haev collided.
obj1.GetComponent<sphereScript> ().Kill ();
obj2.GetComponent<sphereScript> ().Kill ();
//Create a cube.
GameObject cube = GameObject.CreatePrimitive (PrimitiveType.Cube);
cube.transform.position = collisionPoint;
// Invoke delegate invocation list
PostCollision("Something is dead");
}
}
}
SphereScript.cs
using UnityEngine;
using System.Collections;
public class sphereScript : MonoBehaviour {
// Am I alive?
public bool isAlive;
// Use this for initialization
void Start () {
// Add a function we want to be called when the EventManager invokes PostCollision
EventManager.PostCollision += PostCollision;
isAlive = true;
}
// Update is called once per frame
void Update () {
}
//Invoked from EventManager.PostCollision delegate
void PostCollision(string message){
if(isAlive)
Debug.Log (this.name + " message received: " + message);
}
// Called when it is time to destroy this gameobject
public void Kill(){
isAlive = false;
Destroy (this.gameObject);
}
//On collision with another object
void OnCollisionEnter2D(Collision2D collision){
if (collision.gameObject.GetComponent<sphereScript>()) {
EventManager.Collision (this.gameObject, collision.gameObject, collision.contacts [0].point);
}
}
// Called after this object has been destroyed
void OnDestroy(){
// cleanup events for performance.
EventManager.PostCollision -= PostCollision;
}
}
那为什么要用这个方法呢?
此方法非常适合控制从单个位置释放多个 'happenings'。在这种情况下,我们在每个碰撞球的每个球上触发一个函数。然后 EventManager 在它们的尾迹中创建一个单独的立方体,然后通知所有剩余的立方体在场景的其他地方发生了碰撞。