OnCollision / OnSeperation looping?

Jul 1, 2010 at 10:29 PM

Hi

 

I'm making a shmup using farseer, so I can have some decent physics action going on, and I'm using bodies and geoms for the bullets and enemies.

Problem is, when they collide, as soon as they collide I make the bullets inactive and do enemyHealth-- but no matter what I try, the collision event keeps looping, so all enemies die in one hit regardless of their health, because it loops that many times.

I can set the enemyState to "gettingHit" and then do enemyHealth-- in the update loop, but that means that if the enemy gets hit by more than one bullet simultaneously, it only subtracts 1 health, which isn't any use for my game design.

I've tried subtracting the health in the event in the Enemy class, and from the Bullet class too, but OnCollision just gets called way too many times...

Any suggestions??

Here's the code I have at the moment..

//-- BULLET CLASS -- //
 private bool OnCollision(Geom geom1, Geom geom2, ContactList contactList)
        {
            if (geom2.CollisionGroup == 5) // 5 = Enemy Class
            {
                position = new Vector2(1500, 1000); // Set bullet off-screen 
                bulletBody.Position = new Vector2(position.X + offsetX, position.Y + offsetY);
                bulletGeom.CollisionEnabled = false;
                currentState = (int)bulletState.dead;
                Active = false;
                Enemy enemy = (Enemy)geom2.Tag;
                enemy.gotHit();
            }
            return true;
        }





//-- ENEMY CLASS --//
 internal virtual void gotHit()
        {
            if (currentState != (int)enemyState.killed && currentState != (int)enemyState.dead) //Killed = dying animation, Dead = removed
            {
                enemyHealth--;
                currentState = (int)enemyState.hit;
            }
        }

 

I've tried adding the bullets that hit to a List and then removing them in the update loop for the enemy, so that it subtracts 1 health for each bullet in the list, but it came up with an error saying that the Collection size had been modified. I didn't think it was an efficient way to do it anyway..

Just to note, my bullets are part of a List that will get re-used later.

Any ideas how I can make sure that the event only gets called once per bullet collision?? 

Jul 1, 2010 at 10:50 PM

just to note.. not sure if it matters.. but I'm using FP 2.1.3

Jul 1, 2010 at 11:56 PM

Since you set the bullet to a state of dead, have you tried just checking that? Should allow multiple bullets to hit the same target at the same time, but only allow a single bullet to hit something once regardless if it collides multiple times.

Jul 2, 2010 at 9:04 AM
Edited Jul 2, 2010 at 9:05 AM

I'm at work at the moment so I can't check, but I haven't tried that.

I could set the bulletState to Dead in the OnCollision event in the bulletClass.. then have a bool isChecked in the Update loop for the bullet. Something like this, I already have a switch-block for bulletState, so never occured to me to use the enemy.Tag and deduct the health from there..

case ((int)bulletState.Dead) :
{
if (!isChecked)
{
    Enemy enemy = (Enemy)geom2.Tag;
    enemy.enemyHealth - - ;
    isChecked = true;
    Active = false; // so it wont get updated again before its put back into the bulletPool and reset,,
}
break;
}

I think this would work, thankyou =) I guess I was kinda close when I got it to work decreasing enemyHealth in the enemys update loop, but doing it in the bullet loop I think would definetely work.

I wanted to keep all the stuff in the enemy class, rather than have it in the bullet class, but if it works, that's all that's important ;)

I'll try it when I get home, thanks again!

Jul 2, 2010 at 10:59 PM

You can still call do damage in the oncollision you just need to remove the bullet from the sim if it has hit something. You can look at my code below to see how I do it for my game.

virtual public bool OnCollision(Geom self, Geom other, ContactList contacts)
{            
    //make sure they are an entity and not on the same team
    if (other.Tag is IEntity)
     {
        IEntity e = (IEntity)other.Tag;
        if (e.ID != Owner.ID && e.Team != Owner.Team)
            e.Damage(Damage, DamageType, Owner);
        else //hit self
           return false;
     }
           
       //we hit something    
       HaveHit = true;

       return true;
}       

virtual public void Update(GameTime time)
{
    if (HaveHit)
         Kill();
}


public
override void Kill() { if(Spawned) { m_Body.Enabled = false; m_Body.LinearVelocity = Vector2.Zero; Engine.PhysicsSimulator.Remove(m_Body); Engine.PhysicsSimulator.Remove(m_Geom); base.Kill(); } }

 

Jul 3, 2010 at 4:10 PM

Thanks for the reply. One reason I didn't want to remove the bullet from the sim was because when it gets put back into the pool, it'd be fired again within a few seconds, meaning I'd have to re-add it to the simulation. I thought keep adding and removing it so often would be a bit of a performance hit.

But, it seems that maybe using farseer for a shmup with lots of action isn't going to work. So far it's been great, but now I'm getting errors at random times during testing with bullet-enemy collisions..

This is the one I'm getting..

"Function does not accept floating point Not-a-Number values."

 ApplyForce(ref _linearDrag);

            //Calculate rotational drag and apply it as torque
            _rotationalDrag = AngularVelocity * AngularVelocity * Math.Sign(AngularVelocity); << this is the line its pointing to
            _rotationalDrag *= -RotationalDragCoefficient;
            ApplyTorque(_rotationalDrag);

It's stating that AngularVelocity is NaN.. but I'm not using any velocity in the simulation yet, apart from when an enemy dies, then I enable gravity so it plummets to the bottom of the screen.

It doesn't happen all the time either. Would I be best making my bullets use an Angular/Linear Velocity like you do in your example above? It'd make it better for when I come to do firing patterns, but all I'm doing at the moment is a simple position.X += 20 in the update loop. Nothing special.

Jul 3, 2010 at 7:30 PM
Edited Jul 3, 2010 at 7:38 PM

I have never noticed any performance issues with adding and removing things from the sim frequently.

I would definitely suggest using the ApplyForce function.

I too encountered this error. I just added a check  to make sure it wasn't NAN and if it was to set it to 0

//check for not a number
if (float.IsNaN(AngularVelocity))
AngularVelocity = 0f;

It did just occur to me that it could be caused by removing the body and not setting its AngularVelocity to 0 before re-adding it the sim.

So you could try adding m_Body.AngularVelocity = 0f; to the Kill() function to see if that fixes it. Haven't tried it my self.

 

Edit need to fix the nan checking function.

Jul 6, 2010 at 9:14 AM

It seems to me you could just disable the body upon collision. That way when you don't need to worry about repositioning it. I've been just disabling the body so it stops moving and colliding and when I need to fire another particle I just grab any inactive ones that I have and fire them. The body is still there, but isn't being checked by the engine. You'll also want to check in any draw code that it's active before you draw it or else you'll get floating phantom bullets around the place.

The problem with velocity could be down to moving the bullet (which means it may keep the rest of its velocity and forces) so things might go a little bit crazy.

I apologise if you already have this, I was just going on what I've seen of your code.

 

Developer
Jul 6, 2010 at 11:46 PM

If your using a Pool to store your bullets you need to reset all the velocities and and flags that may have been set before reusing them.