Working With Animations


This section looks at some of the example code in jsanimation.c, explaining the following:

Loading an Animation

The Lib3DO functions LoadAnim() loads an animation, as shown in the code fragment in the example below, which is part of the Initialize() function.

Example 1: Loading an animation.

if ( ( gExplosionAnim = LoadAnim("jsdata/art/boom.anim", 
    MEMTYPE_CEL )) == 0)
    {
    PRT(("Can't load the explosion animation\n"));
    goto DONE;
    }
UnifyAnimation(gExplosionAnim);
gExplosionCCB = GetAnimCel(gExplosionAnim, 0);
LAST_CEL(gExplosionCCB);

LoadAnim() expects the name of an animation file in 3DO file format, a buffer, and a pointer to an ANIM structure that is filled by LoadAnim(). The ANIM structure has as one of its fields a pointer to an array of AnimFrame structures. Together, the two structures contain all the information you need to manipulate the animation.

Example 2: The AnimFrame and ANIM structures.

typedef struct tag_AnimFrame
    {
    CCB *af_CCB;                        /*Pointer to CCB for this frame*/
    char *af_PLUT;                        /*Pointer to PLUT for this frame*/
    char *af_pix;                        /*Pointer to pixels for this frame*/
    int32        reserved;
    } AnimFrame;

typedef struct tag_ANIM
    {
    long num_Frames;                    /*max number of PDATs or CCBs in file*/
    frac16 cur_Frame;                    /*allow fract values for smooth speed*/    
    long num_Alloced_Frames;
    AnimFrame *pentries;
    } ANIM;

Playing the Animation

In the simplest case, you can loop through calls to GetAnimCel() to play the animation. GetAnimCel() takes an ANIM structure created by LoadAnim() and a frame increment as arguments. It returns a pointer to the CCB of the next cel in an animation sequence and adds frameIncrement to the current frame counter within the ANIM. It does not draw the cel.

The example below shows a code fragment from the TargetAction() function in jsanimation.c which retrieves and draws the animation. The FRAME_INCREMENT macro is defined at the beginning of the program.

Example 3: Retrieving and drawing an animation.

/*
    Animate the UFO: If it's still exploding, play the explosion's next frame,
    otherwise display the UFO (after a nominal post-explosion delay).
*/
{
    if (gBoomCount)
    {
        gBoomCount--;
        if (gBoomCount)
        {
            GetAnimCel(gExplosionAnim, FRAME_INCREMENT);
            DrawCels(gScreenContext->sc_BitmapItems
                            [gScreenContext->sc_curScreen], gExplosionCCB);
        }
        else
        {
            gExplosionAnim->cur_Frame = 0;                                    /* Reset animation to first frame */
            gPostBoomDelay = gExplosionAnim->num_Frames * 5;    
                                            /* Wait awhile before showing the target */
        }
    }
    else
    {
        if (gPostBoomDelay)
            gPostBoomDelay--;
        if (!gPostBoomDelay)
            DrawCels(gScreenContext->sc_BitmapItems
                                            [gScreenContext->sc_curScreen], aTarget);
    }
}

The actual switching of buffers is done by the main() function, which also displays the image, handles control pad input-discussed in "Getting User Input"-and handles the drawing of the UFO and explosion.

Example 4: Buffer switching and screen display.

while ( HandleControlPad() >= 0 )
    {
        /* Draw the background image */
        DrawImage(gScreenContext->sc_Screens [gScreenContext->
                    sc_curScreen], gBackgroundImage, gScreenContext);

        /* Handle the drawing of the UFO cel and the explosion */
        TargetAction(gUFO_CCB);

        /*
            Display the current screen.

            DisplayScreen waits for the next vertical blank, then
            tells the display hardware to use the specified screen 
            as the display buffer. The hardware continues showing
            this buffer on the TV every frame until another call
            to DisplayScreen specifies a different buffer.
        */
        DisplayScreen(gScreenContext->sc_Screens[gScreenContext->
                    sc_curScreen], 0);

        /* Toggle the current screen */
        gScreenContext->sc_curScreen = 1 - gScreenContext->
                    sc_curScreen;

    }