diff --git a/tutorials/inputs/img/inputs_inputmap.png b/tutorials/inputs/img/inputs_inputmap.png new file mode 100644 index 000000000..63b504845 Binary files /dev/null and b/tutorials/inputs/img/inputs_inputmap.png differ diff --git a/tutorials/inputs/index.rst b/tutorials/inputs/index.rst index 9c3695802..545114055 100644 --- a/tutorials/inputs/index.rst +++ b/tutorials/inputs/index.rst @@ -6,5 +6,6 @@ Inputs :name: toc-learn-features-inputs inputevent + input_examples mouse_and_input_coordinates custom_mouse_cursor diff --git a/tutorials/inputs/input_examples.rst b/tutorials/inputs/input_examples.rst new file mode 100644 index 000000000..109844c2b --- /dev/null +++ b/tutorials/inputs/input_examples.rst @@ -0,0 +1,244 @@ +.. _doc_input_examples: + +Input Examples +============== + +Introduction +------------ + +In this tutorial, you'll learn how to use Godot's :ref:`InputEvent ` +system to capture player input. There are many different types of input your +game may use - keyboard, gamepad, mouse, etc. - and many different ways to +turn those inputs into actions in your game. This document will show you some +of the most common scenarios, which you can use as starting points for your +own projects. + +.. note:: For a detailed overview of how Godot's input event system works, + see :ref:`doc_inputevent`. + +Events versus polling +--------------------- + +Sometimes you want your game to respond to a certain input event - pressing +the "jump" button, for example. For other situations, you might want something +to happen as long as a key is pressed, such as movement. In the first case, +you can use the ``_input()`` function, which will be called whenever an input +event occurs. In the second case, Godot provides the :ref:`Input ` +singleton, which you can use to query the state of an input. + +Examples: + +.. tabs:: + .. code-tab:: gdscript GDScript + + # input event - runs when the input happens + func _input(event): + if event.is_action_pressed("jump"): + jump() + + # polling - runs every frame + func _physics_process(delta): + if Input.is_action_pressed("move_right"): + # move as long as the key/button is pressed + position.x += speed * delta + +This gives you the flexibility to mix-and-match the type of input processing +you do. + +For the remainder of this tutorial, we'll focus on capturing individual +events in ``_input()``. + +Input events +------------ + +Input events are objects that inherit from :ref:`InputEvent `. +Depending on the event type, the object will contain specific properties +related to that event. To see what events actually look like, add a Node and +attach the following script: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Node + + func _input(event): + print(event.as_text()) + +As you press keys, move the mouse, and perform other inputs, you'll see each +event scroll by in the output window. Here's an example of the output: + +:: + + A + InputEventMouseMotion : button_mask=0, position=(551, 338), relative=(-85, 47), speed=(0, 0) + InputEventMouseButton : button_index=BUTTON_LEFT, pressed=true, position=(551, 338), button_mask=1, doubleclick=false + InputEventMouseButton : button_index=BUTTON_LEFT, pressed=false, position=(551, 338), button_mask=0, doubleclick=false + S + F + InputEventMouseMotion : button_mask=0, position=(547, 338), relative=(-1, 0), speed=(0, 0) + InputEventMouseMotion : button_mask=0, position=(542, 338), relative=(-4, 0), speed=(0, 0) + +As you can see, the results are very different for the different types of +input. Key events are even printed as their key symbols. For example, let's consider :ref:`InputEventMouseButton `. +It inherits from the following classes: + +- :ref:`InputEvent ` - the base class for all input events +- :ref:`InputEventWithModifiers ` - adds the ability to check if modifiers are pressed, such as ``Shift`` or ``Alt``. +- :ref:`InputEventMouse ` - adds mouse event properties, such as ``position`` +- :ref:`InputEventMouseButton ` - contains the index of the button that was pressed, whether it was a double-click, etc. + +.. tip:: It's a good idea to keep the class reference open while you're working + with events so you can check the event type's available properties and + methods. + +You can encounter errors if you try and access a property on an input type that +doesn't contain it - calling ``position`` on ``InputEventKey`` for example. To +avoid this, make sure to test the event type first: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventMouseButton: + print("mouse button event at ", event.position) + + +InputMap +-------- + +The :ref:`InputMap ` is the most flexible way to handle a +variety of inputs. You use this by creating named input *actions*, to which +you can assign any number of input events, such as keypresses or mouse clicks. +A new Godot project includes a number of default actions already defined. To +see them, and to add your own, open Project -> Project Settings and select +the InputMap tab: + +.. image:: img/inputs_inputmap.png + +Capturing actions +~~~~~~~~~~~~~~~~~ + +Once you've defined your actions, you can process them in your scripts using +``is_action_pressed()`` and ``is_action_released()`` by passing the name of +the action you're looking for: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event.is_action_pressed("my_action"): + print("my_action occurred!") + +Keyboard events +--------------- + +Keyboard events are captured in :ref:`InputEventKey `. +While it's recommended to use input actions instead, there may be cases where +you want to specifically look at key events. For this example, let's check for +the "T" key: + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventKey and event.pressed: + if event.scancode == KEY_T: + print("T was pressed") + +.. tip:: See :ref:`@GlobalScope_KeyList ` for a list of scancode + constants. + +Keyboard modifiers +~~~~~~~~~~~~~~~~~~ + +Modifier properties are inherited from +:ref:`InputEventWithModifiers `. This allows +you to check for modifier combinations using boolean properties. Let's imagine +you want one thing to happen when the "T" key is pressed, but something +different when it's "Shift+T": + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventKey and event.pressed: + if event.scancode == KEY_T: + if event.shift: + print("Shift+T was pressed") + else: + print("T was pressed") + +.. tip:: See :ref:`@GlobalScope_KeyList ` for a list of scancode + constants. + +Mouse events +------------ + +Mouse events stem from the :ref:`InputEventMouse ` class, and +are separated into two types: :ref:`InputEventMouseButton ` +and :ref:`InputEventMouseMotion `. Note that this +means that all mouse events will contain a ``position`` property. + +Mouse buttons +~~~~~~~~~~~~~ + +Capturing mouse buttons is very similar to handling key events. :ref:`@GlobalScope_ButtonList ` +contains a list of ``BUTTON_*`` constants for each possible button, which will +be reported in the event's ``button_index`` property. Note that the scrollwheel +also counts as a button - two buttons, to be precise, with both +``BUTTON_WHEEL_UP`` and ``BUTTON_WHEEL_DOWN`` being separate events. + +.. tabs:: + .. code-tab:: gdscript GDScript + + func _input(event): + if event is InputEventMouseButton: + if event.button_index == BUTTON_LEFT and event.pressed: + print("Left button was clicked at ", event.position) + if event.button_index == BUTTON_WHEEL_UP and event.pressed: + print("Wheel up") + +Mouse motion +~~~~~~~~~~~~ + +:ref:`InputEventMouseMotion ` events occur whenever +the mouse moves. You can find the move's distance with the ``relative`` +property. + +Here's an example using mouse events to drag-and-drop a :ref:`Sprite ` +node: + +.. tabs:: + .. code-tab:: gdscript GDScript + + extends Node + + var dragging = false + var click_radius = 32 # Size of the sprite + + func _input(event): + if event is InputEventMouseButton and event.button_index == BUTTON_LEFT: + if (event.position - $Sprite.position).length() < click_radius: + # Start dragging if the click is on the sprite. + if !dragging and event.pressed: + dragging = true + # Stop dragging if the button is released. + if dragging and !event.pressed: + dragging = false + + if event is InputEventMouseMotion and dragging: + # While dragging, move the sprite with the mouse. + $Sprite.position = event.position + +Touch events +------------ + +If you are using a touchscreen device, you can generate touch events. +:ref:`InputEventScreenTouch ` is equivalent to +a mouse click event, and :ref:`InputEventScreenDrag ` +works much the same as mouse motion. + +.. tip:: To test your touch events on a non-touchscreen device, open Project + Settings and go to the "Input Devices/Pointing" section. Enable "Emulate + Touch From Mouse" and your project will interpret mouse clicks and + motion as touch events.