OnTriggerStay not functioning properly

I’ve got a strange problem using OnTriggerStay in a script I have attached to an object that contains a MeshCollider and Kinematic Rigidbody.

I have some scene geometry that has an attached compound collider (made of various BoxColliders with IsTrigger set to true and no ridigbody attached), and I just want to determine when my main object is currently colliding with the compound trigger.

The problem is that OnTriggerStay is only being called WHILE I am moving the object. As soon as I stop, even if I am obviously still colliding with the triggers, OnTriggerExit is called.

The documentation says that OnTriggerStay is called every frame as long as the collision is still occuring, but I’m not seeing that to be the case while the object is stationary. Does this only apply to moving objects? If so, is there any way to get around that?

Cheers,
Josiah

Hmmm…after building and running the project, I’m finding that this behavior is not always consistent. Sometimes it finds a collision while at rest, and sometimes it doesn’t (it only consistently DOESN’T while testing in the Unity player window).

Maybe there’s an issue with using compound colliders? I know for sure that the colliders themselves are all set to be triggers, and they’re definitely covering all the required area to approximate my level geometry…the individual colliders intersect with each other, but that’s the nature of compound colliders…

Here’s the trigger code I’m using to test this on my main object/character:

function OnTriggerStay() {

	isColliding = true;
}

function OnTriggerExit() {

	isColliding = false;
}

function OnGUI() {

	if (isColliding) {
		GUI.Label(Rect(10,10,100,20), "Collision!");
	}	
}

Please let me know if it’s at all unclear what I’m trying to accomplish.

What I’m ultimately trying to do is to move an imported mesh around the game scene (in this case, a cargo bay), and determine whether or not a collision is occurring. Due to the lack of ability to use a MeshCollider with the standard Character Controller, I’m going to have to restrict movement myself based on collision with the level geometry. Because I need to use a MeshCollider for my main object (which is a dynamically imported/converted OBJ mesh, so it can basically be anything as long as it stays within the poly limits), the level geometry (cargo bay) has to be composed of various primitive colliders (which is fine, as it’s a one-time setup).

Cheers,
Josiah

Sounds like a problem with the object going to sleep. If you call WakeUp() on it every frame, that should fix it.

–Eric

Thanks Eric, I’ll try that out when I get back (I’m away from my Unity box at the moment). I’m also wondering if it has something to do with screwed up normals on my imported mesh. I’m just applying a basic Diffuse material with a color (no texture image) to the object, and some of it is light, while the other portions are dark…please forgive the extreme noobyness. :wink: Now when I run the app I’m noticing that the collision doesn’t register on the dark portion of the model, but does register on the light portions:

Maybe I need to clean up some geometry…

Cheers,
Josiah
[/img]

I can’t quite tell from the pics, but make sure all the walls and such are modeled with two sides. Collisions don’t happen if you go through the “back” side of polygons.

–Eric

Thanks Eric…the mesh SHOULD be completely enclosed (it was box-modeled in Blender), but I’ll check into it. I’ve notice that I have missing faces sometimes when exporting a OBJ file from Blender…I’m not sure if it’s an export setting or something in the actually modeling that I’m getting wrong.

~Josiah

Actually, I’m not sure if the missing face issue is due to the Blender export or the Unity import…I’ll have to check into that when I get back.

Well, it doesn’t seem to be a problem with bad normals or geometry. The dark areas just have to do with the lighting setup. I double-checked the normals in a couple different packages and everything checks out. The mesh doesn’t seem to have any bad geometry.

Thinking it may just be the rigidbody falling asleep, I added the following code to the gameObject containing the rigidbody:

function Update() {

	gameObject.rigidbody.WakeUp();
	Debug.Log(gameObject.rigidbody.IsSleeping());
}

According to the log, it is still sleeping even though I called WakeUp(). Am I missing something else? Does the fact that I’m using a Kinematic Rigidbody factor into this? Does the WakeUp() function not apply in this situation? As long as I am moving the object it properly calculates the collision, so maybe this is a moot point. I’ll probably have to just restrict motion accordingly for the collisions while the user is moving the model, and then back it out slightly after the user let’s go of the mouse button. That way there will never be a collision to handle while the object is at rest.

I’m really wishing the Character Controller simply had a mesh collider option right about now.

Cheers,
Josiah

Yeah; kinematic rigidbodies are always sleeping basically.

–Eric

Okay, I’ve modified how I’m going about this problem, and I need a second (or many) pair of eyes to help me figure out what’s going wrong here. The way I decided to handle it was to have two separate imported bodies…one with the actual geometry being rendered, and a “ghost” mesh with the Rigidbody and MeshCollider attached. I’ve created a script called GhostObjectManager that is very simple:

using UnityEngine;
using System.Collections;

public class GhostObjectManager : MonoBehaviour {

	public bool triggerHandled = false;
	
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

	void OnTriggerEnter() {

		if (!triggerHandled) {
			
			triggerHandled = true;
		}
	}
}

All I’m interested in here is determining when the MeshCollider enters one of the level geometry triggers. This script is attached to the “Test2Ghost” object.

My other main script is attached to the rendered geometry “Test2” that I’ve imported. The ghost object is the same geometry. Now, in order to detect collisions and not allow movement of the main geometry unless there will be no resulting collision I’ve attached the following ObjectMovementManager script to the main “Test2” object:

using UnityEngine;
using System.Collections;

