Jump to content
IGNORED

More than four sprites per line


hloberg

Recommended Posts

The other day I was reading a article about the TMS9918A and the writer commented that one could have more than 4 sprites per line by quickly changing the sprite priority which would cause some flicker. That's about all he said. Has anyone ever tried this? The logic sounds good but would the processor be able to keep up?

I can't even fathom the logic to do that.

?

Edited by hloberg
Link to comment
Share on other sites

It's something I was planning on trying for my next game... Not sure how well it'd work with auto-motion though...

 

Basically I envision the logic as being:

 

Let's say - for a game like pacman - you have pacman + 4 ghosts. Obviously, you'd want there to be a constant sprite being pacman, so you have 3 sprites for 4 ghosts... What I'd probably attempt to do is overuse sprites. Why? That'd reduce flicker when the sprites were not on the same line... And, we'd only need one sprite to rotate in this case. So, what we'd have

 

1. pacman

2. top ghost sprite

3. next ghost sprite

4. next ghost sprite.

5. next ghost sprite

6. inky

7. pinky

8. blinky

9. clyde

 

So, the 4 ghost sprites (6-9) will move around as normal. then, periodically, the topmost ghosts will flip to some hidden location, change colors, and flip back to the new ghost location. By cycling these over top of the ghosts, I think sprite flicker would be eliminated when there is no overlap and minimized / rotated when there is.

 

I'm sure this could be optimized further... And, again, automotion would be problematic to coordinate. For example, one could probably get away with 3 "over top" sprites since inky would be allowed to flicker when the 3 overtop sprites were on the other ghosts...

 

-H

 

so, the topmost ghost sprite would switch between

Edited by unhuman
Link to comment
Share on other sites

In Colecovision/MSX games it's common to use an intermediate table to contain the 128 bytes of sprite table.

 

Then in interrupt routine, the table is loaded to VDP, but each time a counter is incremented to indicate start sprite and sprites are "jumped" by an offset.

 

By example, talking only about 8 sprites (advance one sprite per frame, 3 sprites offset):

 

First frame: sprite0, sprite3, sprite6, sprite1, sprite4, sprite7, sprite2, sprite5

Second frame: sprite1, sprite4, sprite7, sprite2, sprite5, sprite0, sprite3, sprite6

Third frame: sprite2, sprite5, sprite8, sprite3, sprite6, sprite1, sprite4, sprite7

And so on.

 

The algorithm will not show flicker when no more than 4 sprites are in line.

 

The flicker will show when there are more than 4 sprites in line.

 

Of course, some adjustment should be done per project to offset (3 in example) and increment (1 in example)

 

You can download the Meisei MSX emulator and try the Nemesis ROM game, there is an option in the emulator that shows you the sprite table being used and you'll see the sprite cycling.

Link to comment
Share on other sites

By example, talking only about 8 sprites (advance one sprite per frame, 3 sprites offset):

 

First frame: sprite0, sprite3, sprite6, sprite1, sprite4, sprite7, sprite2, sprite5

Second frame: sprite1, sprite4, sprite7, sprite2, sprite5, sprite0, sprite3, sprite6

Third frame: sprite2, sprite5, sprite8, sprite3, sprite6, sprite1, sprite4, sprite7

And so on.

 

If we, as an example, are talking about only 8 sprites, wouldn't it be enough to swap between these two lists of priorities ?

 

0,1,2,3,4,5,6,7

4,5,6,7,0,1,2,3

 

 

The algorithm will not show flicker when no more than 4 sprites are in line.

 

With both solutions, if we have like only two sprites horizontally, and they move close to each other, then I suppose they will start to flicker where overlapping, sprite1 is above sprite2, then soon the other way around - same goes with my 0 and 4 etc. ? Guess that's a small unavoidable sideeffect ?

 

:)

Edited by sometimes99er
Link to comment
Share on other sites

If we, as an example, are talking about only 8 sprites, wouldn't it be enough to swap between these two lists of priorities ?

 

Yes, of course, but I was showing just the example because I didn't want to extend to the full 32 sprites.

 

Besides there are lots of solutions that don't require sprites. By example in some labyrinth games it would be better to replace enemies by characters even with pixel offset.

 

