Actions

SPG:Calculations

From Sonic Retro

Sonic Physics Guide
Collision
Physics
Gameplay
Presentation
Special

Notes:

  • Research applies to all four of the Mega Drive games, and Sonic CD.
  • If you are not interested in making your Sonic fan game or framework as close to pinpoint accurate as possible (as in, to an almost impossible to notice degree), this page may be less useful for you. Rather than describe gameplay behaviour, it goes into the specific maths the original games used within it's various limitations to perform actions that modern game engines do automatically.

Introduction

A lot of the information on this guide can be used with very high accuracy using the basic calculation functions featured in modern game engines. However, if your goal is 100% accuracy, you may want to use the original methods, routines, and calculations from the original game code directly.

This page will document all the specifics about how the original games process data, that may differ from today's game engines and their out of the box math functions and variable types.

Positions And Speeds

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, the integer or whole-number portion (or the value, floored), 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 true decimal numbers aren't used, this subpixel actually a positive number up to 256.

Note: A subpixel speed can be negative, but for positions, the subpixel portion always represents a positive amount of space from the left or top of the pixel.

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.

SPGSubpixels.pngA single pixel in the classic Sonic games. Each square here is a subpixel

All decimal values touted in this guide can be translated into a subpixel amount simply by multiplying them by 256. The Player's acceleration (acceleration_speed) for example, is equal to 12 sub pixels. Their gravity (gravity_force) is 56 subpixels.

If, for example, The Player's X pixel position is 50, and their X subpixel position is 48, the real/decimal X position is 50 + (subpixel / 256) which results in a final X Position 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/or subpixel component. If the value, like acceleration or gravity for example, is less than 1 pixel per frame then it does not need to have a pixel component, of course, and is just the subpixel amount as a single variable.

Using Decimals

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.

Subpixel to Decimal Conversion

These are not functions the original game uses (the original game never uses decimal), but they may come in handy for modern devs using this guide.

function subpixel_to_decimal(pixel, subpixel)
{
	return pixel + (subpixel / 256);
}

Angles

Hex Angles

The Mega Drive games use angles in hex, 0 through 255, meaning that there are only 256 divisions of a circle, not 360 like we're used to. In addition, there are no fractional angles between these values.

Note: In this case, 256 would be the same angle as 0, much like an angle of 360 is the same as 0.

Throughout the guides, angles will be presented in the following format: Approximate Degrees° (Hex Angle)

Not only this, the original game's angles are clockwise while the degrees shown are counter-clockwise (in order to be compatible with common game engines), so be aware of this when reading any angle other than the Degree conversions.

Using Degrees

Degrees are fine to use and will give you accuracy beyond what most people could notice. However, if you want absolute pinpoint accuracy to the originals, you would need to create and use your own angle system using Hex angles and you'll have to also use the trigonometry functions from the original games

Simply be aware that the degree values presented at any point on this guide are approximate only.

Angle Functions

Because the games use their own format for angles, they of course need their own functions to calculate with them.

Angle Ranges

It's noted under most angle ranges on this guide that they are "asymmetrical". This is due to the calculations used upon the Hex Angles, and the lack of fractional angles.

In the example of getting the air direction: Here, the angle is being split into 90 degree "sectors" which represent up, down, left, and right. So for example, if the Player's movement angle through the air is pointing more to the right than up or down, the game needs to know. The game first subtracts 32 from the hex angle. This is the equivalent of 45 degrees, half the size of each sector and will "rotate" the sector ranges by 45 degrees, centring them on each cardinal direction. It will then perform a bitwise AND operation, like so: new_range_angle = (angle - 32) & 192.

This calculation returns a different angle value for each 90 degree sector, which the game can test and differentiate between.

The reason these ranges tend to end up asymmetrical with this calculation, is that the angle at exactly 45 degrees between each sector cannot be shared between sectors (it has to belong to one or the other), so without extra work, every range is effectively rotated by 1 hex angle.

Sine and Cosine

To perform sine and cosine calculations fast, the game stores a list of values that can be referenced later. The values are the following:

