Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » [A5] Drawing a sprite to a display

This thread is locked; no one can reply to it. rss feed Print
[A5] Drawing a sprite to a display
Havacore
Member #12,591
February 2011
avatar

Just as a little learning project, I want to make an allegro program that just has a sprite on the display, and it walks left when press left, right when I press right etc. Basically all I have done so far is make a pong game which just used primitive bitmaps, rather than loading an image. So basically I just need to know how sprites work.

I need to know where does the sprite come from? Do I take a sprite sheet from google and load a section of that image file into the program?

When the sprite moves, how exactly do I put in the animation? Do I time it so that while a keyboard event is occurring, the program cycles through a few different images on a sprite sheet?

And if there is anything else I should know then I would appreciate any advice you all could give me :)

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Here's a very broad description of how I do it :

#SelectExpand
1class Animation { 2private : 3 BITMAP** frames; 4 double duration; 5 double current_time; 6 int num_frames; 7 int frame_number; 8 9public : 10 void SetFrameTime(double tsec); 11 void AdvanceFrameTime(double tsec) {SetFrameTime(current_time + tsec);} 12 void Draw(BITMAP* bmp , int x , int y); 13}; 14 15class StateAnimation { 16private : 17 map<string , Animation*> 18 Animation* active_animation; 19public : 20 void SetState(string state); 21 void AdvanceFrameTime(double tsec) { 22 if (active_animation) { 23 active_animation->AdvanceFrameTime(tsec); 24 } 25 } 26 void Draw(BITMAP* bmp , int x , int y) { 27 if (active_animation) { 28 active_animation->Draw(bmp , x , y); 29 } 30 } 31/*...*/ 32}; 33 34class Character { 35private : 36 Animation move_left; 37 Animation move_right; 38 Animation crouch; 39 Animation stand_left; 40 Animation stand_right; 41 Animation jump_left; 42 Animation jump_right; 43 StateAnimation anim_state; 44/*...*/ 45};

For an animation, you need to know how long it displays for, how many frames there are, how much time has passed so far, and whether it loops or is static. Then you also need to know the current state of the animation, and how to set that state based on what your character is doing.

Havacore
Member #12,591
February 2011
avatar

oh nice! Good thing my class just covered how to do OOP!

But on a simpler scale. I don't even know how to draw a sprite to the display. I've looked through the allegro manual and I can't find any functions that take in an image file or anything to draw to the display. All I can think of is creating a bitmap, and filling it with a colour :-/

jason perkins
Member #10,524
January 2009

You can go and grab a sprite sheet off google, or make one yourself that looks like this:
603942

This is the code I use to get a single tile off this sheet, and scroll through them over time.

#SelectExpand
1 2CPlayer::CPlayer() 3{ 4 bmp = al_load_bitmap("player.bmp"); 5 al_convert_mask_to_alpha(bmp, (al_map_rgb(255, 0, 255))); 6 7 animationWidth = al_get_bitmap_width(bmp)/32; //the tiles are 32/32 8 animationHeight = al_get_bitmap_height(bmp)/32; 9 10 11 for (int h = 0; h< animationHeight; h++) 12 { 13 for (int w = 0; w<animationWidth; w++) 14 { 15 pallet.push_back(al_create_sub_bitmap(bmp, w*TILE_SIZE, h*TILE_SIZE, TILE_SIZE, TILE_SIZE)); 16 } 17 } 18 19 bmp = pallet[0]; 20} 21 22 23void CPlayer::animate(int state, int cycleTime) 24{ 25 26 animationCount++; 27 if (animationCount >= cycleTime) 28 { 29 animationCount = 0; 30 31 } 32 animationState = state; 33 34 if (state >= 0 && state <= 3) 35 { 36 direction = state; 37 } 38 39 bmp = pallet[state*animationWidth + (animationCount/(cycleTime/4))+1]; 40 41}

To actually draw this I call the players' parent draw function.

void CVisibleTile::draw()
{
  if(x+ TILE_SIZE >= 0 && x < SCREEN_W && y + TILE_SIZE >= 0 && y< SCREEN_H)
  {
    al_draw_bitmap(bmp, x, y, 0);
  }
}

Hope that helps. There's a good tutorial on the wiki for the basics of displaying bitmaps as well. http://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial/Input

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Havacore
Member #12,591
February 2011
avatar

I'm using allegro 5.

what is the pallet object you have at line 15 jason? I hope you don't mind, but I'm just copying down that code so that I can mess around with it later to get a better understanding of what's going on.

Just to clarify, what does al_convert_mask_to_alpha do? I noticed that the rgb code in your function is magenta, so does that just make it so that magenta is transparent?

And I have read all the tutorials so far. And I've implemented them into a little pong game (except font, which I can't get to work so far). The bitmap tutorial unfortunately just explains how to make a solid colored square, not an image. Or in this case, and image out of a bigger image like a sprite sheet.

