Fix your first 3D game issues (#6763)

This commit is contained in:
Matthew
2023-02-22 21:47:40 -08:00
committed by GitHub
parent b7c5dfdfcc
commit 4da9f813fc
18 changed files with 67 additions and 76 deletions
@@ -9,7 +9,7 @@ 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.1.0>`__.
<https://github.com/godotengine/godot-3d-dodge-the-creeps/releases/tag/1.1.0>`__.
Once you downloaded it, extract the .zip archive on your computer. Open the
Godot project manager and click the *Import* button.
@@ -39,7 +39,7 @@ Setting up the playable area
----------------------------
We're going to create our main scene with a plain :ref:`Node <class_Node>` as its root. In the
*Scene* dock, click the *Add Node* button represented by a "+" icon in the
*Scene* dock, click the *Add Child 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).
@@ -64,8 +64,7 @@ its shape. If you click the icon, a popup appears to give you more information.
.. image:: img/01.game_setup/07.collision_shape_warning.png
To create a shape, select the :ref:`CollisionShape3D <class_CollisionShape3D>` node, head to the *Inspector*
and click the *<empty>* field next to the *Shape* property. Create a new *Box
Shape3D*.
and click the *<empty>* field next to the *Shape* property. Create a new *BoxShape3D*.
.. image:: img/01.game_setup/08.create_box_shape3D.jpg
@@ -86,7 +85,7 @@ with it. Select the ``Ground`` node and add a :ref:`MeshInstance3D <class_MeshIn
.. image:: img/01.game_setup/10.mesh_instance3d.png
In the *Inspector*, click on the field next to *Mesh* and create a `BoxMesh <class_BoxMesh>`
In the *Inspector*, click on the field next to *Mesh* and create a :ref:`BoxMesh <class_BoxMesh>`
resource to create a visible box.
.. image:: img/01.game_setup/11.box_mesh.webp
@@ -103,7 +102,7 @@ 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.
.. image:: img/01.game_setup/13.move_gizmo_y_axis.png
.. image:: img/01.game_setup/move_gizmo_y_axis.webp
.. note::
@@ -128,11 +127,11 @@ Ultimately, ``Ground``'s transform.position.y should be -1
.. image:: img/01.game_setup/ground_down1meter.webp
Let's add a directional light so our scene isn't all grey. Select the ``Main``
node and add a child node :ref:`DirectionalLight <class_DirectionalLight3D>`.
node and add a child node :ref:`DirectionalLight3D <class_DirectionalLight3D>`.
.. image:: img/01.game_setup/create_directional_light3d.webp
We need to move and rotate the :ref:`DirectionalLight <class_DirectionalLight3D>` node.
We need to move and rotate the :ref:`DirectionalLight3D <class_DirectionalLight3D>` node.
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.
@@ -23,7 +23,7 @@ character.
# How fast the player moves in meters per second.
@export var speed = 14
# The downward acceleration while in the air, in meters per second squared.
# The downward acceleration when in the air, in meters per second squared.
@export var fall_acceleration = 75
var target_velocity = Vector3.ZERO
@@ -39,7 +39,7 @@ character.
// How fast the player moves in meters per second.
[Export]
public int Speed = 14;
// The downward acceleration while in the air, in meters per second squared.
// The downward acceleration when in the air, in meters per second squared.
[Export]
public int FallAcceleration = 75;
@@ -47,7 +47,7 @@ character.
}
These are common properties for a moving body. The ``velocity`` is a :ref:`3D vector <class_Vector3>`
These are common properties for a moving body. The ``target_velocity`` is a :ref:`3D vector <class_Vector3>`
combining a speed with a direction. Here, we define it as a property because
we want to update and reuse its value across frames.
@@ -186,8 +186,8 @@ fall speed separately. Be sure to go back one tab so the lines are inside the
target_velocity.z = direction.z * speed
# Vertical Velocity
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
# Moving the Character
velocity = target_velocity
@@ -237,27 +237,27 @@ Here is the complete ``Player.gd`` code for reference.
.. tabs::
.. code-tab:: gdscript GDScript
extends CharacterBody3D
extends CharacterBody3D
# 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
# 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 target_velocity = Vector3.ZERO
var target_velocity = Vector3.ZERO
func _physics_process(delta):
var direction = 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 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()
@@ -268,8 +268,8 @@ Here is the complete ``Player.gd`` code for reference.
target_velocity.z = direction.z * speed
# Vertical Velocity
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
# Moving the Character
velocity = target_velocity
@@ -104,15 +104,15 @@ and ``max_speed``, to define a random speed range, which we will later use to de
.. tabs::
.. code-tab:: gdscript GDScript
extends CharacterBody3D
extends CharacterBody3D
# 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
# 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
func _physics_process(_delta):
func _physics_process(_delta):
move_and_slide()
.. code-tab:: csharp
@@ -150,7 +150,7 @@ The function will take a ``start_position``,the mob's spawn position, and the
We position the mob at ``start_position`` and turn it towards the player using
the ``look_at_from_position()`` method, and randomize the angle by rotating a
random amount around the Y axis. Below, ``rand_range()`` outputs a random value
random amount around the Y axis. Below, ``randf_range()`` outputs a random value
between ``-PI / 4`` radians and ``PI / 4`` radians.
.. tabs::
@@ -253,7 +253,7 @@ Here is the complete ``Mob.gd`` script for reference.
.. tabs::
.. code-tab:: gdscript GDScript
extends CharacterBody3D
extends CharacterBody3D
# Minimum speed of the mob in meters per second.
@export var min_speed = 10
@@ -11,7 +11,7 @@ you will have monsters roaming the game board.
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
a default window size of ``1152x648``. We're going to set it to ``720x540``, a
nice little box.
Go to *Project -> Project Settings*.
@@ -234,8 +234,8 @@ With this code, if no collisions occurred on a given frame, the loop won't run.
.. code-tab:: gdscript GDScript
func _physics_process(delta):
#...
# Iterate through all collisions that occurred this frame
#...
# Iterate through all collisions that occurred this frame
# in C this would be for(int i = 0; i < collisions.Count; i++)
for index in range(get_slide_collision_count()):
# We get one of the collisions with the player
@@ -288,7 +288,7 @@ the :ref:`CharacterBody3D <class_CharacterBody3D>` class and are related to
:ref:`KinematicCollision3D<class_KinematicCollision3D>` object that holds
information about where and how the collision occurred. For example, we use its
``get_collider`` property to check if we collided with a "mob" by calling
``is_in_group()`` on it: ``collision.get_collider(index).is_in_group("mob")``.
``is_in_group()`` on it: ``collision.get_collider().is_in_group("mob")``.
.. note::
@@ -75,7 +75,7 @@ a ``die()`` function that helps us put a descriptive label on the code.
queue_free()
func _on_mob_detector_body_entered(_body):
func _on_mob_detector_body_entered(body):
die()
.. code-tab:: csharp
@@ -106,7 +106,7 @@ the character should die when an enemy runs into the collider. Note that without
.. tabs::
.. code-tab:: gdscript GDScript
var player_position = $Player.transform.origin
var player_position = $Player.position
gives error because there is no $Player!
@@ -269,8 +269,8 @@ Next is ``Mob.gd``.
queue_free()
func squash():
squashed.emit()
queue_free() # Destroy this node
squashed.emit()
queue_free() # Destroy this node
.. code-tab:: csharp
using Godot;
@@ -321,6 +321,7 @@ Finally, the longest script, ``Player.gd``:
.. tabs::
.. code-tab:: gdscript GDScript
extends CharacterBody3D
signal hit
@@ -329,6 +330,7 @@ Finally, the longest script, ``Player.gd``:
@export var speed = 14
# The downward acceleration while 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.
@@ -356,14 +358,14 @@ Finally, the longest script, ``Player.gd``:
# Prevent diagonal moving fast af
if direction != Vector3.ZERO:
direction = direction.normalized()
$Pivot.look_at(position + direction,Vector3.UP)
$Pivot.look_at(position + direction, Vector3.UP)
# Ground Velocity
target_velocity.x = direction.x * speed
target_velocity.z = direction.z * speed
# Vertical Velocity
if not is_on_floor(): # If in the air, fall towards the floor
if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
target_velocity.y = target_velocity.y - (fall_acceleration * delta)
# Jumping.
@@ -60,23 +60,14 @@ By default, a theme only has one property, the *Default Font*.
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 :ref:`FontVariation <class_FontVariation>`
|image7|
Expand the :ref:`FontVariation <class_FontVariation>` 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*.
``Montserrat-Medium.ttf`` file we included in the project onto the *Default Font*.
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 is a bit small. Set the *Settings -> Default Font Size* to ``22`` pixels to increase
the text's size.
|image9|
@@ -125,7 +116,7 @@ line:
.. code-tab:: gdscript GDScript
func _on_mob_timer_timeout():
#...
#...
# We connect the mob to the score label to update the score upon squashing one.
mob.squashed.connect($UserInterface/ScoreLabel._on_Mob_squashed.bind())
@@ -200,7 +191,7 @@ child node *ColorRect*, 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
To make it span over the whole viewport, you can use the *Anchor Preset* menu in the
toolbar.
|image12|
@@ -267,7 +258,7 @@ Then, when the player gets hit, we show the overlay.
.. tabs::
.. code-tab:: gdscript GDScript
func _on_Player_hit():
func _on_player_hit():
#...
$UserInterface/Retry.show()
@@ -467,15 +458,13 @@ Here is the complete ``Main.gd`` script for reference.
.. |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.webp
.. |image8| image:: img/08.score_and_replay/06.font_data.webp
.. |image9| image:: img/08.score_and_replay/07.font_size.webp
.. |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
.. |image15| image:: img/08.score_and_replay/retry_color_picker.webp
.. |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
@@ -205,9 +205,9 @@ vector, add the following code.
#...
if direction != Vector3.ZERO:
#...
$AnimationPlayer.playback_speed = 4
$AnimationPlayer.speed_scale = 4
else:
$AnimationPlayer.playback_speed = 1
$AnimationPlayer.speed_scale = 1
.. code-tab:: csharp
@@ -217,11 +217,11 @@ vector, add the following code.
if (direction != Vector3.Zero)
{
// ...
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 4;
GetNode<AnimationPlayer>("AnimationPlayer").SpeedScale = 4;
}
else
{
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = 1;
GetNode<AnimationPlayer>("AnimationPlayer").SpeedScale = 1;
}
}
@@ -271,14 +271,14 @@ following line.
func initialize(start_position, player_position):
#...
$AnimationPlayer.playback_speed = random_speed / min_speed
$AnimationPlayer.speed_scale = random_speed / min_speed
.. code-tab:: csharp
public void Initialize(Vector3 startPosition, Vector3 playerPosition)
{
// ...
GetNode<AnimationPlayer>("AnimationPlayer").PlaybackSpeed = randomSpeed / MinSpeed;
GetNode<AnimationPlayer>("AnimationPlayer").SpeedScale = randomSpeed / MinSpeed;
}
And with that, you finished coding your first complete 3D game.
@@ -293,6 +293,7 @@ Here's the *Player* script.
.. tabs::
.. code-tab:: gdscript GDScript
extends CharacterBody3D
signal hit
@@ -568,7 +569,7 @@ And the *Mob*'s script.
}
.. |image0| image:: img/squash-the-creeps-final.gif
.. |image1| image:: img/09.adding_animations/01.animation_player_dock.png
.. |image1| image:: img/09.adding_animations/animation_player_dock.webp
.. |image2| image:: img/09.adding_animations/02.new_animation.webp
.. |image3| image:: img/09.adding_animations/03.float_name.png
.. |image4| image:: img/09.adding_animations/03.timeline.png
@@ -17,7 +17,7 @@ Where should you begin? Below, youll find a few pages to start exploring and
build upon what youve learned so far.
But before that, heres a link to download a completed version of the project:
`<https://github.com/GDQuest/godot-3d-dodge-the-creeps>`_.
`<https://github.com/godotengine/godot-3d-dodge-the-creeps>`_.
Exploring the manual
--------------------
Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+2 -2
View File
@@ -31,7 +31,7 @@ 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/>`__.
<https://github.com/godotengine/godot-3d-dodge-the-creeps>`__.
.. note::
@@ -42,7 +42,7 @@ source code here: `Squash the Creep source code
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.1.0>`__.
<https://github.com/godotengine/godot-3d-dodge-the-creeps/releases/tag/1.1.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