Sprite logic

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Sprite logic

Post by kyuusaku »

When I move a sprite partially off the top or left of the screen in emulators (I can't see the behavior on a real NES with my TV), after a few pixels it clips the sprite. Why is this and how can I prevent it? This logic isn't implemented in my emulator, but if it's really how it works I'd like to put it in and well, understand it.

Also are we sure that Nintendulator has a perfect renderer? I ask because my PowerPak is having problems with a little test ROM I wrote to test my TV's safe area while emulators are not. I also don't think this has anything to do with the hardware since sprite DMA in games seems to work fine and I can't imagine where the PowerPak's initialization would break my code.

Anyone mind looking at my program? I suppose there's something I could be overlooking but it's pretty straight forward.

http://files-upload.com/files/675089/screentest.zip

It assembles with ASM6. Very sorry about the free file upload site!
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I believe your sprite clipping is due to the fact that it wraps around to be at Y Coordinate $FF, it's going to a pixel that is not displayed, since the screen is only $F0 pixels tall. Once it reaches $EF, you'll be able to see it. This may be total garbage, but this is how I think it works. Say your sprite is at coordinates $FF. At Scanline $FF (Off screen), the game will try to render the sprite. The rest of the sprite should be on the top of the screen, but the NES already rendered stuff up there, and by the time it gets back to the top of the screen, it forgets that there was a sprite at $FF. I'm not certain, but that might be it.

In Nintendulator, It seems that if you put a sprite at coordinates 0,0, it's not exactly in the corner of the screen. It will appear to be at pixel 0,1. I don't know why this is, but I had to mention it while I'm here.

EDIT: You should get a freewebs account if you want a place to store stuff that people can download. It's of course, free, and doesn't require the waiting.
User avatar
Zepper
Formerly Fx3
Posts: 3264
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper »

-deleted-
Last edited by Zepper on Tue Jan 29, 2008 8:17 pm, edited 1 time in total.
bunnyboy
Posts: 449
Joined: Thu Oct 27, 2005 1:44 pm
Location: CA
Contact:

Post by bunnyboy »

But in this case Nintendulator is correct, sprites are delayed 1 scanline. If you put it at 0,0 then displaying at 0,1 is right.

If your sprite is at 0,0 and you move it left, it will now be at 255,0 and should display on the right side. If you move it up it will be at 0,255 which is off the viewing area at the bottom.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I didn't know sprites were delayed a scanline. That's wierd.

So yeah, according to NESdev wiki, sprites do not wrap around if they are in positions $F9-$FF. If your sprite is at $00, and you move left one pixel, it'll be at $FF, so it will jump to the other side of the screen. Since there are no wrap arounds, the rest of your sprite will not be visible. That is also the case for Y coords.

There aren't many ways to prevent this. If you want, you can use the sprite clipping so it won't be visible in the left column of the screen. For vertical movement, it's not that big of a deal if you're using an NTSC system, since the top two bars are cut off. I'd use sprite clipping, because the sudden disappearing is ugly.

I looked over your code, and I noticed some things that looked like they may cause some sorts of problems. Not anything too big though.

Code: Select all

	lda sprite_y
	clc
	sbc #1		; compensate for ppu
Is there any reason for clearing the carry? Maybe there is, but this could be a typo. Just wanted to point it out.

And I see you do this:

Code: Select all


	lda #0
	ldx #$ff
-	sta $200, x
	dex
	bne -		; clear sprite page
You know you can just load X with $00 and it will clear everything? If you do "ldx #$FF" for the loop, it'll just clear everything but the last byte.

EDIT: I also don't think you're clearing the Sprite_X or Sprite_Y values before using them. That could be a problem. You'd do well to clear all of RAM with this simple loop:

Code: Select all

 lda #0
 tax
-
 sta $0,x
 sta $100,x
 sta $200,x
 sta $300,x
 sta $400,x
 sta $500,x
 sta $600,x
 sta $700,x
 inx
 bne -
I'm pretty sure the NES has random values in RAM on reset. Counting on them being #$00 is not a good thing to do.
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

I was briefly looking at Super Mario Bros. Apparently, the programmers of that game didn't realize that sprites were lagged one scanline either.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

It's obvious to me when I look at it. In my game, I have portraits for each of the characters. There aren't enough colors in one attribute section to do shading, hair color, and whatnot. So I have to do sprite overlapping. When I first entered in all the values for the sprites, I had them all multiples of 8. They ended up being 1 pixel too low, so I had to go back and subtract 1 from all the Y values.
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

bunnyboy wrote:If your sprite is at 0,0 and you move it left, it will now be at 255,0 and should display on the right side. If you move it up it will be at 0,255 which is off the viewing area at the bottom.
Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?
Celius wrote:I didn't know sprites were delayed a scanline. That's wierd.
Not weird at all considering evaluation is carried out one line in advance.
Celius wrote: So yeah, according to NESdev wiki, sprites do not wrap around if they are in positions $F9-$FF. If your sprite is at $00, and you move left one pixel, it'll be at $FF, so it will jump to the other side of the screen. Since there are no wrap arounds, the rest of your sprite will not be visible. That is also the case for Y coords.
I know, but it doesn't matter, I don't mind if the sprite goes off screen and takes longer to wrap around. I just don't want the sprite to clip before it's fully off screen as it does only on the left and top.
Celius wrote: There aren't many ways to prevent this. If you want, you can use the sprite clipping so it won't be visible in the left column of the screen. For vertical movement, it's not that big of a deal if you're using an NTSC system, since the top two bars are cut off. I'd use sprite clipping, because the sudden disappearing is ugly.
To me it doesn't matter if it's ugly, I just want to see the sprite scroll off screen without clipping. Otherwise my program will have to scroll the background.
Celius wrote: Is there any reason for clearing the carry? Maybe there is, but this could be a typo. Just wanted to point it out.
You might be onto something... I meant to set carry, maybe I'm not having a clean underflow...
Celius wrote: You know you can just load X with $00 and it will clear everything? If you do "ldx #$FF" for the loop, it'll just clear everything but the last byte.
How do you figure? $200+FF = $2FF = last byte in the page; everything should be cleared.
Celius wrote: I'm pretty sure the NES has random values in RAM on reset. Counting on them being #$00 is not a good thing to do.
Really it doesn't matter what the variables are initialized to, it will just make the sprites appear somewhere else, no biggie.

Edit: nope, the SEC didn't fix anything :)
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

kyuusaku wrote:
Celius wrote: You know you can just load X with $00 and it will clear everything? If you do "ldx #$FF" for the loop, it'll just clear everything but the last byte.
How do you figure? $200+FF = $2FF = last byte in the page; everything should be cleared.
Celius is right. Your code will fail to clear $200, it will only clear $201-$2FF. When X reaches 0, the branch is not taken, so the value at $200 will not be cleared. By initializing X with 0, you take care of that location right on the first write.

Back to the topic, there is no way to smoothly scroll sprites from/to the top or from/to the left of the screen and still see the whole background, this is a limition of the NES. The NES has an option to not render the leftmost 8 pixels, and by using that the sprites can scroll smoothly from/to the left of the screen. To have them scroll smoothly from/to the top, you may keep rendering disabled for a few scanlines after the end of VBlank, achieving a similar effect.

I don't think you should be to worried about this. Most TV's cut the parts of the image affected by this glitch. And almost all commercial games have that effect, and no one has complained until now.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Dwedit wrote:I was briefly looking at Super Mario Bros. Apparently, the programmers of that game didn't realize that sprites were lagged one scanline either.
Yes they did, or sprite 0 wouldn't hit.
kyuusaku wrote:Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?
Clipping to the top and left would require the PPU's registers to be able to represent negative coordinates. The Super NES PPU can do this; the NES PPU cannot.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

Celuis wrote:I'd use sprite clipping, because the sudden disappearing is ugly.
I remeber you said me the other way arround one time. You said this was unnoticeable.

Code: Select all

   lda #0
   ldx #$ff
-   sta $200, x
   dex
   bne -      ; clear sprite page 
In fact to be correct this code just have to be the other way arround : lda #$ff and ldx #$00 so that it will clear all bytes, and SRAM has to be cleared with any value in the $f0-$ff range, NOT $00 to be effectively cleared !

When it comes to scroll smoothly horizontally the only solution is to use hardware clipping, as someone already mentionned. That's most likely why this feature is existing at all. In function of your game engine and scrolling workings, you'll want this area to be clipped or not (most games either always clips it or never clips it). Also you cannot rely to the leftmost&rightmost 8 pixels to be rendered as far I've heard some monitors will hide this. If you use leftmost hardware clipping, a X coordinate of zero will completely hide the sprite and it will have no effect at all (exept to be counted in the 8 sprites per line limitation).

When it comes to scroll smoothly vertically it's not too hard as most monitors and emulators will hide the topmost and bottommost 8 pixels, so this is just enough to scroll smoothly. If you want to do PAL coding or if you are using 8x16 sprites, this isn't true any longer. In both cases, leaving the rendering off ala Battletoads would be one solution, however scrolling becomes a pain in the ass.
What you can do is abuse the 8 sprites per line limitation and place 8 top-priorities sprites at Y coordinate 0. Then you just have to turn the BG (and BG only, not sprites) off via $2001 in order to get a black background on the top of invisible sprites, effectively blanking the area. You'd want to enable BG back at either scanline #9 (PAL) or scanliine #17 (8x16 sprites) for completely smooth movement. I guess the scrolling will be right because the sprites were left enabled technically. It that's not the case, you may consider bankwitching an all-blank CHRROM page for the top scanlines, ending with a similar effect.
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

tokumaru wrote: Celius is right. Your code will fail to clear $200, it will only clear $201-$2FF. When X reaches 0, the branch is not taken, so the value at $200 will not be cleared. By initializing X with 0, you take care of that location right on the first write.
I see, good catch.
tokumaru wrote: I don't think you should be to worried about this. Most TV's cut the parts of the image affected by this glitch. And almost all commercial games have that effect, and no one has complained until now.
Well it does matter since I can see the left side clipping with my TV so it makes it frustrating to get a reading. I guess I'll have to switch to the background then.
tepples wrote: Clipping to the top and left would require the PPU's registers to be able to represent negative coordinates. The Super NES PPU can do this; the NES PPU cannot.
That wouldn't be necessary if the sprites wrapped from $FF to $00 correctly. For the PPU to wrap like that though, I guess it would need to fetch coordinates 8/16 lines early and cycle through all the horizontal pixels.
Bregalad wrote: SRAM has to be cleared with any value in the $f0-$ff range, NOT $00 to be effectively cleared !
In this case I want $00 since I only really care to initialize byte $2 of the sprites.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

kyuusaku wrote:
bunnyboy wrote:If your sprite is at 0,0 and you move it left, it will now be at 255,0 and should display on the right side. If you move it up it will be at 0,255 which is off the viewing area at the bottom.
Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?
This is because wrap arounds don't occur. And also, the sprite is rendered left to right. Sprites are kept track of from the top left corner's X, Y coords. When the top left corner's X coordinate is $FF, it will be rendered first, and by the time the rest is supposed to be rendered, it's already too late. The rest of the sprite will not be rendered. The clipping is not present because of the fact that it keeps track of the sprites top left corner. If it were the top right corner, and they were rendered right to left, the clipping would be for the right side, and not the left.

EDIT:
Bregalad wrote:
Celuis wrote:I'd use sprite clipping, because the sudden disappearing is ugly.
I remeber you said me the other way arround one time. You said this was unnoticeable.
Well, I guess I changed my mind. It doesn't look good.
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

Celius wrote:This is because wrap arounds don't occur.
Wrap around does occur from $FF to $00, just not from $00 to $FF like it should.
Celius
Posts: 2159
Joined: Sun Jun 05, 2005 2:04 pm
Location: Minneapolis, Minnesota, United States
Contact:

Post by Celius »

I get the feeling that we're talking about two different things. Are you asking why the sprite suddenly dissapears as it begins to move off the left side of the screen, and it doesn't when it begins to move off the right?
Post Reply