SpectreNectar
Member #10,969
May 2009
avatar

Havacore said:

so does that just make it so that magenta is transparent?

Yes.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Havacore said:

The bitmap tutorial unfortunately just explains how to make a solid colored square, not an image. Or in this case, and image out of a bigger image like a sprite sheet.

Use al_load_bitmap to load your spritesheet, then create an array of ALLEGRO_BITMAP* and fill it in with al_create_bitmap and al_draw_bitmap_region or use al_create_sub_bitmap on your spritesheet. Then use al_draw_bitmap to draw your images.

Thomas Fjellstrom
Member #476
June 2000
avatar

I think I need to add a second example to that bitmap tutorial that uses the image addon.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

leixiong
Member #12,688
March 2011

Can we copy bitmap to new bitmap without display ?
I find rotate_sprite(pframe->frame, vbitmap, pframe->iwidth / 2, pframe->iheight / 2, fangle).
How to use it? Which ???.h will be include ?

Neil Walker
Member #210
April 2000
avatar

I guess you've figured out from the posts above that there really isn't any such thing as a sprite. In the olden days, the Commodore 64 had sprites. A sprite is just a bitmap that appears to animate simply because you are replacing it with another bitmap, i.e. a sprite is a class you create to manage, control and show the graphics in order :)

Forget about palettes. They are only used in 8 bit graphics and A5 doesn't support them.

Regarding al_convert_mask_to_alpha(): A5 uses the alpha channel to support transparency. A4 didn't and instead usually relied on 'magic pink', i.e. 255,0,255 magenta. So, al_convert_mask_to_alpha will look at your bitmap and any magenta pixel will be converted to transparency.

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

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

Havacore
Member #12,591
February 2011
avatar

Ok so first step, I just want to cut a section (the top left most) of the sprite sheet and put it on the display, just for starters. Now I've got a program that compiles, but it has a segmentation fault in there. Also I'm wondering if I'm doing this right. I'm just making a class for the sprite, and using a drawing method with al_draw_bitmap_region.

(The rest of the code should be good, Its just copied from my pong game for the eventual keyboard input I will want to put in later)

