Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Create bitmap, expand the bitmap, suggestions. Cookies.

Credits go to Arthur Kalliokoski, Schyfis, and Tobias Dammers for helping out!
This thread is locked; no one can reply to it. rss feed Print
Create bitmap, expand the bitmap, suggestions. Cookies.
OnlineCop
Member #7,919
October 2006
avatar

I'm rewriting the game known as Chain Rxn flash game found on Facebook (http://apps.facebook.com/chainrxn/), or Boomshine on the "originator's" site (http://www.k2xl.com/games/boomshine/), and just have the early stages of the game up. Nothing fancy so far, and the code is more spaghetti than anything.

I have everything, for now, in a single file if anyone can give me feedback. What I am trying to do right now is make the ball grow from the size that it IS (roughly 16x16 or 32x32 pixels) to about 10 times its size, stay that big for a bit, then shrink to nothingness. Total time that it will be stretched and shrunk before being "dead" for the rest of the round: 3-4 seconds.

I could create another, larger, bitmap and reference that instead, using stretch_blit to either stretch up (from the smaller image) or stretch down (from the larger), though I don't know how that will affect the semi-transparent properties that each of these Ball objects will need. You can check out the flash counterparts to see what they need to try to look like when they "pop".

Suggestions and feedback? I'll be either updating my original post with changes, or adding new posts with code as I make modifications!

