Write the first three parts of the 3D tutorial
164
getting_started/first_3d_game/01.game_setup.rst
Normal file
@@ -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
getting_started/first_3d_game/02.player_input.rst
Normal file
@@ -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
getting_started/first_3d_game/03.player_movement_code.rst
Normal file
@@ -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
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
BIN
getting_started/first_3d_game/img/01.game_setup/05.main_node.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
BIN
getting_started/first_3d_game/img/01.game_setup/11.cube_mesh.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
BIN
getting_started/first_3d_game/img/squash-the-creeps-final.gif
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
61
getting_started/first_3d_game/index.rst
Normal file
@@ -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
|
||||