So the notorious book that I've been reading for working with Allegro presents its sprite handler as a struct. Since C++ seems to be more in line with what's being used in the "industry" nowadays, I wanted to construct a class for my sprite handler. I've run into some problems though; the main one being that when I attempt to draw_sprite() to make sure it's being loaded, I get the following compile error:
base operand of `->' has non-pointer type `Sprite'
Here's the code in my program:
| 1 | #include <string> |
| 2 | #include <iostream> |
| 3 | #include <allegro.h> |
| 4 | |
| 5 | using namespace std; |
| 6 | |
| 7 | class Sprite |
| 8 | { |
| 9 | public: |
| 10 | Sprite(char bmp_name[10]); |
| 11 | ~Sprite(); |
| 12 | |
| 13 | |
| 14 | string bmp_name; |
| 15 | BITMAP* sprite_img; |
| 16 | int x, y; |
| 17 | int width, height; |
| 18 | int xspeed, yspeed; |
| 19 | int xcount, ycount; |
| 20 | int xdelay, ydelay; |
| 21 | int curframe, maxframe; |
| 22 | int framecount, framedelay, animdir; |
| 23 | }; |
| 24 | |
| 25 | Sprite::Sprite(char bmp_name[10]) |
| 26 | { |
| 27 | allegro_message("Constructor Called"); |
| 28 | sprite_img = load_bitmap(bmp_name, NULL); |
| 29 | } |
| 30 | |
| 31 | Sprite::~Sprite() |
| 32 | { |
| 33 | // allegro_message("Destructor Called"); |
| 34 | destroy_bitmap(sprite_img); |
| 35 | } |
| 36 | |
| 37 | int main() |
| 38 | { |
| 39 | allegro_init(); |
| 40 | install_keyboard(); |
| 41 | set_color_depth(16); |
| 42 | set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0); |
| 43 | |
| 44 | Sprite foo("tank.bmp"); |
| 45 | |
| 46 | draw_sprite(screen, foo->sprite_img, 200, 200); |
| 47 | |
| 48 | while (!key[KEY_ESC]); |
| 49 | |
| 50 | allegro_exit(); |
| 51 | return 0; |
| 52 | } |
| 53 | END_OF_MAIN(); |
Can someone help me find the problem?
Change Sprite foo("tank.bmp"); to Sprite *foo = new Sprite("tank.bmp");, remove allegro_exit (you don't need it), and add delete foo; before returning from main.
Thanks, Kitty Cat. Figured it was something simple. Always is.
Actually, he could have replaced the -> with a period, like
draw_sprite(screen, foo.sprite_img, 200, 200);
Allthough the new/delete is to prefer and should be a habit.
It is okay to declare/create a class like he did in the first place, but it will only be available in the scope of that function.
Actually, he could have replaced the -> with a period
He could, but placing objects on the stack like that is considered bad form. And later on if he makes it a global, he shouldn't construct it before he's in a graphics mode so he'd need to wait to create it anyway.
He could, but placing objects on the stack like that is considered bad form. And later on if he makes it a global, he shouldn't construct it before he's in a graphics mode so he'd need to wait to create it anyway.
I know, I was just saying he could with the code provided
Appreciate all the responses guys!
I've actually advanced my code a little bit, but have now hit another wall. As you saw in my previous code, I was using a class as a sprite-handler. Once Kitty Cat helped me iron out that bug, it worked like a charm, and I even updated my code to the point of being able to move the sprite around, complete with direction changes. But now I want to animate it, and I'm kind of stuck.
With the code I have, I'm calling load_bitmap() each time the class is instantiated, but on that same token, every additional sprite is another object. So I'm kind of stuck as to how to modify my class to do what I need.
I thought about perhaps storing multiple frames per instance, maybe in some sort of container like a vector, but I'm just not sure exactly how to do it. Can any of you guys help me out on this? 
(again, I apologize for my blatant newbie-ness!)
Edit: I actually did some tinkering and figured out how to store my multiple frames in a vector. It probably sound corny to the vets here, but I'm quite proud of my accomplishment! 
Now I have a new problem... Animation... Given my code, does anyone have a suggestion as to how I could animate my sprite? I tried to do it with a for loop and a rest() call, but when I apply it to all directions, it slows the sprite's movement to a crawl and doesn't seem to animate properly.
Here's my code:
| 1 | #include <vector> |
| 2 | #include <allegro.h> |
| 3 | |
| 4 | using namespace std; |
| 5 | |
| 6 | #define WHITE makecol(255,255,255) |
| 7 | #define RED makecol(255,0,0) |
| 8 | #define BLACK makecol(0,0,0) |
| 9 | |
| 10 | #define DISPMODE GFX_AUTODETECT_WINDOWED |
| 11 | #define DISPDEPTH 32 |
| 12 | #define DISPW 600 |
| 13 | #define DISPH 900 |
| 14 | |
| 15 | class Sprite |
| 16 | { |
| 17 | public: |
| 18 | Sprite(char bmp_name[30], int x, int y); |
| 19 | ~Sprite(); |
| 20 | void AddFrame(char bmp_name[30]); |
| 21 | |
| 22 | char bmp_name[30]; |
| 23 | BITMAP* sprite_img; |
| 24 | int x, y; |
| 25 | int width, height; |
| 26 | int xspeed, yspeed; |
| 27 | int xcount, ycount; |
| 28 | int xdelay, ydelay; |
| 29 | int curframe, maxframe; |
| 30 | int framecount, framedelay, animdir; |
| 31 | vector<BITMAP*> frames; |
| 32 | }; |
| 33 | |
| 34 | Sprite::Sprite(char bmp_name[30], int sx, int sy) |
| 35 | { |
| 36 | AddFrame(bmp_name); |
| 37 | x = sx; |
| 38 | y = sy; |
| 39 | xspeed = 2; |
| 40 | yspeed = 2; |
| 41 | xcount = 0; |
| 42 | ycount = 0; |
| 43 | xdelay = 0; |
| 44 | ydelay = 0; |
| 45 | curframe = 0; |
| 46 | maxframe = 4; |
| 47 | framecount = 0; |
| 48 | framedelay = 5; |
| 49 | animdir = 0; |
| 50 | } |
| 51 | |
| 52 | void Sprite::AddFrame(char bmp_name[30]) |
| 53 | { |
| 54 | |
| 55 | int s; |
| 56 | sprite_img = load_bitmap(bmp_name, NULL); |
| 57 | if (sprite_img == NULL) |
| 58 | { |
| 59 | allegro_message("Error loading BITMAP"); |
| 60 | return; |
| 61 | } |
| 62 | frames.push_back(sprite_img); |
| 63 | s = frames.size(); |
| 64 | width = sprite_img->w; |
| 65 | height = sprite_img->h; |
| 66 | |
| 67 | } |
| 68 | |
| 69 | Sprite::~Sprite() |
| 70 | { |
| 71 | int s, x; |
| 72 | s = frames.size(); |
| 73 | for (x=0;x<s;x++) |
| 74 | { |
| 75 | destroy_bitmap(frames[x]); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | void initialize() |
| 80 | { |
| 81 | allegro_init(); |
| 82 | install_keyboard(); |
| 83 | install_timer(); |
| 84 | set_color_depth(DISPDEPTH); |
| 85 | set_gfx_mode(DISPMODE, DISPW, DISPH, 0, 0); |
| 86 | } |
| 87 | |
| 88 | void updatesprite(BITMAP* buffer, Sprite* spr, int dir) |
| 89 | { |
| 90 | float angle; |
| 91 | int x; |
| 92 | textout_ex(buffer, font, "My Project (ESC to quit)", 0, 0, WHITE, -1); |
| 93 | textprintf_ex(buffer, font, 0, 10, WHITE, -1, "sprite1 x, y: %i,%i", spr->x, spr->y); |
| 94 | textprintf_ex(buffer, font, 0, 20, WHITE, -1, "sprite1 width,height: %i,%i", spr->width, spr->height); |
| 95 | switch (dir) |
| 96 | { |
| 97 | case 0 : |
| 98 | rotate_sprite(buffer, spr->frames[0], spr->x, spr->y, itofix(-64)); |
| 99 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 100 | clear_bitmap(buffer); |
| 101 | break; |
| 102 | case 1 : |
| 103 | draw_sprite(buffer, spr->frames[0], spr->x, spr->y); |
| 104 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 105 | clear_bitmap(buffer); |
| 106 | break; |
| 107 | case 2 : |
| 108 | rotate_sprite(buffer, spr->frames[0], spr->x, spr->y, itofix(64)); |
| 109 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 110 | clear_bitmap(buffer); |
| 111 | break; |
| 112 | case 3 : |
| 113 | draw_sprite_h_flip(buffer, spr->frames[0], spr->x, spr->y); |
| 114 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 115 | clear_bitmap(buffer); |
| 116 | break; |
| 117 | default : |
| 118 | // for (x=0;x<spr->frames.size();x++) |
| 119 | // { |
| 120 | draw_sprite(buffer, spr->frames[x], (SCREEN_W/2-spr->width/2), 600); |
| 121 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 122 | // rest(50); |
| 123 | // } |
| 124 | clear_bitmap(buffer); |
| 125 | spr->x = (SCREEN_W/2-spr->width/2); |
| 126 | spr->y = 600; |
| 127 | } |
| 128 | rest(10); |
| 129 | } |
| 130 | |
| 131 | int main() |
| 132 | { |
| 133 | initialize(); |
| 134 | int dir; |
| 135 | BITMAP* buffer = create_bitmap(600, 900); |
| 136 | |
| 137 | Sprite* sprite1 = new Sprite("sprites_pacman1.bmp", 75, 75); |
| 138 | sprite1->AddFrame("sprites_pacman2.bmp"); |
| 139 | sprite1->AddFrame("sprites_pacman3.bmp"); |
| 140 | sprite1->AddFrame("sprites_pacman4.bmp"); |
| 141 | |
| 142 | while (!key[KEY_ESC]) |
| 143 | { |
| 144 | if (key[KEY_LEFT]) |
| 145 | { |
| 146 | dir = 3; |
| 147 | sprite1->x -= sprite1->xspeed; |
| 148 | } |
| 149 | if (key[KEY_RIGHT]) |
| 150 | { |
| 151 | dir = 1; |
| 152 | sprite1->x += sprite1->xspeed; |
| 153 | } |
| 154 | if (key[KEY_UP]) |
| 155 | { |
| 156 | dir = 0; |
| 157 | sprite1->y -= sprite1->yspeed; |
| 158 | } |
| 159 | if (key[KEY_DOWN]) |
| 160 | { |
| 161 | dir = 2; |
| 162 | sprite1->y += sprite1->yspeed; |
| 163 | } |
| 164 | |
| 165 | if (sprite1->y < 0) |
| 166 | { |
| 167 | sprite1->y = 0; |
| 168 | } |
| 169 | if (sprite1->y > SCREEN_H-sprite1->width) |
| 170 | { |
| 171 | sprite1->y = SCREEN_H-sprite1->width; |
| 172 | } |
| 173 | if (sprite1->x > SCREEN_W-sprite1->width) |
| 174 | { |
| 175 | sprite1->x = SCREEN_W-sprite1->width; |
| 176 | } |
| 177 | if (sprite1->x < 0) |
| 178 | { |
| 179 | sprite1->x = 0; |
| 180 | } |
| 181 | updatesprite(buffer, sprite1, dir); |
| 182 | } |
| 183 | delete sprite1; |
| 184 | |
| 185 | return 0; |
| 186 | } |
| 187 | END_OF_MAIN(); |
You basically have two choices, each has its own merits and drawbacks. You can either use multiple images, blitting the appropriate one at the appropriate time, or you can gather them all together into a single sheet, and blit the portion of the image that contains the correct frame.
Regardless of which one you choose, you need a more sophisticated object, one that defines a number of frames that your game can pick from. If you're like me and prefer a data-driven design, you can store all that information in a separate file and build a loader into your sprite class; that's a lot more flexible, and allows a game designer (if you have one) to create sprites without your intervention. However, if you're all alone and embedding the data into your code doesn't bother you, continue doing what you're doing, except load up several images into a single sprite - perhaps with a variable argument list:
| 1 | class Sprite |
| 2 | { |
| 3 | public: |
| 4 | Sprite(const char *spriteFile) |
| 5 | { |
| 6 | // read spec from sprite file and load indicated images |
| 7 | } |
| 8 | |
| 9 | -or- |
| 10 | |
| 11 | Sprite(const char *imageFile, ...) |
| 12 | { |
| 13 | // read arg list, load named images |
| 14 | } |
| 15 | |
| 16 | void drawFrame(int frame, int x, int y) |
| 17 | { |
| 18 | assert(frame < nImages); |
| 19 | draw_sprite(screen, img[frame], x, y); |
| 20 | } |
| 21 | |
| 22 | private: |
| 23 | BITMAP **img; |
| 24 | int nImages; |
| 25 | }; |
You basically have two choices, each has its own merits and drawbacks. You can either use multiple images, blitting the appropriate one at the appropriate time, or you can gather them all together into a single sheet, and blit the portion of the image that contains the correct frame.
Would using multiple, small sprites really have that adverse of an effect on animation though? What I've found is if I pull out the animation entirely, the program moves just fine. Once I put the animation in, it cuts the speed by half. The only other thing I tried was removing a call to "clear_bitmap()" following my blit() where I sent the buffer I'm updating to the screen, but without doing clear_bitmap(), I get copious amounts of artifacting (looks like my character has a motion trail).
Also, what does the ** mean in the code example you presented? I know a single * denotes a pointer, so what does the double-dose mean?
And any suggestions/comments on my animation issues?
Would using multiple, small sprites really have that adverse of an effect on animation though?
It wouldn't have an adverse effect whatsoever - I just wouldn't personally do it that way. That's just me. If using an animation is cutting your speed in half, though, you're doing something wrong (maybe doing it twice).
Also, what does the ** mean in the code example you presented?
It's a pointer to a pointer. If you're not very familiar with pointers, you need to get familiar with them very soon. If you have an array of objects (or anything else), and you don't know how large that array will be, you have to use a dynamic array.
So this:
int fixed[10];
becomes this:
int *dynamic = (int *)malloc(10 * sizeof(int));
That works for primitive types, but a BITMAP must be referred to with a pointer to begin with, since that single object itself is created dynamically (plus you don't want it to go out of scope and disappear). But because you potentially need more than one, you point to pointers.
So this:
BITMAP *fixed[10];
dynamic[0] = load_bitmap("bitmap0.bmp", NULL);
dynamic[1] = load_bitmap("bitmap1.bmp", NULL);
...
becomes this:
BITMAP **dynamic = (BITMAP **)malloc(10 * sizeof(BITMAP *)); dynamic[0] = load_bitmap("bitmap0.bmp", NULL); dynamic[1] = load_bitmap("bitmap1.bmp", NULL); ...
Obviously I'm oversimplifying and throwing out error checking, but that's the gist of it. This is really, really fundamental. If you don't fully understand pointers you'll run into trouble very quickly. Find some tutorials, buy a copy of K&R, do whatever you have to do, but you definitely need to be very comfortable with this sort of construction.
All of what you're saying makes sense, but it doesn't seem like any of it would affect the speed of my program's execution. Would a pointer to a pointer really be faster than accessing a vector element like I am now?
If you could offer some suggestions for changes to my code, it would be much appreciated. I feel like I'm asking you to solve my problem, but that isn't my intention. I just catch onto concepts better when they're shown in context.
Here's the source below. A lot of the stuff I tried to fix my problem (to no avail) is still in there, just commented out (like using a rectfill instead of clear_bitmap):
| 1 | #include <vector> |
| 2 | #include <allegro.h> |
| 3 | |
| 4 | using namespace std; |
| 5 | |
| 6 | #define WHITE makecol(255,255,255) |
| 7 | #define RED makecol(255,0,0) |
| 8 | #define BLACK makecol(0,0,0) |
| 9 | |
| 10 | #define DISPMODE GFX_AUTODETECT_WINDOWED |
| 11 | #define DISPDEPTH 32 |
| 12 | #define DISPW 600 |
| 13 | #define DISPH 900 |
| 14 | |
| 15 | class Sprite |
| 16 | { |
| 17 | public: |
| 18 | Sprite(char bmp_name[30], int x, int y); |
| 19 | ~Sprite(); |
| 20 | void AddFrame(char bmp_name[30]); |
| 21 | |
| 22 | char bmp_name[30]; |
| 23 | BITMAP* sprite_img; |
| 24 | int x, y; |
| 25 | int width, height; |
| 26 | int xspeed, yspeed; |
| 27 | int xcount, ycount; |
| 28 | int xdelay, ydelay; |
| 29 | int curframe, maxframe; |
| 30 | int framecount, framedelay, animdir; |
| 31 | vector<BITMAP*> frames; |
| 32 | }; |
| 33 | |
| 34 | Sprite::Sprite(char bmp_name[30], int sx, int sy) |
| 35 | { |
| 36 | AddFrame(bmp_name); |
| 37 | x = sx; |
| 38 | y = sy; |
| 39 | xspeed = 2; |
| 40 | yspeed = 2; |
| 41 | xcount = 0; |
| 42 | ycount = 0; |
| 43 | xdelay = 0; |
| 44 | ydelay = 0; |
| 45 | curframe = 0; |
| 46 | maxframe = 4; |
| 47 | framecount = 0; |
| 48 | framedelay = 5; |
| 49 | animdir = 0; |
| 50 | } |
| 51 | |
| 52 | void Sprite::AddFrame(char bmp_name[30]) |
| 53 | { |
| 54 | |
| 55 | int s; |
| 56 | sprite_img = load_bitmap(bmp_name, NULL); |
| 57 | if (sprite_img == NULL) |
| 58 | { |
| 59 | allegro_message("Error loading BITMAP"); |
| 60 | return; |
| 61 | } |
| 62 | frames.push_back(sprite_img); |
| 63 | s = frames.size(); |
| 64 | width = sprite_img->w; |
| 65 | height = sprite_img->h; |
| 66 | |
| 67 | } |
| 68 | |
| 69 | Sprite::~Sprite() |
| 70 | { |
| 71 | int s, x; |
| 72 | s = frames.size(); |
| 73 | for (x=0;x<s;x++) |
| 74 | { |
| 75 | destroy_bitmap(frames[x]); |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | void initialize() |
| 80 | { |
| 81 | allegro_init(); |
| 82 | install_keyboard(); |
| 83 | install_timer(); |
| 84 | set_color_depth(DISPDEPTH); |
| 85 | set_gfx_mode(DISPMODE, DISPW, DISPH, 0, 0); |
| 86 | } |
| 87 | |
| 88 | void updatesprite(BITMAP* buffer, Sprite* spr, int dir) |
| 89 | { |
| 90 | float angle; |
| 91 | // int x = 0; |
| 92 | // textout_ex(buffer, font, "My Project (ESC to quit)", 0, 0, WHITE, -1); |
| 93 | // textprintf_ex(buffer, font, 0, 10, WHITE, -1, "sprite1 x, y: %i,%i", spr->x, spr->y); |
| 94 | // textprintf_ex(buffer, font, 0, 20, WHITE, -1, "sprite1 width,height: %i,%i", spr->width, spr->height); |
| 95 | int x = 0; |
| 96 | for (x=0;x<spr->frames.size();x++) |
| 97 | { |
| 98 | switch (dir) |
| 99 | { |
| 100 | case 0 : |
| 101 | rotate_sprite(buffer, spr->frames[x], spr->x, spr->y, itofix(-64)); |
| 102 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 103 | // clear_bitmap(buffer); |
| 104 | rectfill(buffer, (spr->x-spr->width+5), (spr->y-spr->height+5), (spr->x+spr->width+5), (spr->y+spr->height+5), BLACK); |
| 105 | break; |
| 106 | case 1 : |
| 107 | draw_sprite(buffer, spr->frames[x], spr->x, spr->y); |
| 108 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 109 | rectfill(buffer, (spr->x-spr->width+5), (spr->y-spr->height+5), (spr->x+spr->width+5), (spr->y+spr->height+5), BLACK); |
| 110 | // clear_bitmap(buffer); |
| 111 | break; |
| 112 | case 2 : |
| 113 | rotate_sprite(buffer, spr->frames[x], spr->x, spr->y, itofix(64)); |
| 114 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 115 | rectfill(buffer, (spr->x-spr->width+5), (spr->y-spr->height+5), (spr->x+spr->width+5), (spr->y+spr->height+5), BLACK); |
| 116 | // clear_bitmap(buffer); |
| 117 | break; |
| 118 | case 3 : |
| 119 | draw_sprite_h_flip(buffer, spr->frames[x], spr->x, spr->y); |
| 120 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 121 | rectfill(buffer, (spr->x-spr->width+5), (spr->y-spr->height+5), (spr->x+spr->width+5), (spr->y+spr->height+5), BLACK); |
| 122 | // clear_bitmap(buffer); |
| 123 | break; |
| 124 | default : |
| 125 | draw_sprite(buffer, spr->frames[x], (SCREEN_W/2-spr->width/2), 600); |
| 126 | blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 127 | rectfill(buffer, (spr->x-spr->width+5), (spr->y-spr->height+5), (spr->x+spr->width+5), (spr->y+spr->height+5), BLACK); |
| 128 | // clear_bitmap(buffer); |
| 129 | spr->x = (SCREEN_W/2-spr->width/2); |
| 130 | spr->y = 600; |
| 131 | } |
| 132 | rest(1); |
| 133 | } |
| 134 | |
| 135 | // blit(buffer, screen, 0, 0, 0, 0, 600, 900); |
| 136 | // clear_bitmap(buffer); |
| 137 | // rest(30); |
| 138 | } |
| 139 | |
| 140 | int main() |
| 141 | { |
| 142 | initialize(); |
| 143 | int dir; |
| 144 | BITMAP* buffer = create_bitmap(600, 900); |
| 145 | |
| 146 | Sprite* sprite1 = new Sprite("sprites_pacman1.bmp", 75, 75); |
| 147 | sprite1->AddFrame("sprites_pacman2.bmp"); |
| 148 | sprite1->AddFrame("sprites_pacman3.bmp"); |
| 149 | sprite1->AddFrame("sprites_pacman4.bmp"); |
| 150 | |
| 151 | while (!key[KEY_ESC]) |
| 152 | { |
| 153 | if (key[KEY_LEFT]) |
| 154 | { |
| 155 | dir = 3; |
| 156 | sprite1->x -= sprite1->xspeed; |
| 157 | } |
| 158 | if (key[KEY_RIGHT]) |
| 159 | { |
| 160 | dir = 1; |
| 161 | sprite1->x += sprite1->xspeed; |
| 162 | } |
| 163 | if (key[KEY_UP]) |
| 164 | { |
| 165 | dir = 0; |
| 166 | sprite1->y -= sprite1->yspeed; |
| 167 | } |
| 168 | if (key[KEY_DOWN]) |
| 169 | { |
| 170 | dir = 2; |
| 171 | sprite1->y += sprite1->yspeed; |
| 172 | } |
| 173 | |
| 174 | if (sprite1->y < 0) |
| 175 | { |
| 176 | sprite1->y = 0; |
| 177 | } |
| 178 | if (sprite1->y > SCREEN_H-sprite1->width) |
| 179 | { |
| 180 | sprite1->y = SCREEN_H-sprite1->width; |
| 181 | } |
| 182 | if (sprite1->x > SCREEN_W-sprite1->width) |
| 183 | { |
| 184 | sprite1->x = SCREEN_W-sprite1->width; |
| 185 | } |
| 186 | if (sprite1->x < 0) |
| 187 | { |
| 188 | sprite1->x = 0; |
| 189 | } |
| 190 | updatesprite(buffer, sprite1, dir); |
| 191 | } |
| 192 | delete sprite1; |
| 193 | |
| 194 | return 0; |
| 195 | } |
| 196 | END_OF_MAIN(); |
Also, I know my animation process is kind of Frankenstein'ed... Any suggestions on a good tutorial for doing animation that preferably deals with their sprites as a class?
This suggestion may not be what you looking for, but this is what I have ended up doing in my project.
If you want to go OOP simply create a class for everything. So you would end up having a class for Frames, Animation, AnimationMgr, Objects, Tiles, Sprites, etc.
Think of this way, Tiles and Sprites are different, but they share common attributes and functions. Therefore, create an Object class and derive from it Tile and Sprite.
Object class would have an Animation Manager that could contain more then one animation. After all, a sprite would have a number of animations, such as waling, shooting, dying, etc. Tiles can also have multiple animations based on the events.
The Animation Manager (like the name says) manages animations. The Animation class only worries abut frames that build up this animation. While frame class contains the information in regards to a frame.
Frame could have an ID, Name, Delay, Counter, BITMAP
Animation then can have a list or an array of frames, current frame, etc.
Animation Manager can have a list of animations, current animation, etc.
Object has Animation Manager, along with other attributes.
Tile inherits Object class and implements its own attributes and functions.
Sprite, just like a Tile class does the same thing with a difference of its own needs.
From that you can then derive a Player class, Enemy, or whatever else you may need. Best of all, each derived object will be able to handle animations just like any other object.
If you feel that some of the Objects will not be in need for an animation, then all you do is create a single Animation with a single frame and that is it.
Frame could have an ID, Name, Delay, Counter, BITMAP
Animation then can have a list or an array of frames, current frame, etc.
Animation Manager can have a list of animations, current animation, etc.
Object has Animation Manager, along with other attributes.
Tile inherits Object class and implements its own attributes and functions.
Sprite, just like a Tile class does the same thing with a difference of its own needs.
From that you can then derive a Player class, Enemy, or whatever else you may need. Best of all, each derived object will be able to handle animations just like any other object.
If you feel that some of the Objects will not be in need for an animation, then all you do is create a single Animation with a single frame and that is it.
I get what you're saying... I think really my major problem is figuring out how to make part a relate to part b, and for part c to use part b when necessary. I have experience with OOP, though limited, but my biggest hurdle currently is my main source (in terms of reference material) is loaded with questionable practices. So I've been doing code my own way, in a manner of speaking, as not to duplicate the author's mistakes; instead, I'm making a lot of my own.
The best example I can give is my issue with doing animations. I've still yet to find a straight forward tutorial anywhere online.
Not to mention I am baffled by the performance hit between when I have my animation running (though messy in execution) and when my sprites are static and don't animate. From what I can tell about my code, I'm not breaking any major rules of programming, so such a hit in performance shouldn't be happening... But again, it just illustrates my inexperience in the matter, and a lack of a direct mentor doesn't help.
Game Programming All In One 2e? Me too. I recall it saying that the rotate and fliping algorithisms are long and complex. Maybe you should create all of the images at runtime, and then draw_sprite the right one. Use more of his code, it works. The BITMAP array that holds animations actually works. If you want, I'll post my sprite handler, also a class, but closer to what the book said.