Преглед изворни кода

Backport rewrite of the getting started series for Godot 3

Nathan Lovato пре 3 година
родитељ
комит
501cbd41d4
100 измењених фајлова са 5182 додато и 378 уклоњено
  1. BIN
      getting_started/editor/img/editable-children.png
  2. BIN
      getting_started/editor/img/godot-gui-overlay.png
  3. BIN
      getting_started/editor/img/save-branch-as-scene.png
  4. BIN
      getting_started/editor/img/unity-gui-overlay.png
  5. BIN
      getting_started/editor/img/unity-project-organization-example.png
  6. 0 253
      getting_started/editor/unity_to_godot.rst
  7. 0 124
      getting_started/editor/using_the_web_editor.rst
  8. 77 0
      getting_started/first_2d_game/01.project_setup.rst
  9. 100 0
      getting_started/first_2d_game/02.player_scene.rst
  10. 515 0
      getting_started/first_2d_game/03.coding_the_player.rst
  11. 180 0
      getting_started/first_2d_game/04.creating_the_enemy.rst
  12. 446 0
      getting_started/first_2d_game/05.the_main_game_scene.rst
  13. 437 0
      getting_started/first_2d_game/06.heads_up_display.rst
  14. 72 0
      getting_started/first_2d_game/07.finishing-up.rst
  15. 0 0
      getting_started/first_2d_game/files/dodge_assets.zip
  16. BIN
      getting_started/first_2d_game/files/dodge_assets_with_gdnative.zip
  17. 0 0
      getting_started/first_2d_game/img/add_node.png
  18. BIN
      getting_started/first_2d_game/img/add_script_button.png
  19. 0 0
      getting_started/first_2d_game/img/attach_node_window.png
  20. BIN
      getting_started/first_2d_game/img/completed_main_scene.png
  21. BIN
      getting_started/first_2d_game/img/custom_font1.png
  22. BIN
      getting_started/first_2d_game/img/custom_font2.png
  23. BIN
      getting_started/first_2d_game/img/custom_font3.png
  24. 0 0
      getting_started/first_2d_game/img/dodge_preview.gif
  25. 0 0
      getting_started/first_2d_game/img/draw_path2d.gif
  26. BIN
      getting_started/first_2d_game/img/export_variable.png
  27. 0 0
      getting_started/first_2d_game/img/filesystem_dock.png
  28. BIN
      getting_started/first_2d_game/img/folder-content.png
  29. 0 0
      getting_started/first_2d_game/img/grid_snap_button.png
  30. 0 0
      getting_started/first_2d_game/img/group_tab.png
  31. BIN
      getting_started/first_2d_game/img/instance_scene.png
  32. 0 0
      getting_started/first_2d_game/img/lock_children.png
  33. 0 0
      getting_started/first_2d_game/img/main_scene_nodes.png
  34. 0 0
      getting_started/first_2d_game/img/mob_animations.gif
  35. BIN
      getting_started/first_2d_game/img/new-project-button.png
  36. BIN
      getting_started/first_2d_game/img/path2d_buttons.png
  37. BIN
      getting_started/first_2d_game/img/player_coll_shape.png
  38. BIN
      getting_started/first_2d_game/img/player_scale.png
  39. BIN
      getting_started/first_2d_game/img/player_scene_nodes.png
  40. 0 0
      getting_started/first_2d_game/img/player_signal_connection.png
  41. 0 0
      getting_started/first_2d_game/img/player_signals.png
  42. BIN
      getting_started/first_2d_game/img/set_collision_mask.png
  43. BIN
      getting_started/first_2d_game/img/setting-project-width-and-height.png
  44. BIN
      getting_started/first_2d_game/img/setting-stretch-mode.png
  45. 0 0
      getting_started/first_2d_game/img/spriteframes_panel.png
  46. 0 0
      getting_started/first_2d_game/img/spriteframes_panel2.png
  47. BIN
      getting_started/first_2d_game/img/start_button_shortcut.png
  48. BIN
      getting_started/first_2d_game/img/ui_anchor.png
  49. 7 1
      getting_started/first_2d_game/index.rst
  50. 164 0
      getting_started/first_3d_game/01.game_setup.rst
  51. 177 0
      getting_started/first_3d_game/02.player_input.rst
  52. 413 0
      getting_started/first_3d_game/03.player_movement_code.rst
  53. 332 0
      getting_started/first_3d_game/04.mob_scene.rst
  54. 360 0
      getting_started/first_3d_game/05.spawning_mobs.rst
  55. 353 0
      getting_started/first_3d_game/06.jump_and_squash.rst
  56. 467 0
      getting_started/first_3d_game/07.killing_player.rst
  57. 475 0
      getting_started/first_3d_game/08.score_and_replay.rst
  58. 565 0
      getting_started/first_3d_game/09.adding_animations.rst
  59. 42 0
      getting_started/first_3d_game/going_further.rst
  60. BIN
      getting_started/first_3d_game/img/01.game_setup/01.import_button.png
  61. BIN
      getting_started/first_3d_game/img/01.game_setup/02.browse_to_project_folder.png
  62. BIN
      getting_started/first_3d_game/img/01.game_setup/03.import_and_edit.png
  63. BIN
      getting_started/first_3d_game/img/01.game_setup/04.start_assets.png
  64. BIN
      getting_started/first_3d_game/img/01.game_setup/05.main_node.png
  65. BIN
      getting_started/first_3d_game/img/01.game_setup/06.staticbody_node.png
  66. BIN
      getting_started/first_3d_game/img/01.game_setup/07.collision_shape_warning.png
  67. BIN
      getting_started/first_3d_game/img/01.game_setup/08.create_box_shape.png
  68. BIN
      getting_started/first_3d_game/img/01.game_setup/09.box_extents.png
  69. BIN
      getting_started/first_3d_game/img/01.game_setup/10.mesh_instance.png
  70. BIN
      getting_started/first_3d_game/img/01.game_setup/11.cube_mesh.png
  71. BIN
      getting_started/first_3d_game/img/01.game_setup/12.cube_resized.png
  72. BIN
      getting_started/first_3d_game/img/01.game_setup/13.move_gizmo_y_axis.png
  73. BIN
      getting_started/first_3d_game/img/01.game_setup/14.select_mode_icon.png
  74. BIN
      getting_started/first_3d_game/img/01.game_setup/15.translation_amount.png
  75. BIN
      getting_started/first_3d_game/img/01.game_setup/16.turn_on_shadows.png
  76. BIN
      getting_started/first_3d_game/img/01.game_setup/17.project_with_light.png
  77. BIN
      getting_started/first_3d_game/img/02.player_input/01.new_scene.png
  78. BIN
      getting_started/first_3d_game/img/02.player_input/02.instantiating_the_model.png
  79. BIN
      getting_started/first_3d_game/img/02.player_input/03.scene_structure.png
  80. BIN
      getting_started/first_3d_game/img/02.player_input/04.sphere_shape.png
  81. BIN
      getting_started/first_3d_game/img/02.player_input/05.moving_the_sphere_up.png
  82. BIN
      getting_started/first_3d_game/img/02.player_input/06.toggling_visibility.png
  83. BIN
      getting_started/first_3d_game/img/02.player_input/07.adding_action.png
  84. BIN
      getting_started/first_3d_game/img/02.player_input/07.input_map_tab.png
  85. BIN
      getting_started/first_3d_game/img/02.player_input/07.project_settings.png
  86. BIN
      getting_started/first_3d_game/img/02.player_input/08.actions_list_empty.png
  87. BIN
      getting_started/first_3d_game/img/02.player_input/08.create_key_action.png
  88. BIN
      getting_started/first_3d_game/img/02.player_input/09.keyboard_key_popup.png
  89. BIN
      getting_started/first_3d_game/img/02.player_input/09.keyboard_keys.png
  90. BIN
      getting_started/first_3d_game/img/02.player_input/10.joy_axis_option.png
  91. BIN
      getting_started/first_3d_game/img/02.player_input/11.joy_axis_popup.png
  92. BIN
      getting_started/first_3d_game/img/02.player_input/12.move_inputs_mapped.png
  93. BIN
      getting_started/first_3d_game/img/02.player_input/13.joy_button_option.png
  94. BIN
      getting_started/first_3d_game/img/02.player_input/14.add_jump_button.png
  95. BIN
      getting_started/first_3d_game/img/02.player_input/14.jump_input_action.png
  96. BIN
      getting_started/first_3d_game/img/03.player_movement_code/01.attach_script_to_player.png
  97. BIN
      getting_started/first_3d_game/img/03.player_movement_code/02.clicking_main_tab.png
  98. BIN
      getting_started/first_3d_game/img/03.player_movement_code/03.instance_child_scene.png
  99. BIN
      getting_started/first_3d_game/img/03.player_movement_code/04.scene_tree_with_camera.png
  100. BIN
      getting_started/first_3d_game/img/03.player_movement_code/05.camera_preview_checkbox.png

BIN
getting_started/editor/img/editable-children.png


BIN
getting_started/editor/img/godot-gui-overlay.png


BIN
getting_started/editor/img/save-branch-as-scene.png


BIN
getting_started/editor/img/unity-gui-overlay.png


BIN
getting_started/editor/img/unity-project-organization-example.png


+ 0 - 253
getting_started/editor/unity_to_godot.rst

