warmCabin

Rockman 2 animation data format

Jan 16th, 2022 (edited)
546
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.15 KB | None | 0 0
  1. Rockman 2 uses a simple animation system consisting of cels at an even tempo. Each cel is simply a list of tiles and what position to draw them at--I use the old timey term "cel" instead of "frame" so as not to confuse them with frames of video. An animation is one or more cels, usually looping, that each last for the same duration. The offsets for each tile in a cel are determined by a shared set of shape patterns that can be reused.
  2.  
  3. Each sprite only gets one animation. Their AI will typically freeze and loop certain cels to get the needed behavior.
  4. For example, scworms (aka toothpase or gray bacon strips) use cels 0 - 5 for the excretion, then loop 6 - 9 for about 3 seconds for the wiggle,
  5. then use A - E for the poof. Cel F stores data that indicates to terminate instead of loop.
  6.  
  7. First, here's a quick primer on how objects are stored in memory.
  8.  
  9. Each sprite has several values associated with it. Of interest to us are:
  10.  
  11. flags: Relevent to us are:
  12. 0x40: flip sprite (left = normal, right = flipped)
  13. 0x80: alive
  14. ID:
  15. Each object type has a unique ID for fetching graphics data, update routines, hitboxes, and more.
  16. cel number:
  17. Which cel of the animation it's on.
  18. animation timer:
  19. Timer that controls when to advance to the next cel. It is initialized based on the animation tempo.
  20. attribute override:
  21. Can override a tile's palette or set it flipped. I'm not fully sure how this is used, as bits unused by the PPU are often set.
  22. At the very least, I know it's used to make enemies flash white when you shoot them.
  23.  
  24. The game uses several pointer tables to fetch animation data.
  25.  
  26. Enemys:
  27. ENEMY_ANIM_PTRS_LO = $0F:F980
  28. ENEMY_ANIM_PTRS_HI = $0F:FA80
  29. ENEMY_CEL_PTRS_LO = $0A:8100
  30. ENEMY_CEL_PTRS_HI = $0A:8300
  31.  
  32. Player:
  33. PLAYER_ANIM_PTRS_LO = $0F:F900
  34. PLAYER_ANIM_PTRS_HI = $0F:FA00
  35. PLAYER_CEL_PTRS_LO = $0A:8000
  36. PLAYER_CEL_PTRS_HI = $0A:8200
  37.  
  38. SPRITE_CEL_POS_PATTERN_PTRS_LO = $0A:8400
  39. SPRITE_CEL_POS_PATTERN_PTRS_HI = $0A:8500
  40. TILE_OFFSET_SUBTRACTION_TABLE = $0A:8600
  41.  
  42. Use the sprite ID as an offset into the ANIM_PTR tables to get an aimation pointer, which points to animation data.
  43.  
  44. Animation data is laid out as follows:
  45. length, tempo, [cel ID]+
  46.  
  47. length: How many cels in the animation. Cel numbers loop from 0 to this value, so there are really length + 1 of them.
  48. tempo: How many frames each cel should last. The timer counts from 0 to this number, so there are really tempo + 1 frames per cel.
  49. Used to initialize animation timers. I am aware this is technically the exact opposite of tempo...lol
  50. cel ID: an offset into the cel pointer tables. An ID of 0 means to halt the animation and kill the sprite (anims loop by default)
  51.  
  52.  
  53. Use the cel ID as an offset into the CEL_PTR tables to get a cel pointer, which points to cel data.
  54.  
  55. Cel data is laid out as follows:
  56.  
  57. length, shape #, [tile #, attributes]+
  58.  
  59. length: How many tiles to draw
  60. shape #: An offset into the shape pointer tables.
  61. tile #: Now we're speaking PPU! This is a specific sprite tile for the PPU to draw. rm2 uses CHR RAM, so these values need to be coordinated, which is done when each
  62. scroll section is loaded.
  63. attributes: We're speaking PPU some more! These are written directly to OAM, so they specify palette, flipped-ness, and background priority.
  64.  
  65. The game needs to know the relative offset from the object's origin to draw each tile at. These are stored in "shape" sequences
  66. and referenced by the sprite shape pointers.
  67.  
  68. Use the shape number as an offset into the SPRITE_CEL_POS_PATTERN_PTR tables to get a shape pointer. The data actually starts at ptr + 2, so the offsets line up with the main cel data. (This isn't a waste of space. The data is back to back [citation needed]. You just subtract 2 from the pointer before storing it in the table).
  69.  
  70. Shape patterns are laid out as follows:
  71. [x offset, y offset]+
  72. x offset: horizontal offset from sprite's center. Inverted when sprite is flipped
  73. y offset: vertical offset from sprite's center
  74.  
  75. We're not done yet! The x offset needs to be inverted to draw a flipped sprite. The game uses the operation -(x + 8) to do this. Tile coordinates are top-left; think about x offset = 0 to convince yourself that we need to subtract an extra 8 here. The game uses a precomp table instead of actually doing this incredibly complex arithmetic, which I call TILE_OFFSET_SUBTRACTION_TABLE.
  76.  
  77. Now that we know what tile to draw and where to draw it, there are still a few pieces of data left to consider.
  78.  
  79. attribute override, priority override, spriteFlip
  80.  
  81. attribute override:
  82. If the attribute override is nonzero, the lower 4 bits of the base tile attributes are masked out, then that value is OR'd with the attribute override. In simpler terms, the attribute override lets you specify a different palette from the cel data, and set it to flipped (but not UN-flip it). I don't fully understand how this is used, as bits unused by the PPU are often set.
  83. priority override:
  84. On Airman's stage, everything needs to appear behind the background (priority = 1). The code checks if we're on Airman's stage and sets the priority bit on all tiles if so.
  85. sprite flip:
  86. Whether the sprite is flipped. This is applied after the attribute override, via an EOR.
  87.  
Add Comment
Please, Sign In to add comment