[0,6,12,18,25,31,37,43,49,56,62,68,74,80,86,92,97,103,109,115,120,126,131,136,142,147,152,157,162,167,171,176,181,185,189,193,197,201,205,209,212,216,219,222,225,228,231,234,236,238,241,243,244,246,248,249,251,252,253,254,254,255,255,255,
256,255,255,255,254,254,253,252,251,249,248,246,244,243,241,238,236,234,231,228,225,222,219,216,212,209,205,201,197,193,189,185,181,176,171,167,162,157,152,147,142,136,131,126,120,115,109,103,97,92,86,80,74,68,62,56,49,43,37,31,25,18,12,6,
0,-6,-12,-18,-25,-31,-37,-43,-49,-56,-62,-68,-74,-80,-86,-92,-97,-103,-109,-115,-120,-126,-131,-136,-142,-147,-152,-157,-162,-167,-171,-176,-181,-185,-189,-193,-197,-201,-205,-209,-212,-216,-219,-222,-225,-228,-231,-234,-236,-238,-241,-243,-244,-246,-248,-249,-251,-252,-253,-254,-254,-255,-255,-255,
-256,-255,-255,-255,-254,-254,-253,-252,-251,-249,-248,-246,-244,-243,-241,-238,-236,-234,-231,-228,-225,-222,-219,-216,-212,-209,-205,-201,-197,-193,-189,-185,-181,-176,-171,-167,-162,-157,-152,-147,-142,-136,-131,-126,-120,-115,-109,-103,-97,-92,-86,-80,-74,-68,-62,-56,-49,-43,-37,-31,-25,-18,-12,-6]

We'll call this list SINCOSLIST[..] when used within functions.

There's also another list of hex angles used during certain calculations:

[0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,13,13,13,13,13,13,13,14,14,14,14,14,14,14,15,15,15,15,15,15,15,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,0]

We'll call this list ANGLELIST[..] when used within functions.

Sine and Cosine Functions

These functions return a sine or cosine, taking a single hex angle as an parameter.

//Returns a sine value from -256 to 255 (this is done for precision in the original game, divide the result by 256 to get a typical -1 to 1 decimal result)
function angle_hex_sin(hex_ang)
{
	var list_index = hex_ang mod 256;
	return SINCOSLIST[list_index];
}
//Returns a cosine value from -256 to 255 (this is done for precision in the original game, divide the result by 256 to get a typical -1 to 1 decimal result)
function angle_hex_cos(hex_ang)
{
	var list_index = (hex_ang + 64) mod 256;
	return SINCOSLIST[list_index];
}

Misc

Here are various trigonometry functions the original game implements to perform calculations on angles, or to find angles.

// Returns a hex angle representing a direction from one point to another. 
//Effectively these points are represented by [0, 0] and [xdist, ydist]
function angle_hex_point_direction(xdist, ydist)
{
	// Default
	if (xdist == 0) and (ydist == 0) return 64;
		
	// Force positive
	var xx = absolute(xdist);
	var yy = absolute(ydist);
		
	var angle = 0;
		
	// Get initial angle
	if yy >= xx
	{
		var compare = (xx * 256) div yy;
		angle = 64 - ANGLELIST[compare];
	}
	else
	{
		var compare = (yy * 256) div xx;
		angle = ANGLELIST[compare];
	}
		
	// Check angle
	if xdist <= 0
	{
		angle = -angle;
		angle += 128;
	}
		
	if ydist <= 0
	{
		angle = -angle
		angle += 256;
	}
		
	return angle;
}

Hex to Degree Conversion

Of course, if for some reason you only have access to the Hex angle, the 256 slices will not help you if you desire using degrees (or require degrees for final onscreen drawing for example).

Additionally, the direction of angles in these classic games is clockwise, unlike other languages such as GML, so a hex angle of 32 isn't 45° like you'd expect it to be - it's 315°.

In order to convert the original clockwise hex angles into counter clockwise degree angles you can use in Game Maker, use the following functions. These are not functions the original game uses (the original game never uses degrees), but they may come in handy for modern developers using this guide.

// Returns a degree angle converted from a hex angle
function angle_hex_to_degrees(hex_ang)
{
	return ((256 - hex_ang) / 256) * 360;
}
// Returns a hex angle converted from a degree angle
function angle_degrees_to_hex(deg_ang)
{
	return floor(((360 - deg_ang) / 360) * 256);
}