Allegro.cc - Online Community

Allegro.cc Forums » The Depot » ALLEGRO_ANIMATED_BITMAP

This thread is locked; no one can reply to it. rss feed Print
 1   2   3   4 
ALLEGRO_ANIMATED_BITMAP
Christopher Bludau
Member #5,401
January 2005

EDIT:
Newest version in this post

Hi, as posted in this thread I wanted to think about an animated bitmap and so I wanted to show what I came up with.

1) How you use it.

1// create an array with bitmaps
2ALLEGRO_BITMAP* bitmaps[5];
3bitmaps[0] = bmp1;
4bitmaps[1] = bmp2;
5bitmaps[2] = bmp3;
6bitmaps[3] = bmp4;
7bitmaps[4] = bmp5;
8 
9// then create an animated bitmap
10// - you pass the array of the bitmaps
11// - the amount of time each frame should be visible
12// - the number of frames
13ALLEGRO_ANIMATED_BITMAP* anim = create_animation(bitmaps, 0.1, 5);
14 
15// in you update part you update the animated image
16// you pass the animated bitmap and the delta time
17update_animation(anim, dt);
18 
19//In you drawing part you draw the image like this
20al_draw_bitmap(get_active_animation(anim), x_pos, y_pos, 0);
21 
22 
23// when you are done you free the memory
24destroy_animation(anim);
25 
26 
27// NOTE: if you don't use delta time but fixed frame timing then you
28// pass the amount of passed frames here. You will specify the time each
29// frame should be visible in frames as well when creating the animation

2) The code behind it

1 
2typedef struct ALLEGRO_ANIMATED_BITMAP {
3 int frames; // contains the number of frames
4 float frame_time; // the time each frame should be visible
5 float current_time; // the elapsed time so far
6 int active_frame; // the currently active frame
7 ALLEGRO_BITMAP *bitmaps[]; // containing all the different bmps
8 // for the different frams
9}ALLEGRO_ANIMATED_BITMAP;
10 
11 
12ALLEGRO_ANIMATED_BITMAP* create_animation(ALLEGRO_BITMAP *bitmaps[], float frame_time, int frames)
13{
14 ALLEGRO_ANIMATED_BITMAP *anim;
15 anim = (ALLEGRO_ANIMATED_BITMAP*) malloc(sizeof(ALLEGRO_ANIMATED_BITMAP) + frames * sizeof(ALLEGRO_BITMAP*));
16 anim->frames = frames;
17 anim->active_frame = 0;
18 anim->current_time = 0;
19 anim->frame_time = frame_time;
20 
21 for (int i = 0; i < frames; i++)
22 {
23 anim->bitmaps<i> = bitmaps<i>;
24 }
25 
26 return anim;
27}
28 
29void update_animation(ALLEGRO_ANIMATED_BITMAP *anim, float delta_time)
30{
31 float total_time = delta_time + anim->current_time;
32 int delta_frames = (int)(total_time / anim->frame_time);
33 anim->current_time = total_time;
34 while (anim->current_time > anim->frame_time)
35 {
36 anim->current_time -= anim->frame_time;
37 }
38 anim->active_frame = (anim->active_frame + delta_frames) % anim->frames;
39}
40 
41ALLEGRO_BITMAP* get_active_animation(ALLEGRO_ANIMATED_BITMAP *anim)
42{
43 return anim->bitmaps[anim->active_frame];
44}
45 
46void destroy_animation(ALLEGRO_ANIMATED_BITMAP *animated_bitmap)
47{
48 free(animated_bitmap);
49}

Just the first version of the code.
Tested with allegro 4.9.8. Worked.
The destroy method is probably not doing all it should (destroying the bitmaps?) and the create methid could need some error handling.

So what do you think about it?

SiegeLord
Member #7,827
October 2006
avatar

How do you specify ping-pong animations? Looping vs nonlooping? Variable frame-rate?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