#SelectExpand
1#include <stdio.h> 2#include <allegro5/allegro.h> 3#include <allegro5/allegro_image.h> 4 5 6 7/* 8CONSTANTS 9*/ 10 11const int SCREEN_W = 640; 12const int SCREEN_H = 480; 13const float FPS = 60; 14 15enum MYKEYS { 16 KEY_UP, KEY_DOWN, KEY_W, KEY_S 17}; 18 19/* 20FUNCTIONS 21*/ 22 23/* 24OBJECTS 25*/ 26 27class player 28{ 29 30 private: 31 32 ALLEGRO_BITMAP *bmp; 33 ALLEGRO_BITMAP *animation; 34 int animationWidth; 35 int animationHeight; 36 37 38 public: 39 40 //Construtor 41 player(); 42 //Draws the sprite from areas x and y from the sprite sheet 43 void draw(float x, float y); 44 45 46 47}; 48 49 player::player() 50 { 51 //loads the entire spritesheet 52 bmp = al_load_bitmap("player.png"); 53 //converts the sprite sheet's magenta background to transparent pixels 54 al_convert_mask_to_alpha(bmp, (al_map_rgb(255, 0, 255))); 55 56 //each animation in the sprite sheet is 32X32 pixels 57 animationWidth = al_get_bitmap_width(bmp)/32; 58 animationHeight = al_get_bitmap_height(bmp)/32; 59 60 61 } 62 63 void player::draw(float x, float y) 64 { 65 //draw section of sprite sheet 66 //target bitmap, region x, region y, width, height, destination x, destination y, flag(0) 67 al_draw_bitmap_region(bmp, 0, 0, animationWidth, animationHeight, x, y, 0); 68 } 69 70/* 71MAIN 72*/ 73 74int main(int argc, char **argv) 75{ 76 /* 77 ALLEGRO DECLARATIONS 78 */ 79 80 ALLEGRO_DISPLAY *display = NULL; 81 ALLEGRO_EVENT_QUEUE *event_queue = NULL; 82 ALLEGRO_TIMER *timer = NULL; 83 84 85 /* 86 VARIABLES 87 */ 88 89 bool key[4] = {false, false, false, false }; 90 bool redraw = true; 91 bool doexit = false; 92 93 float sprite_x = (SCREEN_W/2); 94 float sprite_y = (SCREEN_H/2); 95 96 player sprite; 97 98 /* 99 CONFIGURATION 100 */ 101 102 if(!al_init()) { 103 fprintf(stderr, "failed to initialized allegro\n"); 104 return -1; 105 } 106 107 if(!al_install_keyboard()) { 108 fprintf(stderr, "failed to install keyboard\n"); 109 return -1; 110 } 111 112 //initialize display (w, h) 113 display = al_create_display(SCREEN_W, SCREEN_H); 114 if(!display) { 115 fprintf(stderr, "failed to create display\n"); 116 return -1; 117 } 118 119 timer = al_create_timer(1.0/FPS); 120 if(!timer) { 121 fprintf(stderr, "failed to create timer\n"); 122 return -1; 123 } 124 125 al_set_target_bitmap(al_get_backbuffer(display)); 126 127 event_queue = al_create_event_queue(); 128 if(!event_queue) { 129 fprintf(stderr, "failed to create event queue\n"); 130 return -1; 131 } 132 133 al_register_event_source(event_queue, al_get_display_event_source(display)); 134 135 al_register_event_source(event_queue, al_get_timer_event_source(timer)); 136 137 al_register_event_source(event_queue, al_get_keyboard_event_source()); 138 139 al_clear_to_color(al_map_rgb(0, 0, 0)); 140 141 al_flip_display(); 142 143 al_start_timer(timer); 144 145 /* 146 MAIN LOOP 147 */ 148 149 while(!doexit) 150 { 151 152 ALLEGRO_EVENT ev; 153 154 al_wait_for_event(event_queue, &ev); 155 156 if(ev.type == ALLEGRO_EVENT_TIMER) 157 { 158 159 160 redraw = true; 161 } 162 163 else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 164 { 165 break; 166 } 167 else if(ev.type == ALLEGRO_EVENT_KEY_UP) 168 { 169 switch(ev.keyboard.keycode) 170 { 171 case ALLEGRO_KEY_ESCAPE: 172 doexit = true; 173 break; 174 } 175 } 176 177 if(redraw && al_is_event_queue_empty(event_queue)) 178 { 179 redraw = false; 180 al_clear_to_color(al_map_rgb(0,0,0)); 181 182 //redraw bitmaps 183 184 185 //draw player 186 sprite.draw(sprite_x, sprite_y); 187 188 al_flip_display(); 189 190 } 191 192 193 } 194 195 al_destroy_timer(timer); 196 al_destroy_display(display); 197 al_destroy_event_queue(event_queue); 198 //destroy bitmaps 199 200 return 0; 201 }

jason perkins
Member #10,524
January 2009

I've never used al_draw_bitmap_region(), but that looks a little off. because your changing units from pixels to pictures with this line:

animationWidth = al_get_bitmap_width(bmp)/32;
...

