Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Allegro and Box2D

Credits go to Neil Walker and Slartibartfast for helping out!
This thread is locked; no one can reply to it. rss feed Print
Allegro and Box2D
Naturally
Member #12,748
April 2011

I have this code which is slightly modified from the Box2D HelloWorld solution to use Allegro and make the box fall down instead of up:

Quote:

/*

*

  • This software is provided 'as-is', without any express or implied

  • warranty. In no event will the authors be held liable for any damages

  • arising from the use of this software.

  • Permission is granted to anyone to use this software for any purpose,

  • including commercial applications, and to alter it and redistribute it

  • freely, subject to the following restrictions:

  • 1. The origin of this software must not be misrepresented; you must not

  • claim that you wrote the original software. If you use this software

  • in a product, an acknowledgment in the product documentation would be

  • appreciated but is not required.

  • 2. Altered source versions must be plainly marked as such, and must not be

  • misrepresented as being the original software.

  • 3. This notice may not be removed or altered from any source distribution.

*/

#include <Box2D/Box2D.h>
#include <allegro5\allegro.h>
#include <allegro5\allegro_image.h>
#include <cstdio>

const float32 SCALE(30.0f);
const int HEIGHT(480);
const int WIDTH(640);
// This is a simple example of building and running a simulation
// using Box2D. Here we create a large ground box and a small dynamic
// box.
// There are no graphics for this example. Box2D is meant to be used
// with your rendering engine in your game engine.
int main(int argc, char** argv)
{
B2_NOT_USED(argc);
B2_NOT_USED(argv);

// Define the gravity vector.
b2Vec2 gravity(0.0f, 10.0f);

// Do we want to let bodies sleep?
bool doSleep = true;

// Construct a world object, which will hold and simulate the rigid bodies.
b2World world(gravity, doSleep);

// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0.0f / SCALE, 420.0f / SCALE);

// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
b2Body* groundBody = world.CreateBody(&groundBodyDef);

// Define the ground box shape.
b2PolygonShape groundBox;

// The extents are the half-widths of the box.
groundBox.SetAsBox(50.0f / SCALE, 10.0f / SCALE);

// Add the ground fixture to the ground body.
groundBody->CreateFixture(&groundBox, 0.0f);

// Define the dynamic body. We set its position and call the body factory.
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0f / SCALE, 4.0f / SCALE);
b2Body* body = world.CreateBody(&bodyDef);

// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(30.0f / SCALE, 30.0f / SCALE);

// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;

// Set the box density to be non-zero, so it will be dynamic.
fixtureDef.density = 1.0f;

// Override the default friction.
fixtureDef.friction = 0.3f;

fixtureDef.restitution = 0.4f;
// Add the shape to the body.
body->CreateFixture(&fixtureDef);

// Prepare for simulation. Typically we use a time step of 1/60 of a
// second (60Hz) and 10 iterations. This provides a high quality simulation
// in most game scenarios.
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 6;
int32 positionIterations = 2;

al_init();
al_init_image_addon();
ALLEGRO_DISPLAY *display(al_create_display(WIDTH, HEIGHT));
//ALLEGRO_BITMAP *ground(al_create_bitmap((50.0f / SCALE) * 2 * SCALE, (10.0f / SCALE) * 2 * SCALE));
ALLEGRO_BITMAP *ground(al_create_bitmap(100, 20));
al_set_target_bitmap(ground);
al_clear_to_color(al_map_rgb(255,255,255));
//ALLEGRO_BITMAP *box(al_create_bitmap((10.0f / SCALE) * 2 * SCALE, (10.0f / SCALE) * 2 * SCALE));
ALLEGRO_BITMAP *box(al_create_bitmap(60, 60));

al_set_target_bitmap(box);
al_clear_to_color(al_map_rgb(255, 0, 255));
al_set_target_bitmap(al_get_backbuffer(display));
al_clear_to_color(al_map_rgb(0,0,0));
al_flip_display();
// This is our little game loop.
for (int32 i = 0; true; ++i)
{
al_clear_to_color(al_map_rgb(0,0,0));
// Instruct the world to perform a single step of simulation.
// It is generally best to keep the time step and iterations fixed.
world.Step(timeStep, velocityIterations, positionIterations);

// Clear applied body forces. We didn't apply any forces, but you
// should know about this function.
world.ClearForces();

// Now print the position and angle of the body.
b2Vec2 position = body->GetPosition();
b2Vec2 groundPos = groundBody->GetPosition();
float32 angle = body->GetAngle();

al_draw_bitmap(ground, groundPos.x * SCALE, groundPos.y * SCALE, 0);
//al_draw_bitmap(box, position.x * SCALE , position.y * SCALE - 20, 0);
al_draw_bitmap(box, position.x * SCALE , position.y * SCALE, 0);
al_flip_display();
al_rest(1.0 / 60.0);
printf("%4.2f %4.2f %4.2f\n", position.x, position.y, angle);
}