@@ -1,253 +0,0 @@
-.. _unity_to_godot:
-
-..    references :
-..    https://wiki.unrealengine.com/Unity3D_Developer's_Guide_to_Unreal_Engine_4
-..    https://docs.unrealengine.com/latest/INT/GettingStarted/FromUnity/
-
-From Unity to Godot Engine
-==========================
-
-This guide provides an overview of Godot Engine from the viewpoint of a Unity user,
-and aims to help you migrate your existing Unity experience into the world of Godot.
-
-.. note::
-
-   This article talks about older versions of Unity. Nestable prefabs ('Nested prefabs') were added to Unity 2018.3. Nestable prefabs are analogous to Godot's scenes, and allow a more Godot-like approach to scene organization.
-
-Differences
------------
-
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-|                   | Unity                                                                              | Godot                                                                                                          |
-+===================+====================================================================================+================================================================================================================+
-| License           | Proprietary, closed, free license with revenue caps and usage restrictions         | MIT license, free and fully open source without any restriction                                                |
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-| OS (editor)       | Windows, macOS, Linux                                                              | Windows, macOS, X11 (Linux, \*BSD)                                                                             |
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-| OS (export)       | * **Desktop:** Windows, macOS, Linux                                               | * **Desktop:** Windows, macOS, X11                                                                             |
-|                   | * **Mobile:** Android, iOS, Windows Phone, Tizen                                   | * **Mobile:** Android, iOS                                                                                     |
-|                   | * **Web:** WebAssembly or asm.js                                                   | * **Web:** WebAssembly                                                                                         |
-|                   | * **Consoles:** PS4, PS Vita, Xbox One, Xbox 360, Wii U, Nintendo 3DS              | * **Console:** See :ref:`doc_consoles`                                                                         |
-|                   | * **VR:** Oculus Rift, SteamVR, Google Cardboard, PlayStation VR, Gear VR, HoloLens| * **VR:** Oculus Rift, SteamVR                                                                                 |
-|                   | * **TV:** Android TV, Samsung SMART TV, tvOS                                       |                                                                                                                |
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-| Scene system      | * Component/Scene (GameObject > Component)                                         | :ref:`Scene tree and nodes <doc_scenes_and_nodes>`, allowing scenes to be nested and/or inherit other scenes   |
-|                   | * Prefabs                                                                          |                                                                                                                |
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-| Third-party tools | Visual Studio or VS Code                                                           | * :ref:`External editors are possible <doc_external_editor>`                                                   |
-|                   |                                                                                    | * :ref:`Android SDK for Android export <doc_exporting_for_android>`                                            |
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-| Notable advantages| * Huge community                                                                   | * Scene System                                                                                                 |
-|                   | * Large assets store                                                               | * :ref:`Animation Pipeline <doc_animations>`                                                                   |
-|                   |                                                                                    | * :ref:`Easy to write Shaders <doc_shading_language>`                                                          |
-|                   |                                                                                    | * Debug on Device                                                                                              |
-|                   |                                                                                    |                                                                                                                |
-|                   |                                                                                    |                                                                                                                |
-+-------------------+------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
-
-
-The editor
-----------
-
-Godot Engine provides a rich-featured editor that allows you to build your games.
-The pictures below display the default layouts of both editors with colored blocks to indicate common functionalities.
-
-.. image:: img/unity-gui-overlay.png
-.. image:: img/godot-gui-overlay.png
-
-While both editors may seem similar, there are many differences below the surface.
-Both let you organize the project using the filesystem,
-but Godot's approach is simpler with a single configuration file, minimalist text format,
-and no metadata. This makes Godot more friendly to VCS systems, such as Git, Subversion, or Mercurial.
-
-Godot's Scene panel is similar to Unity's Hierarchy panel but, as each node has a specific function,
-the approach used by Godot is more visually descriptive. It's easier to understand
-what a scene does at a glance.
-
-The Inspector in Godot is more minimal, it shows only properties.
-Thanks to this, objects can expose more useful parameters to the user
-without having to hide functionality in language APIs. As a plus, Godot allows animating any of those properties visually.
-Changing colors, textures, enumerations, or even links to resources in real-time is possible without needing to write code.
-
-The Toolbar at the top of the screen is similar in both editors, offering control over project playback.
-Projects in Godot run in a separate window, rather than inside the editor
-(but the tree and objects can still be explored in the debugger window).
-
-This approach has several advantages:
-
-- Running the project and closing it is fast (Unity has to save, run the project, close the project, and then reload the previous state).
-- Live editing is a lot more useful because changes done to the editor take effect immediately in the game and are not lost (nor have to be synced) when the game is closed. This allows fantastic workflows, like creating levels while you play them.
-- The editor is more stable because the game runs in a separate process.
-- The running game can be explored from different angles by toggling the "Camera Override" button in the editor viewport, which will switch between using the editor view and the game camera view.
-
-Finally, Godot's top toolbar includes a menu for remote debugging.
-These options allow deployment to a device (connected phone, tablet, or browser via HTML5),
-and debugging/live editing on it after the game is exported.
-
-The scene system
-----------------
-
-This is the most important difference between Unity and Godot and the favorite feature of most Godot users.
-
-Working on a 'level' in Unity usually means embedding all the required assets in a scene
-and linking them together with components and scripts.
-
-Godot's scene system is superficially similar to Unity. A 'level' consists of a collection of nodes, each with its own purpose: Sprite, Mesh, Light, etc. However, in Godot the nodes are arranged in a tree. Each node can have multiple children, which makes each a subscene of the main scene.
-This means you can compose a whole scene with different scenes stored in different files.
-
-For example, think of a platformer level. You would compose it with multiple elements:
-
-- Bricks
-- Coins
-- The player
-- The enemies
-
-In Unity, you would put all the GameObjects in the scene: the player, multiple instances of enemies,
-bricks everywhere to form the ground of the level and then multiple instances of coins all over the level.
-You would then add various components to each element to link them and add logic in the level: For example,
-you'd add a BoxCollider2D to all the elements of the scene so that they can collide. This principle is different in Godot.
-
-In Godot, you would split your whole scene into three separate, smaller scenes, and instance them in the main scene.
-
-1. **A scene for the Player alone.**
-
-Consider the player as an element we'd like to use in different parent scenes (for instance 'level' scenes). In our case, the player element needs at least an AnimatedSprite node. This node contains the sprite textures necessary for various animations (for example, a walking animation).
-
-2. **A scene for the Enemy.**
-
-An enemy is also an element we'd like to use in several scenes. It's almost the same
-as the Player node. The only differences are the script (it needs 'AI' routines to generate the enemy's behavior)
-and the sprite textures used by the AnimatedSprite node.
-
-3. **A Level scene.**
-
-A Level scene is composed of Bricks (for platforms), Coins (for the player to collect) and a
-number of instances of the Enemy scene. Each instance is a node in the Level scene tree. These instances are separate enemies,
-which initially have shared behavior and appearance as defined in the Enemy scene. You can set different properties for each Enemy node (to change its color, for example).
-
-4. **A Main scene.**
-The Main scene would be composed of one root node with 2 children: a Player instance node, and a Level instance node.
-The root node can be anything, generally a "root" type such as "Node" which is the most global type,
-or "Node2D" (root type of all 2D-related nodes), "Spatial" (root type of all 3D-related nodes) or
-"Control" (root type of all GUI-related nodes).
-
-As you can see, every scene is organized as a tree. The same goes for nodes' properties: you don't *add* a
-collision component to a node to make it collidable like Unity does. Instead, you make this node a *child* of a
-new specific node that has collision properties. Godot features various collision types nodes, depending on the usage
-(see the :ref:`Physics introduction <doc_physics_introduction>`).
-
-- What are the advantages of this system? Wouldn't this system potentially increase the depth of the scene tree? And doesn't Unity already allow you to organize GameObjects by putting them inside empty GameObjects?
-
-    - Godot's system is closer to the well-known object-oriented paradigm: Godot provides a number of nodes which are not clearly "Game Objects", but they provide their children with their own capabilities: this is inheritance.
-    - Godot allows the extraction of a subtree of a scene to make it a scene of its own. So if a scene tree gets too deep, it can be split into smaller subtrees. This is better for reusability, as you can include any subtree as a child of any node. Putting multiple GameObjects in an empty GameObject in Unity does not provide the same functionality.
-
-Project organization
---------------------
-
-.. image:: img/unity-project-organization-example.png
-
-There is no perfect project architecture.
-Any architecture can be made to work in either Unity and Godot.
-
-However, a common architecture for Unity projects is to have one Assets folder in the root directory
-that contains various folders, one per type of asset: Audio, Graphics, Models, Materials, Scripts, Scenes, and so on.
-
-Since Godot allows splitting scenes into smaller scenes, each scene and subscene existing as a file in the project, we recommend organizing your project a bit differently.
-This wiki provides a page for this: :ref:`doc_project_organization`.
-
-
-Where are my prefabs?
----------------------
-
-A prefab as provided by Unity is a 'template' element of the scene.
-It is reusable, and each instance of the prefab that exists in the scene has an existence of its own,
-but all of them have the same properties as defined by the prefab.
-
-Godot does not provide prefabs as such, but the same functionality is provided by its scene system:
-The scene system is organized as a tree. Godot allows you to save any subtree of a scene as a scene file. This new scene can then be instanced as many times as you want, as a child of any node.
-Any change you make to this new, separate scene will be applied to its instances.
-However, any change you make to the instance will not have any impact on the 'template' scene.
-
-.. image:: img/save-branch-as-scene.png
-
-To be precise, you can modify the parameters of an instance in the Inspector panel.
-The nodes that compose this instance are initially locked. You can unlock them if you need to by
-right-clicking the instance in the Scene tree and selecting "Editable children" in the menu.
-You don't need to do this to add *new* child nodes to this node.
-Remember that any new children will belong to the instance, not to the 'template' scene on disk.
-If you want to add new children to every instance of your 'template' scene, then you should add them in the 'template' scene.
-
-.. image:: img/editable-children.png
-
-Glossary correspondence
------------------------
-
-- GameObject -> Node
-- Add a component -> Inheriting
-- Prefab -> Reusable Scene file
-
-
-Scripting: GDScript, C# and Visual Script
------------------------------------------
-
-Design
-^^^^^^
-
-Unity supports C#. C# benefits from its integration with Visual Studio and has desirable features such as static typing.
-
-Godot provides its own scripting language, :ref:`GDScript <doc_scripting>` as well as support
-for :ref:`Visual Script <toc-learn-scripting-visual_script>` and :ref:`C# <doc_c_sharp>`.
-GDScript borrows its syntax from Python, but is not related to it. If you wonder about the reasoning for a custom scripting language,
-please read the :ref:`doc_gdscript` and :ref:`doc_faq` pages. GDScript is strongly attached to the Godot API
-and doesn't take long to learn: Between one evening for an experienced programmer and a week for a complete beginner.
-
-Unity allows you to attach as many scripts as you want to a GameObject.
-Each script adds a behavior to the GameObject: For example, you can attach a script so that it reacts to the player's controls,
-and another that controls its specific game logic.
-
-In Godot, you can only attach one script per node. You can use either an external GDScript file
-or include the script directly in the node. If you need to attach more scripts to one node, then you may consider two solutions,
-depending on your scene and on what you want to achieve:
-
-- either add a new node between your target node and its current parent, then add a script to this new node.
-- or, you can split your target node into multiple children and attach one script to each of them.
-
-As you can see, it can be easy to turn a scene tree to a mess. Consider splitting any complicated scene into multiple, smaller branches.
-
-Connections: groups and signals
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-You can control nodes by accessing them via script and calling built-in
-or user-defined functions on them. You can also place nodes in a group
-and call functions on all nodes in this group. See more in the
-:ref:`scripting documentation <doc_scripting_continued>`.
-
-Nodes can send a signal when a specified action occurs. A signal can
-be set to call any function. You can define custom signals and specify
-when they are triggered. See more in the :ref:`signals documentation <doc_gdscript_signals>`.
-
-Script serialization
-^^^^^^^^^^^^^^^^^^^^
-
-Unity can handle script serialization in two ways:
-
-- Implicit: All public fields in a class are automatically serialized if the type is a serializable type (``Dictionary`` is not serializable).
-- Explicit: Non-public fields can be serialized using the ``[SerializeField]`` attribute.
-
-Godot also has a built-in script serialization system, but it works only explicitly.
-You can serialize any serializable type (:ref:`built-in and various engine types <doc_binary_serialization_api>`,
-including :ref:`class_Array` and :ref:`class_Dictionary`) using the ``export`` keyword.
-See the :ref:`exports documentation <doc_gdscript_exports>` for details.
-
-Unity also has a data type called ``ScriptableObject`` used to serialize custom asset objects.
-Its equivalent in Godot is the base class for all resources: :ref:`class_Resource`.
-Creating a script that inherits :ref:`class_Resource` will allow you to create custom serializable objects. More information about resources can be found :ref:`here <doc_resources>`.
-
-Using Godot in C++
-------------------
-
-Godot allows you to develop your project directly in C++ by using its API, which is not possible with Unity at the moment.
-As an example, you can consider Godot Engine's editor as a "game" written in C++ using the Godot API.
-
-If you are interested in using Godot in C++, you may want to start reading the :ref:`Developing in
-C++ <doc_introduction_to_godot_development>` page.

+ 0 - 124
getting_started/editor/using_the_web_editor.rst

@@ -1,124 +0,0 @@
-.. _doc_using_the_web_editor:
-
-Using the Web editor
-====================
-
-Since Godot 3.3, there is a `Web editor <https://editor.godotengine.org/>`__
-you can use to work on new or existing projects.
-
-.. note::
-
-    The web editor is in a preliminary stage. While its feature set may be
-    sufficient for educational purposes, it is currently **not recommended for
-    production work**. See :ref:`doc_using_the_web_editor_limitations` below.
-
-Browser support
----------------
-
-The Web editor requires support for WebAssembly's SharedArrayBuffer. This
-is in turn required to support threading in the browser. The following desktop
-browsers support WebAssembly threading and can therefore run the web editor:
-
-- Chrome 68 or later
-- Firefox 79 or later
-- Edge 79 or later
-
-Opera and Safari are not supported yet. Safari may work in the future once
-proper threading support is added.
-
-**Mobile browsers are currently not supported.**
-
-The web editor supports both the GLES3 and GLES2 renderers, although GLES2 is
-recommended for better performance and compatibility with old/low-end hardware.
-
-.. note::
-
-    If you use Linux, due to
-    `poor Firefox WebGL performance <https://bugzilla.mozilla.org/show_bug.cgi?id=1010527>`__,
-    it's recommended to use a Chromium-based browser instead of Firefox.
-
-.. _doc_using_the_web_editor_limitations:
-
-Limitations
------------
-
-Due to limitations on the Godot or Web platform side, the following features
-are currently missing:
-
-- No C#/Mono support.
-- No GDNative support.
-- No debugging support. This means GDScript debugging/profiling, live scene
-  editing, the Remote Scene tree dock and other features that rely on the debugger
-  protocol will not work.
-- No project exporting. As a workaround, you can download the project source
-  using **Project > Tools > Download Project Source** and export it using a
-  `native version of the Godot editor <https://godotengine.org/download>`__.
-- The editor won't warn you when closing the tab with unsaved changes.
-- No lightmap baking support. You can still use existing lightmaps if they were
-  baked with a native version of the Godot editor
-  (e.g. by importing an existing project).
-
-The following features are unlikely to be supported due to inherent limitations
-of the Web platform:
-
-- No support for external script editors.
-- No support for Android one-click deploy.
-
-.. seealso::
-
-    See the
-    `list of open issues on GitHub related to the web editor <https://github.com/godotengine/godot/issues?q=is%3Aopen+is%3Aissue+label%3Aplatform%3Ahtml5+label%3Atopic%3Aeditor>`__ for a list of known bugs.
-
-Importing a project
--------------------
-
-To import an existing project, the current process is as follows:
-
-- Specify a ZIP file to preload on the HTML5 filesystem using the
-  **Preload project ZIP** input.
-- Run the editor by clicking **Start Godot editor**.
-  The Godot project manager should appear after 10-20 seconds.
-  On slower machines or connections, loading may take up to a minute.
-- In the dialog that appears at the middle of the window, specify a name for
-  the folder to create then click the **Create Folder** button
-  (it doesn't have to match the ZIP archive's name).
-- Click **Install & Edit** and the project will open in the editor.
-
-.. attention::
-
-    It's important to place the project folder somewhere in ``/home/web_user/``.
-    If your project folder is placed outside ``/home/web_user/``, you will
-    lose your project when closing the editor!
-
-    When you follow the steps described above, the project folder will always be
-    located in ``/home/web_user/projects``, keeping it safe.
-
-Editing and running a project
------------------------------
-
-Unlike the native version of Godot, the web editor is constrained to a single
-window. Therefore, it cannot open a new window when running the project.
-Instead, when you run the project by clicking the Run button or pressing
-:kbd:`F5`, it will appear to "replace" the editor window.
-
-The web editor offers an alternative way to deal with the editor and game
-windows (which are now "tabs"). You can switch between the **Editor** and
-**Game** tabs using the buttons on the top. You can also close the running game
-or editor by clicking the **×** button next to those tabs.
-
-Where are my project files?
----------------------------
-
-Due to browser security limitations, the editor will save the project files to
-the browser's IndexedDB storage. This storage isn't accessible as a regular folder
-on your machine, but is abstracted away in a database.
-
-You can download the project files as a ZIP archive by using
-**Project > Tools > Download Project Source**. This can be used to export the
-project using a `native Godot editor <https://godotengine.org/download>`__,
-since exporting from the web editor isn't supported yet.
-
-In the future, it may be possible to use the
-`HTML5 FileSystem API <https://developer.mozilla.org/en-US/docs/Web/API/FileSystem>`__
-to store the project files on the user's filesystem as the native editor would do.
-However, this isn't implemented yet.

+ 77 - 0
getting_started/first_2d_game/01.project_setup.rst

@@ -0,0 +1,77 @@
+.. _doc_your_first_2d_game_project_setup:
+
+Setting up the project
+======================
+
+In this short first part, we'll set up and organize the project.
+
+Launch Godot and create a new project.
+
+.. image:: img/new-project-button.png
+
+.. tabs::
+ .. tab:: GDScript
+
+    Download :download:`dodge_assets.zip <files/dodge_assets.zip>`.
+    The archive contains the images and sounds you'll be using
+    to make the game. Extract the archive and move the ``art/``
+    and ``fonts/`` directories to your project's directory.
+
+ .. tab:: C#
+
+    Download :download:`dodge_assets.zip <files/dodge_assets.zip>`.
+    The archive contains the images and sounds you'll be using
+    to make the game. Extract the archive and move the ``art/``
+    and ``fonts/`` directories to your project's directory.
+
+    Ensure that you have the required dependencies to use C# in Godot.
+    You need the .NET Core 3.1 SDK, and an editor such as VS Code.
+    See :ref:`doc_c_sharp_setup`.
+
+ .. tab:: GDNative C++
+
+    Download :download:`dodge_assets_with_gdnative.zip
+    <files/dodge_assets_with_gdnative.zip>`.
+    The archive contains the images and sounds you'll be using
+    to make the game. It also contains a starter GDNative project
+    including a ``SConstruct`` file, a ``dodge_the_creeps.gdnlib``
+    file, a ``player.gdns`` file, and an ``entry.cpp`` file.
+
+    Ensure that you have the required dependencies to use GDNative C++.
+    You need a C++ compiler such as GCC or Clang or MSVC that supports C++14.
+    On Windows you can download Visual Studio 2019 and select the C++ workload.
+    You also need SCons to use the build system (the SConstruct file).
+    Then you need to `download the Godot C++ bindings <https://github.com/godotengine/godot-cpp>`_
+    and place them in your project.
+
+Your project folder should look like this.
+
+.. image:: img/folder-content.png
+
+This game is designed for portrait mode, so we need to adjust the size of the
+game window. Click on *Project -> Project Settings* to open the project settings
+window and in the left column, open the *Display -> Window* tab. There, set
+"Width" to ``480`` and "Height" to ``720``.
+
+.. image:: img/setting-project-width-and-height.png
+
+Also, scroll down to the bottom of the section and, under the "Stretch" options,
+set ``Mode`` to "2d" and ``Aspect`` to "keep". This ensures that the game scales
+consistently on different sized screens.
+
+.. image:: img/setting-stretch-mode.png
+
+Organizing the project
+~~~~~~~~~~~~~~~~~~~~~~
+
+In this project, we will make 3 independent scenes: ``Player``, ``Mob``, and
+``HUD``, which we will combine into the game's ``Main`` scene.
+
+In a larger project, it might be useful to create folders to hold the various
+scenes and their scripts, but for this relatively small game, you can save your
+scenes and scripts in the project's root folder, identified by ``res://``. You
+can see your project folders in the FileSystem dock in the lower left corner:
+
+.. image:: img/filesystem_dock.png
+
+With the project in place, we're ready to design the player scene in the next lesson.

+ 100 - 0
getting_started/first_2d_game/02.player_scene.rst

@@ -0,0 +1,100 @@
+.. _doc_your_first_2d_game_player_scene:
+
+Creating the player scene
+=========================
+
+With the project settings in place, we can start working on the
+player-controlled character.
+
+The first scene will define the ``Player`` object. One of the benefits of
+creating a separate Player scene is that we can test it separately, even before
+we've created other parts of the game.
+
+Node structure
+~~~~~~~~~~~~~~
+
+To begin, we need to choose a root node for the player object. As a general
+rule, a scene's root node should reflect the object's desired functionality -
+what the object *is*. Click the "Other Node" button and add an :ref:`Area2D
+<class_Area2D>` node to the scene.
+
+.. image:: img/add_node.png
+
+Godot will display a warning icon next to the node in the scene tree. You can
+ignore it for now. We will address it later.
+
+With ``Area2D`` we can detect objects that overlap or run into the player.
+Change the node's name to ``Player`` by double-clicking on it. Now that we've
+set the scene's root node, we can add additional nodes to give it more
+functionality.
+
+Before we add any children to the ``Player`` node, we want to make sure we don't
+accidentally move or resize them by clicking on them. Select the node and click
+the icon to the right of the lock; its tooltip says "Makes sure the object's
+children are not selectable."
+
+.. image:: img/lock_children.png
+
+Save the scene. Click Scene -> Save, or press :kbd:`Ctrl + S` on Windows/Linux
+or :kbd:`Cmd + S` on macOS.
+
+.. note:: For this project, we will be following the Godot naming conventions.
+
+          - **GDScript**: Classes (nodes) use PascalCase, variables and
+            functions use snake_case, and constants use ALL_CAPS (See
+            :ref:`doc_gdscript_styleguide`).
+
+          - **C#**: Classes, export variables and methods use PascalCase,
+            private fields use _camelCase, local variables and parameters use
+            camelCase (See :ref:`doc_c_sharp_styleguide`). Be careful to type
+            the method names precisely when connecting signals.
+
+
+Sprite animation
+~~~~~~~~~~~~~~~~
+
+Click on the ``Player`` node and add an :ref:`AnimatedSprite2D
+<class_AnimatedSprite2D>` node as a child. The ``AnimatedSprite2D`` will handle the
+appearance and animations for our player. Notice that there is a warning symbol
+next to the node. An ``AnimatedSprite2D`` requires a :ref:`SpriteFrames
+<class_SpriteFrames>` resource, which is a list of the animations it can
+display. To create one, find the ``Frames`` property in the Inspector and click
+"[empty]" -> "New SpriteFrames". Click again to open the "SpriteFrames" panel:
+
+.. image:: img/spriteframes_panel.png
+
+
+On the left is a list of animations. Click the "default" one and rename it to
+"walk". Then click the "New Animation" button to create a second animation named
+"up". Find the player images in the "FileSystem" tab - they're in the ``art``
+folder you unzipped earlier. Drag the two images for each animation, named
+``playerGrey_up[1/2]`` and ``playerGrey_walk[1/2]``, into the "Animation Frames"
+side of the panel for the corresponding animation:
+
+.. image:: img/spriteframes_panel2.png
+
+The player images are a bit too large for the game window, so we need to scale
+them down. Click on the ``AnimatedSprite2D`` node and set the ``Scale`` property
+to ``(0.5, 0.5)``. You can find it in the Inspector under the ``Node2D``
+heading.
+
+.. image:: img/player_scale.png
+
+Finally, add a :ref:`CollisionShape2D <class_CollisionShape2D>` as a child of
+``Player``. This will determine the player's "hitbox", or the bounds of its
+collision area. For this character, a ``CapsuleShape2D`` node gives the best
+fit, so next to "Shape" in the Inspector, click "[empty]"" -> "New
+CapsuleShape2D". Using the two size handles, resize the shape to cover the
+sprite:
+
+.. image:: img/player_coll_shape.png
+
+When you're finished, your ``Player`` scene should look like this:
+
+.. image:: img/player_scene_nodes.png
+
+Make sure to save the scene again after these changes.
+
+In the next part, we'll add a script to the player node to move and animate it.
+Then, we'll set up collision detection to know when the player got hit by
+something.

+ 515 - 0
getting_started/first_2d_game/03.coding_the_player.rst

@@ -0,0 +1,515 @@
+.. _doc_your_first_2d_game_coding_the_player:
+
+Coding the player
+=================
+
+In this lesson, we'll add player movement, animation, and set it up to detect
+collisions.
+
+To do so, we need to add some functionality that we can't get from a built-in
+node, so we'll add a script. Click the ``Player`` node and click the "Attach
+Script" button:
+
+.. image:: img/add_script_button.png
+
+In the script settings window, you can leave the default settings alone. Just
+click "Create":
+
+.. note:: If you're creating a C# script or other languages, select the language
+          from the `language` drop down menu before hitting create.
+
+.. image:: img/attach_node_window.png
+
+.. note:: If this is your first time encountering GDScript, please read
+          :ref:`doc_scripting` before continuing.
+
+Start by declaring the member variables this object will need:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Area2D
+
+    export var speed = 400 # How fast the player will move (pixels/sec).
+    var screen_size # Size of the game window.
+
+ .. code-tab:: csharp
+
+    using Godot;
+    using System;
+
+    public class Player : Area2D
+    {
+        [Export]
+        public int Speed = 400; // How fast the player will move (pixels/sec).
+
+        public Vector2 ScreenSize; // Size of the game window.
+    }
+
+ .. code-tab:: cpp
+
+    // A `player.gdns` file has already been created for you. Attach it to the Player node.
+
+    // Create two files `player.cpp` and `player.hpp` next to `entry.cpp` in `src`.
+    // This code goes in `player.hpp`. We also define the methods we'll be using here.
+    #ifndef PLAYER_H
+    #define PLAYER_H
+
+    #include <AnimatedSprite2D.hpp>
+    #include <Area2D.hpp>
+    #include <CollisionShape2D.hpp>
+    #include <Godot.hpp>
+    #include <Input.hpp>
+
+    class Player : public godot::Area2D {
+        GODOT_CLASS(Player, godot::Area2D)
+
+        godot::AnimatedSprite2D *_animated_sprite;
+        godot::CollisionShape2D *_collision_shape;
+        godot::Input *_input;
+        godot::Vector2 _screen_size; // Size of the game window.
+
+    public:
+        real_t speed = 400; // How fast the player will move (pixels/sec).
+
+        void _init() {}
+        void _ready();
+        void _process(const double p_delta);
+        void start(const godot::Vector2 p_position);
+        void _on_Player_body_entered(godot::Node2D *_body);
+
+        static void _register_methods();
+    };
+
+    #endif // PLAYER_H
+
+Using the ``export`` keyword on the first variable ``speed`` allows us to set
+its value in the Inspector. This can be handy for values that you want to be
+able to adjust just like a node's built-in properties. Click on the ``Player``
+node and you'll see the property now appears in the "Script Variables" section
+of the Inspector. Remember, if you change the value here, it will override the
+value written in the script.
+
+.. warning:: If you're using C#, you need to (re)build the project assemblies
+             whenever you want to see new export variables or signals. This
+             build can be manually triggered by clicking the word "Mono" at the
+             bottom of the editor window to reveal the Mono Panel, then clicking
+             the "Build Project" button.
+
+.. image:: img/export_variable.png
+
+The ``_ready()`` function is called when a node enters the scene tree, which is
+a good time to find the size of the game window:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _ready():
+        screen_size = get_viewport_rect().size
+
+ .. code-tab:: csharp
+
+    public override void _Ready()
+    {
+        ScreenSize = GetViewportRect().Size;
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `player.cpp`.
+    #include "player.hpp"
+
+    void Player::_ready() {
+        _animated_sprite = get_node<godot::AnimatedSprite2D>("AnimatedSprite2D");
+        _collision_shape = get_node<godot::CollisionShape2D>("CollisionShape2D");
+        _input = godot::Input::get_singleton();
+        _screen_size = get_viewport_rect().size;
+    }
+
+Now we can use the ``_process()`` function to define what the player will do.
+``_process()`` is called every frame, so we'll use it to update elements of our
+game, which we expect will change often. For the player, we need to do the
+following:
+
+- Check for input.
+- Move in the given direction.
+- Play the appropriate animation.
+
+First, we need to check for input - is the player pressing a key? For this game,
+we have 4 direction inputs to check. Input actions are defined in the Project
+Settings under "Input Map". Here, you can define custom events and assign
+different keys, mouse events, or other inputs to them. For this game, we will
+just use the default events called "ui_right" etc that are assigned to the arrow
+keys on the keyboard.
+
+You can detect whether a key is pressed using ``Input.is_action_pressed()``,
+which returns ``true`` if it's pressed or ``false`` if it isn't.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _process(delta):
+        var velocity = Vector2.ZERO # The player's movement vector.
+        if Input.is_action_pressed("ui_right"):
+            velocity.x += 1
+        if Input.is_action_pressed("ui_left"):
+            velocity.x -= 1
+        if Input.is_action_pressed("ui_down"):
+            velocity.y += 1
+        if Input.is_action_pressed("ui_up"):
+            velocity.y -= 1
+
+        if velocity.length() > 0:
+            velocity = velocity.normalized() * speed
+            $AnimatedSprite2D.play()
+        else:
+            $AnimatedSprite2D.stop()
+
+ .. code-tab:: csharp
+
+    public override void _Process(float delta)
+    {
+        var velocity = Vector2.Zero; // The player's movement vector.
+
+        if (Input.IsActionPressed("ui_right"))
+        {
+            velocity.x += 1;
+        }
+
+        if (Input.IsActionPressed("ui_left"))
+        {
+            velocity.x -= 1;
+        }
+
+        if (Input.IsActionPressed("ui_down"))
+        {
+            velocity.y += 1;
+        }
+
+        if (Input.IsActionPressed("ui_up"))
+        {
+            velocity.y -= 1;
+        }
+
+        var animatedSprite = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
+
+        if (velocity.Length() > 0)
+        {
+            velocity = velocity.Normalized() * Speed;
+            animatedSprite.Play();
+        }
+        else
+        {
+            animatedSprite.Stop();
+        }
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `player.cpp`.
+    void Player::_process(const double p_delta) {
+        godot::Vector2 velocity(0, 0);
+
+        velocity.x = _input->get_action_strength("move_right") - _input->get_action_strength("move_left");
+        velocity.y = _input->get_action_strength("move_down") - _input->get_action_strength("move_up");
+
+        if (velocity.length() > 0) {
+            velocity = velocity.normalized() * speed;
+            _animated_sprite->play();
+        } else {
+            _animated_sprite->stop();
+        }
+    }
+
+We start by setting the ``velocity`` to ``(0, 0)`` - by default, the player
+should not be moving. Then we check each input and add/subtract from the
+``velocity`` to obtain a total direction. For example, if you hold ``right`` and
+``down`` at the same time, the resulting ``velocity`` vector will be ``(1, 1)``.
+In this case, since we're adding a horizontal and a vertical movement, the
+player would move *faster* diagonally than if it just moved horizontally.
+
+We can prevent that if we *normalize* the velocity, which means we set its
+*length* to ``1``, then multiply by the desired speed. This means no more fast
+diagonal movement.
+
+.. tip:: If you've never used vector math before, or need a refresher, you can
+         see an explanation of vector usage in Godot at :ref:`doc_vector_math`.
+         It's good to know but won't be necessary for the rest of this tutorial.
+
+We also check whether the player is moving so we can call ``play()`` or
+``stop()`` on the AnimatedSprite2D.
+
+         ``$`` is shorthand for ``get_node()``. So in the code above,
+         ``$AnimatedSprite2D.play()`` is the same as
+         ``get_node("AnimatedSprite2D").play()``.
+
+.. tip:: In GDScript, ``$`` returns the node at the relative path from the
+         current node, or returns ``null`` if the node is not found. Since
+         AnimatedSprite2D is a child of the current node, we can use
+         ``$AnimatedSprite2D``.
+
+Now that we have a movement direction, we can update the player's position. We
+can also use ``clamp()`` to prevent it from leaving the screen. *Clamping* a
+value means restricting it to a given range. Add the following to the bottom of
+the ``_process`` function (make sure it's not indented under the `else`):
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        position += velocity * delta
+        position.x = clamp(position.x, 0, screen_size.x)
+        position.y = clamp(position.y, 0, screen_size.y)
+
+ .. code-tab:: csharp
+
+        Position += velocity * delta;
+        Position = new Vector2(
+            x: Mathf.Clamp(Position.x, 0, ScreenSize.x),
+            y: Mathf.Clamp(Position.y, 0, ScreenSize.y)
+        );
+
+ .. code-tab:: cpp
+
+        godot::Vector2 position = get_position();
+        position += velocity * (real_t)p_delta;
+        position.x = godot::Math::clamp(position.x, (real_t)0.0, _screen_size.x);
+        position.y = godot::Math::clamp(position.y, (real_t)0.0, _screen_size.y);
+        set_position(position);
+
+.. tip:: The `delta` parameter in the `_process()` function refers to the *frame
+        length* - the amount of time that the previous frame took to complete.
+        Using this value ensures that your movement will remain consistent even
+        if the frame rate changes.
+
+Click "Play Scene" (:kbd:`F6`, :kbd:`Cmd + R` on macOS) and confirm you can move
+the player around the screen in all directions.
+
+.. warning:: If you get an error in the "Debugger" panel that says
+
+            ``Attempt to call function 'play' in base 'null instance' on a null
+            instance``
+
+            this likely means you spelled the name of the AnimatedSprite2D node
+            wrong. Node names are case-sensitive and ``$NodeName`` must match
+            the name you see in the scene tree.
+
+Choosing animations
+~~~~~~~~~~~~~~~~~~~
+
+Now that the player can move, we need to change which animation the
+AnimatedSprite2D is playing based on its direction. We have the "walk" animation,
+which shows the player walking to the right. This animation should be flipped
+horizontally using the ``flip_h`` property for left movement. We also have the
+"up" animation, which should be flipped vertically with ``flip_v`` for downward
+movement. Let's place this code at the end of the ``_process()`` function:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        if velocity.x != 0:
+            $AnimatedSprite2D.animation = "walk"
+            $AnimatedSprite2D.flip_v = false
+            # See the note below about boolean assignment.
+            $AnimatedSprite2D.flip_h = velocity.x < 0
+        elif velocity.y != 0:
+            $AnimatedSprite2D.animation = "up"
+            $AnimatedSprite2D.flip_v = velocity.y > 0
+
+ .. code-tab:: csharp
+
+        if (velocity.x != 0)
+        {
+            animatedSprite2D.Animation = "walk";
+            animatedSprite2D.FlipV = false;
+            // See the note below about boolean assignment.
+            animatedSprite2D.FlipH = velocity.x < 0;
+        }
+        else if (velocity.y != 0)
+        {
+            animatedSprite2D.Animation = "up";
+            animatedSprite2D.FlipV = velocity.y > 0;
+        }
+
+ .. code-tab:: cpp
+
+        if (velocity.x != 0) {
+            _animated_sprite->set_animation("right");
+            _animated_sprite->set_flip_v(false);
+            // See the note below about boolean assignment.
+            _animated_sprite->set_flip_h(velocity.x < 0);
+        } else if (velocity.y != 0) {
+            _animated_sprite->set_animation("up");
+            _animated_sprite->set_flip_v(velocity.y > 0);
+        }
+
+.. Note:: The boolean assignments in the code above are a common shorthand for
+          programmers. Since we're doing a comparison test (boolean) and also
+          *assigning* a boolean value, we can do both at the same time. Consider
+          this code versus the one-line boolean assignment above:
+
+          .. tabs::
+           .. code-tab :: gdscript GDScript
+
+             if velocity.x < 0:
+                 $AnimatedSprite2D.flip_h = true
+             else:
+                 $AnimatedSprite2D.flip_h = false
+
+           .. code-tab:: csharp
+
+             if (velocity.x < 0)
+             {
+                 animatedSprite2D.FlipH = true;
+             }
+             else
+             {
+                 animatedSprite2D.FlipH = false;
+             }
+
+Play the scene again and check that the animations are correct in each of the
+directions.
+
+.. tip:: A common mistake here is to type the names of the animations wrong. The
+        animation names in the SpriteFrames panel must match what you type in
+        the code. If you named the animation ``"Walk"``, you must also use a
+        capital "W" in the code.
+
+When you're sure the movement is working correctly, add this line to
+``_ready()``, so the player will be hidden when the game starts:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    hide()
+
+ .. code-tab:: csharp
+
+    Hide();
+
+ .. code-tab:: cpp
+
+    hide();
+
+Preparing for collisions
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+We want ``Player`` to detect when it's hit by an enemy, but we haven't made any
+enemies yet! That's OK, because we're going to use Godot's *signal*
+functionality to make it work.
+
+Add the following at the top of the script, after ``extends Area2D``:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    signal hit
+
+ .. code-tab:: csharp
+
+    // Don't forget to rebuild the project so the editor knows about the new signal.
+
+    [Signal]
+    public delegate void Hit();
+
+ .. code-tab:: cpp
+
+    // This code goes in `player.cpp`.
+    // We need to register the signal here, and while we're here, we can also
+    // register the other methods and register the speed property.
+    void Player::_register_methods() {
+        godot::register_method("_ready", &Player::_ready);
+        godot::register_method("_process", &Player::_process);
+        godot::register_method("start", &Player::start);
+        godot::register_method("_on_Player_body_entered", &Player::_on_Player_body_entered);
+        godot::register_property("speed", &Player::speed, (real_t)400.0);
+        // This below line is the signal.
+        godot::register_signal<Player>("hit", godot::Dictionary());
+    }
+
+This defines a custom signal called "hit" that we will have our player emit
+(send out) when it collides with an enemy. We will use ``Area2D`` to detect the
+collision. Select the ``Player`` node and click the "Node" tab next to the
+Inspector tab to see the list of signals the player can emit:
+
+.. image:: img/player_signals.png
+
+Notice our custom "hit" signal is there as well! Since our enemies are going to
+be ``RigidBody2D`` nodes, we want the ``body_entered(body: Node)`` signal. This
+signal will be emitted when a body contacts the player. Click "Connect.." and
+the "Connect a Signal" window appears. We don't need to change any of these
+settings so click "Connect" again. Godot will automatically create a function in
+your player's script.
+
+.. image:: img/player_signal_connection.png
+
+Note the green icon indicating that a signal is connected to this function. Add
+this code to the function:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _on_Player_body_entered(body):
+        hide() # Player disappears after being hit.
+        emit_signal("hit")
+        # Must be deferred as we can't change physics properties on a physics callback.
+        $CollisionShape2D.set_deferred("disabled", true)
+
+ .. code-tab:: csharp
+
+    public void OnPlayerBodyEntered(PhysicsBody2D body)
+    {
+        Hide(); // Player disappears after being hit.
+        EmitSignal(nameof(Hit));
+        // Must be deferred as we can't change physics properties on a physics callback.
+        GetNode<CollisionShape2D>("CollisionShape2D").SetDeferred("disabled", true);
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `player.cpp`.
+    void Player::_on_Player_body_entered(godot::Node2D *_body) {
+        hide(); // Player disappears after being hit.
+        emit_signal("hit");
+        // Must be deferred as we can't change physics properties on a physics callback.
+        _collision_shape->set_deferred("disabled", true);
+    }
+
+Each time an enemy hits the player, the signal is going to be emitted. We need
+to disable the player's collision so that we don't trigger the ``hit`` signal
+more than once.
+
+.. Note:: Disabling the area's collision shape can cause an error if it happens
+          in the middle of the engine's collision processing. Using
+          ``set_deferred()`` tells Godot to wait to disable the shape until it's
+          safe to do so.
+
+The last piece is to add a function we can call to reset the player when
+starting a new game.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func start(pos):
+        position = pos
+        show()
+        $CollisionShape2D.disabled = false
+
+ .. code-tab:: csharp
+
+    public void Start(Vector2 pos)
+    {
+        Position = pos;
+        Show();
+        GetNode<CollisionShape2D>("CollisionShape2D").Disabled = false;
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `player.cpp`.
+    void Player::start(const godot::Vector2 p_position) {
+        set_position(p_position);
+        show();
+        _collision_shape->set_disabled(false);
+    }
+
+With the player working, we'll work on the enemy in the next lesson.

+ 180 - 0
getting_started/first_2d_game/04.creating_the_enemy.rst

@@ -0,0 +1,180 @@
+.. _doc_your_first_2d_game_creating_the_enemy:
+
+Creating the enemy
+==================
+
+Now it's time to make the enemies our player will have to dodge. Their behavior
+will not be very complex: mobs will spawn randomly at the edges of the screen,
+choose a random direction, and move in a straight line.
+
+We'll create a ``Mob`` scene, which we can then *instance* to create any number
+of independent mobs in the game.
+
+Node setup
+~~~~~~~~~~
+
+Click Scene -> New Scene and add the following nodes:
+
+- :ref:`RigidBody2D <class_RigidBody2D>` (named ``Mob``)
+
+   - :ref:`AnimatedSprite2D <class_AnimatedSprite2D>`
+   - :ref:`CollisionShape2D <class_CollisionShape2D>`
+   - :ref:`VisibilityNotifier2D <class_VisibilityNotifier2D>`
+
+Don't forget to set the children so they can't be selected, like you did with
+the Player scene.
+
+In the :ref:`RigidBody2D <class_RigidBody2D>` properties, set ``Gravity Scale``
+to ``0``, so the mob will not fall downward. In addition, under the
+``PhysicsBody2D`` section, click the ``Mask`` property and uncheck the first
+box. This will ensure the mobs do not collide with each other.
+
+.. image:: img/set_collision_mask.png
+
+Set up the :ref:`AnimatedSprite2D <class_AnimatedSprite2D>` like you did for the
+player. This time, we have 3 animations: ``fly``, ``swim``, and ``walk``. There
+are two images for each animation in the art folder.
+
+Adjust the "Speed (FPS)" to ``3`` for all animations.
+
+.. image:: img/mob_animations.gif
+
+Set the ``Playing`` property in the Inspector to "On".
+
+We'll select one of these animations randomly so that the mobs will have some
+variety.
+
+Like the player images, these mob images need to be scaled down. Set the
+``AnimatedSprite2D``'s ``Scale`` property to ``(0.75, 0.75)``.
+
+As in the ``Player`` scene, add a ``CapsuleShape2D`` for the collision. To align
+the shape with the image, you'll need to set the ``Rotation Degrees`` property
+to ``90`` (under "Transform" in the Inspector).
+
+Save the scene.
+
+Enemy script
+~~~~~~~~~~~~
+
+Add a script to the ``Mob`` like this:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends RigidBody2D
+
+ .. code-tab:: csharp
+
+    public class Mob : RigidBody2D
+    {
+        // Don't forget to rebuild the project.
+    }
+
+ .. code-tab:: cpp
+
+    // Copy `player.gdns` to `mob.gdns` and replace `Player` with `Mob`.
+    // Attach the `mob.gdns` file to the Mob node.
+
+    // Create two files `mob.cpp` and `mob.hpp` next to `entry.cpp` in `src`.
+    // This code goes in `mob.hpp`. We also define the methods we'll be using here.
+    #ifndef MOB_H
+    #define MOB_H
+
+    #include <AnimatedSprite2D.hpp>
+    #include <Godot.hpp>
+    #include <RigidBody2D.hpp>
+
+    class Mob : public godot::RigidBody2D {
+        GODOT_CLASS(Mob, godot::RigidBody2D)
+
+        godot::AnimatedSprite2D *_animated_sprite;
+
+    public:
+        void _init() {}
+        void _ready();
+        void _on_VisibilityNotifier2D_screen_exited();
+
+        static void _register_methods();
+    };
+
+    #endif // MOB_H
+
+Now let's look at the rest of the script. In ``_ready()`` we play the animation
+and randomly choose one of the three animation types:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _ready():
+        $AnimatedSprite2D.playing = true
+        var mob_types = $AnimatedSprite2D.frames.get_animation_names()
+        $AnimatedSprite2D.animation = mob_types[randi() % mob_types.size()]
+
+ .. code-tab:: csharp
+
+    public override void _Ready()
+    {
+        var animSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
+        animSprite2D.Playing = true;
+        string[] mobTypes = animSprite2D.Frames.GetAnimationNames();
+        animSprite2D.Animation = mobTypes[GD.Randi() % mobTypes.Length];
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `mob.cpp`.
+    #include "mob.hpp"
+
+    #include <RandomNumberGenerator.hpp>
+    #include <SpriteFrames.hpp>
+
+    void Mob::_ready() {
+        godot::Ref<godot::RandomNumberGenerator> random = godot::RandomNumberGenerator::_new();
+        random->randomize();
+        _animated_sprite = get_node<godot::AnimatedSprite2D>("AnimatedSprite2D");
+        _animated_sprite->_set_playing(true);
+        godot::PoolStringArray mob_types = _animated_sprite->get_sprite_frames()->get_animation_names();
+        _animated_sprite->set_animation(mob_types[random->randi() % mob_types.size()]);
+    }
+
+First, we get the list of animation names from the AnimatedSprite2D's ``frames``
+property. This returns an Array containing all three animation names: ``["walk",
+"swim", "fly"]``.
+
+We then need to pick a random number between ``0`` and ``2`` to select one of
+these names from the list (array indices start at ``0``). ``randi() % n``
+selects a random integer between ``0`` and ``n-1``.
+
+.. note:: You must use ``randomize()`` if you want your sequence of "random"
+            numbers to be different every time you run the scene. We're going to
+            use ``randomize()`` in our ``Main`` scene, so we won't need it here.
+
+The last piece is to make the mobs delete themselves when they leave the screen.
+Connect the ``screen_exited()`` signal of the ``VisibilityNotifier2D`` node and
+add this code:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _on_VisibilityNotifier2D_screen_exited():
+        queue_free()
+
+ .. code-tab:: csharp
+
+    public void OnVisibilityNotifier2DScreenExited()
+    {
+        QueueFree();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `mob.cpp`.
+    void Mob::_on_VisibilityNotifier2D_screen_exited() {
+        queue_free();
+    }
+
+This completes the `Mob` scene.
+
+With the player and enemies ready, in the next part, we'll bring them together
+in a new scene. We'll make enemies spawn randomly around the game board and move
+forward, turning our project into a playable game.

+ 446 - 0
getting_started/first_2d_game/05.the_main_game_scene.rst

@@ -0,0 +1,446 @@
+.. _doc_your_first_2d_game_the_main_game_scene:
+
+The main game scene
+===================
+
+Now it's time to bring everything we did together into a playable game scene.
+
+Create a new scene and add a :ref:`Node <class_Node>` named ``Main``. Ensure you
+create a Node, **not** a Node2D. Click the "Instance" button and select your
+saved ``Player.tscn``.
+
+.. image:: img/instance_scene.png
+
+Now, add the following nodes as children of ``Main``, and name them as shown
+(values are in seconds):
+
+- :ref:`Timer <class_Timer>` (named ``MobTimer``) - to control how often mobs
+  spawn
+- :ref:`Timer <class_Timer>` (named ``ScoreTimer``) - to increment the score
+  every second
+- :ref:`Timer <class_Timer>` (named ``StartTimer``) - to give a delay before
+  starting
+- :ref:`Position2D <class_Position2D>` (named ``StartPosition``) - to indicate
+  the player's start position
+
+Set the ``Wait Time`` property of each of the ``Timer`` nodes as follows:
+
+- ``MobTimer``: ``0.5``
+- ``ScoreTimer``: ``1``
+- ``StartTimer``: ``2``
+
+In addition, set the ``One Shot`` property of ``StartTimer`` to "On" and set
+``Position`` of the ``StartPosition`` node to ``(240, 450)``.
+
+Spawning mobs
+~~~~~~~~~~~~~
+
+The Main node will be spawning new mobs, and we want them to appear at a random
+location on the edge of the screen. Add a :ref:`Path2D <class_Path2D>` node
+named ``MobPath`` as a child of ``Main``. When you select ``Path2D``, you will
+see some new buttons at the top of the editor:
+
+.. image:: img/path2d_buttons.png
+
+Select the middle one ("Add Point") and draw the path by clicking to add the
+points at the corners shown. To have the points snap to the grid, make sure "Use
+Grid Snap" and "Use Snap" are both selected. These options can be found to the
+left of the "Lock" button, appearing as a magnet next to some dots and
+intersecting lines, respectively.
+
+.. image:: img/grid_snap_button.png
+
+.. important:: Draw the path in *clockwise* order, or your mobs will spawn
+               pointing *outwards* instead of *inwards*!
+
+.. image:: img/draw_path2d.gif
+
+After placing point ``4`` in the image, click the "Close Curve" button and your
+curve will be complete.
+
+Now that the path is defined, add a :ref:`PathFollow2D <class_PathFollow2D>`
+node as a child of ``MobPath`` and name it ``MobSpawnLocation``. This node will
+automatically rotate and follow the path as it moves, so we can use it to select
+a random position and direction along the path.
+
+Your scene should look like this:
+
+.. image:: img/main_scene_nodes.png
+
+Main script
+~~~~~~~~~~~
+
+Add a script to ``Main``. At the top of the script, we use ``export
+(PackedScene)`` to allow us to choose the Mob scene we want to instance.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends Node
+
+    export(PackedScene) var mob_scene
+    var score
+
+ .. code-tab:: csharp
+
+    public class Main : Node
+    {
+        // Don't forget to rebuild the project so the editor knows about the new export variable.
+
+    #pragma warning disable 649
+        // We assign this in the editor, so we don't need the warning about not being assigned.
+        [Export]
+        public PackedScene MobScene;
+    #pragma warning restore 649
+
+        public int Score;
+    }
+
+ .. code-tab:: cpp
+
+    // Copy `player.gdns` to `main.gdns` and replace `Player` with `Main`.
+    // Attach the `main.gdns` file to the Main node.
+
+    // Create two files `main.cpp` and `main.hpp` next to `entry.cpp` in `src`.
+    // This code goes in `main.hpp`. We also define the methods we'll be using here.
+    #ifndef MAIN_H
+    #define MAIN_H
+
+    #include <AudioStreamPlayer.hpp>
+    #include <CanvasLayer.hpp>
+    #include <Godot.hpp>
+    #include <Node.hpp>
+    #include <PackedScene.hpp>
+    #include <PathFollow2D.hpp>
+    #include <RandomNumberGenerator.hpp>
+    #include <Timer.hpp>
+
+    #include "hud.hpp"
+    #include "player.hpp"
+
+    class Main : public godot::Node {
+        GODOT_CLASS(Main, godot::Node)
+
+        int score;
+        HUD *_hud;
+        Player *_player;
+        godot::Node2D *_start_position;
+        godot::PathFollow2D *_mob_spawn_location;
+        godot::Timer *_mob_timer;
+        godot::Timer *_score_timer;
+        godot::Timer *_start_timer;
+        godot::AudioStreamPlayer *_music;
+        godot::AudioStreamPlayer *_death_sound;
+        godot::Ref<godot::RandomNumberGenerator> _random;
+
+    public:
+        godot::Ref<godot::PackedScene> mob_scene;
+
+        void _init() {}
+        void _ready();
+        void game_over();
+        void new_game();
+        void _on_MobTimer_timeout();
+        void _on_ScoreTimer_timeout();
+        void _on_StartTimer_timeout();
+
+        static void _register_methods();
+    };
+
+    #endif // MAIN_H
+
+We also add a call to ``randomize()`` here so that the random number
+generator generates different random numbers each time the game is run:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _ready():
+        randomize()
+
+ .. code-tab:: csharp
+
+    public override void _Ready()
+    {
+        GD.Randomize();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `main.cpp`.
+    #include "main.hpp"
+
+    #include <SceneTree.hpp>
+
+    #include "mob.hpp"
+
+    void Main::_ready() {
+        _hud = get_node<HUD>("HUD");
+        _player = get_node<Player>("Player");
+        _start_position = get_node<godot::Node2D>("StartPosition");
+        _mob_spawn_location = get_node<godot::PathFollow2D>("MobPath/MobSpawnLocation");
+        _mob_timer = get_node<godot::Timer>("MobTimer");
+        _score_timer = get_node<godot::Timer>("ScoreTimer");
+        _start_timer = get_node<godot::Timer>("StartTimer");
+        // Uncomment these after adding the nodes in the "Sound effects" section of "Finishing up".
+        //_music = get_node<godot::AudioStreamPlayer>("Music");
+        //_death_sound = get_node<godot::AudioStreamPlayer>("DeathSound");
+        _random = (godot::Ref<godot::RandomNumberGenerator>)godot::RandomNumberGenerator::_new();
+        _random->randomize();
+    }
+
+Click the ``Main`` node and you will see the ``Mob Scene`` property in the Inspector
+under "Script Variables".
+
+You can assign this property's value in two ways:
+
+- Drag ``Mob.tscn`` from the "FileSystem" panel and drop it in the ``Mob``
+  property .
+- Click the down arrow next to "[empty]" and choose "Load". Select ``Mob.tscn``.
+
+Next, select the ``Player`` node in the Scene dock, and access the Node dock on
+the sidebar. Make sure to have the Signals tab selected in the Node dock.
+
+You should see a list of the signals for the ``Player`` node. Find and
+double-click the ``hit`` signal in the list (or right-click it and select
+"Connect..."). This will open the signal connection dialog. We want to make a
+new function named ``game_over``, which will handle what needs to happen when a
+game ends. Type "game_over" in the "Receiver Method" box at the bottom of the
+signal connection dialog and click "Connect". Add the following code to the new
+function, as well as a ``new_game`` function that will set everything up for a
+new game:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func game_over():
+        $ScoreTimer.stop()
+        $MobTimer.stop()
+
+    func new_game():
+        score = 0
+        $Player.start($StartPosition.position)
+        $StartTimer.start()
+
+ .. code-tab:: csharp
+
+    public void GameOver()
+    {
+        GetNode<Timer>("MobTimer").Stop();
+        GetNode<Timer>("ScoreTimer").Stop();
+    }
+
+    public void NewGame()
+    {
+        Score = 0;
+
+        var player = GetNode<Player>("Player");
+        var startPosition = GetNode<Position2D>("StartPosition");
+        player.Start(startPosition.Position);
+
+        GetNode<Timer>("StartTimer").Start();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `main.cpp`.
+    void Main::game_over() {
+        _score_timer->stop();
+        _mob_timer->stop();
+    }
+
+    void Main::new_game() {
+        score = 0;
+        _player->start(_start_position->get_position());
+        _start_timer->start();
+    }
+
+Now connect the ``timeout()`` signal of each of the Timer nodes (``StartTimer``,
+``ScoreTimer`` , and ``MobTimer``) to the main script. ``StartTimer`` will start
+the other two timers. ``ScoreTimer`` will increment the score by 1.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _on_ScoreTimer_timeout():
+        score += 1
+
+    func _on_StartTimer_timeout():
+        $MobTimer.start()
+        $ScoreTimer.start()
+
+ .. code-tab:: csharp
+
+    public void OnScoreTimerTimeout()
+    {
+        Score++;
+    }
+
+    public void OnStartTimerTimeout()
+    {
+        GetNode<Timer>("MobTimer").Start();
+        GetNode<Timer>("ScoreTimer").Start();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `main.cpp`.
+    void Main::_on_ScoreTimer_timeout() {
+        score += 1;
+    }
+
+    void Main::_on_StartTimer_timeout() {
+        _mob_timer->start();
+        _score_timer->start();
+    }
+
+    // Also add this to register all methods and the mob scene property.
+    void Main::_register_methods() {
+        godot::register_method("_ready", &Main::_ready);
+        godot::register_method("game_over", &Main::game_over);
+        godot::register_method("new_game", &Main::new_game);
+        godot::register_method("_on_MobTimer_timeout", &Main::_on_MobTimer_timeout);
+        godot::register_method("_on_ScoreTimer_timeout", &Main::_on_ScoreTimer_timeout);
+        godot::register_method("_on_StartTimer_timeout", &Main::_on_StartTimer_timeout);
+        godot::register_property("mob_scene", &Main::mob_scene, (godot::Ref<godot::PackedScene>)nullptr);
+    }
+
+In ``_on_MobTimer_timeout()``, we will create a mob instance, pick a random
+starting location along the ``Path2D``, and set the mob in motion. The
+``PathFollow2D`` node will automatically rotate as it follows the path, so we
+will use that to select the mob's direction as well as its position.
+When we spawn a mob, we'll pick a random value between ``150.0`` and
+``250.0`` for how fast each mob will move (it would be boring if they were
+all moving at the same speed).
+
+Note that a new instance must be added to the scene using ``add_child()``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _on_MobTimer_timeout():
+        # Choose a random location on Path2D.
+        var mob_spawn_location = get_node("MobPath/MobSpawnLocation");
+        mob_spawn_location.offset = randi()
+
+        # Create a Mob instance and add it to the scene.
+        var mob = mob_scene.instance()
+        add_child(mob)
+
+        # Set the mob's direction perpendicular to the path direction.
+        var direction = mob_spawn_location.rotation + PI / 2
+
+        # Set the mob's position to a random location.
+        mob.position = mob_spawn_location.position
+
+        # Add some randomness to the direction.
+        direction += rand_range(-PI / 4, PI / 4)
+        mob.rotation = direction
+
+        # Choose the velocity.
+        var velocity = Vector2(rand_range(150.0, 250.0), 0.0)
+        mob.linear_velocity = velocity.rotated(direction)
+
+ .. code-tab:: csharp
+
+    public void OnMobTimerTimeout()
+    {
+        // Note: Normally it is best to use explicit types rather than the `var`
+        // keyword. However, var is acceptable to use here because the types are
+        // obviously PathFollow2D and Mob, since they appear later on the line.
+
+        // Choose a random location on Path2D.
+        var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
+        mobSpawnLocation.Offset = GD.Randi();
+
+        // Create a Mob instance and add it to the scene.
+        var mob = (Mob)MobScene.Instance();
+        AddChild(mob);
+
+        // Set the mob's direction perpendicular to the path direction.
+        float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;
+
+        // Set the mob's position to a random location.
+        mob.Position = mobSpawnLocation.Position;
+
+        // Add some randomness to the direction.
+        direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
+        mob.Rotation = direction;
+
+        // Choose the velocity.
+        var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
+        mob.LinearVelocity = velocity.Rotated(direction);
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `main.cpp`.
+    void Main::_on_MobTimer_timeout() {
+        // Choose a random location on Path2D.
+        _mob_spawn_location->set_offset((real_t)_random->randi());
+
+        // Create a Mob instance and add it to the scene.
+        godot::Node *mob = mob_scene->instance();
+        add_child(mob);
+
+        // Set the mob's direction perpendicular to the path direction.
+        real_t direction = _mob_spawn_location->get_rotation() + (real_t)Math_PI / 2;
+
+        // Set the mob's position to a random location.
+        mob->set("position", _mob_spawn_location->get_position());
+
+        // Add some randomness to the direction.
+        direction += _random->randf_range((real_t)-Math_PI / 4, (real_t)Math_PI / 4);
+        mob->set("rotation", direction);
+
+        // Choose the velocity for the mob.
+        godot::Vector2 velocity = godot::Vector2(_random->randf_range(150.0, 250.0), 0.0);
+        mob->set("linear_velocity", velocity.rotated(direction));
+    }
+
+.. important:: Why ``PI``? In functions requiring angles, Godot uses *radians*,
+               not degrees. Pi represents a half turn in radians, about
+               ``3.1415`` (there is also ``TAU`` which is equal to ``2 * PI``).
+               If you're more comfortable working with degrees, you'll need to
+               use the ``deg2rad()`` and ``rad2deg()`` functions to convert
+               between the two.
+
+Testing the scene
+~~~~~~~~~~~~~~~~~
+
+Let's test the scene to make sure everything is working. Add this ``new_game``
+call to ``_ready()``:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _ready():
+        randomize()
+        new_game()
+
+ .. code-tab:: csharp
+
+    public override void _Ready()
+    {
+        NewGame();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `main.cpp`.
+    void Main::_ready() {
+        new_game();
+    }
+
+Let's also assign ``Main`` as our "Main Scene" - the one that runs automatically
+when the game launches. Press the "Play" button and select ``Main.tscn`` when
+prompted.
+
+You should be able to move the player around, see mobs spawning, and see the
+player disappear when hit by a mob.
+
+When you're sure everything is working, remove the call to ``new_game()`` from
+``_ready()``.
+
+What's our game lacking? Some user interface. In the next lesson, we'll add a
+title screen and display the player's score.

+ 437 - 0
getting_started/first_2d_game/06.heads_up_display.rst

@@ -0,0 +1,437 @@
+.. _doc_your_first_2d_game_heads_up_display:
+
+Heads up display
+================
+
+The final piece our game needs is a User Interface (UI) to display things like
+score, a "game over" message, and a restart button.
+
+Create a new scene, and add a :ref:`CanvasLayer <class_CanvasLayer>` node named
+``HUD``. "HUD" stands for "heads-up display", an informational display that
+appears as an overlay on top of the game view.
+
+The :ref:`CanvasLayer <class_CanvasLayer>` node lets us draw our UI elements on
+a layer above the rest of the game, so that the information it displays isn't
+covered up by any game elements like the player or mobs.
+
+The HUD needs to display the following information:
+
+- Score, changed by ``ScoreTimer``.
+- A message, such as "Game Over" or "Get Ready!"
+- A "Start" button to begin the game.
+
+The basic node for UI elements is :ref:`Control <class_Control>`. To create our
+UI, we'll use two types of :ref:`Control <class_Control>` nodes: :ref:`Label
+<class_Label>` and :ref:`Button <class_Button>`.
+
+Create the following as children of the ``HUD`` node:
+
+- :ref:`Label <class_Label>` named ``ScoreLabel``.
+- :ref:`Label <class_Label>` named ``Message``.
+- :ref:`Button <class_Button>` named ``StartButton``.
+- :ref:`Timer <class_Timer>` named ``MessageTimer``.
+
+Click on the ``ScoreLabel`` and type a number into the ``Text`` field in the
+Inspector. The default font for ``Control`` nodes is small and doesn't scale
+well. There is a font file included in the game assets called
+"Xolonium-Regular.ttf". To use this font, do the following:
+
+1. Under "Custom Fonts", choose "New Font"
+
+.. image:: img/custom_font1.png
+
+2. Click on the "Font" you added, and under "Font/Data/0",
+   choose "Load" and select the "Xolonium-Regular.ttf" file.
+
+.. image:: img/custom_font2.png
+
+Once you've done this on the ``ScoreLabel``, you can click the down arrow next
+to the Font property and choose "Copy", then "Paste" it in the same place
+on the other two Control nodes.
+Set "Custom Font Size" property of the ``ScoreLabel``. A setting of ``64`` works well.
+
+.. image:: img/custom_font3.png
+
+.. note:: **Anchors and Margins:** ``Control`` nodes have a position and size,
+          but they also have anchors and margins. Anchors define the origin -
+          the reference point for the edges of the node. Margins update
+          automatically when you move or resize a control node. They represent
+          the distance from the control node's edges to its anchor.
+
+Arrange the nodes as shown below. Click the "Layout" button to set a Control
+node's layout:
+
+.. image:: img/ui_anchor.png
+
+You can drag the nodes to place them manually, or for more precise placement,
+use the following settings:
+
+ScoreLabel
+~~~~~~~~~~
+
+-  *Layout* : "Top Wide"
+-  *Text* : ``0``
+-  *Align* : "Center"
+
+Message
+~~~~~~~~~~~~
+
+-  *Layout* : "HCenter Wide"
+-  *Text* : ``Dodge the Creeps!``
+-  *Align* : "Center"
+-  *Autowrap* : "On"
+
+StartButton
+~~~~~~~~~~~
+
+-  *Text* : ``Start``
+-  *Layout* : "Center Bottom"
+-  *Margin* :
+
+   -  Top: ``-200``
+   -  Bottom: ``-100``
+
+On the ``MessageTimer``, set the ``Wait Time`` to ``2`` and set the ``One Shot``
+property to "On".
+
+Now add this script to ``HUD``:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    extends CanvasLayer
+
+    signal start_game
+
+ .. code-tab:: csharp
+
+    public class HUD : CanvasLayer
+    {
+        // Don't forget to rebuild the project so the editor knows about the new signal.
+
+        [Signal]
+        public delegate void StartGame();
+    }
+
+ .. code-tab:: cpp
+
+    // Copy `player.gdns` to `hud.gdns` and replace `Player` with `HUD`.
+    // Attach the `hud.gdns` file to the HUD node.
+
+    // Create two files `hud.cpp` and `hud.hpp` next to `entry.cpp` in `src`.
+    // This code goes in `hud.hpp`. We also define the methods we'll be using here.
+    #ifndef HUD_H
+    #define HUD_H
+
+    #include <Button.hpp>
+    #include <CanvasLayer.hpp>
+    #include <Godot.hpp>
+    #include <Label.hpp>
+    #include <Timer.hpp>
+
+    class HUD : public godot::CanvasLayer {
+        GODOT_CLASS(HUD, godot::CanvasLayer)
+
+        godot::Label *_score_label;
+        godot::Label *_message_label;
+        godot::Timer *_start_message_timer;
+        godot::Timer *_get_ready_message_timer;
+        godot::Button *_start_button;
+        godot::Timer *_start_button_timer;
+
+    public:
+        void _init() {}
+        void _ready();
+        void show_get_ready();
+        void show_game_over();
+        void update_score(const int score);
+        void _on_StartButton_pressed();
+        void _on_StartMessageTimer_timeout();
+        void _on_GetReadyMessageTimer_timeout();
+
+        static void _register_methods();
+    };
+
+    #endif // HUD_H
+
+The ``start_game`` signal tells the ``Main`` node that the button
+has been pressed.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func show_message(text):
+        $Message.text = text
+        $Message.show()
+        $MessageTimer.start()
+
+ .. code-tab:: csharp
+
+    public void ShowMessage(string text)
+    {
+        var message = GetNode<Label>("Message");
+        message.Text = text;
+        message.Show();
+
+        GetNode<Timer>("MessageTimer").Start();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `hud.cpp`.
+    #include "hud.hpp"
+
+    void HUD::_ready() {
+        _score_label = get_node<godot::Label>("ScoreLabel");
+        _message_label = get_node<godot::Label>("MessageLabel");
+        _start_message_timer = get_node<godot::Timer>("StartMessageTimer");
+        _get_ready_message_timer = get_node<godot::Timer>("GetReadyMessageTimer");
+        _start_button = get_node<godot::Button>("StartButton");
+        _start_button_timer = get_node<godot::Timer>("StartButtonTimer");
+    }
+
+    void HUD::_register_methods() {
+        godot::register_method("_ready", &HUD::_ready);
+        godot::register_method("show_get_ready", &HUD::show_get_ready);
+        godot::register_method("show_game_over", &HUD::show_game_over);
+        godot::register_method("update_score", &HUD::update_score);
+        godot::register_method("_on_StartButton_pressed", &HUD::_on_StartButton_pressed);
+        godot::register_method("_on_StartMessageTimer_timeout", &HUD::_on_StartMessageTimer_timeout);
+        godot::register_method("_on_GetReadyMessageTimer_timeout", &HUD::_on_GetReadyMessageTimer_timeout);
+        godot::register_signal<HUD>("start_game", godot::Dictionary());
+    }
+
+This function is called when we want to display a message
+temporarily, such as "Get Ready".
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func show_game_over():
+        show_message("Game Over")
+        # Wait until the MessageTimer has counted down.
+        yield($MessageTimer, "timeout")
+
+        $Message.text = "Dodge the\nCreeps!"
+        $Message.show()
+        # Make a one-shot timer and wait for it to finish.
+        yield(get_tree().create_timer(1), "timeout")
+        $StartButton.show()
+
+ .. code-tab:: csharp
+
+    async public void ShowGameOver()
+    {
+        ShowMessage("Game Over");
+
+        var messageTimer = GetNode<Timer>("MessageTimer");
+        await ToSignal(messageTimer, "timeout");
+
+        var message = GetNode<Label>("Message");
+        message.Text = "Dodge the\nCreeps!";
+        message.Show();
+
+        await ToSignal(GetTree().CreateTimer(1), "timeout");
+        GetNode<Button>("StartButton").Show();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `hud.cpp`.
+    // There is no `yield` in GDNative, so we need to have every
+    // step be its own method that is called on timer timeout.
+    void HUD::show_get_ready() {
+        _message_label->set_text("Get Ready");
+        _message_label->show();
+        _get_ready_message_timer->start();
+    }
+
+    void HUD::show_game_over() {
+        _message_label->set_text("Game Over");
+        _message_label->show();
+        _start_message_timer->start();
+    }
+
+This function is called when the player loses. It will show "Game Over" for 2
+seconds, then return to the title screen and, after a brief pause, show the
+"Start" button.
+
+.. note:: When you need to pause for a brief time, an alternative to using a
+          Timer node is to use the SceneTree's ``create_timer()`` function. This
+          can be very useful to add delays such as in the above code, where we
+          want to wait some time before showing the "Start" button.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func update_score(score):
+        $ScoreLabel.text = str(score)
+
+ .. code-tab:: csharp
+
+    public void UpdateScore(int score)
+    {
+        GetNode<Label>("ScoreLabel").Text = score.ToString();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `hud.cpp`.
+    void HUD::update_score(const int p_score) {
+        _score_label->set_text(godot::Variant(p_score));
+    }
+
+This function is called by ``Main`` whenever the score changes.
+
+Connect the ``timeout()`` signal of ``MessageTimer`` and the ``pressed()``
+signal of ``StartButton`` and add the following code to the new functions:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _on_StartButton_pressed():
+        $StartButton.hide()
+        emit_signal("start_game")
+
+    func _on_MessageTimer_timeout():
+        $Message.hide()
+
+ .. code-tab:: csharp
+
+    public void OnStartButtonPressed()
+    {
+        GetNode<Button>("StartButton").Hide();
+        EmitSignal("StartGame");
+    }
+
+    public void OnMessageTimerTimeout()
+    {
+        GetNode<Label>("Message").Hide();
+    }
+
+ .. code-tab:: cpp
+
+    // This code goes in `hud.cpp`.
+    void HUD::_on_StartButton_pressed() {
+        _start_button_timer->stop();
+        _start_button->hide();
+        emit_signal("start_game");
+    }
+
+    void HUD::_on_StartMessageTimer_timeout() {
+        _message_label->set_text("Dodge the\nCreeps");
+        _message_label->show();
+        _start_button_timer->start();
+    }
+
+    void HUD::_on_GetReadyMessageTimer_timeout() {
+        _message_label->hide();
+    }
+
+Connecting HUD to Main
+~~~~~~~~~~~~~~~~~~~~~~
+
+Now that we're done creating the ``HUD`` scene, go back to ``Main``. Instance
+the ``HUD`` scene in ``Main`` like you did the ``Player`` scene. The scene tree
+should look like this, so make sure you didn't miss anything:
+
+.. image:: img/completed_main_scene.png
+
+Now we need to connect the ``HUD`` functionality to our ``Main`` script. This
+requires a few additions to the ``Main`` scene:
+
+In the Node tab, connect the HUD's ``start_game`` signal to the ``new_game()``
+function of the Main node by typing "new_game" in the "Receiver Method" in the
+"Connect a Signal" window. Verify that the green connection icon now appears
+next to ``func new_game()`` in the script.
+
+In ``new_game()``, update the score display and show the "Get Ready" message:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        $HUD.update_score(score)
+        $HUD.show_message("Get Ready")
+
+ .. code-tab:: csharp
+
+        var hud = GetNode<HUD>("HUD");
+        hud.UpdateScore(Score);
+        hud.ShowMessage("Get Ready!");
+
+ .. code-tab:: cpp
+
+        _hud->update_score(score);
+        _hud->show_get_ready();
+
+In ``game_over()`` we need to call the corresponding ``HUD`` function:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        $HUD.show_game_over()
+
+ .. code-tab:: csharp
+
+        GetNode<HUD>("HUD").ShowGameOver();
+
+ .. code-tab:: cpp
+
+        _hud->show_game_over();
+
+Finally, add this to ``_on_ScoreTimer_timeout()`` to keep the display in sync
+with the changing score:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        $HUD.update_score(score)
+
+ .. code-tab:: csharp
+
+        GetNode<HUD>("HUD").UpdateScore(Score);
+
+ .. code-tab:: cpp
+
+        _hud->update_score(score);
+
+Now you're ready to play! Click the "Play the Project" button. You will be asked
+to select a main scene, so choose ``Main.tscn``.
+
+Removing old creeps
+~~~~~~~~~~~~~~~~~~~
+
+If you play until "Game Over" and then start a new game right away, the creeps
+from the previous game may still be on the screen. It would be better if they
+all disappeared at the start of a new game. We just need a way to tell *all* the
+mobs to remove themselves. We can do this with the "group" feature.
+
+In the ``Mob`` scene, select the root node and click the "Node" tab next to the
+Inspector (the same place where you find the node's signals). Next to "Signals",
+click "Groups" and you can type a new group name and click "Add".
+
+.. image:: img/group_tab.png
+
+Now all mobs will be in the "mobs" group. We can then add the following line to
+the ``new_game()`` function in ``Main``:
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+        get_tree().call_group("mobs", "queue_free")
+
+ .. code-tab:: csharp
+
+        // Note that for calling Godot-provided methods with strings,
+        // we have to use the original Godot snake_case name.
+        GetTree().CallGroup("mobs", "queue_free");
+
+ .. code-tab:: cpp
+
+        get_tree()->call_group("mobs", "queue_free");
+
+The ``call_group()`` function calls the named function on every node in a
+group - in this case we are telling every mob to delete itself.
+
+The game's mostly done at this point. In the next and last part, we'll polish it
+a bit by adding a background, looping music, and some keyboard shortcuts.

+ 72 - 0
getting_started/first_2d_game/07.finishing-up.rst

@@ -0,0 +1,72 @@
+.. _doc_your_first_2d_game_finishing_up:
+
+Finishing up
+============
+
+We have now completed all the functionality for our game. Below are some
+remaining steps to add a bit more "juice" to improve the game experience.
+
+Feel free to expand the gameplay with your own ideas.
+
+Background
+~~~~~~~~~~
+
+The default gray background is not very appealing, so let's change its color.
+One way to do this is to use a :ref:`ColorRect <class_ColorRect>` node. Make it
+the first node under ``Main`` so that it will be drawn behind the other nodes.
+``ColorRect`` only has one property: ``Color``. Choose a color you like and
+select "Layout" -> "Full Rect" so that it covers the screen.
+
+You could also add a background image, if you have one, by using a
+``TextureRect`` node instead.
+
+Sound effects
+~~~~~~~~~~~~~
+
+Sound and music can be the single most effective way to add appeal to the game
+experience. In your game assets folder, you have two sound files: "House In a
+Forest Loop.ogg" for background music, and "gameover.wav" for when the player
+loses.
+
+Add two :ref:`AudioStreamPlayer <class_AudioStreamPlayer>` nodes as children of
+``Main``. Name one of them ``Music`` and the other ``DeathSound``. On each one,
+click on the ``Stream`` property, select "Load", and choose the corresponding
+audio file.
+
+To play the music, add ``$Music.play()`` in the ``new_game()`` function and
+``$Music.stop()`` in the ``game_over()`` function.
+
+Finally, add ``$DeathSound.play()`` in the ``game_over()`` function.
+
+Keyboard shortcut
+~~~~~~~~~~~~~~~~~
+
+Since the game is played with keyboard controls, it would be convenient if we
+could also start the game by pressing a key on the keyboard. We can do this with
+the "Shortcut" property of the ``Button`` node.
+
+In the ``HUD`` scene, select the ``StartButton`` and find its *Shortcut*
+property in the Inspector. Select "New Shortcut" and click on the "Shortcut"
+item. A second *Shortcut* property will appear. Select "New InputEventAction"
+and click the new "InputEventAction". Finally, in the *Action* property, type
+the name ``ui_select``. This is the default input event associated with the
+spacebar.
+
+.. image:: img/start_button_shortcut.png
+
+Now when the start button appears, you can either click it or press :kbd:`Space`
+to start the game.
+
+And with that, you completed your first 2D game in Godot.
+
+.. image:: img/dodge_preview.gif
+
+You got to make a player-controlled character, enemies that spawn randomly
+around the game board, count the score, implement a game over and replay, user
+interface, sounds, and more. Congratulations!
+
+There's still much to learn, but you can take a moment to appreciate what you
+achieved.
+
+And when you're ready, you can move on to :ref:`doc_your_first_3d_game` to learn
+to create a complete 3D game from scratch, in Godot.

+ 0 - 0
getting_started/step_by_step/files/dodge_assets.zip → getting_started/first_2d_game/files/dodge_assets.zip


BIN
getting_started/first_2d_game/files/dodge_assets_with_gdnative.zip


+ 0 - 0
getting_started/step_by_step/img/add_node.png → getting_started/first_2d_game/img/add_node.png


BIN
getting_started/first_2d_game/img/add_script_button.png


+ 0 - 0
getting_started/step_by_step/img/attach_node_window.png → getting_started/first_2d_game/img/attach_node_window.png


BIN
getting_started/first_2d_game/img/completed_main_scene.png


BIN
getting_started/first_2d_game/img/custom_font1.png


BIN
getting_started/first_2d_game/img/custom_font2.png


BIN
getting_started/first_2d_game/img/custom_font3.png


+ 0 - 0
getting_started/step_by_step/img/dodge_preview.gif → getting_started/first_2d_game/img/dodge_preview.gif


+ 0 - 0
getting_started/step_by_step/img/draw_path2d.gif → getting_started/first_2d_game/img/draw_path2d.gif


BIN
getting_started/first_2d_game/img/export_variable.png


+ 0 - 0
getting_started/step_by_step/img/filesystem_dock.png → getting_started/first_2d_game/img/filesystem_dock.png


BIN
getting_started/first_2d_game/img/folder-content.png


+ 0 - 0
getting_started/step_by_step/img/grid_snap_button.png → getting_started/first_2d_game/img/grid_snap_button.png


+ 0 - 0
getting_started/step_by_step/img/group_tab.png → getting_started/first_2d_game/img/group_tab.png


BIN
getting_started/first_2d_game/img/instance_scene.png


+ 0 - 0
getting_started/step_by_step/img/lock_children.png → getting_started/first_2d_game/img/lock_children.png


+ 0 - 0
getting_started/step_by_step/img/main_scene_nodes.png → getting_started/first_2d_game/img/main_scene_nodes.png


+ 0 - 0
getting_started/step_by_step/img/mob_animations.gif → getting_started/first_2d_game/img/mob_animations.gif


BIN
getting_started/first_2d_game/img/new-project-button.png


BIN
getting_started/first_2d_game/img/path2d_buttons.png


BIN
getting_started/first_2d_game/img/player_coll_shape.png


BIN
getting_started/first_2d_game/img/player_scale.png


BIN
getting_started/first_2d_game/img/player_scene_nodes.png


+ 0 - 0
getting_started/step_by_step/img/player_signal_connection.png → getting_started/first_2d_game/img/player_signal_connection.png


+ 0 - 0
getting_started/step_by_step/img/player_signals.png → getting_started/first_2d_game/img/player_signals.png


BIN
getting_started/first_2d_game/img/set_collision_mask.png


BIN
getting_started/first_2d_game/img/setting-project-width-and-height.png


BIN
getting_started/first_2d_game/img/setting-stretch-mode.png


+ 0 - 0
getting_started/step_by_step/img/spriteframes_panel.png → getting_started/first_2d_game/img/spriteframes_panel.png


+ 0 - 0
getting_started/step_by_step/img/spriteframes_panel2.png → getting_started/first_2d_game/img/spriteframes_panel2.png


BIN
getting_started/first_2d_game/img/start_button_shortcut.png


BIN
getting_started/first_2d_game/img/ui_anchor.png


+ 7 - 1
getting_started/first_2d_game/index.rst

@@ -66,6 +66,12 @@ Contents
    :maxdepth: 1
    :name: toc-learn-introduction
 
-   your_first_game.rst
+   01.project_setup
+   02.player_scene
+   03.coding_the_player
+   04.creating_the_enemy
+   05.the_main_game_scene
+   06.heads_up_display
+   07.finishing-up
 
 .. |image0| image:: img/dodge_preview.gif

+ 164 - 0
getting_started/first_3d_game/01.game_setup.rst

@@ -0,0 +1,164 @@
+.. _doc_first_3d_game_game_area:
+
+Setting up the game area
+========================
+
+In this first part, we're going to set up the game area. Let's get started by
+importing the start assets and setting up the game scene.
+
+We've prepared a Godot project with the 3D models and sounds we'll use for this
+tutorial, linked in the index page. If you haven't done so yet, you can download
+the archive here: `Squash the Creeps assets
+<https://github.com/GDQuest/godot-3d-dodge-the-creeps/releases/tag/1.0.0>`__.
+
+Once you downloaded it, extract the .zip archive on your computer. Open the
+Godot project manager and click the *Import* button.
+
+|image1|
+
+In the import popup, enter the full path to the freshly created directory
+``squash_the_creeps_start/``. You can click the *Browse* button on the right to
+open a file browser and navigate to the ``project.godot`` file the folder
+contains.
+
+|image2|
+
+Click *Import & Edit* to open the project in the editor.
+
+|image3|
+
+The start project contains an icon and two folders: ``art/`` and ``fonts/``.
+There, you will find the art assets and music we'll use in the game.
+
+|image4|
+
+There are two 3D models, ``player.glb`` and ``mob.glb``, some materials that
+belong to these models, and a music track.
+
+Setting up the playable area
+----------------------------
+
+We're going to create our main scene with a plain *Node* as its root. In the
+*Scene* dock, click the *Add Node* button represented by a "+" icon in the
+top-left and double-click on *Node*. Name the node "Main". Alternatively, to add
+a node to the scene, you can press :kbd:`Ctrl + a` (or :kbd:`Cmd + a` on MacOS).
+
+|image5|
+
+Save the scene as ``Main.tscn`` by pressing :kbd:`Ctrl + s` (:kbd:`Cmd + s` on MacOS).
+
+We'll start by adding a floor that'll prevent the characters from falling. To
+create static colliders like the floor, walls, or ceilings, you can use
+*StaticBody* nodes. They require *CollisionShape* child nodes to
+define the collision area. With the *Main* node selected, add a *StaticBody*
+node, then a *CollisionShape*. Rename the *StaticBody* as *Ground*.
+
+|image6|
+
+A warning sign next to the *CollisionShape* appears because we haven't defined
+its shape. If you click the icon, a popup appears to give you more information.
+
+|image7|
+
+To create a shape, with the *CollisionShape* selected, head to the *Inspector*
+and click the *[empty]* field next to the *Shape* property. Create a new *Box
+Shape*.
+
+|image8|
+
+The box shape is perfect for flat ground and walls. Its thickness makes it
+reliable to block even fast-moving objects.
+
+A box's wireframe appears in the viewport with three orange dots. You can click
+and drag these to edit the shape's extents interactively. We can also precisely
+set the size in the inspector. Click on the *BoxShape* to expand the resource.
+Set its *Extents* to ``30`` on the X axis, ``1`` for the Y axis, and ``30`` for
+the Z axis.
+
+|image9|
+
+.. note::
+
+    In 3D, translation and size units are in meters. The box's total size is
+    twice its extents: ``60`` by ``60`` meters on the ground plane and ``2``
+    units tall. The ground plane is defined by the X and Z axes, while the Y
+    axis represents the height.
+
+Collision shapes are invisible. We need to add a visual floor that goes along
+with it. Select the *Ground* node and add a *MeshInstance* as its child.
+
+|image10|
+
+In the *Inspector*, click on the field next to *Mesh* and create a *CubeMesh*
+resource to create a visible cube.
+
+|image11|
+
+Once again, it's too small by default. Click the cube icon to expand the
+resource and set its *Size* to ``60``, ``2``, and ``60``. As the cube
+resource works with a size rather than extents, we need to use these values so
+it matches our collision shape.
+
+|image12|
+
+You should see a wide grey slab that covers the grid and blue and red axes in
+the viewport.
+
+We're going to move the ground down so we can see the floor grid. Select the
+*Ground* node, hold the :kbd:`Ctrl` key down to turn on grid snapping (:kbd:`Cmd` on MacOS),
+and click and drag down on the Y axis. It's the green arrow in the move gizmo.
+
+|image13|
+
+.. note::
+
+    If you can't see the 3D object manipulator like on the image above, ensure
+    the *Select Mode* is active in the toolbar above the view.
+
+|image14|
+
+Move the ground down ``1`` meter. A label in the bottom-left corner of the
+viewport tells you how much you're translating the node.
+
+|image15|
+
+.. note::
+
+    Moving the *Ground* node down moves both children along with it.
+    Ensure you move the *Ground* node, **not** the *MeshInstance* or the
+    *CollisionShape*.
+
+Let's add a directional light so our scene isn't all grey. Select the *Main*
+node and add a *DirectionalLight* as a child of it. We need to move it and
+rotate it. Move it up by clicking and dragging on the manipulator's green arrow
+and click and drag on the red arc to rotate it around the X axis, until the
+ground is lit.
+
+In the *Inspector*, turn on *Shadow -> Enabled* by clicking the checkbox.
+
+|image16|
+
+At this point, your project should look like this.
+
+|image17|
+
+That's our starting point. In the next part, we will work on the player scene
+and base movement.
+
+.. |image1| image:: img/01.game_setup/01.import_button.png
+.. |image2| image:: img/01.game_setup/02.browse_to_project_folder.png
+.. |image3| image:: img/01.game_setup/03.import_and_edit.png
+.. |image4| image:: img/01.game_setup/04.start_assets.png
+.. |image5| image:: img/01.game_setup/05.main_node.png
+.. |image6| image:: img/01.game_setup/06.staticbody_node.png
+.. |image7| image:: img/01.game_setup/07.collision_shape_warning.png
+.. |image8| image:: img/01.game_setup/08.create_box_shape.png
+.. |image9| image:: img/01.game_setup/09.box_extents.png
+.. |image10| image:: img/01.game_setup/10.mesh_instance.png
+.. |image11| image:: img/01.game_setup/11.cube_mesh.png
+.. |image12| image:: img/01.game_setup/12.cube_resized.png
+.. |image13| image:: img/01.game_setup/13.move_gizmo_y_axis.png
+.. |image14| image:: img/01.game_setup/14.select_mode_icon.png
+.. |image15| image:: img/01.game_setup/15.translation_amount.png
+.. |image16| image:: img/01.game_setup/16.turn_on_shadows.png
+.. |image17| image:: img/01.game_setup/17.project_with_light.png

+ 177 - 0
getting_started/first_3d_game/02.player_input.rst

@@ -0,0 +1,177 @@
+.. _doc_first_3d_game_player_scene_and_input:
+
+Player scene and input actions
+==============================
+
+In the next two lessons, we will design the player scene, register custom input
+actions, and code player movement. By the end, you'll have a playable character
+that moves in eight directions.
+
+.. TODO: add player animated gif?
+.. player_movement.gif
+
+Create a new scene by going to the Scene menu in the top-left and clicking *New
+Scene*. Create a *KinematicBody* node as the root and name it *Player*.
+
+|image0|
+
+Kinematic bodies are complementary to the area and rigid bodies used in the 2D
+game tutorial. Like rigid bodies, they can move and collide with the
+environment, but instead of being controlled by the physics engine, you dictate
+their movement. You will see how we use the node's unique features when we code
+the jump and squash mechanics.
+
+.. seealso::
+
+    To learn more about the different physics node types, see the
+    :ref:`doc_physics_introduction`.
+
+For now, we're going to create a basic rig for our character's 3D model. This
+will allow us to rotate the model later via code while it plays an animation.
+
+Add a *Spatial* node as a child of *Player* and name it *Pivot*. Then, in the
+FileSystem dock, expand the ``art/`` folder by double-clicking it and drag and
+drop ``player.glb`` onto the *Pivot* node.
+
+|image1|
+
+This should instantiate the model as a child of *Pivot*. You can rename it to
+*Character*.
+
+|image2|
+
+.. note::
+
+    The ``.glb`` files contain 3D scene data based on the open-source GLTF 2.0
+    specification. They're a modern and powerful alternative to a proprietary format
+    like FBX, which Godot also supports. To produce these files, we designed the
+    model in `Blender 3D <https://www.blender.org/>`__ and exported it to GLTF.
+
+As with all kinds of physics nodes, we need a collision shape for our character
+to collide with the environment. Select the *Player* node again and add a
+*CollisionShape*. In the *Inspector*, assign a *SphereShape* to the *Shape*
+property. The sphere's wireframe appears below the character.
+
+|image3|
+
+It will be the shape the physics engine uses to collide with the environment, so
+we want it to better fit the 3D model. Shrink it a bit by dragging the orange
+dot in the viewport. My sphere has a radius of about ``0.8`` meters.
+
+Then, move the shape up so its bottom roughly aligns with the grid's plane.
+
+|image4|
+
+You can toggle the model's visibility by clicking the eye icon next to the
+*Character* or the *Pivot* nodes.
+
+|image5|
+
+Save the scene as ``Player.tscn``.
+
+With the nodes ready, we can almost get coding. But first, we need to define
+some input actions.
+
+Creating input actions
+----------------------
+
+To move the character, we will listen to the player's input, like pressing the
+arrow keys. In Godot, while we could write all the key bindings in code, there's
+a powerful system that allows you to assign a label to a set of keys and
+buttons. This simplifies our scripts and makes them more readable.
+
+This system is the Input Map. To access its editor, head to the *Project* menu
+and select *Project Settings…*.
+
+|image6|
+
+At the top, there are multiple tabs. Click on *Input Map*. This window allows
+you to add new actions at the top; they are your labels. In the bottom part, you
+can bind keys to these actions.
+
+|image7|
+
+Godot projects come with some predefined actions designed for user interface
+design, which we could use here. But we're defining our own to support gamepads.
+
+We're going to name our actions ``move_left``, ``move_right``, ``move_forward``,
+``move_back``, and ``jump``.
+
+To add an action, write its name in the bar at the top and press Enter.
+
+|image8|
+
+Create the five actions. Your window should have them all listed at the bottom.
+
+|image9|
+
+To bind a key or button to an action, click the "+" button to its right. Do this
+for ``move_left`` and in the drop-down menu, click *Key*.
+
+|image10|
+
+This option allows you to add a keyboard input. A popup appears and waits for
+you to press a key. Press the left arrow key and click *OK*.
+
+|image11|
+
+Do the same for the A key.
+
+|image12|
+
+Let's now add support for a gamepad's left joystick. Click the "+" button again
+but this time, select *Joy Axis*.
+
+|image13|
+
+The popup gives you two drop-down menus. On the left, you can select a gamepad
+by index. *Device 0* corresponds to the first plugged gamepad, *Device 1*
+corresponds to the second, and so on. You can select the joystick and direction
+you want to bind to the input action on the right. Leave the default values and
+press the *Add* button.
+
+|image14|
+
+Do the same for the other input actions. For example, bind the right arrow, D,
+and the left joystick's right axis to ``move_right``. After binding all keys,
+your interface should look like this.
+
+|image15|
+
+We have the ``jump`` action left to set up. Bind the Space key and the gamepad's
+A button. To bind a gamepad's button, select the *Joy Button* option in the menu.
+
+|image16|
+
+Leave the default values and click the *Add* button.
+
+|image17|
+
+Your jump input action should look like this.
+
+|image18|
+
+That's all the actions we need for this game. You can use this menu to label any
+groups of keys and buttons in your projects.
+
+In the next part, we'll code and test the player's movement.
+
+.. |image0| image:: img/02.player_input/01.new_scene.png
+.. |image1| image:: img/02.player_input/02.instantiating_the_model.png
+.. |image2| image:: img/02.player_input/03.scene_structure.png
+.. |image3| image:: img/02.player_input/04.sphere_shape.png
+.. |image4| image:: img/02.player_input/05.moving_the_sphere_up.png
+.. |image5| image:: img/02.player_input/06.toggling_visibility.png
+.. |image6| image:: img/02.player_input/07.project_settings.png
+.. |image7| image:: img/02.player_input/07.input_map_tab.png
+.. |image8| image:: img/02.player_input/07.adding_action.png
+.. |image9| image:: img/02.player_input/08.actions_list_empty.png
+.. |image10| image:: img/02.player_input/08.create_key_action.png
+.. |image11| image:: img/02.player_input/09.keyboard_key_popup.png
+.. |image12| image:: img/02.player_input/09.keyboard_keys.png
+.. |image13| image:: img/02.player_input/10.joy_axis_option.png
+.. |image14| image:: img/02.player_input/11.joy_axis_popup.png
+.. |image15| image:: img/02.player_input/12.move_inputs_mapped.png
+.. |image16| image:: img/02.player_input/13.joy_button_option.png
+.. |image17| image:: img/02.player_input/14.add_jump_button.png
+.. |image18| image:: img/02.player_input/14.jump_input_action.png

+ 413 - 0
getting_started/first_3d_game/03.player_movement_code.rst

@@ -0,0 +1,413 @@
+.. _doc_first_3d_game_player_movement:
+
+Moving the player with code
+===========================
+
+It's time to code! We're going to use the input actions we created in the last
+part to move the character.
+
+Right-click the *Player* node and select *Attach Script* to add a new script to
+it. In the popup, set the *Template* to *Empty* before pressing the *Create*
+button.
+
+|image0|
+
+Let's start with the class's properties. We're going to define a movement speed,
+a fall acceleration representing gravity, and a velocity we'll use to move the
+character.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # How fast the player moves in meters per second.
+   export var speed = 14
+   # The downward acceleration when in the air, in meters per second squared.
+   export var fall_acceleration = 75
+
+   var velocity = Vector3.ZERO
+
+ .. code-tab:: csharp
+
+    public class Player : KinematicBody
+    {
+        // Don't forget to rebuild the project so the editor knows about the new export variable.
+
+        // How fast the player moves in meters per second.
+        [Export]
+        public int Speed = 14;
+        // The downward acceleration when in the air, in meters per second squared.
+        [Export]
+        public int FallAcceleration = 75;
+
+        private Vector3 _velocity = Vector3.Zero;
+    }
+
+
+These are common properties for a moving body. The ``velocity`` is a 3D vector
+combining a speed with a direction. Here, we define it as a property because
+we want to update and reuse its value across frames.
+
+.. note::
+
+    The values are quite different from 2D code because distances are in meters.
+    While in 2D, a thousand units (pixels) may only correspond to half of your
+    screen's width, in 3D, it's a kilometer.
+
+Let's code the movement now. We start by calculating the input direction vector
+using the global ``Input`` object, in ``_physics_process()``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _physics_process(delta):
+       # We create a local variable to store the input direction.
+       var direction = Vector3.ZERO
+
+       # We check for each move input and update the direction accordingly.
+       if Input.is_action_pressed("move_right"):
+           direction.x += 1
+       if Input.is_action_pressed("move_left"):
+           direction.x -= 1
+       if Input.is_action_pressed("move_back"):
+           # Notice how we are working with the vector's x and z axes.
+           # In 3D, the XZ plane is the ground plane.
+           direction.z += 1
+       if Input.is_action_pressed("move_forward"):
+           direction.z -= 1
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // We create a local variable to store the input direction.
+        var direction = Vector3.Zero;
+
+        // We check for each move input and update the direction accordingly
+        if (Input.IsActionPressed("move_right"))
+        {
+            direction.x += 1f;
+        }
+        if (Input.IsActionPressed("move_left"))
+        {
+            direction.x -= 1f;
+        }
+        if (Input.IsActionPressed("move_back"))
+        {
+            // Notice how we are working with the vector's x and z axes.
+            // In 3D, the XZ plane is the ground plane.
+            direction.z += 1f;
+        }
+        if (Input.IsActionPressed("move_forward"))
+        {
+            direction.z -= 1f;
+        }
+    }
+
+Here, we're going to make all calculations using the ``_physics_process()``
+virtual function. Like ``_process()``, it allows you to update the node every
+frame, but it's designed specifically for physics-related code like moving a
+kinematic or rigid body.
+
+.. seealso::
+
+    To learn more about the difference between ``_process()`` and
+    ``_physics_process()``, see :ref:`doc_idle_and_physics_processing`.
+
+We start by initializing a ``direction`` variable to ``Vector3.ZERO``. Then, we
+check if the player is pressing one or more of the ``move_*`` inputs and update
+the vector's ``x`` and ``z`` components accordingly. These correspond to the
+ground plane's axes.
+
+These four conditions give us eight possibilities and eight possible directions.
+
+In case the player presses, say, both W and D simultaneously, the vector will
+have a length of about ``1.4``. But if they press a single key, it will have a
+length of ``1``. We want the vector's length to be consistent. To do so, we can
+call its ``normalize()`` method.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   #func _physics_process(delta):
+       #...
+
+       if direction != Vector3.ZERO:
+           direction = direction.normalized()
+           $Pivot.look_at(translation + direction, Vector3.UP)
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // ...
+
+        if (direction != Vector3.Zero)
+        {
+            direction = direction.Normalized();
+            GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
+        }
+    }
+
+Here, we only normalize the vector if the direction has a length greater than
+zero, which means the player is pressing a direction key.
+
+In this case, we also get the *Pivot* node and call its ``look_at()`` method.
+This method takes a position in space to look at in global coordinates and the
+up direction. In this case, we can use the ``Vector3.UP`` constant.
+
+.. note::
+
+    A node's local coordinates, like ``translation``, are relative to their
+    parent. Global coordinates are relative to the world's main axes you can see
+    in the viewport instead.
+
+In 3D, the property that contains a node's position is ``translation``. By
+adding the ``direction`` to it, we get a position to look at that's one meter
+away from the *Player*.
+
+Then, we update the velocity. We have to calculate the ground velocity and the
+fall speed separately. Be sure to go back one tab so the lines are inside the
+``_physics_process()`` function but outside the condition we just wrote.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+    func _physics_process(delta):
+        #...
+        if direction != Vector3.ZERO:
+            #...
+
+        # Ground velocity
+        velocity.x = direction.x * speed
+        velocity.z = direction.z * speed
+        # Vertical velocity
+        velocity.y -= fall_acceleration * delta
+        # Moving the character
+        velocity = move_and_slide(velocity, Vector3.UP)
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // ...
+
+        // Ground velocity
+        _velocity.x = direction.x * Speed;
+        _velocity.z = direction.z * Speed;
+        // Vertical velocity
+        _velocity.y -= FallAcceleration * delta;
+        // Moving the character
+        _velocity = MoveAndSlide(_velocity, Vector3.Up);
+    }
+
+For the vertical velocity, we subtract the fall acceleration multiplied by the
+delta time every frame. Notice the use of the ``-=`` operator, which is a
+shorthand for ``variable = variable - ...``.
+
+This line of code will cause our character to fall in every frame. This may seem
+strange if it's already on the floor. But we have to do this for the character
+to collide with the ground every frame.
+
+The physics engine can only detect interactions with walls, the floor, or other
+bodies during a given frame if movement and collisions happen. We will use this
+property later to code the jump.
+
+On the last line, we call ``KinematicBody.move_and_slide()``. It's a powerful
+method of the ``KinematicBody`` class that allows you to move a character
+smoothly. If it hits a wall midway through a motion, the engine will try to
+smooth it out for you.
+
+The function takes two parameters: our velocity and the up direction. It moves
+the character and returns a leftover velocity after applying collisions. When
+hitting the floor or a wall, the function will reduce or reset the speed in that
+direction from you. In our case, storing the function's returned value prevents
+the character from accumulating vertical momentum, which could otherwise get so
+big the character would move through the ground slab after a while.
+
+And that's all the code you need to move the character on the floor.
+
+Here is the complete ``Player.gd`` code for reference.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # How fast the player moves in meters per second.
+   export var speed = 14
+   # The downward acceleration when in the air, in meters per second squared.
+   export var fall_acceleration = 75
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(delta):
+       var direction = Vector3.ZERO
+
+       if Input.is_action_pressed("move_right"):
+           direction.x += 1
+       if Input.is_action_pressed("move_left"):
+           direction.x -= 1
+       if Input.is_action_pressed("move_back"):
+           direction.z += 1
+       if Input.is_action_pressed("move_forward"):
+           direction.z -= 1
+
+       if direction != Vector3.ZERO:
+           direction = direction.normalized()
+           $Pivot.look_at(translation + direction, Vector3.UP)
+
+       velocity.x = direction.x * speed
+       velocity.z = direction.z * speed
+       velocity.y -= fall_acceleration * delta
+       velocity = move_and_slide(velocity, Vector3.UP)
+
+ .. code-tab:: csharp
+
+    public class Player : KinematicBody
+    {
+        // How fast the player moves in meters per second.
+        [Export]
+        public int Speed = 14;
+        // The downward acceleration when in the air, in meters per second squared.
+        [Export]
+        public int FallAcceleration = 75;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            // We create a local variable to store the input direction.
+            var direction = Vector3.Zero;
+
+            // We check for each move input and update the direction accordingly
+            if (Input.IsActionPressed("move_right"))
+            {
+                direction.x += 1f;
+            }
+            if (Input.IsActionPressed("move_left"))
+            {
+                direction.x -= 1f;
+            }
+            if (Input.IsActionPressed("move_back"))
+            {
+                // Notice how we are working with the vector's x and z axes.
+                // In 3D, the XZ plane is the ground plane.
+                direction.z += 1f;
+            }
+            if (Input.IsActionPressed("move_forward"))
+            {
+                direction.z -= 1f;
+            }
+
+            if (direction != Vector3.Zero)
+            {
+                direction = direction.Normalized();
+                GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
+            }
+
+            // Ground velocity
+            _velocity.x = direction.x * Speed;
+            _velocity.z = direction.z * Speed;
+            // Vertical velocity
+            _velocity.y -= FallAcceleration * delta;
+            // Moving the character
+            _velocity = MoveAndSlide(_velocity, Vector3.Up);
+        }
+    }
+
+Testing our player's movement
+-----------------------------
+
+We're going to put our player in the *Main* scene to test it. To do so, we need
+to instantiate the player and then add a camera. Unlike in 2D, in 3D, you won't
+see anything if your viewport doesn't have a camera pointing at something.
+
+Save your *Player* scene and open the *Main* scene. You can click on the *Main*
+tab at the top of the editor to do so.
+
+|image1|
+
+If you closed the scene before, head to the *FileSystem* dock and double-click
+``Main.tscn`` to re-open it.
+
+To instantiate the *Player*, right-click on the *Main* node and select *Instance
+Child Scene*.
+
+|image2|
+
+In the popup, double-click *Player.tscn*. The character should appear in the
+center of the viewport.
+
+Adding a camera
+~~~~~~~~~~~~~~~
+
+Let's add the camera next. Like we did with our *Player*\ 's *Pivot*, we're
+going to create a basic rig. Right-click on the *Main* node again and select
+*Add Child Node* this time. Create a new *Position3D*, name it *CameraPivot*,
+and add a *Camera* node as a child of it. Your scene tree should look like this.
+
+|image3|
+
+Notice the *Preview* checkbox that appears in the top-left when you have the
+*Camera* selected. You can click it to preview the in-game camera projection.
+
+|image4|
+
+We're going to use the *Pivot* to rotate the camera as if it was on a crane.
+Let's first split the 3D view to be able to freely navigate the scene and see
+what the camera sees.
+
+In the toolbar right above the viewport, click on *View*, then *2 Viewports*.
+You can also press :kbd:`Ctrl + 2` (:kbd:`Cmd + 2` on MacOS).
+
+|image5|
+
+On the bottom view, select the *Camera* and turn on camera preview by clicking
+the checkbox.
+
+|image6|
+
+In the top view, move the camera about ``19`` units on the Z axis (the blue
+one).
+
+|image7|
+
+Here's where the magic happens. Select the *CameraPivot* and rotate it ``45``
+degrees around the X axis (using the red circle). You'll see the camera move as
+if it was attached to a crane.
+
+|image8|
+
+You can run the scene by pressing :kbd:`F6` and press the arrow keys to move the
+character.
+
+|image9|
+
+We can see some empty space around the character due to the perspective
+projection. In this game, we're going to use an orthographic projection instead
+to better frame the gameplay area and make it easier for the player to read
+distances.
+
+Select the *Camera* again and in the *Inspector*, set the *Projection* to
+*Orthogonal* and the *Size* to ``19``. The character should now look flatter and
+the ground should fill the background.
+
+|image10|
+
+With that, we have both player movement and the view in place. Next, we will
+work on the monsters.
+
+.. |image0| image:: img/03.player_movement_code/01.attach_script_to_player.png
+.. |image1| image:: img/03.player_movement_code/02.clicking_main_tab.png
+.. |image2| image:: img/03.player_movement_code/03.instance_child_scene.png
+.. |image3| image:: img/03.player_movement_code/04.scene_tree_with_camera.png
+.. |image4| image:: img/03.player_movement_code/05.camera_preview_checkbox.png
+.. |image5| image:: img/03.player_movement_code/06.two_viewports.png
+.. |image6| image:: img/03.player_movement_code/07.camera_preview_checkbox.png
+.. |image7| image:: img/03.player_movement_code/08.camera_moved.png
+.. |image8| image:: img/03.player_movement_code/09.camera_rotated.png
+.. |image9| image:: img/03.player_movement_code/10.camera_perspective.png
+.. |image10| image:: img/03.player_movement_code/11.camera_orthographic.png

+ 332 - 0
getting_started/first_3d_game/04.mob_scene.rst

@@ -0,0 +1,332 @@
+.. _doc_first_3d_game_designing_the_mob_scene:
+
+Designing the mob scene
+=======================
+
+In this part, you're going to code the monsters, which we'll call mobs. In the
+next lesson, we'll spawn them randomly around the playable area.
+
+Let's design the monsters themselves in a new scene. The node structure is going
+to be similar to the *Player* scene.
+
+Create a scene with, once again, a *KinematicBody* node as its root. Name it
+*Mob*. Add a *Spatial* node as a child of it, name it *Pivot*. And drag and drop
+the file ``mob.glb`` from the *FileSystem* dock onto the *Pivot* to add the
+monster's 3D model to the scene. You can rename the newly created *mob* node
+into *Character*.
+
+|image0|
+
+We need a collision shape for our body to work. Right-click on the *Mob* node,
+the scene's root, and click *Add Child Node*.
+
+|image1|
+
+Add a *CollisionShape*.
+
+|image2|
+
+In the *Inspector*, assign a *BoxShape* to the *Shape* property.
+
+|image3|
+
+We should change its size to fit the 3D model better. You can do so
+interactively by clicking and dragging on the orange dots.
+
+The box should touch the floor and be a little thinner than the model. Physics
+engines work in such a way that if the player's sphere touches even the box's
+corner, a collision will occur. If the box is a little too big compared to the
+3D model, you may die at a distance from the monster, and the game will feel
+unfair to the players.
+
+|image4|
+
+Notice that my box is taller than the monster. It is okay in this game because
+we're looking at the scene from above and using a fixed perspective. Collision
+shapes don't have to match the model exactly. It's the way the game feels when
+you test it that should dictate their form and size.
+
+Removing monsters off-screen
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We're going to spawn monsters at regular time intervals in the game level. If
+we're not careful, their count could increase to infinity, and we don't want
+that. Each mob instance has both a memory and a processing cost, and we don't
+want to pay for it when the mob's outside the screen.
+
+Once a monster leaves the screen, we don't need it anymore, so we can delete it.
+Godot has a node that detects when objects leave the screen,
+*VisibilityNotifier*, and we're going to use it to destroy our mobs.
+
+.. note::
+
+    When you keep instancing an object in games, there's a technique you can
+    use to avoid the cost of creating and destroying instances all the time
+    called pooling. It consists of pre-creating an array of objects and reusing
+    them over and over.
+
+    When working with GDScript, you don't need to worry about this. The main
+    reason to use pools is to avoid freezes with garbage-collected languages
+    like C# or Lua. GDScript uses a different technique to manage memory,
+    reference counting, which doesn't have that caveat. You can learn more
+    about that here :ref:`doc_gdscript_basics_memory_management`.
+
+Select the *Mob* node and add a *VisibilityNotifier* as a child of it. Another
+box, pink this time, appears. When this box completely leaves the screen, the
+node will emit a signal.
+
+|image5|
+
+Resize it using the orange dots until it covers the entire 3D model.
+
+|image6|
+
+Coding the mob's movement
+-------------------------
+
+Let's implement the monster's motion. We're going to do this in two steps.
+First, we'll write a script on the *Mob* that defines a function to initialize
+the monster. We'll then code the randomized spawn mechanism in the *Main* scene
+and call the function from there.
+
+Attach a script to the *Mob*.
+
+|image7|
+
+Here's the movement code to start with. We define two properties, ``min_speed``
+and ``max_speed``, to define a random speed range. We then define and initialize
+the ``velocity``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # Minimum speed of the mob in meters per second.
+   export var min_speed = 10
+   # Maximum speed of the mob in meters per second.
+   export var max_speed = 18
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(_delta):
+       move_and_slide(velocity)
+
+ .. code-tab:: csharp
+
+    public class Mob : KinematicBody
+    {
+        // Don't forget to rebuild the project so the editor knows about the new export variable.
+
+        // Minimum speed of the mob in meters per second
+        [Export]
+        public int MinSpeed = 10;
+        // Maximum speed of the mob in meters per second
+        [Export]
+        public int MaxSpeed = 18;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            MoveAndSlide(_velocity);
+        }
+    }
+
+Similarly to the player, we move the mob every frame by calling
+``KinematicBody``\ 's ``move_and_slide()`` method. This time, we don't update
+the ``velocity`` every frame: we want the monster to move at a constant speed
+and leave the screen, even if it were to hit an obstacle.
+
+We need to define another function to calculate the start velocity. This
+function will turn the monster towards the player and randomize both its angle
+of motion and its velocity.
+
+The function will take a ``start_position``, the mob's spawn position, and the
+``player_position`` as its arguments.
+
+We first position the mob at ``start_position``. Then, we turn it towards the
+player using the ``look_at()`` method and randomize the angle by rotating a
+random amount around the Y axis. Below, ``rand_range()`` outputs a random value
+between ``-PI / 4`` radians and ``PI / 4`` radians.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   # We will call this function from the Main scene.
+   func initialize(start_position, player_position):
+       translation = start_position
+       # We turn the mob so it looks at the player.
+       look_at(player_position, Vector3.UP)
+       # And rotate it randomly so it doesn't move exactly toward the player.
+       rotate_y(rand_range(-PI / 4, PI / 4))
+
+ .. code-tab:: csharp
+
+    // We will call this function from the Main scene
+    public void Initialize(Vector3 startPosition, Vector3 playerPosition)
+    {
+        Translation = startPosition;
+        // We turn the mob so it looks at the player.
+        LookAt(playerPosition, Vector3.Up);
+        // And rotate it randomly so it doesn't move exactly toward the player.
+        RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
+    }
+
+We then calculate a random speed using ``rand_range()`` once again and we use it
+to calculate the velocity.
+
+We start by creating a 3D vector pointing forward, multiply it by our
+``random_speed``, and finally rotate it using the ``Vector3`` class's
+``rotated()`` method.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func initialize(start_position, player_position):
+       # ...
+
+       # We calculate a random speed.
+       var random_speed = rand_range(min_speed, max_speed)
+       # We calculate a forward velocity that represents the speed.
+       velocity = Vector3.FORWARD * random_speed
+       # We then rotate the vector based on the mob's Y rotation to move in the direction it's looking.
+       velocity = velocity.rotated(Vector3.UP, rotation.y)
+
+ .. code-tab:: csharp
+
+    public void Initialize(Vector3 startPosition, Vector3 playerPosition)
+    {
+        // ...
+
+        // We calculate a random speed.
+        float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
+        // We calculate a forward velocity that represents the speed.
+        _velocity = Vector3.Forward * randomSpeed;
+        // We then rotate the vector based on the mob's Y rotation to move in the direction it's looking
+        _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+    }
+
+Leaving the screen
+------------------
+
+We still have to destroy the mobs when they leave the screen. To do so, we'll
+connect our *VisibilityNotifier* node's ``screen_exited`` signal to the *Mob*.
+
+Head back to the 3D viewport by clicking on the *3D* label at the top of the
+editor. You can also press :kbd:`Ctrl + F2` (:kbd:`Alt + 2` on macOS).
+
+|image8|
+
+Select the *VisibilityNotifier* node and on the right side of the interface,
+navigate to the *Node* dock. Double-click the *screen_exited()* signal.
+
+|image9|
+
+Connect the signal to the *Mob*.
+
+|image10|
+
+This will take you back to the script editor and add a new function for you,
+``_on_VisibilityNotifier_screen_exited()``. From it, call the ``queue_free()``
+method. This will destroy the mob instance when the *VisibilityNotifier* \'s box
+leaves the screen.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _on_VisibilityNotifier_screen_exited():
+       queue_free()
+
+ .. code-tab:: csharp
+
+    // We also specified this function name in PascalCase in the editor's connection window
+    public void OnVisibilityNotifierScreenExited()
+    {
+        QueueFree();
+    }
+
+
+Our monster is ready to enter the game! In the next part, you will spawn
+monsters in the game level.
+
+Here is the complete ``Mob.gd`` script for reference.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # Minimum speed of the mob in meters per second.
+   export var min_speed = 10
+   # Maximum speed of the mob in meters per second.
+   export var max_speed = 18
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(_delta):
+       move_and_slide(velocity)
+
+   func initialize(start_position, player_position):
+       translation = start_position
+       look_at(player_position, Vector3.UP)
+       rotate_y(rand_range(-PI / 4, PI / 4))
+
+       var random_speed = rand_range(min_speed, max_speed)
+       velocity = Vector3.FORWARD * random_speed
+       velocity = velocity.rotated(Vector3.UP, rotation.y)
+
+
+   func _on_VisibilityNotifier_screen_exited():
+       queue_free()
+
+ .. code-tab:: csharp
+
+    public class Mob : KinematicBody
+    {
+        // Minimum speed of the mob in meters per second
+        [Export]
+        public int MinSpeed = 10;
+        // Maximum speed of the mob in meters per second
+        [Export]
+        public int MaxSpeed = 18;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            MoveAndSlide(_velocity);
+        }
+
+        // We will call this function from the Main scene
+        public void Initialize(Vector3 startPosition, Vector3 playerPosition)
+        {
+            Translation = startPosition;
+            LookAt(playerPosition, Vector3.Up);
+            RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
+
+            var randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
+            _velocity = Vector3.Forward * randomSpeed;
+            _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+        }
+
+        // We also specified this function name in PascalCase in the editor's connection window
+        public void OnVisibilityNotifierScreenExited()
+        {
+            QueueFree();
+        }
+    }
+
+.. |image0| image:: img/04.mob_scene/01.initial_three_nodes.png
+.. |image1| image:: img/04.mob_scene/02.add_child_node.png
+.. |image2| image:: img/04.mob_scene/03.scene_with_collision_shape.png
+.. |image3| image:: img/04.mob_scene/04.create_box_shape.png
+.. |image4| image:: img/04.mob_scene/05.box_final_size.png
+.. |image5| image:: img/04.mob_scene/06.visibility_notifier.png
+.. |image6| image:: img/04.mob_scene/07.visibility_notifier_bbox_resized.png
+.. |image7| image:: img/04.mob_scene/08.mob_attach_script.png
+.. |image8| image:: img/04.mob_scene/09.switch_to_3d_workspace.png
+.. |image9| image:: img/04.mob_scene/10.node_dock.png
+.. |image10| image:: img/04.mob_scene/11.connect_signal.png

+ 360 - 0
getting_started/first_3d_game/05.spawning_mobs.rst

@@ -0,0 +1,360 @@
+.. _doc_first_3d_game_spawning_monsters:
+
+Spawning monsters
+=================
+
+In this part, we're going to spawn monsters along a path randomly. By the end,
+you will have monsters roaming the game board.
+
+|image0|
+
+Double-click on ``Main.tscn`` in the *FileSystem* dock to open the *Main* scene.
+
+Before drawing the path, we're going to change the game resolution. Our game has
+a default window size of ``1024x600``. We're going to set it to ``720x540``, a
+nice little box.
+
+Go to *Project -> Project Settings*.
+
+|image1|
+
+In the left menu, navigate down to *Display -> Window*. On the right, set the
+*Width* to ``720`` and the *Height* to ``540``.
+
+|image2|
+
+Creating the spawn path
+-----------------------
+
+Like you did in the 2D game tutorial, you're going to design a path and use a
+*PathFollow* node to sample random locations on it.
+
+In 3D though, it's a bit more complicated to draw the path. We want it to be
+around the game view so monsters appear right outside the screen. But if we draw
+a path, we won't see it from the camera preview.
+
+To find the view's limits, we can use some placeholder meshes. Your viewport
+should still be split into two parts, with the camera preview at the bottom. If
+that isn't the case, press :kbd:`Ctrl + 2` (:kbd:`Cmd + 2` on MacOS) to split the view into two.
+Select the *Camera* node and click the *Preview* checkbox in the bottom
+viewport.
+
+|image3|
+
+Adding placeholder cylinders
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's add the placeholder meshes. Add a new *Spatial* node as a child of the
+*Main* node and name it *Cylinders*. We'll use it to group the cylinders. As a
+child of it, add a *MeshInstance* node.
+
+|image4|
+
+In the *Inspector*, assign a *CylinderMesh* to the *Mesh* property.
+
+|image5|
+
+Set the top viewport to the top orthogonal view using the menu in the viewport's
+top-left corner. Alternatively, you can press the keypad's 7 key.
+
+|image6|
+
+The grid is a bit distracting for me. You can toggle it by going to the *View*
+menu in the toolbar and clicking *View Grid*.
+
+|image7|
+
+You now want to move the cylinder along the ground plane, looking at the camera
+preview in the bottom viewport. I recommend using grid snap to do so. You can
+toggle it by clicking the magnet icon in the toolbar or pressing Y.
+
+|image8|
+
+Place the cylinder so it's right outside the camera's view in the top-left
+corner.
+
+|image9|
+
+We're going to create copies of the mesh and place them around the game area.
+Press :kbd:`Ctrl + D` (:kbd:`Cmd + D` on MacOS) to duplicate the node. You can also right-click
+the node in the *Scene* dock and select *Duplicate*. Move the copy down along
+the blue Z axis until it's right outside the camera's preview.
+
+Select both cylinders by pressing the :kbd:`Shift` key and clicking on the unselected
+one and duplicate them.
+
+|image10|
+
+Move them to the right by dragging the red X axis.
+
+|image11|
+
+They're a bit hard to see in white, aren't they? Let's make them stand out by
+giving them a new material.
+
+In 3D, materials define a surface's visual properties like its color, how it
+reflects light, and more. We can use them to change the color of a mesh.
+
+We can update all four cylinders at once. Select all the mesh instances in the
+*Scene* dock. To do so, you can click on the first one and Shift click on the
+last one.
+
+|image12|
+
+In the *Inspector*, expand the *Material* section and assign a *SpatialMaterial*
+to slot *0*.
+
+|image13|
+
+Click the sphere icon to open the material resource. You get a preview of the
+material and a long list of sections filled with properties. You can use these
+to create all sorts of surfaces, from metal to rock or water.
+
+Expand the *Albedo* section and set the color to something that contrasts with
+the background, like a bright orange.
+
+|image14|
+
+We can now use the cylinders as guides. Fold them in the *Scene* dock by
+clicking the grey arrow next to them. Moving forward, you can also toggle their
+visibility by clicking the eye icon next to *Cylinders*.
+
+|image15|
+
+Add a *Path* node as a child of *Main*. In the toolbar, four icons appear. Click
+the *Add Point* tool, the icon with the green "+" sign.
+
+|image16|
+
+.. note:: You can hover any icon to see a tooltip describing the tool.
+
+Click in the center of each cylinder to create a point. Then, click the *Close
+Curve* icon in the toolbar to close the path. If any point is a bit off, you can
+click and drag on it to reposition it.
+
+|image17|
+
+Your path should look like this.
+
+|image18|
+
+To sample random positions on it, we need a *PathFollow* node. Add a
+*PathFollow* as a child of the *Path*. Rename the two nodes to *SpawnPath* and
+*SpawnLocation*, respectively. It's more descriptive of what we'll use them for.
+
+|image19|
+
+With that, we're ready to code the spawn mechanism.
+
+Spawning monsters randomly
+--------------------------
+
+Right-click on the *Main* node and attach a new script to it.
+
+We first export a variable to the *Inspector* so that we can assign ``Mob.tscn``
+or any other monster to it.
+
+Then, as we're going to spawn the monsters procedurally, we want to randomize
+numbers every time we play the game. If we don't do that, the monsters will
+always spawn following the same sequence.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends Node
+
+   export (PackedScene) var mob_scene
+
+
+   func _ready():
+       randomize()
+
+ .. code-tab:: csharp
+
+    public class Main : Node
+    {
+        // Don't forget to rebuild the project so the editor knows about the new export variable.
+
+    #pragma warning disable 649
+        // We assign this in the editor, so we don't need the warning about not being assigned.
+        [Export]
+        public PackedScene MobScene;
+    #pragma warning restore 649
+
+        public override void _Ready()
+        {
+            GD.Randomize();
+        }
+    }
+
+We want to spawn mobs at regular time intervals. To do this, we need to go back
+to the scene and add a timer. Before that, though, we need to assign the
+``Mob.tscn`` file to the ``mob_scene`` property.
+
+Head back to the 3D screen and select the *Main* node. Drag ``Mob.tscn`` from
+the *FileSystem* dock to the *Mob Scene* slot in the *Inspector*.
+
+|image20|
+
+Add a new *Timer* node as a child of *Main*. Name it *MobTimer*.
+
+|image21|
+
+In the *Inspector*, set its *Wait Time* to ``0.5`` seconds and turn on
+*Autostart* so it automatically starts when we run the game.
+
+|image22|
+
+Timers emit a ``timeout`` signal every time they reach the end of their *Wait
+Time*. By default, they restart automatically, emitting the signal in a cycle.
+We can connect to this signal from the *Main* node to spawn monsters every
+``0.5`` seconds.
+
+With the *MobTimer* still selected, head to the *Node* dock on the right and
+double-click the ``timeout`` signal.
+
+|image23|
+
+Connect it to the *Main* node.
+
+|image24|
+
+This will take you back to the script, with a new empty
+``_on_MobTimer_timeout()`` function.
+
+Let's code the mob spawning logic. We're going to:
+
+1. Instantiate the mob scene.
+2. Sample a random position on the spawn path.
+3. Get the player's position.
+4. Add the mob as a child of the *Main* node.
+5. Call the mob's ``initialize()`` method, passing it the random position and
+   the player's position.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _on_MobTimer_timeout():
+       # Create a Mob instance and add it to the scene.
+       var mob = mob_scene.instance()
+
+       # Choose a random location on Path2D.
+       # We store the reference to the SpawnLocation node.
+       var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
+       # And give it a random offset.
+       mob_spawn_location.unit_offset = randf()
+
+       var player_position = $Player.transform.origin
+
+       add_child(mob)
+       mob.initialize(mob_spawn_location.translation, player_position)
+
+ .. code-tab:: csharp
+
+    // We also specified this function name in PascalCase in the editor's connection window
+    public void OnMobTimerTimeout()
+    {
+        // Create a mob instance and add it to the scene.
+        Mob mob = (Mob)MobScene.Instance();
+
+        // Choose a random location on Path2D.
+        // We stire the reference to the SpawnLocation node.
+        var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
+        // And give it a random offset.
+        mobSpawnLocation.UnitOffset = GD.Randf();
+
+        Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
+
+        AddChild(mob);
+        mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+    }
+
+Above, ``randf()`` produces a random value between ``0`` and ``1``, which is
+what the *PathFollow* node's ``unit_offset`` expects.
+
+Here is the complete ``Main.gd`` script so far, for reference.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends Node
+
+   export (PackedScene) var mob_scene
+
+
+   func _ready():
+       randomize()
+
+
+   func _on_MobTimer_timeout():
+       var mob = mob_scene.instance()
+
+       var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
+       mob_spawn_location.unit_offset = randf()
+       var player_position = $Player.transform.origin
+
+       add_child(mob)
+       mob.initialize(mob_spawn_location.translation, player_position)
+
+ .. code-tab:: csharp
+
+    public class Main : Node
+    {
+    #pragma warning disable 649
+        [Export]
+        public PackedScene MobScene;
+    #pragma warning restore 649
+
+        public override void _Ready()
+        {
+            GD.Randomize();
+        }
+
+        public void OnMobTimerTimeout()
+        {
+            Mob mob = (Mob)MobScene.Instance();
+
+            var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
+            mobSpawnLocation.UnitOffset = GD.Randf();
+
+            Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
+
+            AddChild(mob);
+            mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+        }
+    }
+
+You can test the scene by pressing :kbd:`F6`. You should see the monsters spawn and
+move in a straight line.
+
+|image25|
+
+For now, they bump and slide against one another when their paths cross. We'll
+address this in the next part.
+
+.. |image0| image:: img/05.spawning_mobs/01.monsters_path_preview.png
+.. |image1| image:: img/05.spawning_mobs/02.project_settings.png
+.. |image2| image:: img/05.spawning_mobs/03.window_settings.png
+.. |image3| image:: img/05.spawning_mobs/04.camera_preview.png
+.. |image4| image:: img/05.spawning_mobs/05.cylinders_node.png
+.. |image5| image:: img/05.spawning_mobs/06.cylinder_mesh.png
+.. |image6| image:: img/05.spawning_mobs/07.top_view.png
+.. |image7| image:: img/05.spawning_mobs/08.toggle_view_grid.png
+.. |image8| image:: img/05.spawning_mobs/09.toggle_grid_snap.png
+.. |image9| image:: img/05.spawning_mobs/10.place_first_cylinder.png
+.. |image10| image:: img/05.spawning_mobs/11.both_cylinders_selected.png
+.. |image11| image:: img/05.spawning_mobs/12.four_cylinders.png
+.. |image12| image:: img/05.spawning_mobs/13.selecting_all_cylinders.png
+.. |image13| image:: img/05.spawning_mobs/14.spatial_material.png
+.. |image14| image:: img/05.spawning_mobs/15.bright-cylinders.png
+.. |image15| image:: img/05.spawning_mobs/16.cylinders_fold.png
+.. |image16| image:: img/05.spawning_mobs/17.points_options.png
+.. |image17| image:: img/05.spawning_mobs/18.close_path.png
+.. |image18| image:: img/05.spawning_mobs/19.path_result.png
+.. |image19| image:: img/05.spawning_mobs/20.spawn_nodes.png
+.. |image20| image:: img/05.spawning_mobs/20.mob_scene_property.png
+.. |image21| image:: img/05.spawning_mobs/21.mob_timer.png
+.. |image22| image:: img/05.spawning_mobs/22.mob_timer_properties.png
+.. |image23| image:: img/05.spawning_mobs/23.timeout_signal.png
+.. |image24| image:: img/05.spawning_mobs/24.connect_timer_to_main.png
+.. |image25| image:: img/05.spawning_mobs/25.spawn_result.png

+ 353 - 0
getting_started/first_3d_game/06.jump_and_squash.rst

@@ -0,0 +1,353 @@
+.. _doc_first_3d_game_jumping_and_squashing_monsters:
+
+Jumping and squashing monsters
+==============================
+
+In this part, we'll add the ability to jump, to squash the monsters. In the next
+lesson, we'll make the player die when a monster hits them on the ground.
+
+First, we have to change a few settings related to physics interactions. Enter
+the world of :ref:`physics layers
+<doc_physics_introduction_collision_layers_and_masks>`.
+
+Controlling physics interactions
+--------------------------------
+
+Physics bodies have access to two complementary properties: layers and masks.
+Layers define on which physics layer(s) an object is.
+
+Masks control the layers that a body will listen to and detect. This affects
+collision detection. When you want two bodies to interact, you need at least one
+to have a mask corresponding to the other.
+
+If that's confusing, don't worry, we'll see three examples in a second.
+
+The important point is that you can use layers and masks to filter physics
+interactions, control performance, and remove the need for extra conditions in
+your code.
+
+By default, all physics bodies and areas are set to both layer and mask ``0``.
+This means they all collide with each other.
+
+Physics layers are represented by numbers, but we can give them names to keep
+track of what's what.
+
+Setting layer names
+~~~~~~~~~~~~~~~~~~~
+
+Let's give our physics layers a name. Go to *Project -> Project Settings*.
+
+|image0|
+
+In the left menu, navigate down to *Layer Names -> 3D Physics*. You can see a
+list of layers with a field next to each of them on the right. You can set their
+names there. Name the first three layers *player*, *enemies*, and *world*,
+respectively.
+
+|image1|
+
+Now, we can assign them to our physics nodes.
+
+Assigning layers and masks
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In the *Main* scene, select the *Ground* node. In the *Inspector*, expand the
+*Collision* section. There, you can see the node's layers and masks as a grid of
+buttons.
+
+|image2|
+
+The ground is part of the world, so we want it to be part of the third layer.
+Click the lit button to toggle off the first *Layer* and toggle on the third
+one. Then, toggle off the *Mask* by clicking on it.
+
+|image3|
+
+As I mentioned above, the *Mask* property allows a node to listen to interaction
+with other physics objects, but we don't need it to have collisions. The
+*Ground* doesn't need to listen to anything; it's just there to prevent
+creatures from falling.
+
+Note that you can click the "..." button on the right side of the properties to
+see a list of named checkboxes.
+
+|image4|
+
+Next up are the *Player* and the *Mob*. Open ``Player.tscn`` by double-clicking
+the file in the *FileSystem* dock.
+
+Select the *Player* node and set its *Collision -> Mask* to both "enemies" and
+"world". You can leave the default *Layer* property as the first layer is the
+"player" one.
+
+|image5|
+
+Then, open the *Mob* scene by double-clicking on ``Mob.tscn`` and select the
+*Mob* node.
+
+Set its *Collision -> Layer* to "enemies" and unset its *Collision -> Mask*,
+leaving the mask empty.
+
+|image6|
+
+These settings mean the monsters will move through one another. If you want the
+monsters to collide with and slide against each other, turn on the "enemies"
+mask.
+
+.. note::
+
+    The mobs don't need to mask the "world" layer because they only move
+    on the XZ plane. We don't apply any gravity to them by design.
+
+Jumping
+-------
+
+The jumping mechanic itself requires only two lines of code. Open the *Player*
+script. We need a value to control the jump's strength and update
+``_physics_process()`` to code the jump.
+
+After the line that defines ``fall_acceleration``, at the top of the script, add
+the ``jump_impulse``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   #...
+   # Vertical impulse applied to the character upon jumping in meters per second.
+   export var jump_impulse = 20
+
+ .. code-tab:: csharp
+
+    // Don't forget to rebuild the project so the editor knows about the new export variable.
+
+    // ...
+    // Vertical impulse applied to the character upon jumping in meters per second.
+    [Export]
+    public int JumpImpulse = 20;
+
+Inside ``_physics_process()``, add the following code before the line where we
+called ``move_and_slide()``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _physics_process(delta):
+       #...
+
+       # Jumping.
+       if is_on_floor() and Input.is_action_just_pressed("jump"):
+           velocity.y += jump_impulse
+
+       #...
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // ...
+
+        // Jumping.
+        if (IsOnFloor() && Input.IsActionJustPressed("jump"))
+        {
+            _velocity.y += JumpImpulse;
+        }
+
+        // ...
+    }
+
+That's all you need to jump!
+
+The ``is_on_floor()`` method is a tool from the ``KinematicBody`` class. It
+returns ``true`` if the body collided with the floor in this frame. That's why
+we apply gravity to the *Player*: so we collide with the floor instead of
+floating over it like the monsters.
+
+If the character is on the floor and the player presses "jump", we instantly
+give them a lot of vertical speed. In games, you really want controls to be
+responsive and giving instant speed boosts like these, while unrealistic, feel
+great.
+
+Notice that the Y axis is positive upwards. That's unlike 2D, where the Y axis
+is positive downward.
+
+Squashing monsters
+------------------
+
+Let's add the squash mechanic next. We're going to make the character bounce
+over monsters and kill them at the same time.
+
+We need to detect collisions with a monster and to differentiate them from
+collisions with the floor. To do so, we can use Godot's :ref:`group
+<doc_groups>` tagging feature.
+
+Open the scene ``Mob.tscn`` again and select the *Mob* node. Go to the *Node*
+dock on the right to see a list of signals. The *Node* dock has two tabs:
+*Signals*, which you've already used, and *Groups*, which allows you to assign
+tags to nodes.
+
+Click on it to reveal a field where you can write a tag name. Enter "mob" in the
+field and click the *Add* button.
+
+|image7|
+
+An icon appears in the *Scene* dock to indicate the node is part of at least one
+group.
+
+|image8|
+
+We can now use the group from the code to distinguish collisions with monsters
+from collisions with the floor.
+
+Coding the squash mechanic
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Head back to the *Player* script to code the squash and bounce.
+
+At the top of the script, we need another property, ``bounce_impulse``. When
+squashing an enemy, we don't necessarily want the character to go as high up as
+when jumping.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   # Vertical impulse applied to the character upon bouncing over a mob in
+   # meters per second.
+   export var bounce_impulse = 16
+
+ .. code-tab:: csharp
+
+    // Don't forget to rebuild the project so the editor knows about the new export variable.
+
+    // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+    [Export]
+    public int BounceImpulse = 16;
+
+Then, at the bottom of ``_physics_process()``, add the following loop. With
+``move_and_slide()``, Godot makes the body move sometimes multiple times in a
+row to smooth out the character's motion. So we have to loop over all collisions
+that may have happened.
+
+In every iteration of the loop, we check if we landed on a mob. If so, we kill
+it and bounce.
+
+With this code, if no collisions occurred on a given frame, the loop won't run.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _physics_process(delta):
+       #...
+       for index in range(get_slide_count()):
+           # We check every collision that occurred this frame.
+           var collision = get_slide_collision(index)
+           # If we collide with a monster...
+           if collision.collider.is_in_group("mob"):
+               var mob = collision.collider
+               # ...we check that we are hitting it from above.
+               if Vector3.UP.dot(collision.normal) > 0.1:
+                   # If so, we squash it and bounce.
+                   mob.squash()
+                   velocity.y = bounce_impulse
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // ...
+
+        for (int index = 0; index < GetSlideCount(); index++)
+        {
+            // We check every collision that occurred this frame.
+            KinematicCollision collision = GetSlideCollision(index);
+            // If we collide with a monster...
+            if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
+            {
+                // ...we check that we are hitting it from above.
+                if (Vector3.Up.Dot(collision.Normal) > 0.1f)
+                {
+                    // If so, we squash it and bounce.
+                    mob.Squash();
+                    _velocity.y = BounceImpulse;
+                }
+            }
+        }
+    }
+
+ That's a lot of new functions. Here's some more information about them.
+
+The functions ``get_slide_count()`` and ``get_slide_collision()`` both come from
+the :ref:`KinematicBody<class_KinematicBody>` class and are related to
+``move_and_slide()``.
+
+``get_slide_collision()`` returns a
+:ref:`KinematicCollision<class_KinematicCollision>` object that holds
+information about where and how the collision occurred. For example, we use its
+``collider`` property to check if we collided with a "mob" by calling
+``is_in_group()`` on it: ``collision.collider.is_in_group("mob")``.
+
+.. note::
+
+    The method ``is_in_group()`` is available on every :ref:`Node<class_Node>`.
+
+To check that we are landing on the monster, we use the vector dot product:
+``Vector3.UP.dot(collision.normal) > 0.1``. The collision normal is a 3D vector
+that is perpendicular to the plane where the collision occurred. The dot product
+allows us to compare it to the up direction.
+
+With dot products, when the result is greater than ``0``, the two vectors are at
+an angle of fewer than 90 degrees. A value higher than ``0.1`` tells us that we
+are roughly above the monster.
+
+We are calling one undefined function, ``mob.squash()``. We have to add it to
+the Mob class.
+
+Open the script ``Mob.gd`` by double-clicking on it in the *FileSystem* dock. At
+the top of the script, we want to define a new signal named ``squashed``. And at
+the bottom, you can add the squash function, where we emit the signal and
+destroy the mob.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   # Emitted when the player jumped on the mob.
+   signal squashed
+
+   # ...
+
+
+   func squash():
+       emit_signal("squashed")
+       queue_free()
+
+ .. code-tab:: csharp
+
+    // Don't forget to rebuild the project so the editor knows about the new signal.
+
+    // Emitted when the played jumped on the mob.
+    [Signal]
+    public delegate void Squashed();
+
+    // ...
+
+    public void Squash()
+    {
+        EmitSignal(nameof(Squashed));
+        QueueFree();
+    }
+
+We will use the signal to add points to the score in the next lesson.
+
+With that, you should be able to kill monsters by jumping on them. You can press
+:kbd:`F5` to try the game and set ``Main.tscn`` as your project's main scene.
+
+However, the player won't die yet. We'll work on that in the next part.
+
+.. |image0| image:: img/06.jump_and_squash/02.project_settings.png
+.. |image1| image:: img/06.jump_and_squash/03.physics_layers.png
+.. |image2| image:: img/06.jump_and_squash/04.default_physics_properties.png
+.. |image3| image:: img/06.jump_and_squash/05.toggle_layer_and_mask.png
+.. |image4| image:: img/06.jump_and_squash/06.named_checkboxes.png
+.. |image5| image:: img/06.jump_and_squash/07.player_physics_mask.png
+.. |image6| image:: img/06.jump_and_squash/08.mob_physics_mask.png
+.. |image7| image:: img/06.jump_and_squash/09.groups_tab.png
+.. |image8| image:: img/06.jump_and_squash/10.group_scene_icon.png

+ 467 - 0
getting_started/first_3d_game/07.killing_player.rst

@@ -0,0 +1,467 @@
+.. _doc_first_3d_game_killing_the_player:
+
+Killing the player
+==================
+
+We can kill enemies by jumping on them, but the player still can't die.
+Let's fix this.
+
+We want to detect being hit by an enemy differently from squashing them.
+We want the player to die when they're moving on the floor, but not if
+they're in the air. We could use vector math to distinguish the two
+kinds of collisions. Instead, though, we will use an *Area* node, which
+works well for hitboxes.
+
+Hitbox with the Area node
+-------------------------
+
+Head back to the *Player* scene and add a new *Area* node. Name it
+*MobDetector*. Add a *CollisionShape* node as a child of it.
+
+|image0|
+
+In the *Inspector*, assign a cylinder shape to it.
+
+|image1|
+
+Here is a trick you can use to make the collisions only happen when the
+player is on the ground or close to it. You can reduce the cylinder's
+height and move it up to the top of the character. This way, when the
+player jumps, the shape will be too high up for the enemies to collide
+with it.
+
+|image2|
+
+You also want the cylinder to be wider than the sphere. This way, the
+player gets hit before colliding and being pushed on top of the
+monster's collision box.
+
+The wider the cylinder, the more easily the player will get killed.
+
+Next, select the *MobDetector* node again, and in the *Inspector*, turn
+off its *Monitorable* property. This makes it so other physics nodes
+cannot detect the area. The complementary *Monitoring* property allows
+it to detect collisions. Then, remove the *Collision -> Layer* and sets
+the mask to the "enemies" layer.
+
+|image3|
+
+When areas detect a collision, they emit signals. We're going to connect
+one to the *Player* node. In the *Node* tab, double-click the
+``body_entered`` signal and connect it to the *Player*.
+
+|image4|
+
+The *MobDetector* will emit ``body_entered`` when a *KinematicBody* or a
+*RigidBody* node enters it. As it only masks the "enemies" physics
+layers, it will only detect the *Mob* nodes.
+
+Code-wise, we're going to do two things: emit a signal we'll later use
+to end the game and destroy the player. We can wrap these operations in
+a ``die()`` function that helps us put a descriptive label on the code.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   # Emitted when the player was hit by a mob.
+   # Put this at the top of the script.
+   signal hit
+
+
+   # And this function at the bottom.
+   func die():
+       emit_signal("hit")
+       queue_free()
+
+
+   func _on_MobDetector_body_entered(_body):
+       die()
+
+ .. code-tab:: csharp
+
+    // Don't forget to rebuild the project so the editor knows about the new signal.
+
+    // Emitted when the player was hit by a mob.
+    [Signal]
+    public delegate void Hit();
+
+    // ...
+
+    private void Die()
+    {
+        EmitSignal(nameof(Hit));
+        QueueFree();
+    }
+
+    // We also specified this function name in PascalCase in the editor's connection window
+    public void OnMobDetectorBodyEntered(Node body)
+    {
+        Die();
+    }
+
+Try the game again by pressing :kbd:`F5`. If everything is set up correctly,
+the character should die when an enemy runs into it.
+
+However, note that this depends entirely on the size and position of the
+*Player* and the *Mob*\ 's collision shapes. You may need to move them
+and resize them to achieve a tight game feel.
+
+Ending the game
+---------------
+
+We can use the *Player*\ 's ``hit`` signal to end the game. All we need
+to do is connect it to the *Main* node and stop the *MobTimer* in
+reaction.
+
+Open ``Main.tscn``, select the *Player* node, and in the *Node* dock,
+connect its ``hit`` signal to the *Main* node.
+
+|image5|
+
+Get and stop the timer in the ``_on_Player_hit()`` function.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _on_Player_hit():
+       $MobTimer.stop()
+
+ .. code-tab:: csharp
+
+    // We also specified this function name in PascalCase in the editor's connection window
+    public void OnPlayerHit()
+    {
+        GetNode<Timer>("MobTimer").Stop();
+    }
+
+If you try the game now, the monsters will stop spawning when you die,
+and the remaining ones will leave the screen.
+
+You can pat yourself in the back: you prototyped a complete 3D game,
+even if it's still a bit rough.
+
+From there, we'll add a score, the option to retry the game, and you'll
+see how you can make the game feel much more alive with minimalistic
+animations.
+
+Code checkpoint
+---------------
+
+Here are the complete scripts for the *Main*, *Mob*, and *Player* nodes,
+for reference. You can use them to compare and check your code.
+
+Starting with ``Main.gd``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends Node
+
+   export(PackedScene) var mob_scene
+
+
+   func _ready():
+       randomize()
+
+
+   func _on_MobTimer_timeout():
+       # Create a Mob instance and add it to the scene.
+       var mob = mob_scene.instance()
+
+       # Choose a random location on Path2D.
+       var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
+       # And give it a random offset.
+       mob_spawn_location.unit_offset = randf()
+
+       var player_position = $Player.transform.origin
+
+       add_child(mob)
+       mob.initialize(mob_spawn_location.translation, player_position)
+
+
+   func _on_Player_hit():
+       $MobTimer.stop()
+
+ .. code-tab:: csharp
+
+    public class Main : Node
+    {
+    #pragma warning disable 649
+        [Export]
+        public PackedScene MobScene;
+    #pragma warning restore 649
+
+        public override void _Ready()
+        {
+            GD.Randomize();
+        }
+
+        public void OnMobTimerTimeout()
+        {
+            // Create a mob instance and add it to the scene.
+            var mob = (Mob)MobScene.Instance();
+
+            // Choose a random location on Path2D.
+            // We stire the reference to the SpawnLocation node.
+            var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
+            // And give it a random offset.
+            mobSpawnLocation.UnitOffset = GD.Randf();
+
+            Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
+
+            AddChild(mob);
+            mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+        }
+
+        public void OnPlayerHit()
+        {
+            GetNode<Timer>("MobTimer").Stop();
+        }
+    }
+
+Next is ``Mob.gd``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # Emitted when the player jumped on the mob.
+   signal squashed
+
+   # Minimum speed of the mob in meters per second.
+   export var min_speed = 10
+   # Maximum speed of the mob in meters per second.
+   export var max_speed = 18
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(_delta):
+       move_and_slide(velocity)
+
+
+   func initialize(start_position, player_position):
+       translation = start_position
+       look_at(player_position, Vector3.UP)
+       rotate_y(rand_range(-PI / 4, PI / 4))
+
+       var random_speed = rand_range(min_speed, max_speed)
+       velocity = Vector3.FORWARD * random_speed
+       velocity = velocity.rotated(Vector3.UP, rotation.y)
+
+
+    func squash():
+       emit_signal("squashed")
+       queue_free()
+
+
+   func _on_VisibilityNotifier_screen_exited():
+       queue_free()
+
+ .. code-tab:: csharp
+
+    public class Mob : KinematicBody
+    {
+        // Emitted when the played jumped on the mob.
+        [Signal]
+        public delegate void Squashed();
+
+        // Minimum speed of the mob in meters per second
+        [Export]
+        public int MinSpeed = 10;
+        // Maximum speed of the mob in meters per second
+        [Export]
+        public int MaxSpeed = 18;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            MoveAndSlide(_velocity);
+        }
+
+        public void Initialize(Vector3 startPosition, Vector3 playerPosition)
+        {
+            Translation = startPosition;
+            LookAt(playerPosition, Vector3.Up);
+            RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
+
+            float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
+            _velocity = Vector3.Forward * randomSpeed;
+            _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+        }
+
+        public void Squash()
+        {
+            EmitSignal(nameof(Squashed));
+            QueueFree();
+        }
+
+        public void OnVisibilityNotifierScreenExited()
+        {
+            QueueFree();
+        }
+    }
+
+Finally, the longest script, ``Player.gd``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # Emitted when a mob hit the player.
+   signal hit
+
+   # How fast the player moves in meters per second.
+   export var speed = 14
+   # The downward acceleration when in the air, in meters per second squared.
+   export var fall_acceleration = 75
+   # Vertical impulse applied to the character upon jumping in meters per second.
+   export var jump_impulse = 20
+   # Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+   export var bounce_impulse = 16
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(delta):
+       var direction = Vector3.ZERO
+
+       if Input.is_action_pressed("move_right"):
+           direction.x += 1
+       if Input.is_action_pressed("move_left"):
+           direction.x -= 1
+       if Input.is_action_pressed("move_back"):
+           direction.z += 1
+       if Input.is_action_pressed("move_forward"):
+           direction.z -= 1
+
+       if direction != Vector3.ZERO:
+           direction = direction.normalized()
+           $Pivot.look_at(translation + direction, Vector3.UP)
+
+       velocity.x = direction.x * speed
+       velocity.z = direction.z * speed
+
+       # Jumping.
+       if is_on_floor() and Input.is_action_just_pressed("jump"):
+           velocity.y += jump_impulse
+
+       velocity.y -= fall_acceleration * delta
+       velocity = move_and_slide(velocity, Vector3.UP)
+
+       for index in range(get_slide_count()):
+           var collision = get_slide_collision(index)
+           if collision.collider.is_in_group("mob"):
+               var mob = collision.collider
+               if Vector3.UP.dot(collision.normal) > 0.1:
+                   mob.squash()
+                   velocity.y = bounce_impulse
+
+
+   func die():
+       emit_signal("hit")
+       queue_free()
+
+
+   func _on_MobDetector_body_entered(_body):
+       die()
+
+ .. code-tab:: csharp
+
+    public class Player : KinematicBody
+    {
+        // Emitted when the player was hit by a mob.
+        [Signal]
+        public delegate void Hit();
+
+        // How fast the player moves in meters per second.
+        [Export]
+        public int Speed = 14;
+        // The downward acceleration when in the air, in meters per second squared.
+        [Export]
+        public int FallAcceleration = 75;
+        // Vertical impulse applied to the character upon jumping in meters per second.
+        [Export]
+        public int JumpImpulse = 20;
+        // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+        [Export]
+        public int BounceImpulse = 16;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            var direction = Vector3.Zero;
+
+            if (Input.IsActionPressed("move_right"))
+            {
+                direction.x += 1f;
+            }
+            if (Input.IsActionPressed("move_left"))
+            {
+                direction.x -= 1f;
+            }
+            if (Input.IsActionPressed("move_back"))
+            {
+                direction.z += 1f;
+            }
+            if (Input.IsActionPressed("move_forward"))
+            {
+                direction.z -= 1f;
+            }
+
+            if (direction != Vector3.Zero)
+            {
+                direction = direction.Normalized();
+                GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
+            }
+
+            _velocity.x = direction.x * Speed;
+            _velocity.z = direction.z * Speed;
+
+            // Jumping.
+            if (IsOnFloor() && Input.IsActionJustPressed("jump"))
+            {
+                _velocity.y += JumpImpulse;
+            }
+
+            _velocity.y -= FallAcceleration * delta;
+            _velocity = MoveAndSlide(_velocity, Vector3.Up);
+
+            for (int index = 0; index < GetSlideCount(); index++)
+            {
+                KinematicCollision collision = GetSlideCollision(index);
+                if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
+                {
+                    if (Vector3.Up.Dot(collision.Normal) > 0.1f)
+                    {
+                        mob.Squash();
+                        _velocity.y = BounceImpulse;
+                    }
+                }
+            }
+        }
+
+        private void Die()
+        {
+            EmitSignal(nameof(Hit));
+            QueueFree();
+        }
+
+        public void OnMobDetectorBodyEntered(Node body)
+        {
+            Die();
+        }
+    }
+
+See you in the next lesson to add the score and the retry option.
+
+.. |image0| image:: img/07.killing_player/01.adding_area_node.png
+.. |image1| image:: img/07.killing_player/02.cylinder_shape.png
+.. |image2| image:: img/07.killing_player/03.cylinder_in_editor.png
+.. |image3| image:: img/07.killing_player/04.mob_detector_properties.png
+.. |image4| image:: img/07.killing_player/05.body_entered_signal.png
+.. |image5| image:: img/07.killing_player/06.player_hit_signal.png

+ 475 - 0
getting_started/first_3d_game/08.score_and_replay.rst

@@ -0,0 +1,475 @@
+.. _doc_first_3d_game_score_and_replay:
+
+Score and replay
+================
+
+In this part, we'll add the score, music playback, and the ability to restart
+the game.
+
+We have to keep track of the current score in a variable and display it on
+screen using a minimal interface. We will use a text label to do that.
+
+In the main scene, add a new *Control* node as a child of *Main* and name it
+*UserInterface*. You will automatically be taken to the 2D screen, where you can
+edit your User Interface (UI).
+
+Add a *Label* node and rename it to *ScoreLabel*.
+
+|image0|
+
+In the *Inspector*, set the *Label*'s *Text* to a placeholder like "Score: 0".
+
+|image1|
+
+Also, the text is white by default, like our game's background. We need to
+change its color to see it at runtime.
+
+Scroll down to *Custom Colors* and click the black box next to *Font Color* to
+tint the text.
+
+|image2|
+
+Pick a dark tone so it contrasts well with the 3D scene.
+
+|image3|
+
+Finally, click and drag on the text in the viewport to move it away from the
+top-left corner.
+
+|image4|
+
+The *UserInterface* node allows us to group our UI in a branch of the scene tree
+and use a theme resource that will propagate to all its children. We'll use it
+to set our game's font.
+
+Creating a UI theme
+-------------------
+
+Once again, select the *UserInterface* node. In the *Inspector*, create a new
+theme resource in *Theme -> Theme*.
+
+|image5|
+
+Click on it to open the theme editor In the bottom panel. It gives you a preview
+of how all the built-in UI widgets will look with your theme resource.
+
+|image6|
+
+By default, a theme only has one property, the *Default Font*.
+
+.. seealso::
+
+    You can add more properties to the theme resource to design complex user
+    interfaces, but that is beyond the scope of this series. To learn more about
+    creating and editing themes, see :ref:`doc_gui_skinning`.
+
+Click the *Default Font* property and create a new *DynamicFont*.
+
+|image7|
+
+Expand the *DynamicFont* by clicking on it and expand its *Font* section. There,
+you will see an empty *Font Data* field.
+
+|image8|
+
+This one expects a font file like the ones you have on your computer. Two common
+font file formats are TrueType Font (TTF) and OpenType Font (OTF).
+
+In the *FileSystem* dock, Expand the ``fonts`` directory and click and drag the
+``Montserrat-Medium.ttf`` file we included in the project onto the *Font Data*.
+The text will reappear in the theme preview.
+
+The text is a bit small. Set the *Settings -> Size* to ``22`` pixels to increase
+the text's size.
+
+|image9|
+
+Keeping track of the score
+--------------------------
+
+Let's work on the score next. Attach a new script to the *ScoreLabel* and define
+the ``score`` variable.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends Label
+
+   var score = 0
+
+ .. code-tab:: csharp
+
+    public class ScoreLabel : Label
+    {
+        private int _score = 0;
+    }
+
+The score should increase by ``1`` every time we squash a monster. We can use
+their ``squashed`` signal to know when that happens. However, as we instantiate
+monsters from the code, we cannot do the connection in the editor.
+
+Instead, we have to make the connection from the code every time we spawn a
+monster.
+
+Open the script ``Main.gd``. If it's still open, you can click on its name in
+the script editor's left column.
+
+|image10|
+
+Alternatively, you can double-click the ``Main.gd`` file in the *FileSystem*
+dock.
+
+At the bottom of the ``_on_MobTimer_timeout()`` function, add the following
+line.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _on_MobTimer_timeout():
+       #...
+       # We connect the mob to the score label to update the score upon squashing one.
+       mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")
+
+ .. code-tab:: csharp
+
+    public void OnMobTimerTimeout()
+    {
+        // ...
+        // We connect the mob to the score label to update the score upon squashing one.
+        mob.Connect(nameof(Mob.Squashed), GetNode<ScoreLabel>("UserInterface/ScoreLabel"), nameof(ScoreLabel.OnMobSquashed));
+    }
+
+This line means that when the mob emits the ``squashed`` signal, the
+*ScoreLabel* node will receive it and call the function ``_on_Mob_squashed()``.
+
+Head back to the ``ScoreLabel.gd`` script to define the ``_on_Mob_squashed()``
+callback function.
+
+There, we increment the score and update the displayed text.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _on_Mob_squashed():
+       score += 1
+       text = "Score: %s" % score
+
+ .. code-tab:: csharp
+
+    public void OnMobSquashed()
+    {
+        _score += 1;
+        Text = string.Format("Score: {0}", _score);
+    }
+
+The second line uses the value of the ``score`` variable to replace the
+placeholder ``%s``. When using this feature, Godot automatically converts values
+to text, which is convenient to output text in labels or using the ``print()``
+function.
+
+.. seealso::
+
+    You can learn more about string formatting here: :ref:`doc_gdscript_printf`.
+
+You can now play the game and squash a few enemies to see the score
+increase.
+
+|image11|
+
+.. note::
+
+    In a complex game, you may want to completely separate your user interface
+    from the game world. In that case, you would not keep track of the score on
+    the label. Instead, you may want to store it in a separate, dedicated
+    object. But when prototyping or when your project is simple, it is fine to
+    keep your code simple. Programming is always a balancing act.
+
+Retrying the game
+-----------------
+
+We'll now add the ability to play again after dying. When the player dies, we'll
+display a message on the screen and wait for input.
+
+Head back to the *Main* scene, select the *UserInterface* node, add a
+*ColorRect* node as a child of it and name it *Retry*. This node fills a
+rectangle with a uniform color and will serve as an overlay to darken the
+screen.
+
+To make it span over the whole viewport, you can use the *Layout* menu in the
+toolbar.
+
+|image12|
+
+Open it and apply the *Full Rect* command.
+
+|image13|
+
+Nothing happens. Well, almost nothing: only the four green pins move to the
+corners of the selection box.
+
+|image14|
+
+This is because UI nodes (all the ones with a green icon) work with anchors and
+margins relative to their parent's bounding box. Here, the *UserInterface* node
+has a small size and the *Retry* one is limited by it.
+
+Select the *UserInterface* and apply *Layout -> Full Rect* to it as well. The
+*Retry* node should now span the whole viewport.
+
+Let's change its color so it darkens the game area. Select *Retry* and in the
+*Inspector*, set its *Color* to something both dark and transparent. To do so,
+in the color picker, drag the *A* slider to the left. It controls the color's
+alpha channel, that is to say, its opacity.
+
+|image15|
+
+Next, add a *Label* as a child of *Retry* and give it the *Text* "Press Enter to
+retry."
+
+|image16|
+
+To move it and anchor it in the center of the screen, apply *Layout -> Center*
+to it.
+
+|image17|
+
+Coding the retry option
+~~~~~~~~~~~~~~~~~~~~~~~
+
+We can now head to the code to show and hide the *Retry* node when the player
+dies and plays again.
+
+Open the script ``Main.gd``. First, we want to hide the overlay at the start of
+the game. Add this line to the ``_ready()`` function.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _ready():
+       #...
+       $UserInterface/Retry.hide()
+
+ .. code-tab:: csharp
+
+    public override void _Ready()
+    {
+        // ...
+        GetNode<Control>("UserInterface/Retry").Hide();
+    }
+
+Then, when the player gets hit, we show the overlay.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _on_Player_hit():
+       #...
+       $UserInterface/Retry.show()
+
+ .. code-tab:: csharp
+
+    public void OnPlayerHit()
+    {
+        //...
+        GetNode<Control>("UserInterface/Retry").Show();
+    }
+
+Finally, when the *Retry* node is visible, we need to listen to the player's
+input and restart the game if they press enter. To do this, we use the built-in
+``_unhandled_input()`` callback.
+
+If the player pressed the predefined ``ui_accept`` input action and *Retry* is
+visible, we reload the current scene.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _unhandled_input(event):
+       if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
+           # This restarts the current scene.
+           get_tree().reload_current_scene()
+
+ .. code-tab:: csharp
+
+    public override void _UnhandledInput(InputEvent @event)
+    {
+        if (@event.IsActionPressed("ui_accept") && GetNode<Control>("UserInterface/Retry").Visible)
+        {
+            // This restarts the current scene.
+            GetTree().ReloadCurrentScene();
+        }
+    }
+
+The function ``get_tree()`` gives us access to the global :ref:`SceneTree
+<class_SceneTree>` object, which allows us to reload and restart the current
+scene.
+
+Adding music
+------------
+
+To add music that plays continuously in the background, we're going to use
+another feature in Godot: :ref:`autoloads <doc_singletons_autoload>`.
+
+To play audio, all you need to do is add an *AudioStreamPlayer* node to your
+scene and attach an audio file to it. When you start the scene, it can play
+automatically. However, when you reload the scene, like we do to play again, the
+audio nodes are also reset, and the music starts back from the beginning.
+
+You can use the autoload feature to have Godot load a node or a scene
+automatically at the start of the game, outside the current scene. You can also
+use it to create globally accessible objects.
+
+Create a new scene by going to the *Scene* menu and clicking *New Scene*.
+
+|image18|
+
+Click the *Other Node* button to create an *AudioStreamPlayer* and rename it to
+*MusicPlayer*.
+
+|image19|
+
+We included a music soundtrack in the ``art/`` directory, ``House In a Forest
+Loop.ogg``. Click and drag it onto the *Stream* property in the *Inspector*.
+Also, turn on *Autoplay* so the music plays automatically at the start of the
+game.
+
+|image20|
+
+Save the scene as ``MusicPlayer.tscn``.
+
+We have to register it as an autoload. Head to the *Project -> Project
+Settings…* menu and click on the *Autoload* tab.
+
+In the *Path* field, you want to enter the path to your scene. Click the folder
+icon to open the file browser and double-click on ``MusicPlayer.tscn``. Then,
+click the *Add* button on the right to register the node.
+
+|image21|
+
+If you run the game now, the music will play automatically. And even when you
+lose and retry, it keeps going.
+
+Before we wrap up this lesson, here's a quick look at how it works under the
+hood. When you run the game, your *Scene* dock changes to give you two tabs:
+*Remote* and *Local*.
+
+|image22|
+
+The *Remote* tab allows you to visualize the node tree of your running game.
+There, you will see the *Main* node and everything the scene contains and the
+instantiated mobs at the bottom.
+
+|image23|
+
+At the top are the autoloaded *MusicPlayer* and a *root* node, which is your
+game's viewport.
+
+And that does it for this lesson. In the next part, we'll add an animation to
+make the game both look and feel much nicer.
+
+Here is the complete ``Main.gd`` script for reference.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends Node
+
+   export (PackedScene) var mob_scene
+
+
+   func _ready():
+       randomize()
+       $UserInterface/Retry.hide()
+
+
+   func _unhandled_input(event):
+       if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
+           get_tree().reload_current_scene()
+
+
+   func _on_MobTimer_timeout():
+       var mob = mob_scene.instance()
+
+       var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
+       mob_spawn_location.unit_offset = randf()
+
+       var player_position = $Player.transform.origin
+
+       add_child(mob)
+       mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")
+       mob.initialize(mob_spawn_location.translation, player_position)
+
+
+   func _on_Player_hit():
+       $MobTimer.stop()
+       $UserInterface/Retry.show()
+
+ .. code-tab:: csharp
+
+    public class Main : Node
+    {
+    #pragma warning disable 649
+        [Export]
+        public PackedScene MobScene;
+    #pragma warning restore 649
+
+        public override void _Ready()
+        {
+            GD.Randomize();
+            GetNode<Control>("UserInterface/Retry").Hide();
+        }
+
+        public override void _UnhandledInput(InputEvent @event)
+        {
+            if (@event.IsActionPressed("ui_accept") && GetNode<Control>("UserInterface/Retry").Visible)
+            {
+                GetTree().ReloadCurrentScene();
+            }
+        }
+
+        public void OnMobTimerTimeout()
+        {
+            Mob mob = (Mob)MobScene.Instance();
+
+            var mobSpawnLocation = GetNode<PathFollow>("SpawnPath/SpawnLocation");
+            mobSpawnLocation.UnitOffset = GD.Randf();
+
+            Vector3 playerPosition = GetNode<Player>("Player").Transform.origin;
+
+            AddChild(mob);
+            mob.Initialize(mobSpawnLocation.Translation, playerPosition);
+            mob.Connect(nameof(Mob.Squashed), GetNode<ScoreLabel>("UserInterface/ScoreLabel"), nameof(ScoreLabel.OnMobSquashed));
+        }
+
+        public void OnPlayerHit()
+        {
+            GetNode<Timer>("MobTimer").Stop();
+            GetNode<Control>("UserInterface/Retry").Show();
+        }
+    }
+
+
+.. |image0| image:: img/08.score_and_replay/01.label_node.png
+.. |image1| image:: img/08.score_and_replay/02.score_placeholder.png
+.. |image2| image:: img/08.score_and_replay/02.score_custom_color.png
+.. |image3| image:: img/08.score_and_replay/02.score_color_picker.png
+.. |image4| image:: img/08.score_and_replay/02.score_label_moved.png
+.. |image5| image:: img/08.score_and_replay/03.creating_theme.png
+.. |image6| image:: img/08.score_and_replay/04.theme_preview.png
+.. |image7| image:: img/08.score_and_replay/05.dynamic_font.png
+.. |image8| image:: img/08.score_and_replay/06.font_data.png
+.. |image9| image:: img/08.score_and_replay/07.font_size.png
+.. |image10| image:: img/08.score_and_replay/08.open_main_script.png
+.. |image11| image:: img/08.score_and_replay/09.score_in_game.png
+.. |image12| image:: img/08.score_and_replay/10.layout_icon.png
+.. |image13| image:: img/08.score_and_replay/11.full_rect_option.png
+.. |image14| image:: img/08.score_and_replay/12.anchors_updated.png
+.. |image15| image:: img/08.score_and_replay/13.retry_color_picker.png
+.. |image16| image:: img/08.score_and_replay/14.retry_node.png
+.. |image17| image:: img/08.score_and_replay/15.layout_center.png
+.. |image18| image:: img/08.score_and_replay/16.new_scene.png
+.. |image19| image:: img/08.score_and_replay/17.music_player_node.png
+.. |image20| image:: img/08.score_and_replay/18.music_node_properties.png
+.. |image21| image:: img/08.score_and_replay/19.register_autoload.png
+.. |image22| image:: img/08.score_and_replay/20.scene_dock_tabs.png
+.. |image23| image:: img/08.score_and_replay/21.remote_scene_tree.png

+ 565 - 0
getting_started/first_3d_game/09.adding_animations.rst

@@ -0,0 +1,565 @@
+.. _doc_first_3d_game_character_animation:
+
+Character animation
+===================
+
+In this final lesson, we'll use Godot's built-in animation tools to make our
+characters float and flap. You'll learn to design animations in the editor and
+use code to make your game feel alive.
+
+|image0|
+
+We'll start with an introduction to using the animation editor.
+
+Using the animation editor
+--------------------------
+
+The engine comes with tools to author animations in the editor. You can then use
+the code to play and control them at runtime.
+
+Open the player scene, select the player node, and add an animation player node.
+
+The *Animation* dock appears in the bottom panel.
+
+|image1|
+
+It features a toolbar and the animation drop-down menu at the top, a track
+editor in the middle that's currently empty, and filter, snap, and zoom options
+at the bottom.
+
+Let's create an animation. Click on *Animation -> New*.
+
+|image2|
+
+Name the animation "float".
+
+|image3|
+
+Once you created the animation, the timeline appears with numbers representing
+time in seconds.
+
+|image4|
+
+We want the animation to start playback automatically at the start of the game.
+Also, it should loop.
+
+To do so, you can click the button with an "A+" icon in the animation toolbar
+and the looping arrows, respectively.
+
+|image5|
+
+You can also pin the animation editor by clicking the pin icon in the top-right.
+This prevents it from folding when you click on the viewport and deselect the
+nodes.
+
+|image6|
+
+Set the animation duration to ``1.2`` seconds in the top-right of the dock.
+
+|image7|
+
+You should see the gray ribbon widen a bit. It shows you the start and end of
+your animation and the vertical blue line is your time cursor.
+
+|image8|
+
+You can click and drag the slider in the bottom-right to zoom in and out of the
+timeline.
+
+|image9|
+
+The float animation
+-------------------
+
+With the animation player node, you can animate most properties on as many nodes
+as you need. Notice the key icon next to properties in the *Inspector*. You can
+click any of them to create a keyframe, a time and value pair for the
+corresponding property. The keyframe gets inserted where your time cursor is in
+the timeline.
+
+Let's insert our first keys. Here, we will animate both the translation and the
+rotation of the *Character* node.
+
+Select the *Character* and click the key icon next to *Translation* in the
+*Inspector*. Do the same for *Rotation Degrees*.
+
+|image10|
+
+Two tracks appear in the editor with a diamond icon representing each keyframe.
+
+|image11|
+
+You can click and drag on the diamonds to move them in time. Move the
+translation key to ``0.2`` seconds and the rotation key to ``0.1`` seconds.
+
+|image12|
+
+Move the time cursor to ``0.5`` seconds by clicking and dragging on the gray
+timeline. In the *Inspector*, set the *Translation*'s *Y* axis to about
+``0.65`` meters and the *Rotation Degrees*' *X* axis to ``8``.
+
+|image13|
+
+Create a keyframe for both properties and shift the translation key to ``0.7``
+seconds by dragging it on the timeline.
+
+|image14|
+
+.. note::
+
+    A lecture on the principles of animation is beyond the scope of this
+    tutorial. Just note that you don't want to time and space everything evenly.
+    Instead, animators play with timing and spacing, two core animation
+    principles. You want to offset and contrast in your character's motion to
+    make them feel alive.
+
+Move the time cursor to the end of the animation, at ``1.2`` seconds. Set the Y
+translation to about ``0.35`` and the X rotation to ``-9`` degrees. Once again,
+create a key for both properties.
+
+You can preview the result by clicking the play button or pressing :kbd:`Shift + D`.
+Click the stop button or press :kbd:`S` to stop playback.
+
+|image15|
+
+You can see that the engine interpolates between your keyframes to produce a
+continuous animation. At the moment, though, the motion feels very robotic. This
+is because the default interpolation is linear, causing constant transitions,
+unlike how living things move in the real world.
+
+We can control the transition between keyframes using easing curves.
+
+Click and drag around the first two keys in the timeline to box select them.
+
+|image16|
+
+You can edit the properties of both keys simultaneously in the *Inspector*,
+where you can see an *Easing* property.
+
+|image17|
+
+Click and drag on the curve, pulling it towards the left. This will make it
+ease-out, that is to say, transition fast initially and slow down as the time
+cursor reaches the next keyframe.
+
+|image18|
+
+Play the animation again to see the difference. The first half should already
+feel a bit bouncier.
+
+Apply an ease-out to the second keyframe in the rotation track.
+
+|image19|
+
+Do the opposite for the second translation keyframe, dragging it to the right.
+
+|image20|
+
+Your animation should look something like this.
+
+|image21|
+
+.. note::
+
+    Animations update the properties of the animated nodes every frame,
+    overriding initial values. If we directly animated the *Player* node, it
+    would prevent us from moving it in code. This is where the *Pivot* node
+    comes in handy: even though we animated the *Character*, we can still move
+    and rotate the *Pivot* and layer changes on top of the animation in a
+    script.
+
+If you play the game, the player's creature will now float!
+
+If the creature is a little too close to the floor, you can move the *Pivot* up
+to offset it.
+
+Controlling the animation in code
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We can use code to control the animation playback based on the player's input.
+Let's change the animation speed when the character is moving.
+
+Open the *Player*'s script by clicking the script icon next to it.
+
+|image22|
+
+In ``_physics_process()``, after the line where we check the ``direction``
+vector, add the following code.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _physics_process(delta):
+       #...
+       #if direction != Vector3.ZERO:
+           #...
+           $AnimationPlayer.playback_speed = 4
+       else:
+           $AnimationPlayer.playback_speed = 1
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // ...
+        if (direction != Vector3.Zero)
+        {
+            // ...
+            GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
+        }
+        else
+        {
+            GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
+        }
+    }
+
+This code makes it so when the player moves, we multiply the playback speed by
+``4``. When they stop, we reset it to normal.
+
+We mentioned that the pivot could layer transforms on top of the animation. We
+can make the character arc when jumping using the following line of code. Add it
+at the end of ``_physics_process()``.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func _physics_process(delta):
+       #...
+       $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
+
+ .. code-tab:: csharp
+
+    public override void _PhysicsProcess(float delta)
+    {
+        // ...
+        var pivot = GetNode<Spatial>("Pivot");
+        pivot.Rotation = new Vector3(Mathf.Pi / 6f * _velocity.y / JumpImpulse, pivot.Rotation.y, pivot.Rotation.z);
+    }
+
+Animating the mobs
+------------------
+
+Here's another nice trick with animations in Godot: as long as you use a similar
+node structure, you can copy them to different scenes.
+
+For example, both the *Mob* and the *Player* scenes have a *Pivot* and a
+*Character* node, so we can reuse animations between them.
+
+We're going to duplicate the animation using a feature called "merge from
+scene".
+
+Open the *Mob* scene, right-click on the *Mob* node and select *Merge From
+Scene*.
+
+|image23|
+
+Double-click ``Player.tscn`` to open it and import the *AnimationPlayer*. That's
+it; all monsters will now play the float animation.
+
+We can change the playback speed based on the creature's ``random_speed``. Open
+the *Mob*'s script and at the end of the ``initialize()`` function, add the
+following line.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   func initialize(start_position, player_position):
+       #...
+       $AnimationPlayer.playback_speed = random_speed / min_speed
+
+ .. code-tab:: csharp
+
+    public void Initialize(Vector3 startPosition, Vector3 playerPosition)
+    {
+        // ...
+        GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = randomSpeed / MinSpeed;
+    }
+
+And with that, you finished coding your first complete 3D game.
+
+**Congratulations**!
+
+In the next part, we'll quickly recap what you learned and give you some links
+to keep learning more. But for now, here are the complete ``Player.gd`` and
+``Mob.gd`` so you can check your code against them.
+
+Here's the *Player* script.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # Emitted when the player was hit by a mob.
+   signal hit
+
+   # How fast the player moves in meters per second.
+   export var speed = 14
+   # The downward acceleration when in the air, in meters per second per second.
+   export var fall_acceleration = 75
+   # Vertical impulse applied to the character upon jumping in meters per second.
+   export var jump_impulse = 20
+   # Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+   export var bounce_impulse = 16
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(delta):
+       var direction = Vector3.ZERO
+
+       if Input.is_action_pressed("move_right"):
+           direction.x += 1
+       if Input.is_action_pressed("move_left"):
+           direction.x -= 1
+       if Input.is_action_pressed("move_back"):
+           direction.z += 1
+       if Input.is_action_pressed("move_forward"):
+           direction.z -= 1
+
+       if direction != Vector3.ZERO:
+           direction = direction.normalized()
+           $Pivot.look_at(translation + direction, Vector3.UP)
+           $AnimationPlayer.playback_speed = 4
+       else:
+           $AnimationPlayer.playback_speed = 1
+
+       velocity.x = direction.x * speed
+       velocity.z = direction.z * speed
+
+       # Jumping
+       if is_on_floor() and Input.is_action_just_pressed("jump"):
+           velocity.y += jump_impulse
+
+       velocity.y -= fall_acceleration * delta
+       velocity = move_and_slide(velocity, Vector3.UP)
+
+       for index in range(get_slide_count()):
+           var collision = get_slide_collision(index)
+           if collision.collider.is_in_group("mob"):
+               var mob = collision.collider
+               if Vector3.UP.dot(collision.normal) > 0.1:
+                   mob.squash()
+                   velocity.y = bounce_impulse
+
+       $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
+
+
+   func die():
+       emit_signal("hit")
+       queue_free()
+
+
+   func _on_MobDetector_body_entered(_body):
+       die()
+
+ .. code-tab:: csharp
+
+    public class Player : KinematicBody
+    {
+        // Emitted when the player was hit by a mob.
+        [Signal]
+        public delegate void Hit();
+
+        // How fast the player moves in meters per second.
+        [Export]
+        public int Speed = 14;
+        // The downward acceleration when in the air, in meters per second squared.
+        [Export]
+        public int FallAcceleration = 75;
+        // Vertical impulse applied to the character upon jumping in meters per second.
+        [Export]
+        public int JumpImpulse = 20;
+        // Vertical impulse applied to the character upon bouncing over a mob in meters per second.
+        [Export]
+        public int BounceImpulse = 16;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            var direction = Vector3.Zero;
+
+            if (Input.IsActionPressed("move_right"))
+            {
+                direction.x += 1f;
+            }
+            if (Input.IsActionPressed("move_left"))
+            {
+                direction.x -= 1f;
+            }
+            if (Input.IsActionPressed("move_back"))
+            {
+                direction.z += 1f;
+            }
+            if (Input.IsActionPressed("move_forward"))
+            {
+                direction.z -= 1f;
+            }
+
+            if (direction != Vector3.Zero)
+            {
+                direction = direction.Normalized();
+                GetNode<Spatial>("Pivot").LookAt(Translation + direction, Vector3.Up);
+                GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
+            }
+            else
+            {
+                GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
+            }
+
+            _velocity.x = direction.x * Speed;
+            _velocity.z = direction.z * Speed;
+
+            // Jumping.
+            if (IsOnFloor() && Input.IsActionJustPressed("jump"))
+            {
+                _velocity.y += JumpImpulse;
+            }
+
+            _velocity.y -= FallAcceleration * delta;
+            _velocity = MoveAndSlide(_velocity, Vector3.Up);
+
+            for (int index = 0; index < GetSlideCount(); index++)
+            {
+                KinematicCollision collision = GetSlideCollision(index);
+                if (collision.Collider is Mob mob && mob.IsInGroup("mob"))
+                {
+                    if (Vector3.Up.Dot(collision.Normal) > 0.1f)
+                    {
+                        mob.Squash();
+                        _velocity.y = BounceImpulse;
+                    }
+                }
+            }
+
+            var pivot = GetNode<Spatial>("Pivot");
+            pivot.Rotation = new Vector3(Mathf.Pi / 6f * _velocity.y / JumpImpulse, pivot.Rotation.y, pivot.Rotation.z);
+        }
+
+        private void Die()
+        {
+            EmitSignal(nameof(Hit));
+            QueueFree();
+        }
+
+        public void OnMobDetectorBodyEntered(Node body)
+        {
+            Die();
+        }
+    }
+
+
+And the *Mob*'s script.
+
+.. tabs::
+ .. code-tab:: gdscript GDScript
+
+   extends KinematicBody
+
+   # Emitted when the player jumped on the mob.
+   signal squashed
+
+   # Minimum speed of the mob in meters per second.
+   export var min_speed = 10
+   # Maximum speed of the mob in meters per second.
+   export var max_speed = 18
+
+   var velocity = Vector3.ZERO
+
+
+   func _physics_process(_delta):
+       move_and_slide(velocity)
+
+
+   func initialize(start_position, player_position):
+       translation = start_position
+       look_at(player_position, Vector3.UP)
+       rotate_y(rand_range(-PI / 4, PI / 4))
+
+       var random_speed = rand_range(min_speed, max_speed)
+       velocity = Vector3.FORWARD * random_speed
+       velocity = velocity.rotated(Vector3.UP, rotation.y)
+
+       $AnimationPlayer.playback_speed = random_speed / min_speed
+
+
+    func squash():
+       emit_signal("squashed")
+       queue_free()
+
+
+   func _on_VisibilityNotifier_screen_exited():
+       queue_free()
+
+ .. code-tab:: csharp
+
+    public class Mob : KinematicBody
+    {
+        // Emitted when the played jumped on the mob.
+        [Signal]
+        public delegate void Squashed();
+
+        // Minimum speed of the mob in meters per second
+        [Export]
+        public int MinSpeed = 10;
+        // Maximum speed of the mob in meters per second
+        [Export]
+        public int MaxSpeed = 18;
+
+        private Vector3 _velocity = Vector3.Zero;
+
+        public override void _PhysicsProcess(float delta)
+        {
+            MoveAndSlide(_velocity);
+        }
+
+        public void Initialize(Vector3 startPosition, Vector3 playerPosition)
+        {
+            Translation = startPosition;
+            LookAt(playerPosition, Vector3.Up);
+            RotateY((float)GD.RandRange(-Mathf.Pi / 4.0, Mathf.Pi / 4.0));
+
+            float randomSpeed = (float)GD.RandRange(MinSpeed, MaxSpeed);
+            _velocity = Vector3.Forward * randomSpeed;
+            _velocity = _velocity.Rotated(Vector3.Up, Rotation.y);
+
+            GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = randomSpeed / MinSpeed;
+        }
+
+        public void Squash()
+        {
+            EmitSignal(nameof(Squashed));
+            QueueFree();
+        }
+
+        public void OnVisibilityNotifierScreenExited()
+        {
+            QueueFree();
+        }
+    }
+
+.. |image0| image:: img/squash-the-creeps-final.gif
+.. |image1| image:: img/09.adding_animations/01.animation_player_dock.png
+.. |image2| image:: img/09.adding_animations/02.new_animation.png
+.. |image3| image:: img/09.adding_animations/03.float_name.png
+.. |image4| image:: img/09.adding_animations/03.timeline.png
+.. |image5| image:: img/09.adding_animations/04.autoplay_and_loop.png
+.. |image6| image:: img/09.adding_animations/05.pin_icon.png
+.. |image7| image:: img/09.adding_animations/06.animation_duration.png
+.. |image8| image:: img/09.adding_animations/07.editable_timeline.png
+.. |image9| image:: img/09.adding_animations/08.zoom_slider.png
+.. |image10| image:: img/09.adding_animations/09.creating_first_keyframe.png
+.. |image11| image:: img/09.adding_animations/10.initial_keys.png
+.. |image12| image:: img/09.adding_animations/11.moving_keys.png
+.. |image13| image:: img/09.adding_animations/12.second_keys_values.png
+.. |image14| image:: img/09.adding_animations/13.second_keys.png
+.. |image15| image:: img/09.adding_animations/14.play_button.png
+.. |image16| image:: img/09.adding_animations/15.box_select.png
+.. |image17| image:: img/09.adding_animations/16.easing_property.png
+.. |image18| image:: img/09.adding_animations/17.ease_out.png
+.. |image19| image:: img/09.adding_animations/18.ease_out_second_rotation_key.png
+.. |image20| image:: img/09.adding_animations/19.ease_in_second_translation_key.png
+.. |image21| image:: img/09.adding_animations/20.float_animation.gif
+.. |image22| image:: img/09.adding_animations/21.script_icon.png
+.. |image23| image:: img/09.adding_animations/22.merge_from_scene.png

+ 42 - 0
getting_started/first_3d_game/going_further.rst

@@ -0,0 +1,42 @@
+.. _doc_first_3d_game_going_further:
+
+Going further
+=============
+
+You can pat yourself on the back for having completed your first 3D game with
+Godot.
+
+In this series, we went over a wide range of techniques and editor features.
+Hopefully, you’ve witnessed how intuitive Godot’s scene system can be and
+learned a few tricks you can apply in your projects.
+
+But we just scratched the surface: Godot has a lot more in store for you to save
+time creating games. And you can learn all that by browsing the documentation.
+
+Where should you begin? Below, you’ll find a few pages to start exploring and
+build upon what you’ve learned so far.
+
+But before that, here’s a link to download a completed version of the project:
+`<https://github.com/GDQuest/godot-3d-dodge-the-creeps>`_.
+
+Exploring the manual
+--------------------
+
+The manual is your ally whenever you have a doubt or you’re curious about a
+feature. It does not contain tutorials about specific game genres or mechanics.
+Instead, it explains how Godot works in general. In it, you will find
+information about 2D, 3D, physics, rendering and performance, and much more.
+
+Here are the sections we recommend you to explore next:
+
+1. Read the :ref:`Scripting section <toc-scripting-core-features>` to learn essential programming features you’ll use
+   in every project.
+2. The :ref:`3D <toc-learn-features-3d>` and :ref:`Physics <toc-learn-features-physics>` sections will teach you more about 3D game creation in the
+   engine.
+3. :ref:`Inputs <toc-learn-features-inputs>` is another important one for any game project.
+
+You can start with these or, if you prefer, look at the sidebar menu on the left
+and pick your options.
+
+We hope you enjoyed this tutorial series, and we’re looking forward to seeing
+what you achieve using Godot.

BIN
getting_started/first_3d_game/img/01.game_setup/01.import_button.png


BIN
getting_started/first_3d_game/img/01.game_setup/02.browse_to_project_folder.png


BIN
getting_started/first_3d_game/img/01.game_setup/03.import_and_edit.png


BIN
getting_started/first_3d_game/img/01.game_setup/04.start_assets.png


BIN
getting_started/first_3d_game/img/01.game_setup/05.main_node.png


BIN
getting_started/first_3d_game/img/01.game_setup/06.staticbody_node.png


BIN
getting_started/first_3d_game/img/01.game_setup/07.collision_shape_warning.png


BIN
getting_started/first_3d_game/img/01.game_setup/08.create_box_shape.png


BIN
getting_started/first_3d_game/img/01.game_setup/09.box_extents.png


BIN
getting_started/first_3d_game/img/01.game_setup/10.mesh_instance.png


BIN
getting_started/first_3d_game/img/01.game_setup/11.cube_mesh.png


BIN
getting_started/first_3d_game/img/01.game_setup/12.cube_resized.png


BIN
getting_started/first_3d_game/img/01.game_setup/13.move_gizmo_y_axis.png


BIN
getting_started/first_3d_game/img/01.game_setup/14.select_mode_icon.png


BIN
getting_started/first_3d_game/img/01.game_setup/15.translation_amount.png


BIN
getting_started/first_3d_game/img/01.game_setup/16.turn_on_shadows.png


BIN
getting_started/first_3d_game/img/01.game_setup/17.project_with_light.png


BIN
getting_started/first_3d_game/img/02.player_input/01.new_scene.png


BIN
getting_started/first_3d_game/img/02.player_input/02.instantiating_the_model.png


BIN
getting_started/first_3d_game/img/02.player_input/03.scene_structure.png


BIN
getting_started/first_3d_game/img/02.player_input/04.sphere_shape.png


BIN
getting_started/first_3d_game/img/02.player_input/05.moving_the_sphere_up.png


BIN
getting_started/first_3d_game/img/02.player_input/06.toggling_visibility.png


BIN
getting_started/first_3d_game/img/02.player_input/07.adding_action.png


BIN
getting_started/first_3d_game/img/02.player_input/07.input_map_tab.png


BIN
getting_started/first_3d_game/img/02.player_input/07.project_settings.png


BIN
getting_started/first_3d_game/img/02.player_input/08.actions_list_empty.png


BIN
getting_started/first_3d_game/img/02.player_input/08.create_key_action.png


BIN
getting_started/first_3d_game/img/02.player_input/09.keyboard_key_popup.png


BIN
getting_started/first_3d_game/img/02.player_input/09.keyboard_keys.png


BIN
getting_started/first_3d_game/img/02.player_input/10.joy_axis_option.png


BIN
getting_started/first_3d_game/img/02.player_input/11.joy_axis_popup.png


BIN
getting_started/first_3d_game/img/02.player_input/12.move_inputs_mapped.png


BIN
getting_started/first_3d_game/img/02.player_input/13.joy_button_option.png


BIN
getting_started/first_3d_game/img/02.player_input/14.add_jump_button.png


BIN
getting_started/first_3d_game/img/02.player_input/14.jump_input_action.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/01.attach_script_to_player.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/02.clicking_main_tab.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/03.instance_child_scene.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/04.scene_tree_with_camera.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/05.camera_preview_checkbox.png


Неке датотеке нису приказане због велике количине промена