Programming AmigaOS in C

10. Animation

Sprites and VSprites

The Amiga supports Simple (Hardware) Sprites and VSprites (virtal sprites) for animation. The Amiga hardware supports upto 8 hardware sprites which are controlled by the DMA (Direct Memory Access) channels in hardware which control all the sprites simulteously. Unfortunately, using hardware is fast there are limitations: you canm have only upto 8 of them on screen at once, sprites can only be 16 pixels wide (the height can be anything), use upto 3 colours per sprite, you can change its position by changing its X,Y co-ordinates (preferably during the frame blank period to prevent screen glitches) and so on. Simple (Hardware) Sprites are not available on new AmigaOS 4 systems.

To support more sprites, you can use VSprites which are managed by the GELs system (which consists of Rastports, Viewports and Views to split the display up) which is also compatible with different playfields such as dual playfields. Collision detection is a case of using masks to see if sprites overlap at some point (masks include a hitmask, memask, borderline and collmask). To make larger sprites you can 'overlap' sprites to make them wider and larger than the default 16 pixels wide. Also, for more colours, you can 'attach' sprites to each other.

To use bobs, you need to include the following header files:
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <intuition/intuition.h>
#include <graphics/gels.h>

To create sprites you need to define their images and colours using WORD sized values. Here we define two images (16 bits wide) two set of colour data (see VSprite.c example in the Rom Kernel Manuals Lib Examples or RKMCompanion disk). The __chip keyword specifies (SAS/C, GCC) where to put graphics data ie Chip RAM (use double underlines before 'chip' to make it ANSI compliant).

#define GEL_SIZE 4 /* number of lines in the vsprite */
/* VSprite data - there are two sets that are alternated between. */
/* note that this data is always displayed as low resolution. */
   WORD __chip vsprite_data1[] = { 0x7ffe, 0x80ff,
   0x7c3e, 0x803f,
   0x7c3e, 0x803f,
   0x7ffe, 0x80ff,
   0, 0 };
   WORD __chip vsprite_data2[] = { 0x7ffe, 0xff01,
   0x7c3e, 0xfc01,
   0x7c3e, 0xfc01,
   0x7ffe, 0xff01,
   0, 0 };
   WORD mySpriteColors[] = { 0x0000, 0x00f0, 0x0f00 };
   WORD mySpriteAltColors[] = { 0x000f, 0x0f00, 0x0ff0 };

Next, we need to define the newVSprite data structure (see Animtools.h):
/* Information for the new VSprite */
   NEWVSPRITE myNewVSprite = { 
      /* Image data, sprite color array word width (must be 1 for    true VSprite) */
      vsprite_data1, mySpriteColors, 1,
      /* Line height, image depth (must be 2 for true VSprite),    x, y position */ */
      GEL_SIZE, 2, 160, 100,
      /* Flags (VSPRITE == true VSprite), hit mask and me mask */
       VSPRITE, 1 << BORDERHIT, 0
   }; 

We also need a window to display the sprites in. This a 400 x 150 window using the Workbench screen:

struct NewWindow myNewWindow = { 
     80, 20, 400, 150, -1, -1, IDCMP_CLOSEWINDOW | IDCMP_INTUITICKS,
     WFLG_ACTIVATE | WFLG_WINDOWCLOSE | WFLG_WINDOWDEPTH | WFLG_RMBTRAP | WFLG_WINDOWDRAG,
     NULL, NULL, "VSprite", NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
   };
 

We need a simple routine to display the sprites on the screen.

Functions:
void SortGList(struct RastPort *rp)
void DrawGList(struct RastPort *rp, struct ViewPort *vp)
long RethinkDisplay(void)

/* Basic VSprite display subroutine */
   VOID vspriteDrawGList(struct Window *win, struct RastPort *myRPort)
   {
     /* This function sorts the gel list by its x,y co-ords, required before DrawGList */ 
     SortGList(myRPort);
     /* This function draws all the gels including bobs and vsprites */ 
     DrawGList(myRPort, ViewPortAddress(win));
     /* Reconstruct the entire display using viewports */ 
     RethinkDisplay();
   }

For AmigaOS 4, prefix SortGList, DrawGList with 'IGraphics->'. Prefix RethinkDisplay with 'IIntuition->'.

Now for for border collection checking. This routine will check if a sprite has hit the left or right borders. The SetCollision() function in doVSprite
will call this function up if borderflags match RightHit or LeftHit (see graphics/collide.h header) values and then change the sprite colours and VUserExt
(a user definable value).

