Programming tutorial: Part 11–Pens and more collisions

Atari Lynx programming tutorial series:

The 11th part in the tutorial series on Atari Lynx programming will go in more details on the collision detection mechanics, so you get a full understanding of how that works. It also covers the use of pens for drawing sprites, because that has impact on how collisions occur.

How collision detection really works

You have now seen the half of the story of collision detection. The consumption side of the collision detection is fairly easy. To make it so, the hardware does all the heavy lifting. Did you wonder how it does that? Here’s how.

The drawing of sprites happens in two buffers (assuming you have enabled double-buffering, which is the default). Memory is reserved for that at the top edge of the 64K just under the memory mapped register. This consumes two times 160 * 102 / 2 = 8160 is 16320 (0x3FC0) bytes. When you use collision detection a third buffer of again 160 * 102 /2 bytes is used: the collision buffer.

image_thumb[32]

This collision buffer is similar to the two video buffers, but its sole function is to remember where collideable pixels of sprites have been drawn. By inspecting this buffer the hardware can see whether a pixel of a sprite is drawn on top of a pixel of another sprite.

I sort of visualize the collision buffer as a plane underneath the video buffer (which is an indirect representation of the LCD cells). Forget about the double buffers. That is an optimization technique to not have weird drawing effects. Only the drawing buffer is relevant for this part.

image_thumb[28]

Whenever you draw a sprite in the drawing buffer (the inactive video buffer) the sprite “shines through” and drops its collision number in the same location of the collision buffer, provided that number is higher than what might already be there.

Some examples to clarify

We’ll work with three sprites that have collision numbers 2, 4 and 3 respectively. We will draw them on top of each other from orange to blue to green.

image_thumb[35]                                image_thumb[37]

Imagine the draw buffer and collision buffer super-imposed (or merged) on top of each other. The colors indicate what goes into the draw buffer, the numbers represent the values in the collision buffer. We’ll draw the orange sprite first, then the second sprite and see what happens.

image

Notice how the draw/collision buffer shows the 4 pixels of the orange sprite in the picture on the left. The collision buffer gets four values of 2 because the collision number of the sprite is 2. The drawing buffer will get whatever colors are defined in the sprite data. To keep things simple the four pixels are all orange, but it can be any color from your palette.

Next, the blue sprite is drawn. Because it is drawn later the blue pixels are painted on top of everything else in the draw buffer. It overwrites the one orange pixel where the sprites overlap. In the collision buffer the same thing happens. The collision number is 4 and the buffer gets all 4s. But, … when the one pixel that overlaps the 2 is registered, the collision depository of the blue sprite is set to this value.

Finally, the third sprite is drawn.

image

The draw buffer does what you would expect. It overwrites all other pixels. The important thing to notice is that not all cells in the collision buffer get set to 3! The reason is that some cells already contain a higher value. So, in the collision buffer it is not “last one wins” like in the drawing buffer, but “highest number wins”. The green sprite gets a value of 4 in its depository reflecting the fact that pixels were put on top of other ones where a 4-valued sprite was drawn previously.

Pens and palettes revisited

Previously we covered colors and palettes redirects inside the sprite’s SCB. You saw how the global palette is set using addresses GREEN0 to BLUEREDF ($FDA0 to $FDBF). When we worked with sprites we kind of gleaned over the existence of pens in a sprite. We only discussed how the colors in the global palette and the numbers for pixels inside a sprite’s SCB data go together. Now the whole truth and nothing but the truth.

Let’s assume you are using the default palette of the Lynx. This is currently at these values:

image

The colors 0 to F are actually used by pens 0 to F going from left to right. To be precise (and this is slightly different from what I made you believe previously): you are connecting numbers in sprite pixel data to pens, not directly to the colors. Whenever you draw something with pen 0 it will be black – with this palette. Pen 1 will be red and so on. The colors are determined by the palette and nothing else. The colors are used by the corresponding pens.

So, in fact there are two ways to change the colors used by a sprite:

  1. Change the colors in the global palette.
    This will affect everything that is drawn and is therefore pretty unlikely to be useful. Some advanced scenarios, such as more than 16 colors and smart timer handling, might use this strategy.
  2. Use the redirect palette in the sprite’s SCB
    Most of the times it is the 1-to-1 connection. You can do color cycling for effects, change opponents’ colors.

