Problems with OnCollisionEnter()... SOLVED

I moved this from Scripting to Support as Scripting may have been inappropriate…

This is destined for the iPhone and I am working in Unity iPhone, but this seems to be a general scripting issue rather than an iPhone issue.

The problem I am having is this:

I have a rigidbody player object. When the player activates their shields and hits the terrain mesh problems happen.

(Keep in mind that this is an imported mesh, not a Unity Terrain, as it’s destined for the iPhone.)

There are two problems, one related to the function ShieldsOn() and the other with the sound. They could be related.

I have been able to positively recreate the following behaviour:

If the shields go off while the player object is touching the ground, the player object becomes invulnerable as the function OnCollisionEnter() does not seem to notice that the boolean variable for the shield state has changed. This behavior persists until the shields are triggered again and the function finishes when the player object is flying rather than landed.

In more complicated terms, if the function ShieldsOn() finishes and, after the yield step, sets the variable shieldsOn to false while in contact with the terrain mesh and it’s colliders, then all subsequent collisions between the player object and the terrain mesh and colliders act as if shieldsOn = true. The only way to reset the behaviour of OnCollisionEnter() is to re-Trigger the function ShieldsOn() and make sure the player object is not contacting the terrain mesh when var shieldsOn is set to false.

I would have assumed that OnCollisionEnter() would check the variable shieldsOn : boolean; each time there was a collision. That does not seem to be the case.

The second clue is the sound effects… Tho’ I’m not sure what it’s telling me.

Let’s begin with the fact that all of these sounds have been imported as “uncompressed” and “force mono”. This is primarily because of the limitations of the iPhone. They are all sourced from .mp3 files in the finder before the Unity import.

The behaviour I’m getting with the sound is that when the shield sound is called and played in function ShieldsOn(), they play properly until impact with the terrain. The function **OnCollisionEnter()**plays the collision sound properly, but the shield sound stops playing. All of my other sounds play fine concurrently - engines, jets, etc. - but the upon OnCollisionEnter() shieldSound.clip stops.

This whole audio thing could be a red herring, but it is related to the OnCollisionEnter() event (and frankly I’d like the shield sounds to continue playing until they expire…)

Any tho’ts? Suggestions? Slaps in the face or other buffoonery? I could easily be making a blazingly stupid error, but if so, I can’t see it…

// ignoring lots of other code in this script...
// here are the relevant bits of code:


//	INITIAL VARIABLES
var armor : float;
var shieldStrength : int = 5;
var shield1 : GameObject;
var collisionModifier : float;
private var shieldsOn : boolean;
private var canShield : boolean;
private var playerStatus : PlayerStatus;

//	CLASSES
class SoundEffects {
	var clip : AudioClip;
	var volume : float = 1.0;
	var loop = false;
	var pitchVariation : float = 0.0;
}

var shieldSound : SoundEffects;

//	FUNCTIONS
function Awake () {
	canShield = true;
	shieldsOn = false;
}

function Start() {
	playerStatus = GetComponent(PlayerStatus);
	if (!playerStatus)
		Debug.Log("No link to Player Status");
}
 
function ShieldsOn () {
	if (canShield) {
		canShield = false;
		shieldsOn = true;
		shield1.active = true;
 
		if (shieldSound) {
			audio.clip = shieldSound.clip;
			audio.volume = shieldSound.volume;
			audio.loop = shieldSound.loop;
			audio.Play();
		}
 
		yield WaitForSeconds (shieldStrength);	//	 Currently 5.0 seconds
		shield1.active = false;
		shieldsOn = false;
		canShield = true;
	}
}
 
function OnCollisionEnter (collision : Collision) {
 
	//	DAMAGE CALCULATIONS
	var collisonStrength = (collisionModifier * collision.relativeVelocity.sqrMagnitude);
 
	if (!shieldsOn) {
		if (collisonStrength > armor)
			playerStatus.currentHealth -= (collisonStrength);
	}
 
	//	AUDIO
	var collisionSoundEffect : CollisionSoundEffect = collision.gameObject.GetComponent (CollisionSoundEffect);
	if (collisionSoundEffect) {
		audio.clip = collisionSoundEffect.audioClip;
		audio.volume = collisionSoundEffect.volumeModifier * collision.relativeVelocity.sqrMagnitude * specialEffects.collisionVolume;
		audio.Play ();	
	announcementC01.textMessage = "OCE-shieldsOn: " + shieldsOn + "\nOCE-Can Shield? " + canShield;	//	For Debugging
	}
}