public class ObjectMovementManager : MonoBehaviour {

	public string translationAxis = "";
	public bool isTranslating = false;
	private float translationSpeed = 40.0f;	
	private GameObject importedObject;
	private GameObject importedGhostObject;	
	private GhostObjectManager importedGhostObjectManager;
	
	//
	// Used for initialization
	//
	void Start () {
		
		importedObject = GameObject.Find("Test2");
		importedGhostObject = GameObject.Find("Test2Ghost");		
		importedGhostObjectManager = importedGhostObject.GetComponent(typeof(GhostObjectManager)) as GhostObjectManager;	
	}
	
	//
	// Used to reset the ghost object position and rotation
	//
	public void ResetGhostObjectTransform() {
	
		importedGhostObject.transform.position = new Vector3(importedObject.transform.position.x, 
			importedObject.transform.position.y, importedObject.transform.position.z);	
			
		importedGhostObjectManager.triggerHandled = false;	
	}
	
	//
	// Check for ghost object collision
	//
	bool CheckForGhostObjectCollision(Vector3 translation) {
	
		importedGhostObject.transform.Translate(translation);
		
		if (importedGhostObjectManager.triggerHandled) {
			
			return true;
		}
		else {
		
			return false;
		}
	}
	
	//
	// Used for smoothing the translation value
	//
	private float InterpolatedTranslation {
	
		get {
			
			float interpolatedValue = Mathf.SmoothStep(0f, Mathf.Clamp((Time.deltaTime * translationSpeed * Input.GetAxis("Mouse Y")), 
				-5.0f, 5.0f), 0.2f);
			
			return interpolatedValue;
		}
	}
	
	//
	// Apply translation every fixed update
	//
	void FixedUpdate () {
				
		Vector3 objectTranslation = Vector3.zero;
		bool willCollide = false;
		
		if (isTranslating  Mathf.Abs(InterpolatedTranslation) > 0.0f) {
			
			switch (translationAxis) {
				
				case "X" :
					
					objectTranslation.x = InterpolatedTranslation;
					willCollide = CheckForGhostObjectCollision(objectTranslation);
										
					if (!willCollide) {
						
						transform.Translate(-InterpolatedTranslation, 0f, 0f);
						importedObject.transform.Translate(InterpolatedTranslation, 0f, 0f);	
						break;
					}
					else {
					
						ResetGhostObjectTransform();
						break;
					}
					break;
				
				case "Y" :
										
					objectTranslation.y = InterpolatedTranslation;
					willCollide = CheckForGhostObjectCollision(objectTranslation);
					
					if (!willCollide) {
					
						transform.Translate(0, InterpolatedTranslation, 0);
						importedObject.transform.Translate(0, InterpolatedTranslation, 0);
						break;
					}
					else {
				
						ResetGhostObjectTransform();
						break;
					}
				
				case "Z" :
			
					objectTranslation.z = -InterpolatedTranslation;
					willCollide = CheckForGhostObjectCollision(objectTranslation);
				
					if (!willCollide) {
						
						transform.Translate(0, 0, InterpolatedTranslation);
						importedObject.transform.Translate(0, 0, -InterpolatedTranslation);	
						break;
					}
					else {
					
						ResetGhostObjectTransform();
						break;
					}
				
				default :
					break;			
			}
		}	
	}
}

This doesn’t seem to be working…every time I call ResetGhostObjectTransform() it seems to screw up the MeshCollider trigger. That is, I’m getting inconsistent OnTriggerEnter() “triggerHandled” values which results in being able to translate the geometry right through the scene geometry. I feel like I’m just missing something stupid here. Can someone take a look at this code and let me know if you see anything wrong with it? There is one other script which just sets isTranslating to “true” when the user drags the mouse after selecting an axis object, but I don’t think the problem is there (pretty simple script).

Thanks in advance for any help! I’m driving myself crazy looking at this code.

Cheers,
Josiah

I’m looking at this again this morning, and I’m still baffled why this doesn’t work. If I monitor the triggerHandled state in the Inspector while I’m moving the object through level geometry, I see the state flashing on and off constantly.

It seems like the FIRST time the MeshCollider “ghost” geometry touches the trigger OnTriggerEnter() gets called properly. At that point I’m resetting the “ghost” geometry position to that of the non-colliding object and setting the triggerHandled flag to ‘false’. Now when the collider comes in contact with the trigger OnTriggerEnter() is not called until the geometry is some way through the trigger. It then repeats this behavior, with the collision state flashing on and off as I move the geometry through.

It seems like it’s a matter of the rigidbody not calculating the collisions quickly enough, but I thought that using FixedUpdate ensured that the rigidbody would calculate its collisions before executing the containing code. Am I wrong about this? If so, is there any way to counter this behavior?

I’ve been working for weeks trying to figure out this general problem, and I feel like the latest code is a good solution…I just don’t know why the trigger isn’t responding as expected.

On a side note, if anybody is interested in being able to use a MeshCollider-based character controller, I’ll probably be creating one using a similar method after I figure this bit out. It seems like it should work…

To make sure that the flashing collision state wasn’t somehow due to the object entering DIFFERENT collider triggers (the level geometry using a compound collider made up of various box colliders), I tried moving a portion of the object through just ONE of the collider triggers. I’m seeing the same behavior.

It reallys seems like it’s a matter of the physics calculations not keeping up with the movement…which again seems strange to me because I thought that’s what FixedUpdate was for…