With both solutions, if we have like only two sprites horizontally, and they move close to each other, then I suppose they will start to flicker where overlapping, sprite1 is above sprite2, then soon the other way around - same goes with my 0 and 4 etc. ? Guess that's a small unavoidable sideeffect ?

 

:)

Not a problem with games that don't require overlap, like SHMUPS.

 

For other games like RPG it's possible to devise a special flicker algorithm.

 

By example, keeping player as sprite 0 and 1 so player doesn't flicker and gets priority over other sprites, and keep sprites 2-31 cycling for flicker.

Link to comment
Share on other sites

  • 1 year later...

Finally back to the projects I started two years ago.

Anyway, I'm writing a version of 'Megamania' for the TI. Should be a good program to get my feet wet in both C99 and Assembler.

Megamania porting to TI has been fairly straightforward except for the screens that have 2 sets of 5 sprites in a row.

On another tread Tursi suggested an E/A routine embedded in C99 to cycle the sprites at the vertical blank.

From what I understand the VBI on the TI is 1/30 of a second (faster than the eye) and flicker should be minimal.

Therefore, would the best way to cycle be:

1,2,3,4,5

2,3,4,5,1

3,4,5,1,2

etc... (around in a circle)

Would I be putting them in a table and just calling the next in a loop or is there a more efficient way.

Anyone got some code I could look at, I'm still (re-)learning Assembler so a little fuzzy.

Thanks.

Link to comment
Share on other sites

1/60th of a second, and yes. You will still SEE it, but 30hz flicker (every other frame) is usually decent. 20hz flicker (every third frame) is usually the limit of what you want.

 

You can rotate one sprite at a time, but since the limit is four sprites, I rotate by 4 sprites at a time. Other people proposed other solutions in another, I haven't personally tried them.

 

The way I recommend doing it (and this is useful no matter whose algorithm you go with) is to use a "fake" sprite table in CPU memory, and then copy that table to VDP memory during the interrupt. Then you just copy the entries in the order you've selected, it never needs to rotate in CPU memory (meaning you know what address each sprite is actually at ;) ). This will be faster than manipulating the VDP table in real time.

Link to comment
Share on other sites

It's not c99 code, but this is what I used for the ColecoVision:

 

 

  // we MUST update the VDP first, or we may miss the vblank
  // this does the sprite rotation for flicker and writes
  // the sprite data to VRAM
  cv_set_write_vram_address(gSPRITES);
  nmi_n2=(MAX_SPRITES-nSpriteOffset)<<2;  // byte count till end of table
  cv_memtovmemcpy_fast(&sprites[nSpriteOffset], nmi_n2);
  if (nSpriteOffset) {
   // if that wasn't the whole table, write the first part
   nmi_n2=nSpriteOffset<<2;
   cv_memtovmemcpy_fast(&sprites[0], nmi_n2);
  }

 

cv_set_write_vram_address - does what it says, it sets the VDP write address to the beginning of the sprite table.

nmi_n2 is a work variable - it will hold the number of bytes we need to write.

nSpriteOffset is the index of the sprite we are currently drawing as sprite 0 - every frame this is incremented by 4, and wraps at MAX_SPRITES (which was 16 in this app).

The <<2 part multiplies by 4 using shifts, as each sprite has four bytes in the attribute table.

cv_memtovmemcpy_fast - copies from CPU to VDP (assuming address was already set), starting from the offset in the CPU sprite table, and for the number of bytes in nmi_n2. The effect of this is that, if nSpriteOffset is currently '8', for instance, then sprite 8 in the CPU sprite table will be displayed by the VDP as sprite 0 for this frame.

if (nSpriteoffset) - just checks whether the offset was 0. If it was, we copied ALL the sprites this frame. If not, we need a second write to 'wrap around' and copy the other half of the table to VDP.

And the last two lines do that - calculate how many bytes from the start of the CPU table we need, and then copies them (again, assuming the VDP address was already set, so they go right after the ones we already wrote.)

 

So that method resulted in a 4 frame sequence of sprites:

 

0 1 2 3 4 5 6 7 8 9 A B C D E F

4 5 6 7 8 9 A B C D E F 0 1 2 3

8 9 A B C D E F 0 1 2 3 4 5 6 7

C D E F 0 1 2 3 4 5 6 7 8 9 A B

 

It's pretty basic and primitive, but it worked well enough. ;)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...