Copyright (c) Hyperion Entertainment and contributors.

Difference between revisions of "Virtual Sprites"

From AmigaOS Documentation Wiki
Jump to navigation Jump to search
 
Line 5: Line 5:
 
Before the system is told of a VSprite's existence, space for the VSprite data structure must be allocated and initialized to "correctly" represent a VSprite. Since the system does no validity checking on the VSprite structure, the result of using a bogus structure is usually a fireworks display, followed by a system failure.
 
Before the system is told of a VSprite's existence, space for the VSprite data structure must be allocated and initialized to "correctly" represent a VSprite. Since the system does no validity checking on the VSprite structure, the result of using a bogus structure is usually a fireworks display, followed by a system failure.
   
The system software provides a way to detect collisions between VSprites and other on-screen objects. There is also a method of extending the VSprite structure to incorporate user defined variables. These subjects are applicable to all GELs and are explained later in "Collisions and GEL Structure Extensions".
+
The system software provides a way to detect collisions between VSprites and other on-screen objects. There is also a method of extending the VSprite structure to incorporate user defined variables. These subjects are applicable to all GELs and are explained in [[Graphics_Sprites,_Bobs_and_Animation#Collisions_and_GEL_Structure_Extensions|Collisions and GEL Structure Extensions]].
   
 
=== Specification of VSprite Structure ===
 
=== Specification of VSprite Structure ===
Line 11: Line 11:
 
The VSprite structure is defined in the include file <graphics/gels.h> as follows:
 
The VSprite structure is defined in the include file <graphics/gels.h> as follows:
   
  +
<syntaxhighlight>
<pre>
 
 
/* VSprite structure definition */
 
/* VSprite structure definition */
 
struct VSprite {
 
struct VSprite {
Line 35: Line 35:
 
VUserStuff VUserExt;
 
VUserStuff VUserExt;
 
};
 
};
  +
</syntaxhighlight>
</pre>
 
   
 
There are two primary ways to allocate and fill in space for VSprite data. They can be statically declared, or a memory allocation function can be called and they can be filled in programmatically. The declaration to statically set up a VSprite structure is listed below.
 
There are two primary ways to allocate and fill in space for VSprite data. They can be statically declared, or a memory allocation function can be called and they can be filled in programmatically. The declaration to statically set up a VSprite structure is listed below.
   
  +
<syntaxhighlight>
<pre>
 
 
/* VSprite static data definition.
 
/* VSprite static data definition.
 
** must set the following for TRUE VSprites:
 
** must set the following for TRUE VSprites:
Line 50: Line 50:
 
{
 
{
 
NULL, NULL, NULL, NULL, 0, 0, VSPRITE, 0, 0, 5, 1, 2, 0, 0,
 
NULL, NULL, NULL, NULL, 0, 0, VSPRITE, 0, 0, 5, 1, 2, 0, 0,
&amp;myImage, 0, 0, &amp;mySpriteColors, NULL, 0x3, 0, 0
+
&myImage, 0, 0, &mySpriteColors, NULL, 0x3, 0, 0
 
};
 
};
  +
</syntaxhighlight>
</pre>
 
   
 
This static allocation gives the required VSprite structure, but does not allocate or set up collision masks for the VSprite. Note that the VSprite structure itself does not need to reside in Chip memory.
 
This static allocation gives the required VSprite structure, but does not allocate or set up collision masks for the VSprite. Note that the VSprite structure itself does not need to reside in Chip memory.
   
Refer to the makeVSprite() and freeVSprite() functions in the animtools.c listing at the end of the chapter for an example of dynamically allocating, initializing and freeing a VSprite structure.
+
Refer to the makeVSprite() and freeVSprite() functions in the animtools.c listing at the end of the article for an example of dynamically allocating, initializing and freeing a VSprite structure.
   
 
=== Reserved VSprite Members ===
 
=== Reserved VSprite Members ===
Line 73: Line 73:
 
The values can be set like this:
 
The values can be set like this:
   
  +
<syntaxhighlight>
<pre>
 
 
myVSprite.NextVSprite = NULL;
 
myVSprite.NextVSprite = NULL;
 
myVSprite.PrevVSprite = NULL;
 
myVSprite.PrevVSprite = NULL;
Line 80: Line 80:
 
myVSprite.OldY = 0;
 
myVSprite.OldY = 0;
 
myVSprite.OldX = 0;
 
myVSprite.OldX = 0;
  +
</syntaxhighlight>
</pre>
 
   
 
=== Using VSprite Flags ===
 
=== Using VSprite Flags ===
Line 101: Line 101:
 
The VSprite.Flags value should be initialized like this for a VSprite GEL:
 
The VSprite.Flags value should be initialized like this for a VSprite GEL:
   
  +
<syntaxhighlight>
<pre>
 
 
myVSprite.Flags = VSPRITE;
 
myVSprite.Flags = VSPRITE;
  +
</syntaxhighlight>
</pre>
 
   
 
=== VSprite Position ===
 
=== VSprite Position ===
Line 117: Line 117:
 
The ''x, y'' values may be set like this to put the VSprite in the upper-left:
 
The ''x, y'' values may be set like this to put the VSprite in the upper-left:
   
  +
<syntaxhighlight>
<pre>
 
 
myVSprite.Y = 0;
 
myVSprite.Y = 0;
 
myVSprite.X = 0;
 
myVSprite.X = 0;
  +
</syntaxhighlight>
</pre>
 
   
 
=== VSprite Image Size ===
 
=== VSprite Image Size ===
Line 128: Line 128:
 
VSprites always have a Depth of two, allowing for three colors. The values may be set like this:
 
VSprites always have a Depth of two, allowing for three colors. The values may be set like this:
   
  +
<syntaxhighlight>
<pre>
 
 
myVSprite.Width = 1; /* ALWAYS 1 for true VSprites. */
 
myVSprite.Width = 1; /* ALWAYS 1 for true VSprites. */
 
myVSprite.Height = 5; /* The example height. */
 
myVSprite.Height = 5; /* The example height. */
 
myVSprite.Depth = 2; /* ALWAYS 2 for true VSprites. */
 
myVSprite.Depth = 2; /* ALWAYS 2 for true VSprites. */
  +
</syntaxhighlight>
</pre>
 
   
 
=== VSprites and Collision Detection ===
 
=== VSprites and Collision Detection ===
Line 138: Line 138:
 
Some members of the VSprite data structure are used for special purposes such as collision detection, user extensions or for system extensions (such as Bobs and AnimComps). For most applications these fields are set to zero:
 
Some members of the VSprite data structure are used for special purposes such as collision detection, user extensions or for system extensions (such as Bobs and AnimComps). For most applications these fields are set to zero:
   
  +
<syntaxhighlight>
<pre>
 
 
myVSprite.HitMask = 0; /* These are all used for collision detection */
 
myVSprite.HitMask = 0; /* These are all used for collision detection */
 
myVSprite.MeMask = 0;
 
myVSprite.MeMask = 0;
Line 147: Line 147:
   
 
myVSprite.VSBob = NULL; /* Only Bobs and AnimComps need this */
 
myVSprite.VSBob = NULL; /* Only Bobs and AnimComps need this */
  +
</syntaxhighlight>
</pre>
 
   
 
The special uses of this fields are explained further in the sections that follow.
 
The special uses of this fields are explained further in the sections that follow.
Line 211: Line 211:
 
To continue our example, a set of colors can be declared and the VSprite colors set with the following statements:
 
To continue our example, a set of colors can be declared and the VSprite colors set with the following statements:
   
  +
<syntaxhighlight>
<pre>
 
WORD mySpriteColors[] = { 0x0000, 0x00f0, 0x0f00 }; /* Declare colors statically */
+
uint16 mySpriteColors[] = { 0x0000, 0x00f0, 0x0f00 }; /* Declare colors statically */
   
myVSprite.SprColors = mySpriteColors; /* Assign colors to VSprite */
+
myVSprite.SprColors = mySpriteColors; /* Assign colors to VSprite */
  +
</syntaxhighlight>
</pre>
 
   
 
=== Adding and Removing VSprites ===
 
=== Adding and Removing VSprites ===
Line 223: Line 223:
 
A typical calling sequence could be performed like so:
 
A typical calling sequence could be performed like so:
   
  +
<syntaxhighlight>
<pre>
 
 
struct VSprite myVSprite = {0};
 
struct VSprite myVSprite = {0};
 
struct RastPort myRastPort = {0};
 
struct RastPort myRastPort = {0};
   
AddVSprite(&amp;myVSprite, &amp;myRastPort);
+
IGraphics->AddVSprite(&myVSprite, &myRastPort);
   
 
/* Manipulate the VSprite as needed here */
 
/* Manipulate the VSprite as needed here */
   
RemVSprite(&amp;myVSprite);
+
IGraphics->RemVSprite(&myVSprite);
  +
</syntaxhighlight>
</pre>
 
   
 
The &amp;myVSprite argument is a fully initialized VSprite structure and &amp;myRastPort is the RastPort with which this VSprite is to be associated. Note that you will probably not like the results if you try to RemVSprite() a VSprite that has not been added to the system with AddVSprite(). See the SDK for additional information on these functions.
 
The &amp;myVSprite argument is a fully initialized VSprite structure and &amp;myRastPort is the RastPort with which this VSprite is to be associated. Note that you will probably not like the results if you try to RemVSprite() a VSprite that has not been added to the system with AddVSprite(). See the SDK for additional information on these functions.
Line 252: Line 252:
 
The function SortGList() must be used to get the GELs in the correct order before the system is asked to display them. ''This sorting step is essential!'' It should be done before calling DrawGList(), whenever a GEL has changed position. This function is called as follows:
 
The function SortGList() must be used to get the GELs in the correct order before the system is asked to display them. ''This sorting step is essential!'' It should be done before calling DrawGList(), whenever a GEL has changed position. This function is called as follows:
   
  +
<syntaxhighlight>
<pre>
 
 
struct RastPort myRastPort = {0};
 
struct RastPort myRastPort = {0};
   
SortGList(&amp;myRastPort);
+
IGraphics->SortGList(&myRastPort);
  +
</syntaxhighlight>
</pre>
 
   
 
The only argument is a pointer to the RastPort structure containing the GelsInfo.
 
The only argument is a pointer to the RastPort structure containing the GelsInfo.
Line 280: Line 280:
 
The system function called DrawGList() looks through the list of GELS and prepares the necessary Copper instructions and memory areas to display the data. This function is called as follows:
 
The system function called DrawGList() looks through the list of GELS and prepares the necessary Copper instructions and memory areas to display the data. This function is called as follows:
   
  +
<syntaxhighlight>
<pre>
 
 
struct RastPort myRastPort = {0};
 
struct RastPort myRastPort = {0};
 
struct ViewPort myViewPort = {0};
 
struct ViewPort myViewPort = {0};
   
DrawGList(&amp;myRastPort, &amp;myViewPort);
+
IGraphics->DrawGList(&myRastPort, &myViewPort);
  +
</syntaxhighlight>
</pre>
 
   
 
The myRastPort argument specifies the RastPort containing the GelsInfo list with the VSprites that you want to display. The &amp;myViewPort argument is a pointer to the ViewPort for which the VSprites will be created.
 
The myRastPort argument specifies the RastPort containing the GelsInfo list with the VSprites that you want to display. The &amp;myViewPort argument is a pointer to the ViewPort for which the VSprites will be created.
Line 293: Line 293:
 
Once DrawGList() has prepared the necessary instructions and memory areas to display the data, the VSprites are installed into the display with MrgCop(). (DrawGList() does not actually draw the VSprites, it only prepares the Copper instructions.)
 
Once DrawGList() has prepared the necessary instructions and memory areas to display the data, the VSprites are installed into the display with MrgCop(). (DrawGList() does not actually draw the VSprites, it only prepares the Copper instructions.)
   
  +
<syntaxhighlight>
<pre>
 
 
struct View *view;
 
struct View *view;
   
MrgCop(view);
+
IGraphics->MrgCop(view);
  +
</syntaxhighlight>
</pre>
 
   
 
The view is a pointer to the View structure whose Copper instructions are to be merged.
 
The view is a pointer to the View structure whose Copper instructions are to be merged.
Line 305: Line 305:
 
Now that the display instructions include the definition of the VSprites, the system can display this newly configured View with the LoadView() function:
 
Now that the display instructions include the definition of the VSprites, the system can display this newly configured View with the LoadView() function:
   
  +
<syntaxhighlight>
<pre>
 
 
struct View *view;
 
struct View *view;
   
LoadView(view);
+
IGraphics->LoadView(view);
  +
</syntaxhighlight>
</pre>
 
   
 
Again, view is a pointer to the View that contains the the new Copper instruction list (if you are using GELs in an Intuition Screen, do not call LoadView().)
 
Again, view is a pointer to the View that contains the the new Copper instruction list (if you are using GELs in an Intuition Screen, do not call LoadView().)
Line 321: Line 321:
 
WaitTOF() holds your task until the vertical-blanking interval (blank area at the top of the screen) has begun. At that time, the system has retrieved the current Copper instruction list and is ready to allow generation of a new list.
 
WaitTOF() holds your task until the vertical-blanking interval (blank area at the top of the screen) has begun. At that time, the system has retrieved the current Copper instruction list and is ready to allow generation of a new list.
   
  +
<syntaxhighlight>
<pre>
 
 
WaitTOF();
 
WaitTOF();
  +
</syntaxhighlight>
</pre>
 
   
 
WaitTOF() takes no arguments and returns no values. It simply suspends your task until the video beam is at the top of field.
 
WaitTOF() takes no arguments and returns no values. It simply suspends your task until the video beam is at the top of field.
Line 329: Line 329:
 
== Complete VSprite Example ==
 
== Complete VSprite Example ==
   
The listing given here shows a complete VSprite example. This program requires the "animtools.c", "animtools.h" and "animtools_proto.h" support files in order to compile and run. These files are listed at the end of this chapter.
+
The listing given here shows a complete VSprite example. This program requires the "animtools.c", "animtools.h" and "animtools_proto.h" support files in order to compile and run. These files are listed at the end of this article.
   
 
<pre>
 
<pre>
Line 525: Line 525:
 
If a hardware sprite is reserved, the system will not consider it when it makes VSprite assignments. Remember, hardware sprite pairs share color register sets. If a hardware sprite is reserved, its mate should probably be reserved too, otherwise the reserved sprite's colors will change as the unreserved mate is assigned different VSprites. For example, it is common practice to reserve Sprites 0 and 1, so that the Intuition pointer (Sprite 0) is left alone. This could be accomplished with the following statements:
 
If a hardware sprite is reserved, the system will not consider it when it makes VSprite assignments. Remember, hardware sprite pairs share color register sets. If a hardware sprite is reserved, its mate should probably be reserved too, otherwise the reserved sprite's colors will change as the unreserved mate is assigned different VSprites. For example, it is common practice to reserve Sprites 0 and 1, so that the Intuition pointer (Sprite 0) is left alone. This could be accomplished with the following statements:
   
  +
<syntaxhighlight>
<pre>
 
 
struct RastPort myRastPort = {0}; /* the View structure is defined */
 
struct RastPort myRastPort = {0}; /* the View structure is defined */
   
myRastPort.GelsInfo-&gt;sprRsrvd = 0x03; /* reserve 0 and 1 */
+
myRastPort.GelsInfo->sprRsrvd = 0x03; /* reserve 0 and 1 */
  +
</syntaxhighlight>
</pre>
 
   
 
The GfxBase structure may be examined to find which sprites are already in use. This may, at your option, impact what sprites you reserve. If Intuition is running, sprite 0 will already be in use as its pointer.
 
The GfxBase structure may be examined to find which sprites are already in use. This may, at your option, impact what sprites you reserve. If Intuition is running, sprite 0 will already be in use as its pointer.
Line 535: Line 535:
 
The reserved sprite status is accessible as
 
The reserved sprite status is accessible as
   
  +
<syntaxhighlight>
<pre>
 
currentreserved = GfxBase-&gt;SpriteReserved;
+
currentreserved = GfxBase->SpriteReserved;
  +
</syntaxhighlight>
</pre>
 
   
 
The next section presents a few trouble-shooting techniques for VSprite assignment.
 
The next section presents a few trouble-shooting techniques for VSprite assignment.

Latest revision as of 21:18, 9 November 2015

Using Virtual Sprites

This section describes how to set up the VSprite structure so that it represents a true VSprite. True VSprites are managed by the GELs system which converts them to Simple Sprites and displays them. (Later sections describe how a VSprite structure can be set up for Bobs and AnimComps.)

Before the system is told of a VSprite's existence, space for the VSprite data structure must be allocated and initialized to "correctly" represent a VSprite. Since the system does no validity checking on the VSprite structure, the result of using a bogus structure is usually a fireworks display, followed by a system failure.

The system software provides a way to detect collisions between VSprites and other on-screen objects. There is also a method of extending the VSprite structure to incorporate user defined variables. These subjects are applicable to all GELs and are explained in Collisions and GEL Structure Extensions.

Specification of VSprite Structure

The VSprite structure is defined in the include file <graphics/gels.h> as follows:

/* VSprite structure definition */
struct VSprite {
    struct VSprite *NextVSprite;
    struct VSprite *PrevVSprite;
    struct VSprite *DrawPath;
    struct VSprite *ClearPath;
    WORD            OldY, OldX;
    WORD            Flags;
    WORD            Y, X;
    WORD            Height;
    WORD            Width;
    WORD            Depth;
    WORD            MeMask;
    WORD            HitMask;
    WORD           *ImageData;
    WORD           *BorderLine;
    WORD           *CollMask;
    WORD           *SprColors;
    struct Bob     *VSBob;
    BYTE            PlanePick;
    BYTE            PlaneOnOff;
    VUserStuff      VUserExt;
    };

There are two primary ways to allocate and fill in space for VSprite data. They can be statically declared, or a memory allocation function can be called and they can be filled in programmatically. The declaration to statically set up a VSprite structure is listed below.

/* VSprite static data definition.
** must set the following for TRUE VSprites:
**    VSPRITE flag.
**    Width to 1.
**    Depth to 2.
**    VSBob to NULL.
*/
struct VSprite myVSprite =
    {
    NULL, NULL, NULL, NULL, 0, 0, VSPRITE, 0, 0, 5, 1, 2, 0, 0,
    &myImage, 0, 0, &mySpriteColors, NULL, 0x3, 0, 0
    };

This static allocation gives the required VSprite structure, but does not allocate or set up collision masks for the VSprite. Note that the VSprite structure itself does not need to reside in Chip memory.

Refer to the makeVSprite() and freeVSprite() functions in the animtools.c listing at the end of the article for an example of dynamically allocating, initializing and freeing a VSprite structure.

Reserved VSprite Members

These VSprite structure members are reserved for system use (do not write to them):

NextVSprite and PrevVSprite
These are used as links in the GelsInfo list.
DrawPath and ClearPath
These are used for Bobs, not true VSprites.
OldY and OldX
Previous position holder, the system uses these for double buffered Bobs, but application programs can read them too.

The values can be set like this:

myVSprite.NextVSprite = NULL;
myVSprite.PrevVSprite = NULL;
myVSprite.DrawPath  = NULL;
myVSprite.ClearPath = NULL;
myVSprite.OldY = 0;
myVSprite.OldX = 0;

Using VSprite Flags

The Flags member of the VSprite structure is both read and written by the system. Some bits are used by the application to inform the system; others are used by the system to indicate things to the application.

The only Flags bits that are used by true VSprites are:

VSPRITE
This may be set to indicate to the system that it should treat the structure as a true VSprite, not part of a Bob. This affects the interpretation of the data layout and the use of various system variables.
VSOVERFLOW
The system sets this bit in the true VSprites that it is unable to display. This happens when there are too many in the same scan line, and the system has run out of Simple Sprites to assign. It indicates that this VSprite has not been displayed. If no sprites are reserved, this means that more than eight sprites touch one scan line. This bit will not be set for Bobs and should not be changed by the application.
GELGONE
If the system has set GELGONE bit in the Flags member, then the GEL associated with this VSprite is not on the display at all, it is entirely outside the GEL boundaries. This area is defined by the GelsInfo members topmost, bottommost, leftmost and rightmost (see the <graphics/rastport.h> include file).

On the basis of that information, the application may decide that the object need no longer be part of the GEL list and may decide to remove it to speed up the consideration of other objects. Use RemVSprite() (or RemBob(), if it's a Bob) to do this. This bit should not be changed by the application.

The VSprite.Flags value should be initialized like this for a VSprite GEL:

myVSprite.Flags = VSPRITE;

VSprite Position

To control the position of a VSprite, the x and y variables in the VSprite structure are used. These specify where the upper left corner of the VSprite will be, relative to the upper left corner of the playfield area it appears over. So if VSprites are used under Intuition and within a screen, they will be positioned relative to the upper left-hand corner of the screen.

In a 320x200 screen, a y value of 0 puts the VSprite at the top of that display, a y value of (200 - VSprite height) puts the VSprite at the bottom. And an x value of 0 puts the VSprite at the left edge of that display, while an x value of (320 - VSprite width) puts the VSprite at the far right. Values of less than (0,0) or greater than (320, 200) may be used to move the VSprite partially or entirely off the screen, if desired.

See Graphics Primitives for more information on display coordinates and display size. See the "Amiga Hardware Reference Manual" for more information on hardware sprites.

Position VSprites Properly
It is important that the starting position of true VSprites is not less than -20 in the y direction, which is the start of the active display area for sprites. Also, if they are moved too far to the left, true VSprites may not have enough DMA time to be displayed.

The x, y values may be set like this to put the VSprite in the upper-left:

myVSprite.Y = 0;
myVSprite.X = 0;

VSprite Image Size

A true VSprite is always one word (16 pixels) wide and may be any number of lines high. It can be made to appear thinner by making some pixels transparent. Like Simple Sprites, VSprite pixels are always the size of a pixel in low-resolution mode (320x200); regardless of the resolution the display is set to. To specify how many lines make up the VSprite image, the VSprite structure member, Height, is used.

VSprites always have a Depth of two, allowing for three colors. The values may be set like this:

myVSprite.Width  = 1;      /* ALWAYS 1 for true VSprites. */
myVSprite.Height = 5;      /* The example height. */
myVSprite.Depth  = 2;      /* ALWAYS 2 for true VSprites. */

VSprites and Collision Detection

Some members of the VSprite data structure are used for special purposes such as collision detection, user extensions or for system extensions (such as Bobs and AnimComps). For most applications these fields are set to zero:

myVSprite.HitMask    = 0;  /* These are all used for collision detection */
myVSprite.MeMask     = 0;
myVSprite.BorderLine = 0;
myVSprite.CollMask   = 0;
 
myVSprite.VUserExt = 0;    /* Only use this for user extensions to VSprite */
 
myVSprite.VSBob = NULL;    /* Only Bobs and AnimComps need this */

The special uses of this fields are explained further in the sections that follow.

VSprite Image Data

The ImageData pointer of the VSprite structure must be initialized with the address of the first word of the image data array. The image data array must be in Chip memory. It takes two sequential 16-bit words to define each line of a VSprite. This means that the data area containing the VSprite image is always "Heightx2" (10 in the example case) words long.

A VSprite image is defined just like a real hardware sprite. The combination of bits in corresponding locations in the two data words that define each line select the color for that pixel. The first of the pair of words supplies the low-order bit of the color selector for that pixel; the second word supplies the high-order bit.

These binary values select colors as follows:

00 selects "transparent"
01 selects the first of three VSprite colors
10 selects the second VSprite color
11 selects the third VSprite color

In those areas where the combination of bits yields a value of 0, the VSprite is transparent. This means that the playfield, and all Bobs and AnimComps, and any VSprite whose priority is lower than this VSprite will all show through in transparent sections. For example:

(&VSprite->ImageData)      1010 0000 0000 0000
(&VSprite->ImageData + 1)  0110 0000 0000 0000

Reading from top to bottom, left to right, the combinations of these two sequential data words form the binary values of 01, 10, 11, and then all 00s. This VSprite's first pixel will be color 1, the next color 2, the third color 3. The rest will be transparent, making this VSprite appear to be three pixels wide. Thus, a three-color image, with some transparent areas, can be formed from a data set like the following sample:

Address    Binary Data            VSprite Image Data
-------    -----------            ------------------
mem        1111 1111 1111 1111    Defines top line
mem + 1    1111 1111 1111 1111    3333 3333 3333 3333

mem + 2    0011 1100 0011 1100    Defines second line
mem + 3    0011 0000 0000 1100    0033 1100 0011 3300

mem + 4    0000 1100 0011 0000    Defines third line
mem + 5    0000 1111 1111 0000    0000 3322 2233 0000

mem + 6    0000 0010 0100 0000    Defines fourth line
mem + 7    0000 0011 1100 0000    0000 0032 2300 0000

mem + 8    0000 0001 1000 0000    Defines fifth line
mem + 9    0000 0001 1000 0000    0000 0003 3000 0000

The VSprite.Height for this sample image is 5.

Specifying the Colors of a VSprite

The system software provides a great deal of versatility in the choice of colors for Virtual Sprites. Each VSprite has "its own set" of three colors, pointed to by SprColors, which the system jams into the display's Copper list as needed.

SprColors points to the first of three 16-bit values. The first value represents the color used for the VSprite bits that select color 1, the second value is color 2, and the third value is color 3. When the system assigns a hardware sprite to carry the VSprite's image, it jams these color values into the Copper list (the intermediate Copper list, "not" the color table), so that the View's colors will be correct for this VSprite at the time the VSprite is displayed. It doesn't jam the original palette's colors back after the VSprite is done. If there is another VSprite later, that VSprite's colors will get jammed; if there is not another VSprite, the colors will remain the same until the next ViewPort's colors get loaded.

If the SprColors pointer is set to NULL, that VSprite does not generate a color-change instruction stream for the Copper. Instead, the VSprite appears drawn in whatever color set that the hardware sprite happens to have in it already.

Since the registers are initially loaded with the colors from the ViewPort's ColorMap, if all VSprites have NULL SprColors, they will appear in the ViewPort's colors.

To continue our example, a set of colors can be declared and the VSprite colors set with the following statements:

uint16 mySpriteColors[] = { 0x0000, 0x00f0, 0x0f00 }; /* Declare colors statically */
 
myVSprite.SprColors = mySpriteColors;                 /* Assign colors to VSprite */

Adding and Removing VSprites

Once a true VSprite has been set up and initialized, the obvious next step is to give it to the system by adding it to the GEL list. The VSprite may then be manipulated as needed. Before the program ends, the VSprite should be removed from the GELs list by calling RemVSprite().

A typical calling sequence could be performed like so:

struct VSprite  myVSprite = {0};
struct RastPort myRastPort = {0};
 
IGraphics->AddVSprite(&myVSprite, &myRastPort);
 
/* Manipulate the VSprite as needed here */
 
IGraphics->RemVSprite(&myVSprite);

The &myVSprite argument is a fully initialized VSprite structure and &myRastPort is the RastPort with which this VSprite is to be associated. Note that you will probably not like the results if you try to RemVSprite() a VSprite that has not been added to the system with AddVSprite(). See the SDK for additional information on these functions.

Changing VSprites

Once the VSprite has been added to the GELs list and is in the display, some of its characteristics can be changed dynamically by:

  • Changing y, x to a new VSprite position
  • Changing ImageData to point to a new VSprite image
  • Changing SprColors to point to a new VSprite color set

Study the next two sections to find out how to reserve hardware Sprites for use outside the VSprite system and how to assign the VSprites.

Getting the VSprite List in Order

When the system has displayed the last line of a VSprite, it is able to reassign the hardware sprite to another VSprite located at a lower position on the screen. The system allocates hardware sprites in the order in which it encounters the VSprites in the list. Therefore, the list of VSprites must be sorted before the system can assign the use of the hardware Sprites correctly.

The function SortGList() must be used to get the GELs in the correct order before the system is asked to display them. This sorting step is essential! It should be done before calling DrawGList(), whenever a GEL has changed position. This function is called as follows:

struct RastPort myRastPort = {0};
 
IGraphics->SortGList(&myRastPort);

The only argument is a pointer to the RastPort structure containing the GelsInfo.

Displaying the VSprites

The next few sections explain how to display the VSprites. The following system functions are used:

DrawGList()
Draws the VSprites into the current RastPort.
MrgCop()
Installs the VSprites into the display.
LoadView()
Asks the system to display the new View.
WaitTOF()
Synchronizes the functions with the display.

Drawing the Graphics Elements

The system function called DrawGList() looks through the list of GELS and prepares the necessary Copper instructions and memory areas to display the data. This function is called as follows:

struct RastPort myRastPort = {0};
struct ViewPort myViewPort = {0};
 
IGraphics->DrawGList(&myRastPort, &myViewPort);

The myRastPort argument specifies the RastPort containing the GelsInfo list with the VSprites that you want to display. The &myViewPort argument is a pointer to the ViewPort for which the VSprites will be created.

Merging VSprite Instructions

Once DrawGList() has prepared the necessary instructions and memory areas to display the data, the VSprites are installed into the display with MrgCop(). (DrawGList() does not actually draw the VSprites, it only prepares the Copper instructions.)

struct View *view;
 
IGraphics->MrgCop(view);

The view is a pointer to the View structure whose Copper instructions are to be merged.

Loading the New View

Now that the display instructions include the definition of the VSprites, the system can display this newly configured View with the LoadView() function:

struct View *view;
 
IGraphics->LoadView(view);

Again, view is a pointer to the View that contains the the new Copper instruction list (if you are using GELs in an Intuition Screen, do not call LoadView().)

The Copper instruction lists are double-buffered, so this instruction does not actually take effect until the next display field occurs. This avoids the possibility of some function trying to update the Copper instruction list while the Copper is trying to use it to create the display.

Synchronizing with the Display

To synchronize application functions with the display, call the system function WaitTOF().

WaitTOF() holds your task until the vertical-blanking interval (blank area at the top of the screen) has begun. At that time, the system has retrieved the current Copper instruction list and is ready to allow generation of a new list.

WaitTOF();

WaitTOF() takes no arguments and returns no values. It simply suspends your task until the video beam is at the top of field.

Complete VSprite Example

The listing given here shows a complete VSprite example. This program requires the "animtools.c", "animtools.h" and "animtools_proto.h" support files in order to compile and run. These files are listed at the end of this article.

/* vsprite.c
**
** SAS/C V5.10a
** lc -b1 -cfist -v -y vsprite.c
** blink FROM LIB:c.o vsprite.o animtools.o LIB LIB:lc.lib LIB:amiga.lib TO vsprite
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/gels.h>
#include <graphics/collide.h>
#include <libraries/dos.h>
#include <stdlib.h>
#include "animtools.h"

VOID borderCheck(struct VSprite *hitVSprite, LONG borderflags);
VOID process_window(struct Window *win, struct RastPort *myRPort, struct VSprite *MyVSprite);
VOID do_VSprite(struct Window *win, struct RastPort *myRPort);
VOID vspriteDrawGList(struct Window *win, struct RastPort *myRPort);

struct GfxBase           *GfxBase;   /* pointer to Graphics library  */
struct IntuitionBase *IntuitionBase; /* pointer to Intuition library */

int return_code;
#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 };

NEWVSPRITE myNewVSprite = {              /* information for the new VSprite       */
        /* 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
        };

struct NewWindow myNewWindow = {        /* information for the new window */
    80, 20, 400, 150, -1, -1, CLOSEWINDOW | INTUITICKS,
    ACTIVATE | WINDOWCLOSE | WINDOWDEPTH | RMBTRAP | WINDOWDRAG,
    NULL, NULL, "VSprite", NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
    };

/* Basic VSprite display subroutine */
VOID vspriteDrawGList(struct Window *win, struct RastPort *myRPort)
{
SortGList(myRPort);
DrawGList(myRPort, ViewPortAddress(win));
RethinkDisplay();
}

/* Collision routine for vsprite hitting border.  Note that when the collision is VSprite to */
/* VSprite (or Bob to Bob, Bob to AnimOb, etc), then the parameters are both pointers to a VSprite. */
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;
    }
}

/* Process window and dynamically change vsprite. Get messages. Go away on           */
/* CLOSEWINDOW.  Update and redisplay vsprite on INTUITICKS. Wait for more messages. */
VOID process_window(struct Window *win, struct RastPort *myRPort, struct VSprite *myVSprite)
{
struct IntuiMessage *msg;

FOREVER
    {
    Wait(1L << win->UserPort->mp_SigBit);
    while (NULL != (msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
        {
        /* Only CLOSEWINDOW and INTUITICKS are active */
        if (msg->Class == 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);
    DoCollision(myRPort);
    vspriteDrawGList(win, myRPort);
    }
}

/* Working with the VSprite.  Setup the GEL system and get a new VSprite (makeVSprite()).   */
/* Add VSprite to the system and display.  Use the vsprite.  When done, remove VSprite and  */
/* update the display without the VSprite.  Cleanup everything.                             */
VOID do_VSprite(struct Window *win, struct RastPort *myRPort)
{
struct VSprite       *myVSprite;
struct GelsInfo       *my_ginfo;

if (NULL == (my_ginfo = setupGelSys(myRPort, 0xfc)))
    return_code = RETURN_WARN;
else
    {
    if (NULL == (myVSprite = makeVSprite(&myNewVSprite)))
        return_code = RETURN_WARN;
    else
        {
        AddVSprite(myVSprite, myRPort);
        vspriteDrawGList(win, myRPort);
        myVSprite->VUserExt = 20;
        SetCollision(BORDERHIT, borderCheck, myRPort->GelsInfo);
        process_window(win, myRPort, myVSprite);
        RemVSprite(myVSprite);
        freeVSprite(myVSprite);
        }
    vspriteDrawGList(win, myRPort);
    cleanupGelSys(my_ginfo, myRPort);
    }
}

/* Example VSprite program.  First open up the libraries and a window. */
VOID main(int argc, char **argv)
{
struct Window       *win;
struct RastPort    myRPort = {0};

return_code = RETURN_OK;

if (NULL == (GfxBase = (struct GfxBase *)OpenLibrary(GRAPHICSNAME,37L)))
    return_code = RETURN_FAIL;
else
    {
    if (NULL == (IntuitionBase = (struct IntuitionBase *)OpenLibrary(INTUITIONNAME,37L)))
        return_code = RETURN_FAIL;
    else
        {
        if (NULL == (win = OpenWindow(&myNewWindow)))
            return_code = RETURN_WARN;
        else
            {
            InitRastPort(&myRPort);
            myRPort = win->WScreen->RastPort;       /* Copy the structure. */
            do_VSprite(win, &myRPort);
            CloseWindow(win);
            }
        CloseLibrary((struct Library *)IntuitionBase);
        }
    CloseLibrary((struct Library *)GfxBase);
    }
exit(return_code);
}

VSprite Advanced Topics

This section describes advanced topics pertaining to VSprites. It contains details about reserving hardware sprites for use outside of the GELs VSprite system, information about how VSprites are assigned, and more information about VSprite colors.

Reserving Hardware Sprites

To prevent the VSprite system from using specific hardware sprites, set the sprRsrvd member of the GelsInfo structure. The pointer to the GelsInfo structure is contained in the RastPort structure. If all of the bits of this 8-bit value are ones (0xFF), then all of the hardware sprites may be used by the VSprite system. If any of the bits is a 0, the sprite corresponding to that bit will not be utilized by VSprites.

Reserving Can Cause Problems
Reserving sprites increases the likelihood of the system not being able to display a VSprite (VSOVERFLOW). See the next section, "How VSprites are Assigned", for further details on this topic.

You reserve a sprite by setting its corresponding bit in sprRsrvd. For instance, to reserve sprite zero only, set sprRsrvd to 0x01. To reserve sprite three only, set sprRsrvd to 0x08.

If a hardware sprite is reserved, the system will not consider it when it makes VSprite assignments. Remember, hardware sprite pairs share color register sets. If a hardware sprite is reserved, its mate should probably be reserved too, otherwise the reserved sprite's colors will change as the unreserved mate is assigned different VSprites. For example, it is common practice to reserve Sprites 0 and 1, so that the Intuition pointer (Sprite 0) is left alone. This could be accomplished with the following statements:

struct RastPort myRastPort = {0};   /* the View structure is defined */
 
myRastPort.GelsInfo->sprRsrvd = 0x03;   /* reserve 0 and 1 */

The GfxBase structure may be examined to find which sprites are already in use. This may, at your option, impact what sprites you reserve. If Intuition is running, sprite 0 will already be in use as its pointer.

The reserved sprite status is accessible as

currentreserved = GfxBase->SpriteReserved;

The next section presents a few trouble-shooting techniques for VSprite assignment.

How VSprites Are Assigned

Although VSprites are managed for you by the GELs system there are some underlying limitations which could cause the system to run out of VSprites.

As the system goes through the GEL list during DrawGList(), whenever it finds a true VSprite, it goes through the following procedure. If there is a Simple Sprite available (after the reserved sprites and preceding VSprites are accounted for), Copper instructions are added that will load the sprite hardware with this VSprite's data at the right point on the screen. It may need to add a Copper instruction sequence to load the display's colors associated with the sprite as well.

There are only 8 real sprite DMA channels. The system will run out of hardware sprites if it is asked to display more than eight VSprites on one scan line. This limit goes down to four when the VSprites have different SprColor pointers. During the time that there is a conflict, the VSprites that could not be put into Simple Sprites will disappear. They will reappear when (as the VSprites are moved about the screen) circumstances permit.

These problems can be alleviated by taking some precautions:

  • Minimize the number of VSprites to appear on a single horizontal line.
  • If colors for some Virtual Sprites are the same, make sure that the pointer for each of the VSprite structures for these Virtual Sprites points to the same memory location, rather than to a duplicate set of colors elsewhere in memory. The system will know to map these into Sprite pairs.

If a VSprite's SprColors are set to NULL, the VSprite will appear in the ViewPort's ColorMap colors. The system will display the VSprite in any one of a set of four different possible color groupings as indicated in the Simple Sprite section above.

If SprColors points to a color set, the system will jam SprColors into the display hardware (via the Copper list), effectively overriding those ColorMap registers. The values in the ColorMap are not overwritten, but anything in the background display that used to appear in the ColorMap colors will appear in SprColors colors.

How VSprite and Playfield Colors Interact

At the start of each display, the system loads the colors from the ViewPort's color table into the display's hardware registers, so whatever is rendered into the BitMap is displayed correctly. But if the VSprite system is used, and the colors are specified (via SprColors) for each VSprite, the SprColors will be loaded by the system into the display hardware, as needed.

The system does this by generating Copper instructions that will jam the colors into the hardware at specific moments in the display cycle. Any BitMap rendering, including Bobs, which share colors with VSprites, may change colors constantly as the video display beam progresses down the screen.

This color changing can be avoided by taking one of the following precautions:

  • Use a four bitplane playfield, which only allows the lower 16 colors to be rendered into the BitMap (and allows Hires display mode).
  • If a 32-color playfield display is being used, avoid rendering in colors 17-19, 21-23, 25-27, and 29-32, which are the colors affected by the VSprite system.
  • Specify the VSprite SprColors pointer as a value of NULL to avoid changing the contents of any of the hardware sprite color registers. This may cause the VSprites to change colors depending on their positions relative to each other, as described in the previous section.