Body is pushed back from wall :(

Topics: User Forum
Sep 5, 2013 at 2:53 PM
Edited Sep 6, 2013 at 12:47 PM
I have a strange issue with the collision between the wall and my player. If I press W and D or W and A to move up and right respectively up and left, the player sometimes gets pushed back from the wall. But every time only one pixel. This ony happens if I let him walk diagonal.

The payer is currently an circle, but if I use to a rectangle it does not jump, it stops moving immediately.

I have attached the project file and post some relevant code pieces.

Test project: http://s16432482.onlinehome-server.info/physics.zip

I use the latest farseer physics engine for collision.

Create bodies:
    private static Vertices CreateWall(Rectangle rectangle)
    {
        var verticesForTile = new Vertices {
            ConvertUnits.ToSimUnits(new Vector2(rectangle.X, rectangle.Y)), 
            ConvertUnits.ToSimUnits(new Vector2(rectangle.X + rectangle.Width, rectangle.Y)), 
            ConvertUnits.ToSimUnits(new Vector2(rectangle.X, rectangle.Y + rectangle.Height)),
            ConvertUnits.ToSimUnits(new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height))
        };
        return verticesForTile;
    }

        // Bodies bauen
        var wallVertices = this.map.CollisionRects.Select(CreateWall).ToList();

        var wallBody = BodyFactory.CreateCompoundPolygon(this.world, wallVertices, 1);
        wallBody.IsStatic = true;
        wallBody.Restitution = 0f;
        wallBody.Friction = 0;
        wallBody.Enabled = true;

        this.walls = new List<Body>();
        walls.Add(wallBody);


        // Actor bauen
        this.actorBody = BodyFactory.CreateCircle(this.world, ConvertUnits.ToSimUnits(12), 1);
        this.actorBody.Position = ConvertUnits.ToSimUnits(32, 32);
        this.actorBody.BodyType = BodyType.Dynamic;
        this.actorBody.FixedRotation = true;
        this.actorBody.Restitution = 0f;
        this.actorBody.Friction = 0;
Update
        // Zurücksetzten
        this.actorBody.LinearVelocity = Vector2.Zero;

        // Bewegen
        var force = Vector2.Zero;
        if (Keyboard.GetState().IsKeyDown(Keys.S))
        {
            force.Y += 1f;
        }
        if (Keyboard.GetState().IsKeyDown(Keys.W))
        {
            force.Y += -1f;
        }
        if (Keyboard.GetState().IsKeyDown(Keys.D))
        {
            force.X += 1f;
        }
        if (Keyboard.GetState().IsKeyDown(Keys.A))
        {
            force.X += -1f;
        }

        if (force != Vector2.Zero)
        {
            var forceNormal = Vector2.Normalize(force);
            this.actorBody.LinearVelocity = forceNormal * 128 * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }

        // Advance all the elements in the world
        this.world.Step(Math.Min((float)gameTime.ElapsedGameTime.TotalMilliseconds * 0.001f, (1f / 30f)));
Drawing:
        // Karte zeichnen
        this.spriteBatch.Begin();

        this.GraphicsDevice.Clear(Color.CornflowerBlue);

        var position = ConvertUnits.ToDisplayUnits(this.actorBody.Position);
        this.spriteBatch.Draw(this.actorSprite, new Vector2(position.X, position.Y), null, Color.Red, 0f, new Vector2(12, 12), 1f, SpriteEffects.None, 0);

        var projection = Matrix.CreateOrthographicOffCenter(0f, ConvertUnits.ToSimUnits(this.GraphicsDevice.Viewport.Width), ConvertUnits.ToSimUnits(this.GraphicsDevice.Viewport.Height), 0f, 0f, 1f);
        this.debugView.RenderDebugData(ref projection);

        this.spriteBatch.End();
Sep 6, 2013 at 2:44 PM
Edited Sep 6, 2013 at 2:45 PM
hi. I took a quick look at your code. I am at work so could not run it, I'm not sure why you are getting the effects you describe, but from what I did see, it looks like your Update() method does the following:
  1. Make Linear Body Velocity equal Zero
  2. Check input and create force vector depending on keys held. if no input, force is zero
  3. Make unit vector in force direction
  4. set linear velocity as unit force multiplied by an arbitary value (128) times time step
  5. advance world
I am not sure exactly how your game is supposed to work, but just thinking in terms of how to move an object in farseer, explictly setting the velocity of an object is not what you want to do.

You want to apply forces, or more importantly impulses (an impulse is just a force acting over a certain amount of time) to a body, and these will induce acceleration and this will modify velocity.

From the above, im not sure why you need a phyics engine. you are trying to move an object at a constant velocity when keys held, and inducing an effective infinite acceleration when you want to stop or change direction.

