Autor Beitrag
Jörg1
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Sa 11.12.04 00:31 
Hallo zusammen,

Ich habe vor, in der nächsten Zeit ein kleines Spiel zu realisieren. Dieses Spiel soll nur aus den Standardkomponenten bestehen. Weder DirectX, DelphiX oder sonstige Zusatzmodule oder Bibliotheken möchte ich dabei benutzen bzw. einsetzen. Ich arbeite übrigens mit D5 Standard auf XP Prof.

Seit einigen Tagen bin ich nun schon damit beschäftigt, eine Art "Grundgerüst" für dieses Spiel auf die Beine zu stellen. Der momentan wichtigste Bestandteil dieses Gerüsts ist eine funktionierende Spielschleife,also ein Gameloop, der mehr oder weniger so lange durchläuft, bis eine bestimmte Situation eintritt, beispielsweise eine Kollision oder ähnliches. Dies würde jedoch bedeuten, daß das Spiel bzw. das Programm immer oder zumindestens eine längere Zeit in dieser Schleife festhängt und somit nicht mehr auf andere Dinge reagieren könnte, was aber für ein Spiel absolute Vorraussetzung ist.
Dieses Problem löse ich, indem ich in die Spielschleife die Prozedur/den Befehl : Application.ProcessMessages einfüge. Somit bekommt das Programm pro Schleifendurchlauf(Frame) einmal die Möglichkeit, die Botschaftswarteschlange zu durchlaufen, bis diese leer ist und anliegende Nachrichten und Ereignisse verarbeiten zu können. Dies funktioniert bei mir soweit alles eigentlich ganz gut.

Konkret in meinem Fall sieht es nun so aus, daß ich in meinem Spiel, während der Gameloop läuft, 2 Tasten abfrage, die ich zum steuern der Spielfigur benötige. Es handelt sich dabei um die Tasten "Pfeil rechts / VK_RIGHT" und "Pfeil links / VK_LEFT". Diese Abfrage findet statt innerhalb der Ereignisprozedur "FormKeyDown" des Formulars.
Bis hierhin gibt es eigentlich bei mir noch kein Problem, der Ablauf ist klar und funktioniert : Ich drücke eine der vorher genannten Tasten, das Ereignis KeyDown kommt in die Botschaftswarteschlange, der Befehl Application.ProcessMessages wird innerhalb der Gameloop aufgerufen, das Ereignis 'KeyDown' wird in der Botschaftswarteschlange gefunden. Daraufhin wird die Spielschleife kurzzeitig verlassen, um zur Prozedur FormKeyDown springen zu können. Bei mir lasse ich dort zum testen eine Integer-Variable um den Wert 1 erhöhen und diese dann in einem Label anzeigen. Nachdem die Anweisungen/Befehle innerhalb der Prozedur abgearbeitet sind, springt das Programm wieder zurück in die Spielschleife.

Nun aber zu meinem eigentlichen Problem. Dafür muss ich jedoch etwas ausholen, damit es von euch möglichst gut verstanden werden kann. Jeder Windows-Nutzer hat in der Systemsteuerung unter der Rubrik "Tastatur" die Möglichkeit, das Verhalten des Betriebssystems und den darauf laufenden Programmen bei einer Tastenbetätigung individuell nach seinen Bedürfnissen und Wünschen anzupassen. Dies betrifft die Cursorblinkrate, die Verzögerungszeit nach dem ersten Zeichen bei Tastendauerbetätigung und die Wiederholrate. Für normale Programme und Anwendungen ist dies natürlich eine tolle Sache. Der eine Nutzer zieht eine eher langsame Einstellung vor, der andere mag es etwas schneller.
Jedoch bei einem Spiel, wo es auf Geschwindigkeit und möglichst wenig Verzögerung ankommt, sind solche Effekte wie die Verzögerungszeit und die Wiederholrate absolut unerwünscht bzw. unbrauchbar, wenn nicht sogar vollkommen unakzeptabel. Und genau hier liegt sprichwörtlich der Hund begraben. Denn leider ist es nun mal so, daß auch mein Spiel bzw. Gerüst in dem momentanen Zustand diese Betriebssystemeinstellung nicht umgehen kann. Wenn man also alles so lassen würde, wie es jetzt ist, würde sich meine Spielfigur ungefähr genauso verhalten wie ein Cursor. Bei Tastendauerbetätigung würde sie sich kurz bewegen, dann würde sie stoppen, weil die Verzögerungszeit greift. Und die anschließend folgende Wiederholrate wäre viel zu langsam für ein Spiel bzw. für meine Zwecke. Damit kann man natürlich nicht leben. Diese unerwünschten Effekte treten leider wirklich ein, ich habe es ausprobiert. ich erkenne das an der Art und der Geschwindigkeit, wie die Testvariable um den Wert 1 erhöht wird. Es ist nämlich exakt das oben beschriebene Verhalten. Dieses variiert dann je nach Einstellung in der Systemsteuerung.

