3 Stimmen

Aufspringender Ball entspricht nicht dem Energieerhaltungssatz

Ich bin derzeit damit beschäftigt, eine kleine Ballphysik-Engine für meinen Programmierkurs in Win32 API und C++ zu schreiben. Ich habe den GDI-Backbuffer-Renderer und die gesamte GUI fertiggestellt (ein paar Dinge müssen noch angepasst werden), aber ich bin der Fertigstellung sehr nahe. Die einzigen großen Hindernisse, die noch bestehen, sind die Kollisionen zwischen den Bällen (aber das kann ich selbst beheben), aber das größte Problem von allen ist das Springen der Bälle. Was passiert, ist, dass ich einen Ball werfe und er wirklich fällt, aber sobald er abprallt, springt er höher als der Punkt, an dem ich ihn losgelassen habe??? das Lustige daran ist, dass es nur passiert, wenn er unter einer bestimmten Höhe ist. Dieser Teil ist der Physik-Code: (Wenn ihr mehr Code oder Erklärungen braucht, fragt bitte, aber ich würde es sehr schätzen, wenn ihr euch meinen Code ansehen könntet).

#void RunPhysics(OPTIONS &o, vector<BALL*> &b)
{ 
    UINT simspeed = o.iSimSpeed;
    DOUBLE  DT; //Delta T
    BOOL bounce; //for playing sound

    DT= 1/o.REFRESH;

    for(UINT i=0; i<b.size(); i++)
    {
        for(UINT k=0; k<simspeed; k++)
        {           
            bounce=false;

            //handle the X bounce
            if( b.at(i)->rBall.left <= 0 && b.at(i)->dVelocityX < 0 ) //ball bounces against the left wall
            {
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            else if( b.at(i)->rBall.right >= SCREEN_WIDTH && b.at(i)->dVelocityX > 0) //ball bounces against the right wall
            {           
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * -1 * b.at(i)->dBounceCof;
                bounce=true;
            }
            //handle the Y bounce
            if( b.at(i)->rBall.bottom >= SCREEN_HEIGHT && b.at(i)->dVelocityY > 0 ) //ball bounces against the left wall
            {
                //damping of the ball
                if(b.at(i)->dVelocityY < 2+o.dGravity/o.REFRESH)
                {
                    b.at(i)->dVelocityY = 0;
                }

                //decrease the Velocity of the ball according to the bouncecof
                b.at(i)->dVelocityY = b.at(i)->dVelocityY * -1*b.at(i)->dBounceCof;
                b.at(i)->dVelocityX = b.at(i)->dVelocityX * b.at(i)->dBounceCof;

                bounce=true;
            }

            //gravity
            b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;
            b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 
            //METER IS DEFINED GLOBALLY AS 100 which is the amount of pixels in a meter

            b.at(i)->pOrigin.x += b.at(i)->dVelocityX/o.REFRESH*METER; 

            b.at(i)->UpdateRect();
        }
    }
    return;
}

0 Stimmen

DT wird auf 1/o.REFRESH gesetzt und später in o.REFRESH*DT verwendet, also liegt entweder ein logischer Fehler vor, oder man kann es einfach entfernen.

4voto

TrayMan Punkte 6755

Sie verwenden die Eulersche Integrationsmethode. Es ist möglich, dass Ihr Zeitschritt (DT) zu groß ist. Außerdem scheint es einen Fehler in der Zeile zu geben, die die Y-Koordinate aktualisiert:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER; 

Da Sie die Schwerkraft bereits zur Geschwindigkeit addiert haben, brauchen Sie sie nicht zur Position zu addieren, und Sie multiplizieren die Geschwindigkeit nicht mit DT. Es sollte so sein:

  b.at(i)->pOrigin.y += b.at(i)->dVelocityY * DT; 

Außerdem scheint es eine gewisse Verwirrung bezüglich der Einheiten zu geben (die Art, wie METER verwendet wird).

0 Stimmen

Verwenden Sie niemals die Euler-Methode mit Kollisionsabprall. Berechnen Sie bei der Kollisionserkennung die Geschwindigkeit über die Impulserhaltung und die neue Position sofort neu.

0 Stimmen

Wenn ich deine Methode anwende, bewegt sich der Ball überhaupt nicht in y-Richtung, trotzdem danke

0 Stimmen

@Joshua: Ich persönlich habe (mit großem Erfolg) eine variable Zeitsegmentierung verwendet. Solange man nicht an die Grenzen der Berechnungsmöglichkeiten stößt, funktioniert das wunderbar.

1voto

Paul Sonier Punkte 37609

Okay, ein paar Dinge hier.

Sie haben unterschiedliche Codepfade für den Aufprall gegen die linke Wand und gegen die rechte Wand, aber der Code ist der gleiche. Kombinieren Sie diese Codepfade, da der Code derselbe ist.

Zu Ihrem grundsätzlichen Problem: Ich vermute, dass Ihr Problem von der Tatsache herrührt, dass Sie die Schwerkraft erst nach den Dämpfungs- bzw. Abprallkräften anwenden.

1voto

Dario Punkte 230

Wann rufen Sie RunPhysics auf? In einer Timer-Schleife? Dieser Code ist nur eine Annäherung und keine exakte Berechnung. In der kurzen Zeitspanne von delta t hat der Ball bereits seine Position und Geschwindigkeit ein wenig verändert, was in deinem Algorithmus nicht berücksichtigt wird und kleine Fehler produziert. Du musst die Zeit bis zum Auftreffen des Balls auf den Boden berechnen und die Änderungen vorhersagen.

Und die Schwerkraft ist bereits in der Geschwindigkeit enthalten, so dass sie hier nicht zweimal hinzugefügt werden muss:

b.at(i)->pOrigin.y += b.at(i)->dVelocityY + (1/2)*o.dGravity/o.REFRESH*DT*METER;

Im Übrigen: Speichern Sie b.at(i) in einer temporären Variablen, damit Sie es nicht in jeder Zeile neu berechnen müssen.

Ball* CurrentBall = b.at(i);

1voto

Rik Nauta Punkte 238

ANTWORT!!ANTWORT!!ANTWORT!! aber ich habe mein anderes Konto vergessen, also kann ich es nicht markieren :-(

Vielen Dank für all die tollen Antworten, sie haben mir wirklich sehr geholfen! Die Antworten, die Sie gegeben haben, waren in der Tat richtig, ein paar meiner Formeln waren falsch und einige Code-Optimierung könnte getan werden, aber keine war wirklich eine Lösung für das Problem. Also habe ich mich einfach mit einem Stück Papier hingesetzt und angefangen, jeden Wert, den ich von meinem Programm bekommen habe, von Hand auszurechnen, was mich ungefähr zwei Stunden gekostet hat :O Aber ich habe die Lösung für mein Problem gefunden: Das Problem ist, dass ich, wenn ich meine Geschwindigkeit aktualisiere (mit korrigiertem Code), einen dezimalen Wert erhalte, überhaupt kein Problem. Später erhöhe ich die Position in Y, indem ich die Geschwindigkeit mal das Delta T addiere, was ein sehr kleiner Wert ist. Das Ergebnis ist ein sehr kleiner Wert, der addiert werden muss. Das Problem ist nun, dass wenn man eine Elipse() in Win32 zeichnet, der Punkt ein LONG ist und somit alle dezimalen Werte verloren gehen. Das bedeutet, dass erst nach einer sehr langen Zeitspanne, wenn die Geschwindigkeitswerte beginnen, aus den Dezimalwerten herauszukommen, etwas passiert, und dass nebenbei, je höher man den Ball fallen lässt, desto besser die Ergebnisse (eines meiner Symptome) Die Lösung für dieses Problem war wirklich einfach, fügen Sie einen zusätzlichen DOUBLE-Wert zu meiner Ballklasse hinzu, der die wahre Position (einschließlich Dezimalwerte) meines Balls enthielt. Während des RenderFrame() nimmt man einfach den Boden- oder Deckenwert des Double, um die Elipse zu zeichnen, aber für alle Berechnungen verwendet man den Double-Wert. Nochmals vielen Dank für all eure Antworten, STACKOVERFLOW PEOPLE ROCK!!!

0voto

MickaelFM Punkte 887

Wenn Ihr dBounceCof > 1 ist, dann springt Ihr Ball höher. Uns liegen nicht alle Werte vor, um Ihre Frage beantworten zu können.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X