Position
The game stores X and Y subpixel positions, each with a range of 256. Subpixel positions are only used to determine changes in pixel position when speed is not an integer multiple of 256 (and collisions with landscape?). Everything else, such as changes in velocity due to slope, the current angle of the slope, and collisions, are determined by pixel position. Of course, all of those things can be affected indirectly by the subpixel position because that affects the pixel position. The subpixel position is the sum of the character's speed across all previous frames in the current zone, mod 256. Unless directly monitored, this will appear as a source of random variation in the character's movement, and it will often do so regardless of being monitored because it is difficult to reach any specific subpixel position while continuing to accomplish anything in the game.
Because the game ignores subpixel position when determining collisions, pressing into a wall while apparently already stopped against it may in fact cause the character to accumulate speed for when the wall is cleared or removed, as well as moving slightly forward.
Pixel position is stored in a 2-byte variable, which can be overflowed or underflowed. Most levels have walls, or invisible barriers that don't allow the player to pass, or even reach, the 0 point on the x or y axis. However, if this can be overcome, by zipping (see terrain ejection) for instance, the player's position will immediately be set to 32767 along that axis, and the camera will begin scrolling to find the player.
Subpixel position can also be overflowed or underflowed. Normally this results in the player moving 1 pixel, but when pressing against a wall, simply resets the subpixel position.
Sprites
Sprite-based objects (spikes, switches, moving platforms, breakable walls, certain event triggers, etc.) that are not within a certain range of the camera are not rendered and cannot interact with the character in any way. Some objects use the timer for their position and thus continue moving even when they don't exist. Different types of objects seem to have different loading distances. Rings and monitors have the largest loading distances, and enemies with AIs have the smallest.
Event Triggers
Some events, such as ones that load bosses, trigger based on the camera position, and not the player position. If these events cause the camera to lock, and the player is not on-screen at the time, the player will be teleported to the nearest horizontal edge of the locked camera zone. Other events are placed in the level and activate based on the character's position, such as loading new graphics while passing through a loop or changing the active layer so that the player can pass behind part of the level.
Jumping
Jumping is a function of vector addition. When a character jumps, a vector of their jump height is added to their current x & y velocities at a 90 degree angle to the slope they are currently standing on.
During a jump, air drag occurs whenever the character has an (upward)
y velocity between 0 and 1024 subpixels (0-4 pixels), and happens at a rate of 1/32nd the current horizontal velocity per frame. So, for instance if you have a horizontal speed of 3072 subpixels (12 pixels) per frame at the start of a jump, once you start to reach its peak, you'll lose 96 subpixels the first frame, 93 the second, 90 the third, and so on, until your vertical speed reaches 0, or your horizontal speed is below 32 subpixels per frame.
Releasing the jump button when upward velocity is more than 1024 subpixels (3 & 5/32 pixels) per frame causes the upward velocity to drop to 968 subpixels per frame on the next frame. One frame's worth of airdrag will occur at the same time as the drop to 968 subpixels upward.
Holding the jump button when upward velocity is less than 1024 subpixels per frame has no effect on the game.
If the character was rolling before jumping, player control of left and right movement is disabled until landing.
Gravity
In free-fall, there is a constant downward acceleration of 56 subpixels (7/32 pixels) per frame per frame.
Rolling
Rolling is a form of movement which attacks nearby enemies, and can break through some walls, but causes the player to decelerate on a level surface.
Rolling allows the player to gain speed faster on downhill slopes than running does; however, rolling caps the players x-velocity at 4096 subpixels (16 pixels) per frame.
Interestingly, when on ground, both deceleration and y-velocity are based on the player's speed variable, not velocity, and the player's speed variable continues to increase as normal on a downhill, even when the x velocity cap has been reached. Rolling does not cap y velocity.
Sprite Ejection
If a player character becomes partially lodged in a sprite in a manner that does not cause the player damage, or destroy the sprite, the game begins an ejection routine. This ejection routine works by checking the lower left corner of the player's hitbox against the left edge of the sprite's hitbox, the lower right corner of the player's hitbox against the right edge of the sprite's hitbox, and the bottom edge of the player's hitbox against both the top edge and the bottom edge of the sprite's hitbox. If any of the points of the player's bounding box are close enough to the points checked on the sprite's hitbox, the player is ejected from the sprite in that direction. Up-down ejection takes priority over left-right ejection; however, the player must have some speed along the Y axis for up-down ejection to be tested. Ejection is accomplished by changing the relevant pixel position of the player so that the opposing edge of the player's hit-box is adjacent to the sprite, and reducing the player's speed and velocities to 0. IE: when the player is ejected to the left, the player will be positioned so that the right edge of his hitbox is directly touching the left edge of the sprite.
Sprites are "hollow" -- only the edges can be collided with. If, after the first frame, the game cannot determine which direction to eject the player, the player can walk around inside the sprite until he is near enough an edge to be ejected. Note that if the sprite is adjacent to another sprite, the player will collide with the other sprite's bounding box before he can be ejected from the sprite he is currently in. If the sprite is adjacent to (but not partially inside) terrain, the player will be ejected from the sprite before he can collide with the terrain.
Terrain Ejection
If a player character becomes partially lodged in terrain, there are two separate ejection mechanisms. First, it runs the same set of tests as sprite-based ejection. If that test fails, it checks to see if the player is holding left or right, and, if the player is holding left or right, it gives the player a variable speed (based on distance from the edge of the terrain tile) between 16 and 32 pixels per frame, in the direction opposite the one they are holding. This ejection routine (commonly known as zipping) will not trigger if the player is falling. Certain terrain tiles are hollow, and will trap a player rather than eject them. Terrain tiles may block, or not block any given direction of movement. Some tiles, for instance, block movement right, left, or upward, but not downward.
Ducking
Ducking is accomplished by holding down while standing still on the ground. Ducking causes the camera to pan downwards, and slightly decreases Sonic's hitbox.
Slope
Slope is stored as a 1 byte signed value (-128 ... 127). Negative slopes mean that the terrain rises to the right. Positive slopes mean that the terrain falls to the right. Players will lose speed traveling up any slope, and will be actively propelled downwards on slopes with absolute value of 16 or higher.
Slope seems to come in multiples of 2, which would indicate 128, or fewer, slope gradations, but this has yet to be verified.
Terrain Collision
Terrain will only act solid if the player is moving toward it: IE: floors only obstruct downward movement, ceilings only obstruct upward movement.
"Corner"ing
To save time, the collision engine only checks collisions from the player's midpoint to the edge of the hitbox in the directions that the player is moving. Therfore, while moving upward in a jump, you can pass through objects as long as the midpoint of your hitbox is above the top edge of the object's hitbox, similarly, you can pass through objects moving downward if your midpoint is below the bottom edge of the object's hitbox, rightward if your midpoint is further right than the right edge of the object, and leftward if your midpoint is further left than the left edge of the object. This also works with terrain collision, and can be abused in certain circumstances to embed the lower part of your hitbox in the floor.
Lag
In this game, there are occasional frames of lag during graphics or CPU intensive operations. However, unlike most games, objects, including the player and the camera, will still move during lag frames (although Sonic 1 generally does not update the screen during lag frames), but any applicable changes to their speed or state will not be applied until the next frame, and certain changes will be discarded entirely. As a result, lag management is largely unnecessary in Sonic games, and in many cases, lag can even be beneficial to a speed runner, as the player may get an extra frame without any deceleration applied. However, input on a lag frame is completely ignored, which makes it detrimental when zipping, spindashing, or when a jump or double jump is necessary.
In Sonic 1, the music is handled by the 68000 processor, rather than the z80, so lag is much more prevalent here than in the sequels. However, the game always catches up from music related lag the next frame, so the additional lag is almost strictly visual.
Start faster
When traveling at a speed of less than 128 subpixels per frame and braking, Sonic's speed is set to 128 subpixels per frame in the opposite direction. Holding left+right causes the game to think Sonic is traveling left at a speed of 0, and braking. This can shorten the process of acceleration from standstill by up to 10.75 frames (or 9.75 frames if accelerating leftward, as 1 frame of rightward motion is necessary first)
Loop jumping
Because the jump routine is a product of vector addition, jumping on the way up a loop (up-loop jumping) can result in a higher speed at the top of the loop than Sonic had at the bottom. Similarly, jumping on the way back down (down-loop jumping) can result in a higher speed than rolling normally would provide. Additionally, jumping from the curve at the bottom (out-loop jumping) can provide a much higher horizontal speed than Sonic would attain normally, but is generally only helpful if it does not cause Sonic to move upward at all.
Also, even if the speed is not enhanced, loop jumping results in Sonic traversing the loop in a straighter path, and may shave frames off because of that.
Loop breaking
If a loop has very thin sides, it may be possible to skip the loop entirely by jumping at the loop with a very high speed. Even if this results in a lower speed compared to traveling through the loop, this trick is likely to save approximately 30 frames whenever it can be performed, due to reducing redundant motion.