Moved from Box2D Alchemy to Box2D AS3

In the Citrus Engine, people knows that I’m very attached to the Box2D Alchemy version. Thanks to Alchemy we should have better performance than the pure AS3 version. Also this Alchemy version had some cool features mostly on the way you could manage collision : you don’t use a gobal contact listener like in any Box2D version, you use something very closer to AS3 event management. Example :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
override protected function createFixture():void
{
	super.createFixture();
 
	_fixture.m_reportBeginContact = true;
	_fixture.addEventListener(ContactEvent.BEGIN_CONTACT, handleBeginContact);
}
 
protected function handleBeginContact(e:ContactEvent):void
{
	_contact = e.other.GetBody().GetUserData() as Box2DPhysicsObject;
	if (!e.other.IsSensor())
		explode();
}

I love this way. Anyway, many users ask me to change it… let’s go!

Box2D AS3
This is the contact listener class :

package com.citrusengine.physics.box2d {
 
	import Box2D.Dynamics.b2ContactImpulse;
	import Box2D.Collision.b2Manifold;
	import Box2D.Dynamics.Contacts.b2Contact;
	import Box2D.Dynamics.b2ContactListener;
 
	/**
	 * Used to report the contact's interaction between object.
	 */
	public class Box2DContactListener extends b2ContactListener {
 
		public function Box2DContactListener() {
		}
 
		override public function BeginContact(contact:b2Contact):void {
 
			contact.GetFixtureA().GetBody().GetUserData().handleBeginContact(contact);
			contact.GetFixtureB().GetBody().GetUserData().handleBeginContact(contact);
		}
 
		override public function EndContact(contact:b2Contact):void {
 
			contact.GetFixtureA().GetBody().GetUserData().handleEndContact(contact);
			contact.GetFixtureB().GetBody().GetUserData().handleEndContact(contact);
		}
 
		override public function PreSolve(contact:b2Contact, oldManifold:b2Manifold):void {
 
			contact.GetFixtureA().GetBody().GetUserData().handlePreSolve(contact, oldManifold);
			contact.GetFixtureB().GetBody().GetUserData().handlePreSolve(contact, oldManifold);
		}
 
		override public function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void {
 
			contact.GetFixtureA().GetBody().GetUserData().handlePostSolve(contact, impulse);
			contact.GetFixtureB().GetBody().GetUserData().handlePostSolve(contact, impulse);
		}
 
	}
}

And then I override this function handleBeginContact, handleEndContact, handlePreSolve, handlePostSolve with what I need. And I use two very useful function to work the same way that I used to do with the Alchemy version :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * In Box2D we are blind concerning the collision, we are never sure which body is the collider. This function should help.
 * Call this function to obtain the colliding physics object.
 * @param self : in CE's code, we give this. In your code it will be your hero, a sensor, ...
 * @param the contact
 * @return the collider
 */
static public function CollisionGetOther(self:Box2DPhysicsObject, contact:b2Contact):Box2DPhysicsObject {
	return self == contact.GetFixtureA().GetBody().GetUserData() ? contact.GetFixtureB().GetBody().GetUserData() : contact.GetFixtureA().GetBody().GetUserData();
}
 
/**
 * In Box2D we are blind concerning the collision, we are never sure which body is the collider. This function should help.
 * Call this function to obtain the collided physics object.
 * @param self : in CE's code, we give this. In your code it will be your hero, a sensor, ...
 * @param the contact
 * @return the collided
 */
static public function CollisionGetSelf(self:Box2DPhysicsObject, contact:b2Contact):Box2DPhysicsObject {
	return self == contact.GetFixtureA().GetBody().GetUserData() ? contact.GetFixtureA().GetBody().GetUserData() : contact.GetFixtureB().GetBody().GetUserData();
}

And finally an example how it is used :

1
2
3
4
5
6
7
8
9
10
11
12
13
override public function handlePreSolve(contact:b2Contact, oldManifold:b2Manifold):void {
 
	if (!_ducking)
		return;
 
	var other:Box2DPhysicsObject = Box2DPhysicsObject.CollisionGetOther(this, contact);
 
	var heroTop:Number = y;
	var objectBottom:Number = other.y + (other.height / 2);
 
	if (objectBottom < heroTop)
		contact.SetEnabled(false);
}

Pretty cool, isn’t it? I know it will be a problem to upgrade on this last version, but it will not be as hard as it sounds since 99% of the code are similar. Just take a look on this new way to handle events.

Performance
When I made the transition, I told myself : “We shouldn’t see a big difference on a browser version, and if it sucks more than Alchemy on mobile we don’t care since there is Nape”! So I made a test with my quick demo : Live4Sales game. And this is the result on a iPad3 :

Compare now to the Box2D Alchemy & Nape versions : in this previous stress test. Holy s***! Box2D AS3 is very quicker than Alchemy version, really close to Nape in fact. So much close that we should make other tests with different mobiles and other type of games. Nape is always faster, but the difference is small. No more tax, better performances : definitely a good move!
All the demos have been updated on the GitHub.

Version 3?
We’ve never been so close to the final release, it should be in November. I would like to emphasize two points :
– the 3D part (handled with Away3D & AwayPhysics) is a sandbox for me. I never pretend to offer the best 3D game engine like you can have with Unity3D, how could I compare with them?, but just a good engine where you could create quiclky some 3d physics with a character, sensors… It could be used for simple games or for interactive websites. This 3D part will evolve along the Citrus Engine V3.
– the entity/component system is not bad, but not as useful as I would. Since some time, it is in stand by. It is not the main features wished by members, so I will refocus on it after. At the moment, use it at your own risk!

2 thoughts on “Moved from Box2D Alchemy to Box2D AS3

Leave a Reply

Your email address will not be published. Required fields are marked *