Matthew Leverton
Supreme Loser
January 1999
avatar

With what respect do you want us to comment? If by the AD forum, you mean included as part of A5, then I say no. This is something that is hard to abstract away. To do it right, you need a complete framework, which really isn't the same thing as a library (add-on or not).

Christopher Bludau
Member #5,401
January 2005

Quote:

How do you specify ping-pong animations? Looping vs nonlooping?

Didn't think of that. But possible.

Quote:

Variable frame-rate?

Thats one thing I haven't solved yet. Any ideas?

Quote:

With what respect do you want us to comment? If by the AD forum, you mean included as part of A5, then I say no. This is something that is hard to abstract away. To do it right, you need a complete framework, which really isn't the same thing as a library (add-on or not).

Yea, I was looking for that kind of comment. But not only.
Comments about the code are welcome too, or ideas I missed like SiegeLord did.

Would you care to elaborate why this wouldn't fit in a library - or better - why this would need a framework?
I hoped due to the simple update mechanics it was able to be used outside of a framework. Not wanting to change your mind. :) Just want to know what problems you see.

Matthew Leverton
Supreme Loser
January 1999
avatar

I'm not going to try to define fuzzy terms like "framework," but the point is that there are many ways to do an animated bitmap. To me, the things that should belong in Allegro 5 core or first class add-ons are things that are a) very complex (playing sound) or b) easy to agree on a standard method (loading a PNG file, getting clipboard data).

As soon as one person submits his version of animated bitmaps, somebody else is going to want to submit his own version. Then you have to decide which is better, when in reality they may just be different (and incompatible).

Essentially, I think it's just too high level of a concept. I think that it's something that people will find useful if they like your methodologies, but I think we should be careful about adding all sorts of things like this to Allegro just because we can.

Evert
Member #794
November 2000
avatar

Quote:

Essentially, I think it's just too high level of a concept. I think that it's something that people will find useful if they like your methodologies, but I think we should be careful about adding all sorts of things like this to Allegro just because we can.

I agree.

Which isn't to say you can't provide something like that in the form of a third party addon. That would be good. We like third party addons.
;)

Christopher Bludau
Member #5,401
January 2005

Ok. Sounds reasonable.

I will try to add the features SiegeLord mentioned tomorrow.
Code/Feature comments welcome. :)

Dustin Dettmer
Member #3,935
October 2003
avatar

For those interested, I'm going to ramble on about animation libraries.

Take, for instance, this simple sprite character.

Here is the datafile for the sprite, located at datafile.txt for a nice reference (shown below).

