# Recreating retro/NES style physics with intentional imprecision

Background:

I'm having a problem getting the jump curve correct for a retro platformer remake project of mine. The original game is for the NES, and the player's velocity is stored in two separate parts: one byte for the whole number and another for the fractional part.

Gravity is added to the player's Y speed at a rate of 0.25/frame.

When the player jumps, his Y speed is set to -4.64453125. The rest of the jump curve is left to gravity.

As the player ascends his vertical velocity converges to 0 at a rate of 0.25/frame. When the player's velocity reaches a value less than zero, however, the velocity changes following a different pattern. Instead of decreasing steadily by 0.25 every frame, it follows this pattern:

[1.75, -0.25, -0.25, -0.25, 1.75, -0.25, -0.25, -0.25, 1.75, ...]


It appears to have something to do with integer overflow.

Data:

Here is a dump of the data from the original. It's a table of the velocity.

Jump Curve

Y-Hi Y-Lo    Decimal        Change/Frame
4    165     4.64453125     ?
4    101     4.39453125     -0.25
4    37      4.14453125     -0.25
3    229     3.89453125     -0.25
3    165     3.64453125     -0.25
3    101     3.39453125     -0.25
3    37      3.14453125     -0.25
2    229     2.89453125     -0.25
2    165     2.64453125     -0.25
2    101     2.39453125     -0.25
2    37      2.14453125     -0.25
1    229     1.89453125     -0.25
1    165     1.64453125     -0.25
1    101     1.39453125     -0.25
1    37      1.14453125     -0.25
0    229     0.89453125     -0.25
0    165     0.64453125     -0.25
0    101     0.39453125     -0.25
0    37      0.14453125     -0.25
-1   229     -1.89453125    1.75
-1   165     -1.64453125    -0.25
-1   101     -1.39453125    -0.25
-1   37      -1.14453125    -0.25
-2   229     -2.89453125    1.75
-2   165     -2.64453125    -0.25
-2   101     -2.39453125    -0.25
-2   37      -2.14453125    -0.25
-3   229     -3.89453125    1.75
-3   165     -3.64453125    -0.25
-3   101     -3.39453125    -0.25
-3   37      -3.14453125    -0.25
-4   229     -4.89453125    1.75
-4   165     -4.64453125    -0.25
-4   101     -4.39453125    -0.25
-4   37      -4.14453125    -0.25
-5   229     -5.89453125    1.75
-5   165     -5.64453125    -0.25
-5   101     -5.39453125    -0.25
-5   37      -5.14453125    -0.25
-6   229     -6.89453125    1.75


Problem:

In my game I haven't been able to achieve this effect. When the velocity is less than zero it continues to decrease regularly by 0.25 rather than the pattern described above. Rather than storing the whole and fractional parts separately, I am storing them together in a single float.

How can this effect be achieved?

• To be honest I'd just take screenshots to work out his max jump height / length in pixels and just tweak your current function to look as like it as possible. You say the imprecision is intentional, so this shouldn't cause a problem? Jun 11 '11 at 21:36
• I think you need to post the part you are changing the velocity and exactly describe the problem and your need on the code. Jun 11 '11 at 22:01
• @Gajet what? He did describe the problem exactly. Jun 11 '11 at 22:09
• @maikSemder: i'm just curious about how he implemented physics engine to give solution based on his code. Jun 11 '11 at 22:12
• Let me know if you need more details, I didn't want to write a gigantic post for fear I'll get tl;dr responses. Jun 11 '11 at 22:14

one byte for the whole number and another for the fractional part


Basically you just have to subtract 64 from low in order to subtract 0.25, because an 8 bit value can have 256 values, so 256 * 0.25 = 64 When there is an underflow in low also subtract 1 from high.

Disclaimer: This code is intentionally wrong when it comes to negative numbers, it is supposed to model the numerical anomalies described in the question. For comparison reasons the implementation of a proper negative numbers handling fixed point class can be found at the bottom of this answer.

