hi..

in my directx 7 game, i want the player to type in his name. of course i have to program the typing routines and stuff by myself. the code i use first fills the keyboard_state array (invoke ReadKeyboard), and then checks for a pressed letter (from A-Z). if it finds one, it appends the letter to the players name string, and displays it on the screen.

.data is here:
- kkeys represent the constants for DIK_A to DIK_Z (unfortunately the constants aren't in a chain of numbers)
-letters represent the ascii codes from A to Z
-onlettr holds one of the ascii codes depending on the result of "ReadKeyboard"
-plname1 holds the players name
if you see the code you'll get a better understanding of what the variables are for.



kkeys db 30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16,19,31,20,22,47,17,45,21,44
letters db 65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90
onelettr db 2 dup(0)
plname1 db "|",10 dup(0)


this is the code..



CheckLetters proc uses ebx edx esi edi
invoke ReadKeyboard
.if keyboard_state[DIK_RETURN]
mov typemode,0
.endif
xor eax,eax

@@:
lea esi,letters
lea edi,onelettr
xor ecx,ecx
mov cl,kkeys[eax]
.if keyboard_state[ecx]!=0
push eax
invoke GetCursor
pop eax
add esi,eax
movsb
push eax
invoke lstrcat,addr plname1,addr onelettr
invoke RtlZeroMemory,addr onelettr,2
pop eax
.endif
inc eax
cmp eax,26
jl @B
xor eax,eax
ret
CheckLetters endp



ok, now you'll ask, "where's the problem"? the problem is that obviously the pressed letter is attached two times to the player's name.. example:
this is before i press anything:
|

after pressing B:

|BB

then A:

|BBAA

i found out that the procedure returns after attaching one B, but somehow returns to the procedure and appends the letter once again! even when i clear keyboard_state after attaching the letter, it still puts a second letter to the player's name!

hope that anybody of you can help me.. if source is required, please tell me.

bye

: GetCursor API is just used for debugging purposes
Posted on 2002-12-26 08:02:43 by NOP-erator
Afternoon, NOP-erator.

Missing some code in this proc?


xor eax,eax

@@:
lea esi,letters
lea edi,onelettr
xor ecx,ecx
mov cl,kkeys[eax]


What's eax?

Cheers,
Scronty
Posted on 2002-12-27 05:46:35 by Scronty
eax is used as a counter.. you'll find it at the bottom of the code:



inc eax
cmp eax,26
jl @B
Posted on 2002-12-27 07:46:01 by NOP-erator
Originally posted by NOP-erator
: GetCursor API is just used for debugging purposes

you seem to debug with SoftICE. in far ago times i used apis too as breakpoints, but it's much easier:
instead of you api, simply put


int 3

instead of your api-call.
this int-call is specified as a breakpoint, but you have to tell softice to break on it, so type


i3here on

to activate; sure, this int call won't change any registers, it just fires up softice. (usually debuggers use this one)

i'll take a look at your code too
Posted on 2002-12-27 14:45:15 by hartyl
that's a great trick! thank you, i'm sure i'll use that next time..

bye
Posted on 2002-12-27 16:55:34 by NOP-erator
Afternoon, NOP-erator.

Try having something like this then:


.if keyboard_state[ecx]!=0
mov keyboard_state[ecx], 0
push eax


I'm under the impression that DInput uses Unicode, not ASCII.

Cheers,
Scronty
Posted on 2002-12-27 17:47:26 by Scronty
nope.. unfortunately that doesn't change anything.. same problem..
Posted on 2002-12-27 18:00:53 by NOP-erator
Afternoon, NOP-erator.

Could be that the key is being held down too long?

What happens if you hold down the key for half a second? What if you just quickly "tap" a key?

Cheers,
Scronty
Posted on 2002-12-27 18:18:43 by Scronty
i thought of that too, but just forgot to mention it here. no, i had the same problem, no matter if i just "tap" the key, or even hold the key down longer. holding the key down longer just adds two another letters after some milliseconds.. i even played with the keyboard settings in the control panel.. --> no, that's not the problem either..

:(

bye..
Posted on 2002-12-28 03:52:46 by NOP-erator
Afternoon, NOP-erator.

The only other thing I can think of, is that your string-displaying code may be in error.

Have you tried setting plname1 to a known text (i.e. "|1234",0) and displaying that? Does it come out correctly?

Cheers,
Scronty
Posted on 2002-12-28 06:12:32 by Scronty
yup.. it comes out correctly.. the string-displaying routine is used very often with other strings, everything works perfect.. so the error isn't in the string-displaying routine.. :( :(
Posted on 2002-12-30 04:27:04 by NOP-erator
hi again!

hm.. i still don't know where the error is.. but i build in some check if the letter is added two times:

now this little letter adding routine looks like this:



.if keyboard_state[ecx]!=0
mov keyboard_state[ecx],0
add esi,eax
movsb
push eax
invoke lstrcmp,addr oldlettr,addr onelettr
.if eax==0
invoke RtlZeroMemory,addr oldlettr,2
jmp _dontcopy
.endif
invoke lstrcat,addr plname1,addr onelettr
invoke lstrcpy,addr oldlettr,addr onelettr
invoke RtlZeroMemory,addr onelettr,2
_dontcopy:
pop eax
.endif


you see, if a key was pressed, the appropriate letter is added to plname1, and is saved in "oldlettr". when the program returns to this routine and tries to add the letter again to the players name, then oldlettr is compared with onelettr, and if they're the same, just don't do anything! of course oldlettr is cleared, because otherwise you could never type two "A"s one after the other.

now everything works fine ^^ ..

but i'm very curious where the error actually is. the only possibility i see is, that the key you press is sent two times to my program. because it doesn't help clearing the keyboard_state array after getting the letter the first time.. the program returns to my little routine and again finds the same letter in keyboard_state pressed.. :confused:

ok.. the most important thing is that it works now.. :tongue:
Posted on 2002-12-30 04:38:09 by NOP-erator
that reads the keyboard with Direct Input



Read_Keyboard:
;=============

call save_to_old_state_key

call Acquire_Keyboard


mov eax, offset keyboard_state01
push eax

mov eax,256 ;read all keys
push eax

mov eax,[lp_di_keyboard_device]
push eax
mov eax,[eax]
call [eax+DID_GetDeviceState]

.IF eax!=DI_OK
Call OutputDebugValue,eax
Call OutputDebugStringA,offset szDIKeyboardGetDeviceStateFail
call Fail, hwndmain, offset szDIKeyboardGetDeviceStateFail
jmp end_loop
.ENDIF

; Call OutputDebugStringA,offset szDIKeyboardGetDeviceStateOK

ret



;=========================================
;IN:eax=key OUT:eax=flag YES/NO
;=========================================
get_key_state:

mov [tmp_key],eax

call get_key_old_state
cmp eax,0
jnz key_not_pressed

mov eax,[tmp_key]

mov ecx,offset key_state_all
add ecx,eax

mov bl,[ecx]
cmp bl,80h
jnz key_not_pressed

mov eax,1

ret

key_not_pressed:
mov eax,0

ret



Posted on 2002-12-30 04:40:40 by BogdanOntanu
:grin:

this is "my" code:



ReadKeyboard proc
.if lpdikey != NULL
DIDEVINVOKE GetDeviceState, lpdikey, 256, ADDR keyboard_state
.if eax!=DI_OK
FATAL "Could not read keyboard!"
.endif
.else
DIINITSTRUCT ADDR keyboard_state, 256
.endif
ret
ReadKeyboard endp


:tongue:
Posted on 2002-12-30 04:45:29 by NOP-erator
Yeah :P

The ideea above was that you must first save old state and then only if old_state==0 and current_state==80h ONLY then you have a valid key :)


the above HE code is old ... but it works ;)
Posted on 2002-12-30 04:52:06 by BogdanOntanu
alright, thank you! :alright:

