[stella] The Multi-Sprite Trick

Subject: [stella] The Multi-Sprite Trick
From: Christopher Tumber <christophertumber@xxxxxxxxxx>
Date: Fri, 12 Sep 2003 18:33:46 -0400
Zach wrote:

>I'm curious about how you managed 9 sprites in Space Instigators. ;)

It's the RESPn trick.

This is another draft document, this was intended for inclusion with a larger introductory text that Paul was working on. But I suppose there's no real reason to sit on it if someone might find it usefull...



The Multi-Sprite Trick

The multi-sprite trick is a technique which allows a programmer to put more sprites on a scanline than would normally be allowable. Using this trick, up to 18 [?] sprites may be displayed on a scanline.

This trick may be used with Player0, Player1, Missile0 and/or Missile1 in any combination. The most common use is with P0 and P1 to create a row of sprites.

There are important limitations with this trick. This trick essentially allows you to create more than the normal limit of 3 copies of a sprite. However, in doing so you probably will not have time to change the bitmap, colour or size of the sprite (as applicable). So the kinds of displays created with this technique will usually be repetitive patterns.

The trick is accomplished by first setting NUSIZ0 and/or NUSIZ1 to display 2 or more copies of the sprites you want to display [The exact setting is only significant if you want you multi sprites very wide apart]. Your program then must strobe RESP0, RESP1, RESM0 and/or RESM1 repeatedly. If this is timed correctly so that it occurs after the first copy is drawn but before the second [This is where the exact setting of NUSIZn becomes important] then the TIA is tricked into thinking it's just started drawing sprites and contiues drawing the sprite as if it's the first copy. You just continue this for every copy you need.

The tightest formation of sprites possible with this trick is:

[image]

which is done by the following code:

sta RESP0
sta RESP1
sta RESP0
sta RESP1
sta RESP0
sta RESP1
sta RESP0
sta RESP1
sta RESP0
sta RESP1


However, this formation has one problem in that the last sprite on a row is shifted one pixel to the left. There is no known soloution to this problem. If you can work this "glitch" into your game (for example as part of an asynchronous display) then you can use this layout. If not, the closest "stable" formation is:

[image]

which is done by the following code:

sta RESP0,x
sta RESP1,x
sta RESP0,x
sta RESP1,x
sta RESP0,x
sta RESP1,x
sta RESP0,x
sta RESP1,x
sta RESP0,x
sta RESP1,x

or

sta.w RESP0
sta.w RESP1
sta.w RESP0
sta.w RESP1
sta.w RESP0
sta.w RESP1
sta.w RESP0
sta.w RESP1
sta.w RESP0
sta.w RESP1

The ,x and .w in this example are "dummies". They're only used to increased the length of time taken by each instruction by 1 cycle. The tradeoff being that the former requires the X register be set to zero and the latter results in slightly larger code.


If you need to turn off some of the sprites, you can do this by simply skipping their spot by inserting a dummy command. 

For example:

[image]

sta RESP0
sta RESP1
sta Dummy
sta RESP1
sta RESP0
sta Dummy
sta RESP0
sta RESP1
sta RESP0
sta RESP1

Where Dummy is an unsued (or scratch or temporary) RAM location.


This trick is quite easy to use to generate static displays. However, if you want a fully dynamic display things get considerably more complicated.

Since there is no time available while drawing the sprites to do any calculations, if you want a variable number of sprites on and off you must predetermine which sprites to display. A simple way to do this is to create a subroutine for every possible combination of on/off sprites. Then your program just needs to call the appropriate subroutine (this is what Galaxian does). The problem with this approach is that if you have a lot of sprites, the number of subroutines becomes very large. An alternative method is to place you drawing routine in RAM, adjusted for the current display - In the above example, copy all the STA RESP0 and STA RESP1 commands into RAM and then where sprites don't appear, substitute in a dumy RAM location for the relevant RESP0 or RESP1.

One thing you must be aware of if you are allowing individual sprites to be switched on and off. There are a number of cominations which you must treat as an exception. They cannot be displayed using the general display as above. For example, if either RESP0 or RESP1 needs to display only 1 copy of a sprite (because all other copies are off) then you must reset NUSIZ0/NUSIZ1. This would also be the case when two copies of a sprite are set too far apart for the second STA RESPn to occur before a "normal" copy is drawn.

In addition, these sprites are not positioned vertically like normal sprites. Rather their position is determined by which display cycle the first STA RESPn or RESPn occurs. So if you want to be able to reposition your sprites verically, you will either need to add more subroutines (as above) or adjust your RAM routine further. Or some combination of the two. Space Instigators uses a different subroutine for each possible vertical position, copies that routine into RAM and then modifies the STA RESPn commands to turn off dead Instigators. All this initialisation is done during the "blank" scanlines between rows of Instigatiors (there's just not enough space to have more than one routine sitting in RAM so the same buffer gets re-used for each line of Instigators).

The newer, single scanline repositioning routine(s) may be of help here, however the multi-sprite trick tends to use up so much of your scanline time that if you're trying to do other things (ie: display other sprites) on that scanline you may not have the luxury of enough cycles for general purpose positioning code. 

The multi-sprite trick has a side effect in that the formation of sprites is shifted [?exact number?] pixels right as compared to where a normal sprite would appear with an STA RESPn at that cycle. This results in a left margin that's not at the left edge of the screen. "Illegal" HMOVE/HMMn combination tricks may be used to fix this but with a corresponding increase in complexity [This should be elaborated on].


[Some more example code here]


References: The multi-sprite trick was originally used in Galaxian and was pioneered by Eckhard Stolberg, John Saeger, Erik Mooney and Thomas Jentzsch. Search Stella List under "Grid demo","trick18","trick12" and "inv3" for more information.




Chris...

----------------------------------------------------------------------------------------------
Archives (includes files) at http://www.biglist.com/lists/stella/archives/
Unsub & more at http://www.biglist.com/lists/stella/


Current Thread