Programming tutorial: Part 8–Changing appearances

Atari Lynx programming tutorial series:

Adding a little color to your sprites

The previous tutorial on sprites we looked at how to get our first sprite on the screen. Let’s dig a little deeper and see how we can change the way a sprite looks.

Besides the robot sprite, we are going to use a 4×4 pixel sprite, that uses all of the 16 pens that there are. The sprite pixel data looks like this:

char allcolors_data[17] =  
{
  0x04, 0x98, 0x09, 0x18, 
// 1 0011 0000 0001 0010 0011 000 0x04, 0x9a, 0x2b, 0x38,  // 1 0011 0100 0101 0110 0111 000 0x04, 0x9c, 0x4d, 0x58,  // 1 0011 1000 1001 1010 1011 000 0x04, 0x9e, 0x6f, 0x78,  // 1 0011 1100 1101 1110 1111 000 0x00 };

Later you will understand what this is all about. The data could also have been created by using the sprpck.exe tool on a bitmap file with the correct pixels and colors.

The pixel data describes a sprite that has the following colors/pen indexes for each of the pixels

image

For comparison the robot looks like this:

image

The control over palettes and color indexes needs an enhanced version of the SCB structure as well. Again, this struct is defined in _suzy.h, which is included automatically as part of lynx.h.

typedef struct SCB_REHV_PAL {
  unsigned char sprctl0;
  unsigned char sprctl1;
  unsigned char sprcoll;
  char *next;
  unsigned char *data;
  signed int hpos, vpos;
unsigned int hsize, vsize;
unsigned char palette[8]; } SCB_REHV_PAL;

Notice how there are two additional integer values for the size in x and y direction (hsize and vsize) plus a byte array of 8 for the pen indexes of 0 to 15. It is common to use the combination of size and palette in a single struct, although it doesn’t have to be that way. We need the sizes later on anyway.

An instance for this sprite structure could be like this:

SCB_REHV_PAL allcolors =   
{   
BPP_4 | TYPE_NORMAL,
REHV,
  0x01,
0,
  &allcolors_data,
  10, 20, 0x0100, 0x0100, 
{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } 
};

This time we are outputting to the location at (10,20) and we specify a set of pens in the array.  You need to specify that you are using the horizontal and vertical sizing with the REHV flag in sprite control byte 1. You will always use horizontal and vertical together. Also, using the palette you should NOT specify the REUSEPAL flag that we did use earlier on.

Do you see how the palette array

{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }

when you split them up in two nibbles per byte, uses the exact same numbers as in the 4×4 pixel data? The numbers in the pixel data are NOT the numbers in the palette! They have the same values (this time), but it is important to remember that these numbers are not necessarily the same. The pixel data has values for whatever pen index you used. The palette holds other number that tell which color that represents.

image

The picture above is from drawing the 4×4 pixel sprite twice. Once at its normal size and once with a 5 times magnification (which you will learn in a moment).

Pen palette and indexes

The array for the pen indexes in the sprite structure holds the nibbles (4 bits) for each of the pen indexes. The way this works is that a sprite pixel uses a pen with a particular index, say 0x02. The 8-byte color palette holds the 16 4-bit values that refer to the color palette that we discussed in the 5th part of the tutorials. Again, you see a form of indirection here: by changing the palette inside the SCB you can point pen indexes to different colors.

You can think of this as paint by numbers. The robot for example uses colors 0, 1 and 2. You get to choose what each of the numbers mean by connecting them to the colors in the Lynx palette. A simple setup would mean that pen 0 goes to color 0, 1 to 1 and so on till 0x0F to 0x0F. This way the pen indexes correspond exactly with the colors in the palette. Check this in the picture.

image

The palette has the colors that we discussed earlier. Using the 1:1 connection it is easy to see what colors the robot will have. Changing the SCB palette references to swap pen index 1 and 2 to connect to colors 3 and 4 will give the robot other colors. There was no need to create new pixel data nor set a new global palette, just a small change in the SCB’s palette.

image

The palette reference that is set by the last used SCB is remembered. The SCB also determines whether a palette is present and should be reloaded. This means that you do not have to use and load the palette references the sprite SCB if these are the same for sprites that are rendered one after the other. Only when you change the palette is it necessary to reload it.

The reloading of the palette is controlled by sprite control byte 1.  The flag REUSEPAL specifies that the palette should not be reloaded whether it is present or not. This means that two SCBs like this:

SCB_RENONE robot_no_palette = 
{
BPP_4 | TYPE_NORMAL,
REUSEPAL,
0x01,
0,
&robot,
10, 20 };

and

SCB_REHV_PAL robot_with_palette = 
{
BPP_4 | TYPE_NORMAL,
REHV,
0x01,
0,
&robot,
10, 20, 0x0100, 0x0100,
{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }
};

can only be safely drawn in the order

tgi_sprite(&robot_with_palette);
tgi_sprite(&robot_no_palette);

The palette will be set by the first SCB and the second SCB uses the same palette. Drawing the other way around will give unpredictable/unstable results, because the robot_no_palette SCB will use whatever palette was used in the previous loaded SCB that had one.

One size does not fit all

So far we have drawn sprites that have a size of 1:1, meaning that every pixel in the sprite pixel data is drawn by a single pixel on screen. Suzy is capable of sizing your sprites to 256 times as big or draw it smaller to 1/256 of the size. The SCB controls the size in X and Y direction separately with the hsize and vsize that follows the hpos and vpos position values. Like the position these values are 16-bit integers. The value 0x0100 indicates a 1:1 correspondence which is the default. Using 0x0200 for hsize will scale to 2 times its size in the x direction, meaning 2 pixels horizontally for every 1 pixel in the data. The same goes for sizey, but in vertical direction.

image

0xFF00 will scale to 256 times the size and 0x0001 to 1/256th its size. Both extremes of 256 and 1/256 might not be practical or realistic, but do allow for some interesting scaling effects.

To draw the 4×4 matrix of 16 colors at five times its size, all we had to do was change the sizex and sizey of the SCB to 0x0500.

SCB_REHV_PAL allcolors =  {
  // ...
  &allcolors_data,
  10, 20,  0x0500, 0x0500,
  { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }
};

or in code like this:

allcolors.hpos = 20;
allcolors.hsize = 0x500;
allcolors.vsize = 0x500;
tgi_sprite(&allcolors);
image

Now for some weird stuff. Drawing the robot at three horizontal sizes 0x0100, 0x0200 and 0x0180 will render the sprite at its original size, twice its size and one-and-a-half times its size. The result will draw these robots:

image

See how the 1 and 2 times magnification work out great. It’s the 1,5 times scaling that looks odd. The reason is that to make the robot 1,5 times as large the result must be not 8 but 12 pixels wide. This can be accomplished by scaling every other pixel to 2 screen pixels. If you look closely you can see that the first column of pixels from the left is at 1x size, the next at 2x, then 1x and so on. That is the best Suzy can do for you, as half pixels do not exist. The moral of the story: be careful with non-integer scaling values!

Some of the zoom-in/zoom-out effects that you see in games use a different size in one or both directions that is changed for each time the sprite is drawn (every so many frames).

Stretching till it breaks

There are two other appearance changes that Suzy can do for you. That’s tilting and stretching your sprite. For that we will need to enhance the SCB structure once again.

typedef struct SCB_REHVST_PAL {
  unsigned char sprctl0;
  unsigned char sprctl1;
  unsigned char sprcoll;
  char *next;
  unsigned char *data;
  signed int hpos, vpos;
  unsigned int hsize, vsize;
  unsigned int stretch;
  unsigned int tilt;
  unsigned char palette[8];
} SCB_REHVST_PAL;

We added two integer values just before the palette array. These values control the stretch and tilt factor. By default these are both 0 given no stretch or tilt.

To make use of the stretching and tilting you need to add a different flag to sprite control byte 1: REHVST. This flag tells Suzy to read the two extra integers for stretch and tilt before the palette.

Drawing the 4×4 pixel sprite again with a stretch of 0x0100 gives the following result:

image

To truly understand the behavior of stretch I also added the sprite at a 5x magnification. The stretching of a pixel will make the size of every pixel increase by the factor you specify in the stretch integer of the SCB per line, 0x0100 in our example, which means 1: 1 pixel per line. It is somewhat hard to see in the small square, but you can see it increase by four pixels (1 for each of the pixels) on every row.

The 5 times magnified square shows the same thing, but also gives a hint to how sprites are drawn. This is done line by line, not necessarily pixel by pixel (as in data pixels). Even though every pixel from the data is now 5 screen pixels high, the increase by the factor of 0x0100 occurs every line that is put on the screen. This will make the stretching pretty smooth. Should the increase occur for every pixel in the data, the stretching would look very coarse and not pretty at all.

You can also use a stretch factor of 0x0040. This will make a pixel increase size by 1/4th every line, adding up to an actual pixel every four lines. This is somewhat similar to the scaling with a non-integer value. Fractions of pixels are not drawn until another new full pixel fraction has been reached.

The maximum stretch size 0x7F00 is 128 units per scan line, the smallest 0x0001 is 1/256th of a pixel. If you use a stretch size of 0x8000 or bigger it will wrap to negative and cause shrinking instead of stretching!

Stretching occurs in the horizontal direction by default. You can use a flag in the sprite system byte (SPRSYS = 0xFC92) that states whether vertical stretching happens as well. It is the flag in bit 4 (starting from the right at 0) of the byte and is called VStretch. There is a constant VSTRETCH for this in lynx.h, so you can do either a POKE to the address with the bit set or use the SUZY define for the struct mapping to the SUZY address space.

POKE(0xFC92, VSTRETCH);
// Or
SUZY.sprsys = VSTRETCH;

We will leave it at this for this part.

Next time

The next tutorial we will continue exploring the way sprites can change appearance and look into tilting, flipping and reference points. Lots of cool stuff to follow, so stay tuned.

Advertisements
This entry was posted in Tutorial. Bookmark the permalink.

2 Responses to Programming tutorial: Part 8–Changing appearances

  1. Pingback: Programming tutorial: Part 9–Advanced sprites | Diary of an Atari Lynx developer

  2. Pingback: Programming tutorial: Part 10–Collisions | Diary of an Atari Lynx developer

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s