Subpixels
This game uses subpixels for its movement. This is how Link gets the movement speed of 2-1-2-1 on normal ground etc.
tl;dr: You have a "base speed" for any type of movement (0-4), and sometimes you get an extra pixel boost. If you move in the leftwards or upwards direction the pixel boost happens earlier (and in fact, it will always happen on the first frame of movement). This is why pumping is a thing.
How the coordinate and subpixel is updated during movement
Speed
See the table below for what speed Link has in different situations.
The speed values only applies to one axis, so for diagonal movement or movement into a slope, the total speed will be higher.
Movement | Where | Speed (hex) | Speed (pixels/frame) |
---|---|---|---|
Cardinal | Ground | $18 | 1.5 |
Diagonal | Ground | $10 | 1.0 |
Dashing | Ground | $40 | 4.0 |
Sword out, Cardinal | Ground | $14 | 1.25 |
Sword out, Diagonal | Ground | $0D | 0.8125 |
Holding object, Cardinal | Ground | $14 | 1.25 |
Holding object, Diagonal | Ground | $0D | 0.8125 |
Spinspeed, Cardinal | Ground | $40 | 4.0 |
Spinspeed, Diagonal | Ground | $2A | 2.625 |
Pushing statue, Cardinal | Ground | $0C | 0.75 |
Pushing statue, Diagonal | Ground | $10 | 0.75 |
Pulling statue | Ground | $04 | 0.25 |
Cardinal | Stairs | $0B | 0.6875 |
Diagonal | Stairs | $0B | 0.6875 |
Dashing | Stairs | $0A | 0.625 |
Sword out, Cardinal | Stairs | $0B | 0.6875 |
Sword out, Diagonal | Stairs | $0B | 0.6875 |
Holding object, Cardinal | Stairs | $0B | 0.6875 |
Holding object, Diagonal | Stairs | $0B | 0.6875 |
Spinspeed, Cardinal | Stairs | $0A | 0.625 |
Spinspeed, Diagonal | Stairs | $18 | 1.5 |
Cardinal | Shallow water/Grass | $14 | 1.25 |
Diagonal | Shallow water/Grass | $0D | 0.8125 |
Dashing | Shallow water/Grass | $30 | 3.0 |
Sword out, Cardinal | Shallow water/Grass | $10 | 1.0 |
Sword out, Diagonal | Shallow water/Grass | $08 | 0.5 |
Holding object, Cardinal | Shallow water/Grass | $14 | 1.25 |
Holding object, Diagonal | Shallow water/Grass | $0D | 0.8125 |
Spinspeed, Cardinal | Shallow water/Grass | $30 | 3.0 |
Spinspeed, Diagonal | Shallow water/Grass | $18 | 1.5 |
Cardinal | Slope | $10 | 1.0 |
Dashing | Slope | $2A | 2.625 |
Sword out, Cardinal | Slope | $0D | 0.8125 |
Holding object, Cardinal | Slope | $0D | 0.8125 |
Cardinal | Groud, Old man follower | $14 | 1.25 |
Diagonal | Groud, Old man follower | $0D | 0.8125 |
Dashing | Groud, Old man follower | $14 | 1.25 |
Sword out, Cardinal | Groud, Old man follower | $14 | 1.25 |
Sword out, Diagonal | Groud, Old man follower | $0D | 0.8125 |
Cardinal | Slope, Old man follower | $0D | 0.8125 |
Dashing | Slope, Old man follower | $0D | 0.8125 |
Sword out, Cardinal | Slope, Old man follower | $0D | 0.8125 |
- When walking leftwards or upwards, the speed is reversed ($18 becomes $E8)
- The most significant digit (1 in the case of walking on normal ground) is added to the coordinate directly, no matter what.
- The least significant digit (8 in the case of walking on normal ground) is shifted left four times (8 becomes $80), and added first to the subpixel. If this causes the subpixel to overflow/wrap, we add an additional pixel to the coordinate.
- Example: The speed value of 16 (or $10 in hex) represents a clean 1 pixel per frame. 17 ($11 in hex) would be 1 pixel per frame, with an additional pixel being added every 16th frame.
Subpixel
- The game will reset Link's subpixel whenever you change direction of movement. A movement after a standstill counts as changing the direction of movement.
- The game only looks at your dpad to find out if the direction changed. So if you walk upwards alongside a wall, and then press up and rightwards into the wall, it will treat this as a change of direction, even though Link is still moving upwards.
- This means that pumping into a wall will function as two pumps, both when you press (from ^ to ^>) and when you release (from ^> to ^).
- Starting/executing a dash will not reset the subpixel. But if you cancel the dash with a movement, it will be reset.
- If you however start a dash that ends up transitioning to another screen, the subpixel is not reset unless you start moving in a different direction on the next screen.
- Some items resets your subpixel and others do not.
* Does not reset subpixel: Using sword, boomerang, hammer, rod, hookshot, dropping a bomb or picking it up, does not reset your subpixel.
Example: Walking on ground (movement speed is $18)
- Downwards
- frame 1: Subpixel = 0x00 + 0x80 = 0x80. Coordinate = 0 + 1 + 0 = 1
- frame 2: Subpixel = 0x80 + 0x80 = 0x00. Coordinate = 1 + 1 + 1 = 3 (subpixel overflow)
- frame 3: Subpixel = 0x00 + 0x80 = 0x80. Coordinate = 3 + 1 + 0 = 4
- frame 4: Subpixel = 0x80 + 0x80 = 0x00. Coordinate = 4 + 1 + 1 = 6 (subpixel overflow)
- Etc
- Upwards
- frame 1: Subpixel = 0x00 - 0x80 = 0x80. Coordinate = 0 + 1 + 1 = 2 (subpixel overflow)
- frame 2: Subpixel = 0x80 - 0x80 = 0x00. Coordinate = 2 + 1 + 0 = 3
- Etc
Example: Walking on stairs (movement speed is $0B)
- Downwards
- frame 1: Subpixel = 0x00 + 0xB0 = 0xB0. Coordinate = 0 + 0 + 0 = 0
- frame 2: Subpixel = 0xB0 + 0xB0 = 0x60. Coordinate = 0 + 0 + 1 = 1 (subpixel overflow)
- frame 3: Subpixel = 0x60 + 0xB0 = 0x10. Coordinate = 1 + 0 + 1 = 2 (subpixel overflow)
- frame 4: Subpixel = 0x10 + 0xB0 = 0xC0. Coordinate = 2 + 0 + 0 = 2
- Etc
- Upwards
- frame 1: Subpixel = 0x00 - 0xB0 = 0x50. Coordinate = 0 + 0 + 1 = 1 (subpixel overflow)
- frame 2: Subpixel = 0x50 - 0xB0 = 0xA0. Coordinate = 1 + 0 + 1 = 2 (subpixel overflow)
- frame 3: Subpixel = 0xA0 - 0xB0 = 0xF0. Coordinate = 2 + 0 + 1 = 3 (subpixel overflow)
- frame 4: Subpixel = 0xF0 - 0xB0 = 0x40. Coordinate = 3 + 0 + 0 = 3
- Etc
How it affects slope dashes
Dashing on a slope is a fairly volatile thing when you frame advance through it. Link will move anything from 0 to 3 pixels in both the horizontal or vertical direction, seemingly at random. This is now understood better. The base speed is 2, and when the subpixel overflows you will get a 3 movement.
We're not completely sure why we get the 0 movements, but it looks like the game is trying to align Link with the slope. For example if you start your dash at the very first pixel you can start a slope dash, Link will move 1px/f in a perpendicular direction until he's properly aligned and can start the slope dash (until the normal slope dash speed will apply in both directions). This also happens sporadically mid-slopedash and is the cause for slowing down the dash.
In any event, the subpixel you have when a dash is started, will affect how the slopedash will end up working out. If it's close to overflowing, you might get an extra pixel boost immediately, which will completely change the coordinates Link travels through during the slope dash. Basically, even if you start your dash from the exact same coordinate, the slopedash can go fast or slow depending what the subpixel value is before the dash.
One way to make sure you get the same subpixel every time is to hit diagonal before starting the dash (Link would actually have to move diagonally for at least one frame for this to work.) This is because you 1. get a change in direction, so the subpixels are reset and 2. diagonal movement is a clean 1px/f so the subpixel will remain 0.
Bomb Clips
Since a criteria for performing a Bomb Clip is that you are positioned exactly below the explosion, your subpixel value will matter just as much as your coordinate when you start the slopedash, since different paths through the slope might or might not include the coordinate you need to visit.
Some setups works the same for all subpixels, in which case only the coordinate matters.