Jump code

Topics: Developer Forum
Sep 16, 2009 at 5:06 PM
Edited Sep 16, 2009 at 5:08 PM

Hello, i've been using farseer to develope a project of mine. I must say, it's bloody awesome, and i am deeply impressed.

Recently i have been going at jump code. Previously, when i used my own physics code, i would check to see if there was collision with the bottom of the player's hitbox. If so, the player has the option to jump. I do not know how to do this quite yet in farseer, though i do know it is quite possible i just am unable to determine the inner workings of it easily.

 

I am looking for something like

                if (gamePad1.DPad.Up == ButtonState.Pressed ||
                    keyboardState.IsKeyDown(Keys.Up))
                    player1.ApplyImpulse(new Vector2(0, -jumpSpeed));            
            }

 

if (playerGeom.OnCollision(playerGeom, geomList, ContactList contactList)

            {

                if (gamePad1.DPad.Up == ButtonState.Pressed ||

                    keyboardState.IsKeyDown(Keys.Up))

                    player1.ApplyImpulse(new Vector2(0, -jumpSpeed));            

            }

However, playerGeom.OnCollision is a function not a bool. 

I need something that would check to see if collides with any brush in my list of geoms, created when i load my map. However, this would probably allow the player to jump whenever it collides on any side with the map geometry. My guess says the player will be able to walljump (cool, but not what i want). Is there a way to detect collision based on certain sides of a geom? or within certain vertices?

 

Sep 17, 2009 at 7:21 PM
What you can do is register to the OnCollision event and use the ContactList to see where collisions are occuring relative to your player.
playerGeom.OnCollision += delegate(Geom playerGeom, Geom geomList, ContactList contactList)
{   foreach(Contact c in contactList) 
     { c.Position ...blah blah blah... } };

Alternatively, you could use another Geom to sense nearby objects. What I did was I made a "foot" geom, placed it slightly below my character and set it like this:

foot.CollisionResponseEnabled = false;
foot.OnCollision = (a, b, c) => { this.IsStanding = true; };

And elsewhere in my code, I have:

//If player hits the jump key and this.IsStanding == true, then this.jump(), and this.IsStanding = false;

 

Hope this helps.

Sep 18, 2009 at 5:03 PM

i'm a little confused about hte concept of Contact in ContactList, as in the brackets it's referenced as c.Position. Does this mean that c represents a dynamic object (such as the player)? 

thanks for your help so far. it is much appreciated!

-Tom Havlik

Sep 18, 2009 at 6:57 PM

ContactList is a collection, so you are able to enumerate over it (hence the foreach statement). If you're not familiar with foreach, it will iterate through every element in a collection, and within the body, the current element is assigned to c (in this case). It's pretty much equivalent to doing:

for(int i = 0; i < contactList.Count(); i++) {
   Contact c = contactList[i];
   c.Position ... blah blah blah...
}

A Contact object is a discrete position in which the physics engine says "We're going to simulate these two objects colliding at this exact point." The engine does not collide and apply forces to the whole surface of an object, just these points. All Contacts are positioned where the two geoms overlap, typically on one of the geom's vertices. If you were to take some contact c and do

Vector2 v = c.Position - playerGeom.Body.Position;

you'll get a vector pointing in the direction of the contact, relative to the center of your player. So, if you find that v == Vector2.UnitY, then you know that c is directly below your player (assuming Y increases downward).

Sep 20, 2009 at 6:20 PM

bear with me, i'm a bit noobish at events like this.

so i have

public void PlayerCollision(Geom playerGeom, Geom geomList, ContactList contactList)

        {

        }

 

player1.torso_geom.OnCollision += PlayerCollision;
            {
                ContactList contactList;
                foreach (Contact c in contactList)
                {
                    Vector2 v = c.Position - player1.feet_body.Position;
                    if (v == Vector2.UnitY)
                        player1.feet_body.CanJump = true;
                    else
                        player1.feet_body.CanJump = false;
                }
            }

player1.torso_geom.OnCollision += PlayerCollision;

            {

                ContactList contactList;

                foreach (Contact c in contactList)

                {

                    Vector2 v = c.Position - player1.feet_body.Position;

                    if (v == Vector2.UnitY)

                        player1.feet_body.CanJump = true;

                    else

                        player1.feet_body.CanJump = false;

                }

            }

 

but PlayerCollision has the wrong return type. Is it not supposed to be void? A

Also i don't know how to assign contactList. It's a null object right now, i guess.

 

Sep 21, 2009 at 4:36 AM

Hi Keres,

You'll have a method like this:

public bool PlayerCollision(Geom playerGeom, Geom geomList, ContactList contactList)
{
    player1.feet_body.CanJump = false;
    foreach (Contact c in contactList)
    {
        Vector2 v = c.Position - player1.feet_body.Position;

        if (v == Vector2.UnitY)
        {
            player1.feet_body.CanJump = true;
            break;
        }

    }
    return true;
}

And in your initialization code, you'll have:

player1.torso_geom.OnCollision += PlayerCollision;

The method must return bool, and you are to return true if you want the physics simulator to perform the collision response, and false if you want to ignore the collision. In this case, you don't want your player to pass through the objects it collides with, so you must return true. Note that v == Vector2.UnitY will not necessarily happen (unless you make sure to put a vertex directly under the body), and I suggest you make a more general check that will return true for vectors that point "roughly" downward.

Sep 21, 2009 at 10:02 AM
Edited Sep 21, 2009 at 10:04 AM
first add to the oncollision event

player1.torso_geom.OnCollision += PlayerColEvent;

public bool PlayerColEvent(Geom geom1, Geom geom2, ContactList list)
        {

            //now this code detects the angle of the collison and checks it

          

            
            Vector2 position = list[0].Normal; //get the normal of the impact

            float angle = (float)Math.Atan2(position.Y, position.X); //calculate the angle of the normal
          
          
            Player1.Normal_conatact = angle;


//this check the angle of the contact. You can change this to work out what angle slope the player can jump on
            if (Math.Abs(angle) < MathHelper.PiOver2 - .1f || Math.Abs(angle - MathHelper.TwoPi) < MathHelper.PiOver2 - .1f)
            {
         
                Player1.Ground = true; //set ground to true. when it is true the player can jump
            
            }
   

            return true;
        }//##################################

VERY INPORTANT: due to the way farseer works you must add the player to the simulator before all othere bodys and geoms.

if you reset the simulator when loading a level make sure the player is added first again.

hope this helps
Mar 26, 2010 at 4:21 PM
I know this post is old but I'll try anyway. Riddler, your code takes the Absolute value of the collision normal which would work unless the player jumped and hit an object from below. In which case the Absolute value of the collision would be a similar angle and cause the player to go into a walking state even though he is in midair. Or am I missing something?