Menu

Box2D Game Objects Part 1

As promised, this starts the series on how to create custom Game Objects (GOs) in AGE.

Our example uses the box2d framework, for creating physics-based action with AGE. We assume you are somewhat familiar with box2d and won't elaborate too much on its internals.

box2d is made up of a few distinct components, and we will concentrate on the World and Body components, as these will get you started. This first part will concentrate on the internals of the World component implemented for AGE.

public class Box2DWorld extends GameObjectWithProperties implements RequireResourceLoader, TimerCallback, LoadedCallback, UnloadedCallback {
public static final String NAME = "World";
public static class Property {
    public static int LOWER = Constants.Property.USER_DEFINED_START;
    public static int UPPER = Constants.Property.USER_DEFINED_START + 1;
    public static int INITIAL_GRAVITY = Constants.Property.USER_DEFINED_START + 2;
}
World m_world;
AABB m_worldAABB;
final Installer in;
final String[] route;
// list targets (for sequential/random access)
final ArrayList<Box2DBody> list;
// map from box entity to our entity
final HashMap<Body, Box2DBody> bodymap;
final int dur;
final Vec2 force;
/**
 * Ctor.
 * @param in For triggering events.
 * @param route Event route for Box2D listeners.
 */
public Box2DWorld(int dur, Installer in, String[] route) {
    super(NAME, true);
    this.dur = dur;
    this.force = new Vec2();
    this.in = in;
    this.route = route;
    this.list = new ArrayList<Box2DBody>();
    this.bodymap = new HashMap<Body, Box2DBody>();
    this.set(Property.LOWER, new Vec2(-10f, -10f));
    this.set(Property.UPPER, new Vec2(10f, 10f));
    this.set(Property.INITIAL_GRAVITY, new Vec2(0f, -9f));
}
final class GameContactListener implements ContactListener {
    @Override
    public void add(ContactPoint point) {
    }
    @Override
    public void persist(ContactPoint point) {
    }
    @Override
    public void remove(ContactPoint point) {
    }
    @Override
    public void result(ContactResult point) {
        //Log.d("Contact", "impulse=" + point.normalImpulse + ",timpulse=" + point.tangentImpulse);
        final Box2DBody part1 = (Box2DBody) point.shape1.m_body.getUserData();
        final Box2DBody part2 = (Box2DBody) point.shape2.m_body.getUserData();
        try {
            in.event(new Contact(part1, part2, point), route);
        } catch (Exception e) {
        }
    }
}
final class GameBoundsListener implements BoundaryListener {
    @Override
    public void violation(Body body) {
        //Log.d("Boundary", "body=" + body + ",obj=" + body.getUserData());
        try {
            // box2d body is going away after this call; do bookkeeping
            final Box2DBody bb = bodymap.get(body);
            if(bb != null) {
                // do the bookkeeping for box2d
                bb.unload(Box2DWorld.this, bodymap);
                // pass it on
                in.event(new BoundsViolation(bb), route);
            }
        } catch (Exception e) {
        }
    }
}
/**
 * Override to change how forces are calculated.
 * @param force Set to the current force.
 */
protected void getForce(Vec2 force) {
    GlobalForces.get(force);
    force.x *= GlobalForces.X_FACTOR;
    force.y *= GlobalForces.Y_FACTOR;
}
/**
 * Initialize box2d instance and attach listeners for contact and boundary.
 */
@Override
public void load(ResourceLoader rl, Services svc) {
    m_worldAABB = new AABB((Vec2)getAs(Property.LOWER), (Vec2)getAs(Property.UPPER));
    final boolean doSleep = true;
    m_world = new World(m_worldAABB, (Vec2)getAs(Property.INITIAL_GRAVITY), doSleep);
    m_world.setContactListener(new GameContactListener());
    m_world.setBoundaryListener(new GameBoundsListener());
}
/**
 * Create a body in box2d.
 * @param bd body def.
 * @return new instance.
 */
public Body createBody(BodyDef bd) { return m_world.createBody(bd); }
/**
 * Create a joint in box2d.
 * @param jd joint def.
 * @return new instance.
 */
public Joint createJoint(JointDef jd) { return m_world.createJoint(jd); }
/**
 * Destroy body in box2d.
 * @param bd target.
 */
public void destroyBody(Body bd) { m_world.destroyBody(bd); }
/**
 * Destroy joint in box2d.
 * @param jd target.
 */
public void destroyJoint(Joint jd) { m_world.destroyJoint(jd); }
/**
 * Get the number of bodys reported by box2d.
 * Only call after Loaded.
 * @return
 */
public int getBodyCount() { return m_world.getBodyCount(); }
/**
 * Get the current gravity reported by box2d.
 * Only call after Loaded.
 * @return
 */
public Vec2 getGravity() { return m_world.getGravity(); }
/**
 * Return the bodys that match all the filter flags.
 * @param flags set of flags.
 * @param list target list.
 */
@SuppressWarnings("unchecked")
public <T extends Box2DBody> void filterAll(int flags, ArrayList<T> list) {
    for(int ix = 0; ix < list.size(); ix++) {
        final Box2DBody bb = list.get(ix);
        if((bb.filterFlags & flags) == flags) {
            list.add((T)bb);
        }
    }
}
/**
 * Return the bodys that match any of the filter flags.
 * @param flags set of flags.
 * @param list target list.
 */
@SuppressWarnings("unchecked")
public <T extends Box2DBody> void filterAny(int flags, ArrayList<T> list) {
    for(int ix = 0; ix < list.size(); ix++) {
        final Box2DBody bb = list.get(ix);
        if((bb.filterFlags & flags) != 0) {
            list.add((T)bb);
        }
    }
}
public void resetGravity() {
    m_world.setGravity((Vec2)getAs(Property.INITIAL_GRAVITY));
}
public void adjustGravity(Vec2 gravity) {
    final Vec2 gv = m_world.getGravity();
    final Vec2 ng = new Vec2(gv.add(gravity));
    m_world.setGravity(ng);
}
public Shape[] query(AABB box, int maxc) { return m_world.query(box, maxc); }
/**
 * Connect Box2DBody instances to the world.
 */
@Override
public void loaded(GameObject arg0, Exception arg1, Locator lc) {
    if(arg1 == null && arg0 instanceof Box2DBody) {
        final Box2DBody bb = (Box2DBody)arg0;
        list.add(bb);
        bb.load(this, bodymap);
    }
}
@Override
public void unloaded(GameObject arg0, Exception arg1, Locator arg2) {
    if(arg1 == null && arg0 instanceof Box2DBody) {
        final Box2DBody bb = (Box2DBody)arg0;
        list.remove(bb);
    }
}
@Override
public void execute(long delta, long elapsed, boolean last, Locator lc, Installer in) {
    if (delta > 0) {
        if(list.size() > 0) {
            // apply forces
            getForce(force);
            for(int ix = 0; ix < list.size(); ix++) {
                final Box2DBody bx = list.get(ix);
                if(bx instanceof UnderForce) {
                    ((UnderForce)bx).applyForce(force);
                }
            }
        }
        m_world.step((float)delta/1000f, 6);
        if(list.size() > 0) {
            // sync new positions
            for(int ix = 0; ix < list.size(); ix++) {
                final Box2DBody bx = list.get(ix);
                bx.sync();
            }
        }
    }
}
@Override
public boolean getRegisterOnInstall() {
    return true;
}
@Override
public void setConfig(TimerConfig tc, int tbt) {
    tc.durationMS = dur;
    tc.continuous = true;
    tc.autoRepeat = true;
}
}
Posted by g-dollar 2013-06-04 | Draft

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.