Darum bitte ich euch hier um Hilfestellung. Wie ist es möglich, diese "Tastaturbremse" zu umgehen, damit man in der Lage ist, mit der vollen Systemgeschwindigkeit zu arbeiten? Es wäre sehr schade, wenn wegen so einer "blöden" Geschichte das Projekt scheitern würde. Manche von euch werden sich jetzt sicher fragen : "Mein Gott, was macht der da?" Mir ist durchaus bewusst, das es ganz bestimmt viel effizientere, ausgeklügeltere, einfach bessere Lösungen gibt, was eine Spielschleife und das abarbeiten von Nachrichten angeht. Da ich kein Profi bin und noch nie ein Spiel programmiert habe, versuche ich erstmal, einfache und möglichst unkomplizierte Wege einzuschlagen. Auch wenn ihr mir vielleicht helfen könnt, dieses Problem zu lösen, ist mir jetzt schon klar, daß noch sehr viele andere Probleme folgen werden. Trotzdem möchte ich eigentlich nicht die Flinte ins Korn werfen. Über eine Antwort von euch würde ich mich jedenfalls sehr freuen.

Mit freundlichen Grüßen

Jörg
GSE
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 740

Win 2k, Win XP Pro
D5 Prof, D6 Ent, D2k5 PE
BeitragVerfasst: Sa 11.12.04 09:55 
Hi,
frag am Besten die Tasten nicht im OnKeyDown ab, sondern direkt innerhalb der GameLoop mit GetAsyncKeyState

mfg
GSE

_________________
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs
and the universe trying to produce bigger and better idiots. So far, the universe is winning. (Richard Cook)
Phantom1
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 390



BeitragVerfasst: Sa 11.12.04 10:31 
es gibt noch eine andere möglichkeit, du deklarierst eine (globale) variable zb:

ausblenden Delphi-Quelltext
1:
Keys: array[0..255] of Boolean;					


und in den Ereignisprozeduren von KeyUp und KeyDown fügst du folgendes ein:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  Keys[Key]:=True;
end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  Keys[Key]:=False;
end;


dann kannst du bequem in deiner Gameloop die Tasten abfragen. zb:

ausblenden Delphi-Quelltext
1:
if Keys[VK_RIGHT] then x := x + 1 * movecount;					
GSE
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 740

Win 2k, Win XP Pro
D5 Prof, D6 Ent, D2k5 PE
BeitragVerfasst: Sa 11.12.04 11:07 
Zitat:
es gibt noch eine andere möglichkeit, du deklarierst eine (globale) variable zb:

dann hätte er aber wieder das Problem mit der Tastenwiederholrate

mfg
GSE

_________________
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs
and the universe trying to produce bigger and better idiots. So far, the universe is winning. (Richard Cook)
Raphael O.
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1596


VS 2013
BeitragVerfasst: Sa 11.12.04 11:44 
entweder ich habe das Problem mit der Wiederholrate nicht verstanden, oder aber du liegst falsch...

man fragt dann ja immer den Zustand des keys-array ab und der ändert sich nur, wenn eine taste gedrückt wird oder losgelassen wird.
für den gesamten zeitraum wo sie gedrückt ist ist also der entsprechende keys wert auf true und ansonsten false...

und dabei dürfte es keien Verzögerung, stocken o.ä. geben :)
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Sa 11.12.04 13:00 
Hallo zusammen,

Zunächst einmal vielen, vielen Dank für diese doch aufschlussreichen Lösungsvorschläge. Welche für mich nun am besten und geeignetsten ist, werde ich wohl probieren müssen. Sobald ich wieder weiterprogrammiere in den nächsten Tagen werde ich mir die Lösungen von euch genau anschauen und erarbeiten, damit ich auch verstehe, was dort genau passiert. Denn einfach abschreiben und einsetzen bringt keinen Lerneffekt. Naja, es liegt jedenfalls noch viel Arbeit vor mir. Wenn diese Sache mit den Tasten erledigt ist, wird der nächste Schritt sein, eine Zeitmessung bezüglich der Frameratenbegrenzung einzubauen. Werde das erstmal alleine versuchen, obwohl ich nicht davon ausgehe, daß ich es ohne Hilfe schaffen kann. Mal sehen :)

