OnCollision Idea

Topics: User Forum
Jun 20, 2010 at 11:30 PM

I know I've posted a lot of things lately but it's mainly because I'm finally getting the hang of FP3 and thought I'd share an idea that I had. I was looking at how the OnCollision works in FP3 and have been using it for determining when I am on the ground. My character runs his animation while his canJump is equal to true. I made a little line segment that juts out the bottom of my character that sets isSensor as true. This causes problems though. If my character just falls off the side of a cliff, he keeps doing his animation which I don't want happening. So then I decided to use OnSeparation as well with the line segment so that when there was a separation, canJump becomes false and the animation stops. I don't know why but OnSeparation sometimes is called when there really was no separation. I didn't look into it much except for running my character across a flat static body and it does it every so often.

I need when I can jump and when the animation to run completely reliable so I came up with an idea. At the end of my characters update, it sets canJump equal to false. Then in the main game engine, the Farseer world is then updated. I needed a way to have OnCollision called whenever there is a collision with a body for the line segment. I looked into the Contact Update method and saw this line (line 265 where the OnCollision methods are called) which is keeping OnCollision from being called all the time:

 

if (wasTouching == false && touching)

 

Would it be a safe thing to do to change this line of code to this:

if ((!wasTouching || sensor) && touching)

 

This just seems like it would be better. I don't see a reason when you would ever not want to see what a sensor fixture is colliding with and this solves that. I understand if you need one of your sensors to just activate something once (like spawn one enemy when a character walks over it) that this would defeat that but I think it would be better to leave to the user to decide what should happen when something is colliding and not the engine (if you get what I mean). Most of the times I ever need to see OnCollision is when I am colliding with something. Just a thought. I was trying to find another way to determine when something is being collided with but this was the best thing I could find.

Jun 21, 2010 at 1:39 AM

I hit almost the exact same problem you did when I used a sensor like this. I even thought the same thing you probably were.

The problem is OnSeperate happens when the sensor stops colliding with ANYthing, not everything. The solution I used was something similar to this.

 

I don't know if FP3 still uses Geoms, but my solution in 2.1.3 was :

 

List<Geom> onGroundList = new List<Geom>();

 

In OnCollide(Geom g1, Geom g2, ContactList list)

            if (!onGroundList.Contains(g2))
                onGroundList.Add(g2);

 

in  OnSeperate(Geom g1, Geom g2)
            onGroundList.Remove(g2);

 

This then allows you to use onGroundList.Count == 0 in OnSeperate() (after Removing the Geom) to set the variable you are using to see if the player can jump.

Jun 21, 2010 at 1:36 PM

I'm also using FP3, and to check if my character is allowed to jump I just use ray casting.  It's a bit tricky to understand at first but once you get the hang of it it's quite useful.  Basically on each frame this code gets called for the player:

 

// In the player update function...

this.IsOnGround = false;

float xPosition1 = this.Position.X - Player.ModelWidth / 4;
float xPosition2 = this.Position.X + Player.ModelWidth / 4;

{
    Vector2 v1 = new Vector2(xPosition1, this.Bounds.Bottom);
    Vector2 v2 = new Vector2(xPosition1, this.Bounds.Bottom - 0.1f);
    this.Body.World.RayCast(this.jumpRayCastCallback, v1, v2);
}
if (!this.IsOnGround)
{
    // Try again on the other side of the player.
    Vector2 w1 = new Vector2(xPosition2, this.Bounds.Bottom);
    Vector2 w2 = new Vector2(xPosition2, this.Bounds.Bottom - 0.1f);
    this.Body.World.RayCast(this.jumpRayCastCallback, w1, w2);
}





//Elsewhere in the class, the callback is defined....

private float jumpRayCastCallback(Fixture fixture, Vector2 point, Vector2 normal, float fraction)
{
    if (fixture.Body.UserData != this)
    {
        if (fixture.Body.UserData is Tile)
        {
            this.IsOnGround = true;
            return fraction;
        }
    }
    return 0;
}

 

I simplified the callback a bit but you should get the idea.  Basically it makes two ray casts pointing downward, once from each of the lower corners of the player's physics model.  If there is a Tile (like a platform block or whatever) beneath him, IsOnGround gets set to true and the ray cast is stopped.  Otherwise it just continues.

Anyway, hope this helps.  It has worked for me more reliably than onCollision/onSeperation.