Browse Source

Write the first three parts of the 3D tutorial

Nathan Lovato 4 years ago
parent
commit
e32454978c
53 changed files with 683 additions and 0 deletions
  1. 164 0
      getting_started/first_3d_game/01.game_setup.rst
  2. 177 0
      getting_started/first_3d_game/02.player_input.rst
  3. 280 0
      getting_started/first_3d_game/03.player_movement_code.rst
  4. BIN
      getting_started/first_3d_game/img/01.game_setup/01.import_button.png
  5. BIN
      getting_started/first_3d_game/img/01.game_setup/02.browse_to_project_folder.png
  6. BIN
      getting_started/first_3d_game/img/01.game_setup/03.import_and_edit.png
  7. BIN
      getting_started/first_3d_game/img/01.game_setup/04.start_assets.png
  8. BIN
      getting_started/first_3d_game/img/01.game_setup/05.main_node.png
  9. BIN
      getting_started/first_3d_game/img/01.game_setup/06.staticbody_node.png
  10. BIN
      getting_started/first_3d_game/img/01.game_setup/07.collision_shape_warning.png
  11. BIN
      getting_started/first_3d_game/img/01.game_setup/08.create_box_shape.png
  12. BIN
      getting_started/first_3d_game/img/01.game_setup/09.box_extents.png
  13. BIN
      getting_started/first_3d_game/img/01.game_setup/10.mesh_instance.png
  14. BIN
      getting_started/first_3d_game/img/01.game_setup/11.cube_mesh.png
  15. BIN
      getting_started/first_3d_game/img/01.game_setup/12.cube_resized.png
  16. BIN
      getting_started/first_3d_game/img/01.game_setup/13.move_gizmo_y_axis.png
  17. BIN
      getting_started/first_3d_game/img/01.game_setup/14.select_mode_icon.png
  18. BIN
      getting_started/first_3d_game/img/01.game_setup/15.translation_amount.png
  19. BIN
      getting_started/first_3d_game/img/01.game_setup/16.turn_on_shadows.png
  20. BIN
      getting_started/first_3d_game/img/01.game_setup/17.project_with_light.png
  21. BIN
      getting_started/first_3d_game/img/02.player_input/01.new_scene.png
  22. BIN
      getting_started/first_3d_game/img/02.player_input/02.instantiating_the_model.png
  23. BIN
      getting_started/first_3d_game/img/02.player_input/03.scene_structure.png
  24. BIN
      getting_started/first_3d_game/img/02.player_input/04.sphere_shape.png
  25. BIN
      getting_started/first_3d_game/img/02.player_input/05.moving_the_sphere_up.png
  26. BIN
      getting_started/first_3d_game/img/02.player_input/06.toggling_visibility.png
  27. BIN
      getting_started/first_3d_game/img/02.player_input/07.adding_action.png
  28. BIN
      getting_started/first_3d_game/img/02.player_input/07.input_map_tab.png
  29. BIN
      getting_started/first_3d_game/img/02.player_input/07.project_settings.png
  30. BIN
      getting_started/first_3d_game/img/02.player_input/08.actions_list_empty.png
  31. BIN
      getting_started/first_3d_game/img/02.player_input/08.create_key_action.png
  32. BIN
      getting_started/first_3d_game/img/02.player_input/09.keyboard_key_popup.png
  33. BIN
      getting_started/first_3d_game/img/02.player_input/09.keyboard_keys.png
  34. BIN
      getting_started/first_3d_game/img/02.player_input/10.joy_axis_option.png
  35. BIN
      getting_started/first_3d_game/img/02.player_input/11.joy_axis_popup.png
  36. BIN
      getting_started/first_3d_game/img/02.player_input/12.move_inputs_mapped.png
  37. BIN
      getting_started/first_3d_game/img/02.player_input/13.joy_button_option.png
  38. BIN
      getting_started/first_3d_game/img/02.player_input/14.add_jump_button.png
  39. BIN
      getting_started/first_3d_game/img/02.player_input/14.jump_input_action.png
  40. BIN
      getting_started/first_3d_game/img/03.player_movement_code/01.attach_script_to_player.png
  41. BIN
      getting_started/first_3d_game/img/03.player_movement_code/02.clicking_main_tab.png
  42. BIN
      getting_started/first_3d_game/img/03.player_movement_code/03.instance_child_scene.png
  43. BIN
      getting_started/first_3d_game/img/03.player_movement_code/04.scene_tree_with_camera.png
  44. BIN
      getting_started/first_3d_game/img/03.player_movement_code/05.camera_preview_checkbox.png
  45. BIN
      getting_started/first_3d_game/img/03.player_movement_code/06.two_viewports.png
  46. BIN
      getting_started/first_3d_game/img/03.player_movement_code/07.camera_preview_checkbox.png
  47. BIN
      getting_started/first_3d_game/img/03.player_movement_code/08.camera_moved.png
  48. BIN
      getting_started/first_3d_game/img/03.player_movement_code/09.camera_rotated.png
  49. BIN
      getting_started/first_3d_game/img/03.player_movement_code/10.camera_perspective.png
  50. BIN
      getting_started/first_3d_game/img/03.player_movement_code/11.camera_orthographic.png
  51. BIN
      getting_started/first_3d_game/img/squash-the-creeps-final.gif
  52. 61 0
      getting_started/first_3d_game/index.rst
  53. 1 0
      index.rst

