I kinda stuck with my script right now. When a gameObject, with my script attached to it, has a trigger event with a specific gameObject, I want to destroy the specific gameObject after an amount of time.
So i came to this:
void OnTriggerEnter ( Collider other) {
if (other.gameObject.tag == "leaf1"){
StartCoroutine (LeafDestruction ());
}
}
IEnumerator LeafDestruction(){
yield return new WaitForSeconds (5);
Destroy (gameObject);
}
I know it's a noob mistake but i think i miss something, because when i run this script, it destroys the gameObject with the script attached to it, and not the specific gameObject(with tags).
How can i fix that?
A simpler solution is to use the optional 2nd parameter of the Destroy method:
The object obj is destroyed immediately after the current Update loop, or t seconds from now if a time is specified.
Given the official parameters:
Parameters
obj
The object to destroy.
t
The optional amount of time to delay before destroying the object.
You can adjust your if statement to:
if (other.gameObject.tag == "leaf1")
Destroy(other.gameObject, 5.0f);
Basically you need to tell your coroutine that it should destroy the other.gameObject and not the gameObject that is running this script.
So what you could do is add a parameter to your coroutine, passing in the gameObject that it should really be destroyed:
void OnTriggerEnter ( Collider other) {
if (other.gameObject.tag == "leaf1")
{
IEnumerator coroutine = LeafDestruction(other.gameObject);
StartCoroutine (coroutine);
}
}
IEnumerator LeafDestruction(GameObject toDestroy){
yield return new WaitForSeconds (5);
Destroy (toDestroy);
}
You destroyed the object instead of the leaf. The gameObject is an alias of this.gameObject, which is the game object this script component is attached to. Note MonoBehaviour inherits from Behaviour and Behaviour inherits from Component.
GameObject leafObject;
void OnTriggerEnter ( Collider other) {
if (other.gameObject.tag == "leaf1"){
leafObject = other.gameObject;
StartCoroutine (LeafDestruction ());
}
}
IEnumerator LeafDestruction(){
yield return new WaitForSeconds (5);
Destroy (leafObject);
}
Related
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.layer == layer)
{
StopAllCoroutines();
Destroy(gameObject, 1f);
}
}
//Coroutine is called from another script
public IEnumerator MoveRb(Vector3 destination)
{
yield return new WaitUntil(() => rb.velocity == Vector3.zero);
destination.y = rb.position.y;
rb.velocity = transform.right;
//this is where i get an error
yield return new WaitUntil(() => Vector3.Distance(rb.position, destination) < 0.1f);
rb.velocity = Vector3.zero;
}
Basically, getting "MissingReferenceException" when trying to destroy an object while running coroutine. Delay of 1 second, nor replacing "WaitUntil" with while loop and "yield return null" doesn't fix this issue. The only place where object gets destroyed is inside of "OnCollisionEnter" inside of the same gameObject script. What am I missing here?
Full exception message:
MissingReferenceException: The object of type 'Rigidbody' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
As you posted on the other answer you actually run this routine from another script
public class ConveyourBelt : MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
if (other.gameObject.TryGetComponent(out MovingPart movingPart))
{
// runs this Coroutine on THIS component
StartCoroutine(movingPart.MoveRb(transform.position + transform.right));
}
}
}
The issue here is that this ConveyourBelt component is running the Coroutine, not the MovingPart component attached to the other object
=> The call to
StopAllCoroutines();
in the MovingPart component has no effect at all since it is never running that routine!
So when you destroy the object after 1 second the routine could still be running on the ConveyourBelt component.
As a solution you should rather make the routine running on the other component like
public class ConveyourBelt : MonoBehaviour
{
void OnCollisionEnter(Collision other)
{
if (other.gameObject.TryGetComponent<MovingPart>(out var movingPart))
{
// rather runs the Coroutine on the movingPart component
movingPart.StartCoroutine(movingPart.MoveRb(transform.position + transform.right));
}
}
}
The code should work in theory. I think the problem is you are trying to access the coroutine again during this 1 second destroy delay. This results in the coroutine running while the gameobject is destroyed.
If you want this 1s delay then either make sure that the coroutine can't get called again after you have collided with something or the easier way is to make the coroutine error proof. Means you check if the rigidbody is null before you use it.
Would like to know if it is possible to destroy an object if not in collision with anything?
When I start the game I generate a grid randomly, on each GridSquare I've made a collider2D. In the editor, I add some objects on this grid manually, with a collider2D too and a rigidbody2D to detect the collision.
I know how to destroy the object if in a collision, what I want is "when the game start, if this object is not in collision with anything, then destroy it"
I've tried to tell unity "if in collision with grid square then destroy object (just for testing), else destroy object"
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "GridSquare")
{
Destroy(gameObject);
}
else {
Destroy(gameObject);
}
}
So actually it destroys well the object when it is on a GridSquare, and it should destroy object if it is not in collision with GridSquare too but apparently not...
private var collisionCount = 0;
void OnCollisionEnter () {
collisionCount++
}
void OnCollisionExit () {
collisionCount--;
}
void checkForCollision(){
if(0 == collisionCount) Destroy(gameObject);
}
If the gameObject is not colliding, OnTriggerEnter2D method wont be activated for that specific gameObject. Bear in mind that method only triggers on collision event.
For that case you might have in your gameObjects a boolean _isColliding which you can change with the OnTriggerEnter2D method to check the ones that are colliding, setting that to true.
bool _isColliding = false;
void OnTriggerEnter2D(Collider2D collision)
{
_isColliding = true;
}
Then you loop through all of the gameObjects and destroy only the ones that have the _isColliding == false.
Hope that helps!
I am new to Unity and C#, I am trying to add a 'wait time' to my death script. So when my Player dies it shows a particle animation and then resets the level, however, at the moment my particles are playing but the level doesn't reset.
public GameObject particles;
public Player_Movement player;
void OnCollisionEnter2D(Collision2D Col)
{
if (Col.collider.tag == "Enemy")
{
Instantiate(particles, transform.position, Quaternion.identity);
Destroy(gameObject);
StartCoroutine("RestartGameCo");
}
}
public IEnumerator RestartGameCo()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene("Level1");
}
Destroy(gameObject);
StartCoroutine("RestartGameCo");
Your code is fine, but you destroy the gameobject that has this script on it. Which also destroys the script, and stops all the coroutines. So it will never be called.
A solution is to make the object invisible in some way, like disabling the mesh renderer and collider instead of destroying it.
You are actually destroying the gameobject itself before even running the co-routine. It means that the co-routine "RestartGameCo" won't even run. One way to debug these kind of things is using Debug.log messages.
Code:
void OnCollisionEnter2D(Collision2D Col)
{
if (Col.collider.tag == "Enemy")
{
Instantiate(particles, transform.position, Quaternion.identity);
StartCoroutine("RestartGameCo");
}
}
public IEnumerator RestartGameCo()
{
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene("Level1");
}
Let me know if it helps.
I know a couple options about that. Here's one.
By using the Thread, it will pause your application for the amount of time of your desire. To do so, you need to add using System.Threading; to your the current cs file you are editing. Now simply change your code to that:
void OnCollisionEnter2D(Collision2D Col)
{
Thread.Sleep(5000) // 5000 in milliseconds
if (Col.collider.tag == "Enemy")
{
Instantiate(particles, transform.position, Quaternion.identity);
Destroy(gameObject);
StartCoroutine("RestartGameCo");
}
}
public IEnumerator RestartGameCo()
{
Thread.Sleep(5000)
yield return new WaitForSeconds(0.5f);
SceneManager.LoadScene("Level1");
}
Already 2 days I'm trying to solve this problem but I can't.I'm trying to delete the unlimited spawning barriers consistently when they are collide with the boundary.The barriers are made from 5 cubes.Any ideas?
public List<GameObject> spawning=new List<GameObject>();
public Vector3[] positions = new Vector3[5];
public GameObject barrier;
public GameObject boundary;
void Start()
{
StartCoroutine (SpawnBarrier());
}
void Update()
{
if(true)
{
foreach (GameObject move in spawning)
move.transform.Translate (0f, 0f, -0.1f);
}
}
IEnumerator SpawnBarrier(){
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
yield return new WaitForSeconds (3f);
}
}
Your barriers are marked as isTrigger on their colliders therefore you can use OnTriggerEnter to detect when any of the barriers collides with the boundary.
You need to create new script, lets call that BoundaryDetector and attach it to the barrier prefab so that every instance of the barrier will have this script attached to it.
When OnTriggerEnter is called, check if the trigger is made by the boundary. This can be done by checking for the "Player" tag since boundary is tagged as Player in your screenshot. If the detected trigger tag is "Player", first remove the spawning from the spawning List then Destroy it.
The BoundaryDetector script is as below (Must be attached to the barrier prefab):
ScriptFromYourQuestion yourInstancingSript;
void Start()
{
GameObject obj = GameObject.Find("NameOfObjectScriptInYourQuestionIsAttachedTo");
yourInstancingSript = obj.GetComponent<ScriptFromYourQuestion>();
}
void OnTriggerEnter(Collider other)
{
//Detect if we collided with the boundary
if (other.CompareTag("Player"))
{
//Remove Self/barrier from the List
yourInstancingSript.spawning.Remove(this.gameObject);
//Delete Self/barrier
Destroy(this.gameObject);
}
}
Note: The ScriptFromYourQuestion should be replaced with the name of the script in your question.
MUST DO:
To get the code above working, the following changes must be made in the Update function of the script from your question:
1.You must attach Rigidbody to at-least one of the Objects (boundary or barrier). In this case, I suggest you do so to the barrier prefab.
2.You must remove move.transform.Translate (0f, 0f, -0.1f); and replace it with Rigidbody.MovePosition and use that to move your barries because you have now attached Rigidbody to your barries and this is the proper way to move a Rigidbody Object.
Maybe something like this:
Your Update function in the code form your quesion should looks something like this:
public float speed = 100.0f;
void Update()
{
if (true)
{
foreach (GameObject move in spawning)
{
//Get Rigidbody component
Rigidbody rb = move.GetComponent<Rigidbody>();
//Calculate Z-axis pos to move to
Vector3 pos = new Vector3(0, 0, 1);
pos = pos.normalized * speed * Time.deltaTime;
//Move with Rigidbody
rb.MovePosition(rb.transform.position + pos);
}
}
}
Your problem is that you have an infinite loop while true that you're not breaking out of. Instead you should add a counter. You haven't explained what you want to do but my example is that it will breakout once it completes 10 instantiations of 5 blocks.
IEnumerator SpawnBarrier(){
spawnCount=0
yield return new WaitForSeconds (3f);
while (true) {
for(int i=0;i<=4;i++)
{
spawning.Add (Instantiate (barrier, positions [i], Quaternion.identity)as GameObject);
}
if (++spawnCount==10)
{
break;
}
yield return new WaitForSeconds (3f);
}
}
If I understood corrently, you want to destroy barriers that collided with some kind of boundary?
If the 'boundary' is the visible screen space
In this case, we can assume that, once the object is invisible to MainCamera it can be counted as out of the boundary. Keeping that in mind, we can use OnBecameInvisible() method, which is method (or message, as stated in Unity Scripting API) called by MonoBehaviour class:
// A piece of barrier that gets destroyed once out of screen
public sealed class BarrierPiece : MonoBehaviour
{
// Called once the object is no longer visible to any (scene editor included) camera
private void OnBecameInvisible()
{
Destroy(gameObject);
}
}
NOTE: if you have more than one Camera in your scene, the object must be invisible to all of the cameras in order for the OnBecameInvisible() to be called.
If the 'boundary' is other object
In this case, there are many approaches based on your game:
Create GameObject with Rigidbody component and add a script which would have OnTriggerEnter(...) method and destroy colliding barriers in it.
(Inefficient) have two static Vector2 variables, one for pivotPosition, other for maximumOffset (this one should be squared for faster calculations). Then, have script on each of the barriers and in Update() checks whether the object's squared distance to the pivotPosition is greater than maximumOffset and call Destroy(gameObject) if the condition is true.
(others) ...
Those are two that came up quickly in my mind.
Hope this helps!
EDIT
Didn't look at the images...
So for your solution, you would need to assign Rigidbody component to the Boundary and add BoxCollider component on all of the barriers. Now, create a tag, called Barrier and tag all the barriers with it. Create a script:
// Destroys barriers on collision
public sealed class BarrierBoundary : MonoBehaviour
{
// Called once the script is created
// Checks if the object has Rigidbody component attached
private void Awake()
{
Debug.Assert(GetComponent<Rigidbody>() != null);
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Barrier")
Destroy(collision.collider.gameObject);
}
}
(The code wasn't tested, so it can have some typos.)
Now, assign the script to the Boundary. And voila! Barriers are destroyed once hitting the boundary.
Hope (again) this helps!
I have a BonusController script as a component of a Bonus gameObject. This Bonus must be destroyed on collision but has to "act" for a few seconds. I have started a coroutine for this, but the script stops its execution (I think it's because I set the gameObject inactive). Here is the code of BonusController:
void OnTriggerEnter2D(Collider2D col)
{
StartCoroutine(speedUp(1));
gameObject.SetActive(false);
}
IEnumerator speedUp(float seconds)
{
Debug.Log("before");
yield return new WaitForSeconds(seconds);
Debug.Log("after"); // <--- This is never called
}
How can I remove the object and don't stop the coroutine script execution?
Can't you just disable the mesh renderer and collider? This way the gameobject will still exists, but the user won't be able to see it.
You cannot pull the ground on which you are standing. :)
Just disable the SpriteRenderer as you are using 2D methods. And keep the object alive and enable.
{
StartCoroutine(speedUp(1));
//gameObject.SetActive (false);
GetComponent<SpriteRenderer> ().enabled = false;
GetComponent<Collider2D> ().enabled = false;
// Above line will deactivate the first collider it will find.
}
IEnumerator speedUp(float seconds)
{
Debug.Log("before");
yield return new WaitForSeconds(seconds);
Debug.Log("after"); // <--- This is never called
}
In order to destroy the object after some time, call destroy inside the coroutine after the yield.
yield return WaitForSeconds(second);
Destroy(this);
Now it is properly destroyed and will free up memory opposed to still being there, invisible, but taking up resources.