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.