Skip to content
bozar42 edited this page Apr 10, 2020 · 7 revisions

04: Listen Keyboard Inputs

Source code.

At the end of this chapter, your will be able to move pc sprite by arrow keys.

Respond To Inputs Through Output Messages

Check out commit: 11fbf11.

Godot provides several ways to handle inputs on different levels. The topic is covered in Tutorials/Inputs. Our approach in this demo involves two steps. First register inputs with a specific name in Project Settings/Input Map. Then respond to input events in scripts by implementing _unhandled_input().

Open Input Map (official tutorial). Bind arrow keys and Vi keys (hjkl) to one of four actions: move_left, move_right, move_up and move_down.

Add PCMove (Node2D node) to MainScene node. Attach PCMove.gd to the newly created node. Add InputName.gd to library/ folder to store action names as string constants. Inside PCMove.gd, write code to respond to keyboard inputs by printing messages in the console window.

# InputName.gd

const MOVE_LEFT: String = "move_left"


# PCMove.gd

var _new_InputName := preload("res://library/InputName.gd").new()


func _unhandled_input(event: InputEvent) -> void:
    if event.is_action_pressed(_new_InputName.MOVE_LEFT):
        print("move left")

Try To Move PC

Check out commit: fb307f1.

Moving PC involves three steps:

  • Get a reference to pc node in PCMove.gd.
  • Convert inputs to a pair of integers.
  • Set PC's new position based on the two integers.

Let's solve step two first.

# PCMove.gd

func _unhandled_input(event: InputEvent) -> void:
    var x: int = 0
    var y: int = 0

    if event.is_action_pressed(_new_InputName.MOVE_LEFT):
        x -= 1
    elif event.is_action_pressed(_new_InputName.MOVE_RIGHT):
        x += 1
    elif event.is_action_pressed(_new_InputName.MOVE_UP):
        y -= 1
    elif event.is_action_pressed(_new_InputName.MOVE_DOWN):
        y += 1

In order to get a reference to pc node, we can call get_tree().get_nodes_in_group("pc")[0]. Because according to InitWorld.gd, pc group has only one member, that is, pc node. However, I think this is a good chance to introduce signal. Let InitWorld.gd emit a signal, sprite_created, whenever a new sprite is instanced. PCMove.gd receives and decodes the signal and gets a reference to pc.

# InitWorld.gd

signal sprite_created(new_sprite)


func _create_sprite(prefab: PackedScene, group: String, x: int, y: int,
        x_offset: int = 0, y_offset: int = 0) -> void:

    # Add this line to the end.
    emit_signal("sprite_created", new_sprite)


# PCMove.gd

var _pc: Sprite


func _ready() -> void:
    var __ = get_node("../InitWorld").connect("sprite_created", self,
            "_on_InitWorld_sprite_created")
    print("connect: {0}".format([__]))


func _unhandled_input(event: InputEvent) -> void:
    print("pc: {0}".format([_pc]))

    var source: Array = _new_ConvertCoord.vector_to_array(_pc.position)
    var x: int = source[0]
    var y: int = source[1]

    if event.is_action_pressed(_new_InputName.MOVE_LEFT):
        x -= 1
    elif event.is_action_pressed(_new_InputName.MOVE_RIGHT):
        x += 1
    elif event.is_action_pressed(_new_InputName.MOVE_UP):
        y -= 1
    elif event.is_action_pressed(_new_InputName.MOVE_DOWN):
        y += 1

    _pc.position = _new_ConvertCoord.index_to_vector(x, y)


func _on_InitWorld_sprite_created(new_sprite: Sprite) -> void:
    if new_sprite.is_in_group(_new_GroupName.PC):
        _pc = new_sprite

When testing the game, it turns out that even though connect returns 0, which means that sprite_created is successfully connected with _on_InitWorld_sprite_created(), the reference to pc node is still null. This is due to the fact that PCMove._ready() is called after InitWorld._ready(). The signal is connected to the function, but it is not received even once. One possible solution is to move initialization code from InitWorld._ready() to InitWorld._process().

# InitWorld.gd

var _initialized: bool = false


func _process(_delta) -> void:
    if not _initialized:
        _initialized = true
        _init_floor()
        _init_wall()
        _init_dwarf()
        _init_PC()
        _init_indicator()

The full code at current stage is available at the start of this part. We will take another approach to this problem in the next part.

Clone this wiki locally