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.

0voto

AShelly Punkte 33678

Ich glaube nicht, dass Ihre Gleichung für die Position richtig ist:

 b.at(i)->dVelocityY += (o.dGravity)/o.REFRESH;

Dies ist v=v0+gt - das scheint in Ordnung zu sein, obwohl ich schreiben würde dGravity*DT 代わりに dGravity/REFRESH_FREQ .

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

Aber das scheint nicht zu stimmen: Es ist äquivalent zu p = p0+v + 1/2gt^2 .

  • Sie sollten Geschwindigkeit * Zeit multiplizieren, um die richtigen Einheiten zu erhalten.
  • Sie skalieren den Gravitationsterm nach Pixel/Meter, aber nicht den Geschwindigkeitsterm. Das sollte also auch mit METER multipliziert werden
  • Sie haben die Wirkung der Schwerkraft bereits berücksichtigt, als Sie die Geschwindigkeit aktualisiert haben, so dass Sie den Schwerkraftterm nicht noch einmal hinzufügen müssen.

0voto

Danke für die schnellen Antworten!!! Sorry, ich hätte mich deutlicher ausdrücken sollen, die RunPhysics wird nach einer PeekMessage ausgeführt. Ich habe auch einen Frame-Limiter hinzugefügt, der dafür sorgt, dass nicht mehr Berechnungen pro Sekunde durchgeführt werden als die Bildwiederholrate des Monitors. Mein DT ist also 1 Sekunde geteilt durch die Bildwiederholrate. Vielleicht ist mein DT tatsächlich zu klein, um ihn zu berechnen, obwohl er ein doppelter Wert ist??? Mein Restitutionskoeffizient ist einstellbar, beginnt aber bei 0,9.

0 Stimmen

Der dt sollte funktionieren, wenn er zwischen 50 ms und 2 us liegt. Versuchen Sie, den Wert zu verringern, um Ihre Simulation genauer zu machen, aber mehr Rechenzeit zu benötigen. Wenn Sie Euler verwenden, wie Sie es tun, sollten Sie einen dt von 2-10 ms verwenden.

0voto

Macke Punkte 23640

Sie müssen Ihre Position beim Aufprall neu berechnen, um sicherzustellen, dass Sie von der richtigen Stelle an der Wand aufprallen.

D.h. den genauen Zeitpunkt des Abprallens bestimmen und die neue Geschwindigkeit/Position auf der Grundlage dieser Richtungsänderung berechnen (teilweise in einem "Frame" der Berechnung), um sicherzustellen, dass sich der Ball nicht bei jedem Abprall mehr und mehr über die Wände hinaus bewegt.

In Bezug auf den Zeitschritt sollten Sie sich folgende Informationen ansehen meine Antwort hier .

0 Stimmen

Mein Timer ist genau so :P je nach Aktualisierungsgrad des Monitors ändert sich das Zeitintervall. Und es stört mich nicht, wenn der Ball über die Wand hinausspringt, da es nur für eine so kurze Zeitspanne ist, und es sollte keinen Einfluss auf den Rückprall haben, aber der Punkt, an dem er abprallt (über die Wand hinaus), sollte durch das Senken der Geschwindigkeit wieder kompensiert werden

0 Stimmen

Haben Sie eine Obergrenze für Ihr dt? Zum Beispiel maximal 0,05s? Haben Sie versucht, Ihre Berechnungsschleife mehrmals pro Frame auszuführen? (z.B. 10 oder 50)

0voto

Adrian McCarthy Punkte 42872

Bei einer Starrkörpersimulation müssen Sie die Integration bis zum Zeitpunkt der Kollision durchführen, dann die Geschwindigkeiten anpassen, um ein Eindringen bei der Kollision zu vermeiden, und dann die Integration wieder aufnehmen. Es handelt sich um eine Art Momentaufnahme, um der Tatsache Rechnung zu tragen, dass Starrkörper nur eine Annäherung sind. (Ein echter Ball verformt sich bei einer Kollision. Das ist schwer zu modellieren und für die meisten Zwecke unnötig.)

Sie kombinieren diese beiden Schritte (Integration der Kräfte und Auflösung der Kollisionen). Für eine einfache Simulation wie die von Ihnen gezeigte reicht es wahrscheinlich aus, die Schwerkraft bei jeder Iteration zu überspringen, bei der Sie einen vertikalen Abprall behandelt haben.

In einer fortgeschrittenen Simulation würden Sie jedes Intervall (dt), das eine Kollision enthält, an der Stelle aufteilen, an der die Kollision tatsächlich auftritt. Integrieren Sie bis zur Kollision, lösen Sie dann die Kollision auf (durch Anpassung der Geschwindigkeit) und integrieren Sie dann für den Rest des Intervalls. Aber das scheint für Ihre Situation zu viel des Guten zu sein.

0 Stimmen

Danke, der Grund, warum ich Schwerkraft haben will, ist, dass mein Ziel ist, alles einstellbar zu halten, ich habe nichts dagegen, wenn es eine Annäherung ist, solange der Effekt zeigt. Könnten Sie vielleicht ein Beispiel zeigen, wie Sie die Flugbahn berechnen würden?

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