1Angel.walk_right.frames.[+].url=tod/walk_right.1.gif
2Angel.walk_right.frames.[].width=15
3Angel.walk_right.frames.[].height=26
4Angel.walk_right.frames.[].ttl=125
5Angel.walk_right.frames.[+].url=tod/walk_right.2.gif
6Angel.walk_right.frames.[].width=15
7Angel.walk_right.frames.[].height=26
8Angel.walk_right.frames.[].ttl=125
9Angel.walk_right.frames.[+].url=tod/walk_right.3.gif
10Angel.walk_right.frames.[].width=15
11Angel.walk_right.frames.[].height=26
12Angel.walk_right.frames.[].ttl=125
13Angel.walk_right.frames.[+].url=tod/walk_right.4.gif
14Angel.walk_right.frames.[].width=15
15Angel.walk_right.frames.[].height=26
16Angel.walk_right.frames.[].ttl=125
17 
18Angel.walk_left.frames.[+].url=tod/walk_left.1.gif
19Angel.walk_left.frames.[].width=15
20Angel.walk_left.frames.[].height=26
21Angel.walk_left.frames.[].ttl=125
22Angel.walk_left.frames.[+].url=tod/walk_left.2.gif
23Angel.walk_left.frames.[].width=15
24Angel.walk_left.frames.[].height=26
25Angel.walk_left.frames.[].ttl=125
26Angel.walk_left.frames.[+].url=tod/walk_left.3.gif
27Angel.walk_left.frames.[].width=15
28Angel.walk_left.frames.[].height=26
29Angel.walk_left.frames.[].ttl=125
30Angel.walk_left.frames.[+].url=tod/walk_left.4.gif
31Angel.walk_left.frames.[].width=15
32Angel.walk_left.frames.[].height=26
33Angel.walk_left.frames.[].ttl=125
34 
35Angel.up_right.frames.[+].url=tod/up_right.1.gif
36Angel.up_right.frames.[].width=15
37Angel.up_right.frames.[].height=26
38Angel.up_left.frames.[+].url=tod/up_left.1.gif
39Angel.up_left.frames.[].width=15
40Angel.up_left.frames.[].height=26
41 
42Angel.hover_right.frames.[+].url=tod/hover_right.1.gif
43Angel.hover_right.frames.[].width=15
44Angel.hover_right.frames.[].height=26
45Angel.hover_left.frames.[+].url=tod/hover_left.1.gif
46Angel.hover_left.frames.[].width=15
47Angel.hover_left.frames.[].height=26
48 
49Angel.down_right.frames.[+].url=tod/down_right.1.gif
50Angel.down_right.frames.[].width=15
51Angel.down_right.frames.[].height=26
52Angel.down_left.frames.[+].url=tod/down_left.1.gif
53Angel.down_left.frames.[1].width=15
54Angel.down_left.frames.[].height=26

When the sprite starts walking right the display switches to walk_right at the first frame. After 125 milliseconds the next frame is shown and so on.

The system needs to be smart enough to accept events that cause a frame to start over at frame 0 and loop from there. You also need to be able to "run once" and quit.

Another thing to consider: What if the game pauses? In certain cases the animations should pause as well. Also, what if the computer goes to sleep for some length of time and returns? This last one is a harder problem that I think is mostly safe to ignore though an easy solution would be worth considering.

Milan Mimica
Member #3,877
September 2003
avatar

You should to it in spirit of A5 - emit an event when an animated bitmap is updated.

Trezker
Member #1,739
December 2001
avatar

I think you should have ALLEGRO_ANIMATION to which you can load an animation file.
Then an ALLEGRO_ANIMATION_INSTANCE with functions for playback of an animation.

Thus you can use the same animation at different speeds and loop settings at the same time.

Christopher Bludau
Member #5,401
January 2005

Quote:

For those interested, I'm going to ramble on about animation libraries.

Will have a look at this. Thanks!

Quote:

Another thing to consider: What if the game pauses? In certain cases the animations should pause as well.

If the game pauses then it shouldn't call the update methods for any of its containing objects. Not doing so causes the animation to pause too.

Quote:

Also, what if the computer goes to sleep for some length of time and returns? This last one is a harder problem that I think is mostly safe to ignore though an easy solution would be worth considering.

After such a case the delta time would be huge updating the bitmap accordingly and correct. Or I misunderstood you here.

Quote:

You also need to be able to "run once" and quit.

I will add nonlopping animation today after work. What do you mean with quit?
That the animation gets destroyed?

Quote:

You should to it in spirit of A5 - emit an event when an animated bitmap is updated.

Ok. Will have to look into that.
When should the event be emitted? When the update method is called? Or when the animtaion is switching to the next frame?
The second thing seems more logically.
What happpens if the delta time is so huge, that the animtaion is skipping one frame. Should only one event be emitted? Or should the amount of events be similar to the skipped frames?

EDIT:

Quote:

I think you should have ALLEGRO_ANIMATION to which you can load an animation file.
Then an ALLEGRO_ANIMATION_INSTANCE with functions for playback of an animation.

Thus you can use the same animation at different speeds and loop settings at the same time.

Don't think I get what you mean here. Could you explain a little bit more?

Audric
Member #907
January 2001