// When the world destructor is called, all bodies and joints are freed. This can
// create orphaned pointers, so be careful about your world management.

return 0;
}

I'm trying to learn Box2D, and so far my biggest/only problem is converting meters to pixels. I have a variable to says 30 pixels = 1 meter, but when I draw bitmaps to visually see the ground and the box in the Box2D HelloWorld code, it doesn't seem to draw right. The box falls through the top of the "ground" and lands on the bottom of it.

The only way around it is to + 20 when I draw the y axis for the ground, or - 20 from the y axis when I draw the y axis for the falling box, or +10 to the y axis for the ground and -10 for the y axis for the falling box. I don't want to do this 'cause it makes things overly complicated for me especially if I use varying sizes for the ground and boxes. Plus when I look at other code all they do is multiply it by the scale to know where to draw it. It also makes me wonder if the x axis is even right, it's hard to know when I'm not 100% sure Allegro is drawing the bitmaps exactly where they would be in Box2D's world.

Help me please :( Being stuck on simple stuff like this that shouldn't be so problematic really discourages me. It's always the simple stuff I get stuck on >_>

Image: http://i.imgur.com/ISO7g.png

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

cgman24
Member #12,536
February 2011
avatar

Your code is very confusing-looking, so I made something similar from scratch. Hopefully it will help clear up some points for you. I tried to demonstrate some other things as well.

#SelectExpand
1 2#include <Box2D/Box2D.h> 3#include <allegro5/allegro.h> 4#include <cstdio> 5 6 7const int SCREEN_W = 640; 8const int SCREEN_H = 480; 9//The scale. 32 is a better number than 30, because almost all screen resolution dimensions are a multiple of 32. 10const int PIX_METER = 32; 11const int FPS = 60; 12int main(int argc, char** argv) 13{ 14 B2_NOT_USED(argc); 15 B2_NOT_USED(argv); 16 17 b2Vec2 gravity(0.0f, 10.0f); 18 bool doSleep = true; 19 b2World world(gravity, doSleep); 20 21 // Define the ground body. 22 b2BodyDef groundBodyDef; 23 //Box2D positions are the center. 24 //Put it in the middle: 10.0f, 7.5f 25 groundBodyDef.position.Set((float)(SCREEN_W/2)/PIX_METER, (float)(SCREEN_H/2)/PIX_METER); 26 27 b2Body* groundBody = world.CreateBody(&groundBodyDef); 28 b2PolygonShape groundBox; 29 30 // The extents are the half-widths of the box. 31 //I chose these numbers arbitrarily 32 float g_width = 6.0f; 33 float g_height = 2.4f; 34 groundBox.SetAsBox(g_width/2, g_height/2); 35 36 // Add the ground fixture to the ground body. 37 groundBody->CreateFixture(&groundBox, 0.0f); 38 39 // Define the dynamic body. We set its position and call the body factory. 40 b2BodyDef bodyDef; 41 bodyDef.type = b2_dynamicBody; 42 bodyDef.position.Set((float)(SCREEN_W/2)/PIX_METER, 0.0f); //top of the screen 43 b2Body* body = world.CreateBody(&bodyDef); 44 45 // Define another box shape for our dynamic body. 46 b2PolygonShape dynamicBox; 47 float d_width = 2.0f; 48 float d_height = 2.0f; 49 dynamicBox.SetAsBox(d_width/2, d_height/2); 50 51 b2FixtureDef fixtureDef; 52 fixtureDef.shape = &dynamicBox; 53 fixtureDef.density = 1.0f; 54 fixtureDef.friction = 0.3f; 55 body->CreateFixture(&fixtureDef); 56 57 //give the body some spin and some motion to make it interesting 58 body->SetAngularVelocity(0.8f); 59 b2Vec2 initial_v (1.6f, 0.0f); 60 body->SetLinearVelocity(initial_v); 61 62 // Prepare for simulation. Typically we use a time step of 1/60 of a 63 // second (60Hz) and 10 iterations. This provides a high quality simulation 64 // in most game scenarios. 65 float32 timeStep = 1.0f / FPS; 66 int32 velocityIterations = 10; 67 int32 positionIterations = 10; 68 69 //prepare for drawing 70 ALLEGRO_DISPLAY *display = NULL; 71 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 72 ALLEGRO_TIMER *timer = NULL; 73 ALLEGRO_BITMAP *ground = NULL; 74 ALLEGRO_BITMAP *box = NULL; 75 bool redraw = true; 76 //ALWAYS check return values of these Allegro functions. It will save you headaches later. 77 if(!al_init()) { 78 fprintf(stderr, "failed to initialize allegro!\n"); 79 return -1; 80 } 81 timer = al_create_timer(1.0 / FPS); 82 if(!timer) { 83 fprintf(stderr, "failed to create timer!\n"); 84 return -1; 85 } 86 87 display = al_create_display(SCREEN_W, SCREEN_H); 88 if(!display) { 89 fprintf(stderr, "failed to create display!\n"); 90 al_destroy_timer(timer); 91 return -1; 92 } 93 //typically, if using Box2D, you will have everything operating with Box2D coordinates throughout your game. 94 //This is because Box2D methods return in Box2D coordinates. 95 //Then, when drawing, convert them to "Allegro coordinates" using the scale. 96 //Here, the g_ variables are in Box2D coordinates, so we convert them to Allegro coordinates to represent the box on the screen. 97 ground = al_create_bitmap(g_width*PIX_METER, g_height*PIX_METER); 98 if(!ground) { 99 fprintf(stderr, "failed to create ground bitmap!\n"); 100 al_destroy_display(display); 101 al_destroy_timer(timer); 102 return -1; 103 } 104 box = al_create_bitmap(d_width*PIX_METER, d_height*PIX_METER); 105 if(!box) { 106 fprintf(stderr, "failed to create box bitmap!\n"); 107 al_destroy_bitmap(ground); 108 al_destroy_display(display); 109 al_destroy_timer(timer); 110 return -1; 111 } 112 113 al_set_target_bitmap(ground); 114 al_clear_to_color(al_map_rgb(255, 0, 255)); 115 al_set_target_bitmap(box); 116 al_clear_to_color(al_map_rgb(128, 128, 255)); 117 118 al_set_target_bitmap(al_get_backbuffer(display)); 119 120 event_queue = al_create_event_queue(); 121 if(!event_queue) { 122 fprintf(stderr, "failed to create event_queue!\n"); 123 al_destroy_bitmap(ground); 124 al_destroy_bitmap(box); 125 al_destroy_display(display); 126 al_destroy_timer(timer); 127 return -1; 128 } 129 130 al_register_event_source(event_queue, al_get_display_event_source(display)); 131 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 132 al_clear_to_color(al_map_rgb(0,0,0)); 133 al_flip_display(); 134 al_start_timer(timer); 135 // This is our little game loop. 136 while (1) 137 { 138 ALLEGRO_EVENT ev; 139 al_wait_for_event(event_queue, &ev); 140 141 if(ev.type == ALLEGRO_EVENT_TIMER) { 142 world.Step(timeStep, velocityIterations, positionIterations); 143 world.ClearForces(); 144 redraw = true; 145 } 146 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) { 147 break; 148 } 149 150 if (redraw && al_is_event_queue_empty(event_queue)) { 151 redraw = false; 152 al_clear_to_color(al_map_rgb(0,0,0)); 153 154 //draw the ground 155 //note that drawing is from the top-left corner, 156 //and NOT the center as it is in Box2D. 157 b2Vec2 g_pos = groundBody->GetPosition(); //the center 158 //we need to get the top left corner from this center 159 float g_x = g_pos.x - g_width/2; 160 float g_y = g_pos.y - g_height/2; 161 //now convert the coordinates and draw 162 al_draw_bitmap(ground, g_x*PIX_METER, g_y*PIX_METER, 0); 163 164 //do the same for the other box 165 b2Vec2 b_pos = body->GetPosition(); 166 float32 angle = body->GetAngle(); 167 //When using the rotated drawing function, the center is pinned to a point. 168 //so essentially, we can draw from the center. 169 float b_x = b_pos.x; 170 float b_y = b_pos.y; 171 172 float midx = al_get_bitmap_width(box)/2; 173 float midy = al_get_bitmap_height(box)/2; 174 175 al_draw_rotated_bitmap(box, midx, midy, b_x*PIX_METER, b_y*PIX_METER, angle, 0); 176 177 al_flip_display(); 178 } 179 180 } 181 182 // When the world destructor is called, all bodies and joints are freed. This can 183 // create orphaned pointers, so be careful about your world management. 184 185 return 0; 186}

Basically, you have two coordinate systems: Box2D and Allegro/Drawing. Box2D is in meters, and Allegro is in pixels. Throughout your game logic, use Box2D coordinates. This is easier because all Box2D methods return in Box2D coordinates (of course). Then, when you draw, you convert to Allegro coordinates using a scaling factor. Here, I set PIX_METER (should probably be PIXELS_PER_METER but I'm too lazy to type all that out) to 32, and use that when drawing. By doing it this way, you can easily change the scale, or do something like a zoom-in/out feature. You just have to keep track of which coordinate system each value is in (you could even prefix them with b2_ or a_ if you wanted. That's probably a good idea, actually).

Hope that helps.

-cgman

Slartibartfast
Member #8,789
June 2007
avatar

Just skimming, I'm guessing the problem is that Box2D coordinates for bodies give the center of the body, whereas drawing with allegro you specify the corner of the image.
Try something along the lines of:
al_draw_bitmap(ground, groundPos.x * SCALE - al_get_bitmap_width(ground), groundPos.y * SCALE - al_get_bitmap_height(ground), 0);
(or maybe a + for height, depending on how exactly you store your coordinates.

Neil Walker
Member #210
April 2000
avatar

The position is indeed the centre point of a shape (e.g. see SetAsBox function). Box2D works best if you think in metres as pixels doesn't make sense in a physics based system. It's entirely up to you where you set your screen origin (most people have centre 0,0 in the middle of the screen), but all you have to do is create a scale and apply that to every object to get a pixel location. In my little programs I made life a little simpler and moved 0 to the edge, e.g. I pretended my player sprite was 1m tall and 0.5m wide and used that as a base for every other object.

Regarding up and down, you've probably noticed that it's opposite in the y axis, i.e. increase y to go down the screen in Allegro but increase y to go up in Box2D. This is the same for opengl, etc. Just reverse your y axis.

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

Naturally
Member #12,748
April 2011

Thanks so much! I didn't know Box2D was giving me center coordinates >_<

This fixed it:

al_draw_bitmap(ground, (groundPos.x * SCALE)  - (al_get_bitmap_width(ground) / 2.0f), (groundPos.y * SCALE) - (al_get_bitmap_height(ground) / 2.0f), 0);
al_draw_bitmap(fallingBox, (position.x * SCALE) - (al_get_bitmap_width(fallingBox) / 2.0f), (position.y * SCALE) - (al_get_bitmap_height(fallingBox) / 2.0f), 0);

Thanks again! xD

Edit: Thanks to cgman24 too (Forgot to highlight your name in the credit thing) xD

Finished Code for anyone in future that ever needs help like this (Some numbers changed a little from first post):

#SelectExpand
1/* 2* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com 3* 4* This software is provided 'as-is', without any express or implied 5* warranty. In no event will the authors be held liable for any damages 6* arising from the use of this software. 7* Permission is granted to anyone to use this software for any purpose, 8* including commercial applications, and to alter it and redistribute it 9* freely, subject to the following restrictions: 10* 1. The origin of this software must not be misrepresented; you must not 11* claim that you wrote the original software. If you use this software 12* in a product, an acknowledgment in the product documentation would be 13* appreciated but is not required. 14* 2. Altered source versions must be plainly marked as such, and must not be 15* misrepresented as being the original software. 16* 3. This notice may not be removed or altered from any source distribution. 17*/ 18 19#include <Box2D/Box2D.h> 20#include <allegro5\allegro.h> 21#include <allegro5\allegro_image.h> 22#include <cstdio> 23 24const float32 SCALE(30.0f); 25const int HEIGHT(480); 26const int WIDTH(640); 27// This is a simple example of building and running a simulation 28// using Box2D. Here we create a large ground box and a small dynamic 29// box. 30// There are no graphics for this example. Box2D is meant to be used 31// with your rendering engine in your game engine. 32int main(int argc, char** argv) 33{ 34 B2_NOT_USED(argc); 35 B2_NOT_USED(argv); 36 37 // Define the gravity vector. 38 b2Vec2 gravity(0.0f, 10.0f); 39 40 // Do we want to let bodies sleep? 41 bool doSleep = true; 42 43 // Construct a world object, which will hold and simulate the rigid bodies. 44 b2World world(gravity, doSleep); 45 46 // Define the ground body. 47 b2BodyDef groundBodyDef; 48 groundBodyDef.position.Set(200.0f / SCALE, 420.0f / SCALE); 49 50 // Call the body factory which allocates memory for the ground body 51 // from a pool and creates the ground box shape (also from a pool). 52 // The body is also added to the world. 53 b2Body* groundBody = world.CreateBody(&groundBodyDef); 54 55 // Define the ground box shape. 56 b2PolygonShape groundBox; 57 58 // The extents are the half-widths of the box. 59 groundBox.SetAsBox(50.0f / SCALE, 10.0f / SCALE); 60 61 // Add the ground fixture to the ground body. 62 groundBody->CreateFixture(&groundBox, 0.0f); 63 64 // Define the dynamic body. We set its position and call the body factory. 65 b2BodyDef bodyDef; 66 bodyDef.type = b2_dynamicBody; 67 bodyDef.position.Set(255.5f / SCALE, 350.0f / SCALE); 68 b2Body* body = world.CreateBody(&bodyDef); 69 70 // Define another box shape for our dynamic body. 71 b2PolygonShape dynamicBox; 72 dynamicBox.SetAsBox(15.0f / SCALE, 15.0f / SCALE); 73 74 // Define the dynamic body fixture. 75 b2FixtureDef fixtureDef; 76 fixtureDef.shape = &dynamicBox; 77 78 // Set the box density to be non-zero, so it will be dynamic. 79 fixtureDef.density = 1.0f; 80 81 // Override the default friction. 82 fixtureDef.friction = 0.3f; 83 84 //fixtureDef.restitution = 0.4f; 85 // Add the shape to the body. 86 body->CreateFixture(&fixtureDef); 87 88 // Prepare for simulation. Typically we use a time step of 1/60 of a 89 // second (60Hz) and 10 iterations. This provides a high quality simulation 90 // in most game scenarios. 91 float32 timeStep = 1.0f / 60.0f; 92 int32 velocityIterations = 6; 93 int32 positionIterations = 2; 94 95 96 al_init(); 97 al_init_image_addon(); 98 ALLEGRO_DISPLAY *display(al_create_display(WIDTH, HEIGHT)); 99 //ALLEGRO_BITMAP *ground(al_create_bitmap((50.0f / SCALE) * 2 * SCALE, (10.0f / SCALE) * 2 * SCALE)); 100 ALLEGRO_BITMAP *ground(al_create_bitmap(100, 20)); 101 al_set_target_bitmap(ground); 102 al_clear_to_color(al_map_rgb(255,255,255)); 103 //ALLEGRO_BITMAP *box(al_create_bitmap((10.0f / SCALE) * 2 * SCALE, (10.0f / SCALE) * 2 * SCALE)); 104 ALLEGRO_BITMAP *fallingBox(al_create_bitmap(30, 30)); 105 106 al_set_target_bitmap(fallingBox); 107 al_clear_to_color(al_map_rgb(255, 0, 255)); 108 al_set_target_bitmap(al_get_backbuffer(display)); 109 al_clear_to_color(al_map_rgb(0,0,0)); 110 al_flip_display(); 111 // This is our little game loop. 112 for (int32 i = 0; true; ++i) 113 { 114 al_clear_to_color(al_map_rgb(0,0,0)); 115 // Instruct the world to perform a single step of simulation. 116 // It is generally best to keep the time step and iterations fixed. 117 world.Step(timeStep, velocityIterations, positionIterations); 118 119 // Clear applied body forces. We didn't apply any forces, but you 120 // should know about this function. 121 world.ClearForces(); 122 123 // Now print the position and angle of the body. 124 b2Vec2 position = body->GetPosition(); 125 b2Vec2 groundPos = groundBody->GetPosition(); 126 float32 angle = body->GetAngle(); 127 128 al_draw_bitmap(ground, (groundPos.x * SCALE) - (al_get_bitmap_width(ground) / 2.0f), (groundPos.y * SCALE) - (al_get_bitmap_height(ground) / 2.0f), 0); 129 //al_draw_bitmap(box, position.x * SCALE , position.y * SCALE - 20, 0); 130 al_draw_bitmap(fallingBox, (position.x * SCALE) - (al_get_bitmap_width(fallingBox) / 2.0f), (position.y * SCALE) - (al_get_bitmap_height(fallingBox) / 2.0f), 0); 131 al_flip_display(); 132 al_rest(1.0 / 5.0); 133 printf("%4.2f %4.2f %4.2f\n", position.x, position.y, angle); 134 } 135 136 // When the world destructor is called, all bodies and joints are freed. This can 137 // create orphaned pointers, so be careful about your world management. 138 139 return 0; 140}

Neil Walker
Member #210
April 2000
avatar

If you check out the Box2D forums there are quite a few add-ons people have created.

The two of note are buoyancy and 'mario' platforums (i.e. jump up through but not down).

Neil.
MAME Cabinet Blog / AXL LIBRARY (a games framework) / AXL Documentation and Tutorial

wii:0356-1384-6687-2022, kart:3308-4806-6002. XBOX:chucklepie

Go to: