16
\$\begingroup\$

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?

\$\endgroup\$
9
  • 1
    \$\begingroup\$ 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? \$\endgroup\$ Jun 11, 2011 at 21:36
  • \$\begingroup\$ I think you need to post the part you are changing the velocity and exactly describe the problem and your need on the code. \$\endgroup\$
    – Ali1S232
    Jun 11, 2011 at 22:01
  • 2
    \$\begingroup\$ @Gajet what? He did describe the problem exactly. \$\endgroup\$ Jun 11, 2011 at 22:09
  • \$\begingroup\$ @maikSemder: i'm just curious about how he implemented physics engine to give solution based on his code. \$\endgroup\$
    – Ali1S232
    Jun 11, 2011 at 22:12
  • \$\begingroup\$ 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. \$\endgroup\$ Jun 11, 2011 at 22:14

1 Answer 1

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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .