Using the keyboard to move a sprite: whether I use the KeyPress, KeyDown, or GetAsyncKeyState, I still get a delay. Sure, it moves, but between the vertical directions and the horizontal directions, there is a delay. It'll start, stop, then start and continue. How do I remove the STOP part.
(I've checked my Keyboard settings in Control Panel... all is as fast as possible, so altering that won't help.)
It sounds like you're talking about the pause between pressing a key, and then before it starts repeating. There may be a way to change that. However, if it were me, I wouldn't use that pause at all. Both KeyDown and KeyPress are going to acknowledge that pause. And, it's my understanding that GetAsyncKeyState is going to acknowledge it as well.
When I'm doing something like that, I look at the first KeyPress (or KeyDown) and then start monitoring keys with GetKeyState, not worrying about when the next key comes in. Using that approach, you'd have to use a timer, or possibly a continuous loop with Sleep (until the key is up), to monitor how long the key was down.
Maybe That'll Help,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
It was actually a bit more work than I thought, and this code needs more clean-up, but it'll get you going. It's just some code I quickly cobbled together:
Just throw it into Form1, with no controls on the form, to test.
Code:
Option Explicit
'
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type msg
hWnd As Long
message As Long
wParam As Long
lParam As Long
Time As Long
pt As POINTAPI
End Type
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As msg, ByVal hWnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
'
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
Const VK_E As Long = &H45&
If KeyCode = vbKeyE Then
Do
ClearKeyboardBuffer
Debug.Print Int(Rnd * 9000) + 1000
Sleep 10
If GetKeyState(VK_E) >= 0 Then Exit Do
Loop
End If
End Sub
Public Sub ClearKeyboardBuffer()
Const WM_KEYFIRST = 256
Const WM_KEYLAST = 264
Const PM_REMOVE = 1
Dim msg As msg
Dim rc As Long
Do
rc = PeekMessage(msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)
Loop While rc <> 0
End Sub
Good Luck,
Elroy
EDIT1: This sample is all about the "e" key.
EDIT2: Also, I just made it repeat every 10 milliseconds, with no pause. But that could be adjusted to whatever you need.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Thanks, Elroy, but that doesn't solve the issue I'm running into. A single key seems to perform smoothly, even using the three ways I mentioned in the original post. The problem arises when switching from one key to another. For instance, say you're moving a ball on the form to the right with the D key, then you want to move the ball up with the W key. When immediately switching from D to W, there is a hesitation before the ball moves. That's the problem... getting rid of the hesitation.
Just got back. Also, I'm throwing together a procedure to process multiple keys. I'll post it in a few.
Best Regards,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Here, try throwing this into a Form1 (no controls), and see if it's something similar to what you're trying to do.
Code:
Option Explicit
'
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" (ByVal lpMsg As Long, ByVal hWnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
'
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
ProcessDownKeys 50&, vbKeyE, vbKeyS, vbKeyD, vbKeyX
End Sub
Private Sub ProcessDownKeys(millisecDelay As Long, ParamArray vArgs() As Variant)
' This doesn't return until all the specified keys are released.
' The ParamArray should contain a mutually exclusive list of the KeyCodeConstants (vbKey...).
' Will error if no arguments are provided.
' For any/all keys specified, there should also be a function in this form named Do?Key where the ? is the key name. If not found, an error will result.
' For the time being, this just works on the alphabet and numbers. Other keys to come later.
'
Static l(6) As Long ' Just a spoof for the lpMsg structure. We don't need its contents.
Dim i As Long
Dim bDown() As Boolean
Dim bSomeDown As Boolean
'
Do
Do While PeekMessage(VarPtr(l(0)), 0&, 256&, 264&, 1&) <> 0: Loop ' Clear keyboard buffer.
bSomeDown = False
ReDim bDown(UBound(vArgs))
For i = 0 To UBound(vArgs)
If GetKeyState(vArgs(i)) < 0& Then
bDown(i) = True
bSomeDown = True
CallByName Me, "Do" & Chr$(vArgs(i)) & "Key", VbMethod
End If
Next i
If Not bSomeDown Then Exit Sub
Debug.Print ' Just for testing.
Sleep millisecDelay
Loop
End Sub
Public Sub DoEKey()
Debug.Print "E";
End Sub
Public Sub DoSKey()
Debug.Print "S";
End Sub
Public Sub DoDKey()
Debug.Print "D";
End Sub
Public Sub DoXKey()
Debug.Print "X";
End Sub
Good Luck,
Elroy
EDIT1: Just looking at that code, I'm thinking I didn't handle key-code-combinations very well, but there's certainly no reason they couldn't be handled. Pretty much everything you'd need is listed in the KeyCodeConstants enumeration of the VBRUN library.
EDIT2: Here, I enhanced the above code to handle the arrow keys as well. And then, made a sort of PacMan-like thing with it. The project is attached:
Last edited by Elroy; Mar 11th, 2018 at 01:33 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
... The problem arises when switching from one key to another...
Hmm, when Form.KeyPreview is enabled, then you can solve the problem without any APIs, by using this simple Class here:
(name it cGameKeys):
Code:
Option Explicit
Public Enum enmDirection
kDirLeft = 1
kDirUp = 2
kDirRight = 4
kDirDown = 8
End Enum
Event StateChange(ByVal Up As Boolean)
Public State As enmDirection
Public Sub ProcessKeyEvt(KeyCode As Integer, ByVal Up As Boolean)
Dim D As enmDirection: D = State
Select Case KeyCode
Case 37, 65: D = IIf(Up, D And Not kDirLeft, D Or kDirLeft)
Case 38, 87: D = IIf(Up, D And Not kDirUp, D Or kDirUp)
Case 39, 68: D = IIf(Up, D And Not kDirRight, D Or kDirRight)
Case 40, 83: D = IIf(Up, D And Not kDirDown, D Or kDirDown)
End Select
If D <> State Then State = D: RaiseEvent StateChange(Up)
End Sub
Now, to test this Class (which allows to move a little red Circle even diagonally by using either the "wasd" or the arrow-keys),
you will have to add a simple Timer1-Control to a form and paste the following code into the Form:
Code:
Option Explicit
Private WithEvents GK As cGameKeys, x, y
Private Sub Form_Load()
KeyPreview = True: ScaleMode = vbPixels: DrawWidth = 12
x = ScaleWidth \ 2: y = ScaleHeight \ 2
Set GK = New cGameKeys
Timer1.Enabled = False: Timer1.Interval = 50
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
GK.ProcessKeyEvt KeyCode, False
End Sub
Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer)
GK.ProcessKeyEvt KeyCode, True
End Sub
Private Sub GK_StateChange(ByVal Up As Boolean)
Debug.Print "Key-"; IIf(Up, "Up", "Down"), GK.State
Timer1.Enabled = GK.State: If Not Up Then Timer1_Timer
End Sub
Private Sub Timer1_Timer()
If GK.State And kDirLeft Then x = x - 1
If GK.State And kDirUp Then y = y - 1
If GK.State And kDirRight Then x = x + 1
If GK.State And kDirDown Then y = y + 1
If GK.State Then Cls: PSet (x, y), vbRed: Caption = x & ", " & y
End Sub
HTH
Olaf
Last edited by Schmidt; Mar 11th, 2018 at 09:10 PM.
The take-home message is to forget about using the Windows key-repeat feature. You've got to (somehow) figure out when your keys-of-interest are down or up, and create your own repeat (possibly starting the moment it's detected that the key has been pressed).
Basically, that's what both Olaf and I have done.
Good Luck,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Re: [RESOLVED] Removing Delay On KeyPress/KeyDown...
Additionally, if there is a way to use Schmidt's code to include the diagonal directions using, for example, A and W to move Up-Left, that would also be helpful.
Re: [RESOLVED] Removing Delay On KeyPress/KeyDown...
as im creating a game i need the keys to work without issues.
its quite simple, when key down, it will set that key to true until key up. no need for a loop/timer or api.
now, in the game loop, if the key is true it will move.
other keys can only get pressed once, until the key is up, this to prevent repeats.