#SelectExpand
1#include <allegro.h> 2#include <cmath> 3#include <iostream> 4#include <vector> 5 6 7/* Stolen from BZflag (BattleZone Capture the Flag game)'s 'common.h' file. 8 * I thought I'd be nice enough to at least credit them that I took the code 9 * from their files, though I'll probably redo this later before it gets 10 * officially released. We'll see. Proper headings and all that will follow 11 * if it does. 12 */ 13 14/* (radians <--> degrees) conversion values */ 15#define DEG2RAD 0.0174532925199432957692369076848861271344287189 16#define RAD2DEG 57.29577951308232087679815481410517033240547247 17#define DEG2RADf ((float)DEG2RAD) 18#define RAD2DEGf ((float)RAD2DEG) 19 20 21const float possible_angles[] = 22{ 23 DEG2RADf * 30.0f, DEG2RADf * 45.0f, DEG2RADf * 60.0f, // Q1, top-right 24 DEG2RADf * 120.0f, DEG2RADf * 135.0f, DEG2RADf * 150.0f, // Q2, top-left 25 DEG2RADf * 210.0f, DEG2RADf * 225.0f, DEG2RADf * 240.0f, // Q3, bottom-left 26 DEG2RADf * 300.0f, DEG2RADf * 315.0f, DEG2RADf * 330.0f, // Q4, bottom-right 27}; 28const unsigned NUM_ANGLES = sizeof(possible_angles) / sizeof(float); 29 30 31const unsigned MIN_SPEED = 1; 32const unsigned MAX_SPEED = 4; 33 34 35 36class Ball 37{ 38public: 39 enum eState 40 { 41 STATE_MOVING = 0, // Kinda implemented 42 STATE_STOPPED, // Just barely implemented 43 STATE_DEAD // Not yet implemented, next on the the TODO 44 } state; 45 46 float x, y; // Will always be the CENTER of the ball, not the top-left corner 47 float angle; // Should be 30, 45, or 60 48 float velocity; // Slower typically makes levels HARDER, not EASIER 49 int color; // Uses an 'int' so it can include a transparency value 50 BITMAP* image; // Well, it has to be drawn as SOMETHING 51 52 53 Ball() : 54 state(STATE_MOVING), 55 x(0), y(0), 56 angle(possible_angles[0]), 57 velocity(0), 58 color(0), 59 image(NULL) 60 { 61 // Nothing to do 62 } 63 64 65 virtual ~Ball() 66 { 67 if (image) 68 { 69 destroy_bitmap(image); 70 image = NULL; 71 } 72 state = STATE_DEAD; 73 } 74 75 76 /* Must be created AFTER SCREEN_W and SCREEN_H are initialized */ 77 void Init(unsigned size = 32) 78 { 79 image = create_bitmap(size, size); 80 clear_bitmap(image); 81 int temp = rand(); 82 int color = makecol((temp >> 16) & 0xFF, (temp >> 8) & 0xFF, temp & 0xFF); 83 84 /* Sides and bottoms of circles look "sheared off" unless they have an 85 * odd number of pixels. This trims one pixel off the right and bottom 86 * edges automatically. 87 */ 88 if (size % 2 == 0) 89 { 90 circlefill(image, size / 2, size / 2, (size / 2) - 1, color); 91 } 92 else 93 { 94 circlefill(image, size / 2, size / 2, size / 2, color); 95 } 96 97 /* Guarantee that none start right near the edge to prevent some odd 98 * errors we get with the bouncing math. To do this, give a buffer of 99 * about 32 around each edge initially. 100 */ 101 x = (rand() % (SCREEN_W - 64)) + 32; 102 y = (rand() % (SCREEN_H - 64)) + 32; 103 angle = possible_angles[rand() % NUM_ANGLES]; 104 velocity = rand() % (MAX_SPEED - MIN_SPEED) + MIN_SPEED; 105 } 106 107 108 /* TODO: These will all be semi-transparent, with fully transparent outer 109 * borders so you don't see them overlap as they cross each other. 110 */ 111 void Draw(BITMAP* dest) 112 { 113 if (dest && image && state != STATE_DEAD) 114 { 115 blit(image, dest, 0, 0, x - image->w / 2, y - image->h / 2, image->w, image->h); 116 } 117 } 118 119 120 void Update(clock_t delta) 121 { 122 if (state == STATE_MOVING) 123 { 124 x += velocity * cos(angle) * delta; 125 y += velocity * sin(angle) * delta; 126 127 /* First handle horizontal corrections */ 128 if ((x - image->w / 2 < 0) || (x + image->w / 2 > SCREEN_W)) 129 { 130 velocity = -velocity; 131 angle = -angle; 132 } 133 134 /* Second handle vertical corrections */ 135 if ((y - image->h / 2 < 0) || (y + image->h / 2 > SCREEN_H)) 136 { 137 angle = -angle; 138 } 139 } 140 } 141 142 143 /* TODO: flesh out to actually grow to be a nice "explosion" for a few 144 * seconds, wait, then shrink rapidly then change state to STATE_DEAD. 145 */ 146 void Pop() 147 { 148 if (state == STATE_MOVING) 149 { 150 state = STATE_STOPPED; 151 } 152 } 153 154 155 /* This will be removed completely; the real game will never have this, 156 * and it is only for temporary debugging. 157 */ 158 void Unpop() 159 { 160 if (state == STATE_STOPPED) 161 { 162 state = STATE_MOVING; 163 } 164 } 165}; 166 167 168 169/////////////////////////////////////////////////////////////////////////////// 170 171volatile int ticks = 0; 172void ticker() 173{ 174 ++ticks; 175} END_OF_FUNCTION(ticker) 176 177 178 179/////////////////////////////////////////////////////////////////////////////// 180 181volatile int game_time = 0; 182void game_time_ticker() 183{ 184 ++game_time; 185} END_OF_FUNCTION(game_time_ticker) 186 187 188const int updates_per_second = 60; 189 190 191 192/////////////////////////////////////////////////////////////////////////////// 193 194int main(int argc, char** argv) 195{ 196 unsigned int numBalls = 16; 197 if (argc == 2) 198 { 199 numBalls = atoi(argv[1]); 200 } 201 202 /* Double buffer, where everything is drawn before it's dumped to the screen */ 203 BITMAP *screen_buffer = NULL; 204 205 int x = 0; 206 int y = 0; 207 int color = 0; 208 209 bool quit = false; 210 211 int fps = 0; 212 int frames_done = 0; 213 int old_time = 0; 214 215 int frames_array[10]; //an array to store the number of frames we did during the last 10 tenths of a second 216 int frame_index = 0; //used to store the index of the last updated value in the array 217 for (int ii = 0; ii < 10; ++ii) 218 { 219 frames_array[ii] = 0; //initialize the array to 0 220 } 221 222 223 224 srand(time(NULL)); 225 226 227 228 allegro_init(); 229 install_keyboard(); 230 install_timer(); 231 232 LOCK_VARIABLE(ticks); 233 LOCK_FUNCTION(ticker); 234 install_int_ex(ticker, BPS_TO_TIMER(updates_per_second)); 235 236 LOCK_VARIABLE(game_time); 237 LOCK_FUNCTION(game_time_ticker); 238 install_int_ex(game_time_ticker, BPS_TO_TIMER(10)); //i.e. game time is in tenths of seconds 239 240 241 set_color_depth(16); 242 if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0) != 0) 243 { 244 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); 245 allegro_message("Unable to set any graphic mode\n%s\n", allegro_error); 246 return -1; 247 } 248 249 250 /* Create the arena: should be the same size as the window that the balls 251 * will use to bounce around in. 252 */ 253 screen_buffer = create_bitmap(SCREEN_W, SCREEN_H); 254 clear_bitmap(screen_buffer); 255 256 x = SCREEN_W / 2; 257 y = SCREEN_H / 2; 258 color = makecol(255, 0, 0); 259 260 261 std::vector<Ball*> balls; 262 Ball* ball; 263 for (unsigned i = 0; i < numBalls; ++i) 264 { 265 ball = new Ball; 266 ball->Init(16); 267 balls.push_back(ball); 268 } 269 270 271 272 /* One ball, at random, will be able to be paused and unpaused when the 273 * SPACE and ENTER key are pressed, respectively. Just to see that the 274 * Pop() functionality is initially working like it should, and that the 275 * Ball object is properly changing states. 276 */ 277 unsigned randomly_targeted_ball = rand() % numBalls; 278 279 280 while (!quit) 281 { 282 while (ticks == 0) 283 { 284 rest(100 / updates_per_second); 285 } 286 287 288 while (ticks > 0) 289 { 290 int old_ticks = ticks; 291 292 // Do logic start 293 // /*{*/ 294 295 //DoLogic(); 296 if (key[KEY_ESC]) 297 { 298 quit = true; 299 } 300 301 if (key[KEY_SPACE]) 302 { 303 balls[randomly_targeted_ball]->Pop(); 304 } 305 if (key[KEY_ENTER]) 306 { 307 balls[randomly_targeted_ball]->Unpop(); 308 } 309 310 for (std::vector<Ball*>::iterator itr_b = balls.begin(); itr_b != balls.end(); ++itr_b) 311 { 312 Ball* ball = *itr_b; 313 if (ball) 314 { 315 ball->Update(ticks); 316 } 317 } 318 319 // /*}*/ 320 // Do logic end 321 322 --ticks; 323 324 if (old_ticks <= ticks) 325 { 326 break; 327 } 328 } 329 330 331 if (game_time >= old_time + 1) // i.e. a 0.1 second has passed since we last counted the frames 332 { 333 fps -= frames_array[frame_index]; //decrement the fps by the frames done a second ago 334 frames_array[frame_index] = frames_done; //store the number of frames done this 0.1 second 335 fps += frames_done; //increment the fps by the newly done frames 336 337 frame_index = (frame_index + 1) % 10; //increment the frame index and snap it to 10 338 339 frames_done = 0; 340 old_time += 1; 341 } 342 343 344 // Draw everything start 345 // /*{*/ 346 347 //DrawEverything(); 348 for (std::vector<Ball*>::iterator itr_b = balls.begin(); itr_b != balls.end(); ++itr_b) 349 { 350 Ball* ball = *itr_b; 351 ball->Draw(screen_buffer); 352 } 353 354 /* Blit buffer to screen */ 355 blit(screen_buffer, screen, 0, 0, (SCREEN_W - screen_buffer->w) / 2, (SCREEN_H - screen_buffer->h) / 2, screen_buffer->w, screen_buffer->h); 356 357 /* clears sprite buffer with color 0 */ 358 clear_bitmap(screen_buffer); 359 360 // /*}*/ 361 // Draw everything end 362 363 ++frames_done; 364 } 365 366 367 /* Free the memory for all allocated balls. This should happen at the end 368 * of every level, which is why I'm writing it (instead of having the end 369 * of the program clean up the memory for me). 370 */ 371 for (std::vector<Ball*>::iterator itr_b = balls.begin(); itr_b != balls.end(); ++itr_b) 372 { 373 Ball* ball = *itr_b; 374 if (ball) 375 { 376 delete ball; 377 ball = NULL; 378 } 379 } 380 381 382 return 0; 383} END_OF_MAIN()