To get just the top left image with al_create_sub_bitmap(). you'd do this:

bmp = al_load_bitmap("player.png")
ALLEGRO_BITMAP* top_left_image = al_create_sub_bitmap(bmp, 0, 0, 32, 32);

and draw with al_draw_bitmap(top_left_image, x, y, 0)

It just makes more practical sense to create an entire array at once though. so if you have a 4x4 sheet and 32x32 tiles...

bmp = al_load_bitmap("player.png")
ALLEGRO_BITMAP* bmp_array[4][4];

for (int x = 0; x < 4; x++)
{
  for (int y = 0; y < 4; y++)
  {
    bmp_array[x][y] = al_create_sub_bitmap(bmp, 32*x, 32*y, 32, 32);
  }
}

sprite.bmp = bmp_array[0][0];

Also instead of creating the variables sprite_x, and sprite_y, you should just make them private members of your player class, and write public functions to modify the values. Same goes for the bitmap so you can switch which element in the bitmap array is getting drawn.

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

@Havacore
Your program is crashing because you are calling al_load_bitmap before you have initialized allegro and you forgot to initialize the image addon. You declare 'player sprite' (which calls player::player(), which calls al_load_bitmap) before you call al_init().

Havacore
Member #12,591
February 2011
avatar

Ok I did it! I now have a program that draws a piece of the sprite sheet to the display! Now for animation, which is going to be quite the mountain for me to conquer :-/

@Edgar
I've been looking at the code you posted in the first reply. It all looks good an makes sense to me now, I just don't quite know how to keep track of time in a program. (whether in an allegro program or in a regular C++ program). The only thing I can think of is using my FPS constant with the allegro timer, making the animation state change every 15 frames or so. Would that be how you would go about tackling that problem?

Also that map<string , Animation*> in the animationState class, what is that?

Thanks for all the help so far btw everyone, this is awesome!

Desmond Taylor
Member #11,943
May 2010
avatar

Without giving you full code since you are doing it for a course of some sort. You can use al_create_timer( ALLEGRO_BPS_TO_SECS( 60 ) ); "60" being the FPS and use the Allegro Event system ALLEGRO_EVENT_TIMER.

Hope this helps you.

Edit:

Havacore said:

Also that map<string , Animation*> in the animationState class, what is that?

It's an array. Similar to std::vector and array[10]

Havacore
Member #12,591
February 2011
avatar

Without giving you full code since you are doing it for a course of some sort

Just to clarify, this isn't for a class, allegro programming is something I'm just learning on my own time. The course I mentioned earlier is just a regular computer science course.

On the other hand though, that is useful. I'll have to play around with events some more

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Havacore said:

I just don't quite know how to keep track of time in a program. (whether in an allegro program or in a regular C++ program). The only thing I can think of is using my FPS constant with the allegro timer, making the animation state change every 15 frames or so. Would that be how you would go about tackling that problem?

Use an ALLEGRO_TIMER, and set the the number of seconds between ticks using your FPS variable. You get the number of seconds per tick by taking the reciprocal of your FPS. Then for each timer event you receive, you call Animation::AdvanceFrameTime(seconds_per_tick).

#SelectExpand
1const float FPS = 60.0;// or whatever your monitors refresh rate is 2const float SPT = 1.0/FPS;// seconds per tick 3ALLEGRO_TIMER* timer = al_create_timer(SPT); 4 5//... Create display ... 6 7al_clear_to_color(al_map_rgb(0,0,0)); 8al_flip_display(); 9al_start_timer(timer);// start the timer just after a vsync (flip) so we always get the most amount of time for logic and drawing 10 11//... Logic ... 12while (!quit) { 13 while (1) { 14 ALLEGRO_EVENT ev; 15 al_wait_for_event(event_queue , &ev); 16 if (ev.type == ALLEGRO_EVENT_TIMER) { 17 animation.AdvanceFrameTime(SPT); 18 redraw = true; 19 } 20 if (al_is_event_queue_empty(event_queue)) {break;} 21 } 22 if (redraw) { 23 al_clear_to_color(al_map_rgb(0,0,0)); 24 animation.Display(x,y); 25 al_flip_display(); 26 } 27} 28// ... Clean up ... 29return 0;

