Guten Abend Community,
ich weiß nicht genau, wie ich mein Problem schildern soll und ich weiß auch, dass es nur eine Kleinigkeit ist, bei der ich leider nicht den Fehler finde.
Folgende Ausgangssituation:
Ich programmiere ein 2d Spiel und bin soweit mit dem Zeichnen der Karte fertig. Jetzt wollte ich mich an das Implementieren des Spielers machen, alles schön mit Klassen und drumrum. Geht soweit auch alles astrein; nur eine Sache bereitet mir kopfschmerzen. Da mein Spiel auf einem 32x32-Raster aufbaut, will ich, dass sich mein Character immer um 32px bewegt.
Beispiel, um den Spieler nach unten zu bewegen:
Code:
if GetAsyncKeyState(vk_down) <> 0then
Player1.MoveDown:=true;
if Player1.MoveDownthenbegin
if MoveCounter>=32thenbegin
Player1.MoveDown:=false;
MoveCounter:=0;
endelsebegin
Player1.pos.y:=Player1.pos.y+1;
inc(MoveCounter);
end;
end;
Er wird solange um 1 Pixel verschoben, bis er auf einem anderen 32x32-Feld ist. Klappt soweit auch astrein - allerdings nur viel zu schnell. Ich vermute, dass sich das ganze mit Timebased Movement lösen lässt. Rumprobiert habe ich damit vorhin schon, allerdings war dann mein Speedfactor ab und zu <= 0.4, was durch abrunden zu 0 führt. Wenn ich jetzt Player1.pos.y:=Player1.pos.y+1*Speedfactor; mache und der Speedfactor, wie oben schon gesagt, <= 0.4 ist, wird der Spieler um 0 Pixel verschoben.
Kann mir jemand weiterhelfen? Ich hoffe, dass ich mich verständlich ausgedrückt habe, denn ich kann mein Problem nicht so gut beschreiben.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Ja, dein Problem ist klar und altbekannt.
Um erklären zu können, wie du Timebased Movement, solltest du mal zeigen, welche Methode du zum Rendern benutzt. Also ob OnIdle, ne Schleife oder Timer.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
// Windows denken lassen, das wir noch nicht fertig wären
Done :=False;
// Nummer des gezeichneten Frames erhöhen
inc(Frames);
// FPS aktualisieren
if GetTickCount - StartTick >=500then
begin
FPS := Frames/(GetTickCount-StartTick)*1000;
Frames :=0;
StartTick := GetTickCount
end;
end;
Ich denke, dass man das Timebased Movement mit Sicherheit irgendwie in den FPS-Counter mit einarbeiten kann, oder? Habe mir vorhin schon den Artikel im Wiki dazu angesehen, zusätzlich auch noch den Artikel beim Bomberman-Clone, da es da auch erklärt wird. Aber irgendwie komme ich auf keinen grünen Zweig..
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Also ich habe mein TBM immer so in der art aufgebaut:
Code:
procedure DoFrame;// Bei dir halt das OnIdle-Event
var
CurrentFrame:Cardinal;
TimeInterval:Cardinal;
PassedSeconds:Double;
begin
CurrentFrame := GetTickCount;
TimeInterval := CurrentFrame - LastFrame;// LastFrame: Würde ich als Member von
// deiner Form-Klasse vom Typ Cardinal deklarieren
LastFrame := CurrentFrame;
PassedSeconds := TimeInterval /1000;
// Stell irgendwas damit an und render...
end;
Was jetzt dein Playermovement betrifft: Du solltest deine Position nicht als Integer sondern als Double speichern und dann nur beim Rendern auf das aktuelle Feld runden. So hast du die "zwischenwerte" mit drin.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Hallo,
warum sollte ich LastFrame als Member von der Form-Klasse deklarieren? Wäre es nicht sinnvoller, wenn ich PassedSeconds als Member der Form-Klasse deklariere? So wie ich das verstehe wäre ja PassedSeconds dann der Wert, den ich mit meinen Player-Movement-Berechnungen multiplizieren muss, oder?
Warum dividierst du durch 1000? Kann man mit dem Wert rumspielen, und somit die 'Geschwindigkeit' festlegen?
Edit: Ich habe deinen Code komplett ÜBER den ganzen Renderkram geschrieben. Ist das überhaupt richtig? Logischerweise ja, aber man weiß ja nie Hab auch mal zu Testzwecken alle Werte ausgeben lassen, sie steigen immer an:
Zur Erklärung:
im Endeffekt ist es egal, wo du Last Frame deklarierst, solange du es nicht lokal tust, weil dann isses humbug. Nur isses halt Stilistisch schöner/besser wenn du es so machst wie vorgeschlagen
Gettickcount gibt die Zeit in Millisekunden an, seit der dein Windows gestartet ist (da gab es Witzigeweise mal ein Windows, was nach c.a. 16 tagen an nem Integerüberlauf abgestürzt ist).
Wenn du den Wert speicherst, irgendwas machst und dann den alten gespeicherten, von dem neuem Wert abziehst, dann erhältst du die zeit in Millisekunden, die das irgendwas gedauert hat. In dem Fall(dem beispiel von Lord Horrazont) is das die variable TimeInterval.
Da eine Sekunde 1000 Millisekunden hat, ergibt die Division durch 1000 die vergangene Zeit in Sekunden. In dem Falle halt passedSeconds.
Womit du multiplizierst ist im endeffekt egal, ob du mit TimeInterval oder Passed seconds multiplizierst, du must halt nur noch ne Speedfaktor einnehmen und den dann anpasssen, bis es ne schöne Bewegungsgeschwindigkeit gibt.
Einziges Problem: wenn du was darstellst, was sehr schnell geht, dann hast du das problem, das du mir der Method nur zeitunterschiede > 15ms rauskriegst. Falls das Darstellen schneller geht erhältst du 0 und irgendwas * 0 = 0. Ergo Stillstand. könntest du lösen, indem du z.B. die Zeit misst, die er braucht um 10 Frames zu malen.
Zu den Werten: die stimmen nie im Leben. Wenn du LastFrame von CurrentFrame abziehst, kommt da 0 raus und nicht irgendwas komisches. Da solltest du nochmal in deinen code blicken.
sry, für die Rechtschreibung, aber es ist mitten in der Nacht und ich seit 18h wach und in denen hab ich auch nich grad gefaulenzt ...
_________________ Es gibt eine Theorie, die besagt, wenn jemals irgendwer genau rausfindet, wozu das Universum da ist und warum es da ist, dann verschwindet es auf der Stelle und wird durch etwas noch Bizarreres ersetzt.
Es gibt eine andere Theorie, die besagt, dass das schon passiert ist.
Player1.pos.y:=(Player1.pos.y+1)*TimeInterval;// Hier die wichtige Zeile - es hieß ja, dass es egal sei, mit was ich multipliziere. <!-- s:) --><img src=\"{SMILIES_PATH}/icon_smile.gif\" alt=\":)\" title=\"Smile\" /><!-- s:) -->
inc(MoveCounter);
end;
end;
Ordentliche Werte krieg ich jetzt zumindest schonmal ausgegeben:
In der ersten Zeile steht CurrentFrame, in der zweiten TimeInterval, in der dritten LastFrame und in der vierten PassedSeconds. (Und in der allerersten die FPS ) Wobei TimeInterval kontinuierlich zwischen 0 und 16 hin- und herwechselt.
Edit1: Ok, habs jetzt doch hingekriegt. Ich muss natürlich Player1.pos.y:=Player1.pos.y+(1*TimeInterval); rechnen. Da das ganze jetzt allerdings recht zügig ging (ZU schnell) hab ich das ganze noch mit 0.06 multipliziert. Die Geschwindigkeit passt jetzt zwar, allerdings kommt es durch auf / abrunden doch immer wieder vor, dass er aus dem 32x32 Grid rauskommt. Da landet er mal auf 257, anstatt auf 256.
Habe die komplette Bewegung nochmal anders gemacht, aber da ist der fehler immernoch:
Ich verschiebe den Char jetzt solange, bis er die gewünsche Position (=DestBuff) erreicht hat. Anschließend dividiere ich die aktuelle Position durch 32 und speichere sie in einer Integer-Variable. Dadurch wird die Aktuelle Position im 32x32 Grid ohne Nachkommastellen gespeichert. Anschließend multipliziere ich diesen Wert wieder mit 32 und erreiche somit die aktuelle Spieler-Position, die genau im 32x32 Raster ist. Vom Funktionieren her nearly perfect - aber ihr habt sicherlich noch Tipps, wie man das ganze verbessern könnte, oder?
Werde mich jetzt ins Bett begeben, der Post hier ist über Nacht noch lang geworden Ich hoffe ihr könnt mir noch Antworten dazu geben und ich hab euch nicht zu viel verwirrt *g*
Du kannst doch einfach nach Abschluss der Bewegung der Position den Wert von buffvar geben.
Da sparst du dir ne Division und ne Multiplikation ...
Und du könntest beim verändern der Position das 1* weglassen. Weil irgendwas * 1 = irgendwas ... Ich weis nich inwiefern das Delphi im Compiler selber rausnimmt ... Wobei, dadurch das du mit gleitkommazahlen Rechnest kann es sogar etwas bewirken weil in Delphi manchmal 1 <> 1.0 ...
Jedenfall bläht es nur den Quelltext unnötig auf
_________________ Es gibt eine Theorie, die besagt, wenn jemals irgendwer genau rausfindet, wozu das Universum da ist und warum es da ist, dann verschwindet es auf der Stelle und wird durch etwas noch Bizarreres ersetzt.
Es gibt eine andere Theorie, die besagt, dass das schon passiert ist.
Hallo,
danke, ja, die Fehler habe ich heute Nacht dann auch noch bemerkt. Es war leider schon spät Aber du meintest sicher, dass ich der Player-Position am Ende die Variable DestBuff zuweisen soll, nicht die BuffVar. Die BuffVar habe ich mittlerweile komplett rausgeschmissen.
Es hat nun zwar den Anschein, dass das "laufen" (verschieben) etwas ruckelt, aber das liegt sicherlich an meinem billigen OnBoard Grafik-Chip hier im Notebook
Na ja, es wird halt immer um 1 px verschoben und wenn die Auflösung zu gering ist, sieht man das.
Vor allem, wenn das Zeitintervall zwischen zwei Sprüngen zu groß ist. Aber du hast ja nen fps counter. Der sagt dir ja, wie schnell das geht. Eigendlich sollte es ab 30 fps flüssig laufen.
_________________ Es gibt eine Theorie, die besagt, wenn jemals irgendwer genau rausfindet, wozu das Universum da ist und warum es da ist, dann verschwindet es auf der Stelle und wird durch etwas noch Bizarreres ersetzt.
Es gibt eine andere Theorie, die besagt, dass das schon passiert ist.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Ich habe das bei mir so gelöst, dass ich immer, wenn der Abstand zwischen beginn des letzten Frames und beginn des aktuellen Frames kleiner als 10 ms war, er dann einen Frame fallengelassen hat, also ich die Renderroutine einfach verlassen habe ohne irgendetwas zu tun (außer in deinem Falle natürlich Done auf False setzen). So kannst du sicher sein, dass du da immer halbwegs gescheite werte rausbekommst und du sparst Rechenleistung, zumindest wenn du das mit Sleep(5); oder so kombinierst.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Mitglieder in diesem Forum: 0 Mitglieder und 3 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.