Sorry perhaps I am missing what your program is trying to do. I would suggest changing :

this.actorBody.LinearVelocity = forceNormal * 128 * (float)gameTime.ElapsedGameTime.TotalSeconds;

to

this.actorBody.ApplyImpulse(forceNormal * 128 * (float)gameTime.ElapsedGameTime.TotalSeconds);

i forget if ApplyImpulse is the right method spelling, but you will find it.

good luck
Sep 6, 2013 at 3:48 PM
Thank u for answer :)
I try to explain what I want. The rectangles are walls in a map and the actorBody is a player that walk through this wold.
I use the phyics engine in first line for collisions => player -> wall or projectile -> wall.

I used ApplyImpulse first time, but it doesn't make any difference.
I have read the article about 'Ghost vertices' http://www.iforce2d.net/b2dtut/ghost-vertices
Could that the cause of all my problems? If I use an rectangle, then exactly the described manner happens.

Now I changed my code to use not rectangle polygones but rather edges. That works for rectangles as well. But not for my circle.
It makes it even worse. If my circle lies on an edge, the app hangs until i close it.
        this.map = new Map();

        // Welt bauen
        this.world = new World(Vector2.Zero);
        this.walls = new List<Body>();

        // Bodies bauen
        for (var y = 0; y < this.map.MapData.GetLength(0); y++)
        {
            for (var x = 0; x < this.map.MapData.GetLength(1); x++)
            {
                if (this.map.MapData[y, x] == 1)
                {
                    var rect = new Rectangle(x * this.map.TileSize.X, y * this.map.TileSize.Y, this.map.TileSize.X, this.map.TileSize.Y);

                    // Links
                    var left = new Vertices
                    {
                        ConvertUnits.ToSimUnits(new Vector2(rect.X, rect.Y)),
                        ConvertUnits.ToSimUnits(new Vector2(rect.X, rect.Y + rect.Height))
                    };

                    // Open
                    var top = new Vertices
                    {
                        ConvertUnits.ToSimUnits(new Vector2(rect.X, rect.Y)),
                        ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width, rect.Y))
                    };

                    // Unten
                    var bottom = new Vertices
                    {
                        ConvertUnits.ToSimUnits(new Vector2(rect.X, rect.Y + rect.Height)),
                        ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width, rect.Y + rect.Height))
                    };

                    // Rechts
                    var right = new Vertices
                    {
                        ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width, rect.Y)),
                        ConvertUnits.ToSimUnits(new Vector2(rect.X + rect.Width, rect.Y + rect.Height))
                    };

                    var wallBody = new Body(this.world) { IsStatic = true };

                    if (!this.CheckCollision(x + 1, y))
                    {
                        FixtureFactory.AttachEdge(right[0], right[1], wallBody);
                    }
                    if (!this.CheckCollision(x - 1, y))
                    {
                        FixtureFactory.AttachEdge(left[0], left[1], wallBody);
                    }
                    if (!this.CheckCollision(x, y + 1))
                    {
                        FixtureFactory.AttachEdge(bottom[0], bottom[1], wallBody);
                    }
                    if (!this.CheckCollision(x, y - 1))
                    {
                        FixtureFactory.AttachEdge(top[0], top[1], wallBody);
                    }

                    walls.Add(wallBody);
                }
            }
        }


        // Actor bauen
        //this.actorBody = BodyFactory.CreatePolygon(this.world, CreateWall(new Rectangle(0, 0, 24, 24)), 0);
        this.actorBody = BodyFactory.CreateCircle(this.world, ConvertUnits.ToSimUnits(12), 1);
        this.actorBody.Position = ConvertUnits.ToSimUnits(32, 32);
        this.actorBody.BodyType = BodyType.Dynamic;
        this.actorBody.FixedRotation = true;

        // Sprite laden
        this.actorSprite = this.Content.Load<Texture2D>("goo");

        // DebugView
        this.debugView = new DebugViewXNA(this.world);
        this.debugView.AppendFlags(DebugViewFlags.DebugPanel);
        this.debugView.LoadContent(this.GraphicsDevice, this.Content);
Sep 10, 2013 at 10:22 AM
Edited Sep 10, 2013 at 10:23 AM
hi. i am not sure to be honest. I know ghost vertices can exist, but I don't think they are your problem. I still think it might be to do with the approach to moving a body. Edges should work fine (they need to be on a static body). Rectangles should also be fine.

Are you now moving your actor with forces / impulses or are you still explicitly setting velocity and position?

furthermore, depending on speed, you might need to active Continuous collision detection CCD to ensure a body does not pass through a wall. I suspect your actor is too low speed for that to be a major impact but might help.

sorry i cant be more useful.