Physix Helpers - GameDevWeek/CodeBase GitHub Wiki
We're using Box2D for physics, which comes with LibGDX. You are free to ignore the classes we provide with this tutorial, but we found it helps reducing the workload a lot.
Commons-GDX contains a couple of Helper classes to get started with physics development:
- A body component
- A modifier component
- A debug-renderer system
- Two physics systems (with and without fixed step)
- Builder patterns to easily create bodies and fixtures.
- Component-aware contact listeners.
These are located in de.hochschuletrier.gdw.commons.gdx.physix
.
Components
PhysixBodyComponent
You can add this component to an entity to link a Box2D body to it. This is usually done like this:
body = engine.createComponent(PhysixBodyComponent.class);
body.init(bodyDef, physixSystem, entity);
body.createFixture(fixtureDef);
entity.add(body);
Keep in mind that you can't add this component during a world step(update).
Removing this component will remove the body from the world as well. Since Ashley removes components after system updates, you can do this without worries about the world step.
PhysixModifierComponent
You probably know, that you can't add, remove or move bodies during a world step (update). To make it possible, this component has been created. With it you can schedule code to be executed at a suitable time.
The basic syntax is:
PhysixModifierComponent modifier= engine.createComponent(PhysixModifierComponent.class);
entity.add(modifier);
modifier.schedule(() -> {
// add the body component here
});
Keep in mind: To remove a body, just remove the PhysixBodyComponent
. No need to schedule it!
When all scheduled code has been run, the modifier-component will be removed! So if you want to modify a body, you should first check, if a modifier-component already exists:
PhysixModifierComponent modifier = ComponentMappers.physixModifier.get(entity);
if(!modifier) {
modifier = engine.createComponent(PhysixModifierComponent.class);
entity.add(modifier);
}
modifier.schedule(() -> {
// modify the body here
});
Systems
PhysixDebugRenderSystem
This system uses a Box2DDebugRenderer
to render debug physics using the current ProjectionMatrix
of DrawUtil.batch
.
You don't need to know anything about rendering to add this to your game. As long as DrawUtil.batch
is used for drawing, you should be fine by just adding this system to your Ashley Engine
.
PhysixSystem
This system creates and updates a Box2D world for you. It also handles PhysixModifierComponent
for you.
Check out the public methods for stuff like:
- Box2D to World coordinates conversion and vice versa.
- Changing gravity
- Reset the whole world
PhysixSystemFixedStep
PhysixSystemFixedStep
extends PhysixSystem to allow for a fixed time-step.
Online tutorials will tell you to use a fixed step to avoid problems. In my test cases, it always caused stuttering, so I made this optional. Use if you encounter simulation problems with PhysixSystem. You can reduce the stuttering by using a higher framerate for the physics than the rendering. For example, if you render at 60 FPS, use a framerate of 120 or even 240 for physics.
PhysixBodyDef / PhysixFixtureDef
These are Fluent Interfaces (a chained Builder-Pattern) to help you construct a BodyDef
or a FixtureDef
easily.
An example for how to create a new bodydef:
PhysixBodyDef bodyDef = new PhysixBodyDef(BodyType.DynamicBody, physixSystem)
.position(100, 100)
.fixedRotation(true)
.position(x, y);
PhysixBodyDef
extends Box2Ds BodyDef
, so you can use it anwywhere BodyDef
is expected! Same goes for PhysixFixtureDef
.
Aside from allowing you to set different properties of the definition, it also takes care of world to box2d coordinate conversion.
An example how to create a circle shape fixture:
PhysixFixtureDef fixtureDef = new PhysixFixtureDef(physixSystem)
.density(5)
.friction(0.2f)
.restitution(0.4f)
.shapeCircle(30);
body.createFixture(fixtureDef);
Contact Listeners
PhysixComponentAwareContactListener
Of course, you are free to implement your own contact listener, but this one has a couple of benefits:
- You can add listeners for specified component classes.
- You can always be sure that the first body in the contact has the specified component class.
- You get the
PhysixBodyComponent
(and its entity) from the contacts for free (no need to hassle with user-data)
Adding it is as simple as this:
PhysixComponentAwareContactListener contactListener = new PhysixComponentAwareContactListener();
physixSystem.getWorld().setContactListener(contactListener);
contactListener.addListener(TriggerComponent.class, new TriggerListener());
PhysixContactListener / PhysixContactAdapter
Just as with Box2D, there is a listener Interface:
public interface PhysixContactListener {
void beginContact(PhysixContact contact);
void endContact(PhysixContact contact);
void preSolve(PhysixContact contact, Manifold oldManifold);
void postSolve(PhysixContact contact, ContactImpulse impulse);
}
The only difference is that you get a PhysixContact
instead of a Contact
object.
PhysixContactAdapter
just implements all methods from PhysixContactListener
, so you just need to override the ones you actually need.
PhysixContact
PhysixContact
differs from Contact
a bit:
- In
Contact
you had the fixtures "A" and "B", but no idea which is which. - In `PhysixContact you get the fixtures "My" and "Other".
- "My" is always the fixture which has the specified component class.
- "Other" is the other fixture, but it might also have the specified component class!
All methods from Contact
are proxied by PhysixContact
. Plus you get a couple of getMy* and getOther* Methods. Check out your code-completion.