Hey all. I added a NavMesh to my game and for some reason a couple of coroutines from one of my scripts stopped working. I have no idea why. I didn’t touch the script nor the object it’s attached to. Even when I cleared the NavMesh, the coroutines still didn’t work.
In a nutshell, the object drops when the player hits a trigger on the floor, killing the player. The object then resets its position after a few seconds. The trap drops just fine, but then just stays there. Nothing in the coroutines is run. Here are the relevant scripts:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrapDrop : MonoBehaviour {
Vector3 spawnPoint;
Rigidbody rb;
public Vector3 drop;
MeshRenderer meshRenderer;
PlayerMovement playerMovement;
void Start ()
{
rb = GetComponent<Rigidbody>();
drop = new Vector3(0, -800 , 0);
spawnPoint = transform.position;
meshRenderer = GetComponent<MeshRenderer>();
playerMovement = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerMovement>();
}
private void OnCollisionEnter(Collision collision)
{
if (collision.collider.tag == "Player")
{
StartCoroutine(WaitTwo());
StartCoroutine(Wait());
}
}
public IEnumerator WaitTwo()
{
if(playerMovement.isDead)
{
yield return new WaitForSeconds(1);
meshRenderer.enabled = false;
}
}
public IEnumerator Wait()
{
if (playerMovement.isDead)
{
yield return new WaitForSeconds(2);
transform.position = spawnPoint;
rb.isKinematic = true;
meshRenderer.enabled = true;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour {
public float moveSpeed;
float maxSpeed = 5;
Rigidbody rb;
Vector3 input;
public Vector3 spawnPoint;
public GameObject deathParticles;
MeshRenderer meshRenderer;
Enemy enemy;
public bool isDead = false;
public bool enemyTrigger = false;
void Start ()
{
rb = GetComponent<Rigidbody>();
spawnPoint = transform.position;
meshRenderer = GetComponent<MeshRenderer>();
enemy = GameObject.FindGameObjectWithTag("Enemy").GetComponent<Enemy>();
}
void FixedUpdate ()
{
input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
if(rb.velocity.magnitude < maxSpeed)
{
rb.AddForce(input * moveSpeed, ForceMode.Acceleration);
}
FallDeath();
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Enemy")
{
Die();
}
if (other.tag == "EnemyTrigger")
{
enemyTrigger = true;
}
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Trap")
{
Die();
}
}
public void Die()
{
Instantiate(deathParticles, transform.position, Quaternion.identity);
meshRenderer.enabled = false;
rb.isKinematic = true;
isDead = true;
transform.position = spawnPoint;
enemyTrigger = false;
enemy.enemyMesh.enabled = false;
StartCoroutine (Wait());
}
void FallDeath()
{
if (transform.position.y <= 0.9f)
{
Die();
}
}
public IEnumerator Wait()
{
if (isDead)
{
yield return new WaitForSeconds(2);
rb.isKinematic = false;
isDead = false;
meshRenderer.enabled = true;
enemy.enemyMesh.enabled = true;
}
}
}
Can you see anything in the above scripts that might be causing the problem?
so what does the ‘drop’ variable do? I never see it used.
It’s kinda funny that your WaitTwo waits 1 second, and your Wait waits 2 seconds haha.
Have you debugged the collision? Is it being called on either the player or the trap?
Do you get any debug log statements there, above even the tag check…?
The drop is called in another script for the object that triggers the drop (I’m not the most efficient coder yet!). That works fine. The trap drops. All the other objects reset their positions when the player dies, but the trap just stays put.
I think the collision is being called on both. In the player script, it executes the Die() function. In the TrapDrop script, it executes the coroutines. It all worked before I created a NavMesh.
I’m embarrassed to say I don’t know how to go about debugging.
You just wrote that it executes the coroutines, but how do you know that? From Debug.Log?
I wonder if it’s possible that this script gets the collision message first, and that means the player “isDead” is not true, at the time it goes to the coroutine.
You could try:
yield return new WaitForEndOfFrame();
Put that just above the check for is the player is dead in your trap drop coroutines (or at least 1 to check).
The only reason I know it runs (or used to run them, at least) the coroutines is because it did it fine before. However, it doesn’t do it any more. I checked settings on the inspector to see if any lines code were being executed, but none are.
That line didn’t work, unfortunately. I also tried increasing the Wait Coroutine in the Player Movement script, to make sure isDead was true for longer. Didn’t work.
Ahh okay, and that is not where I meant, okay.
So, remove the isDead from the collision area…
then re-write the coroutines like so (I’ll just do 1 to show you):
public IEnumerator WaitTwo()
{
yield return new WaitForEndOfFrame();
if(playerMovement.isDead == true)
{
yield return new WaitForSeconds(1);
meshRenderer.enabled = false;
}
}
No, I wouldn’t say so necessarily, about your waitfor seconds. Because that sounds like you want to wait to do other stuff (not check if the player is dead).
What happened is that the trap got the collision message first, before the player, so the player didn’t know it was dead, yet. Now, the trap is checking “hey, is the player dead”, but their script is running later in the same frame, and it reports “no”. Yielding until the end of the frame lets you wait out the rest of the scripts to run…
All this being said, and it’s good it’s working. If it were me, and I had a trap that was going to kill the player. Maybe what I would do , instead, is check if I’ve hit the player, then I’d check if they’re dead (similar to you), and if they’re not dead, I’d kill them myself lol Maybe make the trap being the killer. Rather than the player dying. Not really sure one way is better than the other, however in my example you would be sure of that at the time…
You’re an absolute gem, sir. Thanks for all the help. Can’t tell you how much I appreciate it.
The same thing from last night is bugging me, though. Both of those problems didn’t exist before the NavMesh was created. That is to say, those mechanics worked fine with those lines of code. No idea why they suddenly stopped working.
Sure, well don’t feel too spooked about it. Sometimes, when you compare a Vector3 for equality, you can get lucky and it will equate to true. As I was telling you yesterday, though, it’s better to always do a “close to” check, instead, because that will always work. Remembering and practicing that will ensure you don’t get caught in bad spots
As for why this might have worked before, it’s possible that you added scripts, or moved something around in the game (to be honest, I’m not sure how Unity decides which scripts go first). Perhaps, in physics, it’s just a coincidence which one gets it, perhaps there are rules, perhaps it’s related to Unity scripts’ order… so on , so on… beyond my knowledge/scope of this question But essentially, you were “lucky” it worked before, too 50/50 lol
Now, it will always work, like the position check, too.