// first, we create the Box2D body (as a dynamic box):
// Define the dynamic body. We set its position and call the body factory.
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0f, -40.0f);
b2Body* body = world.CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(1.0f, 1.0f);
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
// Add the shape to the body.
body->CreateFixture(&fixtureDef);
// now we'll create a ness-engine sprite
Ness::SpritePtr player = node->create_sprite("ness-engine/resources/gfx/crab.png");
player->set_anchor(Ness::Point::HALF);
player->set_blend_mode(Ness::BLEND_MODE_BLEND);
player->set_scale(0.25f);
// and we use our new connector class to connect the two and set starting position at top center:
NessBoxConnector playerBodyConnector(player, body);
playerBodyConnector.set_position(Ness::Point(renderer.get_screen_center().x, 0));
Tutorial 15: Physics
In this tutorial you will learn how to integrate physics into ness-engine
Before starting this tutorial be sure to setup an empty project with the Setup Tutorial.
That's it. :)
Now that you know about the physics engine, time to learn about particles!
continue to next tutorial -->
What are game physics?
Game physics is basically the simulation that produce position & rotation animation by mimicing real-world physics. for example, implementing gravity (making your sprites fall when not standing on a ground) is a basic form of game physics.
Physics and ness-engine
Ness-engine is a rendering engine and not a physics engine, and therefore does not implicitly support physics.
However, ness-engine can be easily integrated with any physical engine to create awesome physics with awesome graphics.
in this tutorial we will learn how to integrate ness-engine with a third party physical engine. if you rather skip this tutorial you can find a working example project with physics here.
Box2D
In this tutorial I've chosen Box2D as the external physics engine.
I picked Box2D for two main reasons:
-
it's an open-source project with the zlib license.
-
it's API is clean and simple.
so let's begin!
Step 1: get Box2D
Go to box2d.org and download either version 2.3.0 (what I used for this tutorial) or a later version. the source code comes with a visual studio project and you need to compile it yourself.
After compiling Box2D get the output lib files and add them to your project, and add the include path for Box2D. there are tutorials on Box2D that teach you how to do it.
Note: in this tutorial I used Box2D version 2.3.0.
Step 2: create a physical world
I will not teach much about Box2D (read the manual for more info), but I will provide here the code of how to create a physical world object:
int _tmain(int argc, _TCHAR* argv[])
{
Ness::init();
Ness::Renderer renderer("new project", Ness::Sizei(800,600), false);
// create the scene and a node
Ness::ScenePtr scene = renderer.create_scene();
Ness::NodePtr node = scene->create_node();
// Define the gravity vector (9.8 m/s^ is gravity on earth).
b2Vec2 gravity(0.0f, 9.8f);
// Construct a world object, which will hold and simulate the rigid bodies.
b2World world(gravity);
// Prepare for simulation.
int32 velocityIterations = 4;
int32 positionIterations = 0;
// create the events handlers
Ness::Utils::EventsPoller EventsPoller;
Ness::Utils::ApplicationEvents app;
EventsPoller.add_handler(app);
while( !app.got_quit() )
{
// poll all events
EventsPoller.poll_events();
// Instruct the world to perform a single step of simulation.
// It is generally best to keep the time step and iterations fixed.
world.Step(renderer.time_factor(), velocityIterations, positionIterations);
// run ness-engine frame
renderer.start_frame();
renderer.end_frame();
}
}
With the code above you now have ness-engine with running Box2D emulator. we can start using it.
Step 3: converting between ness-engine and Box2D
Ness-engine works with sprites, entities and nodes. Box2D works with rigid bodies and shapes. we need to connect the two.
first, we start by the basic units. ness-engine works with pixels, Box2D works with meters. ness-engine works with angles, Box2D works with radians.
so let's write two small utility classes to covnert between the two:
// ratio between meter and pixels. 1 meter == 100 pixels.
#define METERS_TO_PIXELS 100.0f
// convert from box2d stuff to ness-engine stuff
class Box2dToNess
{
public:
// convert box2d to ness point
static Ness::Point point(const b2Vec2& point) {return Ness::Point(point.x, point.y) * METERS_TO_PIXELS; }
// convert box2d to ness float
static float scalar(const float32 scalar) {return scalar * METERS_TO_PIXELS; }
// convert box2d to ness angle (radian to degree)
static int angle(const float32 radians) {return (int)RADIAN_TO_DEGREE(radians);}
};
// convert from ness-engine stuff to box2d stuff
class NessToBox2d
{
public:
// convert ness to box2d point
static b2Vec2 point(const Ness::Point& point) {return b2Vec2(point.x/METERS_TO_PIXELS, point.y/METERS_TO_PIXELS); }
// convert ness to box2d float
static float32 scalar(const float scalar) {return scalar / METERS_TO_PIXELS; }
// convert ness to box2d angle (degree to radian)
static float32 angle(const int angle) {return (float)DEGREE_TO_RADIAN(angle);}
};
First, we define how much pixels does a 1 meter worth. I think 100 pixels per meter is a good ratio. then we created two classes, one to convert from Box2D units to ness-engine units, and one to convert from ness-engine to Box2D.
Step 4: Connection ness-engine entity to a Box2D body
Now we need to create ness-engine entities and connect them with Box2D bodies. the "connection" between the two will be implemented by matching the position and rotation of the ness-engine entities to the position and rotation of the physical bodies. let's create a helper class for that:
// connect Box2D body to a ness-engine entity
class NessBoxConnector
{
private:
Ness::RenderablePtr m_ness_entity;
b2Body* m_box_body;
public:
// create the connector
NessBoxConnector(const Ness::RenderablePtr& nessEntity, b2Body* box2Dbody)
: m_ness_entity(nessEntity), m_box_body(box2Dbody)
{ }
// update position and rotation (must be called every frame)
inline void update()
{
m_ness_entity->set_position(Box2dToNess::point(m_box_body->GetPosition()));
m_ness_entity->set_rotation((float)RADIAN_TO_DEGREE(m_box_body->GetAngle()));
}
// set position
inline void set_position(const Ness::Point& NewPos)
{
m_box_body->SetTransform(NessToBox2d::point(NewPos), m_box_body->GetAngle());
}
// set rotation
inline void set_rotation(int NewAngle)
{
m_box_body->SetTransform(m_box_body->GetPosition(), NessToBox2d::angle(NewAngle));
}
};
What the class above does is to take a ness-engine renderable entity and a Box2D body, and connect them by setting the position and rotation of the ness-engine entity to fit the body. the class also implement functions to set rotation and position of the body, since now changing the position or rotation of the ness-entity will no longer work.
now let's create a ness-entity and give it a physical body:
now inside the main loop we need to call:
playerBodyConnector.update();
Woha! you should now see the crab falling pretty fast through the screen! if it's too fast you can play with the gravity or the timestep you advance the physical world with.

Now you have the basics of connecting ness-engine with Box2D. mastering Box2D and achieve more complicated stuff is up to you.
You can integrate ness-engine with other physical engines as well, you just need to write the right converters.
the full code for this tutorial can be found here.