Here is a working Allegro 4 based example of animation :
My post complete with animation example

If you download the zip file from that post you can view the source code I used for it and run the example I provided. The important functions from Animation.cpp are AnimationBase::AdvanceFrameTime, and AnimationBase::SetFrameTime, which I posted on the page I linked to. If you have any questions, feel free to ask. If I have time in the next few days, I may make a short version that works with A5.

Havacore said:

Also that map<string , Animation*> in the animationState class, what is that?

It's an array. Similar to std::vector and array[10]

No, it's not an array. At best, it is like a sorted linked list. I gave William Labbett a short explanation of how to use the std::map class here :
std::map explanation, with link to SGI STL documentation

Havacore
Member #12,591
February 2011
avatar

Ok I'm trying to use the code you put in the first response with the animation, stateAnimation and character classes. I'm still trying to wrap my head around the map thing, but I'm wondering if I can get away without using it. I worked on it a bit today but this is about as far as I got

sprite.h:

#SelectExpand
1#ifndef _SPRITE_H_ 2#define _SPRITE_H_ 3 4#include <allegro5/allegro.h> 5#include <stdio.h> 6#include <cstring> 7 8class animation 9{ 10 11 private: 12 ALLEGRO_BITMAP *bmp; 13 ALLEGRO_BITMAP *bmp_array[8][5]; 14 double duration; 15 double current_time; 16 int num_frames; 17 int frame_number; 18 19 20 public: 21 animation(); 22 void setFrameTime(double tsec); 23 void advanceFrameTime(double tsec); 24 void draw(BITMAP* bmp, int x, int y); 25 26} 27 28class stateAnimation 29{ 30 31 private: 32 //map? 33 Animation* active_animation; 34 35 public: 36 void setState(string state); 37 void advanceFrameTime(double tsec); 38 void draw(BITMAP* bmp, int x, int y); 39} 40 41class player 42{ 43 44 private: 45 46 ALLEGRO_BITMAP *current_animation; 47 int animationWidth; 48 int animationHeight; 49 animation move_left; 50 animation move_right; 51 stateAnimation anim_state; 52 53 public: 54 55 //Construtor 56 player(); 57 58}; 59 60#endif

sprite.cc:

#SelectExpand
1#include "sprite.h" 2#include <stdio.h> 3#include <allegro5/allegro.h> 4 5 6/********************/ 7/* animation class */ 8/********************/ 9 10animation::animation() 11{ 12 //loads the entire spritesheet 13 bmp = al_load_bitmap("player.png"); 14 //converts the sprite sheet's magenta background to transparent pixels 15 al_convert_mask_to_alpha(bmp, (al_map_rgb(255, 0, 255))); 16 17 18 for(int x = 0; x < 8; x++) 19 { 20 for(int y = 0; y < 5; y++) 21 { 22 bmp_array[x][y] = al_create_sub_bitmap(bmp, 32*x, 32*y, 32, 32); 23 } 24 } 25 26 num_frames = 40; 27 current_time = 0; 28} 29 30//sets the time for a frame 31void animation::setFrameTime(double tsec) 32{ 33 duration = tsec; 34} 35 36//advances the frame time 37void animation::advanceFrameTime(double tsec) 38{ 39 setFrameTime(current_time + tsec); 40} 41 42void animation::draw(BITMAP* bmp, int x, int y) 43{ 44 al_draw_bitmap(bmp, x, y, 0); 45} 46 47/**************************/ 48/* animation state class */ 49/**************************/ 50 51void stateAnimation::setState(string state) 52{ 53 54} 55 56void stateAnimation::advanceFrameTime(double tsec) 57{ 58 59} 60 61void stateAnimation::draw(BITMAP* bmp, int x, int y) 62{ 63 64} 65 66/*****************/ 67/* Player class */ 68/*****************/ 69 70 71//Constructor 72player::player() 73{ 74 75}

