Moving and Distorting a Cel


You can move and distort a cel in one of two ways:

Manipulating a Cel by Changing CCB Values

You can move and distort a cel by changing its position fields. The ccb_XPos and ccb_YPos fields specify the location of the upper-left corner of the cel. These fields are stored as 16.16 numbers-that is, the upper 16 bits are an integer and the lower 16 bits are a fraction. Most of the time, this simply means that you'll shift the desired coordinate up 16 bits before storing it in the CCB. Changing the fields changes the location of the cel the next time the cel is rendered.

The TargetAction() function from jsinteractivesound.c moves and scales the UFO cel. The UFO starts out at distance 8000 (in arbitrary units) and flies toward the player until it reaches 200 or less, at which point it vanishes and the player is penalized for letting a UFO get away. As the UFO flies toward the camera, it is scaled appropriately. Meanwhile, the UFO also bounces off the edges of an imaginary rectangle 10 pixels in from the edges of the screen. The speed at which the UFO bounces around is divided by the distance, to reflect the way a real object would behave.

The example below shows a code fragment from jsinteractivesound.c.

Example 1: Moving a cel

void TargetAction(CCB* aTargetCCB)
    /*
        Animate the UFO: If it's still exploding, play the explosion's next frame,
        otherwise display the UFO (after a nominal post-explosion delay).
    */
    {
    static int32 iDeltaX = Convert32_F16(1);
    static int32 iDeltaY = Convert32_F16(3) >> 1;

    int32 iTest;

    if (gDistance > UFO_MAXDISTANCE)
        {
        gDistance = UFO_MAXDISTANCE;
        gVelocity = -gVelocity;
        gMisses++;
    
        aTargetCCB->ccb_XPos = Convert32_F16(VISIBLE_INSET + randbits(8));
        aTargetCCB->ccb_YPos = Convert32_F16(VISIBLE_INSET + randbits(9));
        }
    else if (gDistance < UFO_MINDISTANCE)
        {
        gDistance = UFO_MINDISTANCE;
        gVelocity = -gVelocity;
        }

    if ( gHoverMode && (gDistance < UFO_HOVER_DISTANCE) )
        {
        gDistance += (gVelocity >> 5);
        aTargetCCB->ccb_YPos += iDeltaY >> 1;
        aTargetCCB->ccb_XPos += iDeltaX >> 1;
        }
    else
        {
        gDistance += gVelocity;
        iTest = PROJECTED(UFO_MINDISTANCE);
        aTargetCCB->ccb_YPos += (iDeltaY >> 7) * iTest;
        aTargetCCB->ccb_XPos += (iDeltaX >> 7) * iTest;
    
        if (!randbits(8))
            iDeltaX *= -1;
    
        if (!randbits(8))
            iDeltaY *= -1;
        }

    aTargetCCB->ccb_HDX = PROJECTED(Convert32_F20(1));
    aTargetCCB->ccb_VDY = PROJECTED(Convert32_F16(1));

    iTest = ConvertF16_32(aTargetCCB->ccb_YPos) + 
                                                PROJECTED( aTargetCCB->ccb_Height );
    if (aTargetCCB->ccb_YPos <= Convert32_F16(VISIBLE_INSET))
        {
        aTargetCCB->ccb_YPos = Convert32_F16(VISIBLE_INSET);
        iDeltaY *= -1;
        }
    else if (iTest >= 149)
        {
        aTargetCCB->ccb_YPos -= Convert32_F16(iTest - 149);
        iDeltaY *= -1;
        }


The jsmovecel program actually uses a convenience function for moving a cel, which is then used by HandleControlPad().

Example 2: Using the MoveCCB convenience function.

void MoveCCB( CCB *aCCB, int32 xPos, int32 yPos )
/*
Convenience routine to move a cel to the specified int32 coordinates
*/
{
    aCCB->ccb_XPos = Convert32_F16(xPos);
    aCCB->ccb_YPos = Convert32_F16(yPos);
}

Distorting a Cel

In addition to moving a cel, you can stretch and rotate the cel. You can either use the MapCel() function, discussed below, or change field values:

Together, these six fields allow you to map the cel into any cel projection quadrilateral. You can scale, rotate, and skew cels and achieve a number of other effects. The operating system call MapCel(), discussed next, sets up these six fields and the x and y fields properly.

Using MapCel to Manipulate a Cel

All cels start out as rectangles. Even when the object itself doesn't fill the rectangle, the transparent pixels surrounding the object still exist. Whenever you draw the rectangle to the screen, you can put its four corners wherever you want. The cel engine stretches and skews the image to fit the destination quadrilateral.

Cel mapping gives you access to many effects.

The graphics folio function MapCel() takes two parameters:

The example below from jsinteractivesound.c uses MapCel to scale a cel.

Example 3: Code fragment using MapCel().

    /* While the C button and arrows are pressed distort the cel */
    if ( controlBits & ControlC )
    {
    if ( controlBits & ControlRight )
    {
        ++xDistPt;
    }
    else if ( controlBits & ControlLeft )
    {
        --xDistPt;
    }
    
    if ( controlBits & ControlUp )
    {
        --yDistPt;
    }
    else if ( controlBits & ControlDown )
    {
        ++yDistPt;
    }
    
    SetQuad (aQuad, gXPos, gYPos, 
            gXPos + gCcb->ccb_Width + xMovePt,
             gYPos + gCcb->ccb_Height+ yMovePt);
    
    aQuad[2].pt_X += xDistPt;
    aQuad[2].pt_Y += yDistPt;
    
    MapCel( gCcb, aQuad );
        
    goto DONE;
    }