Sprite Noob Question

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Sprite Noob Question
by on (#145401)
Im rewriting my sprites engine since i was using the old "brad taylor" method as we know is outdated.
I find a problem when sprites have X=255 or 0xFF. When a sprite has this coordinate value is it visible?, since im getting errors.

Thanks in advance.
Re: Sprite Noob Question
by on (#145405)
Sprite 0 hits don't trigger on sprites with X=255, even if there are visible pixels. Otherwise I think it's normal?
Re: Sprite Noob Question
by on (#145406)
I know it doesn't trigger when its 255, but my question is if X is set intentionally at 255, is the the sprite pixel shown??
Re: Sprite Noob Question
by on (#145412)
mean this:
I have 8 output units wich this unit is a C struct:

Code:
typedef struct
{
   unsigned char Y, TileIndex, Attr, PT0, PT1, pre_spr_zero, spr_zero;
   int X;
} SPRBUFFER, * PSPRBUFFER;


"X" is copied from "sprite temp" memory in sprite fetches phase.

When rendering each ppu cc i decrese this "X", when X == 0 the unit is "active" and check if the sprite is not transparent.

What i got is this:


Image
Re: Sprite Noob Question
by on (#145415)
The game should not set 255 to the left sprite when Mario moves to the left edge of the screen so I think it is a bug with the CPU, not PPU.
Re: Sprite Noob Question
by on (#145420)
Maybe it just assumes that Mario can never be off-screen horizontally? Especially since it'd be impossible to see on a TV anyway (due to overscan).
Re: Sprite Noob Question
by on (#145421)
I solved it!! it was an sprite logic problem anyway I tested it in a real NES and it doesn't produce that effect.
Re: Sprite Noob Question
by on (#145457)
Now I'm curious. :oops: :oops: :oops:

Is this a bug? Isn't Mario sprite supposed to wrap?
Re: Sprite Noob Question
by on (#145466)
Zepper wrote:
Now I'm curious. :oops: :oops: :oops:

Is this a bug? Isn't Mario sprite supposed to wrap?


You are right, somehow that glitch didn't cross my mind when I saw the first picture.
Re: Sprite Noob Question
by on (#145469)
Anes wrote:
I solved it!! it was an sprite logic problem anyway I tested it in a real NES and it doesn't produce that effect.


What was your fix?? I'd like to know in details to fix my emu too.
Re: Sprite Noob Question
by on (#145478)
I've tested it in my Sony Triniton using service menu and it happends on real hardware(sorry by the image).
Re: Sprite Noob Question
by on (#145481)
IIRC, SMB calculates sprite positions in a strange way, it doesn't surprise me that this bug is actually in the game.
Re: Sprite Noob Question
by on (#145483)
Is this along the glitches that ShaneM fixed?
Re: Sprite Noob Question
by on (#145487)
Yeah it happens in real hardware.

I have a real NES and a PAL Dendy. The thing that i couldn't test it in the real NES yestarday becouse my current transformer was broken and i tought that Dendy was the same, but not.

I have just bought one for the NES and IT DO HAPPEN.
Re: Sprite Noob Question
by on (#145488)
This sprite wrapping occurs in Nintendulator, but not on puNES. If Anes "has fixed it", it just took ppu_cycle MINUS sprite_xpos > 0 (should be >= 0).

So, that's it. No panic - it's not an emulator glitch.
Re: Sprite Noob Question
by on (#145489)
I partially fixed it, it was a "hack".

Anyway i have found this too:

Image
Re: Sprite Noob Question
by on (#145491)
I think it is the responsibility of the game to test for negative values and so you don't need to fix that.

But if you want to make it look better, I can think of two ways of doing it:
1. Detect whether the x value of the sprite comes from a subtraction or not. In that case, you know whether it is a negative value or the sprite really is on the right screen edge.

2. Set a negative range for x, let say 4 pixels, so that positive values for x is limited to 0 - 251 and anything above it will be treated as negative values and are rendered on the left edge. Then you can optionally cover up 4 pixels on each side of the screen.
Re: Sprite Noob Question
by on (#145492)
It turns out this bug in SMB1 isn't easily fixable, as the fix ShaneM tried caused the player to get stuck in the wall when attempting the walk through walls glitch. He had to back out the fix.
Re: Sprite Noob Question
by on (#145493)
tepples wrote:
It turns out this bug in SMB1 isn't easily fixable, as the fix ShaneM tried caused the player to get stuck in the wall when attempting the walk through walls glitch. He had to back out the fix.


Yes, it is important to fix the rendering without changing the logic behind it. However I understand that this is difficult to do for emulators where the rendering is done directly by the PPU module.
Re: Sprite Noob Question
by on (#145494)
tepples wrote:
It turns out this bug in SMB1 isn't easily fixable, as the fix ShaneM tried caused the player to get stuck in the wall when attempting the walk through walls glitch. He had to back out the fix.



tepples is right. We talked about this on IRC.

The issue actually deals with collision. The routine in question is "CheckSideMTiles:". That is the routine for left/right collision against any solid object. The glitch occurs when Mario is all the way at the left of the screen, holding left in between another block and the screen, jumping, he gets pushed to the right, making him go partially into the block thus causing him to be pushed more leftward and disappearing for a frame and then reappearing with part of him on the other side. It would be the same since blocks are to the right of Mario and the screen is to the left. He no longer would get pushed if I corrected him from being pushed leftward when going into a block, which fixes that part, but gets stuck in places like where tepples linked where it is required to move on. If Mario is to the left of the screen and jumps, he gets partially sucked into the block to the right and pushed by out the opposite way (leftward) causing him to "wrap" around.

Notice in the link to themushroomkingdom that tepples linked to, the picture called "Mario standing on the wall" specifically. You can see part of Mario's hand on the other side. That wall pushing (intentional) causes the wrap around as seen there. In all the versions, SNES, GBC etc. this collision stands for that very reason. --ShaneM, the Master of ASM.

EDIT: I think I would be able to correct this if I had more PRG. If this was on something like loopy's MMC3 version, I would be able to. A fix would specifically have to be

Code:
LDY Player_OffscreenBits,
CPY #$F0 ;are we at a specific point at the left of the screen?
BCS ImpedePlayerMove ;if so, branch


This fix would have to go right at the beginning of the routine "ChkPBtm:" which is really part of "CheckSideMTiles:". If the player's movement is stopped when going leftward, he'd still be able to "walk through walls" to the right.
Re: Sprite Noob Question
by on (#145531)
The Mario Master has fixed this issue! Here is my fix:

Code:
RenderPlayerSub:
        sta $07                      ;store number of rows of sprites to draw
        ldy Player_Rel_XPos   ;SHANEM CODE
        cpy #$FF                 ;SHANEM CODE
      bne label         ;SHANEM CODE
      iny                  ;SHANEM CODE
label:   sty Player_Pos_ForScroll     ;SHANEM CODE store player's relative horizontal position
        sty $05                      ;store it here also
        lda Player_Rel_YPos
        sta $02                      ;store player's vertical position
        lda PlayerFacingDir
        sta $03                      ;store player's facing direction
        lda Player_SprAttrib
        sta $04                      ;store player's sprite attributes
        ldx PlayerGfxOffset          ;load graphics table offset
        ldy Player_SprDataOffset     ;get player's sprite data offset


Originally, it was "lda Player_Rel_XPos" and "sta Player_Pos_ForScroll". RAM $03AD was causing underflow when the player jumped in this specific way, but I fixed this and now you can still walk through walls if you get stuck. --ShaneM, the Master of ASM
Re: Sprite Noob Question
by on (#155271)
As I'm getting ready to commit a new build, I realized that the code to SMB: The Lost Levels had to be slightly different to fix this OAM wraparound mentioned by OP; this is due to wind in the game pushing player further left when caught in between the leftmost screen and a block and having RAM $86 increment by an additional #1. So $03AD was actually being underflowed by more than #$FF. This fixes that issue:

Code:
RenderPlayerSub:
        sta $07                      ;store number of rows of sprites to draw
               ldy Player_Rel_XPos   ;SHANEM CODE
               cpy #$FD                 ;SHANEM CODE    SMB:TLL different
               bcc label         ;SHANEM CODE       SMB:TLL different
          ldy #$00                  ;SHANEM CODE   SMB:TLL different
label:   sty Player_Pos_ForScroll     ;SHANEM CODE store player's relative horizontal position
       
         sty $05                      ;store it here also
        lda Player_Rel_YPos
        sta $02                      ;store player's vertical position
        lda PlayerFacingDir
        sta $03                      ;store player's facing direction
        lda Player_SprAttrib
        sta $04                      ;store player's sprite attributes
        ldx PlayerGfxOffset          ;load graphics table offset
        ldy Player_SprDataOffset     ;get player's sprite data offset


EDIT: I couldn't use A register instead and utilize a transfer and save a byte since neither X nor Y were loaded with zero at the time and the fact that 6502 is lacking in an STZ instruction. So this fix uses 1 additional byte of PRG compared to the NES SMB1 fix above. Don't worry, you can optimize this next routine after to save some space:

Code:


;original

ProcessPlayerAction:
        lda Player_State      ;get player's state
        cmp #$03
        beq ActionClimbing    ;if climbing, branch here
        cmp #$02
        beq ActionFalling     ;if falling, branch here
        cmp #$01
        bne ProcOnGroundActs  ;if not jumping, branch here


;optimized

RenderPlayerSub:
        lda Player_State      ;get player's state
        beq ProcOnGroundActs  ;if not jumping, branch here
        cmp #$03
        beq ActionClimbing    ;if climbing, branch here
        cmp #$02
        beq ActionFalling     ;if falling, branch here

;saves 2 bytes of PRG