I changes some stuff from the original post, and I haven't filled in the player or the animation state class yet. I haven't figured out what exactly the setFrameTime and advanceFrameTime functions should do. One second I'm thinking that setFrameTime will set the duration, but then I think why wouldn't you just do that in the constructor... Maybe it's just because I need to step away from this for a second..

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

Havacore said:

I haven't figured out what exactly the setFrameTime and advanceFrameTime functions should do.

SetFrameTime should take a time value and turn it into a frame number. AdvanceFrameTime should take the current value, add the delta time to it, and then use that value in a call to SetFrameTime.

I'll go over the basics of a forward playing looped animation :

#SelectExpand
1 2class Animation { 3protected : 4 ALLEGRO_BITMAP** frames; 5 double duration; 6 double frames_per_sec; 7 double frametime; 8 int num_frames; 9 int frame_num; 10 11public : 12 Animation() : 13 frames(0), 14 duration(0.0), 15 frames_per_sec(0.0), 16 frametime(0.0), 17 num_frames(0), 18 frame_num(0) 19 {} 20 ~Animation() {Free();} 21 22 void Free() { 23 if (frames) { 24 delete [] frames; 25 frames = 0; 26 } 27 } 28 29 void Setup(double play_duration , int number_of_frames) { 30 Free(); 31 assert(number_of_frames > 0); 32 assert(play_duration > 0.0); 33 duration = play_duration; 34 num_frames = number_of_frames; 35 frames = new ALLEGRO_BITMAP*[num_frames]; 36 frames_per_sec = (double)num_frames / duration; 37 SetFrameTime(0.0); 38 } 39 void AdvanceFrameTime(double delta_time) { 40 SetFrameTime(frametime + delta_time); 41 } 42 void SetFrameTime(double new_frame_time) { 43 while(new_frame_time < 0.0) {new_frame_time += duration;} 44 while(new_frame_time >= duration) {new_frame_time -= duration;} 45 frametime = new_frame_time; 46 frame_num = (int)(frametime*frames_per_sec); 47 } 48 void SetBitmap(ALLEGRO_BITMAP* bmp, int frame_number) { 49 assert(frames); 50 assert((frame_number >= 0) && (frame_number < num_frames)); 51 frames[frame_number] = bmp; 52 } 53 void Display(int x , int y) { 54 assert(frames); 55 assert(frames[frame_num]); 56 al_draw_bitmap(frames[frame_num] , (float)x , (float)y , 0); 57 } 58 59};

And that's it. It gets slightly more complicated if you want more features, but it's a good place to start. Use it like this :

#SelectExpand
1double duration = 10.0; 2int number_of_frames = 40; 3Animation anime; 4anime.Setup(duration , number_of_frames); 5 6ALLEGRO_BITMAP* bmp = al_load_bitmap("player.png"); 7//converts the sprite sheet's magenta background to transparent pixels al_convert_mask_to_alpha(bmp, (al_map_rgb(255, 0, 255))); 8int i = 0; 9ALLEGRO_BITMAP* bmp_array[40]; 10for(int x = 0; x < 8; x++) { 11 for(int y = 0; y < 5; y++) { 12 bmp_array[x][y] = al_create_sub_bitmap(bmp, 32*x, 32*y, 32, 32); 13 anime.SetBitmap(bmp_array[x][y] , i); 14 ++i; 15 } 16} 17 18// Event loop 19 while (1) { 20 ALLEGRO_EVENT ev; 21 al_wait_for_event(event_queue , &ev); 22 if (ev.type == ALLEGRO_EVENT_TIMER) { 23 anime.AdvanceFrameTime(SECONDS_PER_TICK); 24 redraw = true; 25 } 26 if (al_is_event_queue_empty(event_queue)) {break}; 27 } 28 if (redraw) { 29 al_clear_to_color(al_map_rgb(0,0,0)); 30 anime.Draw(400 - al_get_bitmap_width(bmp)/2 , 300 - al_get_bitmap_height(bmp)/2); 31 al_flip_display(); 32 } 33}

Go to: