Entwickler-Ecke
Windows API - Wie Tastaturverzögerung ausschalten?
galagher - Mo 07.05.18 19:52
Titel: Wie Tastaturverzögerung ausschalten?
Hallo!
Ich möchte in meinem Programm - und nur dort! - die Tastaturverzögerung ausschalten! Wie geht das denn?
lg
galagher
Moderiert von Narses: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 07.05.2018 um 22:07
Narses - Mo 07.05.18 22:09
Moin!
Womit dein Programm, unterstellt, dass man Windows überhaupt sowas begebogen kriegt, faktisch unbenutzbar wird. :shock: Welchen Sinn sollte das haben? :gruebel: :nixweiss:
Suchst du nicht eher sowas?
GETASYNCKEYSTATE :lupe: :think:
cu
Narses
Delete - Di 08.05.18 00:37
- Nachträglich durch die Entwickler-Ecke gelöscht -
galagher - Di 08.05.18 08:14
Narses hat folgendes geschrieben : |
Welchen Sinn sollte das haben? :gruebel: :nixweiss: |
Für ein Spiel, das man auf die altmodische Art per Pfeiltasten steuert, da wäre es nützlich, keine Verzögerung der Tasten zu haben!
Wenn da etwas dabei ist, das das kann, ja!
Frühlingsrolle hat folgendes geschrieben : |
eine solche Einstellung gilt systemweit und kann daher auch nicht für die Anwendung allein umgestellt werden. |
Das ist eher schlecht, denn der unschöne Umweg wäre dann, bei Programmstart den aktuellen Wert zu sichern, dann auf 0 zusetzen und bei Programmende den gesicherten Wert wieder zurück zu setzen, bzw. ist das ja jedesmal notwendig, wenn das Programm den Fokus verliert.
OlafSt - Di 08.05.18 08:42
Tut auch überhaupt nicht Not, sowas zu machen.
Per AsyncKeyState kann man feststellen, ob eine Taste gedrückt ist. Nun kann man seine Spielfigur so lang ein die passende Richtung bewegen, bis man merkt, das die Taste eben nicht mehr gedrückt ist.
Geht auch einigermaßen per OnKeyDown, OnKeyUp.
Delete - Di 08.05.18 16:34
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson - Di 08.05.18 17:45
Also bisher hat bei mir TForm1.FormKeyDown bzw TForm1.FormKeyUp immer ausgereicht bei dem, was ich an Delphi an Spielchen gemacht habe.
Da man in Delphi sowiso nur langsamere Spiele hinbekommt, sollte die Tastatur im Endeffekt nicht dein größtes Pronblem sein.
solltest du wirklich vorhaben ein schnelles Spiel zu entwickeln empfehle ich dir entweder eine Spielengine wie Unity oder Unreal etc. oder C++ oder Java und OpenGL oä. Muss ja nichtmal 3D sein...
Ist nur für den Anfang mit sehr großen Kanonen geschossen ;)
galagher - Di 08.05.18 18:43
Frühlingsrolle hat folgendes geschrieben : |
GetAsyncKeyState() hat seine Berechtigung. Es reagiert "schneller" auf Tasteneingaben. |
Ich finde die Lösung mit
Msg.CharCode am Besten, da läuft alles flüssiger:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| protected procedure Steuerung(var Msg: TWMKEYDOWN); message WM_KEYDOWN; end;
procedure TForm1.Steuerung(var Msg: TWMKEYDOWN); begin if (Msg.CharCode = VK_LEFT) or (Msg.CharCode = VK_RIGHT) or end; |
Das behebt aber leider nicht die Verzögerung der Pfeiltasten,
GetAsyncKeyState() übrigens auch nicht!
Delete - Di 08.05.18 19:00
- Nachträglich durch die Entwickler-Ecke gelöscht -
Sinspin - Di 08.05.18 19:05
Warum soll es mit GetAsyncKeyState() Verzögerungen geben?
Ich vermute, das hängt nur am Intervall der Abfrage und wie Du die gewonnen Informationen verwendest um eine Reaktion zu visualisieren.
Symbroson hat folgendes geschrieben : |
Da man in Delphi sowiso nur langsamere Spiele hinbekommt, sollte die Tastatur im Endeffekt nicht dein größtes Pronblem sein. |
Das hier unter Delphi zu posten halte ich schon ein bisschen für Blasphemie. Versuch es doch erstmal!
galagher - Di 08.05.18 19:27
Frühlingsrolle hat folgendes geschrieben : |
Schwer zu sagen, was du mit der Abfrage bezwecken möchtest?![/delphi] |
Naja, die Spielfigur bewegen! Funktioniert auch sehr gut, nur nach dem Drücken einer Pfeiltaste passiert, was eben normalerweise passiert, wenn man eine Taste drückt: Die Spielfigur bewegt sich in die gewünschte Richtung (die Taste "wirkt sich also aus"), dann folgt eine kurze Pause, dann geht's weiter. Die Tastaturverzögerung eben!
Frühlingsrolle hat folgendes geschrieben : |
Das ginge noch etwas kürzer: if Msg.CharCode in [VK_LEFT, VK_RIGHT, ...] then |
Das ist natürlich noch eleganter!
Symbroson - Di 08.05.18 20:56
Nun, in dem Fall hast du einen einfach zu behebenden "Denkfehler". Denn du verwendest die Funktion der Tastatur, dass das OnKey Event bei langem Drücken oft ausgelöst wird. In der Spieleentwicklung macht man das aber anders:
Wenn eine Taste gedrückt wird, setzt man eine entsprechende Flag, die markiert, dass die Taste gedrückt wurde. Die verwendest du dann auch für deine Tastenabfrage. Und wenn schließlich das OnKeyUp Event ausgelöst wurde entfernst du die Flag wieder. Die Flag kann ein einfacher Boolean oder ein Integer (im Binärsystem) sein.
Delete - Di 08.05.18 23:31
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson - Di 08.05.18 23:41
@Frühlingsrolle Beispiel: Öffne den Text-Editor, halte einen beliebigen Buchstaben gedrückt und schau, was passiert. Genau das gleiche Verhalten zeigt sich in seinem Spiel. Das meint er
t.roller - Di 08.05.18 23:52
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
| unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type TForm1 = class(TForm) Label1: TLabel; Button1: TButton; procedure Button1KeyPress(Sender: TObject; var Key: Char); private public end;
var Form1: TForm1; i : Integer;
implementation
{$R *.dfm}
procedure TForm1.Button1KeyPress(Sender: TObject; var Key: Char); begin if True then inc(i); Label1.Caption:= INTTOSTR(i); end;
end. |
Eine Verzögerung kann ich bei diesem Beispiel nicht feststellen.
Delete - Mi 09.05.18 00:00
- Nachträglich durch die Entwickler-Ecke gelöscht -
t.roller - Mi 09.05.18 01:12
Mein Beispiel läuft in 4 sec 100x durch die Routine.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:
| unit Unit1;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type TForm1 = class(TForm) Label1: TLabel; Label2: TLabel; Button2: TButton; Label3: TLabel; Button1: TButton; procedure Button2Click(Sender: TObject); procedure FormActivate(Sender: TObject); procedure Button1KeyPress(Sender: TObject; var Key: Char); private public end;
var Form1: TForm1; i : Integer;
implementation
{$R *.dfm}
procedure TForm1.Button1KeyPress(Sender: TObject; var Key: Char); begin if i=0 then Label3.Caption:= TimeToStr(Now);
if True then inc(i); Label1.Caption:= INTTOSTR(i); Label2.Caption:= TimeToStr(Now); end;
procedure TForm1.Button2Click(Sender: TObject); begin i:=0; Label1.Caption:= '0'; Label2.Caption:= '0'; Label3.Caption:= '0'; Button1.SetFocus; end;
procedure TForm1.FormActivate(Sender: TObject); begin Button1.SetFocus; Label1.Caption:= '0'; Label2.Caption:= '0'; Label3.Caption:= '0'; end;
end. |
Symbroson - Mi 09.05.18 07:18
Frühlingsrolle hat folgendes geschrieben : |
Wenn es das ist, was Symbroson meinte, dann könnte man auf den TTimer ausweichen und im OnTimer Ereignis jene Tasten mit GetAsyncKeyState() abfragen.
Was ist das kleinstmögliche Intervall dafür? 50ms? |
Wie gesagt, Flags zu nutzen finde ich ist die beste Variante. Wie ein Lichtschalter.
Sinspin - Mi 09.05.18 09:52
Du kannst bei einem "echten Spiel" ja nicht rein mit den Ereignissen arbeiten wie ein normales Windows Programm. Das reicht nicht um einen gleichmäßigen Spielfluss hinzubekommen.
Du braucht einen Renderer für die Anzeige, am besten in einem eigenen Thread. So bekommst du es auch hin das sich alles gleichmäßig bewegt.
Du brauchst einen weiteren Thread der sich Tastatur und Maus zur Brust nimmt und alles, am besten als Flags bereitstellt.
Und natürlich einen weiteren Thread in dem die ganzen Positionsinformationen in deiner Spielewelt zusammengestellt werden, die dann der Renderer in Bilder umsetzt.
Anstatt mit Threads kann man am Anfang auch mit Timern arbeiten. Aber die können nicht parallel ausgeführt werden, sind also nicht für rechenintensive Aufgaben geeignet.
Was die Anzeige / das Rendern angeht, das ist immer einen Kanonen und Spatzen Frage. OpenGL, Direct2D. Beide können für 2D Ausgabe verwendet werden. Das hinten dran hängt vom Anwendungsfall ab.
galagher - Mi 09.05.18 10:58
Symbroson hat folgendes geschrieben : |
In der Spieleentwicklung macht man das aber anders:
Wenn eine Taste gedrückt wird, setzt man eine entsprechende Flag, die markiert, dass die Taste gedrückt wurde. Die verwendest du dann auch für deine Tastenabfrage. Und wenn schließlich das OnKeyUp Event ausgelöst wurde entfernst du die Flag wieder. Die Flag kann ein einfacher Boolean oder ein Integer (im Binärsystem) sein. |
Klingt gut, werde versuchen, das umuzsetzen!
Symbroson hat folgendes geschrieben : |
@Frühlingsrolle Beispiel: Öffne den Text-Editor, halte einen beliebigen Buchstaben gedrückt und schau, was passiert. Genau das gleiche Verhalten zeigt sich in seinem Spiel. Das meint er |
Genau!
Delete - Mi 09.05.18 14:12
- Nachträglich durch die Entwickler-Ecke gelöscht -
Symbroson - Mi 09.05.18 14:43
Hier mal mein Beispiel mit Integer(Bit)-Flags:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85:
| unit Unit1;
interface
uses Windows, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls;
type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormPaint(Sender: TObject); end;
const KLef: integer = 1 shl 0; KRig: integer = 1 shl 1; KFwd: integer = 1 shl 2; KBwd: integer = 1 shl 3;
var Form1: TForm1; keys: Byte; running: boolean;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); begin keys := 0; running := false; end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of 37: keys := keys or KLef; 38: keys := keys or KFwd; 39: keys := keys or KRig; 40: keys := keys or KBwd; end; end;
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin case Key of 37: keys := keys and not KLef; 38: keys := keys and not KFwd; 39: keys := keys and not KRig; 40: keys := keys and not KBwd; end; end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin running := false; end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if running then begin running := false; Canvas.TextOut(0, 0, 'Click to Start '); end else begin running := true; Canvas.TextOut(0, 0, 'Click to Stop ');
while running do begin if keys and KLef > 0 then Left := Left - 1; if keys and KRig > 0 then Left := Left + 1; if keys and KFwd > 0 then Top := Top - 1; if keys and KBwd > 0 then Top := Top + 1; Application.ProcessMessages; end; end; end;
procedure TForm1.FormPaint(Sender: TObject); begin Canvas.TextOut(0, 0, 'Click to toggle key listener '); end;
end. |
galagher - Do 10.05.18 19:49
Symbroson hat folgendes geschrieben : |
Hier mal mein Beispiel mit Integer(Bit)-Flags: |
Das ist aber wieder mit KeyDown/KeyUp! Da habe ich wieder die Verzögerung!
Sinspin hat folgendes geschrieben : |
Du kannst bei einem "echten Spiel" ja nicht rein mit den Ereignissen arbeiten wie ein normales Windows Programm. Das reicht nicht um einen gleichmäßigen Spielfluss hinzubekommen. |
Für meine Zwecke reichen Ereignisse und Timer völlig!
Symbroson hat folgendes geschrieben : |
In der Spieleentwicklung macht man das aber anders:
Wenn eine Taste gedrückt wird, setzt man eine entsprechende Flag, die markiert, dass die Taste gedrückt wurde. Die verwendest du dann auch für deine Tastenabfrage. Und wenn schließlich das OnKeyUp Event ausgelöst wurde entfernst du die Flag wieder. |
Wo setze ich die Flag und wo verarbeite ich sie für die Tastenabfrage? Ich muss ja erst einmal dem Programm mitteilen, dass eine der Pfeiltasten gedrückt wurde. Da kenne ich nur KeyDown, dort habe ich aber in jedem Fall eine Verzögerung.
Symbroson - Do 10.05.18 20:00
Hast du den Code schon ausprobiert? Ich kann keine Verzögerung feststellen. Das KeyDown Event sollte keine Verzögerung verursachen, da es ja sofort ausgelöst wird, sobald die Taste gedrückt wurde.
galagher - Do 10.05.18 21:15
Symbroson hat folgendes geschrieben : |
Das KeyDown Event sollte keine Verzögerung verursachen, da es ja sofort ausgelöst wird, sobald die Taste gedrückt wurde. |
Nein, ausprobiert nicht, aber was ich meine, ist: Halte mal in irgendeiner Textverarbeitung eine Taste gedrückt, meinetwegen "a". Da kommt dann:
a
Nun kommt eine Pause
dann kommt:
aaaaaaaaaaaaaaaaaaa
Das meine ich!
//Edit:
Ok, ich habe ihn in einer zusätzlichen Form zu meinem Projekt getestet, aber da tut sich gar nichts, einzig Mausklicks bewirken eine Änderung des dargestellten Textes. Also habe ich ein TEdit eingefügt, und im OI KeyPreview := True gesetzt. Deinen Code habe ich wie folgt abgeändert:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| case Key of 37: begin keys := keys or KLef; Edit1.Text := Edit1.text+'a'; end; |
Und es passiert, was da immer passiert (ausser, man hat keine Tastaturverzögerung eingestellt): Es folgt ein "a", dann die Pause, dann "aaaaaa". Und
die Pause will ich nicht!
Symbroson - Do 10.05.18 21:17
Wie gesagt, mit Flags sollte das nicht passieren. Probiers einfach aus!
galagher - Do 10.05.18 21:31
Symbroson hat folgendes geschrieben : |
Wie gesagt, mit Flags sollte das nicht passieren. Probiers einfach aus! |
Das mache ich die ganze Zeit! Wo setze ich die Flags denn? Denn immer, wenn ich eine Taste drücke, "gibt Windows an mein Programm die eingestellte Verzögerung weiter" - das soll ja normalerweise auch so sein!
Also, wo setze ich ein Flag?
Symbroson - Do 10.05.18 21:35
Was hast du denn bisher probiert? Kannst du mal deinen cide schicken?
Flags kannst du als einfache Booleans ausdrücken oder wie in meinem Beispiel als Bits von einem Integer-Typ. Da du nur 4 Tasten hast, reicht ein Byte (bis maximal 8 Tasten) aus
galagher - Do 10.05.18 21:44
Symbroson hat folgendes geschrieben : |
Was hast du denn bisher probiert? Kannst du mal deinen cide schicken? |
Den habe ich wieder gelöscht. Wo ausser in KeyDown setze ich ein Boolean auf True, das angibt, dass eine Pfeiltaste gedrückt wurde? Wenn ich das erstmal weiss, kann ich die Prozedur aufrufen, die die Spielfigur steuert.
Symbroson hat folgendes geschrieben : |
Flags kannst du als einfache Booleans ausdrücken oder wie in meinem Beispiel als Bits von einem Integer-Typ. Da du nur 4 Tasten hast, reicht ein Byte (bis maximal 8 Tasten) aus |
Das weiss ich, das ist aber nicht das Problem!
Vielleicht reden wir aneinander vorbei:
Ich möchte, dass mein Programm einen Pfeiltastendruck (die Pfeiltasten werden meistens gedrückt gehalten) verarbeitet, ohne dass dabei die übliche Verzögerung auftritt.
Symbroson - Do 10.05.18 21:48
dein eigenes Programm muss dann irgendwo anders (entweder mit Application.ProcessMessages oder besser als separaten Thread) eigenständig laufen und die Flags abfragen, um etwas zu bewegen. Nur per Event das Spiel zu aktualisieren wird nicht funktionieren, wenn sich zB. etwas von allein bewegen soll.
galagher - Do 10.05.18 22:38
Symbroson hat folgendes geschrieben : |
dein eigenes Programm muss dann irgendwo anders (entweder mit Application.ProcessMessages oder besser als separaten Thread) eigenständig laufen und die Flags abfragen, um etwas zu bewegen. |
Ja, sieht so aus. Mit Threads habe ich mich noch nicht wirklich beschäftigt, vor alem fürchte ich, dass damit dann irgendwelche Probleme im Programm auftauchen.
Symbroson hat folgendes geschrieben : |
Nur per Event das Spiel zu aktualisieren wird nicht funktionieren, wenn sich zB. etwas von allein bewegen soll. |
Mit Timern klappt das hervorragend!
Symbroson - Do 10.05.18 22:52
Dann nimm Timer, wenn es dir gefällt
Delete - Do 10.05.18 23:15
- Nachträglich durch die Entwickler-Ecke gelöscht -
galagher - Mo 14.05.18 12:01
Frühlingsrolle hat folgendes geschrieben : |
Wie schon mehrmals erwähnt und zitiert, mit dem Timer bekommst du es gelöst. |
Ja, aber der Timer stoppt die Spielfigur nicht exakt dann, wenn ich die Taste loslasse. Kann man aber sicher optimieren!
Delete - Di 15.05.18 00:32
- Nachträglich durch die Entwickler-Ecke gelöscht -
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!