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
The jsmovecel program actually uses a convenience function for moving a cel, which is then used by
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;
}
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);
}
MapCel()
function, discussed below, or change field values:
ccb_HDX
and ccb_HDY
-control the offset of the upper-right corner from the upper-left corner
ccb_VDX
and ccb_VDY
-control the offset of the lower-left corner from the upper-left corner
ccb_HDDX
and ccb_HDDY
-control the offset of the lower-right corner from the lower-left corner
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.
MapCel()
takes two parameters:
Point
structures that contains the simple XY locations of the four corners of the destination rectangle. The upper-left corner of the source gets mapped to Corner[0]
, the upper-right corner to Corner[1]
, the lower-right corner to Corner[2]
, and the lower-left corner to Corner[3]
.
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;
}