[4.1.5] Sprite Cycling

AllDarnDavey

Active member
You may know that the NES can only display up to 8 sprites on a horizontal scanline. That's 8, 8x8 blocks, not 8 objects... so if enemies are just 2blocks wide after 4 in a row you get problems. Anything after the first 8 are just invisible and it isn't necessarily the first 8 left to right, it's the order they're drawn by the CPU.
YoXifQP.gif


You probably remember the flicker many NES games had. This is caused by sprite cycling. The flicker is mildly annoying, but it's tonnes better then getting killed by enemies or projectiles you can't see. Basically, you still only see 8 sprites per scanline, but you cycle the sprites being turned off to a different one every frame.
Nr96IIh.gif


chronicleroflegends recently found and adjusted the code responsible for sprite draw order, and I used those changes as a base to add sprite cycling to it as well. I even borrowed the bit detecting the player to make it draw on top (to also keep the player from being part of the flicker cycle). It was actually a pretty small change to get it to work.

If you'd like to add Sprite Cycling to your NESmaker project.

Find the original UpdateDrawOrder section in HandleUpdateObjects.asm (around line 823, I also like to make a backup copy of the file first, just-in-case). Select the entire selection listed below:
Code:
UpdateDrawOrder:
	
	LDX #$1
OrderLoop:

	LDY drawOrder,x
	LDA Object_y_hi,y
	STA temp
	;;;; what would drawOrder-1 be?  if it is 0, would would have to become 0f.
	
	LDY drawOrder-1,x
	LDA Object_y_hi,y
	CMP temp
	BCS doneWithSwapItem
	LDA drawOrder,x
	STA drawOrder-1,x
	TYA
	STA drawOrder,x
doneWithSwapItem:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BNE OrderLoop
	RTS

Then replace with those lines with the new sprite cycling version.
Code:
UpdateDrawOrder:
;;Now with sprite cycling	
	LDX #$1
OrderLoop:

	LDA drawOrder,x
	LDY drawOrder-1,x
	CPY player1_object ;; if previous is player object, don't swap them.
	BEQ doneWithSwapItem	
	LDA drawOrder,x
	STA drawOrder-1,x
	TYA
	STA drawOrder,x
doneWithSwapItem:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BNE OrderLoop
	RTS

EDIT: was having issues with sprites of different heights, should be fixed.
EDIT2: removed unnecessary draw sorting.
 

Mugi

Member
that's pretty neat!
ways better than the whole deal with dissappearing monsters or half of them :p
 
What if I don't want the player to draw on top and I don't mind player being part of the flickering? How would I change the code. I have instances where my player goes behind sprites, such as behind bed sheets to sleep.
 

AllDarnDavey

Active member
dark_crow_66 said:
What if I don't want the player to draw on top and I don't mind player being part of the flickering? How would I change the code. I have instances where my player goes behind sprites, such as behind bed sheets to sleep.

You can make the player part of the sprite cycle by just commenting out that bit of code that checks against the player.
Code:
UpdateDrawOrder:
;;Now with sprite cycling	
	LDX #$1
OrderLoop:

	LDA drawOrder,x
	LDY drawOrder-1,x
	;CPY player1_object ;; if previous is player object, don't swap them.
	;BEQ doneWithSwapItem	
	;LDA drawOrder,x
	STA drawOrder-1,x
	TYA
	STA drawOrder,x
doneWithSwapItem:
	INX
	CPX #TOTAL_MAX_OBJECTS
	BNE OrderLoop
	RTS

Also, it doesn't affect the sorting with tiles, if you are using "walk behind" tiles (I use them all the time), they work just fine. I only affects sorting with other sprite objects. If your using walk behind tiles for bed sheets no change needed, if your sheets are actual sprite objects, you'd have to find a way to check against the sprite the bed sheet uses to exclude it instead.
 

DanielT1985

Member
This is awesome. I tested it in 4.1.0, and it works on that version as well. I think it actually made my game a little bit faster, with all of the entities on the screen, but I'm not sure. Either way, thanks for the code.
 
Thanks again for this!

I found that when I played on actual hardware, I had an issue with my second boss battle where it was possible to go over the sprites per scanline limit and make part of the boss disappear (making an unfair encounter for the player)

This helped out quite a bit. It still doesn't look nice to flicker like that, but at least where you can see where the boss is shooting you from.
 

retrothunder

New member
I'm pretty slow when it come to this could someone give me a step by step cause I can't seem to find the script please help..
 

dale_coop

Moderator
Staff member
The "HandleUpdateObjects.asm" script in your "System" sub folder, under your root folder.
In "Project Settings > Script Settings", select the ROOT element and click on the "Edit" button :
2019-07-10-09-30-26-Project-Settings.png


Then double clic on the "System" folder.... you will see a lot of scripts, find "HandleUpdateObjects.asm" and open it with your favorite text editor (notepadd++ for example).
 

Atarath

Member
So I noticed that if you don't want the player drawn on top, whenever you interact with another object such as an npc, both objects with flicker. Both this and being drawn on top removes the illusion of depth, being able to walk behind an npc object for example. Is there anyway to use this without that happening, or a way to count sprites on a given line so that the cycling only happens when the limit is reached?
 

AllDarnDavey

Active member
Elarath said:
So I noticed that if you don't want the player drawn on top, whenever you interact with another object such as an npc, both objects with flicker. Both this and being drawn on top removes the illusion of depth, being able to walk behind an npc object for example. Is there anyway to use this without that happening, or a way to count sprites on a given line so that the cycling only happens when the limit is reached?

I can't think of an easy way to pull this off. My solution is stupidly simple, just shifting draw order every frame (excluding or including the player depending on the version). We'd somehow have to do more tracking, that's a lot extra coding work, and possibly too expensive on the NES. But I'll think on it, and if I can come up with anything I'll let you know.
 
Do this have a size limit? Noticing yiur sprites look little smaller. I puth this in tried it and it didnt work but my sprites are larger.
 

AllDarnDavey

Active member
Electric cat said:
Do this have a size limit? Noticing yiur sprites look little smaller. I puth this in tried it and it didnt work but my sprites are larger.

There is a bit of a size limit. A "sprite" is an 8x8 pixel block, but the draw order cycling is per character not per sprite block, so if you had a large boss or something that was wider then 8 sprite blocks this wouldn't have any affect. But I tested with characters that were 1 sprite wide through 4 sprites wide and didn't have any issues. The wider your characters are the quicker you'll start to get flicker, and the worse the flicker will be, but it should still work.

Maybe try the second version I posted that doesn't exclude the player character when cycling. If your characters are larger you may need to make sure the player gets included in the flicker cycle.
 
That sounds like a plan. I have a tank the is 6 wide by 5 tall. I wanted to ask first. When the tank passes my 1wx2t player the tank ingulfs the player until the tank passes.
 
Top Bottom