Special pens

You probably ask yourself: what does this have to do with collisions? Good question. Now that you know about pens, you should also be told that out of the 16 pens, three are special when it comes to collisions.

In the previous part we looked at collisions and everything about a sprite seemed to be be collideable. Every pixel drawn will collide. That’s hardly useful if it is always that way. Take the example of a bouncing ball. When it touches a wall only when pixels overlap, it will penetrate the wall before you can detect a collision. It would be better to have a collision when the pixels of the ball and the wall are next to each other. With what you saw so far that’s not possible. But, … fortunately there are specials pens that can draw pixel that have special collision behavior.

What you cannot see, you still feel

One of the special pen types is a pixel that you cannot see, but it will collide. The pixel is transparent in the video buffer, but the collision number is used in the collision buffer.

image

The figure shows the ball in orange with a collision number of 2. The wall is in green. It uses a collision number of 3. But, as you can see there are transparent pixels to the left of the wall that do set the value at the collision buffer. These are drawn with the special pen type, which we will call boundary-pen. When the ball just touches the wall visually (in the picture on the right) the depository of the ball will be set to 3 from the wall, but not because the visible pixels overlap. It is because of the invisible, but collideable pixels to the left of the wall.

Can’t touch this

Another special pen is one that you can see, but does not collide. This is particularly useful for background items, that you do want to see, but should not interact with the rest of the playfield. An example (ball-related) would be lines on the field or shadows on the ground.

image

In this figure you see how the video buffer contains a blue dashed line, but the collision buffer is still at zeros. When the ball reaches, touches and crosses over the line, it will not pick up a value higher than zero, because the buffer contains only zeros. Hence, the ball does not collide with the line. The other type of special pen was used to draw the line. Let’s call this pen the shadow-pen.

Invisible in every way

It is sometimes very useful to have a pen that you can use to draw pixel that aren’t there, not visible and not collideable. Although we didn’t discuss this yet, sprite pixel data is continous per line. That means that you need a way to add holes in your sprite should you need them. E.g., when you want to draw a donut (with a shine-through hole in the middle) you always need pixels to put in the middle, no matter what your action point is, to get to the pixels on the outside of the donut. These pixels are like fillers. They must not be visible and not collide. This pen is the “invisible pen”.

OK, so there a total of 3 special ways to draw pixels . They line up with the pen numbers like so:

Pen type Number
Boundary F
Shadow E
Invisible 0

It would be too easy if that was it, right? Because you wouldn’t want to use them all three at the same time every time. This is where the sprite types come in.

Sprite types

Which of the special pens are active, is determined by the sprite type. You will always use pens 0 through F, but for some sprite types you get the special pens. For other sprite types the special pens might behave like normal pens or slightly different. The following table shows the various sprite types and which special active pens it has (or does not have).

image

This is a pretty intimidating table, that looks like the one in the Epyx documentation. It trips me up every time I look at it. Here’s another way to select your sprite types and pens.

  • Normal pens (1-D) are always visible and collideable, except for Background-Non-Collideable and Non-Collideable when they do not collide.
  • You want to use boundary detection:
    Use pen F and Boundary or Boundary-Shadow sprite type.
  • You want to clear the collision buffer:
    Use pen E and Background-Shadow sprite. Sprite must have collision number 0. This is what tgi_clear does for you, btw.
  • You want to use pen 0 as a solid color:
    Select Background or Background-Non-Collideable (depending on whether it should be collideable)
  • When using Boundary-Shadow only normal pens are visible.

Here’s a screenshot of the tutorial sample that shows a block of four squares drawn with four pen types (pens 0, A, E, F)

image

The black square is drawn with 0, blue with pen A, yellow with E and white (including the edge) with F). The sample shows what this sprite will look like for each of the sprite types.

image

Also, it copies the contents of the collision buffer “behind” the sprites and makes it visible. The collision number for the sprite is 5, which represents a color of cyan (or something like it).

image

The black in this picture clearly shows you where the collision buffer is untouched. The cyan pixels tell you where the sprite will collide. Check this against the table to get a better understanding.

