Browse Source

Write first script and player input pages

Closes #4173
Nathan Lovato 4 years ago
parent
commit
f68abde1e8
20 changed files with 438 additions and 42 deletions
  1. 1 1
      classes/class_script.rst
  2. BIN
      getting_started/step_by_step/img/scripting_first_script_add_sprite_node.png
  3. BIN
      getting_started/step_by_step/img/scripting_first_script_attach_node_script.png
  4. BIN
      getting_started/step_by_step/img/scripting_first_script_attach_script.png
  5. BIN
      getting_started/step_by_step/img/scripting_first_script_centering_sprite.png
  6. BIN
      getting_started/step_by_step/img/scripting_first_script_click_other_node.png
  7. BIN
      getting_started/step_by_step/img/scripting_first_script_dragging_sprite.png
  8. BIN
      getting_started/step_by_step/img/scripting_first_script_godot_turning_in_place.gif
  9. BIN
      getting_started/step_by_step/img/scripting_first_script_moving_with_input.gif
  10. BIN
      getting_started/step_by_step/img/scripting_first_script_print_hello_world.png
  11. BIN
      getting_started/step_by_step/img/scripting_first_script_property_tooltips.png
  12. BIN
      getting_started/step_by_step/img/scripting_first_script_rotating_godot.gif
  13. BIN
      getting_started/step_by_step/img/scripting_first_script_scene_tree.png
  14. BIN
      getting_started/step_by_step/img/scripting_first_script_setting_texture.png
  15. 2 1
      getting_started/step_by_step/index.rst
  16. 29 29
      getting_started/step_by_step/scripting_continued.rst
  17. 257 0
      getting_started/step_by_step/scripting_first_script.rst
  18. 141 0
      getting_started/step_by_step/scripting_player_input.rst
  19. 0 5
      getting_started/step_by_step/your_first_script.rst
  20. 8 6
      tutorials/scripting/gdscript/gdscript_basics.rst

+ 1 - 1
classes/class_script.rst

