• Andrew Jonhardt

Going too far into the Godot input methods, probably

Today's post is late because of some changes I've been trying. Specifically, I've started trying to do at least 30 minutes of programming in Godot every day. So far, with covid and my day job messing up my social life, I've managed to find the time.


My increased productivity and stress has resulted in an obvious downside that I felt the full impact of Friday: simple exhaustion. It's nowhere near as bad as when I was trying to do a full hour every day, but I did find myself completely disinterested in even looking at my main project when I woke up Saturday. However, I don't feel that I'm at the point yet of needing to return to working only on the weekends. On the contrary, the exhaustion I experienced Friday feels closer to the exhaustion one gets after exercise versus what one might feel after staying up for 24 hours. It feels good.


To further support this, I followed up the crash of Friday by working 6 hours straight on my second project. Now, the work I put in I'd classify as experimental: I became obsessed with reducing the ugly mass of my character input code down to something slimmer and easier to process. To some, this might read as "I proceeded to completely waste my time". However, I now feel that I have a much greater understanding of how the input system works in Godot.


The Input call, typically used like this

Input.is_action_pressed("move_right")

is the simplest and arguably most robust input handling method. For one thing, you don't need alot of extra muss to use it. You can use shortcuts like

direction.x = Input.get_action_strength("right") - \
	Input.get_action_strength("left")

to reduce the size of your movement code to a single freaking line (depending on your game).


By contrast, the (apparently) recommended input method is the _unhandled_input function:

func _unhandled_input(event):
    if event is InputEventKey:
        if event.pressed and event.scancode == KEY_ESCAPE:
            get_tree().quit()

_unhandled_input catches the inputs the _input (more on this one in a moment) and GUI scripts don't catch. So, hypothetically, using _unhandled_input is better because it lets you easily create and manage menus. Unfortunately, it's way easier to get behavior like this when you try to take shortcuts:


The green cube in the above image is freezing when inputs overlap. Specifically, left and right. I never figured out a solution that would allow me to combine _unhandled_input with a custom dictionary of inputs

var inputs = {"right": Vector2.RIGHT,
			"left": Vector2.LEFT,
			"up": Vector2.UP,
			"down": Vector2.DOWN}

However, if I used almost the same code with Input, ie

func player_input():
	for imp in inputs.keys():
		if Input.is_action_pressed(imp):
			filter_input(imp)

instead of

func _unhandled_input(event):
	for imp in inputs.keys():
		if event.is_action_pressed(imp,true):
			filter_input(imp)

in combination with

func filter_input(imp):
	if typeof(inputs[imp]) == 5:
		direction = inputs[imp]

suddenly everything works!


Now, there's a 3rd method that I tried as well, the _input function

func _input(event):
    if event.is_action_pressed("jump"):
        jump()

Unfortunately, using _input resulted in the exact same behavior as _unhandled_input.


From the little research I've done, it seems the _input and _unhandled_input functions are preferred because they only react to input. The Input method, by contrast, is constantly checking with your operating system to confirm if an input has been pressed. Both _input and _unhandled_input are also far more customizable.


The Input method constantly checking if a button has been pressed explains the issue shown in the gifs. I could only get the player character to respond, even though it continues freezing, when I used

event.is_action_pressed(imp,true)

instead of the normal

event.is_action_pressed(imp)

The true flag forces the input event system to begin accepting duplicate inputs.


With the method I was attempting, the left and right inputs would occasionally overlap while I was mashing the left and right keys on my keyboard. The player would then, correctly, freeze as the left and right inputs canceled each other out. However, when I lifted my finger off of one of the keys (I was mashing, so this was all happening very quickly), the input event system (tied to _input and _unhandled_input) would still remember the overlap. If I didn't set the event system to keep duplicate inputs, it would then keep the player frozen until I tapped the key I was holding again. However, even with the event system keeping duplicate inputs, there remains a brief freeze as the event system waits for... whatever it's waiting for.


The Input method, by contrast, runs constantly, and even gives primacy to the last key pressed. So, even though I mashed keys just as much when using

Input.is_action_pressed(imp)

I was doing so fast enough that 1 key always had priority, and the player object never stopped moving.


So, if you're worried about performance, use _input and _unhandled_input for things like jumping, dodging, etc, to ensure a player's computer isn't always checking like

"Did they roll yet?"

"Did they roll yet?"

"Did they roll yet?"

...etc


Me, I'm lazy, so I might never try using _input and _unhandled_input again. Who knows.


This is not the post I wanted to write this weekend. Part of the reason I put work into my second project over my first was because I wanted to show off player state machines, I don't have a state machine setup for the player in Project Splatter, and I might never need one. Yes, I finally think I understand what a state machine is, and I feel dumb for ever feeling like I didn't. There're basically just this:

func _process(delta):
	match state:
		IDLE:
			idle()
		WALKING:
			walking(delta)
		HAMMER:
			hammer()
		LADDER:
			ladder()
		FALLING:
			player_input()
		HURT:
			hurt()

The problem with state machines is the logic. I've made a few for the bosses in Project Splatter, but I'm having trouble ironing out the logic for a player state machine. I'm positive I'll have it sorted before the 2nd of August, so expect my next post to be about state machines!


Fuck, time flies.

0 views

© 2023 by Andrew Jonhardt. Proudly created with Wix.com