In CosPlay, sprite animation is supported by the following classes:
CPAnimation.getKeyFrame(ctx)
method. Animation is defined as a sequence of key frames. Each key frame has an image and the index of its position in the sequence of key frames. Animation produces a key frame given animation context via method CPAnimation.keyFrame()
. Note that animation definition is abstracted out from the way it is rendered. The same animation can be rendered differently.
One such rendering is implemented by the built-in sprite CPAnimationSprite class. Class CPAnimationSprite provides convenient built-in support for rendering the animation. Unless you need some very specific rendering implementation - you will use or extend this sprite class to work with the animation.
Animation Is An Asset
Just like other assets such as CPFont, CPImage, CPParticleEmitter, CPShader or CPVideo they are not managed or governed by the CosPlay game engine unlike scenes and scene object, that are managed and governed by the game engine. Assets are typically created outside the game loop and managed by the developer, they can be freely shared between scenes or scene objects as any other standard Scala objects.
Note that CPAnimation companion object provides factory methods that produce often used types of animation:
CPAnimation.timeBased()
- creates new time-based animation. Time-based animation is based on idea that animation is a sequence of image and duration pairs. Playing such animation simply means sequentially playing and image for its specified duration and then switching to the next pair and repeating the same process.
CPAnimation.filmStrip()
- creates new filmstrip animation. Filmstrip animation is a variation of time-based animation where all key frames have the same duration, like in a movie, hence the name.
Here's a code snippet from the built-in Animation Example that demonstrates the basic usage of animation functionality:
// Create key frames for animation. val imgVert = new CPArrayImage( prepSeq( """ | o/ | /| | / \ |------ | o) | /| | ( \ |------ | (o | |\ | / ) |------ | \o | |\ | / \ |------ """).filter(!_.endsWith("------") ), skin).split(5, 3) // Animation descriptor. val vertAni = CPAnimation.filmStrip("vert", 150, imgs = imgVert) // Animated player sprite. val player = new CPAnimationSprite("player", Seq(vertAni), 45, 19, 0, "vert") { ... }
NOTES:
The Animation Example looks like this:
CosPlay comes with several prefab images that can be used in animation. These images are located in org.cosplay.prefabs.images.ani
package. Note that you can preview these images as animation using built-in animation previewer CPImage.previewAnimation(...) method.
Even though CosPlay's effective FPS is usually around 1,000 on modern computers, the typical ANSI terminal struggles to render a full screen update even at partly 30 FPS. Large frame updates tend to result in "rolling shutter" effect, which is pronounced more for certain shapes and certain movement directions. It is important to note that this effect is more pronounced for horizontal movement of large vertical shapes. Try to avoid this type of movement in your games.
It should come as no surprise that animation in ANSI terminal can only happen in one character units. You can't move by individual pixel - you only move by 10-20 pixel at a time depending on the font size used by your terminal. Even the specific font size is not directly available to the native ASCII game.
Discrete animation is obviously more jerky and more stuttering comparing to the pixel-based animation of the traditional graphics games. Vertical movement is more jerky than horizontal one since character height is usually larger than the character width (and we can move only a one character at a time).
There are two ways to mitigate this limitation:
In CosPlay there are different ways one could implement animated scene objects. In the end, all of these approaches deliver similar results but each individual technique is tailor-made for a specific animation type:
The classic sprite animation technique (discussed in this section) ideally suited for animations that can be defined as a sequence of individual similarly or identically sized images. When played, each individual images is shown for a short period of time one after another delivering animation effect.
Lets consider an animation of the airplane in a top-down view game play. The basic animations for the airplane banking left or right, taking off or landing are ideally suited for sprite-based animation as they can easily be defined as a short sequence of individual images.
Particle Effects animation is based on the concept of a pixel-based particle and particle emitter. Particle emitter emits particles. Each particle and its emitter have a fully programmable behavior. The key characteristic of particle effect animation is the randomness over the large number of individual elements that you can easily implement using fully programmable particles and emitters.
In our airplane example, lets consider how one could implement the explosion when the airplane is hit with the missile. One could potentially implement such animated explosion as a long sequence of images but such process would be very tidies and lack the desired randomness. Particle effect-based animation fits the bill perfectly in such cases allowing to implement such random explosion animation in just a few lines of code.
Sometime, a simple drawing on the canvas is all that's needed for a desired animation. Consider how one could implement a laser strike in our airplane example. A laser strike can be defined as a variable length line of pixel shown for a split second. The best way to implement it is with one line of code using many of the drawing functions in CPCanvas class and putting this logic into CPSceneObject.render()
method.
Video sprite is a variation of sprite-based animation. In case of video, there are typically a lot more frames (often 1000s of frames) and all these frames have the same dimension. CPVideo and CPVideoSprite provide easy-to-use mechanism to implement it. Back to our airplane example, the video-based animation would be ideal choice for the cutscenes, entry video, etc.
Shader is a piece of user-defined code that is executed on each frame for each scene object that has one or more shaders attached to it. There are types of animations that simply don't fit any previous type. The typical example of shader-based animation is the various lighting effect: flash-lite, sun shadows, transitions, highlighting, etc. These types of animation simply cannot be reasonably implemented using sprites, or particles, or canvas drawing. In such cases, shaders provide simple and effective contract to implement this behavior. Yet another unique characteristic of shaders is their application reusability. In fact, the same shader can be added to multiple scene objects to provide its effect.
In our airplane example, shaders can be used for shadow effect or "flashing" the airplane when it is hit by the enemy fire.