-
Notifications
You must be signed in to change notification settings - Fork 4
Jumping around (Part 8)
We have initial obstacle for player ready, but is the player ready himself? He seems to be way too much ready, able to roam around the screen freely. Let's add some more constraint to this to make the snowballs avoiding a bit more challenging task.
With hindsight of having the code already done (for the purpose of the example) - the player's logic is quite a bit of assembly code, so in this part we will focus only at running at platforms (resolving the player's collision against floor), jumping and falling, not tackling the ladder climbing logic yet.
We will reuse the GetPlatformPosUnder
routine used by snowballs to figure out if the player is standing at floor or falling down, the left/right movement itself will be actually really close to the free-roam version of code and we have to figure out and implement the whole mechanics of jumping and falling.
The jumps (and free-falling) in this game don't allow for any movement adjustments once the player is airborne, although the player can jump backward without the need to first face in the correct direction (we will even keep him facing backward for fun in such case). And any free fall from bigger height will be deadly in the final game, so we will already build in detection of long free fall (debug-displaying it as "freeze" of player for short time).
The player will respect play-field boundaries by simply clamping his position to the edge of designed area (if you know well the original arcade used as inspiration for this game, you may know the player will in certain situations rather bounce-back from the edge, but I decided to simplify this part of code to just clamp the position to the edge).
(here is a good spot to open the SpecBong.asm
and try to match the source with this text)
(you can also check the total difference between "Part 8" and "Part 7")
There are few lines added to modify initial player position to start near top of the screen, so we can fall from somewhere during testing.
Then almost whole Player1MoveByControls
routine is replaced with new code and that's it. Bye?
Or we can take a more detailed look on the new routine.
The "fire-cool-down" counter got promoted to "controls-cool-down" counter making the whole player sprite frozen in time and space when non-zero.
Then the position of the platform under the player is detected. This is not as straightforward as with snowball, just calling the GetPlatformPosUnder
, but it first shifts secretly player 3 pixels left (without any validation), gets the platform under there (let's say this is "under player's left foot"), then it secretly shifts player back to original position plus 3 pixels to right and calls the platform detection again (to test it "under player's right foot"). The original X position of player is restored finally, and the height of the higher platform of the two calls is preserved. The "flag" value is taken from the "right foot" in any case, as it will be sufficient for our purpose (it will do no harm even if the player is standing on edge of higher platform with left foot and the flag comes from "floor below" as we will use only the "ladder" flag, and you will be not near-enough the ladder if only the tile under left foot signals ladder - the floor tile data are adjusted to cover it well enough).
Then the jump/free-fall part of the routines comes, checking if the jump/fall counter Player1JumpIdx
is non-zero. If non zero, the jump/fall logic kicks in, using the counter as low-byte of jump table address and advancing the counter until it will reach 255 (staying at 255 for infinite time to keep free-falling until some floor is hit).
You may want to take a peek at the PlayerJumpYOfs
table, you will see there are sinus-like Y-coordinate deltas moving player sprite upward and back on a curve. If it will stay on the last value for 3 frames, the total amount of movement up and down will cancel out and player will reach the original spot, but also just starting using the tail of the table without beginning will produce "free fall"-like movement instead of jump.
Back to the code, so far it does use provided Player1JumpIdx
. That's because the jump/fall happens at one step per frame, so the code is written in a way to fetch the jump-index from variable, do single step, advance the index and continue with other stuff, not doing FOR
like loop driving whole jump or whole fall in one go.
So based on the jump-index variable the expected delta of movement is fetched into register E
from the table, to make the sprite go a bit up or down (or just hang a bit at the top of the curve).
Here comes small intermezzo, adjusting first the X coordinate of player in the direction of jump/fall (the player can't adjust the movement if not standing at the floor), that's done by calling routine .updateXPosAplusL
which adds register A
value to current sprite X position, sanitizes the result to stay inside the play-area and updates the sprite attribute with new coordinate. The horizontal movement happens actually only three frames out of four, creating average speed of movement 0.75px per frame, the TotalFrames
counter is used to detect frame at which the player does not move sideways.
Finally the Y coordinate of player is updated by the value from the jump table and compared to the platform height detected at beginning. If the platform height is same or above, the .didLand
code will take control. But otherwise the new y coordinate is written to player sprite and we are actually done with jump/fall logic completely, returning to main loop.
The .didLand
branch of code will be a bit more complex. First it will set player precisely at the platform, clear the jump-index to zero, modify the sprite pattern number to the "landing" sprite graphics and freeze the player for 4 frames as penalty for regular "soft landing". Then the landing height is compared with the remembered value of floor height counting as safe soft landing, and if the player is even below it, the deadly "hard landing" is done, for this moment just by extending the cool-down timer to freeze player for whole second (for testing purposes).
This was full code dealing with jump/falling processing (there is a bit more code dealing with start of jump/falling).
The code branching to .notInTableJump
continues to process player when there is no jumping/falling happening (yet).
It will first restore "landing" sprite pattern to regular one.
Then it checks if player is +-1px standing at the (higher) platform, aligning him precisely to it without any side-effect. But if the difference is more than +-1px, the player will enter free-fall logic by clearing "jump direction" (to fall just directly downward) and going back to the previously skipped logic of jump/fall, setting the jump-index into the table where the free-fall data start (doing +1 pixel fall initially), doing the first step of fall in this frame already.
After aligning the player with current floor position (at .almostAtPlatform
label), the tracking of "safe floor height to land to" is refreshed (as it is relative to the current stable position of player, the player can fall down further 18 pixels without killing himself from too big fall).
Finally the basic maintenance of player position is done and we will read user inputs and let him control the game.
If the fire button is pressed, the new jump is initialized by changing the sprite graphics to "jumping" pattern, setting Player1JumpDir
to -1/0/+1 value depending on the current state of controls, and setting the jump-index to the beginning of jump table (full jump arc including the going upward into the air part) and jumping back to the previous code doing the table-jump movement to already do the first step of table jump in current frame.
If there was no jump, the left/right inputs are checked, if both are not pressed the code just resets the sprite pattern to the "standing" one and exits.
If left or right inputs are pressed (left has higher priority), both do pretty similar thing. Reset or set the mirrorX flag in sprite attributes to face the correct direction, and either move by -1 or +1 pixel horizontally for three frames out of four (creating that 0.75px per frame movement speed at average), or during the fourth frame the sprite pattern is alternating between patterns 0..3 to create "running" animation of the player's sprite.
Build the NEX file and try it out, that was it. The player can now run around and jump, fall down from platforms and become frozen for a second if he falls from too high. The platform-high alignment will make the player sprite to traverse the small slope and stay "at" floor all the time. And those jumps, just watch that arc! So beautiful, almost perfect... ehm.. *cough* ... but you can't get to upper area of stage once you fall down, that seems like serious issue, so not so perfect. Yet...