Jedenfalls nochmals Danke :)

MfG

Jörg
GSE
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 740

Win 2k, Win XP Pro
D5 Prof, D6 Ent, D2k5 PE
BeitragVerfasst: Sa 11.12.04 13:38 
Zitat:
entweder ich habe das Problem mit der Wiederholrate nicht verstanden, oder aber du liegst falsch...
ok, ich gebe zu hier lag der Denkfehler wohl bei mir. ich hatte gedacht das OnKeyDown wird erst nach der eingestellten Wiederholrate ausgeführt, was mir jetzt aber auch unlogisch erscheint, will heißen beide Lösungen sollten so eigentlich funktionieren (auch wenn ich lieber GetAsyncKeyState nehme :wink:)

mfg
GSE

_________________
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs
and the universe trying to produce bigger and better idiots. So far, the universe is winning. (Richard Cook)
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Sa 11.12.04 16:44 
Hallo zusammen,

Ich konnte es nicht erwarten und hab mich doch schon einmal damit auseinandergesetzt :) . Leider liefert der deutschsprachige Teil der D5-Hilfe keinerlei Infos über den Aufruf 'GetAsyncKeyState', sondern nur der englische Teil. Ich habe es dort jedenfalls so verstanden, das der Aufruf überprüft, ob eine bestimmte Taste gedrückt oder eben nicht gedrückt ist und einen Wert zwischen 0 und 256 zurückliefert. ich habe nun diesen Aufruf in meine Gameloop platziert und eine globale Integervariable deklariert mit dem Namen 'tasten_status'. Innerhalb der Gameloop habe ich nun folgende Zeilen zum testen eingefügt:

1: tasten_status := GetAsyncKeyState(VK_LEFT);
2: Label3.Caption := inttostr(tasten_status);

Wenn ich nun die linke Pfeiltaste betätige, wird mir der Wert -32768 angezeigt. Lasse ich los, wird mir der Wert 0 angezeigt. Allein schon dieser große negative Wert zeigt mir, daß hier etwas nicht stimmen kann. Ich hatte zuerst an eine Bereichsüberschreitung gedacht, aber die Variablendeklaration vom Typ Integer müsste doch eigentlich ausreichend sein. Tausche ich z.B. VK_LEFT gegen VK_RIGHT aus, so bekomme ich dann beim Drücken und Loslassen der rechten Pfeiltaste exakt die gleichen Werte. Das wäre dann der zweite Hinweis darauf, daß etwas von mir nicht richtig gemacht worden ist. Wo habe ich nun den Fehler gemacht bzw. was habe ich nicht richtig gedeutet bzw. verstanden? Für Hilfestellung bin ich natürlich wie immer dankbar :)

MfG

Jörg
GSE
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 740

Win 2k, Win XP Pro
D5 Prof, D6 Ent, D2k5 PE
BeitragVerfasst: So 12.12.04 11:00 
Zitat:
If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState.
du musst also abfragen ob das höchste oder das niedrigste bit gesetzt (1) sind.

Bsp:
ausblenden Delphi-Quelltext
1:
2:
if Hi(GetAsyncKeyState(VK_LEFT)) = 128 then // Die Taste "Links" ist gedrückt, (10000000 entspricht 128)
if Lo(GetAsyncKeyState(VK_LEFT)) = 1 then // Die Taste wurde seit dem letzten Aufruf von GetAsyncKeyState gedrückt


mfg
GSE

_________________
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs
and the universe trying to produce bigger and better idiots. So far, the universe is winning. (Richard Cook)
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: So 12.12.04 13:06 
Hallo,

Vielen Dank für die Lösung. Ich verstehe einfach nicht, warum es für die Autoren der Hilfedateien anscheinend nicht möglich ist, aussagekräftige Beispiele einzufügen. Manchmal ist das zwar der Fall, aber speziell bei GetAsyncKeyState hat man sehr schlechte Chancen, zu verstehen, was genau beim Aufruf passiert und welche Werte zurückgeliefert werden. Dies wäre auch in deutscher Sprache genauso unverständlich gewesen für mich. Wo steht da etwas von Hi oder Lo ??? Nämlich nirgendwo!!!