Arthur Kalliokoski
Second in Command
February 2005
avatar

I'd say OpenGL would be easiest (allegrogl)

They all watch too much MSNBC... they get ideas.

Schyfis
Member #9,752
May 2008
avatar

If you draw the balls as primitive circles (instead of a pre-created bitmap), you can easily change the size of them.
By using drawing_mode(DRAW_MODE_TRANS, NULL, 0, 0); in combination with set_trans_blender(255, 255, 255, my_alpha_value); you can preserve the transparency effect of the primitives.

________________________________________________________________________________________________________
[freedwill.us]
[unTied Games]

OnlineCop
Member #7,919
October 2006
avatar

Arthur: I've used OpenGL but not allegrogl, so I'll take a look at it; it seems like the most logical approach, since I'd really rather have an arbitrary shape (say, for example, changing the shape from a sphere to a rotating triangle or even a "chain link" so I can get away with making a knock-off without stepping on anyone's copyright) and OpenGL is the best way to handle that.

Schyfis: Would 'my_alpha_value' be black (you've no doubt seen the code, and may have seen that the surrounding color appears to be "black" around the outer edge of the balls) or a different value? Will I need it if I decide to try to use OpenGL as Arthur suggested?

Tobias Dammers
Member #2,604
August 2002
avatar

OnlineCop said:

I've used OpenGL but not allegrogl,

In that case, you can treat allegrogl as a convenience library for setting up an OpenGL window in a cross-platform way, and adding allegro's input, sound, file, and what have you routines to it. All regular OpenGL commands are available in allegrogl, plus it loads extensions for you automatically.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Schyfis
Member #9,752
May 2008
avatar

OnlineCop said:

Would 'my_alpha_value' be black

I'm not sure what you mean. my_alpha_value is a value from 0 to 255, with 0 being fully transparent and 255 being fully opaque.

________________________________________________________________________________________________________
[freedwill.us]
[unTied Games]

OnlineCop
Member #7,919
October 2006
avatar

How's the best way to transition states over a period of time?

First, the balls need to be bouncing around and ignore all collisions with everything EXCEPT balls whose states are non-STATE_MOVING (or STATE_DEAD).

When a ball gets popped, it needs to grow for a short period of time, stay big, then shrink, and finally die.

I thought that keyframes would be the best way to go with this. Until they actual get popped, there will be no keyframe (or, there will be a single keyframe whose value returns the default value or size of the ball).

As soon as the ball collides with another popped ball, it adds four more keyframes:

(max_size, time_now + 2 seconds)
(max_size, time_now + 4 seconds)
(min_size, time_now + 6 seconds)

After 6 seconds, the ball dies.

Of course, there will need to be some sort of method of dealing with a dead ball so it is no longer updated.

Any ideas or thoughts on this?

Should I be using some sort of Event manager instead to be handing the Pop event instead, or each of the different life cycles, rather than keyframes? What's the best way to do this part in your opinion(s)?

Go to: