Skip to content

Commit

Permalink
add C# code tabs to pages under first_3d_game
Browse files Browse the repository at this point in the history
  • Loading branch information
paulloz committed Mar 19, 2021
1 parent 0a087e7 commit b7e9a08
Show file tree
Hide file tree
Showing 7 changed files with 927 additions and 37 deletions.
145 changes: 139 additions & 6 deletions getting_started/first_3d_game/03.player_movement_code.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ 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

Expand All @@ -27,6 +28,23 @@ character.

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.
Expand All @@ -40,7 +58,8 @@ we want to update and reuse its value across frames.
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.
Expand All @@ -58,6 +77,34 @@ using the global ``Input`` object, in ``_physics_process()``.
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
Expand All @@ -80,7 +127,8 @@ 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):
#...
Expand All @@ -89,6 +137,19 @@ call its ``normalize()`` method.
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.

Expand All @@ -110,9 +171,10 @@ 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):_
func _physics_process(delta):
#...
if direction != Vector3.ZERO:
#...
Expand All @@ -125,6 +187,21 @@ fall speed separately. Be sure to go back one tab so the lines are inside the
# 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 - ...``.
Expand Down Expand Up @@ -153,7 +230,8 @@ 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

Expand Down Expand Up @@ -186,6 +264,61 @@ Here is the complete ``Player.gd`` code for reference.
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
-----------------------------

Expand Down
111 changes: 106 additions & 5 deletions getting_started/first_3d_game/04.mob_scene.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ 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

Expand All @@ -112,6 +113,27 @@ the ``velocity``.
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
Expand All @@ -129,7 +151,8 @@ 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):
Expand All @@ -139,14 +162,30 @@ between ``-PI / 4`` radians and ``PI / 4`` radians.
# 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)
Expand All @@ -155,6 +194,20 @@ We start by creating a 3D vector pointing forward, multiply it by our
# 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
------------------

Expand All @@ -180,17 +233,28 @@ This will take you back to the script editor and add a new function for you,
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

Expand Down Expand Up @@ -218,6 +282,43 @@ Here is the complete ``Mob.gd`` script for reference.
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
Expand Down
Loading

0 comments on commit b7e9a08

Please sign in to comment.