Most games needs much more than just sprite number + duration on each frame.
Some examples:
- character dimensions, for axis-aligned bounding box.
- sprite offsets
- collision flags (think a Street fighter animation : which parts of character are hitting, which parts are vulnerable)
- position of "attachments" (character holding an object: which Shield sprite do we display, at which coords.)
- a function pointer, to handle anything.

Each game has different needs, so the best system for a tactical RPG isn't going to be good for a VS fighting game or a platform shooter.

Christopher Bludau
Member #5,401
January 2005

Yea. But originally I only wanted to do an animated bitmap. That has nothing to do with all the collision stuff... but i get you point here.

But this proofs why it would be a bad idea to integrate something like that into allegro.

Trezker
Member #1,739
December 2001
avatar

I give code example.

1ALLEGRO_ANIMATION* my_animation = al_animation_load("animated_thing.ani");
2//Instances hold a pointer to the animation.
3ALLEGRO_ANIMATION_INSTANCE *slow_thing = al_animation_create_instance(my_animation);
4ALLEGRO_ANIMATION_INSTANCE *quick_thing = al_animation_create_instance(my_animation);
5//Set slow thing to run at half speed
6al_animation_set_speed(slow_thing, .5);
7//Set quick thing to run at double speed
8al_animation_set_speed(quick_thing, 2);
9 
10float dt = [insert time code here]
11al_animation_update(slow_thing, dt);
12al_animation_update(quick_thing, dt);
13 
14al_draw_bitmap(al_animation_current_frame(slow_thing), slow_x, slow_y, 0);
15al_draw_bitmap(al_animation_current_frame(quick_thing), quick_x, quick_y, 0);
16 
17al_animation_destroy_instance(slow_thing);
18al_animation_destroy_instance(quick_thing);
19al_animation_destroy(my_animation);

Christopher Bludau
Member #5,401
January 2005

Ah, ok. Now I got it. I like the idea.

What I don't see is how a variable frame rate would be possible with this.
But I think that wasn't your goal to show with this approach.
But I already have an idea how to solve the varialbe frame rate thing.

Edgar Reynaldo
Member #8,592
May 2007
avatar

Quote:

What I don't see is how a variable frame rate would be possible with this.

You can take a look at a C++ animation example I coded up a while ago :
http://www.allegro.cc/forums/thread/599116/792937#target

Look at the AnimBase::AdvanceTime function to see what I did for it. All you have to do is store the frame rate in seconds per frame and store the residual time accumulated so far. When it exceeds the frame rate, advance by the correct number of frames using division and then assign the residual time a new value using modulus and the frame rate.

Kotlet
Member #10,714
February 2009

I am currently coping with bitmap animation as well. Ideas spread by you guys around the forum helped me a lot, thank you all. In my opinion Trezker's idea is very interesting. Keeping bitmap sequences and animation instances separate makes stuff more flexible. Keep them in two data structures in some animation manager object and you got everything simple and under control.

Christopher Bludau
Member #5,401
January 2005

Ok, a new version is finally available.
New features are:
- varialbe frame rate
- looping
- ping pong
- Trezker's animation_instance idea

What's missing:
- events (haven't looked into allegro's events yet, if you have tips feel free to say where I can look)
- loading from file so the arrays and stuff don't have to be created manually

1) So again first how you would use it:

