Fundamentally you're on the right track - you need to know how long an animation lasts to do this sort of thing. Animations are more than just a collection of frames, there is all sorts of other information around them that you need. E.g. how many frames are there, does the animation loop, how quickly does it play back (e.g. 10 animation frames per second or 25, or 60?). Every animation can be defined in terms of a few pieces of data, which some generalised animation code can look at and play back. You should be encapsulating the animation part in its own bit of code, which is aware of nothing except these animation definitions and how to display the individual image frames. That is, have an animation object which you can load, start playing, stop playing, and tell to render at a particular location on screen.
A flexible approach is to use a sort of animation definition to encapsulate this sort of information. So rather than just saying "animation X is all of these frames, just play through them", you get something a bit more complex.
E.g. with some sort of mocked up data format
animations =
{
{ name="walk", files="walk*.png", frameCount="12", loop="true" },
{ name="fire" files="fire*.png" frameCount="6",
events = {
{ name="bulletLeavesGun", frame="4", param1="43", param2="30" }
}
}
}
So your code says something like:
currentAnimation = animations.Get("fire");
currentAnimation.Play();
How you detect events can either be with the animation code calling you back (i.e. when it detects a new event because the animation has played to a certain frame, it calls your game code to tell it about the new event), or by polling the animation like so:
List<Event> events = currentAnimation.EventsSinceLastCheck();
foreach (AnimationEvent event in events)
{
if (event.name == "bulletLeavesGun")
{
Vector2 bulletPosition = new Vector2(event.param1, event.param2);
Vector2 actualBulletPosition = new Vector2(
character.x + bulletPosition.x,
character.y + bulletPosition.y);
CreateBulletAt(actualBulletPosition);
}
}
Points to note:
- Animation code should exist separately from game code. You really don't want your gameplay code too tightly tied to the nuts and bolts of animation playback.
- The animation code knows whether or not to loop based on the animation definition
- The animation code knows when the animation is done, and can call back to some other code to say 'hey, the animation called "fire" just finished, what do you want to do now?'
- The animation code doesn't know anything about events other than that they have a name and some arbitrary data associated with them (param1 and param2)
- The animation code knows what frame it's currently on, and when it changes to a new frame, it can check and say 'oh, I'm on frame 4 now, that means this event called "fire" has just happened, add that to my list of recent events so I can tell anyone who asks about it'.
If you don't need the bullet firing to happen within the animation, but only once it has finished, you can get away with a much less complex system without the notion of events. But you'll still want a system where animations play back on their own, know how long they are, and can call back to the game code when an animation completes.