Anyone here care to share their animation class with me? I've written so many (that I never saved) and I don't want to write another one. I'd love to use someone else's if they would be kind to share with a little doc.
Check out my sig. axl includes an animation library that can be used by itself.
the direct link to the online manual is here:
http://retrospec.sgn.net/users/nwalker/axl/index.html
| 1 | #ifndef ANIMATION_H |
| 2 | #define ANIMATION_H |
| 3 | |
| 4 | #include "datafile.h" |
| 5 | #include "stopwatch.h" |
| 6 | #include "bitmap.h" |
| 7 | #include <vector> |
| 8 | #include <allegro.h> |
| 9 | |
| 10 | class animation { |
| 11 | public: |
| 12 | |
| 13 | // path must be ready to use with the specified datafile. |
| 14 | // The path will be postfixed with a number starting at 0 and |
| 15 | // incremented until no image is found. An example path could |
| 16 | // be: CHARACTERS/BRILLY/FIST/LEFT/ |
| 17 | // |
| 18 | // Note the object in the datafile must have an attribute called |
| 19 | // TIME which specifies how many milliseconds the frame will display |
| 20 | // for. If it is not specified the time defaults to 0 |
| 21 | animation(const std::string &path, datafile_t* = &datafile); |
| 22 | |
| 23 | animation(); |
| 24 | animation(const animation&); |
| 25 | |
| 26 | // Animation is running (started) by default |
| 27 | void start(); |
| 28 | void restart(); |
| 29 | void unpause(); |
| 30 | void stop(); |
| 31 | void pause(); |
| 32 | void setframe(unsigned int frame); |
| 33 | |
| 34 | // Alais functions representing the same operations |
| 35 | bool unpaused(); |
| 36 | bool paused(); |
| 37 | bool started(); |
| 38 | bool stopped(); |
| 39 | |
| 40 | // Calculates the current Bitmap and returns it |
| 41 | // Returns 0 if there is no frame available. |
| 42 | Bitmap *get_bmp(); |
| 43 | |
| 44 | // Finds the frame associated with the parameter 'frame'. Frame can be |
| 45 | // any value and this function will wrap it to the correct frame number. |
| 46 | // Returns 0 if there are no frames available. |
| 47 | Bitmap *get_bmp(unsigned int frame); |
| 48 | |
| 49 | // Must be set to point to a variable that holds the milliseconds since |
| 50 | // program start |
| 51 | static volatile unsigned int *msecs; |
| 52 | |
| 53 | private: |
| 54 | |
| 55 | struct Frame { |
| 56 | |
| 57 | int msec_delay; |
| 58 | Bitmap bmp; |
| 59 | |
| 60 | Frame(BITMAP *tmp) : bmp(tmp) {} |
| 61 | }; |
| 62 | |
| 63 | std::vector<Frame> frames; |
| 64 | |
| 65 | unsigned int cur_frame; |
| 66 | unsigned int frame_msec_start; |
| 67 | |
| 68 | bool running; |
| 69 | }; |
| 70 | |
| 71 | #endif |
| 72 | |
| 73 | // .... |
| 74 | |
| 75 | #include "animation.h" |
| 76 | #include "log.h" |
| 77 | #include <sstream> |
| 78 | #include <iostream> |
| 79 | using namespace std; |
| 80 | |
| 81 | volatile unsigned int *animation::msecs = 0; |
| 82 | |
| 83 | animation::animation(const string &path, datafile_t *datafile) |
| 84 | : cur_frame(0), running(1) |
| 85 | { |
| 86 | DATAFILE *datf; |
| 87 | BITMAP *bmp; |
| 88 | |
| 89 | for(int i = 0; 1; i++) { |
| 90 | |
| 91 | ostringstream os; |
| 92 | |
| 93 | os << path << i; |
| 94 | |
| 95 | bmp = (BITMAP*)datafile->loadfind(os.str(), &datf); |
| 96 | |
| 97 | if(!bmp) |
| 98 | break; |
| 99 | |
| 100 | frames.push_back(Frame(bmp)); |
| 101 | |
| 102 | frames.back().msec_delay = atoi(get_datafile_property(datf, DAT_ID('T', 'I', 'M', 'E'))); |
| 103 | } |
| 104 | |
| 105 | if(frames.empty()) |
| 106 | out << "Unknown anim \"" << path << "\"\n"; |
| 107 | |
| 108 | frame_msec_start = *msecs; |
| 109 | } |
| 110 | |
| 111 | animation::animation() |
| 112 | : cur_frame(0), frame_msec_start(*msecs), running(1) |
| 113 | { |
| 114 | |
| 115 | } |
| 116 | |
| 117 | animation::animation(const animation &src) |
| 118 | : frames(src.frames), cur_frame(src.cur_frame), frame_msec_start(src.frame_msec_start), running(src.running) |
| 119 | { |
| 120 | |
| 121 | } |
| 122 | |
| 123 | Bitmap *animation::get_bmp() |
| 124 | { |
| 125 | if(frames.empty()) |
| 126 | return 0; |
| 127 | |
| 128 | unsigned int s = frames.size(); |
| 129 | |
| 130 | while(running && frame_msec_start + frames[cur_frame].msec_delay < *msecs) { |
| 131 | |
| 132 | frame_msec_start += frames[cur_frame].msec_delay; |
| 133 | |
| 134 | cur_frame++; |
| 135 | |
| 136 | if(cur_frame >= s) |
| 137 | cur_frame = 0; |
| 138 | |
| 139 | if(!frames[cur_frame].msec_delay) |
| 140 | break; |
| 141 | } |
| 142 | |
| 143 | Bitmap *ret = &frames[cur_frame].bmp; |
| 144 | |
| 145 | if(!ret) |
| 146 | err << "Returning 0 for get_bmp()\n"; |
| 147 | |
| 148 | return ret; |
| 149 | } |
| 150 | |
| 151 | Bitmap *animation::get_bmp(unsigned int frame) |
| 152 | { |
| 153 | if(frames.empty()) |
| 154 | return 0; |
| 155 | |
| 156 | Bitmap *ret = &frames[frame % frames.size()].bmp; |
| 157 | |
| 158 | if(!ret) |
| 159 | err << "Returning 0 for get_bmp(" << frame << ")\n"; |
| 160 | |
| 161 | return ret; |
| 162 | } |
| 163 | |
| 164 | void animation::start() |
| 165 | { |
| 166 | running = 1; |
| 167 | frame_msec_start = *msecs; |
| 168 | } |
| 169 | |
| 170 | void animation::setframe(unsigned int frame) |
| 171 | { |
| 172 | running = 0; |
| 173 | if (frame>frames.size()-1) frame=frames.size()-1; |
| 174 | cur_frame=frame; |
| 175 | } |
| 176 | |
| 177 | void animation::restart() |
| 178 | { |
| 179 | running = 1; |
| 180 | cur_frame = 0; |
| 181 | frame_msec_start = *msecs; |
| 182 | } |
| 183 | |
| 184 | void animation::unpause() |
| 185 | { |
| 186 | running = 1; |
| 187 | } |
| 188 | |
| 189 | void animation::stop() |
| 190 | { |
| 191 | cur_frame = 0; |
| 192 | running = 0; |
| 193 | } |
| 194 | |
| 195 | void animation::pause() |
| 196 | { |
| 197 | running = 0; |
| 198 | } |
| 199 | |
| 200 | bool animation::unpaused() |
| 201 | { |
| 202 | return running; |
| 203 | } |
| 204 | |
| 205 | bool animation::paused() |
| 206 | { |
| 207 | return !running; |
| 208 | } |
| 209 | |
| 210 | bool animation::started() |
| 211 | { |
| 212 | return running; |
| 213 | } |
| 214 | |
| 215 | bool animation::stopped() |
| 216 | { |
| 217 | return !running; |
| 218 | } |
Obviously dependant on the datafile, stopwatch, bitmap and log modules of my engine which I haven't included.
My question with these is, what happens if I have multiples of the same unit? It would seem I would have duplicates of the images loaded into memory with these classes.
It would seem I would have duplicates of the images loaded into memory with these classes.
Well with amount of memory in most modern computers storing a new copy of a bitmap for each unit of a RTS probably will not cause much troubles. If you want to be efficient about it remember Allegro deals with (BITMAP *) pointers. That means you can set multiple units bitmap images to point at one bitmap in memory.
For example:
int itor; BITMAT * GruntStand = LoadBitmap("GruntStand.bmp"); for(itor =0; itor < NUMGRUNTS; itor++){ Army[itor] = new Grunt; Army[itor]->Image = GruntStand; }
Will only store one copy of the Bitmap GruntStand.bmp in memory but all the Grunt instances will be able to access it as their own class variable.
Yeah I know. The 2 animation classes above store bitmaps and a current frame variable together. That means one of two things.
A) Every like unit animates the same
B) Duplicate copies of the bitmaps per units
I think I'll end up writting my own to handle these issues. Basically the currentFrame should be seperately stored from the bitmaps that make up the animation. That way each unit can have different currentFrame values and just get the image from a manager type class from this units currentFrame.
My question with these is, what happens if I have multiples of the same unit? It would seem I would have duplicates of the images loaded into memory with these classes.
The animation class has no concept of storing one copy of a bitmap.
The Bitmap class handles that.
I see now Dustin, you are using an already loaded datafile.
hey rick check out my game character editor I'm making it to were all animations are reusable
http://www.allegro.cc/depot/Thegame/
Posting the animation classes for The Mighty Stupid. It's two main classes; an animation class that holds all the animation data for a given entity, and a tracking class, which references the animation and handles all playback and frame tracking. That way I can have one animation loaded and have 20 different enemies (with 20 different trackers) running off it without wasting memory. 
Just the interfaces ....
| 1 | class CAnimation |
| 2 | { |
| 3 | public: |
| 4 | // a sprite with bounding boxes used for collision detection. |
| 5 | class CFrame |
| 6 | { |
| 7 | public: |
| 8 | class CBBox |
| 9 | { |
| 10 | public: |
| 11 | // all variables |
| 12 | int xpos, ypos, width, height; |
| 13 | char state; // for flags, like aggressive, passive, etc. |
| 14 | int type; |
| 15 | int ID; |
| 16 | int damage; |
| 17 | |
| 18 | // ctor; no dtor needed |
| 19 | CBBox(int x = 0, int y = 0, int w = 0, int h = 0, int s = 0): xpos(x), ypos(y), width(w), height(h), state(s){} |
| 20 | }; |
| 21 | |
| 22 | // all variables |
| 23 | std::vector<CBBox> bboxes; |
| 24 | bool hflip; |
| 25 | BITMAP *bitmap; |
| 26 | int xoff, yoff; |
| 27 | long unsigned int keyframe; |
| 28 | |
| 29 | CFrame(): hflip(0) { } |
| 30 | CFrame(BITMAP* b, int x, int y, int k, int f): hflip(f), bitmap(b), xoff(x), yoff(y), keyframe(k) { } |
| 31 | |
| 32 | // for drawing .... |
| 33 | void DrawRects(BITMAP* b, int x, int y); |
| 34 | void Draw(BITMAP *b, int x, int y); |
| 35 | void DrawLit(BITMAP *b, int x, int y, int trans); |
| 36 | void DrawTrans(BITMAP *b, int x, int y, int trans); |
| 37 | void DrawTransLit(BITMAP *b, int x, int y, int trans, int color, int fade); |
| 38 | }; |
| 39 | |
| 40 | // class that holds one 2D sequence of images and bounding boxes. It's just |
| 41 | // one big vector, with a few helper functions. An array wouldn't give any real |
| 42 | // speed here, since frames change relatively rarely. Very simple class ... |
| 43 | class CObjectAnime |
| 44 | { |
| 45 | public: |
| 46 | std::vector<CFrame> images; |
| 47 | int looping; |
| 48 | inline int size(); |
| 49 | inline CFrame& operator[](int index); |
| 50 | }; |
| 51 | |
| 52 | typedef std::vector<CFrame::CBBox>::iterator boxiterator; |
| 53 | |
| 54 | // CAnimation's variables. Just a map, a bitmap array, and the number of bmp's ... |
| 55 | std::map<std::string, CObjectAnime> sprites; |
| 56 | BITMAP** images; |
| 57 | int numimages; |
| 58 | |
| 59 | // ctors and dtor |
| 60 | CAnimation(); |
| 61 | CAnimation(std::string filename); |
| 62 | void ConstructFromDatafile(std::string filename); |
| 63 | ~CAnimation(); |
| 64 | |
| 65 | // add a frame of animation. If framename doesn't exist, the sequence is created |
| 66 | void AddFrame(std::string framename, int frame, BITMAP* b, int x, int y, int k, int f); |
| 67 | inline void Destroy(); |
| 68 | inline bool IsEmpty(); |
| 69 | inline bool Find(std::string s); |
| 70 | }; |
| 1 | class CAnimeTracker |
| 2 | { |
| 3 | private: |
| 4 | // all the variables |
| 5 | CAnimation* animations; |
| 6 | CAnimation::CObjectAnime* curanimation; |
| 7 | CAnimation::CFrame* cursprite; |
| 8 | int frame; // current frame of current animation |
| 9 | float time; // current time in animation |
| 10 | float oldtime; // time last update |
| 11 | float speed; // speed control |
| 12 | bool paused; // is it paused? |
| 13 | float delay; // pause delay |
| 14 | bool stopped; // is it stopped? |
| 15 | std::list<std::string> sequences; // for "programming" an animation sequence |
| 16 | |
| 17 | public: |
| 18 | // ctors and dtor |
| 19 | CAnimeTracker(CAnimation& a); |
| 20 | CAnimeTracker(CAnimation& a, std::string init); |
| 21 | ~CAnimeTracker(); |
| 22 | |
| 23 | // advance animation |
| 24 | void Update(int externaltime = 0); |
| 25 | |
| 26 | // lots of inline junk |
| 27 | void ResetTime(); // moved to .cpp; explained there |
| 28 | void Delay(int d); // moved to .cpp; explained there |
| 29 | void Unpause(); // moved to .cpp; explained there |
| 30 | void SetTime(float t); // more than just time = x. moved to .cpp; explained there |
| 31 | inline void Pause(); |
| 32 | inline bool IsPaused(); |
| 33 | inline bool IsStopped(); |
| 34 | inline void AdjustSpeed(float s); |
| 35 | inline void SetSpeed(float s); |
| 36 | inline float GetSpeed(); |
| 37 | inline float GetTime(); |
| 38 | |
| 39 | inline CAnimation::CFrame* GetCurrentSprite(); |
| 40 | |
| 41 | // for changing, checking and manipulating the sequences |
| 42 | bool ChangeSequence(std::string str); |
| 43 | void AppendSequence(std::string str); |
| 44 | bool InterruptSequence(std::string str); |
| 45 | void MakeNextSequence(std::string str);; |
| 46 | void SwitchSequence(std::string str); |
| 47 | void ResetAnimation() { ResetTime(); } |
| 48 | |
| 49 | std::string CurrentSequence(); |
| 50 | |
| 51 | // drawing functions |
| 52 | void Draw(BITMAP* b, int x, int y); |
| 53 | void DrawLit(BITMAP* b, int x, int y, int trans); |
| 54 | void DrawTrans(BITMAP* b, int x, int y, int trans); |
| 55 | void DrawTransLit(BITMAP* b, int x, int y, int trans, int color, int fade); |
| 56 | void DrawRects(BITMAP* b, int x, int y); |
| 57 | }; |
Explanations available upon request, but I think it's pretty self explanitory. Implementations are left as an exercise for the reader.
I see now Dustin, you are using an already loaded datafile.
Actually the images in the datafile are loaded into textures, which is handled by Bitmap.
Bitmap maintains a lookup table with pointers to items in the datafile as the key and the texture IDs as the value.
If passed a pointer to a bitmap that is already in the lookup table, that texture ID is used, otherwise the upload is performed.
Obviously there is the minute chance that a bitmap could be passed with the same address of an old bitmap that was deallocated, but the work-around for this is speed and memory demanding. And I'm keeping the whole datafile loaded into memory anyway.
That way I can have one animation loaded and have 20 different enemies (with 20 different trackers) running off it without wasting memory.
character editor I'm making it to were all animations are reusable:P
same thing i was shooting for