(Java/LibGDX) Weak/Unresponsive Movement

General discussion about Box2D tutorials
Post Reply
Kain24
Posts: 2
Joined: Fri Jun 09, 2017 7:48 pm

(Java/LibGDX) Weak/Unresponsive Movement

Post by Kain24 » Fri Jun 09, 2017 8:13 pm

Hello, I am using Box2D for a platformer game with the LibGDX framework on Java. I've been struggling to reach the point where I'm content with the movement of my player. After solving my issue of the player "gliding," all of the movement within my Box2D world was weak and unresponsive to impulses/force/velocity. In other terms, it would take a max velocity of 70F to even move my character at a decent pace (at 7F it is trudging along). Because of this, I wiped all of the Box2D code I wrote and replaced it with code from this example:
http://www.badlogicgames.com/wordpress/?p=2017. Yet, nothing really changed. It would still take a max velocity (movement speed) of 70F to even budge my player.

I was convinced I was doing something wrong outside of Box2D, but, when I disabled other things from updating, the problem persisted.

This is how my code runs: main loop -> UPDATE SECTION -> update time -> step box2d world -> update ashley entity engine -> update my entities -> DRAW SECTION -> update camera position -> draw stage segments -> draw entities -> box2d debug render

Only stepping the world and doing the box2d debug render provided the same results.

Here is my player code:

Code: Select all

	public Body body;
	public Fixture upperFixture, bottomFixture;

	public boolean grounded;
	public long lastGroundTime;
	public Body groundedPlatform;
	
	public float walkSpeed = 2F, maxVelocity = 7F, jumpSpeed = 30F;

	@Override
	public void update(float deltaTime) {
		super.update(deltaTime);

		this.grounded = isGrounded();
	
		if(grounded) {
			lastGroundTime = System.nanoTime();
		} else {
			if(System.nanoTime() - lastGroundTime < 100000000) {
				grounded = true;
			}
		}

		Vector2 pos = body.getPosition();
		Vector2 vel = body.getLinearVelocity();
		
		if(Math.abs(vel.x) > maxVelocity) {
			vel.x = Math.signum(vel.x) * maxVelocity;
			body.setLinearVelocity(vel);
		}

		if(!moveLeft && !moveRight) {
			body.setLinearVelocity(vel.x * 0.9F, vel.y);
			
			walkTime = 0F;
			idleTime += deltaTime;
		} else {
			idleTime = 0F;
			walkTime += deltaTime;
		}
		
		if(!grounded) {
			upperFixture.setFriction(0F);
			bottomFixture.setFriction(0F);
		} else {
			if(!moveLeft && !moveRight && idleTime > 0.2) {
				upperFixture.setFriction(100F);
				bottomFixture.setFriction(100F);
			} else {
				upperFixture.setFriction(0.2F);
				bottomFixture.setFriction(0.2F);
			}
		}
		
		if(moveLeft) {
			facing = Direction.LEFT;
			
			body.applyLinearImpulse(-walkSpeed, 0, pos.x, pos.y, true);
		}

		if(moveRight) {
			facing = Direction.RIGHT;
			
			body.applyLinearImpulse(walkSpeed, 0, pos.x, pos.y, true);
		}
		
		body.setAwake(true);
	}

	public void jump() {
		if(grounded) {
			Vector2 pos = body.getPosition();
			Vector2 vel = body.getLinearVelocity();
			
			body.setLinearVelocity(vel.x, 0);
			body.setTransform(pos.x, pos.y + 0.01F, 0);
			body.applyLinearImpulse(0, jumpSpeed, pos.x, pos.y, true);
		}
	}

	public boolean isGrounded() {
		groundedPlatform = null;

		World world = stage.getBox2DWorld();
		Array<Contact> contacts = world.getContactList();

		for(int i = 0; i < contacts.size; i++) {
			Contact contact = contacts.get(i);

			if(contact.isTouching() && (contact.getFixtureA() == bottomFixture || contact.getFixtureB() == bottomFixture)) {
				Vector2 pos = getPosition();
				WorldManifold manifold = contact.getWorldManifold();

				boolean below = true;

				for(int j = 0; j < manifold.getNumberOfContactPoints(); j++) {
					below &= (manifold.getPoints()[j].y < pos.y - 1.5F);
				}

				if(below) {
					if(contact.getFixtureA() != null) {
						groundedPlatform = contact.getFixtureA().getBody();
					}

					if(contact.getFixtureB() != null) {
						groundedPlatform = contact.getFixtureB().getBody();
					}

					return true;
				}

				return false;
			}
		}

		return false;
	}

	@Override
	public void createBodies() {
		World world = stage.getBox2DWorld();

		BodyDef def = new BodyDef();
		def.type = BodyType.DynamicBody;
		Body body = world.createBody(def);

		float hx = getWidth() / 2F, hy = getHeight() / 2F;

		PolygonShape poly = new PolygonShape();
		poly.setAsBox(hx, hy - (hx / 2F), new Vector2(0, (hx / 2F)), 0);
		upperFixture = body.createFixture(poly, 1);
		poly.dispose();

		CircleShape circle = new CircleShape();
		circle.setRadius(getWidth() / 2F);
		circle.setPosition(new Vector2(0, -hy + hx));
		bottomFixture = body.createFixture(circle, 0);

		body.setBullet(true);
		body.setFixedRotation(true);
		
		this.body = body;
	}