// Behavior is: If after the yield, the ShieldsOn () function sets the variable shieldsOn = false
// when in contact with the terrain collider, on the next and subsequent impacts with that collider
// the OnCollisionEnter () function seems to fail and neither applies damage nor plays the collision sound.
// The only way to return the OnCollisonEnter behaviour to what is expected is to re-Trigger function
// ShieldsOn() and make sure it expires and sets the variable shieldsOn = flase when NOT in contact
// with the terrain collider.

SO -

I stayed up way too late last night trying to crack this one, and the only thing I can add it that after creating a counter variable and inserting a line after OnCollisonEnter():

private var collisionCount : int;

function OnCollisionEnter (collision : Collision) {
   collisionCount += 1
   //   DAMAGE CALCULATIONS
   var collisonStrength = (collisionModifier * collision.relativeVelocity.sqrMagnitude);

I discovered that as soon as the function ShieldsOn() ends and the shield colliders become inactive, no collision after that adds to collisionCount implying that collisions do not trigger OnCollisionEnter()…

Give a flag to the terrain and a flag for other objects. Don’t detect collision between the player and the terrain, since this is always true if this is a ground based game. You need another trigger or switch per say for turning on and off the shields in general, like a key stroke, a button press, some timer, etc.

on button press of S (or gui element [shield])
If flag !=terrain AND shields=off then shields = on
Although I am not sure why you have the terrain as a trigger anyway, that is usually not an object that has a trigger. You can hide scripts into the terrain or sound or what ever, but I wouldn’t put a trigger element on the terrain.

Only check collisions and triggers on players or game objects really.

Zumwalt, Thanks for the reply!

I think I may have been unclear.

The player object is primarily a flying rigidbody. Collision with the terrain is one of the hazards of the game.

Those bits of script above are from the controller script attached to the player object.

function ShieldsOn(); is called from a button press in an OnGUI(); layer.

The terrain object is a .blend file with a simple mesh imported into Unity where a sound “CollisionSoundEffect” is attached and a single texture added.

The intent of this bit of code is that when the player object collides with the terrain, and if(!shieldsOn){}, damage is applied to the player object based on the strength of the collision.

In practice: I’m crashing! Put on my shields or I’ll get hurt!

The bad behaviour is when the player activates their shields, crashes and is in contact with the terrain when the 5 second wait timer expires turning the shields off again. At that point the function OnCollisionEnter(); stops workin and does not register a collision - and thus, with no collision detected, no damage is applied to the player object. The player object is now invulnerable.

There isn’t an active trigger on the terrain. I’m trying to let the OnCollisionEnter(); take care of that.

I was/am suspicious of the yield statement in function ShieldsOn(). However, when this yield expires, all of the remaining code fires - shield1.active = false; shieldsOn = false; canShield = true. These all prove correct behaviour in the Debug.Log(); The collisionCount line continues to count collisions with the player object up until the time that the yield expires. At this point, when the remaining code fires, I get no further recorded collisions, which indicates that function OnCollisionEnter (collision : Collision) {} is never firing after that point regardless of the actual collisions I can see taking place in the game.

The usual way to “break” or “reset” this behaviour is to re-trigger the function ShieldsOn(); from the button press and make sure the player object is flying and not in contact with the terrain when it expires. At that point collisions are behaving correctly again.

With further testing I have also noted that the behaviour returns to normal after an unknown period of time - long enough that I’ve lost track of it.

I’ll keep testing…

Add another global variable for IsOnGround=true / false
If Player IsOnGround=true then deal with the event for crashed or however since this is a flying game. It is possible for the player to bounce off the ground and physics should cause this effect. If the player is on the ground for longer than your set duration then the player should be crashed or damage should continue to apply to the ship i would imagine.

New Evidence:

I have a sphere collider that is a child to the main rigidbody player object. It is normally set to “shield1.active = false”.

As part of the function ShieldsOn();, it is set to “active = true” for the duration of the wait, and then reset back to false when the function expires.

The sphere provides the game play and the visual effects needed for the shields; the player object is wrapped in a semi-transparent sphere and immune to damage.

Currently this seems to be the key to the problem. If I quote out the lines regarding the sphere:

function ShieldsOn () {
   if (canShield) {
      canShield = false;
      shieldsOn = true;
      // shield1.active = true;      // HERE!
 
      <snip>
 
      yield WaitForSeconds (shieldStrength);   //    Currently 5.0 seconds
      // shield1.active = false;     // HERE!
      shieldsOn = false;
      canShield = true;
   }
}

…then the OnCollisionEnter(); behaves normally.

It seems that function OnCollisionEnter(); loses track of what the primary object is (in this case the player object) when the shield sphere collider becomes inactive when in contact with the terrain.

I assume that shield1 is the GameObject that has the sphere collider attached to it?

I think the problem you’re having is that OnCollisionEnter() is only called on the first frame of the collision, so if the collision has already happened and the shields turn off, OnCollisionEnter won’t get called again (since it’s effectively the same collision between those two objects). You could try OnCollisionStay() since that is called every frame during the life of the collision. You’ll have to rework your code to suit but that should make the functionality your after.

It sounds like the collider doesn’t recognize the collision if something else is inside it when it become active. As I understand it, colliders work by only checking when an object comes in contact with it, not when something is inside of it.

What if you made the shield a rigidbody instead? Then it would bounce off the terrain as you’d expect and you would only need to know if the shield is on or off and the collider for the ship mesh would determine the damage done to the ship.

Now that’s thinkin’…

I hadn’t thought of that one. I’ll give that a shot. I was poised to see if instantiating and destroying the gameobject with the collider would work, but my gut feeling was that it would be the same.

[EDIT]

Ah - right. I think I’ve been down this route before…

If you make the sphere object a rigidbody, it now has a physical life of it’s own and doesn’t stay in the family tree (as it were). The rigidbody means the sphere goes off on a life of its own under it’s own physics and maths.

If only you could tame the wild beast! It probably would get a bit strange and unpredictable adding a RigidBody for only 5 seconds and then taking it out again.

I hope some of my other ramblings are of some use at least!

This helps explain the behaviour:

This at least effectively explains the apparent behaviour.

Any thoughts on how to pop a bubble shield around a ship without this problem? A collider sphere seemed so smart!

At 4.9 sec’s I could pop the ship up for the moment it turns off the shields so it wasn’t in contact with the terrain when it turns off… >_<

I’ll have to put on my thinking cap. It’s tall, white, conical and has the word DUNCE written down one side in large red letters…

[Additional]

I’m trying to think of something that checks for damage with the OnCollisionEnter(); because that just seems the correct way of doing things, but use Stay or Exit to reset something… You can’t seem to turn the collider itself on and off yet leave the gameObject active… hrm…

Hi,

I don’t know if you found your answer yet or not, but… if I understand what you’re saying, then to turn off the shield, do a 5 second count in the Stay function, then turn it off. I’m not real sure why you want to disable the collider, but simply put the collider in an empty game object, child to your shield. Then you can flip it on or off without disrupting the game object.

Hope this helps,

Galen

Galen,

Thanks for getting back to me.

No, I’ve not found a solution yet, but I’ll admit that I’ve been working on some larger problems.

Let me try to get a cross what I’m doing.

I’m going to be a bit pedantic, because often I try to be brief and I end up being confusing.

It’s very easy for me to get distracted by the collision with the terrain rather than the functionality of the shields.

This means that being in contact with a collider when the shields turn off is not a common occurrence.

But when it does happen, it bugs the game.

The way I’ve worked it so far is that the ship has a child object with Unity sphere mesh and physics mesh collider attached (actually 2 - counter rotating).

These are toggled on and off by two functions activated by a button press, and there is a timer in function update to clock the use and turn it off when the timer expires:

function ShieldsOn () {
	if (canShield) {
		canShield = false;
		shieldsOn = true;
		shield1.active = true;
		shield2.active = true;
		
		startTime = Time.time;
			
		if (shieldsOnSound) {
			audio.clip = shieldsOnSound.clip;
			audio.volume = shieldsOnSound.volume;
			audio.loop = shieldsOnSound.loop;
			audio.Play();
		}
	}
}	



function Update () {
<snip>
	//	Check to see if the Special PowerUp "shields" are on or turn them off if dead
	if (shieldsOn) {
		if ((Time.time - startTime) > shieldStrength) {
			ShieldsOff();
		}
		
		if (playerStatus.dead) {
			ShieldsOff();
		}
	}
<snip>
}




function ShieldsOff () {
//	shieldsOffCount += 1; 														 // For debugging
//	announcementC03.textMessage = "fSO SO Ct: " + shieldsOffCount;	// For Debugging
			
	if (shieldsOffSound) {
		audio.clip = shieldsOffSound.clip;
		audio.volume = shieldsOffSound.volume;
		audio.loop = shieldsOffSound.loop;
		audio.Play();
	}
	
	shield1.active = false;
	shield2.active = false;
	shieldsOn = false;
	canShield = true;
}

So - ultimately I’ll have to come up with some sort of poll or query that checks the state of the shields when they turn off.

But so far every time I turn off the shields in any manner when they are in contact with any collider, the ship doesn’t sense any more collisions until I toggle the shields and have them turn off while NOT in any collision contact.

Hi,

Ok, to find an answer, first let’s validate some of my assumptions:

I’ll assume the ship itself has a collider, otherwise, that is the collision problem (you turn off the colliders, and leave the rigidbody without a collider to sense.

I assume that the code that moves the ship is in a FixedUpdate() function.

The rigidbody is not Kinematic.

The code that applies damage isn’t somewhere in the shield object(s) that you disabled.

Somewhere, as you deactivate your shields, you are resetting all values under your control associated with collision processing.

Oh, and have you tried hitting something else (other than the thing you were colliding with when the shields went off - your code looks for collision enter, but colliders are assembled in a hierarchy (as I recall) meaning that disabling one collider at the end of the chain, doesn’t mean when the next one in line touches the same object there is any “new” collision just a continuation of the same collision, unless there is a gap in collision - probably requiring some new force, based on your code (read no relative velocity).)

Are these all assumptions correct?

Galen

PS. I’d change that mesh collider to a sphere collider for better performance (probably not related to this problem).

Let me try to answer these as best as I can:

Yes, the ship has it’s own collider that is untouched in the shielding process. This approach was trying to be simple and “pop” a collider bubble around the existing ship.

The ship’s physics is in FixedUpdate();, the Visual Effects are in Update();. function ShieldsOn(); and function ShieldsOff(); are stand alone functions. The timer is in function Update();.

Correct on both accounts.

Yes, as far as I know. I’ve written it so if ShieldsOn is true, it bypasses the damage application, otherwise it applies damage.

Here is the function OnCollisonEnter();:

function OnCollisionEnter (collision : Collision) {

//	DEBUGGING CODE:
//	collisionCount += 1;
//	announcement.textMessage = "OCE Col Ct: " + collisionCount;	

	//	DAMAGE
	//	Calculate Impact Strength
	var collisonStrength = (collisionModifier * collision.relativeVelocity.sqrMagnitude);
	
	// Check to see if Special PowerUp "ShieldsOn" is active, then check "armour", then apply damage
	if (!shieldsOn) {
		if (collisonStrength > armor)
			playerStatus.currentHealth -= (collisonStrength);
	}
	
	//	AUDIO
	//	Get the sound effect from the object struck
	var collisionSoundEffect : CollisionSoundEffect = collision.gameObject.GetComponent (CollisionSoundEffect);

	//	Setup and Play sound.
	if (collisionSoundEffect) {
		audio.clip = collisionSoundEffect.audioClip;
		audio.volume = collisionSoundEffect.volumeModifier * collision.relativeVelocity.sqrMagnitude * specialEffects.collisionVolume;
		audio.Play ();
	}
}

If you look at the debug code I put in, I have pretty much proven that OnCollisonEnter(); does not get called after the shields turn off when in contact with the ground. When the shields turn off when in contact with the ground, the var collisonCount does not increase.

From what I can gather, your analysis is correct. Once one collider had made an OnCollisionEnter(); all of the colliders are considered to be part of that collison. (Little Angels shuffles his feet and looks at his shoes…) Well, there is only one other object in the game right now - the game terrain - so I can’t naturally test it against another object. I can try that, but I’m not sure if that would be valid.

Since this thread has come back up, I’ve put more thought into the problem (and I’ll ignore the sphere collider note at the bottom for now…) but my current thinking is to turn off the renderer and turn off the “Convex” variable in the mesh collider - at least until there is an OnCollisionExit();, I’ve just not worked out the gameObject/component hierarchy and I’ve yet to test my current logic.

This should make the shields both in visible and not actively resisting the ground. Using the check boxes, when I do this - the shields disappear and the the ship touches the ground rather than being suspended in the middle of the bubble.

I have yet to get this in the game code.

I’m going to have to do some sort of getComponent (in children) and since I have TWO components, I’ll have to look at an array. I’ve copied and pasted some array functionality before… but it’s always a trick to get it to work with my limited experience.

I can’t remember why I did that… Use the mesh collider… I think it was because a sphere out of my 3D package had fewer verts than the Unity Sphere, and I imported it with “colliders” checked. You are correct that a sphere collider could be better.

I’m thinking “aloud” (as such) here but why not add a rigidbody to each of the shields. I thought (and someone please correct me if I’m way off) that it’s not only colliders but colliders and rigidbodies that were hierarchical. If that’s so, then …

right now, your one rigidbody is experiencing all collisions from the children colliders as a single experience, but if you add a rigidbody then the colliders are not in the same path, thus generating different OnCollisionEnter() calls in the scenario you describe. Let me illustrate and tell me if I’m off:

current setup:
rigidbody → collider1 → child collider2…

(gee it’s hard to draw in a simple text editor :slight_smile: )

suggested flow:
Rigidbody → collider 1
→ rigidbody 2 → collider a
→ rigidbody 3 → collider b

Messages should still be processed up the tree (so code can be in Rigidbody), but collisions from collider a should be distinct from colliders b or 1

Am I way off? anyone??

Thanks,

Galen

I experimented with simply adding a rigid body to one of the colliders, and as soon as the shields are activated, the one with the rigid body goes flying off on it’s own path. It still seems parented to the ship, but is not physically connected.

I think the logic that I need to work out is turning off the renderer and the convex variable in the mesh collider, which for all intents and purposes turns off the Shield Effect.

With my stab in the dark technique perfected:

Trying to change:

		shield1.active = true;

to:

		shield1.MeshCollider.convex = true;

gives me the error:

BCE0019: 'MeshCollider' is not a member of 'UnityEngine.GameObject'.

So I’m working along the lines of something like:

		var getMesh : MeshCollider = GetComponent(MeshCollider);
		var mesh : Mesh = getMesh.convex;

which (now that I see it) not surprisingly gives me:

BCE0022: Cannot convert 'boolean' to 'UnityEngine.Mesh'.

But as you can see, I’m a blind man in a blind fold fiddling in the dark. Eventually I’ll actually learn about what I’m doing.

		var getMeshes = GetComponentsInChildren(MeshCollider);
		for (var meshSwitch in getMeshes) {
			meshSwitch.convex = true;
		}

gets me:

BCE0019: 'convex' is not a member of 'UnityEngine.Component'.

How do I access that?

I just want to toggle the var : bool MeshCollider.convex

I’ve also removed the collider from one of the two child gameObjects used to make the shields, so there should be only one CHILD collider, but I’m realizing as I say this I don’t want to turn off the ship’s collider, so I’ll have to check against something - perhaps a tag.

Tired now.

Time to go bathe the baby.

for (var meshSwitch in getMeshes …

should be:
for (var meshSwitch : MeshCollider in getMeshes…

Your problem is now driving me nuts… must find solution :twisted:

Galen

(posting before I try it)

I was so annoyed with the errors I was getting I found that the less I typed my variables the less errors I got, so I took all my typing out and it came down to just 1 error rather than 4 or 5! :shock: :sweat_smile: :roll:

Lemme try one more crack at it…