so it turns out that it isn't really my fault :tongue:
Posted on 2002-12-30 04:54:55 by NOP-erator
Hehehehe,

.data
the_Keys db

.code

Mov edx,0x61
in eax,edx
and 11111111b
LEA the_Keys


Maybe?.....
Posted on 2003-01-10 11:53:17 by realvampire
hmm.. maybe *g* .. but it works now.. and i don't want to mess around with ports and stuff.. i'll use dx-functions for compatibility reasons..

thank you anyway!

bye
Posted on 2003-01-10 17:06:04 by NOP-erator
Hi all!

ok, my snake game is a bit buggy when trying to go through walls. i can't find the error, and think that the problem could be the key that is send twice to my program. you never know i thought..

ok, this is what i added to my ReadKeyboard procedure to prevent ghost-press, you can find the original ReadKeyboard some postings above:



ReadKeyboard proc uses esi edi edx ebx

.if lpdikey != NULL
DIDEVINVOKE GetDeviceState, lpdikey, 256, ADDR keyboard_state
.if eax!=DI_OK
FATAL "Could not read keyboard!"
.endif
lea esi,keyboard_state
cmp key_old_pos,0 ; is there an old key saved?
ja _checkforoldkey ; if yes, check if old key is pressed again in keyboard_state
jmp _findletter ; if no, find a key and save it in old key
_checkforoldkey:
add esi,key_old_pos ; add the position where the old key was found
cmp byte ptr [esi],0 ; is the key pressed again
ja _resetoldkey ; if yes, then clear keyboard_state and old key
_findletter:
xor ecx,ecx
lea esi,keyboard_state
@@: cmp byte ptr [esi],0 ; is there a letter pressed?
ja _foundletter ; if yes, save it in old key
inc esi ; if no, check the whole keyboard_state array
inc ecx
cmp ecx,257
je _resetoldkey ; if there was no pressed key found, set old key to zero
jmp @B
_foundletter:
mov key_old_pos,ecx ; if there was a letter found save it in old key
jmp _end
_resetoldkey:
mov key_old_pos,0
mov byte ptr [esi],0
_end:
.else

