Recréer la physique de style rétro / NES avec une imprécision intentionnelle


16

Contexte:

J'ai un problème pour obtenir la courbe de saut correcte pour un projet de remake de plateforme rétro. Le jeu original est pour la NES, et la vitesse du joueur est stockée dans deux parties distinctes: un octet pour le nombre entier et un autre pour la partie fractionnaire.

La gravité est ajoutée à la vitesse Y du joueur à un taux de 0,25 / image.

Lorsque le joueur saute, sa vitesse Y est fixée à -4,64453125. Le reste de la courbe de saut est laissé à la gravité.

Au fur et à mesure que le joueur monte, sa vitesse verticale converge vers 0 à raison de 0,25 / image. Cependant, lorsque la vitesse du joueur atteint une valeur inférieure à zéro, la vitesse change suivant un modèle différent. Au lieu de diminuer régulièrement de 0,25 à chaque image, il suit ce modèle:

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

Il semble avoir quelque chose à voir avec un débordement d'entier.

Les données:

Voici un vidage des données de l'original. C'est un tableau de la vitesse.

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

Problème:

Dans mon jeu, je n'ai pas réussi à obtenir cet effet. Lorsque la vitesse est inférieure à zéro, elle continue de diminuer régulièrement de 0,25 plutôt que le schéma décrit ci-dessus. Plutôt que de stocker les parties entières et fractionnaires séparément, je les stocke ensemble dans un seul flotteur.

Comment obtenir cet effet?


1
Pour être honnête, je prendrais simplement des captures d'écran pour déterminer sa hauteur / longueur de saut maximale en pixels et je modifierais simplement votre fonction actuelle pour qu'elle ressemble le plus possible. Vous dites que l'imprécision est intentionnelle, donc cela ne devrait pas poser de problème?
Jonathan Connell

Je pense que vous devez poster la partie dont vous changez la vitesse et décrire exactement le problème et votre besoin sur le code.
Ali1S232

2
@Gajet quoi? Il a décrit le problème exactement.
Maik Semder

@maikSemder: je suis simplement curieux de savoir comment il a implémenté un moteur physique pour donner une solution basée sur son code.
Ali1S232

Faites-moi savoir si vous avez besoin de plus de détails, je ne voulais pas écrire un article gigantesque de peur que je reçoive des réponses tl; dr.
Zack The Human

Réponses:


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

Fondamentalement, il vous suffit de soustraire 64 lowpour soustraire 0,25, car une valeur de 8 bits peut avoir 256 valeurs, donc 256 * 0,25 = 64 Lorsqu'il y a un sous-dépassement, lowsoustrayez également 1 de high.

Avertissement: Ce code est intentionnellement erroné en ce qui concerne les nombres négatifs, il est censé modéliser les anomalies numériques décrites dans la question. Pour des raisons de comparaison, la mise en œuvre d'une classe de points négatifs appropriée pour la gestion des classes à virgule fixe se trouve au bas de cette réponse.

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 : J'ai également ajouté la conversion en float et de float et la sortie

La sortie générée est la même que dans votre tableau:

 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 revanche, cette classe de points fixes gère correctement les nombres négatifs:

#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;
    }
};

1
@Zack oui bien sûr, voir ma structure de position, j'ai ajouté une fonction de conversion en float qui fait exactement ça.
Maik Semder

1
@Zack a également ajouté une conversion fromFloat
Maik Semder

1
@Maik Vous monsieur, êtes un gentleman. Merci pour l'aide. Cela me remettra sur la bonne voie.
Zack The Human

1
@Zack vous êtes les bienvenus, heureux de vous aider, surtout pour une si belle question :)
Maik Semder

1
@Zack au cas où vous seriez intéressé, j'ai ajouté une classe à virgule fixe qui gère correctement les nombres négatifs pour comparaison
Maik Semder
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.