1// create an array with bitmaps
2ALLEGRO_BITMAP* bitmaps[5];
3bitmaps[0] = bmp1;
4bitmaps[1] = bmp2;
5bitmaps[2] = bmp3;
6bitmaps[3] = bmp4;
7bitmaps[4] = bmp5;
8 
9// crate an array with frame times
10float frame_times[5];
11frame_times[0] = 0.5;
12frame_times[1] = 0.5;
13frame_times[2] = 0.5;
14frame_times[3] = 0.5;
15frame_times[4] = 0.5;
16 
17 
18// then create an animation
19// - you pass the array of the bitmaps
20// - array with frame times
21// - the number of frames
22ANIMATION* anim = create_animation(bitmaps, frame_times, 5);
23 
24 
25// then you create an animation instance
26// - you pass the animation
27// - whether it should loop or not
28// - ping pong or not
29// - the speed multiplier
30// this would be a non ping pong, looping animation with doubled speed
31ANIMATION_INSTANCE* anim_instance = create_animation_instance(anim, true, false, 2.0);
32 
33// in you update part you update the animation_instance
34// you pass the animated bitmap and the delta time
35update_animation(anim_instance, dt);
36 
37//In you drawing part you draw the image like this
38al_draw_bitmap(get_active_animation(anim_instance), x_pos, y_pos, 0);
39 
40 
41// when you are done you free the memory
42destroy_animation(anim);
43destroy_animation_instance(anim_instance);

2) the code

1typedef struct ANIMATION_FRAME {
2 float frame_time; // the time this frame should be visible
3 float current_time; // the elapsed time so far
4 ALLEGRO_BITMAP *bitmap;
5} ANIMATION_FRAME;
6 
7typedef struct ANIMATION {
8 int frame_count; // contains the number of frames
9 int active_frame; // the currently active frame
10 ANIMATION_FRAME *frames[]; // containing all the different bmps
11 // for the different frams
12}ANIMATION;
13 
14typedef struct ANIMATION_INSTANCE {
15 bool run; // is set to false after a total animation
16 // if looping is false
17 bool looping;
18 bool ping_pong;
19 int direction; // 1 = upwards, -1 = downwards (for ping pong)
20 float speed_multiplier;
21 ANIMATION *animation;
22} ANIMATION_INSTANCE;
23 
24 
25ANIMATION* create_animation(ALLEGRO_BITMAP *bitmaps[], float frame_time[], int frame_count)
26{
27 ANIMATION *anim;
28 anim = (ANIMATION*) malloc(sizeof(ANIMATION) + frame_count * sizeof(ANIMATION_FRAME));
29 anim->frame_count = frame_count;
30 anim->active_frame = 0;
31 
32 for (int i = 0; i < frame_count; i++)
33 {
34 ANIMATION_FRAME *f = new ANIMATION_FRAME;
35 f->bitmap = bitmaps<i>;
36 f->current_time = 0;
37 f->frame_time = frame_time<i>;
38 anim->frames<i> = f;
39 }
40 
41 return anim;
42}
43 
44ANIMATION_INSTANCE* create_animation_instance(ANIMATION *anim, bool looping, bool ping_pong, float speed_multiplier)
45{
46 ANIMATION_INSTANCE *anim_instance = new ANIMATION_INSTANCE;
47 anim_instance->animation = anim;
48 anim_instance->looping = looping;
49 anim_instance->ping_pong = ping_pong;
50 anim_instance->speed_multiplier = speed_multiplier;
51 anim_instance->run = true;
52 anim_instance->direction = 1;
53 
54 return anim_instance;
55}
56 
57void update_animation(ANIMATION_INSTANCE *anim_instance, float delta_time)
58{
59 ANIMATION *anim = anim_instance->animation;
60 int i = anim->active_frame;
61 while (delta_time > 0 && anim_instance->run)
62 {
63 if (i >= anim->frame_count)
64 {
65 // restarts a lopping anim at frame 0
66 if(anim_instance->looping && !anim_instance->ping_pong)
67 i = 0;
68 
69 // reverses the ping pong direction
70 if(anim_instance->ping_pong)
71 {
72 // we don't want to see the last frame twice
73 // that's why we substract 2
74 i = anim->frame_count-2;
75 anim_instance->direction = -1;
76 }
77 
78 // animation stop
79 if(!anim_instance->looping && !anim_instance->ping_pong)
80 {
81 delta_time = 0;
82 i = anim->frame_count-1;
83 anim_instance->run = false;
84 }
85 }
86 if (i < 0)
87 {
88 // reverses the ping pong direction
89 if(anim_instance->looping && anim_instance->ping_pong)
90 {
91 // we don't want to see the first frame twice
92 // thats' why we set i to 1
93 i = 1;
94 anim_instance->direction = 1;
95 }
96 
97 // animation stop
98 if(!anim_instance->looping && anim_instance->ping_pong)
99 {
100 delta_time = 0;
101 i = 0;
102 anim_instance->run = false;
103 }
104 }
105 delta_time -= (anim->frames<i>->frame_time / anim_instance->speed_multiplier) - anim->frames<i>->current_time;
106 anim->frames<i>->current_time = 0;
107 if(delta_time > 0)
108 i+=anim_instance->direction;
109 }
110 anim->frames<i>->current_time = (anim->frames<i>->frame_time / anim_instance->speed_multiplier) + delta_time;
111 anim->active_frame = i;
112}
113 
114ALLEGRO_BITMAP* get_active_animation(ANIMATION_INSTANCE *anim_instance)
115{
116 return anim_instance->animation->frames[anim_instance->animation->active_frame]->bitmap;
117}
118 
119void destroy_animation(ANIMATION *anim)
120{
121 for(int i = 0; i < anim->frame_count; i++)
122 {
123 free(anim->frames<i>);
124 }
125 free(anim);
126}
127 
128void destroy_animation_instance(ANIMATION_INSTANCE *anim_instance)
129{
130 free(anim_instance);
131}