DIINITSTRUCT ADDR keyboard_state, 256
.endif
ret
ReadKeyboard endp


but: if i play my game now, it seems that there are a lot of keys not recognized that i press. of course, in a game like snake, this is "unpretty". now my question: is there a logical error in the code, or is the code just to slow? no matter what, could you help me to find a better way (especially bogdan)?

thanks in advance..
NOP
Posted on 2003-01-17 17:18:23 by NOP-erator
Well i will not examine your code now...

But instead i will explain you how i do it in HE:

Kepp in mind that HE is a realtime game;
(i belive the code for this was posted above if i am wrong let me know)

1)At each frame start i save the crt keyboard state to old_key_state and then i read the new keyboard state in.

2)AT variouse moments in game loop i use get_gey_state procedure or get_key_state_async procedure to test if a key was pressed since last read and/or if a key is pressed/hold down right now. Based on true/false flags returne by those 2 functions i do required tasks/actions

3) Go to 1)

Conclusion:
-----------------
As you can notice i never care to check each and every key and generate a message if something new happened... i do not need to .... i only check for keys i know are appropiate/required in that game phase

GetKeyState (as any other DI state reading function) is not appropiate IF your game loop is slow... is it is very likely to miss events if you do not read the "state" fast enough

You will have a problem if your game can not cope with missing keys/events...i doubt it is the case for such a simple game...but who knows

In such cases one of the callback/event driven functions should be used instead...

We use such a function when reading the mouse in HE because it is not nice to loose mouse moves :)
Posted on 2003-01-18 14:35:14 by BogdanOntanu