statement after waitforseconds() not working in unity - c#

I m new to unity and c# as well. I want to delay a function hence using WaitForSeconds() but my problem is that the statement after WaitForSeconds() is not executed and hence there is no delay shown.
Below is the code:
public void GameOver(){
StartCoroutine (Load ());
Debug.Log("loadDelay");
}
IEnumerator Load(){
Debug.Log ("enum");
yield return new WaitForSeconds(3);
Debug.Log("waited");
}
the output on console shows:
enum
loadDelay
At the same time without any delay when the GameOver() is called and Debug.Log("waited");
is not executed at all. I really don't understand the problem. Please explain if I'm doing something wrong.
Thanks
Code where GameOver() is called:
public class Collision : MonoBehaviour {
public GameObject explosion;
BgScroll bg;
void Start () {
bg = GetComponent<BgScroll> ();
}
// Update is called once per frame
void Update () {
}
void OnCollisionEnter2D(Collision2D col){
if (col.gameObject.tag == "Enemy") {
//Debug.Log("destroyed");
explosion.renderer.sortingLayerName = "foreground";
Instantiate (explosion, transform.position, Quaternion.identity);
Destroy (gameObject);
Debug.Log("destroyed");
bg.GameOver();
}
}
}

According to Unity's Documentation for WaitForSeconds you are doing it right. and your code works good when i tried to run it. There must be some other problem, or you might have other logs before "waited". I got output in console window as
enum
loadDelay
waited
and obviously "waited" will be printed after 3 seconds if your player is running at that time.

Related

MissingReferenceException after destroying an object using a coroutine in Unity

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.

How can I fix "The object of type 'GameObject' has been destroyed but you are still trying to access it"

I'm creating the platform falling effect by unchecking the isKinematic but I keep getting the error :
"MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Here is my code:
// Update is called once per frame
void Update()
{
}
private void OnCollisionExit(Collision collision){
if(collision.gameObject.tag == "Player")
{
Fall();
Invoke("Fall", 0.2f); //delay 0.2 s chu y dau va viet thuong
}
}
void Fall(){
GetComponent<Rigidbody>().isKinematic = false;
Destroy(gameObject,1f);
}
}
Here is the error in unity
Can anyone know how to fix this problem? Thank you.
The flow of your current code is as following:
Called Fall() once (After 1 second from that call the gameObject will be destroyed).
You are using Invoke to call Fall() after 0.2 seconds while you have already a task to destroy the gameObject from the first call.
Update: I assumed that you want to set kinematic false immediately. I updated the piece of code :)
Code
using UnityEngine;
public class Q69205071 : MonoBehaviour
{
private void OnCollisionExit(Collision collision)
{
if (collision.gameObject.tag == "Player")
{
//Fall(1f);
/// since you want everything to be executed after 0.2 seconds
/// you could use Invoke or coroutines.
Invoke("Fall", 0.2f);
}
}
private void Fall()
{
GetComponent<Rigidbody>().isKinematic = false;
Destroy(gameObject, 1f); // 1 seconds delay before destroying the gameObject
}
}
Happy Coding :)

C# - Unity - audio when two objects collide, not working :(

I've got a problem. I am creating a small game project where where a ball escapes from ghosts.
I am trying to write a script that plays "evil laughter" when the ball collides with a ghost.
When i added the audio upon collision code into the script, the other function which gets the player to the "Game over"-scene stops working
Does anyone know what the problem might be?
Thanks a lot in advance! (code below) <3
public class GhostScript : MonoBehaviour
public AudioSource ghostCollision; //this is the collision sound reference
public GameObject target; //this is the player or a reference for him
UnityEngine.AI.NavMeshAgent agent;
// Start is called before the first frame update
void Start()
{
ghostCollision = GetComponent<AudioSource>();
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
if (target == null) {
target = GameObject.FindGameObjectWithTag("Player");
}
}
// Update is called once per frame
void Update()
{
agent.destination = target.transform.position;
}
public void OnCollisionEnter(Collision collision){
if (collision.gameObject.tag == "Player"){
ghostCollision.Play();
SceneManager.LoadScene("menu");
}
}
That's because as soon as you call SceneManager.LoadScene the current scene (which contains your Player, AI and AudioSource) gets lost and replaced with the new Scene. Adding a delay to the scene load should do the trick, you can use Invoke for that (or Coroutines if you're more advanced).
private void OnCollisionEnter(Collision other)
{
if (other.gameObject.tag == "Player")
{
ghostCollision.Play();
// Invokes the function after the AudioClip is over
Invoke("LoadScene", ghostCollision.clip.length);
}
}
private void LoadScene()
{
SceneManager.LoadScene("your-scene-name");
}

How to add wait time between death and level reset. c#

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");
}

Trouble with WaitForSeconds() in Unity

I am trying to invoke a shooting animation in the Update function and then wait for 0.5 seconds before spawning a laser shot. The below code isn't working for me. What can I do to achieve the desired result?
void Update()
{
if (Input.GetMouseButtonDown (0))
{
animator.SetTrigger("Shoot"); // Start animation
WaitAndShoot();
}
}
IEnumerator WaitAndShoot()
{
yield return new WaitForSeconds(0.5f);
Instantiate (shot, shotSpawn.transform.position,shotSpawn.transform.rotation);
}
You're forgetting to call it as a coroutine using StartCoroutine().
It should be:
void Update()
{
if (Input.GetMouseButtonDown (0))
{
animator.SetTrigger("Shoot"); // Start animation
StartCoroutine(WaitAndShoot());
}
}
IEnumerator WaitAndShoot()
{
yield return new WaitForSeconds(0.5f);
Instantiate (shot, shotSpawn.transform.position,shotSpawn.transform.rotation);
}
Keep in mind that this still allows you to trigger multiple shots before the first shot has been spawned. If you want to prevent that from happening, keep track of a shot being fired or not with a boolean, and check for that in addition to your GetMouseButtonDown.

Categories

Resources