If a game object has exited a trigger, shouldn’t OnTriggerStay() be not executed since it has been confirmed the game object is no longer in the trigger at that exact game update?
I happened to come across a problem where I am dependent on OnTriggerExit and OnTriggerStay in my project, leading to this interesting quirk to appear. I don’t know if it’s a bug or it’s intentional…
I’m very sure it fired just after my game object left the trigger. I even tried stepping through the updates on both the editors and in Visual Studio 2015. I marked breakpoints everywhere, and found a consistent flow of execution from OnTriggerExit() to OnTriggerStay().
Can you simplify this down to the simplest code possible and recreate?
I just test using this simple code in 5.2.1f1:
using UnityEngine;
using System.Collections;
public class TriggerTest : MonoBehaviour {
void OnTriggerEnter()
{
Debug.Log("ON ENTER");
}
void OnTriggerStay()
{
Debug.Log("ON STAY");
}
void OnTriggerExit()
{
Debug.Log("ON EXIT");
}
}
Worked as expected.
If the simplest code still fails to run as expected on the latest version, log a bug.
If it works, start building it up to the code that you have adding a piece by piece, and narrow down what causes the issue.
Note, I have witnessed when an exception occurs in my code, unity will sometimes jump to a strange part of the code. It’s usually because I have a bug in my logic resulting in a strange loop of dependencies. It’s like unity just says “oh, screw this crazy jumble of nonsense… what’s the next part of the update loop that makes sense? lets go there.”
I am on 5.2.1f1, just like you. Except, what I have is networking instead of MonoBehaviour. Maybe it might be issues with synchronization. I decided to try doing a workaround to actually get the synchronization of network codes work together on MonoBehaviours.
Here’s a full sample code showing the workaround that I used:
using UnityEngine;
using System.Collections.Generic;
public class LineOfSight : MonoBehaviour {
public List<GameUnit> enemiesInRange;
public List<GameUnit> removeList;
public List<GameUnit> exitedList;
public float radius;
public GameUnit parent;
public void Start() {
this.enemiesInRange = new List<GameUnit>();
this.removeList = new List<GameUnit>();
this.exitedList = new List<GameUnit>();
SphereCollider collider = this.GetComponent<SphereCollider>();
if (collider != null) {
this.radius = collider.radius;
}
this.parent = this.GetComponentInParent<GameUnit>();
if (parent == null) {
Debug.LogError("There's something wrong with this parent GameUnit object.");
}
}
//To use OnTrigger...() methods, you need to attach this script with a game object that have a Collider and a RigidBody.
//And remember to untick (uncheck) the "Use Gravity" in RigidBody.
public void OnTriggerEnter(Collider other) {
GameUnit unit = other.GetComponent<GameUnit>();
GameUnit myself = this.GetComponentInParent<GameUnit>();
if (unit != null && myself != null && (unit != myself) && unit.CheckIfVisible() && !unit.hasAuthority && !this.enemiesInRange.Contains(unit)) {
this.enemiesInRange.Add(unit);
}
if (this.exitedList.Count > 0) {
this.exitedList.Clear();
}
}
public void OnTriggerExit(Collider other) {
GameUnit unit = other.GetComponent<GameUnit>();
GameUnit myself = this.GetComponentInParent<GameUnit>();
if (unit != null) {
if ((myself != null && (unit != myself) && !unit.hasAuthority && this.enemiesInRange.Contains(unit)) || (!unit.CheckIfVisible() && this.enemiesInRange.Contains(unit))) {
this.enemiesInRange.Remove(unit);
this.exitedList.Add(unit);
}
}
}
public void OnTriggerStay(Collider other) {
GameUnit unit = other.GetComponentInParent<GameUnit>();
if (unit != null && !unit.hasAuthority && unit.CheckIfVisible() && !(unit.Equals(parent)) && !this.enemiesInRange.Contains(unit) && !this.exitedList.Contains(unit)) {
this.enemiesInRange.Add(unit);
}
}
public void FixedUpdate() {
if (this.enemiesInRange.Count > 0) {
foreach (GameUnit unit in this.enemiesInRange) {
if (unit == null) {
this.removeList.Add(unit);
}
}
}
if (this.removeList.Count > 0) {
foreach (GameUnit unit in this.removeList) {
if (this.enemiesInRange.Contains(unit)) {
this.enemiesInRange.Remove(unit);
}
}
this.removeList.Clear();
}
}
public bool Recheck() {
bool result = false;
Collider[] objs = Physics.OverlapSphere(this.transform.position, this.radius / 2f);
foreach (Collider col in objs) {
GameUnit unit = col.gameObject.GetComponentInParent<GameUnit>();
if (unit != null && !unit.Equals(this.parent) && !unit.hasAuthority && !this.enemiesInRange.Contains(unit)) {
this.enemiesInRange.Add(unit);
result = true;
}
}
return result;
}
}
Highlights are the OnTrigger…() event methods, and two additional lists. RemoveList maintains the primary list, EnemiesInRange, of all detected enemies in a trigger. ExitedList maintains all game objects that have left the trigger, and makes sure OnTriggerStay() doesn’t execute on game objects already exited from the trigger.
It’s plausible that NetworkBehaviours and MonoBehaviours don’t go along with each other, since the trigger is attached to a network non-player object prefab, and the MonoBehaviour, LineOfSight, is dependent on the timings from the server (timings for NetworkTransform and the correct position in a network game).
I don’t know, but I did report this to Unity. (Case 735901)