A Lifetime of HappiNES: Sprites, Collisions, Actions
Last time we finished up our static level screen.
Let’s add in our player next and let him stretch his legs a little.
Enter Stage Left
Our player is going to be much larger than 8px × 8px, so we’ll need to use multiple sprites.
For each direction our player can face, we’ll need a different arrangement of 16 sprites (4×4) to fully render him.
Let’s start by creating a mapping of tile indices that correspond with each of his possible states:
We’ll also reserve a chunk of OAM memory to hold all of the necessary sprite information.
We could just identify the player’s overall position by any one of his sprites (top-left, for instance), but let’s keep
track of state outside of OAM.
We’re finally ready to render him on the screen!
There are a few important things to point out here.
Firstly, we’re using sprite attributes to set two different palettes for our groom’s top half (first two rows) and bottom half (last two rows).
This is akin to how we set background attribute data for him in the static credits screen.
Secondly, it appears that we’re using multiplication in our code, and the NES CPU does not have any multiplication instructions!
We’re not using a fancy cartridge mapper like the MMC5 which came with its own 8-bit multiplier, and cc65 multiplication subroutines seem to always mess up my ROM, so I’m being very careful to ensure that all runtime multiplication that we do is by a power of two so that it can be accomplished by left shift instructions.
Lastly, we declared GROOM_FRONT as a 2D array, but here we’re getting a pointer to the first element (GROOM_FRONT) and then working with that as though it’s a 1D array.
For some reason, accessing it in the conventional C way to access 2D arrays results in generated assembly that does not work as desired.
Keep these last two points in mind as we work with 2D arrays and looping over them in nested loops.
Now that he’s on the screen, let’s move him around with controller input.
First, we’ll add some code to update the player’s state.
Next, we’ll want to update the sprites based on that state every frame.
This is the same code we used to initialize the player’s sprites, but we no longer need to worry about setting the palette attributes.
Our player can now move freely about the screen!
A little too freely, though.
Keeping our player inside the confines of the border we drew is as easy as enforcing min and max positions.
We’ll make use of our MIN_X, MIN_Y, MAX_X, MAX_Y constants that provide pixel boundaries for the screen as well as SPRITE_HEIGHT and SPRITE_WIDTH.
The top five rows of our screen are off limits, so we’ll make sure that player.y (the top of our player) is below (greater than) MIN_Y + SPRITE_HEIGHT * 5.
We can’t multiply by five at runtime, so let’s translate that to multiplying by the closest power of two (four) and then adding one more, or MIN_Y + SPRITE_HEIGHT * 4 + SPRITE_HEIGHT.
Only the bottom row of the screen is off limits, but we have to account for the fact that player.y is the top of our player and our player is four sprites tall, for a total of five SPRITE_HEIGHTs, which yields a very similar looking restriction.
We can extrapolate this to all of the boundaries:
Now our player can only move within the borders we drew in our background, but he can still stomp all over his bride-to-be!
And we’ll just backtrack state if moving our player would result in an overlap:
Our bride-to-be is now made of solid matter!
We can add the ring onto the level very similarly to how we initialized the player, but our ring won’t be moving (except to predetermined locations), so we don’t need to track its state the same way.
We’ll also want to make the ring solid, so let’s add it to our collision detection routine.
Let’s change the return type and add a little more info about which thing our player is colliding with to make things
easier later when we want to interact with them.
Conveniently, when used in an if condition, COLLISION_NONE is 0, which will evaluate as false, and the others values will all evaluate as true, so we can leave our MovePlayer code as is.
Now that the ring has a corporeal form, our player should be able to pick it up.
We want our player to be able to push the A button and pick up the ring if both:
The ring has not yet been picked up.
He is facing it and directly adjacent to it in the direction he is facing.
The first one is easy; we can just check if the ring’s position is still the same (ring_sprites.x == RING_START_X).
The second one is a little trickier.
First, we’ll determine which direction he is facing.
Then, we’ll pretend he moved one pixel in that direction and we’ll check if that causes him to collide with the ring.
Finally, we’ll undo that one pixel move.
If we determine that he satisfies those conditions, we’ll play a little sound effect and move the ring up into the inventory box.
With the ring in his grasp, our player’s final task is to propose.
A Modest Proposal
We’ve done all the hard work already; this is going to be very similar to interacting with the ring.
If our player is both facing and directly adjacent to the bride-to-be in the direction he is facing, and he has the
ring in his inventory, we’ll play the sound effect again, move the ring to the bride, and transition to the credit screen.
To make things even easier, we can ignore the possibility of our player being above or to the right of the bride-to-be since she’s right up against the border in the top-right corner.
We can’t forget to clean up our sprites so they don’t clog up the credits screen:
This sudden transition feels jarring and anti-climactic.
We only see the bride with her ring for a fraction of a second!
That’s all for now, but next time we’ll add a slow fade-to-black before we roll credits.