Mario jumps not correctly

Topics: User Forum
May 13, 2013 at 4:06 PM
I have a little problem with jumping. For example, the character(Mario) jumps from the top of a tile. The character is now in the air and not touching a tile. After that, he touches the right or left side of a tile and is still in the air. In this case, the character can jump again(and extremely high) because it touches the side of a tile. I don't want that Mario can jump again when it touches the side of a tile. I just want that the character can jump when it's feet are on the ground(top of a tile).
How can I do that?
How can I determine which side of a tile is colliding with the character?

In this video, you see how Mario jumps: http://www.vidup.de/v/zTVpF/
public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        World world;
        Body rectangle, rectangle2, rectangle3, rectangle4, rectangle5, playerrect;
        Texture2D rectangleSprite, playerSprite;

        private static float _displayUnitsToSimUnitsRatio = 100f;

        public static Vector2 ToDisplayUnits(Vector2 simUnits)
        {
            return simUnits * _displayUnitsToSimUnitsRatio;
        }

        private bool isOnGround;

        bool Player_OnCollision(Fixture fixtureA, Fixture fixtureB, FarseerPhysics.Dynamics.Contacts.Contact contact)
        {
            if (fixtureB.CollisionCategories == Category.Cat5)
            {
                isOnGround = true;
            }
            return true;
        }
        void Player_OnSeparation(Fixture fixtureA, Fixture fixtureB)
        {
            if (fixtureB.CollisionCategories == Category.Cat5)
            {
                isOnGround = false;
            }
        }

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            playerSprite = Content.Load<Texture2D>("retromario");
            rectangleSprite = Content.Load<Texture2D>("retrotile");

            if (world == null)
            {
                world = new World(new Vector2(0, 2));
            }
            else
            {
                world.Clear();
            }

            playerrect = BodyFactory.CreateRectangle(world, 0.64f, 0.64f, 1.0f);
            playerrect.BodyType = BodyType.Dynamic;
            playerrect.Position = new Vector2(0.96f, 0f);

            rectangle = BodyFactory.CreateRectangle(world, 0.64f, 0.64f, 1.0f);
            rectangle.BodyType = BodyType.Static;
            rectangle.Position = new Vector2(0.96f, 4.16f);
            rectangle.CollisionCategories = Category.Cat5;

            rectangle2 = BodyFactory.CreateRectangle(world, 0.64f, 0.64f, 1.0f);
            rectangle2.BodyType = BodyType.Static;
            rectangle2.Position = new Vector2(0.32f, 4.80f);
            rectangle2.CollisionCategories = Category.Cat5;

            rectangle3 = BodyFactory.CreateRectangle(world, 0.64f, 0.64f, 1.0f);
            rectangle3.BodyType = BodyType.Static;
            rectangle3.Position = new Vector2(0.96f, 4.80f);
            rectangle3.CollisionCategories = Category.Cat5;

            rectangle4 = BodyFactory.CreateRectangle(world, 0.64f, 0.64f, 1.0f);
            rectangle4.BodyType = BodyType.Static;
            rectangle4.Position = new Vector2(1.96f, 3.10f);
            rectangle4.CollisionCategories = Category.Cat5;

            rectangle5 = BodyFactory.CreateRectangle(world, 0.64f, 0.64f, 1.0f);
            rectangle5.BodyType = BodyType.Static;
            rectangle5.Position = new Vector2(1.60f, 4.80f);
            rectangle5.CollisionCategories = Category.Cat5;

            playerrect.FixedRotation = true;

            playerrect.OnCollision += Player_OnCollision;
            playerrect.OnSeparation += Player_OnSeparation;

        }
        
        protected override void Update(GameTime gameTime)
        {           
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            world.Step(Math.Min(elapsed, (1f / 60f)));
            KeyboardState kbState = Keyboard.GetState();
           
            // Move the player
            if (kbState.IsKeyDown(Keys.Left))
           {
               playerrect.ApplyForce(new Vector2(-0.5f, 0));
           }
           if (kbState.IsKeyDown(Keys.Right))
           {
               playerrect.ApplyForce(new Vector2(0.5f, 0));
           }
           if (isOnGround == true)
           {
             if (kbState.IsKeyDown(Keys.Space))
             {
                 playerrect.ApplyForce(new Vector2(0, -20.0f));
             }
           }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            spriteBatch.Draw(playerSprite, ConvertUnits.ToDisplayUnits(playerrect.Position),
                                           null,
                                           Color.White, playerrect.Rotation, new Vector2(playerSprite.Width / 2.0f, playerSprite.Height / 2.0f), 1f,
                                           SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle.Position),
                                            null,
                                            Color.White, rectangle.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle2.Position),
                                            null,
                                            Color.White, rectangle2.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle3.Position),
                                            null,
                                            Color.White, rectangle3.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle4.Position),
                                            null,
                                            Color.White, rectangle4.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                            SpriteEffects.None, 0f);

            spriteBatch.Draw(rectangleSprite, ConvertUnits.ToDisplayUnits(rectangle5.Position),
                                          null,
                                          Color.White, rectangle5.Rotation, new Vector2(rectangleSprite.Width / 2.0f, rectangleSprite.Height / 2.0f), 1f,
                                          SpriteEffects.None, 0f);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
May 14, 2013 at 6:11 PM
Edited May 14, 2013 at 6:13 PM
Interesting. I think the problem is lying on Player_OnCollision method about Collision, because the engine don't know what part of the body is the wall. Wait for the expert to explain how to deal with this situation. In my opinion...

     |   this is wall.
     |/                   this is character.
     |          _______/
     |         [       ]
     |         [       ]
     |_________[   X   ]_____________
use the X point to be the Collision Point to avoid the wall collision.
May 14, 2013 at 6:32 PM
I know, that's the problem. The variable isOnGround is always set on true when it touches a rectangle(tile). I want to set it just on true when it touches the topside of a tile, but I don't know how to do that.
May 15, 2013 at 11:12 AM
You need the X point that I mentioned above to check for collision instead of the whole body. When touching the Wall the X point is still on the Floor not the Wall. Maybe creating another body on the foot(as the X point) and use Joint(to joint it with the character) can solve this. Cheers!
May 15, 2013 at 4:11 PM
johnnysayasane is right, you need an additional "foot sensor" body to use for your collision detection. I found this article very helpful when solving a similar problem: http://www.iforce2d.net/b2dtut/jumpability

Cheers,
Mike.
May 17, 2013 at 6:26 PM
Edited May 18, 2013 at 10:13 AM