Sprite logic
Moderator: Moderators
Sprite logic
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!
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:
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.
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.
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.
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:
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.
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:
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:
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.
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
And I see you do this:
Code: Select all
lda #0
ldx #$ff
- sta $200, x
dex
bne - ; clear sprite page
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 -
-
Celius
- Posts: 2159
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
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.
Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?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.
Not weird at all considering evaluation is carried out one line in advance.Celius wrote:I didn't know sprites were delayed a scanline. That's wierd.
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: 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.
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: 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.
You might be onto something... I meant to set carry, maybe I'm not having a clean underflow...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.
How do you figure? $200+FF = $2FF = last byte in the page; everything should be cleared.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.
Really it doesn't matter what the variables are initialized to, it will just make the sprites appear somewhere else, no biggie.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.
Edit: nope, the SEC didn't fix anything :)
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.kyuusaku wrote:How do you figure? $200+FF = $2FF = last byte in the page; everything should be cleared.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.
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.
Yes they did, or sprite 0 wouldn't hit.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.
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.kyuusaku wrote:Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?
I remeber you said me the other way arround one time. You said this was unnoticeable.Celuis wrote:I'd use sprite clipping, because the sudden disappearing is ugly.
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 !Code: Select all
lda #0 ldx #$ff - sta $200, x dex bne - ; clear sprite page
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.
I see, good catch.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.
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.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.
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.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.
In this case I want $00 since I only really care to initialize byte $2 of the sprites.Bregalad wrote: SRAM has to be cleared with any value in the $f0-$ff range, NOT $00 to be effectively cleared !
-
Celius
- Posts: 2159
- Joined: Sun Jun 05, 2005 2:04 pm
- Location: Minneapolis, Minnesota, United States
- Contact:
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.kyuusaku wrote:Any idea why the top and left clip while sprites can scroll smoothly off the right and bottom?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.
EDIT:
Well, I guess I changed my mind. It doesn't look good.Bregalad wrote:I remeber you said me the other way arround one time. You said this was unnoticeable.Celuis wrote:I'd use sprite clipping, because the sudden disappearing is ugly.