In Part 2 of this series, we look at the Body component for AGE.
The primary purpose for this GO is to handle the box2d bookkeeping for Body instances, map that information to its drawable resources, and provide an abstract base class for more complicated objects.
In here we set up some more infrastructure by way of an abstract class:
Notice how none of this has to do with the visual rendering, that's for DrawableGameObject!
Let's get started!
public abstract class Box2DBody extends DrawableGameObject {
protected Body body;
protected BodyDef bd;
protected boolean passive;
public int filterFlags;
public Box2DBody(String name, Geometry model, int depth) {
super(name, true, model, depth);
set(Constants.Property.TRANSFORM, new Transform());
}
These are the box2d configuration methods.
protected abstract void configureBodyDef(BodyDef bd);
protected abstract void configureBody(Body bd);
These are some convenience methods.
public boolean isPassive() { return passive; }
public void setPassive(boolean passive) { this.passive = passive; }
public boolean isBody(Body bd) { return bd == body; }
public float getInertia() { return body == null ? 0f : body.getInertia(); }
The sync method handles setting up the Transform property from the current values computed by the World update (see Part 1).
The Transform.scale is not adjusted. Subclasses are expected to set the scale appropriately based on other properties, e.g. Radius or HalfDimension or you can use "actual size" Geometry.
public void sync() {
if (body != null) {
final Vec2 wc = body.getWorldCenter();
final Transform tx = getAs(Constants.Property.TRANSFORM);
tx.tx = wc.x;
tx.ty = wc.y;
tx.rz = (float) Math.toDegrees(body.getAngle());
set(Constants.Property.TRANSFORM, tx);
}
}
The unload method handles the bookkeeping associated with removing an object from box2d. The bodymap is a parameter, because it is possible that a GO can have composite geometry in box2d.
The World calls this method during its BoundaryListener.violated method. The World does not call this during UnloadCallback because it is possible to recycle these GOs.
public void unload(Box2DWorld world, HashMap<Body, Box2DBody> bodymap) {
final Body prev = this.body;
if (prev != null) {
prev.setUserData(null);
world.destroyBody(prev);
body = null;
bodymap.remove(prev);
}
}
The reload method handles re-initializing box2d resources for the GO. This requires less work than initial loading, since BodyDef was already set up.
public void reload(Box2DWorld world, HashMap<Body, Box2DBody> bodymap) {
final Body prev = this.body;
if(prev != null) {
prev.setUserData(null);
world.destroyBody(prev);
this.body = null;
bodymap.remove(prev);
}
final Body bx = world.createBody(bd);
if (bx != null) {
// createBody() can fail!
configureBody(bx);
bx.setUserData(this);
bodymap.put(bx, this);
}
this.body = bx;
}
The load method handles the initial loading of box2d resources.
The World calls this method during its LoadedCallback.
public void load(Box2DWorld world, HashMap<Body, Box2DBody> bodymap) {
bd = new BodyDef();
configureBodyDef(bd);
final Body bx = world.createBody(bd);
if(bx != null) {
// createBody() can fail!
configureBody(bx);
bx.setUserData(this);
bodymap.put(bx, this);
}
this.body = bx;
}
}