Naja, wie auch immer, es freut mich jedenfalls sehr, daß ihr mich hier so toll unterstützt. Ich würde auch gerne Ratschläge geben und helfen, aber so langsam habe ich das Gefühl, daß hier alle schon wesentlich weiter sind als man selbst. Ich bitte deshalb um ein wenig Nachsicht.

MfG

Jörg
Phantom1
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 390



BeitragVerfasst: So 12.12.04 16:12 
@Jörg1: man muss sich in diesem beispiel nur mit Bit operationen auskennen. Dann prüft man ob das erste oder letzte bit gesetzt ist, so wie es in der hilfe steht. Man braucht dazu übrigens kein Hi() oder Lo().

ausblenden Delphi-Quelltext
1:
2:
if GetAsyncKeyState(vk_left) and $8000 = $8000 then // taste ist gedrückt
if GetAsyncKeyState(vk_left) and $0001 = $0001 then // taste wurde seit letztem aufruf gedrückt


mfg
GSE
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 740

Win 2k, Win XP Pro
D5 Prof, D6 Ent, D2k5 PE
BeitragVerfasst: So 12.12.04 16:34 
Zitat:
Man braucht dazu übrigens kein Hi() oder Lo()
hab' ich auch nie behauptet und deine variante läuft auf das selbe hinaus wie meine

mfg
GSE

_________________
Programming today is a race between software engineers striving to build bigger and better idiot-proof programs
and the universe trying to produce bigger and better idiots. So far, the universe is winning. (Richard Cook)
Phantom1
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 390



BeitragVerfasst: So 12.12.04 16:56 
GSE hat folgendes geschrieben:
Zitat:
Man braucht dazu übrigens kein Hi() oder Lo()
hab' ich auch nie behauptet und deine variante läuft auf das selbe hinaus wie meine

das ist mir auch klar, ich wollte damit Jörg1 nur zu verstehen geben (bezogen auf seinem letzten beitrag), das man nicht zwingend dieses Hi() bzw Lo() benötigt :wink:
Jörg1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 27



BeitragVerfasst: Mo 13.12.04 13:22 
Hallo zusammen,

Bitte bekommt jetzt keinen Streit untereinander wegen dieser Hi/Lo - Geschichte, daß ist es nun wirklich nicht wert. Auch, wenn beide Versionen vielleicht auf das gleiche herauskommen, fest steht für mich einfach, daß
die Hilfedateien zu Wünschen übrig lassen. Wird vielleicht auch extra gemacht, damit nur Insider wissen, was abläuft, oder damit Zusatzliteratur gekauft werden muss, wer weiß.

Nun aber wieder zu meinem Spiel :) Ich habe nun in die Loop folgende Zeile zum testen bzw. ermitteln von Rückgabewerten der verschiedenen Tasten eingefügt :

1: tasten_status := Hi(GetAsyncKeyState(VK_Left));
2: Label3.Caption := inttostr(tasten_status);

Als Rückgabewert bekomme ich nun tatsächlich den Wert 128 geliefert. Bei VK_RIGHT / VK_ESCAPE, die ich auch ermitteln wollte, ist es exakt das gleiche, also ebenfalls 128. Lasse ich die jeweilige Taste los, ist
der Rückgabewert 0. Dies wäre dann im Prinzip alles, was ich für meine Zwecke brauche. Sowie ich es bis jetzt sehe bzw. verstehe, brauche ich diesen Lo-Wert in meinem Spiel nicht, kann aber sein, daß sich das
noch als falsch rausstellt.
Die Frage, die ich mir jetzt allerdings noch stelle, ist die, ob die Werte, die ich für die verschiedenen Tasten hier auf meinem System angezeigt bekomme, wirklich auch auf allen Rechnern und
Betriebssystemen(w98/2000/xp) gleich sind, um die Funktionalität des Programms sicherstellen zu können. Aber das werde ich demnächst noch ausgiebig testen. Jedenfalls scheint das Problem mit der
Tastaturabfrage dank eurer Hilfe wirklich geknackt zu sein :) Bevor ich jedoch zum nächsten Problempunkt übergehe, werde ich das alles erstmal ein wenig verfeinern, ausbauen und auf mehreren,
verschiedenen Rechnern abchecken.

Nochmals dankeschön.

MfG

Jörg