struct velocity
{
char high;
unsigned char low;

// fall -0.25
void fall()
{
if(low < 64) --high;
low -= 64;;
}

// convert to a float
float toFloat() const
{
float ret = high;
float frac = (float)low / 256.0f;
if(high >= 0) ret += frac;
else ret -= frac;
return ret;
}

// convert from float
void fromFloat(float f)
{
high = (char)f;
float frac = f - high;
low = (unsigned char)(frac * 256.0f);
}
};

velocity v;
v.high = 4;
v.low = 165;
for(int i = 0; i < 30; ++i)
{
printf("%2d     %3d   %f\n", v.high, v.low, v.toFloat());
v.fall();
}


EDIT: I also added the conversion to float and from float and the output

The generated output is the same as in your table:

 4     165   4.644531
4     101   4.394531
4      37   4.144531
3     229   3.894531
3     165   3.644531
3     101   3.394531
3      37   3.144531
2     229   2.894531
2     165   2.644531
2     101   2.394531
2      37   2.144531
1     229   1.894531
1     165   1.644531
1     101   1.394531
1      37   1.144531
0     229   0.894531
0     165   0.644531
0     101   0.394531
0      37   0.144531
-1     229   -1.894531
-1     165   -1.644531
-1     101   -1.394531
-1      37   -1.144531
-2     229   -2.894531
-2     165   -2.644531
-2     101   -2.394531
-2      37   -2.144531
-3     229   -3.894531
-3     165   -3.644531
-3     101   -3.394531
-3      37   -3.144531
-4     229   -4.894531
-4     165   -4.644531
-4     101   -4.394531
-4      37   -4.144531
-5     229   -5.894531
-5     165   -5.644531
-5     101   -5.394531
-5      37   -5.144531
-6     229   -6.894531


In contrast this fixed point class handles the negative numbers properly:

#include <iomanip>
#include <iostream>

struct fixed_point
{
union
{
struct
{
unsigned char low;
signed char high;
};
short s;
};

float toFloat() const
{
fixed_point tmp;
if(high < 0) tmp.s = ~s;
else tmp.s = s;

float ret = tmp.high;
float frac = (float)tmp.low / 256.0f;
ret += frac;
if(high < 0) ret = 0 - ret;
return ret;
}

void fromFloat(float f)
{
float tmp;
if(f < 0.0f) tmp = -f;
else tmp = f;

high = (char)tmp;
float frac = tmp - high;
low = (unsigned char)(frac * 256.0f);

if(f < 0.0f) s = ~s;
}

fixed_point operator+(const fixed_point &fp) const
{
fixed_point ret;
ret.s = s + fp.s;
return ret;
}

fixed_point operator-(const fixed_point &fp) const
{
fixed_point ret;
ret.s = s - fp.s;
return ret;
}

void print(const char *msg) const
{
std::cout << msg << ":" << std::endl;
std::cout << std::hex << std::uppercase;
// cout'ing the hex value for a char is kind of a pain ..
unsigned int _high = 0;
memcpy(&_high, &high, 1);
std::cout << "  high : 0x" << std::setfill('0') << std::setw(2) << _high << std::endl;
unsigned int _low = 0;
memcpy(&_low, &low, 1);
std::cout << "  low  : 0x" << std::setfill('0') << std::setw(2) << _low << std::endl;
std::cout << "  all  : 0x" << std::setfill('0') << std::setw(4) << s << std::endl;
std::cout << "  float: " << toFloat() << std::endl;
std::cout << std::endl;
}
};

• @Zack yes sure, see my position structure, I added a conversion to float function which does exactly that. Jun 11 '11 at 22:35
• @Zack also added a fromFloat conversion Jun 11 '11 at 22:40
• @Maik You sir, are a gentleman. Thanks for the help. This will get me back on track. Jun 11 '11 at 23:38
• @Zack you're very welcome, glad to help, especially for such a nice question :) Jun 11 '11 at 23:40
• @Zack in case you are interested, I added a fixed point class that handles the negative numbers properly for comparison Jun 12 '11 at 8:49