Wizard Fu's Cocos2dx Platformer Game Engine v1.0.1

nat@wizardfu.com

See the file LICENSE for the license governing this code.

The abstract interface for a physics engine. The purpose of abstraction is to be able to swap in a different engine just by changing the cpp file.

The current implementation uses Box2d. Use it as an example for integrating a different physics engine, or rolling your own.

As this guide to implementing 2d platformers mentions, when using a physics library — like Box2d — care must be taken to ensure the physics feels just right. We find that by setting object bounciness to zero, using velocities instead of applying forces, and putting quality time into the physics can produce that “just right” feeling.

Filter categories and masks

A physics body has a filter category and filter mask. The filter category determines the type of the physics body (technically, it’s fixtures). The filter mask is a bit mask of all the filter categories that a physics body will make contact or collide with.

Physics filter categories make it easy for us to switch a body from solid to non-solid. They are also used by one way tiles.

enum
{
	kPhysicsCategoryTile = 0x01,
	kPhysicsCategoryTileOneWayUp = 0x02,
	kPhysicsCategoryTileOneWayRight = 0x04,
	kPhysicsCategoryTileOneWayDown = 0x08,
	kPhysicsCategoryTileOneWayLeft = 0x10,
	kPhysicsCategorySolid = 0x20,
	kPhysicsCategoryNonSolid = 0x40
};

const int kPhysicsFilterMask = 0xffff;
const int kPhysicsFilterMaskSolid = 0xffff ^ kPhysicsCategoryNonSolid;
const int kPhysicsFilterMaskNonSolid = 0xffff ^ kPhysicsCategorySolid;

Static fixture types

A static fixture is something that doesn’t move like the ground or a wall. Using these types helps distinguish a collision. “Is the player touching the ground or perhaps a wall to the left?”

enum
{
	kPhysicsFixtureFloor = 1,
	kPhysicsFixtureWall,
	kPhysicsFixtureCeiling
};

PhysicsDelegate

Classes that derive from the PhysicsDelegate abstract base can receive the beginContact message when a collision occurs between two objects.

The Level class inherits from this so it can pass on the appropriate contactBegan message to the physics objects.

#pragma once
#include "Headers.h"

class PhysicsDelegate
{
	public:
		virtual bool beginContact(void* userDataA, int categoryA, void* userDataB, int categoryB) = 0;
};

Physics

The physics engine. Contains a world, collision/contact listener and optionally a debug drawer. Set kDrawDebugBoxes to 1 to enable the drawing of debug boxes for the physics engine.

Implements a PhysicsWorld opaque pointer so that the actual type of PhysicsWorld is only known in the cpp.

Similarly implements a PhysicsListener opaque pointer member.

class Physics
{
	public:
		class PhysicsWorld;

		Physics();
		~Physics();

Create the physics engine given pixels per meter and a PhysicsDelegate.

		void create(float pixelsPerMeter, PhysicsDelegate* delegate);

Set the gravity for the world.

		void setGravity(const Vec2& gravity);

Create a static fixture (like the ground or a tile) in the world.

		void createStaticFixture(const Vec2& center, float x1, float y1, float x2, float y2, long type, float friction, int category);

Perform a time step on the world for the given duration using the given iterations.

		void step(float timeStep, int velocityIterations = kPhysicsVelocityIterations, int positionIterations = kPhysicsPositionIterations);

Get the physics world (used by PhysicsObject when creating a body).

		PhysicsWorld* getWorld() const;

Draw debug information.

		void drawDebug();

	private:
		PhysicsWorld* world;

		class PhysicsListener;
		PhysicsListener* listener;

		#if kDrawDebugBoxes
			PhysicsDebug* debugDraw;
		#endif
};

PhysicsObject

A dynamic object in the physics world. Dynamic objects have a position, velocity, rotation, filter category, filter mask, gravity scale, mass, friction and bounciness. Multiple fixtures can be added to physics objects to give them different shapes.

After time stepping the world, a physics object’s body’s position and rotation can be synchronized with a sprite or level object. This is how LevelObjects move.

PhysicsBody is an opaque pointer so we can neatly tuck away the implementation in the cpp.

class PhysicsObject
{
	public:
		PhysicsObject();
		virtual ~PhysicsObject();

Create the physics body given the physics engine and user data pointer.

		virtual void createBody(Physics& physics, void* userData);

Return true if the object has a PhysicsBody.

		bool hasBody() const;

Create a rectangular fixture and attach it to the physics body.

		virtual void createRectangularFixture(float width, float height);

Create a circular fixture and attach it to the physics body.

		virtual void createCircularFixture(float radius);

Get / set the body’s position (in pixels).

		Vec2 getBodyPosition() const;
		void setBodyPosition(const Vec2&);

Get / set the body’s angle (radians).

		float getBodyAngle() const;
		void setBodyAngle(float);

Get / set the filter category.

		unsigned getFilterCategory() const;
		void setFilterCategory(unsigned);

Get / set the filter mask.

		unsigned getFilterMask() const;
		void setFilterMask(unsigned);

Get / set if the object uses a solid filter category.

		bool isSolid() const;
		void setIsSolid(bool);

Get / set if the object is a sensor. Sensors do not collide with other objects.

		bool isSensor() const;
		void setIsSensor(bool);

Get / set if the object is a bullet. Intended for fast moving objects, this can prevent objects from accidentally passing through segments.

		bool isBullet() const;
		void setIsBullet(bool);

Set whether the object uses a fixed rotation. By default the object uses a fixed rotation. Set to false to enable rotation of the object’s angle as a result of physics calculations.

		void setFixedRotation(bool);

Get / set the object’s gravity scale. Set this to zero to enable flight. Set to one to use regular gravity.

		float getGravityScale() const;
		void setGravityScale(float);

Get / set the object’s mass.

		float getMass() const;
		void setMass(float);

Get / set the object’s friction.

		float getFriction() const;
		void setFriction(float);

Get / set the object’s bounciness.

		float getBounciness() const;
		void setBounciness(float);

Get / set the object’s velocity (in meters).

		Vec2 getVelocity() const;
		void setVelocity(const Vec2&);

Apply impulse sets the given velocity if the current velocity is less.

		void applyImpulse(const Vec2&);

For subclasses to implement. Is called by the PhysicsDelegate when a contact begins.

		virtual void contactBegan(void* userData) {}

Getters for if the object has contact in a certain region.

		GETTER(bool, hasContactBelow, HasContactBelow);
		GETTER(bool, hasContactAbove, HasContactAbove);
		GETTER(bool, hasContactToLeft, HasContactToLeft);
		GETTER(bool, hasContactToRight, HasContactToRight);
		GETTER(bool, hasContactWithObject, HasContactWithObject);
		GETTER(bool, hasContactWithPlatform, HasContactWithPlatform);

Reset contact data.

		void resetContacts();

Update contact data.

		void updateContacts();

Loop over all current contacts, calling the given function pointer passing in the user data pointer of the other object in the collision.

		void foreachContact(function<void (void*)> func);

Wake a body up (call this after game has been paused).

		void wakeBody();

	private:
		class PhysicsBody;
		PhysicsBody* body;
};
 
h