@@ -25,7 +25,7 @@ The ``new`` method of a script subclass creates a new instance. :ref:`Object.set
 Tutorials
 Tutorials
 ---------
 ---------
 
 
-- :doc:`../getting_started/step_by_step/your_first_script`
+- :doc:`../getting_started/step_by_step/scripting_first_script`
 
 
 Properties
 Properties
 ----------
 ----------

BIN
getting_started/step_by_step/img/scripting_first_script_add_sprite_node.png


BIN
getting_started/step_by_step/img/scripting_first_script_attach_node_script.png


BIN
getting_started/step_by_step/img/scripting_first_script_attach_script.png


BIN
getting_started/step_by_step/img/scripting_first_script_centering_sprite.png


BIN
getting_started/step_by_step/img/scripting_first_script_click_other_node.png


BIN
getting_started/step_by_step/img/scripting_first_script_dragging_sprite.png


BIN
getting_started/step_by_step/img/scripting_first_script_godot_turning_in_place.gif


BIN
getting_started/step_by_step/img/scripting_first_script_moving_with_input.gif


BIN
getting_started/step_by_step/img/scripting_first_script_print_hello_world.png


BIN
getting_started/step_by_step/img/scripting_first_script_property_tooltips.png


BIN
getting_started/step_by_step/img/scripting_first_script_rotating_godot.gif


BIN
getting_started/step_by_step/img/scripting_first_script_scene_tree.png


BIN
getting_started/step_by_step/img/scripting_first_script_setting_texture.png


+ 2 - 1
getting_started/step_by_step/index.rst

@@ -19,7 +19,8 @@ where appropriate.
    nodes_and_scenes
    nodes_and_scenes
    instancing
    instancing
    scripting_languages
    scripting_languages
-   your_first_script
+   scripting_first_script
+   scripting_player_input
    scripting_continued
    scripting_continued
    signals
    signals
    your_first_game
    your_first_game

+ 29 - 29
getting_started/step_by_step/scripting_continued.rst

@@ -3,21 +3,21 @@
 Built-in callbacks
 Built-in callbacks
 ==================
 ==================
 
 
-Processing
-----------
+Idle and Physics Processing
+---------------------------
+
+Several actions in Godot are triggered by callbacks or virtual functions, so there is no need to write code that runs all the time. However, it is still common to need a script to be processed on every frame.
 
 
-Several actions in Godot are triggered by callbacks or virtual functions,
-so there is no need to write code that runs all the time.
+There are two types of processing:
 
 
-However, it is still common to need a script to be processed on every
-frame. There are two types of processing: idle processing and physics
-processing.
+1. Idle processing allows you
+2. Physics.
 
 
 Idle processing is activated when the method :ref:`Node._process() <class_Node_method__process>`
 Idle processing is activated when the method :ref:`Node._process() <class_Node_method__process>`
 is found in a script. It can be turned off and on with the
 is found in a script. It can be turned off and on with the
 :ref:`Node.set_process() <class_Node_method_set_process>` function.
 :ref:`Node.set_process() <class_Node_method_set_process>` function.
 
 
-This method will be called every time a frame is drawn:
+The engine calls this method every time it draws a frame:
 
 
 .. tabs::
 .. tabs::
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
@@ -33,30 +33,32 @@ This method will be called every time a frame is drawn:
         // Do something...
         // Do something...
     }
     }
 
 
-It's important to bear in mind that the frequency with which ``_process()``
-will be called depends on how many frames per second (FPS) your application
-is running at. This rate can vary over time and devices.
+It's important to bear in mind that the frequency with which ``_process()`` will
+be called depends on how many frames per second (FPS) your application is
+running at. This rate can vary over time and devices.
 
 
 To help manage this variability, the ``delta`` parameter contains the time
 To help manage this variability, the ``delta`` parameter contains the time
-elapsed in seconds as a floating-point number since the previous call to ``_process()``.
+elapsed in seconds as a floating-point number since the previous call to
+``_process()``.
 
 
-This parameter can be used to make sure things always take the same
-amount of time, regardless of the game's FPS.
+This parameter can be used to make sure things always take the same amount of
+time, regardless of the game's FPS.
 
 
 For example, movement is often multiplied with a time delta to make movement
 For example, movement is often multiplied with a time delta to make movement
 speed both constant and independent of the frame rate.
 speed both constant and independent of the frame rate.
 
 
-Physics processing with ``_physics_process()`` is similar, but it should be used for processes that
-must happen before each physics step, such as controlling a character.
-It always runs before a physics step and it is called at fixed time intervals:
-60 times per second by default. You can change the interval from the Project Settings, under
-Physics -> Common -> Physics Fps.
+Physics processing with ``_physics_process()`` is similar, but it should be used
+for processes that must happen before each physics step, such as controlling a
+character. It always runs before a physics step and it is called at fixed time
+intervals: 60 times per second by default. You can change the interval from the
+Project Settings, under Physics -> Common -> Physics Fps.
 
 
-The function ``_process()``, however, is not synced with physics. Its frame rate is not constant and is dependent
-on hardware and game optimization. Its execution is done after the physics step on single-threaded games.
+The function ``_process()``, however, is not synced with physics. Its frame rate
+is not constant and is dependent on hardware and game optimization. Its
+execution is done after the physics step on single-threaded games.
 
 
-A simple way to see the ``_process()`` function at work is to create a scene with a single Label node,
-with the following script:
+A simple way to see the ``_process()`` function at work is to create a scene
+with a single Label node, with the following script:
 
 
 .. tabs::
 .. tabs::
  .. code-tab:: gdscript GDScript
  .. code-tab:: gdscript GDScript
@@ -145,12 +147,10 @@ calling
 
 
     var enemies = GetTree().GetNodesInGroup("enemies");
     var enemies = GetTree().GetNodesInGroup("enemies");
 
 
-The :ref:`SceneTree <class_SceneTree>` class provides many useful methods,
-like interacting with scenes, their node hierarchy and groups of nodes.
-It allows you to easily switch scenes or reload them,
-to quit the game or pause and unpause it.
-It even comes with interesting signals.
-So check it out if you have some time!
+The :ref:`SceneTree <class_SceneTree>` class provides many useful methods to
+interact with scenes, their node hierarchy, and groups of nodes. It allows you
+to easily switch scenes or reload them, to quit the game or pause and unpause
+it. It also provides useful signals.
 
 
 Notifications
 Notifications
 -------------
 -------------

+ 257 - 0
getting_started/step_by_step/scripting_first_script.rst

@@ -0,0 +1,257 @@
+..
+    Intention:
+
+    - Giving a *short* and sweet hands-on intro to GDScript. The page should
+      focus on working in the code editor.
+    - We assume the reader has programming foundations, as explained in
+    getting_started/introduction.
+
+    Techniques:
+
+    - Creating a sprite.
+    - Creating a script.
+    - _init() and _process().
+    - Moving an object on screen.
+
+.. _doc_scripting_first_script:
+
+Creating your first script
+==========================
+
+In this lesson, you will code your first script to make the Godot icon turn in
+circles using GDScript. As we mentioned :ref:`in the introduction
+<toc-learn-introduction>`, we assume you have programming foundations.
+
+.. image:: img/scripting_first_script_rotating_godot.gif
+
+.. seealso:: To learn more about GDScript, its keywords, and its syntax, head to
+             the :ref:`GDScript reference<doc_gdscript>`.
+
+Project setup
+-------------
+
+Please :ref:`create a new project <doc_creating_and_importing_projects>` to
+start with a clean slate. Your project should contain one picture: the Godot
+icon, which we often use for prototyping in the community.
+
+.. Godot icon
+
+We need to create a Sprite node to display it in the game. In the Scene dock,
+click the Other Node button.
+
+.. image:: img/scripting_first_script_click_other_node.png
+
+Type "Sprite" in the search bar to filter nodes and double-click on Sprite to
+create the node.
+
+.. image:: img/scripting_first_script_add_sprite_node.png
+
+Your Scene tab should now only have a Sprite node.
+
+.. image:: img/scripting_first_script_scene_tree.png
+
+A Sprite node needs a texture to display. In the Inspector on the right, you can
+see that the Texture property says "[empty]". To display the Godot icon, click
+and drag the file ``icon.png`` from the FileSystem dock onto the Texture slot.
+
+.. image:: img/scripting_first_script_setting_texture.png
+
+.. note::
+
+    You can create Sprite nodes automatically by dragging and dropping images on
+    the viewport.
+
+    .. image:: img/scripting_first_script_dragging_sprite.png
+
+Then, click and drag the icon in the viewport to center it in the game view.
+
+.. image:: img/scripting_first_script_centering_sprite.png
+
+Creating a new script
+---------------------
+
+To create and attach a new script to our node, right-click on Sprite in the
+scene dock and select "Attach Script".
+
+.. image:: img/scripting_first_script_attach_script.png
+
+The Attach node Script window appears. It allows you to select the script's
+language and file path, among other options.
+
+Change the Template from Default to Empty to start with a clean file. Leave the
+other options by default and click the Create button to create the script.
+
+.. image:: img/scripting_first_script_attach_node_script.png
+
+The Script workspace should appear with your new file open and the following
+line of code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Sprite
+
+Every GDScript file is implicitly a class. The ``extends`` keyword defines the
+class this script inherits or extends. In this case, it's ``Sprite``, meaning
+our script will get access to all the properties and functions of the Sprite
+node, including classes it extends, like ``Node2D``, ``CanvasItem``, and
+``Node``.
+
+.. note:: In GDScript, if you omit the line with the ``extends`` keyword, your
+          class will implicitly extend :ref:`Reference <class_Reference>`, which
+          Godot uses to manage your application's memory.
+
+Inherited properties include the ones you can see in the Inspector dock, like
+our node's ``texture``.
+
+.. note::
+
+    By default, the Inspector displays a node's properties in "Title Case", with
+    capitalized words separated by a space. In GDScript code, these properties
+    are in "snake_case", lowercase, and words separated by an underscore.
+
+    You can hover any property's name in the Inspector to see a description and
+    its identifier in code.
+
+Hello, world!
+-------------
+
+Our script currently doesn't do anything. Let's make it print the text "Hello,
+world!" to the Output bottom panel to get started.
+
+Add the following code to your script:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _init():
+        print("Hello, world!")
+
+Let's break it down. The ``func`` keyword defines a new function named
+``_init``. This is a special name for our class's constructor. The engine calls
+``_init()`` on every object or node upon creating it in memory, if you define
+this function.
+
+.. note:: GDScript is an indent-based language. The tab at the start of the line
+          that says ``print()`` is necessary for the code to work. If you omit
+          it or don't indent a line correctly, the editor will highlight it in
+          red and display the following error message: "Unexpected indentation."
+
+Save the scene if you haven't already, then press :kbd:`F6` to run it. Look at
+the Output bottom panel that expands. It should display "Hello, world!"
+
+.. image:: img/scripting_first_script_print_hello_world.png
+
+Delete the ``_init()`` function, so you're only left with the line ``extends
+Sprite``.
+
+Turning around
+--------------
+
+It's time to make our node move and rotate. To do so, we're going to add two
+member variables to our script: the movement speed in pixels per second and the
+angular speed in radians per second.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Sprite
+
+    var speed = 400
+    var angular_speed = PI
+
+Member variables sit at the top of the script, before functions. Every node
+instance with this script attached to it will have its own copy of the ``speed``
+and ``angular_speed`` properties.
+
+.. note:: As in some other engines, angles in Godot work in radians by default,
+          but you have built-in functions and properties available if you prefer
+          to calculate angles in degrees instead.
+
+To move our icon, we need to update its position and rotation every frame in the
+game loop. We can use the ``_process()`` virtual function of the ``Node`` class.
+If you define it in any class that extends the Node class, like Sprite, Godot
+will call the function every frame and pass it an argument named ``delta``, the
+time elapsed since the last frame.
+
+.. note::
+
+    Games work by rendering many images per second, each called a frame, and
+    they do so in a loop. We measure the rate at which a game produces images in
+    Frames Per Second (FPS). Most games aim for 60 FPS, although you might find
+    figures like 30 FPS on slower mobile devices or 90 to 240 for virtual
+    reality games.
+
+    The engine and game developers do their best to update the game world and
+    render images at a constant time interval, but there are always small
+    variations in frame render times. That's why the engine provides us with
+    this delta time value, making our motion independent of our framerate.
+
+At the bottom of the script, define the function:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _process(delta):
+        rotation += angular_speed * delta
+
+The ``func`` keyword defines a new function. After it, we have to write the
+function's name and arguments it takes in parentheses. A colon ends the
+definition, and the indented blocks that follow are the function's content or
+instructions.
+
+.. note:: Notice how ``_process()``, like ``_init()``, starts with a leading
+          underscore. By convention, Godot's virtual functions, that is to say,
+          built-in functions you can override to communicate with the engine,
+          start with an underscore.
+
+The line inside the function, ``rotation += angular_speed * delta``, increments
+our sprite's rotation every frame. Here, ``rotation`` is a property inherited
+from the class ``Node2D``, which ``Sprite`` extends. It controls the rotation of
+our node and works with radians.
+
+.. tip:: In the code editor, you can ctrl-click on any built-in property or
+         function like ``position``, ``rotation``, or ``_process`` to open the
+         corresponding documentation in a new tab.
+
+Run the scene to see the Godot icon turn in-place.
+
+.. image:: img/scripting_first_script_godot_turning_in_place.gif
+
+Moving forward
+~~~~~~~~~~~~~~
+
+Let's now make the node move. Add the following two lines to the ``_process()``
+function, ensuring the new lines are indented the same way as the one before
+them.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    var velocity = Vector2.UP.rotated(rotation) * speed
+    position += velocity * delta
+
+As we already saw, the ``var`` keyword defines a new variable. If you put it at
+the top of the script, it defines a property of the class. Inside a function, it
+defines a local variable: it only exists within the function's scope.
+
+We define a local variable named ``velocity``, a 2D vector representing both a
+direction and a speed. To make the node move forward, we start from the Vector2
+class's constant Vector2.UP, a vector pointing up, and rotate it by calling the
+``Vector2.rotated()`` method. This expression, ``Vector2.UP.rotated(rotation)``,
+is a vector pointing forward relative to our icon. Multiplied by our ``speed``
+property, it gives us a velocity we can use to move the node forward.
+
+We add ``velocity * delta`` to the node's ``position`` to move it. The position
+itself is of type :ref:`Vector2 <class_Vector2>`, a built-in type in Godot
+representing a 2D vector.
+
+Run the scene to see the Godot head run in circles.
+
+.. image:: img/scripting_first_script_rotating_godot.gif
+
+.. note:: Moving a node like that does not take into account colliding with
+          walls or the floor. In :ref:`doc_your_first_game`, you will learn
+          another approach to moving objects while detecting collisions.
+
+Our node currently moves by itself. In the next part, we'll use player input to control it.

+ 141 - 0
getting_started/step_by_step/scripting_player_input.rst

@@ -0,0 +1,141 @@
+.. Intention: only introduce one necessary input method at this point. The
+   Inputs section of the docs should provide more guides comparing the various
+   tools you have to manage the complexity of user input.
+
+.. _doc_scripting_player_input:
+
+Listening to player input
+=========================
+
+Building upon the previous lesson, let's look at another important feature of
+any game: giving control to the player. To add this, we need to modify our code.
+
+.. image:: img/scripting_first_script_moving_with_input.gif
+
+You have two main tools to process the player's input in Godot:
+
+1. The built-in input callbacks, mainly ``_unhandled_input()``. Like
+   ``_process()``, it's a built-in virtual function that Godot calls every time
+   the player presses a key. It's the tool you want to use to react to events
+   that don't happen every frame, like pressing :kbd:`Space` to jump. To learn
+   more about input callbacks, see :ref:`doc_inputevent`.
+2. The ``Input`` singleton. A singleton is a globally accessible object. Godot
+   provides access to several in scripts. It's the right tool to check for input
+   every frame.
+
+We're going to use the ``Input`` singleton here as we need to know if the player
+wants to turn or move every frame.
+
+For turning, we should use a new variable: ``direction``. Update the top of the
+``_process()`` function like so, up to the line where we increment the sprite's
+``rotation``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _process(delta):
+        var direction = 0
+        if Input.is_action_pressed("ui_left"):
+            direction = -1
+        if Input.is_action_pressed("ui_right"):
+            direction = 1
+
+        rotation += angular_speed * direction * delta
+
+Our ``direction`` local variable is a multiplier representing the direction in
+which the player wants to turn. A value of ``0`` means the player isn't pressing
+the left or the right arrow key. A value of ``1`` means the player wants to turn
+right, and ``-1`` means they want to turn left.
+
+To produce these values, we introduce conditions and the use of ``Input``. A
+condition starts with the ``if`` keyword in GDScript and ends with a colon. The
+condition is the expression between the keyword and the end of the line.
+
+To check if a key was pressed this frame, we call ``Input.is_action_pressed()``.
+The method takes a text string representing an input action and returns ``true``
+if the action is pressed, ``false`` otherwise.
+
+The two actions we use above, "ui_left" and "ui_right", are predefined in every
+Godot project. They respectively trigger when the player presses the left and
+right arrows on the keyboard or left and right on a gamepad's D-pad.
+
+.. note:: You can see and edit input actions in your project by going to Project
+          -> Project Settings and clicking on the Input Map tab.
+
+Finally, we use the ``direction`` as a multiplier when we update the node's
+``rotation``: ``rotation += angular_speed * direction * delta``.
+
+If you run the scene with this code, the icon should rotate when you press
+:kbd:`Left` and :kbd:`Right`.
+
+Moving when pressing "up"
+-------------------------
+
+To only move when pressing a key, we need to modify the code that calculates the
+velocity. Replace the line starting with ``var velocity`` with the code below.
+
+.. tabs::
+   .. code-tab:: gdscript GDScript
+
+    var velocity = Vector2.ZERO
+    if Input.is_action_pressed("ui_up"):
+        velocity = Vector2.UP.rotated(rotation) * speed
+
+    position += velocity * delta
+
+We initialize the ``velocity`` with a value of ``Vector2.ZERO``, another
+constant of the built-in ``Vector`` type representing a 2D vector of length 0.
+
+If the player presses the "ui_up" action, we then update the velocity's value,
+causing the sprite to move forward.
+
+Here is the complete ``Sprite.gd`` file for reference.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Sprite
+
+    var speed = 400
+    var angular_speed = PI
+
+
+    func _process(delta):
+        var direction = 0
+        if Input.is_action_pressed("ui_left"):
+            direction = -1
+        if Input.is_action_pressed("ui_right"):
+            direction = 1
+
+        rotation += angular_speed * direction * delta
+
+        var velocity = Vector2.ZERO
+        if Input.is_action_pressed("ui_up"):
+            velocity = Vector2.UP.rotated(rotation) * speed
+
+        position += velocity * delta
+
+If you run the scene, you should now be able to rotate with the left and right
+arrow keys and move forward by pressing :kbd:`Up`.
+
+.. image:: img/scripting_first_script_moving_with_input.gif
+
+Summary
+-------
+
+In summary, every script in Godot represents a class and extends one of the
+engine's built-in classes. The node types your classes inherit from give you
+access to properties like ``rotation`` and ``position`` in our sprite's case.
+You also inherit many functions, which we didn't get to use in this example.
+
+In GDScript, the variables you put at the top of the file are your class's
+properties, also called member variables. Besides variables, you can define
+functions, which, for the most part, will be your classes' methods.
+
+Godot provides several virtual functions you can define to connect your class
+with the engine. These include ``_process()``, to apply changes to the node
+every frame, and ``_unhandled_input()``, to receive input events like key and
+button presses from the users. There are quite a few more.
+
+The ``Input`` singleton allows you to react to the players' input anywhere in
+your code. In particular, you'll get to use it in the ``_process()`` loop.

+ 0 - 5
getting_started/step_by_step/your_first_script.rst

@@ -1,5 +0,0 @@
-.. _doc_your_first_script:
-
-Creating your first script
---------------------------
-

+ 8 - 6
tutorials/scripting/gdscript/gdscript_basics.rst

@@ -1,10 +1,12 @@
 .. _doc_gdscript:
 .. _doc_gdscript:
 
 
-GDScript basics
-===============
+GDScript reference
+==================
 
 
-Introduction
-------------
+:ref:`GDScript<doc_gdscript>` is a high-level, `object-oriented
+<https://en.wikipedia.org/wiki/Object-oriented_programming>`_, `imperative
+<https://en.wikipedia.org/wiki/Imperative_programming>`_, and `gradually typed
+<https://en.wikipedia.org/wiki/Gradual_typing>`_ programming language built for Godot.
 
 
 *GDScript* is a high-level, dynamically typed programming language used to
 *GDScript* is a high-level, dynamically typed programming language used to
 create content. It uses an indentation-based syntax similar to languages like
 create content. It uses an indentation-based syntax similar to languages like
@@ -15,7 +17,7 @@ allowing great flexibility for content creation and integration.
 GDScript is entirely independent from Python and is not based on it.
 GDScript is entirely independent from Python and is not based on it.
 
 
 History
 History
-~~~~~~~
+-------
 
 
 .. note::
 .. note::
 
 
@@ -23,7 +25,7 @@ History
     :ref:`Frequently Asked Questions <doc_faq_what_is_gdscript>`.
     :ref:`Frequently Asked Questions <doc_faq_what_is_gdscript>`.
 
 
 Example of GDScript
 Example of GDScript
-~~~~~~~~~~~~~~~~~~~
+-------------------
 
 
 Some people can learn better by taking a look at the syntax, so
 Some people can learn better by taking a look at the syntax, so
 here's a simple example of how GDScript looks.
 here's a simple example of how GDScript looks.