The only changes I made were removing the MovingPlatform class and storing a Body instance instead and moving the jump mechanics to a separate method that is called externally.

I only have one other body right now, and it is creating using this method:

Code: Select all

	public void createRectangle(World world, float x, float y, float width, float height) {
		BodyDef bD = new BodyDef();

		bD.type = BodyType.StaticBody;
		bD.position.set(x + (width / 2), y + (height / 2));

		FixtureDef fD = new FixtureDef();

		PolygonShape shape = new PolygonShape();
		shape.setAsBox(width / 2, height / 2);

		fD.shape = shape;
		fD.density = 3F;

		Body body = world.createBody(bD);
		body.createFixture(fD);
		shape.dispose();

		bodies.add(body);
	}
Here is where I create my world and update it:

Code: Select all

	public GameStage() {
		engine = new Engine();

		world = new World(new Vector2(0, -20F), true);

		segments = new ArrayList<Segment>();

		EntityPlayer player = new EntityPlayer(this, PlayerColor.GREEN);
		addEntity(player);

		this.focus = player;

		addSegment(new SpawnSegment());
	}

	public void update(float deltaTime) {
		world.step(deltaTime, 4, 4);
		
		engine.update(deltaTime);

		for(Entity entity : engine.getEntities()) {
			if(entity instanceof EntityBase) {
				((EntityBase) entity).update(deltaTime);
			}
		}
	}
This problem came up after another issue was resolved on the java-gaming forums, and a user directed me here.

I am open to further inquiries, thank you.

iforce2d
Site Admin
Posts: 860
Joined: Sat Dec 22, 2012 7:20 pm

Re: (Java/LibGDX) Weak/Unresponsive Movement

Post by iforce2d » Sat Jun 10, 2017 9:52 am

This is a pretty common complaint about using a physics engine for character control. I'm afraid most solutions end up being pretty hacky and you are in for an uphill battle to get a universal control method that can be used for all phases of movement.

If you want some more interest in your question it might help if you explain some things better, ie. what exactly do you mean by gliding and what did you do to solve it, what exactly is the problem with having a max velocity of "70F" (I presume the F means floating point, but then why not just say 70, perhaps I am missing something).
In general, try to explain things in English before you expect anyone to analyze pages of code.

Kain24
Posts: 2
Joined: Fri Jun 09, 2017 7:48 pm

Re: (Java/LibGDX) Weak/Unresponsive Movement

Post by Kain24 » Sat Jun 10, 2017 6:34 pm

iforce2d wrote:This is a pretty common complaint about using a physics engine for character control. I'm afraid most solutions end up being pretty hacky and you are in for an uphill battle to get a universal control method that can be used for all phases of movement.

If you want some more interest in your question it might help if you explain some things better, ie. what exactly do you mean by gliding and what did you do to solve it, what exactly is the problem with having a max velocity of "70F" (I presume the F means floating point, but then why not just say 70, perhaps I am missing something).
In general, try to explain things in English before you expect anyone to analyze pages of code.
Well, the "gliding" was when the character would experience a longer fall time when moving around. A user on java-gaming.org explained to me that the velocity was being capped since the vertical velocity was much lower than the horizontal velocity (my jump impulse was 100F whereas my horizontal velocity was in no way restricted to a maximum). The issue was resolved when I capped the horizontal velocity to be much lower. Anyway, gliding is no longer an issue since it occurred before I wiped my code.

And yes, F is floating point, I just always append F as a habit of identifying what parameters are taken.

The main point I'm trying to investigate is why my world simulation is so different from the example that I linked. "70F" seemed high since the example uses a max velocity of 7F, and the player seen in the example's video seems to be moving pretty quickly in comparison to the trudging pace that I'm experiencing.

My apologies for any misunderstandings; I guess I did not do a good job in my original post.

TL;DR: my world and the example world are operating in (seemingly) the exact same parameters, but my world is either extremely slow or is unresponsive to velocity.

iforce2d
Site Admin
Posts: 860
Joined: Sat Dec 22, 2012 7:20 pm

Re: (Java/LibGDX) Weak/Unresponsive Movement

Post by iforce2d » Sun Jun 11, 2017 3:40 pm

The way you mention velocities being capped suggests to me that you are dealing with things in the wrong scale. Box2D only caps velocities when they are very high, like around 432 km/h or so (http://www.iforce2d.net/b2dtut/gotchas#speedlimit)

Typically this problem shows up when people try to use pixels as their physics units. Box2D has nothing to do with the on-screen display, and all units should be in meters. So while your character may be drawn as 150 pixels tall on screen, the physics dimension would be say 1.5 meters. If you use 150 units for the physics size it would be more like a tall building, which might explain why even large forces only move things slowly. Check whether the example you are comparing with is using a similar scale to yours.

Post Reply