You are on your own from here. You will need to figure out which pens you need in your sprites and select the proper sprite type in your SCB like so:

sprite_collideable robot =  {
  0x0,  
{ BPP_4 | TYPE_NORMAL,

At the point of TYPE_NORMAL, you can also select any of the other types in the table. The definition of these constants is in _suzy.h.

#define TYPE_BACKGROUND  0x00
#define TYPE_BACKNONCOLL 0x01
#define TYPE_BSHADOW     0x02
#define TYPE_BOUNDARY    0x03
#define TYPE_NORMAL      0x04
#define TYPE_NONCOLL     0x05
#define TYPE_XOR         0x06
#define TYPE_SHADOW      0x07

Pick your sprite type, use the correct pens in your bitmap or define a redir palette in the SCB. Then set the correct sprite type in the SPRCTL0 field of the SCB. Like Beggar’s Canyon.

Internally yours (for hardcore game devs)

Inside of the TGI graphics driver lynx-160-102-16.tgi lies the implementation of collision detection with TGI. It handles the internal stuff that is required to make this all work. Let’s have a look at a bit of assembly code to see what happens.

; Set up collision buffer to $A058
	lda	#$58
	sta	COLLBASL
	lda	#$A0
	sta	COLLBASH
; Put collision index before sprite data
	lda	#$FF
	sta	COLLOFFL
	lda	#$FF
	sta	COLLOFFH

First the collision buffer is located at COLLBAS, which is set to $A058 by writing to the high and low parts of the address. Because it requires $1FE0 bytes and is followed by the two video buffers of $3FC0 bytes we end up at $FFF8, right before the register you do not want to touch.

Also, the relative offset of the collision depository (for all sprites) from the start address of a SCB is set to –1, represented by the $FFFF value of a signed integer. This is written to the COLLOFF register (in high and low byte).

The other thing you need to do is make a call to tgi_setcollisiondetection and pass the value 1 to enable it. This does two things:

  1. Change bit 5 of SPRSYS ($FC92), which is the global on/off switch for collision detection. Clearing the bit will enable, whereas setting it disables detection.
  2. Change the sprite that is used to clear the screen from the normal 1 pixel of size 160×102 to have a collision detection enabled sprite. When this sprite is drawn it will not only clear the screen but also write all zeros to the collision buffer, effectively clearing it.

Here’s the assembly to prove it:

	lda	ptr1		; Activate/deactivate collision detection
	bne	@L0           ; ptr1 contains the argument of 1 or 0
	lda	#%00000001	; tgi_clear does not erase collision buffer
	sta	cls_sprite
	lda	#%00100000
	sta	cls_sprite+2
	lda	__sprsys
	ora	#$20
	bra	@L1
@L0:	lda	#%00000000	; tgi_clear erases collision buffer
	sta	cls_sprite
	sta	cls_sprite+2
	lda	__sprsys
	and	#$df
@L1:	sta	__sprsys
	sta	SPRSYS

Enabling and disabling collision detection

There might be occasions where you do not want the collisions to occur.  This can be on a per-sprite basis or globally. There are three ways to disable collision detection:

  1. Set the sprite type to non-colliding in SPRCTL0 (per sprite)
    Choose TYPE_NONCOLL or TYPE_BACKNONCOLL as your sprite type.
  2. Disable collision detection per sprite

    We briefly touched on this in the previous part of the tutorial. Set bit 5 to 1 in the SPRCOLL field of the SCB. You can do it declaratively in the SCB or in code.

    sprite_collideable robot =  {
      0x0,  {
        BPP_4 | TYPE_NORMAL,
        REHV,
        NO_COLLIDE | 0x05, // ...


    robot.sprcoll = NO_COLLIDE | 0x05;
  3. Disable collision detection globally
    Same here: set bit 5 to 1 except now for the SPRSYS address. Decide whether to use the TGI function tgi_setcollisiondetection for this, like so:

    tgi_setcollisiondetection(0);

  4. Remember that using the TGI function causes a change in the behavior of the tgi_clear function!

Next time

We’ve covered a lot on collision detection. It’s about time we get into some input handling so we can use the buttons of the Lynx and get some action into our game. Or it might be interrupt handling. We’ll see. Till next time.

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

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