From Sonic Retro
The aim of this guide is to accurately describe the mechanics of classic Sonic games, while explaining the concepts well enough to allow for different ways to implement the same ideas. All that is described can be implemented in many different ways with the same or very similar results, and nothing needs to be done the exact way the Genesis hardware did.
Before we dive into how the game works, it is first important to know some basic attributes about how the games are structured which will apply to multiple aspects throughout this guide. This is relevant mostly for those looking for extremely close accuracy, and to provide context for some of the game's more subtle behaviour involving collision or motion.
The following variables/constants will be referenced frequently in this guide.
//Variables xpos: The X-coordinate of Sonic's center. ypos: The Y-coordinate of Sonic's center. xsp: The speed at which Sonic is moving horizontally. ysp: The speed at which Sonic is moving vertically. gsp: The speed at which Sonic is moving on the ground. slope: The current slope factor (slp) value being used. ang: Sonic's angle on the ground. //Grounded speed constants acc: 0.046875 ;acceleration dec: 0.5 ;deceleration frc: 0.046875 ;friction (same as acc) top: 6 ;top horizontal speed slp: 0.125 ;slope factors slprollup: 0.078125 slprolldown: 0.3125 fall: 2.5 ;tolerance ground speed for sticking to walls and ceilings //Jumping Constants air: 0.09375 ;air acceleration (2x acc) jmp: 6.5 ;jump force knxjmp: 6 ;knuckles' jump force grv: 0.21875 ;gravity
Game engines these days can effortlessly use real decimal values to move objects around and perform precise calculations. In the original Genesis games, they work a bit differently.
Pixel and Subpixel
Positions and Speeds in the Genesis games are in 2 parts, these being the pixel and the subpixel. The pixel is quite plainly what you would expect, it's what you would see on screen. Think of each pixel as a cell in a grid and this is which cell to look at. The subpixel, however, acts as the fractional part of the position.
Because decimal numbers aren't used, this subpixel actually a positive number up to 256.
Each pixel is effectively split into 256 slices along both axis and this means the finest fidelity available for movement is 1/256th of a pixel (aka 0.00390625). Think of this as where in that grid cell the real position currently is.
All decimal values touted in this guide can be translated into a subpixel amount simply by multiplying them by 256. Sonic's acceleration (acc) for example, is equal to 12 sub pixels. His gravity (grv) is 56 subpixels.
If, for example, Sonic's X pixel position is 50, and his X subpixel position is 48, the real/decimal X position is 50 + (subpixel / 256) which results in a final xpos of 50.1875. Even when the pixel portion is negative, the subpixel is always relative to the left within that pixel, and is added to the pixel position. So you can easily think of the pixel value to be the object's position, but floored (rounded down). This applies to all positions for any object.
Lastly, this exact same principle is applied to speeds, like an X speed or an acceleration, where they all have a pixel and subpixel component.
Decimal values can be used just fine to emulate this in a modern game engine, but keeping this pixel/subpixel mechanic in mind is important for absolute accuracy.
Degree angles are counterclockwise with 0 facing right (flat floor).
Different calculations may be needed if using the hex angle values.
Reference: Converting Hex Angles
The Mega Drive games use angles in hex, $00 through $FF, meaning that there are only 256 divisions of a circle, not 360 like we're used to. Worse, the direction is anti-clockwise compared to other languages like GML, so $20 isn't 45° like it should be - it's 315°.
In order to convert the original hex angles into angles you can use in GML, use this calculation:
real_angle = (256-hex_angle)*1.40625
Objects are the building blocks of each Sonic game. Sonic is an object, items are objects, and so on. Except for the Solid level terrain, that is.
Each game object follows the same or similar conventions. They all have X and Y positions, X and Y speeds, a width radius, a height radius, animation variables, an angle, and more.
For the most part, the width and height radius of an object describes it's solid size, so it's the box size if you can push against it or how big it is when touching solid tiles if it can move around. Or both, as the case may be.
Width and Height Radius
In a game with small sprites and pixel art, a pixel difference with worth acknowledging. A size in Sonic games isn't simply 2 times the radius.
A radius of 8 would end up being 17 px wide rather than 16, and the sensors work in much the same way. Making his pushing width radius of 10 become a total width of 21, not 20. This goes for all heights as well as all widths. This basically results in every collision mask, box, or sensor arrangement always being an odd number in overall size.
It's a matter of 1 pixel in width and height, but it could make all the difference in accuracy. To avoid confusion, both measurements will be mentioned (otherwise, refer to image examples).
The sizes used by characters will be referenced a lot by guides covering solid collision of all types.
Sonic's width radius we will call his Body Width Radius which is usually 9, and his height radius we will call his Body Height Radius which is usually 19. Sonic also has another radius that he uses for pushing against objects and tiles. We will call this his Push Radius which is always 10. This radius is implied rather than actually defined in-game, though it simply makes more sense to describe it as a constant.
These can change depending on Sonic's action, and how they behave will be described in Solid Tiles, as they are most relevant to those specifically.
The main thing to keep in mind with any collision is that it typically only concerns itself with any object's whole pixel position, totally ignoring any subpixel amount. So when you perform any collision calculations, you can emulate this by just comparing/testing/using a floored position instead of the real position.