VOID borderCheck(struct VSprite *hitVSprite, LONG borderflags)
   {
     if (borderflags & RIGHTHIT)
     {
       hitVSprite->SprColors = mySpriteAltColors;
       hitVSprite->VUserExt = -40;
     }
     if (borderflags & LEFTHIT)
     {
       hitVSprite->SprColors = mySpriteColors;
       hitVSprite->VUserExt = 20;
     }
   }

Now, you can setup a VSprite with the following function which will create and display a given sprite.

Functions:
void AddVSprite(struct VSprite *vs, struct RastPort *rp)
void SetCollision(long vectornum, void (*)routine(), struct GelsInfo *info)
void InitGels(struct VSprite *head, struct VSprite *tail, struct GelsInfo *info)
void RemVSprite(struct VSprite *vs)

VOID do_VSprite(struct Window *win, struct RastPort *myRPort)
   {
     /* myVSprite stores data about the VSprite to be displayed */
     struct VSprite *myVSprite;
     /* my_ginfo will store information about GELs display to display VSprites - see Animtools.c */
     struct GelsInfo *my_ginfo;
 
  /* Set up the Gels system and return pointer to my_ginfo otherwise return a warning code */
     if (NULL == (my_ginfo = setupGelSys(myRPort, 0xfc)))
       return_code = RETURN_WARN;
     else
     {
      /* Create the VSprite structure read to be used else return warning code */
       if (NULL == (myVSprite = makeVSprite(&myNewVSprite)))
          return_code = RETURN_WARN;
       else
      {
         /* Add VSprite to Rastport and display it    */ 
         AddVSprite(myVSprite, myRPort);
         vspriteDrawGList(win, myRPort);
         myVSprite->VUserExt = 20;
         /* Set collision dection routine using BorderHit vector get GelsInfo structure */
         SetCollision(BORDERHIT, borderCheck, myRPort->GelsInfo);
        /* do sprite animation here */
         process_window(win, myRPort, myVSprite);
        /* remove the vsprite and clean up system */
         RemVSprite(myVSprite);
         freeVSprite(myVSprite);
      }
      vspriteDrawGList(win, myRPort);
      cleanupGelSys(my_ginfo, myRPort);
     }
   }

For AmigaOS 4, prefix AddVSprite, SetCollision and RemVSprite with 'IGraphics->'.
See VSprite.c for definitions for setupGelSys, makeVSprite, vspriteDrawGList, freeVSprite and cleanupGelSys,

Finally, the animation routine to move the sprite around the screen by changing the sprite's X and Y values and change the image
by changing the ImageData value to point to different image data. Also, in the loop, check for any collisions using DoCollision
function (set up earlier with SetCollision function).

Functions:
void DoCollision( struct RastPort *myRPort)

VOID process_window(struct Window *win, struct RastPort *myRPort, struct VSprite *myVSprite)
   {
     struct IntuiMessage *msg;
     /* Do a for loop for ever */
     FOREVER
     {
       /* Wait for a messaging signal to appear otherwise do nothing *
       Wait(1L << win->UserPort->mp_SigBit);
       /* There may be a queue of them so respond to each one and get a message */
       while (NULL != (msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
       {
        /* Only CLOSEWINDOW and INTUITICKS are active    */
        /* If CloseWindow message is received, reply and exit this routine */
        if (msg->Class == IDCMP_CLOSEWINDOW) 
        {
          ReplyMsg((struct Message *)msg);
          return;
        }
       /* Must be an INTUITICKS: change x and y values on the fly. Note offset by
       ** window left and top edge--sprite relative to the screen, not window. Divide
       ** the MouseY in half to adjust for Lores movement increments on a Hires screen.
       */
       myVSprite->X = win->LeftEdge + msg->MouseX + myVSprite->VUserExt;
       myVSprite->Y = win->TopEdge + msg->MouseY/2 + 1;
       ReplyMsg((struct Message *)msg);
     }
     /* Got a message, change image data on the fly */
     myVSprite->ImageData = (myVSprite->ImageData == vsprite_data1) ? vsprite_data2 : vsprite_data1;
     SortGList(myRPort);
     /* Test for any collisions by checking every VSprite in the gel    list */
     DoCollision(myRPort);
     vspriteDrawGList(win, myRPort);
   }
}

For AmigaOS 4, prefix Wait and ReplyMsg with 'IExec->'. Prefix SortGList and DoCollision with 'IGraphics->'.
See VSprite.c for definitions for vspriteDrawGList.

Next Page