3rd February 2012

Detecting Keypresses

Thanks to FOSE's IsKeyPressed function, it is possible to detect when a certain key is pressed, and run some code when it happens. This makes it possible to turn any key into a hotkey that activates a scripted action.

IsKeyPressed takes a single parameter, which specifies a key by its DirectX Scancode. It returns 1 when the specified key is being pressed, and 0 when it is not being pressed. Because of this, in order to run some code once when a key is pressed, it is necessary to store the previous state of the key in a variable.

Using the "N" key (scancode 49) as an example:

int bIsNPressed

Begin GameMode
	if bIsNPressed != IsKeyPressed 49 ; N
		set bIsNPressed to IsKeyPressed 49 ; N
	endif
End

Initially, the value of the variable "bIsNPressed" (which I have prefixed with "b" to specify that it is used to store boolean values only) is 0. "0 != IsKeyPressed 49" will return 1 when IsKeyPressed returns 1.

As soon as this happens, the value of "bIsNPressed" is set to the return value of IsKeyPressed, so the condition won't evaluate to 1 again until the value returned by IsKeyPressed changes to 0.

"bIsKeyPressed != IsKeyPressed 49" will always return 1 during the frame that the return value of IsKeyPressed changes. This will occur under two circumstances:

  • The "N" key is pressed
  • The "N" key is released

If "N" was just pressed, "bIsNPressed" will be 1. Likewise, if "N" was just released, "bIsNPressed" will be 0:

int bIsNPressed

Begin GameMode
	if bIsNPressed != IsKeyPressed 49 ; N
		set bIsNPressed to IsKeyPressed 49 ; N
		if bIsNPressed
			; N has been pressed
		else
			; N has been released
		endif
	endif
End

Sometimes, it can be useful to assign multiple functions to a hotkey. This can be done in a variety of ways, such as specifying a modifier key to be used, or giving the key a different function depending on whether it is held down or just tapped.

Multiple functions via a modifier:

int bIsNPressed

Begin GameMode
	if bIsNPressed != IsKeyPressed 49 ; N
		set bIsNPressed to IsKeyPressed 49 ; N
		if bIsNPressed
			if IsKeyPressed 50 ; M
				; Function 2
			else
				; Function 1
			endif
		endif
	endif
End

Because we only detect when the state of the scripted hotkey changes, the modifier key must be held down beforehand. In this example, holding "N" and tapping "M" will not activate the second function, but holding "M" and tapping "N" will.

Other modifiers, such as whether or not the player is currently sneaking, can be used just as easily in the same way.

Multiple functions by holding or tapping:

int bIsNPressed
int bNHeld

float fTimer

Begin GameMode
	if fTimer > 0
		set fTimer to fTimer - GetSecondsPassed
	elseif bIsNPressed > bNHeld ; bIsNPressed == 1 && bNHeld == 0
		set bNHeld to 1
		; Function 2 (Held)
	endif

	if bIsNPressed != IsKeyPressed 49 ; N
		set bIsNPressed to IsKeyPressed 49 ; N
		if bIsNPressed
			set fTimer to 0.8
			set bNHeld to 0
		elseif fTimer > 0
			set fTimer to 0
			; Function 1 (Tapped)
		endif
	endif
End

A similar script to this one can be used to assign a function which depends on how long the key was held for, which could be useful for something like an attack that gets powered up gradually:

int bIsNPressed
int bNHeld

float fTimer

Begin GameMode
	if fTimer > 0
		set fTimer to fTimer - GetSecondsPassed
	elseif bIsNPressed > bNHeld ; bIsNPressed == 1 && bNHeld == 0
		set bNHeld to 1
		; Function 6 (Held 5 seconds)
	endif

	if bIsNPressed != IsKeyPressed 49 ; N
		set bIsNPressed to IsKeyPressed 49 ; N
		if bIsNPressed
			set fTimer to 5
			set bNHeld to 0
		else
			set fTimer to 0
			if fTimer < 1
				; Function 5 (Held 4 seconds then released)
			elseif fTimer < 2
				; Function 4 (Held 3 seconds then released)
			elseif fTimer < 3
				; Function 3 (Held 2 seconds then released)
			elseif fTimer < 4
				; Function 2 (Held 1 second then released)
			elseif fTimer < 5
				; Function 1 (Tapped)
			endif
		endif
	endif
End

If you're confused at all by my "bIsNPressed > bNHeld" expression, keep in mind that both of these variables will only ever take two values - 0 or 1. The only combination which satisfies that condition is when "bIsNPressed" is 1, and "bNHeld" is 0. I've used this method because it's more efficient than the commented expression.