So again, comments, ideas, feature requests welcome!

Milan Mimica
Member #3,877
September 2003
avatar

I don't think you need events any more.

GullRaDriel
Member #3,861
September 2003
avatar

You should include a check and a destroy_animation call into destroy_animation_instance.

I mean:

1void destroy_animation(ANIMATION **anim)
2{
3 for(int i = 0; i < (*anim)->frame_count; i++)
4 {
5 free((*anim)->frames<i>);
6 }
7 free((*anim));
8 (*anim) = NULL ;
9}
10 
11void destroy_animation_instance(ANIMATION_INSTANCE **anim_instance)
12{
13 if( (*anim_instance) -> anim )
14 destroy_animation( &(*anim_instance) -> anim )
15
16 free((*anim_instance));
17 (*anim_instance) = NULL ;
18}

I probably messed up something, but the idea behind that is that when you make a call to destroy_animation_instance it also check and destroy the frames if needed.

Hoping you get what I mean :-/

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

Christopher Bludau
Member #5,401
January 2005

Yea, I get what you mean.
At first I had this included in the destroy_animation_instance function.

BUT then I thought about it, and one animation could be part of MANY animation_instances (that's the point of the animation_instance).

So only beacuse I destroy one instance I can't destroy the contained animation.
Other instances would stop working then beacause they lost their animation.

Hope that makes sense?

Quote:

I don't think you need events any more.

How comes that?

Dustin Dettmer
Member #3,935
October 2003
avatar

What would be really great is datafile integration.

Thomas Fjellstrom
Member #476
June 2000
avatar

Quote:

You should include a check and a destroy_animation call into destroy_animation_instance.

Maybe I'm just being stupid, but why in the hell would you want to destroy the ANIMATION when you're just freeing the ANIMATION_INSTANCE? Other ANIMATION_INSTANCES could be using that ANIMATION at the time, or you may want to use it again after the call to destroy_animation_instance.

--
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

Christopher Bludau
Member #5,401
January 2005

No, not stupid at all.
I think the same as you do here:

Quote:

and one animation could be part of MANY animation_instances (that's the point of the animation_instance).

So only beacuse I destroy one instance I can't destroy the contained animation.
Other instances would stop working then beacause they lost their animation.

Quote:

What would be really great is datafile integration.

Wow, didn't think of that. Nice idea!
Will have to think about the whole animation loading/file/datafile things...

Dustin Dettmer
Member #3,935
October 2003
avatar

.gif Import and stuff.

 1   2   3   4 


Go to: