News: 11 March 2016 - Forum Rules, Mobile Version
Current Moderators - DarkSol, KingMike, MathOnNapkins, Azkadellia

Author Topic: NES Question - Best Method for Sprite Engine?  (Read 362 times)

RetroRain

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
NES Question - Best Method for Sprite Engine?
« on: August 22, 2017, 11:48:57 am »
I know how sprites work (Ycoor, TileID, Pal/Rot, Xcoor), and how to get them to the screen.

Most tutorials and documents tell you how to handle sprites, but there is very little information when it comes to game logic, in coding a sprite engine.

Most games are not a singe 8x8 sprite tile.  Characters and enemies are made up of a bunch of 8x8 sprite tiles.  This is where it becomes difficult for me.

Let's say I have this routine, which is only executed once when the player object is created:

Code: [Select]
; initialize player object

lda #$60
sta player_xpos
lda #$50
sta player_ypos

; simply set the player's x and y position on the screen

Simple enough, right?

I know how to draw all of the sprites for the player, but I don't know how to do it in the most efficient way.  So far, most of the ways I have done it, use up a ton of code.

Let's say my player objects consists of 9 sprite tiles (Megaman, for example, while he is standing, is 9 8x8 sprite tiles)

I have this routine, which is executed every frame:

Code: [Select]
; player handler

; draw player - method 1

; The X and Y coordinates are the center origin of the player, so that if the player has to be mirrored, he'll be mirrored smoothly

; ***
; ***
; ***

; These asterisks represent the 9 8x8 sprite tiles

; One way I have done it, is start in the middle, and draw from there

; ***
; *+*
; ***

So, the plus sign represents the x and y position

[CODE]
lda player_ypos
sbc #$08
sta $200

; store the y for the first tile

lda player_xpos
sbc #$08
sta $203

; store the x for the first tile

; Then draw the tile and pal/rot values

lda #$00
sta $201

lda #$00
sta $202

When ROM hacking, I've seen games keep a table of the tile ID and pal/rot value right next to each other, so I tried programming that way too.  That way seems very difficult as well.  I'm manually programming every single sprite to the screen.  There has to be a more efficient way of doing things.  I just can't wrap my head around the logic.

Mirroring is a pain, because depending on which direction (left or right) the sprite is facing, the tiles that were supposed to be on the left side, now have to be on the right, but the middle can stay the same.

There are a few different ways that I have used to get a player sprite to appear, but the coding is intensive.  Is there a way to efficiently create a sprite engine, so that no matter what sprite you want to display, you can display all of the tiles, and correctly depending on the mirroring?

I have even tried making the xpos and ypos the top-left corner of the player, but then when it comes time to mirroring, it doesn't look right.  Unless I were to simply change the x/y positions when mirroring occurs.

I'm going to do some more studying of ROMs to see how the sprite engines are handled, but if anyone has any suggestions, that would be of great help.

Thanks.

Disch

  • Hero Member
  • *****
  • Posts: 2519
  • NES Junkie
    • View Profile
Re: NES Question - Best Method for Sprite Engine?
« Reply #1 on: August 22, 2017, 02:49:17 pm »
Sprite tile layout can be stored in a lookup table.  This particular table might not be ideal, but should illustrate the concept:

Code: [Select]
; For this example, a player consists of 4 sprites
;  The "origin" point is the bottom-center -- that is, all X/Y coordinates are relative to the bottom-center
player_sprites:
   ; 1 byte to indicate how many sprites this object has
   .byte    4

   ; followed by X sets of 4 bytes, where each set is information about each sprite
   ;    tile ID,   palette,  X position,  Y position
   .byte      0,         1,          -8,         -16    ; top-left
   .byte      1,         1,           0,         -16    ; top-right
   .byte      2,         1,          -8,          -8    ; bottom-left
   .byte      3,         1,           0,          -8    ; bottom-right

You can then write code that loops through this table and effectively just copies those values to OAM.  Only thing you'd really have to change is you'd ADC the object's position to each of the X/Y coords.

If H-flipping, instead of ADC'ing the X coord, you'd SBC it.
For V-flipping, instead of ADC'ing the Y coord, you'd SBC it.

(EDIT:  actually, flipping might not be that simple.. you have have to SBC it and add 8.)

And of course, you'd also have to set the H/V flip in the attribute byte as well.


This kind of general structure could be used for any sprite, so you'd really only need 1 routine to draw sprites, and then you could re-use it to draw any sprite -- you'd just have to give it a pointer to a different table.
« Last Edit: August 22, 2017, 03:01:21 pm by Disch »

darkmoon2321

  • Jr. Member
  • **
  • Posts: 31
    • View Profile
Re: NES Question - Best Method for Sprite Engine?
« Reply #2 on: August 22, 2017, 06:46:30 pm »
This is a very relevant topic to me right now, as I'm thinking about attempting to optimize the sprite display code in the Ninja Gaiden hack I'm working on.  I find that a large portion of the code executed each frame is dedicated to arranging sprite data.  Ninja Gaiden uses a system similar to the method Disch talked about above, but there's also additional processing to constantly rotate the order of the sprites every frame.  Basically, they write xF0 to the Y position for each sprite every frame to place it off-screen, then they recalculate the position for every sprite, starting at a different place in the OAM array each frame.  They also alternate the top bit of the offset into the OAM array after each tile they place.  For example, since OAM data to transmit to the PPU starts at $200 in RAM, they'll write sprite Y position data in an order something like $240, $2C4, $248, $2C8, and so on.  The end result is that even with a lot of sprites on-screen, you get flickering, but you don't have sprites permanently hidden because of too many other higher priority sprites.  It seems like there's a lot of room for improvement in the efficiency department though.

The only other thing I have to add right now is to check the carry flag when you're calculating sprite positions to ensure that some of the wider sprites don't end up displaying parts on the wrong side of the screen.  I'll be looking into this more over the next few days.

RetroRain

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: NES Question - Best Method for Sprite Engine?
« Reply #3 on: August 24, 2017, 01:11:03 pm »
Thank you Disch.  I didn't have a chance to reply the past couple of days.  Just been so busy with work.  I read your post on my phone, but the table didn't look right.  I'm looking at it now from my PC, and it looks right.  I will take a closer look at your post tomorrow, so I can absorb the information from it.  I just haven't had much time the past couple of days.

Thanks for the information.  I'm sure it will help clear things up a bit. :)