Recreando la física de estilo retro / NES con imprecisión intencional

16

Antecedentes:

Tengo un problema para obtener la curva de salto correcta para un proyecto de remake de plataformas retro mío. El juego original es para el NES, y la velocidad del jugador se almacena en dos partes separadas: un byte para el número entero y otro para la parte fraccionaria.

La gravedad se agrega a la velocidad Y del jugador a una velocidad de 0.25 / fotograma.

Cuando el jugador salta, su velocidad Y se establece en -4.64453125. El resto de la curva de salto se deja a la gravedad.

A medida que el jugador asciende, su velocidad vertical converge a 0 a una velocidad de 0.25 / cuadro. Sin embargo, cuando la velocidad del jugador alcanza un valor inferior a cero, la velocidad cambia siguiendo un patrón diferente. En lugar de disminuir constantemente en 0.25 cada cuadro, sigue este patrón:

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

Parece tener algo que ver con el desbordamiento de enteros.

Datos:

Aquí hay un volcado de los datos del original. Es una tabla de la velocidad.

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

Problema:

En mi juego no he podido lograr este efecto. Cuando la velocidad es menor que cero, continúa disminuyendo regularmente en 0.25 en lugar del patrón descrito anteriormente. En lugar de almacenar las partes enteras y fraccionarias por separado, las estoy almacenando juntas en un solo flotador.

¿Cómo se puede lograr este efecto?

Zack The Human
fuente
1
Para ser honesto, solo tomaría capturas de pantalla para calcular su altura / longitud máxima de salto en píxeles y simplemente ajustar su función actual para que se vea lo más parecida posible. ¿Dices que la imprecisión es intencional, por lo que esto no debería causar un problema?
Jonathan Connell
Creo que debe publicar la parte en la que está cambiando la velocidad y describir exactamente el problema y su necesidad en el código.
Ali1S232
2
@Gajet qué? Él describió el problema exactamente.
Maik Semder
@maikSemder: Tengo curiosidad por saber cómo implementó el motor de física para dar una solución basada en su código.
Ali1S232
Avíseme si necesita más detalles, no quería escribir una publicación gigantesca por temor a que recibiera respuestas.
Zack The Human

Respuestas:

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

Básicamente, solo tiene que restar 64 lowpara restar 0.25, porque un valor de 8 bits puede tener 256 valores, entonces 256 * 0.25 = 64 cuando hay un flujo inferior enlow restar también 1 high.

Descargo de responsabilidad: este código es intencionalmente incorrecto cuando se trata de números negativos, se supone que modela las anomalías numéricas descritas en la pregunta. Por razones de comparación, la implementación de una clase adecuada de manejo de números negativos se puede encontrar al final de esta respuesta.

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();
}

EDITAR : También agregué la conversión a flotante y de flotante y la salida

El resultado generado es el mismo que en su tabla:

 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

En contraste, esta clase de punto fijo maneja los números negativos correctamente:

#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;
    }
};
Maik Semder
fuente
1
@Zack sí, claro, vea mi estructura de posición, agregué una función de conversión a flotante que hace exactamente eso.
Maik Semder
1
@Zack también agregó una conversión fromFloat
Maik Semder
1
@Maik Usted señor, es un caballero. Gracias por la ayuda. Esto me volverá a encaminar.
Zack The Human
1
@Zack de nada, encantado de ayudar, especialmente para una pregunta tan bonita :)
Maik Semder
1
@Zack en caso de que esté interesado, agregué una clase de punto fijo que maneja los números negativos correctamente para la comparación
Maik Semder