+ 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 Ctrl a (or Cmd a on MacOS).
+
+|image5|
+
+Save the scene as ``Main.tscn`` by pressing Ctrl s (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 a *CollisionShape* node as their child 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 Ctrl key down to turn on grid snapping (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_up``,
+``move_down``, 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

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

@@ -0,0 +1,280 @@
+.. _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.
+
+::
+
+   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
+
+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’re going to build upon its value frame after frame.
+
+.. 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()``.
+
+::
+
+   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_down"):
+           # 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_up"):
+           direction.z -= 1
+
+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.
+
+::
+
+   #func _physics_process(delta):
+       #...
+
+       if direction.length() > 0:
+           direction = direction.normalized()
+           $Pivot.look_at(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.
+
+::
+
+       #if direction.length() > 0:
+           #direction = direction.normalized()
+           #$Pivot.look_at(translation + direction, Vector3.UP)
+
+       # 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)
+
+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.
+
+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.
+
+::
+
+   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_down"):
+           direction.z += 1
+       if Input.is_action_pressed("move_up"):
+           direction.z -= 1
+
+       if direction.length() > 0:
+           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)
+
+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 Ctrl 2 (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 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

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


BIN
getting_started/first_3d_game/img/03.player_movement_code/06.two_viewports.png


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


BIN
getting_started/first_3d_game/img/03.player_movement_code/08.camera_moved.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/09.camera_rotated.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/10.camera_perspective.png


BIN
getting_started/first_3d_game/img/03.player_movement_code/11.camera_orthographic.png


BIN
getting_started/first_3d_game/img/squash-the-creeps-final.gif


+ 61 - 0
getting_started/first_3d_game/index.rst

@@ -0,0 +1,61 @@
+Your first 3D game
+==================
+
+In this step-by-step tutorial series, you will create your first complete 3D
+game with Godot. By the end of the series, you will have a simple yet finished
+project of your own like the animated gif below.
+
+|image0|
+
+The game we’ll code here is similar to :ref:`doc_your_first_game`, with a twist:
+you can now jump and your goal is to squash the creeps. This way, you will both
+**recognize patterns** you learned in the previous tutorial and **build upon
+them** with new code and features.
+
+You will learn to:
+
+- Work with 3D coordinates with a jumping mechanic.
+- Use kinematic bodies to move 3D characters and detect when and how they
+  collide.
+- Use physics layers and a group to detect interactions with specific entities.
+- Code basic procedural gameplay by instancing monsters at regular time
+  intervals.
+- Design a movement animation and change its speed at run-time.
+- Draw a simple interface on a 3D game.
+
+And more.
+
+This tutorial is for beginners who followed the complete getting started series.
+We’ll start slow with detailed instructions and shorten them as we do similar
+steps. If you’re an experienced programmer, you can browse the complete demo’s
+source code here: `Squash the Creep source code
+<https://github.com/GDQuest/godot-3d-dodge-the-creeps/>`__.
+
+.. note::
+
+    You can follow this series without having done the 2D one. However, if
+    you’re new to game development, we recommend you to start with 2D. 3D game
+    code is always more complex and the 2D series will give you foundations to
+    follow along more comfortably.
+
+We prepared some game assets so we can jump straight to the code. You can
+download them here: `Squash the Creeps assets
+<https://github.com/GDQuest/godot-3d-dodge-the-creeps/releases/tag/1.0.0>`__.
+
+We will first work on a basic prototype for the player’s movement. We will then
+add the monsters that we’ll spawn randomly around the screen. After that, we’ll
+implement the jump and squashing mechanic before refining the game with some
+nice animation. We’ll wrap up with the score and the retry screen.
+
+Contents
+--------
+
+.. toctree::
+   :maxdepth: 1
+   :name: toc-learn-introduction
+
+   01.game_setup
+   02.player_input
+   03.player_movement_code
+
+.. |image0| image:: img/squash-the-creeps-final.gif

+ 1 - 0
index.rst

@@ -67,6 +67,7 @@ The main documentation for the site is organized into the following sections:
 
    getting_started/introduction/index
    getting_started/step_by_step/index
+   getting_started/first_3d_game/index
 
 
 .. The sections below are sorted alphabetically. Please keep them that way.