Dependent on the time-lapse lib, the animation code provides you with several count methods to manipulate your animations in an easy and comprehensible way.
If you add a sprite atlas library (I’ll share later) you’ll be able to manipulate sprites with animation in quite a flexible way without too much hassle.
The idea is to encapsulate the animation functionality in such a way that it makes it easy to work with. You have the ability to start, stop, pause, change speed, etc. You can create animations that range from any frame to any frame and that can animate in many ways such as sequential (loop), play-once and ping-pong (back and forth, or rather the inverse). By making use of the time-lapse library we can easily create time-based animation.
The base code:
;############################################################################## ;$$$ file: Animation.pb - date: 05:05 p.m. 06/08/2009 $$$ ;$$$ author: Gustavo Julio Fiorenza (info@gushh.net || info@gushh.com.ar) $$$ ;$$$ aka: gushh, dagcrack. $$$ ;$$$ description: provides several count modes for animation purposes $$$ ;############################################################################## EnableExplicit XIncludeFile "TimeLapse.pb" ; Used for timing. Structure ANIMATION *timer.TIMELAPSE ; The timer's speed is "per frame", use the Get/SetLength functions to overcome this. frame_max.i ; Maximum global frame frame_min.i ; Minimum global frame frame_now.i ; Current local frame mode.i ; The animation mode (loop, play once, ping-pong, etc). status.i ; Internal var used for keeping track of the animation "status". It's used as a bool. is_playing.i ; Used to determine whether the animation is playing or not. EndStructure Enumeration 1 #ANIMATION_LOOP #ANIMATION_PLAYONCE #ANIMATION_PINGPONG EndEnumeration Procedure.i AnimationCreate( Speed.i, MinFrame.i, MaxFrame.i, CurrentFrame.i = 0, Mode.i = #ANIMATION_LOOP, *callback.i = #Null ) Define.ANIMATION *this = AllocateMemory( SizeOf(ANIMATION) ) If *this With *this \timer = TimeLapseCreate( Speed, *callback, *this ) ; We're passing this instance as the timer's user-data, useful in callbacks. \frame_max = MaxFrame \frame_min = MinFrame \frame_now = MinFrame + CurrentFrame \mode = Mode \status = 0 \is_playing = #True EndWith ProcedureReturn *this EndIf EndProcedure Procedure.i AnimationDestroy( *this.ANIMATION ) If *this If *this\timer TimeLapseDestroy(*this\timer) EndIf FreeMemory(*this) *this = #Null ProcedureReturn *this EndIf EndProcedure Procedure.i AnimationUpdate( *this.ANIMATION ) If *this If *this\is_playing If TimeLapseUpdate( *this\timer ) Select *this\mode Case #ANIMATION_LOOP *this\frame_now + 1 If *this\frame_now > *this\frame_max *this\frame_now = *this\frame_min EndIf Case #ANIMATION_PINGPONG If *this\status *this\frame_now - 1 If *this\frame_now < = *this\frame_min *this\status = #True - *this\status EndIf Else *this\frame_now + 1 If *this\frame_now => *this\frame_max *this\status = #True - *this\status EndIf EndIf Case #ANIMATION_PLAYONCE If *this\status ; status is set to one, this means we already played the animation. Else *this\frame_now + 1 If *this\frame_now => *this\frame_max *this\status = #True - *this\status EndIf EndIf EndSelect ProcedureReturn #True ; always return true unless you want to invalidate the update call! EndIf EndIf EndIf EndProcedure
The helper functions:
Procedure.i AnimationGetCurrentFrame( *this.ANIMATION ) ProcedureReturn *this\frame_now - *this\frame_min EndProcedure Procedure.i AnimationSetCurrentFrame( *this.ANIMATION, Frame.i ) *this\frame_now = (Frame - *this\frame_min) EndProcedure Procedure.i AnimationGetTotalFrames( *this.ANIMATION ) ProcedureReturn (*this\frame_max - *this\frame_min) EndProcedure Procedure.i AnimationGetMode( *this.ANIMATION ) ProcedureReturn *this\mode EndProcedure Procedure.i AnimationSetMode( *this.ANIMATION, NewMode.i ) *this\mode = NewMode EndProcedure Procedure.i AnimationGetSpeed( *this.ANIMATION ) ProcedureReturn TimeLapseGet( *this\timer ) EndProcedure Procedure.i AnimationSetSpeed( *this.ANIMATION, Speed.i ) TimeLapseSet( *this\timer, Speed ) EndProcedure Procedure.i AnimationPlay( *this.ANIMATION ) *this\is_playing = #True EndProcedure Procedure.i AnimationPause( *this.ANIMATION ) *this\is_playing = #False EndProcedure Procedure.i AnimationStop( *this.ANIMATION ) *this\is_playing = #False *this\frame_now = *this\frame_min EndProcedure Procedure.i AnimationReset( *this.ANIMATION ) AnimationStop(*this) AnimationPlay(*this) EndProcedure Procedure.i AnimationGetLength( *this.ANIMATION ) ProcedureReturn AnimationGetTotalFrames( *this ) * AnimationGetSpeed( *this ) EndProcedure Procedure.i AnimationSetLength( *this.ANIMATION, NewLength.i ) AnimationSetSpeed( *this, NewLength / AnimationGetTotalFrames( *this ) ) EndProcedure
Thanks to the base lib, you can also use callbacks, this are useful for executing events on known frames (say you’re writing a game and you want a sound to be played at frame 12 from the jumping cycle… easy!)
A minimal example:
Define.ANIMATION *anim If OpenConsole() *anim = AnimationCreate( 250, 0, 9, 0, #ANIMATION_PINGPONG ) If *anim Repeat If AnimationUpdate( *anim ) ; the reason frame 0 is not seen in the first print is because we update before we display. PrintN( Str( AnimationGetCurrentFrame( *anim ) ) ) EndIf Delay(10) Until Inkey() = Chr(27) AnimationDestroy(*anim) EndIf CloseConsole() EndIf
A somewhat bloated example with callbacks can be found in here.
The playback functionality could be extended, however I found little need for other methods — That’s basically my excuse for “I ran out of ideas” — One thing I’d do is decouple the “play once” functionality from the playing mode, so essentially you’d be able to play a loop or ping-pong animation just once.
That’s pretty much it. Once again, this is trivial code but it’s a must and I’m sure someone will benefit from it.
The only reason I didn’t post a graphical example is because you need a way of dealing with sprite sheets and that’s the job for yet another library. Should you use the sprite library, you’d still need to feed the ClipSprite() function with meaningful data, there is no point in doing it by hand. That said, I’ll post the “sprite atlas” library tomorrow, hopefully.
Cheers
This is great stuff!! thanks!!
I implemented it in my game and it works flawlessly! its way better than what i had.
I wouldn’t mind to have a delay option so after the frames are played the animation waits X amount of time but other than that its a great idea!
One thing i noticed is that you cant do fast animations with this code, how can i go around that? oh and will you release the sprite library?, what is it about?, did you implement a whole framework?, do tell!
Thanks for the suggestion. It seems like a useful addition, I’ll be adding it eventually. Good to know it works for you.
Animations are timed through TimeLapse which uses two timing methods depending on the current platform, under Windows it uses timeGetTime() however we’re not defining a resolution, so this could be the problem in your case. Although if you need “very fast” animations you might want to look into the performance counter, needless to say it’s beyond the scope of this little library but should it prove a requirement, I’ll modify it accordingly.
The “sprite library” is a wrapper around the official libs, providing atlas support (sprite sheets), it was initially designed for a friend with retro games in mind (no alpha channel, etc) so I’ll probably end up modifying it quite a bit before release. It only supports uniform grids to avoid using metadata. A more thorough solution would be to pack the sprites and automatically generate the metadata however it’s once again beyond the scope of the library.
Cheers.