![]() |
Geschwindigkeit bei bewegten Objekten
Hi,
Ich habe jetzt schon des öfteren kleine Spiele programmiert, aber ich hatte jedesmal Probleme wenn es darum ging verschiedene Geschwindigkeiten darzustellen. Jetzt habe ich wieder das Problem. Ich habe ein relativ simples Programm: Es gibt einen Ball, und diesem kann man mit einer bestimmten Kraft in eine beliebige Richtung "werfen". Mithilfe von Vektoren kann ich den Einfluss der Gravitation und des Windes beobachten. Soweit ist das kein Problem. Aber jetzt geht es darum einen Geschwindigkeits Faktor einzubauen. Hier hab ich jetzt einfach die Länge (oder den Betrag) des Vektors genommen, und ihn auf die Anzahl der Pixel übertragen, die sich der Ball in diesem Timer durchlauf bewegt. Funktionieren tut das ohne Frage, nur es gibt 2 Nachteile: 1. Es sieht doof aus ^^. Der Ball macht einfach zu große Sprünge (10-30 Pixel) 2. Kollisionsabfrage wird schwierig, da der Ball nicht genau auf Objekte trifft, sondern quasi direkt in sie hinein, da die Sprünge eben so groß sind. Eine andere Möglichkeit wäre, die Zeit zwisachen den Timer Aufrufen dynamisch an die Geschwindigkeit anzupassen. Aber hier gibt es dann schnell eine untere Grenze, und es ist stark von der Belastung des PC`s abhängig wie schnell sich der Ball bewegt. Hat vielleicht jemand eine Idee wie man soetwas elegant lösen könnte? Vielen Dank im vorraus ;) |
Re: Geschwindigkeit bei bewegten Objekten
Wo mit bewegst du denn das Objekt? Über einen einfachen Timer?
Ich denke du solltest einfach das Timer-Intervall erhöhen, so dass kleinere Bewegungen pro Intervall ausreichen. Ich habe für sowas immer das OnIdle benutzt; es wird so oft wie es dem Rechner möglich ist ausgeführt. Dann kannst du die Koordinaten des Objektes in kleinen Schritten vergrößern oder verkleinern und das ganze sollte flüßig laufen. Also statt OnTimer (oder was auch immer du verwendet hast):
Delphi-Quellcode:
in das OnCreate und dann den zu wiederholenden Code in eine Methode mit Namen 'procedure1'.
Application.OnIdle := procedure1;
Auf DelphiGL.com gibt es einen Bomberman Tutorial, dort steht etwas über 'Timebased Movement', dass du dir mal durchlesen solltest. Der Nachteil vom OnIdle ist nämlich, dass es von der Rechenleistung abhängt, wie oft es ausgeführt wird und das Programm dann auf schnellen Rechner möglicherweise zu schnell ausgeführt wird. Dort ist beschrieben, wie du das umgehen kannst. |
Re: Geschwindigkeit bei bewegten Objekten
Hi!
Ich würde das timer-Intervall nicht ändern, sondern immer bei 13 ms lassen (ca. 75 Hz). Dann kann man große Sprünge auch nicht wahrnehmen, wenn es sie denn geben sollte. Zur Kollisionsabfrage: Deine OnTimer-Procedure sieht wahrscheinlich ungefähr so aus:
Delphi-Quellcode:
wobei der Vektor dann die die Geschwindigkeit ist. Dabei fliegt der Ball aber, wie bereits von dir erwähnt evtl. direkt ins Objekt hinein. Mach's doch stattdessen so:
Ball.Left := Ball.Left + Vektor;
Kollisionsabfrage;
Delphi-Quellcode:
Dann fliegt der Ball nämlich nur so lange, bis es eine Kollision gibt.
For i := 1 to Vektor do begin
Ball.Left := Ball Left + 1; Kollisionsabfrage; If Kollision then Break; end; |
Re: Geschwindigkeit bei bewegten Objekten
danke euch beiden. Dieses Time Based Movement habe ich mir mal angeschaut. Scheint vielversprechend. Auch das OnIdle Event kannte ich noch nicht. Danke :)
Zur Kollisionsabfrage: gute Idee. Ich denke mal so wirds klappen, danke :) |
Re: Geschwindigkeit bei bewegten Objekten
Hallo Antigo
Welchen Typ haben deine Vektorenelemtene? Ganzzahl? Dann war das die falsche Wahl. Wenn du Gleitkommazahlen fuer die Vektoren nimmst, dann sollte es eigentlich keine Schwierigkeiten bezueglich Genauigkeiten geben. Was die Kollisionserkennung betrifft: Ich wuerde in folgender Reihenfolge vorgehen:
greetz Mike |
Re: Geschwindigkeit bei bewegten Objekten
Ähm meine Vektorelemente sind in der Tat Integer. Und es funktioniert auch nicht so wie es soll, da er durch Rundungen so wie es aussieht einfach immer einen Pixel vorgeht. Jedenfalls ist es so, das der Ball sich anders verhält wenn ich das Timer Interval ändere, und genau das sollte beim Timebased Movement ja nicht der Fall sein.
Optimal wäre es wohl wenn ich die komplette Flugbahn des Balles schon kennen würde und dann beim Timer durchlauf nur gucken müsste in welchem Zeitindex ich mich befinde und die dazugehörige Position raussuchen würde. Dann würde das Timer Intervall wirklich keinen EInfluss mehr haben, und es sollte auch nicht mehr ruckeln, aber ich weiss nicht wie ich das bewerkstelligen sollte... Zurück zum vektor :D inwiefern würden Gleitkommazahlen da helfen? Zur Kollisionsabfrage: DIe Möglichkeit, wäre quasi andersrum. Ist vielleicht ressourcenschonender, weil man nicht immer in Einzelschritten vorgeht. Naja mal sehen, erstmal muss der Ball vernünftig fliegen ;) Vielen dank schonmal :) |
Re: Geschwindigkeit bei bewegten Objekten
Zitat:
![]() Zitat:
Bei der Verwendung von Floats wuerde es nach 1 Sekunde an der richtigen Stelle befinden, naemlich 5 Pixel weiter ;) greetz Mike |
Re: Geschwindigkeit bei bewegten Objekten
Zitat:
Zitat:
danke nochmal |
Re: Geschwindigkeit bei bewegten Objekten
ok es kann gar nicht funktionieren mit diesem Time Based Movement :/
Ich habe es so geregelt, dass bei jedem mal, wenn der Ball bewegt wird, seine Flugbahn direkt mit der Gravitation und dem Wind verrechnet wird:
Delphi-Quellcode:
Jetzt ist es natürlich so, dass die Flugbahn anders verläuft, wenn der Vektor des Balles öfters mit den beiden anderen verrechnet wird. Deshalb ist es immer noch so das der Ball bei Timer Intervall 1 anders fliegt als bei Timer Intervall 50 :(
var
Gravitation,Wind:TVektor; Ball: TBall; ... ... procedure TForm1.Timer1Timer(Sender: TObject); ... Ball.vektor.addieren(Gravitation); Ball.vektor.addieren(wind); ... //Time Based Movement time:=gettickcount()-timestamp; ball.bewegen((5*ball.vektor.gibLaenge/10)*time/1000); Vielleicht hat ja jemand einen Ansatz wie man das lösen kann. Ich hab schon an sowas gedacht:
Delphi-Quellcode:
Also das nur bei jedem vollen Pixel die Flugbahn verändert wird, aber das haut auch nicht hin. Denn bei Timer Intervall 50 können ja auch mehrere Pixel gegangen worden sein, und trotzdem nur eine einzige Flugbahn anpasung vorgenommen worden sein.
if ball.x mod 1 = 0 then begin
Ball.vektor.addieren(Gravitation); Ball.vektor.addieren(wind); end; Ich hoffe da blickt jetzt noch jemand durch ;) |
Re: Geschwindigkeit bei bewegten Objekten
Zitat:
Code:
sondern es muss auch der Wind noch miteinbezogen werden:
v0 * t * cos(b)
Code:
wobei vw der Vektor des Windes ist und c ein Faktor, mit dem die Kraft des Windes an dem Objekt wirkt. (Siehe dazu
xy(t) = v0 * t * cos(b) + vw * t * c
= t * (v0 * cos(b) + vw * c) ![]() Zitat:
Zitat:
Code:
So sieht das aus im Vergleich, wenn du einmal Position und Pixel als Integer speicherst, und einmal, wenn du Position als floats und Pixel als integers speicherst.
Schritt Position/Pixel (float) Position/Pixel (int)
1 0 / 0 0 / 0 (+0.25) 2 0.25 / 0 0 / 0 (+0.25) 3 0.50 / 0 0 / 0 (+0.25) .... 6 1.25 / 1 0 / 0 (+0.25) 7 1.50 / 1 0 / 0 (+0.25) .... 9 2.00 / 2 0 / 0 (+0.25) 10 2.25 / 2 0 / 0 (+0.25) ect. Zitat:
Delphi-Quellcode:
dt ist die wichtige Variable. Diese gibt dir in Millisekunden an, wieviel Zeit seit dem letzten Tick vergangen ist. Wie du sie anwendest: hier mal ein kleines Beispiel:
var
LastTick: Integer; //... procedure TForm1.OnTimer(Sender: TObject); var CurrentTick, dt: Integer; begin CurrentTick := GetTickCount(); dt := CurrentTick - LastTick; //dt = Delta T, die Zeitdifferenz zwischen dem letzten Tick und dem derzeitigen //hier dann dein Verarbeitungscode LastTick := CurrentTick; end;
Delphi-Quellcode:
Damit sollte das Timebased Movement korrekt implementiert sein.
procedure PhysicTick(dt: real); //dt, aber in Sekunden! (angenehmer fuer physische Berechnungen)
var Speed, Position: TVektor; //Geschwindigkeits- und Positionsvektoren begin Position := Position + (Speed * dt); end; procedure OnTimer(...) begin //hier dann dein Verarbeitungscode PhysicTick(dt / 1000); //division durch tausend, um von Millisekunden auf Sekunden umzurechnen. end; Code is allerdings im Kopf entstanden, also koennten kleinere Fehlerchen dabeisein ;) greetz Mike |
Re: Geschwindigkeit bei bewegten Objekten
zu der Wurfparabel: Ich würde gern flexibler bleiben. WIe gesagt möchte ich den Ball auch gerne gegen eine Wand fliegen bzw. dort abprallen lassen. Das haut mit der Wurparabel natürlich nicht hin. Bei Vektoren muss ich da z.B. nur die x1 Komponente umdrehen.
zu dem Time Based Movement: Ich habe schon verstanden wie das funktionieren soll. Mein Problem ist auch nicht die Bewegung, sondern wie gesagt die Vektoranpassung. Wenn ich jetzt eine Framerate von 20FPS habe, wird der Timer 20 mal in der Sekunde aufgerufen. Die Pixelverschiebung kann ich durch das Time based Movement leicht anpassen, indem halt nur einen Bruchteil von pixeln gehe. Hier ist nicht das Problem. Habe ich nun 20 FPS, wende ich auch folgendes 20 mal pro Sekunde an:
Delphi-Quellcode:
Habe ich jetzt aber nur 10 FPS, wird der Vektor nur 10 mal pro Sekunde angepasst, und die Flugbahn sieht anders aus. Genau das muss ich jetzt irgendwie kompensieren. Aber ich hab absolut keinen Ahnung wie...
Ball.vektor.addieren(Gravitation);
Ball.vektor.addieren(wind); edit: Und dann kommt noch das Geschwindigkeitsproblem hinzu. Ich will abhängig von der Länge des Vektors des Balles natürlich auch eine größre Strecke in der selben Zeit zurücklegen. Ohne Time Based Movement habe ich wie gesagt einfach die Länge des Vektors genommen, und bin soviele Pixel auf einmal gegangen. Was zwar nicht schön aussah, aber geklappt hat.... |
Re: Geschwindigkeit bei bewegten Objekten
Zitat:
Welchen Vorteil das hat? Ein Beispiel: du hast eine Kugel, und 2 Waende, die (relativ) weit ausseinanderstehen. Nehmen wir an, die Kugel braucht 200 Takte zu je ein paar ms, um von einer Wand zur anderen zu gelangen. Wenn du nun bei jedem Takt auf Kollision pruefst, pruefst du in einem Flug 200mal auf Kollisionen, wobei nur einmal eine Kollision auftritt. D.h. nur 0.5% der Berechnungen waeren wirklich notwendig gewesen. Wenn du die Kollision mit der Parabel berechnest, so berechnest du in einem Flug einmal, um zu wissen, wo die Kollision auftritt. D.h. bei der anderen Variante rechnest du 200mal so oft, mit dem selben Ergebnis. Zudem loest die Wurfparabel auch das zweite Problem: Zitat:
Mit der Parabel hast du keine Abhaengigkeit vom zuvor berechneten Punkt. Du hast die entsprechende Parabelfunktion, mit der du berechnen kannst, zu welchem Zeitpunkt sich das Objekt wo befindet. Die Parabel ist dabei natuerlich abhaengig von der Geschwindigkeit v0 des Vektors. Wenn du die Parabel zusammen mit dem Timebased Movement verwendest, kriegst du, was du brauchst: Du speicherst dir lediglich nicht die Zeitdifferenz zum vorherigen Tick, sondern seit der letzten Parabel-neuberechnung. Die Position des Objektes erhaelst du dann, indem du einfach in die Parabelfunktion die vergangene Zeit einsetzt. Damit hast du:
greetz Mike |
Re: Geschwindigkeit bei bewegten Objekten
genial :) es funktioniert einwandfrei. Vielen Dank für deine Hilfe :)
|
Re: Geschwindigkeit bei bewegten Objekten
EIn Problem habe ich jetzt allerdings noch:
Der Ball trifft auf den Boden auf. Ich weiss zwar zu welchem Zeitpunkt das geschieht, und daher auch wo, allerdings kenn ich nicht den Winkel in dem er auftritt. Daher kann ich nicht nach guter alter EInfallswinkel gleich Ausfallswinkel Manier den Ball weiterhopsen lassen. Gut den Winkel könnte ich herausfinden, indem ich die Poisition des Balles kurz vor dem Aufprall mit der während des aufpralles vergleiche, und über die Steigung dann an den Winkel herankomme. Relativ kompliziert, aber dann weiss ich nicht mit welcher Geschwindigkeit ich ihn wieder losschicken soll. Hat da jemand eine Idee? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:11 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz