From fff6a367f1bbdfc21c49b3123d2020c4d073a243 Mon Sep 17 00:00:00 2001 From: Chris Bradfield Date: Tue, 1 Aug 2023 14:12:03 -0700 Subject: [PATCH] updates --- docs/4.x/2d/2d_shooting/index.html | 40 ++++++----- docs/4.x/2d/8_direction/index.html | 38 +++++----- docs/4.x/2d/car_steering/index.html | 32 +++++---- docs/4.x/2d/enter_exit_screen/index.html | 24 ++++--- docs/4.x/2d/grid_movement/index.html | 36 +++++----- docs/4.x/2d/grid_pathfinding/index.html | 46 ++++++------ docs/4.x/2d/index.html | 24 ++++--- docs/4.x/2d/moving_platforms/index.html | 24 ++++--- docs/4.x/2d/multi_target_camera/index.html | 34 ++++----- docs/4.x/2d/platform_character/index.html | 28 ++++---- docs/4.x/2d/screen_wrap/index.html | 24 ++++--- docs/4.x/2d/topdown_movement/index.html | 24 ++++--- docs/4.x/2d/using_ysort/index.html | 38 +++++----- docs/4.x/3d/3d_align_surface/index.html | 30 ++++---- docs/4.x/3d/3d_sphere_car/index.html | 42 +++++------ docs/4.x/3d/basic_fps/index.html | 28 ++++---- .../3d/characterbody3d_examples/index.html | 32 +++++---- docs/4.x/3d/click_to_move/index.html | 38 +++++----- docs/4.x/3d/healthbars/index.html | 48 +++++++------ docs/4.x/3d/index.html | 24 ++++--- docs/4.x/3d/interpolated_camera/index.html | 24 ++++--- docs/4.x/3d/rolling_cube/index.html | 36 +++++----- docs/4.x/3d/shooting_raycasts/index.html | 24 ++++--- docs/4.x/3d/spaceship/index.html | 40 ++++++----- docs/4.x/404.html | 4 +- docs/4.x/ai/homing_missile/index.html | 32 +++++---- docs/4.x/ai/index.html | 24 ++++--- docs/4.x/animation/index.html | 24 ++++--- .../spritesheet_animation/index.html | 40 ++++++----- docs/4.x/audio/audio_manager/index.html | 32 +++++---- docs/4.x/audio/index.html | 24 ++++--- docs/4.x/basics/file_io/index.html | 24 ++++--- docs/4.x/basics/getting_nodes/index.html | 32 +++++---- docs/4.x/basics/index.html | 24 ++++--- docs/4.x/basics/migrating/index.html | 24 ++++--- docs/4.x/basics/node_communication/index.html | 40 ++++++----- docs/4.x/basics/tree_ready_order/index.html | 28 ++++---- .../4.x/basics/understanding_delta/index.html | 36 +++++----- docs/4.x/categories/index.html | 24 ++++--- docs/4.x/g101/3d/101_3d_01/index.html | 56 ++++++++------- docs/4.x/g101/3d/101_3d_02/index.html | 38 +++++----- docs/4.x/g101/3d/101_3d_03/index.html | 44 ++++++------ docs/4.x/g101/3d/index.html | 24 ++++--- docs/4.x/g101/gdscript/gdscript_01/index.html | 38 +++++----- docs/4.x/g101/gdscript/index.html | 24 ++++--- docs/4.x/g101/index.html | 28 ++++---- docs/4.x/g101/start/101_01/index.html | 28 ++++---- docs/4.x/g101/start/101_02/index.html | 40 ++++++----- docs/4.x/g101/start/101_03/index.html | 40 ++++++----- docs/4.x/g101/start/index.html | 28 ++++---- .../4.x/games/first_2d/first_2d_01/index.html | 32 +++++---- .../4.x/games/first_2d/first_2d_02/index.html | 44 ++++++------ .../4.x/games/first_2d/first_2d_03/index.html | 36 +++++----- .../4.x/games/first_2d/first_2d_04/index.html | 28 ++++---- .../4.x/games/first_2d/first_2d_05/index.html | 28 ++++---- .../4.x/games/first_2d/first_2d_06/index.html | 30 ++++---- .../4.x/games/first_2d/first_2d_07/index.html | 28 ++++---- .../4.x/games/first_2d/first_2d_08/index.html | 24 ++++--- .../4.x/games/first_2d/first_2d_09/index.html | 38 +++++----- .../4.x/games/first_2d/first_2d_10/index.html | 24 ++++--- .../games/first_2d/first_2d_end/index.html | 24 ++++--- docs/4.x/games/first_2d/index.html | 28 ++++---- docs/4.x/games/index.html | 24 ++++--- docs/4.x/images/amazon_button.png | Bin 0 -> 30697 bytes docs/4.x/images/book_ad.png | Bin 0 -> 365540 bytes docs/4.x/images/cover_01.png | Bin 0 -> 180296 bytes docs/4.x/index.html | 28 ++++---- docs/4.x/index.json | 2 +- docs/4.x/index.xml | 4 +- docs/4.x/input/custom_actions/index.html | 44 ++++++++++++ docs/4.x/input/index.html | 26 +++---- docs/4.x/input/index.xml | 5 +- docs/4.x/input/input_actions/index.html | 24 ++++--- docs/4.x/input/mouse_capture/index.html | 24 ++++--- docs/4.x/input/mouse_input/index.html | 24 ++++--- docs/4.x/input/multi_unit_select/index.html | 32 +++++---- docs/4.x/kyn/index.html | 24 ++++--- docs/4.x/kyn/raycast2d/index.html | 38 +++++----- docs/4.x/math/dot_cross_product/index.html | 38 +++++----- docs/4.x/math/index.html | 24 ++++--- docs/4.x/math/interpolation/index.html | 24 ++++--- docs/4.x/math/transforms/index.html | 48 +++++++------ docs/4.x/physics/asteroids_physics/index.html | 32 +++++---- .../4.x/physics/character_vs_rigid/index.html | 40 ++++++----- docs/4.x/physics/index.html | 26 +++---- docs/4.x/physics/index.xml | 4 +- .../physics/rigidbody_drag_drop/index.html | 24 ++++--- .../physics/smooth_rigid_rotate/index.html | 28 ++++++++ docs/4.x/recent/index.html | 24 ++++--- docs/4.x/sitemap.xml | 2 +- docs/4.x/tags/index.html | 24 ++++--- docs/4.x/ui/index.html | 24 ++++--- docs/4.x/ui/level_select/index.html | 42 +++++------ docs/index.html | 4 +- src-4/content/2D/2d_align_surface.md | 3 +- src-4/content/2D/2d_shooting.md | 2 +- src-4/content/2D/multi_target_camera.md | 2 +- src-4/content/_index.md | 2 +- src-4/content/ai/tilemap_navigation.md | 12 ++++ src-4/content/g101/3d/101_3d_01.md | 12 ++-- src-4/content/g101/3d/101_3d_02.md | 12 ++-- src-4/content/g101/3d/101_3d_03.md | 12 ++-- src-4/content/g101/3d/101_3d_04.md | 13 ++-- src-4/content/g101/gdscript/gdscript_01.md | 14 ++-- src-4/content/g101/gdscript/gdscript_02.md | 17 ++++- src-4/content/games/first_2d/first_2d_02.md | 2 +- src-4/content/input/custom_actions.md | 68 ++++++++++++++++++ src-4/content/physics/character_vs_rigid.md | 3 + src-4/content/physics/smooth_rigid_rotate.md | 44 ++++++++++++ .../layouts/partials/book-ad.html | 12 ++++ .../layouts/partials/menu.html | 2 + .../static/images/amazon_button.png | Bin 0 -> 30697 bytes .../static/images/book_ad.png | Bin 0 -> 365540 bytes .../static/images/cover_01.png | Bin 0 -> 180296 bytes 114 files changed, 1637 insertions(+), 1244 deletions(-) create mode 100644 docs/4.x/images/amazon_button.png create mode 100644 docs/4.x/images/book_ad.png create mode 100644 docs/4.x/images/cover_01.png create mode 100644 docs/4.x/input/custom_actions/index.html create mode 100644 docs/4.x/physics/smooth_rigid_rotate/index.html create mode 100644 src-4/content/ai/tilemap_navigation.md create mode 100644 src-4/content/input/custom_actions.md create mode 100644 src-4/content/physics/smooth_rigid_rotate.md create mode 100644 themes_shared/hugo-theme-relearn-main/layouts/partials/book-ad.html create mode 100644 themes_shared/hugo-theme-relearn-main/static/images/amazon_button.png create mode 100644 themes_shared/hugo-theme-relearn-main/static/images/book_ad.png create mode 100644 themes_shared/hugo-theme-relearn-main/static/images/cover_01.png diff --git a/docs/4.x/2d/2d_shooting/index.html b/docs/4.x/2d/2d_shooting/index.html index 5dac759f..c66dde5b 100644 --- a/docs/4.x/2d/2d_shooting/index.html +++ b/docs/4.x/2d/2d_shooting/index.html @@ -1,11 +1,11 @@ -Shooting projectiles :: Godot 4 Recipes - +Shooting projectiles :: Godot 4 Recipes +

Shooting projectiles

Problem

You want to shoot projectiles from your player/mob/etc..

Solution

Setting up the bullet

First, we’ll set up a “bullet” object that we can instance. Here are the nodes we’ll use:

 Area2D: Bullet
      Sprite2D
      CollisionShape2D
-

For the Sprite2D’s texture, you can use any image you like. Here’s an example one:

alt -alt

Set up the nodes and configure the sprite and collision shape. If your texture is oriented pointing up, like the one above, make sure to rotate the Sprite node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.

Add a script and connect the Area2D’s body_entered signal.

extends Area2D
+

For the Sprite2D’s texture, you can use any image you like. Here’s an example one:

alt +alt

Set up the nodes and configure the sprite and collision shape. If your texture is oriented pointing up, like the one above, make sure to rotate the Sprite node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.

Add a script and connect the Area2D’s body_entered signal.

extends Area2D
 
 var speed = 750
 
@@ -16,32 +16,34 @@
     if body.is_in_group("mobs"):
         body.queue_free()
     queue_free()
-

For this example, we’ll remove the bullet if it hits anything at all. We’ll also delete anything tagged in the “mobs” group that it hits.

Shooting

We need to set up a spawn location for the bullets. Add a Marker2D and place it where you want the bullets to spawn. Here’s an example, placed at the barrel of the gun. I’ve named it “Muzzle”.

alt -alt

Notice that as the player rotates, the Muzzle’s transform remains oriented the same way relative to the gun. This will be very convenient when spawning the bullets, as they can use the transform to get the proper position and direction. We just set the new bullet’s transform equal to the muzzle’s.

Tip

This will work for any character type, not just the “rotate-and-move” style shown here. Just attach the Marker2D where you want the bullets to spawn.

In the character’s script we add a variable to hold the bullet scene for instancing:

export var Bullet : PackedScene
+

For this example, we’ll remove the bullet if it hits anything at all. We’ll also delete anything tagged in the “mobs” group that it hits.

Shooting

We need to set up a spawn location for the bullets. Add a Marker2D and place it where you want the bullets to spawn. Here’s an example, placed at the barrel of the gun. I’ve named it “Muzzle”.

alt +alt

Notice that as the player rotates, the Muzzle’s transform remains oriented the same way relative to the gun. This will be very convenient when spawning the bullets, as they can use the transform to get the proper position and direction. We just set the new bullet’s transform equal to the muzzle’s.

Tip

This will work for any character type, not just the “rotate-and-move” style shown here. Just attach the Marker2D where you want the bullets to spawn.

In the character’s script we add a variable to hold the bullet scene for instancing:

@export var Bullet : PackedScene
 

And check for our defined input action:

    if Input.is_action_just_pressed("shoot"):
         shoot()
 

Now in our shoot() function we can instance a bullet and add it to the tree. A common mistake is to add the bullet as a child of the player:

func shoot():
     var b = Bullet.instantiate()
     add_child(b)
     b.transform = $Muzzle.transform
-

The problem here is that since the bullets are children of the player, they are affected when the player moves or rotates.

alt -alt

To fix this, we should make sure the bullets are added to the world instead. In this case, we’ll use owner, which refers to the root node of the scene the player is in. Note that we also need to use the muzzle’s global transform, or else the bullet would not be where we expected.

func shoot():
+

The problem here is that since the bullets are children of the player, they are affected when the player moves or rotates.

alt +alt

To fix this, we should make sure the bullets are added to the world instead. In this case, we’ll use owner, which refers to the root node of the scene the player is in. Note that we also need to use the muzzle’s global transform, or else the bullet would not be where we expected.

func shoot():
     var b = Bullet.instantiate()
     owner.add_child(b)
     b.transform = $Muzzle.global_transform
-

alt -alt

Download This Project

Download the project code here: https://github.com/godotrecipes/2d_shooting

+ + \ No newline at end of file diff --git a/docs/4.x/2d/8_direction/index.html b/docs/4.x/2d/8_direction/index.html index aefc9a1f..54b4b3ae 100644 --- a/docs/4.x/2d/8_direction/index.html +++ b/docs/4.x/2d/8_direction/index.html @@ -1,11 +1,11 @@ -8-Directional Movement/Animation :: Godot 4 Recipes - +8-Directional Movement/Animation :: Godot 4 Recipes +

8-Directional Movement/Animation

Problem

You need a 2D character that has 8-directional movement, including animation.

Solution

For our example, we’ll use the Isometric Mini-Crusader, which contains 8-directional animations for idle, run, attack, and several other states.

alt -alt

The animations are organized in folders, with a separate image for each frame. We’ll use an AnimatedSprite2D and we’ll name each animation based on its direction. For example, idle0 pointing to the right and going clockwise to idle7.

When our character moves, it will pick an animation based on the direction of movement:

alt -alt

We’ll use the mouse to move - the character will always face the mouse and run in that direction when we click the mouse button.

To choose which animation to play, we need to get the mouse direction and map it to this same range of 0-7. get_local_mouse_position() gives us the position of the mouse relative to the character. We can then use snappedf() to snap the angle of the mouse vector to the closest multiple of 45° (PI/4 radians) giving the following result:

alt -alt

Divide each value by 45° (PI/4 radians), and we have:

alt -alt

Finally, we need to map the resulting range to 0-7 using the wrapi() function, and we’ll have our correct values. Adding that value to the end of the animation name (“idle”, “run”, etc) gives us the correct animation:

func _physics_process(delta):
+

8-Directional Movement/Animation

Problem

You need a 2D character that has 8-directional movement, including animation.

Solution

For our example, we’ll use the Isometric Mini-Crusader, which contains 8-directional animations for idle, run, attack, and several other states.

alt +alt

The animations are organized in folders, with a separate image for each frame. We’ll use an AnimatedSprite2D and we’ll name each animation based on its direction. For example, idle0 pointing to the right and going clockwise to idle7.

When our character moves, it will pick an animation based on the direction of movement:

alt +alt

We’ll use the mouse to move - the character will always face the mouse and run in that direction when we click the mouse button.

To choose which animation to play, we need to get the mouse direction and map it to this same range of 0-7. get_local_mouse_position() gives us the position of the mouse relative to the character. We can then use snappedf() to snap the angle of the mouse vector to the closest multiple of 45° (PI/4 radians) giving the following result:

alt +alt

Divide each value by 45° (PI/4 radians), and we have:

alt +alt

Finally, we need to map the resulting range to 0-7 using the wrapi() function, and we’ll have our correct values. Adding that value to the end of the animation name (“idle”, “run”, etc) gives us the correct animation:

func _physics_process(delta):
     current_animation = "idle"
 
     var mouse = get_local_mouse_position()
@@ -17,8 +17,8 @@
         velocity = mouse.normalized() * speed
         move_and_slide()
     $AnimatedSprite2D.animation = current_animation + str(a)
-

Testing the movement, we see this:

alt -alt

Keyboard input

If you’re using keyboard controls instead of mouse, you can get the angle of movement based on which keys are being held. The rest of the process works in the same way.

func _process(delta):
+

Testing the movement, we see this:

alt +alt

Keyboard input

If you’re using keyboard controls instead of mouse, you can get the angle of movement based on which keys are being held. The rest of the process works in the same way.

func _process(delta):
     current_animation = "idle"
     var input_dir = Input.get_vector("left", "right", "up", "down")
     if input_dir.length() != 0:
@@ -31,15 +31,17 @@
 

Download This Project

Download the project code here: https://github.com/godotrecipes/8_direction_animation

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/car_steering/index.html b/docs/4.x/2d/car_steering/index.html index 6043bd69..cc474836 100644 --- a/docs/4.x/2d/car_steering/index.html +++ b/docs/4.x/2d/car_steering/index.html @@ -1,5 +1,5 @@ -Car steering :: Godot 4 Recipes - +Car steering :: Godot 4 Recipes +

Car steering

Problem

You need to create a 2D top-down car controller.

Solution

When approaching this problem, beginners often wind up creating something that handles nothing like a real car. Some common mistakes you’ll find in amateur car games:

  • A car doesn’t rotate around its center. Put another way, a car’s rear wheels don’t slide side-to-side. (Unless it’s drifting, but we’ll talk about that later.)
  • A car can only turn when it’s moving - it can’t spin in place.
  • A car isn’t a train; it’s not on rails. Turning at high speeds should involve some sliding (drifting).

There are many approaches to 2D car physics, mainly depending on how “realistic” you want to be. For this solution, we’re going for an “arcade” level of realism, meaning we’ll prioritize action over realism.

Note

The method below is based on the algorithm found here: http://engineeringdotnet.blogspot.com/2010/04/simple-2d-car-physics-in-games.html

The recipe below is broken into 5 parts, each adding a different feature to the car’s movement. Feel free to mix-and-match for your needs.

Scene setup

Here’s the car scene setup:

 CharacterBody2D
      Sprite2D
@@ -49,8 +49,8 @@
     move_and_slide()
 

Now when you run, the car should gradually increase its speed. Careful: we don’t have any way to slow down yet!

Part 3: Friction/drag

A car experiences two different deceleration forces: friction and drag.

  • Friction is the force applied by the ground. It’s very high if driving on sand, but very low if driving on ice. Friction is proportional to velocity - the faster you’re going the stronger the force.

  • Drag is the force resulting from wind resistance. It’s based on the car’s cross-section - a large truck or van experiences more drag than a sleek race car. Drag is proportional to the velocity squared.

This means that friction is more significant when moving slowly, but drag becomes dominant at high speeds. We’ll add both of these forces to our calculation. As a bonus, the values of these quantities will also give our car a maximum speed - the point where the force from the engine can’t overcome the drag force any longer.

Here are our starting values for these quantities:

var friction = -55
 var drag = -0.06
-

As you can see in this graph, these values mean that at a speed of 600 the drag force overcomes the friction force.

alt -alt

You can play with the values here to see how they change: +

As you can see in this graph, these values mean that at a speed of 600 the drag force overcomes the friction force.

alt +alt

You can play with the values here to see how they change: https://www.desmos.com/calculator/e4ayu3xkip

In _physics_process() we’ll call a function to calculate the current friction and apply it to the acceleration force.

func _physics_process(delta):
     acceleration = Vector2.ZERO
     get_input()
@@ -100,19 +100,21 @@
     if d < 0:
         velocity = -new_heading * min(velocity.length(), max_speed_reverse)
     rotation = new_heading.angle()
-

Here, we select which traction value to use and apply lerp() to the velocity.

Adjustments

At this point, we have a large number of settings that control the car’s behavior. Adjusting them can drastically change how the car drives. To make experimenting with different values easier, download the project for this recipe below. When you run the game, you’ll see a set of sliders you can use to change the car’s behavior as you drive (press <Tab> to show/hide the slider panel).

alt -alt

Download This Project

Download the project code here: https://github.com/godotrecipes/2d_car_steering

+ + \ No newline at end of file diff --git a/docs/4.x/2d/enter_exit_screen/index.html b/docs/4.x/2d/enter_exit_screen/index.html index 22c8f65c..aac3ce32 100644 --- a/docs/4.x/2d/enter_exit_screen/index.html +++ b/docs/4.x/2d/enter_exit_screen/index.html @@ -1,5 +1,5 @@ -Entering/Exiting the screen :: Godot 4 Recipes - +Entering/Exiting the screen :: Godot 4 Recipes +

Entering/Exiting the screen

Problem

You want to detect when an object enters or exits the screen.

Solution

The engine provides a node for this: VisibleOnScreenNotifier2D. Attach this node to your object, and you’ll be able to use its screen_entered and screen_exited signals. *

Example 1

Consider a projectile that travels in a straight line after it’s fired. If we continue firing, eventually we’ll have a large number of objects for the engine to track, event though they’re offscreen, which can cause lag.

Here’s the movement code for the projectile:

extends Area2D
@@ -25,15 +25,17 @@
 
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/grid_movement/index.html b/docs/4.x/2d/grid_movement/index.html index 65d73653..db0db147 100644 --- a/docs/4.x/2d/grid_movement/index.html +++ b/docs/4.x/2d/grid_movement/index.html @@ -1,5 +1,5 @@ -Grid-based movement :: Godot 4 Recipes - +Grid-based movement :: Godot 4 Recipes +

Grid-based movement

Problem

You need a 2D character that moves in a grid pattern.

Solution

Grid- or tile-based movement means the character’s position is restricted. They can only stand on a particular tile - never between two tiles.

Character setup

Here are the nodes we’ll use for the player:

  • Area2D (“Player”): Using an Area2D means we can detect overlap (for picking up objects or colliding with enemies).
    • Sprite2D: You can use a sprite sheet here (we’ll set up the animation below).
    • CollisionShape2D: Don’t make the hitbox too big. Since the player will be standing on the center of a tile, overlaps will be from the center.
    • RayCast2D: For checking if movement is possible in the given direction.
    • AnimationPlayer: For playing the character’s walk animation(s).

Add some input actions to the Input Map. We’ll use “up”, “down”, “left”, and “right” for this example.

Basic movement

We’ll start by setting up the tile-by-tile movement, without any animations or interpolation.

extends Area2D
@@ -19,16 +19,16 @@
 
 func move(dir):
     position += inputs[dir] * tile_size
-

Here’s the actual movement code. When an input event occurs, we check the four directions to see which one matched, then pass it to move() to change the position.

alt -alt

Collision

Now we can add some obstacles. You can add StaticBody2Ds to manually add some obstacles (enable snapping to make sure they’re aligned with the grid) or use a TileMap (with collisions defined), as in the example below.

We’ll use the RayCast2D to determine whether a move to the next tile is allowed.

onready var ray = $RayCast2D
+

Here’s the actual movement code. When an input event occurs, we check the four directions to see which one matched, then pass it to move() to change the position.

alt +alt

Collision

Now we can add some obstacles. You can add StaticBody2Ds to manually add some obstacles (enable snapping to make sure they’re aligned with the grid) or use a TileMap (with collisions defined), as in the example below.

We’ll use the RayCast2D to determine whether a move to the next tile is allowed.

onready var ray = $RayCast2D
 
 func move(dir):
     ray.target_position = inputs[dir] * tile_size
     ray.force_raycast_update()
     if !ray.is_colliding():
         position += inputs[dir] * tile_size
-

When changing a raycast’s target_position property, the physics engine won’t recalculate its collisions until the next physics frame. force_raycast_update() lets you update the ray’s state immediately. If it’s not colliding, then we allow the move.

alt -alt

Note

Another common method is to use 4 separate raycasts, one for each direction.

Animating movement

Lastly we can interpolate the position between tiles, giving a smooth feel to the movement. We’ll use the Tween node to animate the position property.


+

When changing a raycast’s target_position property, the physics engine won’t recalculate its collisions until the next physics frame. force_raycast_update() lets you update the ray’s state immediately. If it’s not colliding, then we allow the move.

alt +alt

Note

Another common method is to use 4 separate raycasts, one for each direction.

Animating movement

Lastly we can interpolate the position between tiles, giving a smooth feel to the movement. We’ll use the Tween node to animate the position property.


 var animation_speed = 3
 var moving = false
 

Add a reference to the Tween node and a variable to set our movement speed.

func _unhandled_input(event):
@@ -48,19 +48,21 @@
         moving = true
         await tween.finished
         moving = false
-

alt -alt

Experiment with different tween transitions for different movement effects.

Download This Project

Download the project code here: https://github.com/godotrecipes/2d_grid_movement/

+ + \ No newline at end of file diff --git a/docs/4.x/2d/grid_pathfinding/index.html b/docs/4.x/2d/grid_pathfinding/index.html index 98315bfb..b0eaf350 100644 --- a/docs/4.x/2d/grid_pathfinding/index.html +++ b/docs/4.x/2d/grid_pathfinding/index.html @@ -1,5 +1,5 @@ -Pathfinding on a 2D Grid :: Godot 4 Recipes - +Pathfinding on a 2D Grid :: Godot 4 Recipes +

Pathfinding on a 2D Grid

Problem

You have a grid-based environment and you’d like to set up pathfinding to allow navigation.

Solution

Godot provides a number of methods for pathfinding. For this recipe, we’ll consider the A* algorithm.

About A*

A* is a widely-used algorithm for finding the shortest path between two points. It can be used in any graph-based data structure, not just a grid.

AStarGrid2D is a specialized version of Godot’s more generic AStar2D class. Because it’s specialized for using with a grid, it’s quicker and easier to set up because you don’t have to manually add all the individual grid cells and their connections.

Setting up the Grid

The most important configuration decision is the size of the cells and the size of the grid itself. We’ll use (64, 64) for this example, and we’ll use the window size to determine how many cells fit on the screen, but everything will work the same regardless of cell size.

Add this code to a Node2D.

extends Node2D
 
@@ -29,17 +29,17 @@
         draw_line(Vector2(0, y * cell_size.y),
             Vector2(grid_size.x * cell_size.x, y * cell_size.y),
             Color.DARK_GRAY, 2.0)
-

This gives us a nice visual of the grid:

alt -alt

Drawing the Path

In order to find a path, we need a start and end point. Add these variables at the top of the script:

var start = Vector2i.ZERO
+

This gives us a nice visual of the grid:

alt +alt

Drawing the Path

In order to find a path, we need a start and end point. Add these variables at the top of the script:

var start = Vector2i.ZERO
 var end = Vector2i(5, 5)
 

And a couple of lines in _draw() to show them:

    draw_rect(Rect2(start * cell_size, cell_size), Color.GREEN_YELLOW)
     draw_rect(Rect2(end * cell_size, cell_size), Color.ORANGE_RED)
 

We can find the path between the two points using the get_point_path() method, but we also need to visualize it. We can use a Line2D, so add one to the scene.

Here’s how we can get the path, and add the resulting points to the Line2D:

func update_path():
     $Line2D.points = PackedVector2Array(astar_grid.get_point_path(start, end))
-

Here’s the result:

alt -alt

Note that we have a diagonal line between the two points. This is because, by default, the path will use diagonals. This can be modified by changing the diagonal_mode:

  • DIAGONAL_MODE_ALWAYS - The default value, uses diagonals.
  • DIAGONAL_MODE_NEVER - All movement is orthogonal.
  • DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE - This allows diagonals, but prevents the path going “between” diagonally placed obstacles.
  • DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES - This allows diagonals only in “open” areas, not near obstacles.

Modifying this property can give you very different results, so make sure to experiment based on your setup. Let’s add this in the initialize_grid() function:

astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
-

Now we only have orthogonal moves:

alt -alt

Adding Obstacles

We can also add obstacles to the grid. By marking a cell as “solid”, the path will not include that cell. A cell can be toggled solid/not solid by using the set_point_solid() function.

Let’s add some code to draw our walls (when they exist), by finding any solid cells and coloring them in:

func fill_walls():
+

Here’s the result:

alt +alt

Note that we have a diagonal line between the two points. This is because, by default, the path will use diagonals. This can be modified by changing the diagonal_mode:

  • DIAGONAL_MODE_ALWAYS - The default value, uses diagonals.
  • DIAGONAL_MODE_NEVER - All movement is orthogonal.
  • DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE - This allows diagonals, but prevents the path going “between” diagonally placed obstacles.
  • DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES - This allows diagonals only in “open” areas, not near obstacles.

Modifying this property can give you very different results, so make sure to experiment based on your setup. Let’s add this in the initialize_grid() function:

astar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER
+

Now we only have orthogonal moves:

alt +alt

Adding Obstacles

We can also add obstacles to the grid. By marking a cell as “solid”, the path will not include that cell. A cell can be toggled solid/not solid by using the set_point_solid() function.

Let’s add some code to draw our walls (when they exist), by finding any solid cells and coloring them in:

func fill_walls():
     for x in grid_size.x:
         for y in grid_size.y:
             if astar_grid.is_point_solid(Vector2i(x, y)):
@@ -53,23 +53,25 @@
                 astar_grid.set_point_solid(pos, not astar_grid.is_point_solid(pos))
             update_path()
             queue_redraw()
-

Note that we’re checking is_in_boundsv() first - this will prevent errors from being thrown if we click outside the grid boundaries.

Now we can see the effect of obstacles on the path:

alt -alt

Choosing a Heuristic

A big factor that affects the resulting path is what heuristic you choose to use. The term “heuristic” refers to a “best guess”, and in the context of pathfinding just means: what direction should we try first when moving toward the goal?

For example, the Euclidean distance uses the Pythagorean theorem to estimate the path to try:

alt -alt

While Manhattan distance only considers distance in N/S or E/W directions:

alt -alt

And the Octile heuristic results in a path like this:

alt -alt

You can choose the heuristic using this property:

astar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_OCTILE
+

Note that we’re checking is_in_boundsv() first - this will prevent errors from being thrown if we click outside the grid boundaries.

Now we can see the effect of obstacles on the path:

alt +alt

Choosing a Heuristic

A big factor that affects the resulting path is what heuristic you choose to use. The term “heuristic” refers to a “best guess”, and in the context of pathfinding just means: what direction should we try first when moving toward the goal?

For example, the Euclidean distance uses the Pythagorean theorem to estimate the path to try:

alt +alt

While Manhattan distance only considers distance in N/S or E/W directions:

alt +alt

And the Octile heuristic results in a path like this:

alt +alt

You can choose the heuristic using this property:

astar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_OCTILE
 

Which of these works best (results in the most pleasing paths) depends on the nature of your environment. Is it mostly wide-open spaces with few obstacles scattered around? Or is it a maze of twisty passages? Make sure to experiment with your specific project.

Download the example project below to experiment with this setup yourself. In addition to placing walls, you can use the right/middle mouse buttons to move the end/start locations.

Download This Project

Download the project’s example code here: https://github.com/godotrecipes/grid_pathfinding

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/index.html b/docs/4.x/2d/index.html index 2282c315..aad52a9a 100644 --- a/docs/4.x/2d/index.html +++ b/docs/4.x/2d/index.html @@ -1,17 +1,19 @@ -2D :: Godot 4 Recipes - +2D :: Godot 4 Recipes +
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/moving_platforms/index.html b/docs/4.x/2d/moving_platforms/index.html index d0924067..aa559fed 100644 --- a/docs/4.x/2d/moving_platforms/index.html +++ b/docs/4.x/2d/moving_platforms/index.html @@ -1,5 +1,5 @@ -Moving Platforms :: Godot 4 Recipes - +Moving Platforms :: Godot 4 Recipes +

Moving Platforms

Problem

You need moving platforms in your 2D platformer.

Solution

There are several ways to approach this problem. In this recipe, we’ll use AnimatableBody2Ds for our platform and move it with a Tween. This allows for a variety of movement styles while minimizing the amount of code we need to write.

Info

You can also implement this moving platform technique using an AnimationPlayer rather than a tween. Much of the setup will be the same, but rather than tween code, you’ll animate the body’s position property.

Setting up

We’ll start with a basic platformer setup using the Platform character recipe. The basic movement from that recipe will work fine with the platforms. If you’ve modified it or used your own, everything should still work the same.

Creating the platform

The platform scene contains the following nodes:

  • Node2D (“MovingPlatform”): The Node2D parent is there to act as the “anchor” or start point for the platform. We’ll animate the platform’s position relative to this parent node.
    • AnimatableBody2D: This represents the platform itself. This is the node that will move.
      • Sprite2D: You can use a sprite sheet here, individual images, or even a TileMap.
      • CollisionShape2D: Don’t make the hitbox too big, or the player will appear to be “hovering” off the edge of the platform.

Set up the Sprite2D’s Texture and the collision shape appropriately. In the AnimatableBody2D, set the Sync to Physics property “On”. Since we’re moving the body in code, this ensures that it’s moved during the physics step, keeping it in sync with the player and other physics bodies.

Now add a script to the root Node2D:

extends Node2D
 
@@ -17,15 +17,17 @@
 

We’ve used a few of Tween’s options here to make everything work smoothly:

  • set_process_mode(): ensures that all movement takes place during the physics processing step.
  • set_loops(): this makes the tween repeat.
  • set_parallel(false): by default, all tween_property() changes would happen at that same time. This makes the two happen one after another: moving to one end of the offset, then back to the start.

Using the two exported properties, you can adjust the platform’s movement. Set the offset to determine where the tween moves relative to its starting point, and the duration to determine how long it takes to complete the cycle.

Add some platforms in your level/world and try them out:

Download This Project

Download the project code here: https://github.com/godotrecipes/2d_moving_platforms

Like video?

Coming soon

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/multi_target_camera/index.html b/docs/4.x/2d/multi_target_camera/index.html index f2882e03..569e8be0 100644 --- a/docs/4.x/2d/multi_target_camera/index.html +++ b/docs/4.x/2d/multi_target_camera/index.html @@ -1,8 +1,8 @@ -Multitarget Camera :: Godot 4 Recipes - +Multitarget Camera :: Godot 4 Recipes +

Multitarget Camera

Problem

You need a dynamic camera that moves and zooms to keep multiple objects on screen at the same time.

An example might be in a 2 player game, keeping both players on-screen as they move farther and closer together, like so:

alt -alt

Solution

In a single-player game, you’re probably used to attaching the camera to the player, so that it automatically follows them. We can’t really do this here because we have 2 (or more) players or other game objects that we want to keep on the screen at all times.

We need our camera to do 3 things:

  1. Add/remove any number of targets.
  2. Keep the camera’s position centered at the midpoint of the targets.
  3. Adjust the camera’s zoom to keep all targets on screen.

Create a new scene with a Camera2D and attach a script. We’ll add this camera to our game once we’re done.

Let’s break down how the script works.

Note

You can see the full script at the end of the article.

Here’s how the script starts:

extends Camera2D
+

Multitarget Camera

Problem

You need a dynamic camera that moves and zooms to keep multiple objects on screen at the same time.

An example might be in a 2 player game, keeping both players on-screen as they move farther and closer together, like so:

alt +alt

Solution

In a single-player game, you’re probably used to attaching the camera to the player, so that it automatically follows them. We can’t really do this here because we have 2 (or more) players or other game objects that we want to keep on the screen at all times.

We need our camera to do 3 things:

  1. Add/remove any number of targets.
  2. Keep the camera’s position centered at the midpoint of the targets.
  3. Adjust the camera’s zoom to keep all targets on screen.

Create a new scene with a Camera2D and attach a script. We’ll add this camera to our game once we’re done.

Let’s break down how the script works.

Note

You can see the full script at the end of the article.

Here’s how the script starts:

extends Camera2D
 
 @export var move_speed = 30 # camera position lerp speed
 @export var zoom_speed = 3.0  # camera zoom lerp speed
@@ -13,7 +13,7 @@
 var targets = []  # Array of targets to be tracked.
 
 @onready var screen_size = get_viewport_rect().size
-

These settings will let you adjust the camera’s behavior. We’ll lerp() all camera changes, setting the move/zoom speeds to low values will introduce some delay in the camera “catching up” to sudden changes.

Maximum and minimum zoom values will also depend on the size of objects in your game and how close or far you want to get. Adjust to suit.

The margin property is going to add some extra space around the targets so they’re not right on the edge of the viewable area.

Lastly, we have our array of targets and we get the viewport size so that we can properly calculate the scale.

func add_target(t):
+

These settings will let you adjust the camera’s behavior. We’ll lerp() all camera changes, so setting the move/zoom speeds to lower values will introduce some delay in the camera “catching up” to sudden changes.

Maximum and minimum zoom values will also depend on the size of objects in your game and how close or far you want to get. Adjust to suit.

The margin property is going to add some extra space around the targets so they’re not right on the edge of the viewable area.

Lastly, we have our array of targets and we get the viewport size so that we can properly calculate the scale.

func add_target(t):
     if not t in targets:
         targets.append(t)
 
@@ -40,8 +40,8 @@
 else:
     z = 1 / clamp(r.size.y / screen_size.y, min_zoom, max_zoom)
 zoom = lerp(zoom, Vector2.ONE * z, zoom_speed)
-

The key functionality here comes from Rect2. We want to find a rectangle that encloses all the targets, which we can get with the expand() method. We then grow the rect by the margin.

Here you can see the rectangle being drawn (press “Tab” in the demo project to enable this drawing):

alt -alt

Then, depending whether the rectangle is wider or taller (relative to the screen’s aspect ratio), we find the scale and clamp it in the max/min range we’ve defined.

Full script

extends Camera2D
+

The key functionality here comes from Rect2. We want to find a rectangle that encloses all the targets, which we can get with the expand() method. We then grow the rect by the margin.

Here you can see the rectangle being drawn (press “Tab” in the demo project to enable this drawing):

alt +alt

Then, depending whether the rectangle is wider or taller (relative to the screen’s aspect ratio), we find the scale and clamp it in the max/min range we’ve defined.

Full script

extends Camera2D
 
 @export var move_speed = 30 # camera position lerp speed
 @export var zoom_speed = 3.0  # camera zoom lerp speed
@@ -89,15 +89,17 @@
 

Download This Project

Download the project’s example code here: https://github.com/godotrecipes/multitarget_camera

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/platform_character/index.html b/docs/4.x/2d/platform_character/index.html index e8962dac..420daf69 100644 --- a/docs/4.x/2d/platform_character/index.html +++ b/docs/4.x/2d/platform_character/index.html @@ -1,5 +1,5 @@ -Platform character :: Godot 4 Recipes - +Platform character :: Godot 4 Recipes +

Platform character

Problem

You need to make a 2D platform-style character.

Solution

New developers are often surprised at how complex a platform character can be to program. Godot provides some built-in tools to assist, but there are as many solutions as there are games. In this tutorial, we won’t be going in-depth with features like double-jumps, crouching, wall-jumps, or animation. Here we’ll discuss the fundamentals of platformer movement. See the rest of the recipes for other solutions.

Tip

While it’s possible to use RigidBody2D to make a platform character, we’ll be focusing on CharacterBody2D. Kinematic bodies are well-suited for platformers, where you are less interested in realistic physics than in responsive, arcade feel.

Start with a CharacterBody2D node, and add a Sprite2D and CollisionShape2D to it.

Attach the following script to the root node of the character. Note that we’re using input actions we’ve defined in the InputMap: "walk_right", "walk_left", and "jump". See InputActions.

extends CharacterBody2D
 
@@ -40,19 +40,21 @@
     move_and_slide()
     if Input.is_action_just_pressed("jump") and is_on_floor():
         velocity.y = jump_speed
-

Try changing the values for friction and acceleration to see how they affect the game’s feel. An ice level, for example, could use very low values, making it harder to maneuver.

alt -alt

Conclusion

This code gives you a starting point for building your own platformer controller. For more advanced platforming features such as wall jumps, see the other recipes in this section.

Download This Project

Download the project code here: https://github.com/godotrecipes/2d_platform_basic

+ + \ No newline at end of file diff --git a/docs/4.x/2d/screen_wrap/index.html b/docs/4.x/2d/screen_wrap/index.html index a4ef7313..3933312f 100644 --- a/docs/4.x/2d/screen_wrap/index.html +++ b/docs/4.x/2d/screen_wrap/index.html @@ -1,5 +1,5 @@ -Screen wrap :: Godot 4 Recipes - +Screen wrap :: Godot 4 Recipes +

Screen wrap

Problem

You want to allow the player to “wrap around” the screen, teleporting from one side of the screen to the other. This is a common feature, especially in old-school 2D games (think Pac-man).

Solution

  1. Get your screen (viewport) size

    @onready var screen_size = get_viewport_rect().size
     

    get_viewport_rect() is available to any CanvasItem derived node.

  2. Compare your player’s position

    if position.x > screen_size.x:
    @@ -15,15 +15,17 @@
     
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/topdown_movement/index.html b/docs/4.x/2d/topdown_movement/index.html index 9193e0e9..88cc3c12 100644 --- a/docs/4.x/2d/topdown_movement/index.html +++ b/docs/4.x/2d/topdown_movement/index.html @@ -1,5 +1,5 @@ -Top-down movement :: Godot 4 Recipes - +Top-down movement :: Godot 4 Recipes +

Top-down movement

Problem

You’re making a 2D top-down game, and you want to control a character’s movement.

Solution

For this solution, we’ll assume you have the following input actions defined:

Action NameKey(s)
"up"W,↑
"down"S,↓
"right"D,→
"left"A,←
"click"Mouse button 1

We will also assume you’re using a CharacterBody2D node.

We can solve this problem in many ways, depending on what type of behavior you’re looking for.

Option 1: 8-way movement

In this scenario, the player uses the four directional keys to move (including diagonals).

extends CharacterBody2D
 
@@ -49,15 +49,17 @@
 

Note that we stop moving if we get close to the target position. If you don’t do this, the character will “jiggle” back and forth as it moves a little bit past the target, moves back, goes a little past it, and so on. Optionally, you can use look_at() to face in the direction of movement.

Download This Project

Download the project code here: https://github.com/godotrecipes/topdown_movement

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/2d/using_ysort/index.html b/docs/4.x/2d/using_ysort/index.html index 7a3f5031..138a2044 100644 --- a/docs/4.x/2d/using_ysort/index.html +++ b/docs/4.x/2d/using_ysort/index.html @@ -1,24 +1,26 @@ -Using Y-Sort :: Godot 4 Recipes - +Using Y-Sort :: Godot 4 Recipes +

Using Y-Sort

Problem

Many 2D games use a “3/4 view” perspective, giving the impression that the camera is looking at the world at an angle. To make this work, objects that are “farther” away need to be rendered behind “nearer” objects. In practice, that means we want to “y-sort” - making the drawing order tied to the object’s y coordinate. The higher on the screen, the farther away and therefore lower the render order.

Here’s an example of the problem:

alt -alt

These objects are being drawn in the default render order: tree order. They are arranged like this in the scene tree:

alt -alt

Solution

Godot has a built-in option to change the render order: on any CanvasItem node (Node2D or Control), we can enable the Y Sort Enabled property. When this is enabled, all child nodes are then y-sorted.

In the above example, we can enable the property on the TileMap node. However, there’s still a problem:

alt -alt

The draw order is based on each object’s y coordinate. By default, that is the object’s center:

alt -alt

Since we want to give the impression that the objects are on the “ground”, we can solve this by offsetting each object’s sprite so that the object’s position is aligned with the bottom of the sprite:

alt -alt

Now things look a lot better:

alt -alt

Download This Project

Download the project’s example code here: https://github.com/godotrecipes/using_ysort

+ + \ No newline at end of file diff --git a/docs/4.x/3d/3d_align_surface/index.html b/docs/4.x/3d/3d_align_surface/index.html index 88fd4f92..0fde9f5e 100644 --- a/docs/4.x/3d/3d_align_surface/index.html +++ b/docs/4.x/3d/3d_align_surface/index.html @@ -1,9 +1,9 @@ -CharacterBody3D: Align with Surface :: Godot 4 Recipes - +CharacterBody3D: Align with Surface :: Godot 4 Recipes +

CharacterBody3D: Align with Surface

Problem

You need your character body to align with the surface or terrain.

Solution

This recipe builds on the basic CharacterBody3D controller described in the CharacterBody3D: Movement recipe, so read that one first.

First, we’ve added some terrain to the scene. You can download the terrain from here: https://fertile-soil-productions.itch.io/modular-terrain-pack. This is low-poly terrain, but you can use or make any terrain you like for this technique.

As you can see, the movement still works with the terrain, but the tank seems to “float” above the slopes because it doesn’t change its orientation.

Instead, we need to rotate the tank so that its treads are aligned with the ground, even as the slope changes. To do that, we need to know which way is up.

Surface normals

A surface normal is a unit vector (“normal vector” and “unit vector” mean the same thing) perpendicular to a surface. It shows which way the surface is facing. In the case of a mesh, every surface has a normal pointing outward.

alt -alt

alt -alt

In Godot, when a body collides, you can get the normal of the collision. This will be the colliding body’s normal at the point of contact.

Once we have the surface normal, we need to align the tank’s Y axis with it. Note that we can’t use Transform3D.looking_at(), because that will align the -Z (forward) axis with the normal.

To do this, we’ll use the following function:

func align_with_y(xform, new_y):
+

CharacterBody3D: Align with Surface

Problem

You need your character body to align with the surface or terrain.

Solution

This recipe builds on the basic CharacterBody3D controller described in the CharacterBody3D: Movement recipe, so read that one first.

First, we’ve added some terrain to the scene. You can download the terrain from here: https://fertile-soil-productions.itch.io/modular-terrain-pack. This is low-poly terrain, but you can use or make any terrain you like for this technique.

As you can see, the movement still works with the terrain, but the tank seems to “float” above the slopes because it doesn’t change its orientation.

Instead, we need to rotate the tank so that its treads are aligned with the ground, even as the slope changes. To do that, we need to know which way is up.

Surface normals

A surface normal is a unit vector (“normal vector” and “unit vector” mean the same thing) perpendicular to a surface. It shows which way the surface is facing. In the case of a mesh, every surface has a normal pointing outward.

alt +alt

alt +alt

In Godot, when a body collides, you can get the normal of the collision. This will be the colliding body’s normal at the point of contact.

Once we have the surface normal, we need to align the tank’s Y axis with it. Note that we can’t use Transform3D.looking_at(), because that will align the -Z (forward) axis with the normal.

To do this, we’ll use the following function:

func align_with_y(xform, new_y):
     xform.basis.y = new_y
     xform.basis.x = -xform.basis.z.cross(new_y)
     xform.basis = xform.basis.orthonormalized()
@@ -32,15 +32,17 @@
 

Feel free to experiment with the interpolation amount. We found 12 to work well in this situation, but you might find a higher or lower value works better for your setup.

Download This Project

Download the project’s example code here: https://github.com/godotrecipes/characterbody3d_examples

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/3d/3d_sphere_car/index.html b/docs/4.x/3d/3d_sphere_car/index.html index d7c2bf91..13413ca2 100644 --- a/docs/4.x/3d/3d_sphere_car/index.html +++ b/docs/4.x/3d/3d_sphere_car/index.html @@ -1,14 +1,14 @@ -Arcade-style Car :: Godot 4 Recipes - +Arcade-style Car :: Godot 4 Recipes +

Arcade-style Car

Problem

You want to make an arcade-style car game, so you’re looking for simplicity over realistic physics. In this recipe, you’ll learn how to make a fun, driveable car using a rolling sphere.

Solution

There are a lot of ways to make a driving game. Different games need different levels of realism. If you’re trying to make a light, arcade-style car, you don’t need all of the features that Godot’s VehicleBody3D node provides, such as supension, independently modeled wheels, etc.

Instead, we’re going to use a single RigidBody3D sphere to handle the driving physics. The sphere will be invisible, and the car mesh will be placed at the sphere’s location, making it look like it’s the car that’s driving.

As you can see in the preview clip above, the result looks remarkably good (and feels great to play!). Read on, and you’ll see that the amount of code required is also surprisingly small.

Inputs

For control, we’re going to add four inputs to the Input Map:

  • accelerate
  • brake
  • steer_left
  • steer_right

You can use keyboard input, game controller, or both. However, we recommend going with the analog stick for better steering.

Node setup

The car is made with two main nodes: a RigidBody3D sphere for the physics, and a MeshInstance3D to display the car body. Here’s the scene layout:

 RigidBody3D (Car)
       CollisionShape3D (Sphere)
       CarMesh (Imported model)
-

Here’s how these nodes will interact: pressing “accelerate” will apply a force on the RigidBody3D in the direction the CarMesh is facing, while the turning inputs will rotate the CarMesh. As the ball rolls, it will carry the car mesh along with it (we’ll ignore the ball’s rotation).

CarMesh

Here’s the car model we’ll use:

alt -alt

Note

You can find this and other car models in Kenney’s “Car Kit”, available here: -https://kenney.nl/assets/car-kit. Download the whole kit; you can use any of them that you choose. Note that this kit includes the models in multiple formats - you won’t need all of them for your project. GLTF is the recommended format for use with Godot.

If you use the GLTF models, you shouldn’t have adjust anything in the import settings.

Here’s what the node tree looks like when importing the “suv” model:

alt -alt

Note that the wheels & body are separate meshes. This will make it easy to add some visual appeal - like turning the wheels when steering.

Ball

Add a sphere shape to the CollisionShape3D. We’re using a radius of 1 here, but you’ll want to experiment with the size of the ball to get different driving behaviors.

Here’s how to adjust the settings on the body:

  • Angular Damp: 10 - this property will have a huge effect on the driving feel. A higher value will bring the car to a stop much faster.
  • Gravity Scale: 5 - Default gravity in Godot (9.8) feels a bit floaty, especially when going for an action feel. This will really matter if you plan to have jumps, hills, etc. in your world. You can set this globally in the Project Settings instead, if you prefer.
  • Physics Material/Bounce: 0.1 - Playing around with this value can be a lot of fun. Be careful going above 0.5, though!

For the demo, we’ve also added a spherical mesh to the collision shape for debugging purposes. You don’t need this, but it helps when troubleshooting to have a visual of the ball rolling.

RayCast

Finally, add a RayCast3D node as a child of the CarMesh. Set its Target Position to (0, -1, 0).

alt -alt

We’re going to use this for ground detection. When the car’s in the air, steering and acceleration won’t work. We can also use it to align the car mesh to a slope (if your game’s track isn’t flat).

Now we’re ready to start coding.

Script

We’ll begin the script with some node references we’ll need:

extends RigidBody3D
+

Here’s how these nodes will interact: pressing “accelerate” will apply a force on the RigidBody3D in the direction the CarMesh is facing, while the turning inputs will rotate the CarMesh. As the ball rolls, it will carry the car mesh along with it (we’ll ignore the ball’s rotation).

CarMesh

Here’s the car model we’ll use:

alt +alt

Note

You can find this and other car models in Kenney’s “Car Kit”, available here: +https://kenney.nl/assets/car-kit. Download the whole kit; you can use any of them that you choose. Note that this kit includes the models in multiple formats - you won’t need all of them for your project. GLTF is the recommended format for use with Godot.

If you use the GLTF models, you shouldn’t have adjust anything in the import settings.

Here’s what the node tree looks like when importing the “suv” model:

alt +alt

Note that the wheels & body are separate meshes. This will make it easy to add some visual appeal - like turning the wheels when steering.

Ball

Add a sphere shape to the CollisionShape3D. We’re using a radius of 1 here, but you’ll want to experiment with the size of the ball to get different driving behaviors.

Here’s how to adjust the settings on the body:

  • Angular Damp: 10 - this property will have a huge effect on the driving feel. A higher value will bring the car to a stop much faster.
  • Gravity Scale: 5 - Default gravity in Godot (9.8) feels a bit floaty, especially when going for an action feel. This will really matter if you plan to have jumps, hills, etc. in your world. You can set this globally in the Project Settings instead, if you prefer.
  • Physics Material/Bounce: 0.1 - Playing around with this value can be a lot of fun. Be careful going above 0.5, though!

For the demo, we’ve also added a spherical mesh to the collision shape for debugging purposes. You don’t need this, but it helps when troubleshooting to have a visual of the ball rolling.

RayCast

Finally, add a RayCast3D node as a child of the CarMesh. Set its Target Position to (0, -1, 0).

alt +alt

We’re going to use this for ground detection. When the car’s in the air, steering and acceleration won’t work. We can also use it to align the car mesh to a slope (if your game’s track isn’t flat).

Now we’re ready to start coding.

Script

We’ll begin the script with some node references we’ll need:

extends RigidBody3D
 
 @onready var car_mesh = $CarMesh
 @onready var body_mesh = $CarMesh/suv2
@@ -59,24 +59,26 @@
 

And right after getting input, add the following:

    # rotate wheels for effect
     right_wheel.rotation.y = rotate_input
     left_wheel.rotation.y = rotate_input
-

alt -alt

3. Tilt the body

This one adds lots of visual appeal. We’re going to tilt the car’s body based on the speed of the turn. Add a variable at the top of the script:

var body_tilt = 35
+

alt +alt

3. Tilt the body

This one adds lots of visual appeal. We’re going to tilt the car’s body based on the speed of the turn. Add a variable at the top of the script:

var body_tilt = 35
 

The smaller this number, the more extreme the tilt effect will be. Between 35 and 40 works well for the SUV model.

Now add the following right after rotating the car mesh (in the if statement):

# tilt body for effect
 var t = -rotate_input * ball.linear_velocity.length() / body_tilt
 body_mesh.rotation.z = lerp(body_mesh.rotation.z, t, 10 * delta)
-

Observe the difference:

alt -alt

Credits

The demo project seen here uses the following open-source/creative commons assets:

Download This Project

Download the project code here: https://github.com/godotrecipes/3d_car_sphere

+ + \ No newline at end of file diff --git a/docs/4.x/3d/basic_fps/index.html b/docs/4.x/3d/basic_fps/index.html index ee7f14f9..2cfb5571 100644 --- a/docs/4.x/3d/basic_fps/index.html +++ b/docs/4.x/3d/basic_fps/index.html @@ -1,5 +1,5 @@ -Basic FPS Character :: Godot 4 Recipes - +Basic FPS Character :: Godot 4 Recipes +

Basic FPS Character

Problem

You need to make a first-person shooter (FPS) character.

Solution

Start with a CharacterBody3D node, and add a CollisionShape3D to it. The CapsuleShape3D collision shape is the most common choice. Depending on your world setup, you may want to add additional shapes here, but for the purposes of this example, we’ll stick to the basics.

We’ll leave all the sizing at the default values, meaning the capsule will be 2 meters high. Move it up by 1.0 m to align its bottom with the ground.

Next, add a Camera3D as a child of the body and move it up about 1.6 m.

Where’s the body?

For this example, we’ll leave the character “bodyless” - meaning we’re not adding a mesh to display for the player’s body. Depending on your setup, you may or may not need to see the player’s body.

Attach a script to the body and start by defining some properties:

extends CharacterBody3D
 
@@ -28,19 +28,21 @@
         rotate_y(-event.relative.x * mouse_sensitivity)
         $Camera3D.rotate_x(-event.relative.y * mouse_sensitivity)
         $Camera3D.rotation.x = clampf($Camera3D.rotation.x, -deg_to_rad(70), deg_to_rad(70))
-

Holding a weapon

alt -alt

An FPS character typically has a 3D mesh of a weapon positioned in front. Setting this up can be easy with a couple of Godot editor tricks.

Add your weapon mesh as a child of the Camera3D. Then, in the editor view menu, choose “2 Viewports” and set one of them to preview the camera. Then, you can move around the weapon and easily see how it will look from the player’s perspective.

To add a little personality, try using an AnimationPlayer to animate the weapon’s position from side-to-side as the player moves.

Download This Project

Download the project code here: https://github.com/godotrecipes/basic_fps

+ + \ No newline at end of file diff --git a/docs/4.x/3d/characterbody3d_examples/index.html b/docs/4.x/3d/characterbody3d_examples/index.html index c43f56cf..516b0bc3 100644 --- a/docs/4.x/3d/characterbody3d_examples/index.html +++ b/docs/4.x/3d/characterbody3d_examples/index.html @@ -1,10 +1,10 @@ -CharacterBody3D: Movement :: Godot 4 Recipes - +CharacterBody3D: Movement :: Godot 4 Recipes +

CharacterBody3D: Movement

Problem

You need a player-controlled 3D character body.

Solution

For this recipe, we’ll be using this adorable tank model:

alt -alt

You can grab this model on Itch.io: https://gtibo.itch.io/mini-tank or use any other model you’d like. We won’t be doing anything that’s tank-specific here.

In the case of this asset, the download includes an OBJ file, and we’ll find it more convenient if we import it as a scene:

alt -alt

We can add the model to the scene, but we’ll need a couple of additional nodes:

alt -alt

For the collision shape, we’re just going to use a BoxShape aligned and sized with the tank’s treads. CamPos is a Position3D we’ll use to place our following camera. It’s placed behind and above the tank, angled down.

We’ve also rotated the individual MeshInstance nodes 180 degrees around the Y axis. This is because they were modeled facing towards +Z, but -Z is the forward direction in Godot, and we don’t want our tank to look like it’s backwards.

Before we add a script, open the “Project Settings” and add the following inputs +

CharacterBody3D: Movement

Problem

You need a player-controlled 3D character body.

Solution

For this recipe, we’ll be using this adorable tank model:

alt +alt

You can grab this model on Itch.io: https://gtibo.itch.io/mini-tank or use any other model you’d like. We won’t be doing anything that’s tank-specific here.

In the case of this asset, the download includes an OBJ file, and we’ll find it more convenient if we import it as a scene:

alt +alt

We can add the model to the scene, but we’ll need a couple of additional nodes:

alt +alt

For the collision shape, we’re just going to use a BoxShape aligned and sized with the tank’s treads. CamPos is a Position3D we’ll use to place our following camera. It’s placed behind and above the tank, angled down.

We’ve also rotated the individual MeshInstance nodes 180 degrees around the Y axis. This is because they were modeled facing towards +Z, but -Z is the forward direction in Godot, and we don’t want our tank to look like it’s backwards.

Before we add a script, open the “Project Settings” and add the following inputs on the “Input Map” tab:

Input ActionKey
forwardW
backS
rightD
leftA

Now let’s add a script, starting with the required variables:

extends CharacterBody3D
 
 @export var speed = 4.0
@@ -24,15 +24,17 @@
 

Let’s examine this more closely. Player input should affect horizontal movement: forward/back along the ground, and rotation around the tank’s center. Movement in the Y direction should only be affected by gravity, which means we don’t want to set it to 0 every frame. This is why we’re using the vy variable to temporarily hold that value while we assign a new velocity vector for the horizontal movement, then add it back in at the end.

For the forward and back movement, we’re using transform.basis.z so that we’ll move in our body’s local forward direction.

Here’s the tank in action. We’ve made a test scene with a StaticBody3D plane for the ground and an Camera3D using the Interpolated Camera recipe.

Wrapping up

This is the basis of movement for any kind of kinematic character. From here you can add jumping, shooting, AI behavior, etc. See the related recipes for examples that build on this recipe.

Download This Project

Download the project’s example code here: https://github.com/godotrecipes/characterbody3d_examples

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/3d/click_to_move/index.html b/docs/4.x/3d/click_to_move/index.html index b3bde005..2b2e8946 100644 --- a/docs/4.x/3d/click_to_move/index.html +++ b/docs/4.x/3d/click_to_move/index.html @@ -1,9 +1,9 @@ -Click to move :: Godot 4 Recipes - +Click to move :: Godot 4 Recipes +

Click to move

Problem

You want to move a 3D object to a clicked position.

Solution

We’ll start with a flat plane for our world. Our actor will move on this plane.

alt -alt

The actor for this demo is a triangular prism mesh:

alt -alt

Here is the code for the movement. If given a target, the object will turn and move toward it.

extends CharacterBody3D
+

Click to move

Problem

You want to move a 3D object to a clicked position.

Solution

We’ll start with a flat plane for our world. Our actor will move on this plane.

alt +alt

The actor for this demo is a triangular prism mesh:

alt +alt

Here is the code for the movement. If given a target, the object will turn and move toward it.

extends CharacterBody3D
 
 @export var speed = 5
 @export var gravity = -5
@@ -20,24 +20,26 @@
             target = Vector3.ZERO
             velocity = Vector3.ZERO
     move_and_slide()
-

We’ve also added a MeshInstance3D called “Marker” to the scene. This will be moved to indicate the clicked position.

alt -alt

Mouse -> 3D

Now we need a way to map mouse position into our 3D world. If you imagine the screen as a window into the 3D world, the mouse is trapped on the glass. To select something in 3D, we must project a ray from our eye (the camera), through the mouse’s position and into the world.

While this can be done manually using the Camera3D’s project_ray methods, we can take advantage of the fact that CollisionObject3D nodes do this automatically. All we need to do is connect our StaticBody3D ground’s input_event signal:

func _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx):
+

We’ve also added a MeshInstance3D called “Marker” to the scene. This will be moved to indicate the clicked position.

alt +alt

Mouse -> 3D

Now we need a way to map mouse position into our 3D world. If you imagine the screen as a window into the 3D world, the mouse is trapped on the glass. To select something in 3D, we must project a ray from our eye (the camera), through the mouse’s position and into the world.

While this can be done manually using the Camera3D’s project_ray methods, we can take advantage of the fact that CollisionObject3D nodes do this automatically. All we need to do is connect our StaticBody3D ground’s input_event signal:

func _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx):
     if event is InputEventMouseButton and event.pressed:
         $Marker.transform.origin = click_position
         $Player.target = click_position
-

We set the position of the marker and the Player’s target to the clicked position:

alt -alt

Wrapping up

You can use this technique to detect clicks on any objects in your 3D world.

Like video?

+ + \ No newline at end of file diff --git a/docs/4.x/3d/healthbars/index.html b/docs/4.x/3d/healthbars/index.html index 002f916c..c8069245 100644 --- a/docs/4.x/3d/healthbars/index.html +++ b/docs/4.x/3d/healthbars/index.html @@ -1,20 +1,20 @@ -3D Unit Healthbars :: Godot 4 Recipes - +3D Unit Healthbars :: Godot 4 Recipes +

3D Unit Healthbars

Problem

You want a floating “healthbar” for your 3D game objects (mobs, characters, etc.).

Solution

For this solution, we’re going to re-use a 2D healthbar based on a TextureProgressBar node. It’s already set up with textures and code for updating the value and color. If you already have something similar, feel free to use it here. In the example, we’ll name this scene “Healthbar2D”.

alt -alt

If you need some assets, here are the three images used in the bar:

alt -alt

alt -alt

alt -alt

Note

Re-using existing objects can save you a lot of time. Don’t re-invent the wheel everytime you need a healthbar, camera, or other common object.

Project setup

For our example “mob”, we’ll start with a CharacterBody3D node. It’s programmed to spawn and travel in a straight line. It also has the following code to handle damage:

func _on_input_event(camera, event, position, normal, shape_idx):
+

3D Unit Healthbars

Problem

You want a floating “healthbar” for your 3D game objects (mobs, characters, etc.).

Solution

For this solution, we’re going to re-use a 2D healthbar based on a TextureProgressBar node. It’s already set up with textures and code for updating the value and color. If you already have something similar, feel free to use it here. In the example, we’ll name this scene “Healthbar2D”.

alt +alt

If you need some assets, here are the three images used in the bar:

alt +alt

alt +alt

alt +alt

Note

Re-using existing objects can save you a lot of time. Don’t re-invent the wheel everytime you need a healthbar, camera, or other common object.

Project setup

For our example “mob”, we’ll start with a CharacterBody3D node. It’s programmed to spawn and travel in a straight line. It also has the following code to handle damage:

func _on_input_event(camera, event, position, normal, shape_idx):
     if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
         health -= 1
         if health <= 0:
             queue_free()
-

alt -alt

Clicking on a unit deals one damage. Do ten damage, and the unit is destroyed. Now we need a visual representation of that using our 2D bar.

2D in 3D

We can display a 2D image in 3D using a Sprite3D. Add one to a new scene and name it “Healthbar3D”. First, we’ll get it configured and sized, so set the Texture property to the green bar image.

The Sprite3D acts like any other 3D object - as we pan the camera around, our perspective on it changes. However, we want the healthbar to always “face” toward the camera so that we can see it.

In the Inspector, under Flags, set Billboard to “Enabled”.

Now try moving the camera to confirm that the texture is always facing you.

alt -alt

Add an instance of this scene to the Mob scene and position the bar above the mob’s body.

alt -alt

Viewport texture

We don’t want the Sprite3D to show a static texture - we want it to display the 2D TextureProgressBar. We can do that using a SubViewport node, which can export a texture.

Add a SubViewport as a child of the Sprite3D. In the Inspector set Transparent BG to On.

We also need to set the size of the viewport to match the size of the healthbar texture, which is (200, 26).

Instance the HealthBar2D as a child of the Viewport. Your scene should look like this:

alt -alt

If the SubViewport were not a child of the Sprite3D, we could set it as the sprite’s texture directly in the Inspector. Since it’s a child, it won’t be ready at the right time, so we’ll need to set it in a script attached to the Sprite3D:

extends Sprite3D
+

alt +alt

Clicking on a unit deals one damage. Do ten damage, and the unit is destroyed. Now we need a visual representation of that using our 2D bar.

2D in 3D

We can display a 2D image in 3D using a Sprite3D. Add one to a new scene and name it “Healthbar3D”. First, we’ll get it configured and sized, so set the Texture property to the green bar image.

The Sprite3D acts like any other 3D object - as we pan the camera around, our perspective on it changes. However, we want the healthbar to always “face” toward the camera so that we can see it.

In the Inspector, under Flags, set Billboard to “Enabled”.

Now try moving the camera to confirm that the texture is always facing you.

alt +alt

Add an instance of this scene to the Mob scene and position the bar above the mob’s body.

alt +alt

Viewport texture

We don’t want the Sprite3D to show a static texture - we want it to display the 2D TextureProgressBar. We can do that using a SubViewport node, which can export a texture.

Add a SubViewport as a child of the Sprite3D. In the Inspector set Transparent BG to On.

We also need to set the size of the viewport to match the size of the healthbar texture, which is (200, 26).

Instance the HealthBar2D as a child of the Viewport. Your scene should look like this:

alt +alt

If the SubViewport were not a child of the Sprite3D, we could set it as the sprite’s texture directly in the Inspector. Since it’s a child, it won’t be ready at the right time, so we’ll need to set it in a script attached to the Sprite3D:

extends Sprite3D
 
 func _ready():
     texture = $SubViewport.get_texture()
@@ -30,19 +30,21 @@
 		texture_progress = bar_yellow
 	if value < 0.45 * max_value:
 		texture_progress = bar_red
-

Click on the mobs to see the health bars change.

alt -alt

Wrapping up

You can use this technique to display any other Node2D or Control nodes, such as Label, VideoStreamPlayer, etc. You can even use the SubViewport to “project” an entire 2D game in 3D space.

Download This Project

Download the project code here: https://github.com/godotrecipes/3d_object_healthbars

+ + \ No newline at end of file diff --git a/docs/4.x/3d/index.html b/docs/4.x/3d/index.html index 374015fb..0fce8b06 100644 --- a/docs/4.x/3d/index.html +++ b/docs/4.x/3d/index.html @@ -1,17 +1,19 @@ -3D :: Godot 4 Recipes - +3D :: Godot 4 Recipes +
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/3d/interpolated_camera/index.html b/docs/4.x/3d/interpolated_camera/index.html index c6d6685b..a266ba5f 100644 --- a/docs/4.x/3d/interpolated_camera/index.html +++ b/docs/4.x/3d/interpolated_camera/index.html @@ -1,5 +1,5 @@ -Interpolated Camera :: Godot 4 Recipes - +Interpolated Camera :: Godot 4 Recipes +

Interpolated Camera

Problem

You need a 3D camera that smoothly follows a target (interpolates).

Solution

Info

Godot’s built-in InterpolatedCamera node is deprecated and will be removed in the release of Godot 4.0.

Attach the script below to a Camera3D node in your scene. The three export properties let you choose:

  • lerp_speed - the camera’s movement speed. Lower values result in a “lazier” camera.
  • target_path - choose the camera’s target node.
  • offset - position of the camera relative to the target.

See below for some examples of the camera in action.

extends Camera3D
 
@@ -24,15 +24,17 @@
 

In the _physics_process() function we interpolate the camera’s position with the target’s (plus offset).

Examples

  • lerp_speed: 3.0
  • offset: (0, 7, 5)

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/3d/rolling_cube/index.html b/docs/4.x/3d/rolling_cube/index.html index 88d50748..9073018b 100644 --- a/docs/4.x/3d/rolling_cube/index.html +++ b/docs/4.x/3d/rolling_cube/index.html @@ -1,15 +1,15 @@ -Rolling Cube :: Godot 4 Recipes - +Rolling Cube :: Godot 4 Recipes +

Rolling Cube

Problem

You want to make a rolling cube in 3D.

Solution

Rolling a cube is trickier than it seems. You can’t just rotate the cube around its center:

alt -alt

Instead, the cube needs to be rotated around its bottom edge.

alt -alt

Here’s the tricky part: which bottom edge? It depends on which direction the cube is rolling.

In preparing this recipe, I experimented with a few different solutions to this problem:

  • Pure math - calculating and applying rotation transforms
  • AnimationPlayer - using animations to key the rotations and offsets
  • Helper nodes - using Spatial(s) as rotation helpers

They all worked fine, but I found the last option the most flexible and easiest to adapt, so that’s what we’ll do here.

Node setup

Cube:  CharacterBody3D
+

Rolling Cube

Problem

You want to make a rolling cube in 3D.

Solution

Rolling a cube is trickier than it seems. You can’t just rotate the cube around its center:

alt +alt

Instead, the cube needs to be rotated around its bottom edge.

alt +alt

Here’s the tricky part: which bottom edge? It depends on which direction the cube is rolling.

In preparing this recipe, I experimented with a few different solutions to this problem:

  • Pure math - calculating and applying rotation transforms
  • AnimationPlayer - using animations to key the rotations and offsets
  • Helper nodes - using Spatial(s) as rotation helpers

They all worked fine, but I found the last option the most flexible and easiest to adapt, so that’s what we’ll do here.

Node setup

Cube:  CharacterBody3D
     Pivot:  Node3D
         Mesh:  MeshInstance3D
     Collision:  CollisionShape3D
-
Tip

You can do this with RigidBody3D, CharacterBody3D, or Area3D as your collision node. There will be minor differences in how you handle movement. Which node you choose should depend on what other behavior you want in your game. For this recipe, we’re only concerned with the movement.

By default, everything is centered at (0, 0, 0) so the first thing we’re going to do is offset everything so that the bottom center of the cube is the CharacterBody3D’s position.

The default size of a BoxMesh3D is (1, 1, 1), so do this, move the mesh and collision nodes both up to (0, 0.5, 0), leaving the rest where they are. Now when you select the root node, its position will be the bottom of the cube:

alt -alt

Now when you want to roll the cube, you’ll need to move the Pivot 0.5 in the direction you want to move. Since the mesh is attached, you need to move it the opposite amount. For example, to roll to the right (+X), you’ll end up with this:

alt -alt

Now the pivot node is at the correct edge and rotating it will also rotate the mesh.

Movement script

The movement is broken in to 3 steps:

Step 1

Here we apply the two offsets shown above: shift the Pivot in the direction of movement, and shift the Mesh in the opposite direction.

Step 2

In this step we animate the rotation. We find the axis of rotation using the cross product of the direction and the down vector. Then we use a Tween to animate rotating the pivot’s transform.

Step 3

Finally, once the animation has finished, we need to reset everything so that it’s ready to happen again. In the end, we want to have the cube moved 1 unit in the chosen direction (for a cube of size 1) and have the pivot and mesh back at their original positions.

extends CharacterBody3D
+
Tip

You can do this with RigidBody3D, CharacterBody3D, or Area3D as your collision node. There will be minor differences in how you handle movement. Which node you choose should depend on what other behavior you want in your game. For this recipe, we’re only concerned with the movement.

By default, everything is centered at (0, 0, 0) so the first thing we’re going to do is offset everything so that the bottom center of the cube is the CharacterBody3D’s position.

The default size of a BoxMesh3D is (1, 1, 1), so do this, move the mesh and collision nodes both up to (0, 0.5, 0), leaving the rest where they are. Now when you select the root node, its position will be the bottom of the cube:

alt +alt

Now when you want to roll the cube, you’ll need to move the Pivot 0.5 in the direction you want to move. Since the mesh is attached, you need to move it the opposite amount. For example, to roll to the right (+X), you’ll end up with this:

alt +alt

Now the pivot node is at the correct edge and rotating it will also rotate the mesh.

Movement script

The movement is broken in to 3 steps:

Step 1

Here we apply the two offsets shown above: shift the Pivot in the direction of movement, and shift the Mesh in the opposite direction.

Step 2

In this step we animate the rotation. We find the axis of rotation using the cross product of the direction and the down vector. Then we use a Tween to animate rotating the pivot’s transform.

Step 3

Finally, once the animation has finished, we need to reset everything so that it’s ready to happen again. In the end, we want to have the cube moved 1 unit in the chosen direction (for a cube of size 1) and have the pivot and mesh back at their original positions.

extends CharacterBody3D
 
 @onready var pivot = $Pivot
 @onready var mesh = $Pivot/MeshInstance3D
@@ -70,15 +70,17 @@
 

Download This Project

Download the project code here: https://github.com/godotrecipes/rolling_cube

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/3d/shooting_raycasts/index.html b/docs/4.x/3d/shooting_raycasts/index.html index 1c50f366..cceb19a1 100644 --- a/docs/4.x/3d/shooting_raycasts/index.html +++ b/docs/4.x/3d/shooting_raycasts/index.html @@ -1,5 +1,5 @@ -Shooting with Raycasts :: Godot 4 Recipes - +Shooting with Raycasts :: Godot 4 Recipes +

Shooting with Raycasts

Problem

You need to implement shooting in an FPS, but moving individual projectiles is impractical.

Solution

Game physics engines often break down when trying to handle very fast-moving objects. The solution is to cast a ray from the shooter’s location and detect the first thing that would be hit.

There are two ways to approach raycasting in Godot: the RayCast3D node, or directly casting a ray in space using the physics engine. While they can both accomplish the same thing, each has its uses. The node method tends to be best for situations where you continuously want to check for collisions - a downward-facing ray to check if you’re on the floor, for example.

We’ll use the second method, querying the physics state, because we want to know, at the moment we press the “shoot” key, whether we’ve hit anything.

Note

This recipe assumes you already have a working FPS character controller and a world to move around in. If you don’t, see the Basic FPS Character recipe first.

To display what we’ve hit, add a CanvasLayer with a Label node to the FPSPlayer scene.

We’ll add an input check in the _input() function, which we’re already using to handle mouse input.

    if event.is_action_pressed("shoot"):
         shoot()
@@ -15,15 +15,17 @@
 

Download This Project

Download the project code here: https://github.com/godotrecipes/3d_shoot_raycasts

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/3d/spaceship/index.html b/docs/4.x/3d/spaceship/index.html index d584aef8..211db6a3 100644 --- a/docs/4.x/3d/spaceship/index.html +++ b/docs/4.x/3d/spaceship/index.html @@ -1,8 +1,8 @@ -Arcade-style Spaceship :: Godot 4 Recipes - +Arcade-style Spaceship :: Godot 4 Recipes +

Arcade-style Spaceship

Problem

You want to make a 3D spaceship that flies in an arcade/cinematic way. You’re not looking for realistic physics, but more of a dog-fighting, “Star Wars”-style of spaceflight.

Solution

To accomplish this, we’ll use a CharacterBody3D for the ship. The three axis inputs (pitch, roll, and yaw) will rotate the body’s basis around the corresponding axis. The direction of motion will always point forward.

Note

You can do this with RigidBody3D and get the same results. See the example project linked below, which includes a rigid body version as well.

Assets

Spaceship models are from this asset pack:

Ultimate Spaceships Pack by Quaternius

I’ve chosen the “Executioner” ship model:

alt -alt

Feel free to choose your favorite design.

Setup

Select the gltf file of the ship you want, and click the Import tab. Change the Root Type to CharacterBody3D and click “Reimport”. Then double-click the gltf and you’ll have a new inherited scene with a CharacterBody3D root and a MeshInstance child. Add a CollisionShape3D to the body.

In Project Settings -> Input Map, set up the following inputs:

  • roll_right / roll_left
  • pitch_up / pitch_down
  • yaw_right / yaw_left
  • throttle_up / throttle_down

You can assign keys or controller inputs. Analog stick inputs will work best.

Movement

To start the script, let’s handle the forward movement. Pressing the throttle buttons smoothly increases/decreases the speed.

extends CharacterBody
+

Arcade-style Spaceship

Problem

You want to make a 3D spaceship that flies in an arcade/cinematic way. You’re not looking for realistic physics, but more of a dog-fighting, “Star Wars”-style of spaceflight.

Solution

To accomplish this, we’ll use a CharacterBody3D for the ship. The three axis inputs (pitch, roll, and yaw) will rotate the body’s basis around the corresponding axis. The direction of motion will always point forward.

Note

You can do this with RigidBody3D and get the same results. See the example project linked below, which includes a rigid body version as well.

Assets

Spaceship models are from this asset pack:

Ultimate Spaceships Pack by Quaternius

I’ve chosen the “Executioner” ship model:

alt +alt

Feel free to choose your favorite design.

Setup

Select the gltf file of the ship you want, and click the Import tab. Change the Root Type to CharacterBody3D and click “Reimport”. Then double-click the gltf and you’ll have a new inherited scene with a CharacterBody3D root and a MeshInstance child. Add a CollisionShape3D to the body.

In Project Settings -> Input Map, set up the following inputs:

  • roll_right / roll_left
  • pitch_up / pitch_down
  • yaw_right / yaw_left
  • throttle_up / throttle_down

You can assign keys or controller inputs. Analog stick inputs will work best.

Movement

To start the script, let’s handle the forward movement. Pressing the throttle buttons smoothly increases/decreases the speed.

extends CharacterBody
 
 @export var max_speed = 50.0
 @export var acceleration = 0.6
@@ -19,8 +19,8 @@
     get_input(delta)
     velocity = -transform.basis.z * forward_speed
     move_and_collide(velocity * delta)
-

Make a test scene with a Camera3D to try it out. You can use a stationary camera or a chase camera. Check that the ship accelerates and slows before moving on to the next step.

alt -alt

Rotation

Now we can handle rotation in the three axes. Add the following variables at the top of the script:

@export var pitch_speed = 1.5
+

Make a test scene with a Camera3D to try it out. You can use a stationary camera or a chase camera. Check that the ship accelerates and slows before moving on to the next step.

alt +alt

Rotation

Now we can handle rotation in the three axes. Add the following variables at the top of the script:

@export var pitch_speed = 1.5
 @export var roll_speed = 1.9
 @export var yaw_speed = 1.25
 
@@ -37,16 +37,16 @@
 transform.basis = transform.basis.rotated(transform.basis.y,
     yaw_input * yaw_speed * delta)
 transform.basis = transform.basis.orthonormalized()
-

alt -alt

Improvements

Currently the rotations are a little to “sharp”. The ship starts and stops rotating instantly, which feels a bit too unnatural. We can solve this with lerp(), and by adding one more configuration variable to set how “floaty” we’d like the controls to be:

@export var input_response = 8.0
+

alt +alt

Improvements

Currently the rotations are a little to “sharp”. The ship starts and stops rotating instantly, which feels a bit too unnatural. We can solve this with lerp(), and by adding one more configuration variable to set how “floaty” we’d like the controls to be:

@export var input_response = 8.0
 

Change the three axis inputs in get_input() to the following:

pitch_input = lerp(pitch_input, Input.get_axis("pitch_down", "pitch_up"),
         input_response * delta)
 roll_input = lerp(roll_input, Input.get_axis("roll_right", "roll_left"),
         input_response * delta)
 yaw_input = lerp(yaw_input, Input.get_axis("yaw_right", "yaw_left"),
         input_response * delta)
-

Now when stopping or changing direction, there’s a little bit of inertia.

alt -alt

Linking roll/yaw

One problem with this control scheme is that it’s awkward. Having to use a separate stick for the yaw input makes it difficult to control, especially when also shooting and using other controls. Many games solve this by linking the roll input to also apply a small amount of yaw. To do this, change the yaw_speed to around 1/4 to 1/2 of the roll_speed.

In the get_input() function, change the line getting yaw_input to the following:

yaw_input = roll_input
+

Now when stopping or changing direction, there’s a little bit of inertia.

alt +alt

Linking roll/yaw

One problem with this control scheme is that it’s awkward. Having to use a separate stick for the yaw input makes it difficult to control, especially when also shooting and using other controls. Many games solve this by linking the roll input to also apply a small amount of yaw. To do this, change the yaw_speed to around 1/4 to 1/2 of the roll_speed.

In the get_input() function, change the line getting yaw_input to the following:

yaw_input = roll_input
 

This is another fun place to experiment by changing the roll and yaw speeds. For example, what if yaw was primary and roll smaller? What if other axes were linked? If your game has different ships, you can give them different values for variety in flight styles/performance.

Wrapping up

That’s it, now you can fly! This controller is a great start for whatever space-based game you might have in mind. Add some other ships, and a few effects, and you’re ready go:

Full script

Here’s the complete script:

extends CharacterBody3D
 
 @export var max_speed = 50.0
@@ -89,15 +89,17 @@
 

Download This Project

Download the project code here: https://github.com/godotrecipes/3d_spaceship

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/404.html b/docs/4.x/404.html index 07ff6837..696da4cb 100644 --- a/docs/4.x/404.html +++ b/docs/4.x/404.html @@ -1,3 +1,3 @@ -404 Page not found :: Godot 4 Recipes - +404 Page not found :: Godot 4 Recipes +

Error

Woops. Looks like this page doesn't exist ¯\_(ツ)_/¯.

Go to homepage

Page not found!

\ No newline at end of file diff --git a/docs/4.x/ai/homing_missile/index.html b/docs/4.x/ai/homing_missile/index.html index 1c2c6b67..fed7adf7 100644 --- a/docs/4.x/ai/homing_missile/index.html +++ b/docs/4.x/ai/homing_missile/index.html @@ -1,12 +1,12 @@ -Homing missile :: Godot 4 Recipes - +Homing missile :: Godot 4 Recipes +

Homing missile

Problem

You need a “homing missile” - a projectile that will seek a moving target.

Solution

For this example, we’ll use an Area2D node for the projectile. Areas are typically good choices for bullets because we need to detect when they contact something. If you also need a bullet that bounces/ricochets, one of the PhysicsBody type node might be a better choice.

The node setup and behavior of the missile is the same you would use for a “dumb” bullet. If you’re creating many bullet types, you can use inheritance to base all your projectiles on the same core setup.

The nodes we’ll use:

 Area2D: Missile
      Sprite2D
      CollisionShape2D
      Timer: Lifetime
-

For the texture, you can use any image you like. Here’s an example one:

alt -alt

Set up the nodes and configure the sprite’s texture and the collision shape. Make sure to rotate the Sprite2D node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.

Add a script and connect the Area2D’s body_entered signal and the Timer’s timeout signal.

Here’s the starting script:

extends Area2D
+

For the texture, you can use any image you like. Here’s an example one:

alt +alt

Set up the nodes and configure the sprite’s texture and the collision shape. Make sure to rotate the Sprite2D node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.

Add a script and connect the Area2D’s body_entered signal and the Timer’s timeout signal.

Here’s the starting script:

extends Area2D
 
 export var speed = 350
 
@@ -36,8 +36,8 @@
 func start(_transform, _target):
     target = _target
     ...
-

To change the missile’s direction to move toward the target, it needs to accelerate in that direction (acceleration is change in velocity). The missile “wants” to move straight towards the target, but its current velocity is pointing in a different direction. Using a little vector math, we can find that difference:

alt -alt

The green arrow represents the needed change in velocity (i.e. acceleration). However, if we turn instantly, that will look unnatural, so the “steering” vector’s length needs to be limited. This is the purpose of the steer_force variable.

This is the function to calculate that acceleration. Note that if there’s no target, there will be no steering, so the missile remains traveling in a straight line.

func seek():
+

To change the missile’s direction to move toward the target, it needs to accelerate in that direction (acceleration is change in velocity). The missile “wants” to move straight towards the target, but its current velocity is pointing in a different direction. Using a little vector math, we can find that difference:

alt +alt

The green arrow represents the needed change in velocity (i.e. acceleration). However, if we turn instantly, that will look unnatural, so the “steering” vector’s length needs to be limited. This is the purpose of the steer_force variable.

This is the function to calculate that acceleration. Note that if there’s no target, there will be no steering, so the missile remains traveling in a straight line.

func seek():
     var steer = Vector2.ZERO
     if target:
         var desired = (target.position - position).normalized() * speed
@@ -93,15 +93,17 @@
 
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/ai/index.html b/docs/4.x/ai/index.html index 8928e4bd..cfcb57e9 100644 --- a/docs/4.x/ai/index.html +++ b/docs/4.x/ai/index.html @@ -1,17 +1,19 @@ -AI/Behavior :: Godot 4 Recipes - +AI/Behavior :: Godot 4 Recipes +

 AI/Behavior

Automated behavior and (sometimes) smarter entities.

In this section:

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/animation/index.html b/docs/4.x/animation/index.html index ffcb6021..b772967c 100644 --- a/docs/4.x/animation/index.html +++ b/docs/4.x/animation/index.html @@ -1,17 +1,19 @@ -Animation :: Godot 4 Recipes - +Animation :: Godot 4 Recipes +
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/animation/spritesheet_animation/index.html b/docs/4.x/animation/spritesheet_animation/index.html index 1c5c77e0..ceef301a 100644 --- a/docs/4.x/animation/spritesheet_animation/index.html +++ b/docs/4.x/animation/spritesheet_animation/index.html @@ -1,28 +1,30 @@ -Spritesheet animation :: Godot 4 Recipes - +Spritesheet animation :: Godot 4 Recipes +

Spritesheet animation

Problem

You want to use a spritesheet containing 2D animations.

Solution

Spritesheets are a common way for 2D animations to be distributed. In a spritesheet, all of the animation frames are packed into a single image.

For this demo, we’ll be using the excellent “Adventurer” sprite by Elthen. You can get this and lots of other great art athttps://elthen.itch.io/.

alt -alt

Warning

Make sure the images in your spritesheet are laid out in a constant-sized grid. This will enable Godot to automatically slice them. If they’re packed irregularly, you will not be able to use the following technique.

Node setup

This animation technique uses a Sprite2D node to display the texture, and then we animate the changing frames with AnimationPlayer. This can work with any 2D node, but for this demo, we’ll use a CharacterBody2D.

Add the following nodes to your scene:

CharacterBody2D: Player
+

Spritesheet animation

Problem

You want to use a spritesheet containing 2D animations.

Solution

Spritesheets are a common way for 2D animations to be distributed. In a spritesheet, all of the animation frames are packed into a single image.

For this demo, we’ll be using the excellent “Adventurer” sprite by Elthen. You can get this and lots of other great art athttps://elthen.itch.io/.

alt +alt

Warning

Make sure the images in your spritesheet are laid out in a constant-sized grid. This will enable Godot to automatically slice them. If they’re packed irregularly, you will not be able to use the following technique.

Node setup

This animation technique uses a Sprite2D node to display the texture, and then we animate the changing frames with AnimationPlayer. This can work with any 2D node, but for this demo, we’ll use a CharacterBody2D.

Add the following nodes to your scene:

CharacterBody2D: Player
    Sprite2D
    CollisionShape2D
    AnimationPlayer
-

Drag the spritesheet texture into the Texture property of the Sprite2D. You’ll see the entire spritesheet displayed in the viewport. To slice it up into individual frames, expand the “Animation” section in the Inspector and set the Hframes to 13 and Vframes to 8. Hframes and Vframes are the number of horizontal and vertical frames in your spritesheet.

alt -alt

Try changing the Frame property to see the image change. This is the property we’ll be animating.

Adding animations

Select the AnimationPlayer and click the “Animation” button followed by “New" -. Name the new animation “idle”. Set the animation length to 2 and click the “Loop” button so that our animation will repeat (see below).

With the scrubber at time 0, select the Sprite2D node. Set its Animation/Frame to 0, then click the key icon next to the value.

alt -alt

If you try playing the animation, you’ll see it doesn’t appear to do anything. That’s because the last frame (12) looks the same as the first (0), but we’re not seeing any of the frames in-between (1-11). To fix this, change the “Update Mode” of the track from its default value of “Discrete” to “Continuous”. You can find this button at the end of the track on the right side.

alt -alt

Note that this will only work for spritesheets where the frames are already in order. If they are not, you’ll have to keyframe each Frame seperately along the timeline.

alt -alt

Feel free to add the other animations yourself. For example, the “jump” animation is on frames 65 through 70.

+ + \ No newline at end of file diff --git a/docs/4.x/audio/audio_manager/index.html b/docs/4.x/audio/audio_manager/index.html index ff589b45..592b57c5 100644 --- a/docs/4.x/audio/audio_manager/index.html +++ b/docs/4.x/audio/audio_manager/index.html @@ -1,5 +1,5 @@ -Audio Manager :: Godot 4 Recipes - +Audio Manager :: Godot 4 Recipes +

Audio Manager

Problem

You’ve tried adding an AudioStreamPlayer to your mob/coin/etc. to play when the object dies or is collected. But the problem is that when you remove the object, the audio player goes with it, chopping off the sound. You need an easier way to manage playing audio.

Solution

We’ll solve this problem with a node that is available from anywhere in the SceneTree. This node manages a set of AudioStreamPlayer nodes and a queue of sound streams to play.

Create a new script in the script editor.

extends Node
 
@@ -35,22 +35,24 @@
         available[0].stream = load(queue.pop_front())
         available[0].play()
         available.pop_front()
-

Set this script as an autoload in Project Settings. Give it an easily recognizable name, such as “AudioStreamManager”.

alt -alt

Anywhere in your project that you want to play a sound, use:

AudioStreamManager.play("res://path/to/sound")
+

Set this script as an autoload in Project Settings. Give it an easily recognizable name, such as “AudioStreamManager”.

alt +alt

Anywhere in your project that you want to play a sound, use:

AudioStreamManager.play("res://path/to/sound")
 
Note

This audio manager is adapted with thanks from [SFXPlayer by TheDuriel] -(https://github.com/TheDuriel/DurielsGodotUtilities).

Example project

Below you can download an example project showing the use of the audio manager node. This project reads a folder full of audio files and generates a grid of buttons. Click the button to play the sound.

alt -alt

At the top, you can see the audio manager’s live statistics.

Download This Project

Download the project’s example code here: https://github.com/godotrecipes/audio_manager

+ + \ No newline at end of file diff --git a/docs/4.x/audio/index.html b/docs/4.x/audio/index.html index f13d2d75..6adb4a4f 100644 --- a/docs/4.x/audio/index.html +++ b/docs/4.x/audio/index.html @@ -1,17 +1,19 @@ -Audio :: Godot 4 Recipes - +Audio :: Godot 4 Recipes +

 Audio

Helpful recipes for adding sound effects and music to your game.

In this section:

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/file_io/index.html b/docs/4.x/basics/file_io/index.html index b5d4e0d6..44fecc35 100644 --- a/docs/4.x/basics/file_io/index.html +++ b/docs/4.x/basics/file_io/index.html @@ -1,5 +1,5 @@ -Saving/loading data :: Godot 4 Recipes - +Saving/loading data :: Godot 4 Recipes +

Saving/loading data

Problem

You need to save and load local data between game sessions.

Solution

Godot’s file I/O (input/output) system is based around the FileAccess object. You open a file by calling open().

var file = FileAccess.open("user://myfile.name", File.READ)
 
Warning

User data should only be stored in the user:// path. While res:// can be used when running from the editor, when your project is exported, the res:// path becomes read-only.

The second argument after the file path is the “Mode Flag”, which can be one of the following:

  • FileAccess.READ - Open for reading.
  • FileAccess.WRITE - Open for writing. Creates the file if it doesn’t exist and truncates if it does.
  • FileAccess.READ_WRITE - Open for reading and writing. Doesn’t truncate the file.
  • FileAccess.WRITE_READ - Open for reading/writing. Creates the file if it doesn’t exist and truncates if it does.

Storing data

You can save data using its specific data type (store_float(), store_string(), etc.), or using the generic store_var(), which will use Godot’s built-in serialization to encode your data, including complex data like objects (more on this later).

Let’s start with a small example: saving the player’s high score. We can write a function that we can call whenever the score needs to be saved:

var save_path = "user://score.save"
@@ -34,15 +34,17 @@
 

Resources can contain subresources, so you could have your player’s inventory Resource included as well, and so on.

What about JSON?

I see it very often (and some readers may be asking it already): “What if I want to use JSON to save my data?” This is my response:

Don’t use JSON for your save files!

While Godot has JSON support, saving game data is not what JSON is for. JSON is a data interchange format - its purpose is to allow systems using different data formats and/or languages to exchange data between each other.

This means JSON has limitations that are negatives for you when it comes to saving your game data. JSON doesn’t support many data types (no int vs. float, for example) so you have to do a lot of converting and validating to try and save/load your data. It’s cumbersome and time consuming.

Don’t waste your time. Using Godot’s built-in serialization, you can store native Godot objects - Nodes, Resources, even Scenes - without any effort, which means less code and fewer errors.

There’s a reason that Godot itself doesn’t use JSON for saving scenes and resources.

Wrapping up

This article just scratches the surface of what you can do with FileAccess. For the full list of available FileAccess methods, see the FileAccess documentation.

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/getting_nodes/index.html b/docs/4.x/basics/getting_nodes/index.html index 2dd53867..d06530c7 100644 --- a/docs/4.x/basics/getting_nodes/index.html +++ b/docs/4.x/basics/getting_nodes/index.html @@ -1,11 +1,11 @@ -Understanding node paths :: Godot 4 Recipes - +Understanding node paths :: Godot 4 Recipes +

Understanding node paths

Problem

It’s probably the most common problem seen in the Godot help channels: an invalid node reference. Most often, it appears as the following error message:

Invalid get index ‘position’ (on base: ’null instance’).

Solution

It’s that last part, the “null instance”, that’s the source of this problem, and the main source of confusion for Godot beginners.

The way to avoid this problem is to understand the concept of node paths.

Understanding node paths

The scene tree is made of nodes, which are connected together in parent-child relationships. A node path is the path it takes to get from one node to another by moving through this tree.

As an example, let’s take a simple “Player” scene:

alt -alt

The script for this scene is on the Player node. If the script needs to call play() on the AnimatedSprite node, it needs a reference to that node:

get_node("AnimatedSprite").play()
+

Understanding node paths

Problem

It’s probably the most common problem seen in the Godot help channels: an invalid node reference. Most often, it appears as the following error message:

Invalid get index ‘position’ (on base: ’null instance’).

Solution

It’s that last part, the “null instance”, that’s the source of this problem, and the main source of confusion for Godot beginners.

The way to avoid this problem is to understand the concept of node paths.

Understanding node paths

The scene tree is made of nodes, which are connected together in parent-child relationships. A node path is the path it takes to get from one node to another by moving through this tree.

As an example, let’s take a simple “Player” scene:

alt +alt

The script for this scene is on the Player node. If the script needs to call play() on the AnimatedSprite node, it needs a reference to that node:

get_node("AnimatedSprite").play()
 

The argument of the get_node() function is a string representing the path to the desired node. In this case, it’s a child of the node the script is on. If the path you give it is invalid, you’ll get the dreaded null instance error (as well as “Node not found”).

Getting a node reference with get_node() is such a common situation that GDScript has a shortcut for it:

$AnimatedSprite.play()
-
Info

get_node() returns a reference to the desired node.

Let’s look at a more complex scene tree:

alt -alt

If the script on Main needs to access ScoreLabel it can do so with this path:

get_node("HUD/ScoreLabel").text = "0"
+
Info

get_node() returns a reference to the desired node.

Let’s look at a more complex scene tree:

alt +alt

If the script on Main needs to access ScoreLabel it can do so with this path:

get_node("HUD/ScoreLabel").text = "0"
 # or using the shortcut:
 $HUD/ScoreLabel.text = "0"
 
Tip

When using $ notation, the Godot editor will autocomplete paths for you. You can also right-click on a node in the Scene tab and choose “Copy Node Path”.

What if the node you want to access is higher in the tree? You can use get_parent() or ".." to reference the parent node. In the above example tree, to get the Player node from the ScoreLabel:

get_node("../../Player")
@@ -16,15 +16,17 @@
 

While this may work fine at first, it is brittle, meaning it can break easily. There are two main problems with this kind of arrangement:

  1. You can’t test the player scene independently. If you run the player scene by itself or in a test scene that doesn’t have a UI, the get_node() line will cause a crash.
  2. You can’t change your UI. If you decide to rearrange or redesign your UI, the path will no longer be valid and you have to change it.

For this reason, you should try to avoid using node paths that go up the scene tree. In the above situation, if the player instead emitted a signal when the health changed, the UI could listen for that signal to update itself. You could then rearrange and separate nodes without fear of breaking your game.

Wrapping up

Once you understand how to use node paths, you’ll see how easy it is to reference any node you need. And put a stop to seeing those null instance error messages.

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/index.html b/docs/4.x/basics/index.html index 55b04aa5..b9f24ad5 100644 --- a/docs/4.x/basics/index.html +++ b/docs/4.x/basics/index.html @@ -1,17 +1,19 @@ -Basics :: Godot 4 Recipes - +Basics :: Godot 4 Recipes + - - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/migrating/index.html b/docs/4.x/basics/migrating/index.html index 5378c51f..a282f124 100644 --- a/docs/4.x/basics/migrating/index.html +++ b/docs/4.x/basics/migrating/index.html @@ -1,5 +1,5 @@ -Migrating from 3.x :: Godot 4 Recipes - +Migrating from 3.x :: Godot 4 Recipes +

Migrating from 3.x

This is an evolving list of the main changes and “gotchas” to look out for if you’re transitioning to 4.0.

New Names

One of the biggest changes in Godot 4 is a whole bunch of renaming - of nodes, functions, and property names. Most of it is done to make things consistent or clear. Here are a few of the biggest ones to watch out for:

  • 2D/3D nodes - In Godot 3.x, 2D nodes had the “2D” suffix, but 3D nodes had none. This has been made consistent - they all now have “2D” or “3D” suffixes. For example: RigidBody2D vs. RigidBody3D.

  • Also in the category of 3D, the Spatial node is renamed to Node3D to match.

  • One of the most popular nodes, KinematicBody, has been renamed to CharacterBody2D/CharacterBody3D. See below for further changes with this node’s API.

  • PackedScene’s instance() function has been renamed to instantiate().

  • The position and global_position properties replace translation and global_translation in 3D, making them consistent with 2D.

Signals and Callables

Working with signals is much more streamlined in 4.0. Signal is a native type now, so you’ll be using fewer strings, meaning you get autocomplete and error checking. This applies to functions as well, which can now be directly referenced rather than using strings.

Here’s an example of defining, connecting, and emitting a signal.

extends Node
 
@@ -22,15 +22,17 @@
 
- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/node_communication/index.html b/docs/4.x/basics/node_communication/index.html index e5c0919b..be0371e5 100644 --- a/docs/4.x/basics/node_communication/index.html +++ b/docs/4.x/basics/node_communication/index.html @@ -1,32 +1,32 @@ -Node communication (the right way) :: Godot 4 Recipes - +Node communication (the right way) :: Godot 4 Recipes +

Node communication (the right way)

Info

Many thanks to @TheDuriel on the Godot Discord for the original diagram that inspired this article. Save this and keep it handy.

Problem

Your project has started getting complex. You have multiple scenes, instances, and a lot of nodes. You’ve probably found yourself writing code like the following:

get_node("../../SomeNode/SomeOtherNode")
 get_parent().get_parent().get_node("SomeNode")
 get_tree().get_root().get_node("SomeNode/SomeOtherNode")
-

If you do this, you’ll soon find that node references like this break easily. As soon as you change one thing about your scene tree, none of those references may be valid anymore.

Communication between nodes and scenes doesn’t have to be complicated. There is a better way.

Solution

As a general rule, nodes should manage their children, not the other way around. If you’re using get_parent() or get_node(".."), then you’re probably headed for trouble. Node paths like this are brittle, meaning they can break easily. The three main problems with this arrangement:

  1. You can’t test a scene independently. If you run the scene by itself or in a test scene that doesn’t have the exact same node setup, get_node() will cause a crash.

  2. You can’t change things easily. If you decide to rearrange or redesign your tree, paths will no longer be valid.

  3. Ready order is children-first, parent-last. This means that trying to access a parent’s property in a node’s _ready() can fail because the parent isn’t ready yet.

Tip

See Understanding tree order for an explanation of how nodes enter the tree and become ready.

Generally speaking, a node or scene should be able to be instanced anywhere in your game, and it should make no assumptions about what its parent is going to be.

We’ll go into detailed examples later in this tutorial, but for now, here’s the “golden rule” of node communication:

Call down, signal up.

If a node is calling a child (i.e. going “down” the tree), then get_node() is appropriate.

If a node needs to communicate “up” the tree, it should probably use a signal.

If you keep this rule in mind when designing your scene setup, you’ll be well on your way to a maintainable, well-organized project. And you’ll avoid using the cumbersome node paths that lead to problems.

Now, let’s look at each of these strategies along with some examples.

1. Using get_node()

get_node() traverses the scene tree using a given path to find the named node.

Tip

See Understanding node paths for a more detailed explanation of node paths.

get_node() example

Let’s consider the following common configuration:

alt -alt

The script in the Player node needs to notify the AnimatedSprite2D which animation to play, based on the player’s movement. In this situation, get_node() works well:

extends CharacterBody2D
+

If you do this, you’ll soon find that node references like this break easily. As soon as you change one thing about your scene tree, none of those references may be valid anymore.

Communication between nodes and scenes doesn’t have to be complicated. There is a better way.

Solution

As a general rule, nodes should manage their children, not the other way around. If you’re using get_parent() or get_node(".."), then you’re probably headed for trouble. Node paths like this are brittle, meaning they can break easily. The three main problems with this arrangement:

  1. You can’t test a scene independently. If you run the scene by itself or in a test scene that doesn’t have the exact same node setup, get_node() will cause a crash.

  2. You can’t change things easily. If you decide to rearrange or redesign your tree, paths will no longer be valid.

  3. Ready order is children-first, parent-last. This means that trying to access a parent’s property in a node’s _ready() can fail because the parent isn’t ready yet.

Tip

See Understanding tree order for an explanation of how nodes enter the tree and become ready.

Generally speaking, a node or scene should be able to be instanced anywhere in your game, and it should make no assumptions about what its parent is going to be.

We’ll go into detailed examples later in this tutorial, but for now, here’s the “golden rule” of node communication:

Call down, signal up.

If a node is calling a child (i.e. going “down” the tree), then get_node() is appropriate.

If a node needs to communicate “up” the tree, it should probably use a signal.

If you keep this rule in mind when designing your scene setup, you’ll be well on your way to a maintainable, well-organized project. And you’ll avoid using the cumbersome node paths that lead to problems.

Now, let’s look at each of these strategies along with some examples.

1. Using get_node()

get_node() traverses the scene tree using a given path to find the named node.

Tip

See Understanding node paths for a more detailed explanation of node paths.

get_node() example

Let’s consider the following common configuration:

alt +alt

The script in the Player node needs to notify the AnimatedSprite2D which animation to play, based on the player’s movement. In this situation, get_node() works well:

extends CharacterBody2D
 
 func _process(delta):
     if speed > 0:
         get_node("AnimatedSprite2D").play("run")
     else:
         get_node("AnimatedSprite2D").play("idle")
-
Tip

In GDScript you can use $ as a shorthand for get_node(), writing $AnimatedSprite2D instead.

2. Using signals

Signals should be used to call functions on nodes that are higher in the tree or at the same level (i.e. “siblings”).

You can connect a signal in the editor (most often for nodes that exist before the game starts) or in code (for nodes that you’re instancing at runtime). The syntax for connecting a signal is:

signal_name.connect(target_node.target_function)

Looking at this, you may be thinking “Wait, if I’m connecting to a sibling, won’t I need a node paths like ../Sibling?”. While you could do this, it breaks our rule above. The answer to this puzzle is to make sure that connections are made by the common parent.

Following the rule of calling down the tree, a node that’s a common parent to the signaling and receiving nodes will by definition know where they are and be ready after both of them.

Signal example

A very common use case for signals is updating your UI. Whenever the player’s health variable changes, you want to update a Label or ProgressBar display. However, your UI nodes are completely separated from your player (as they should be). The player knows nothing about where those nodes are and how to find them.

Here’s our example setup:

alt -alt

Note that the UI is an instanced scene, we’re just showing the contained nodes. This is where you often see things like get_node("../UI/VBoxContainer/HBoxContainer/Label).text = str(health), which is what we want to avoid.

Instead the player emits a health_changed signal whenever it adds/loses health. We need to send that signal to the UI’s update_health() function, which handles setting the Label value. In the Player script we use this code whenever the player’s health is changed:

health_changed.emit(health)
+
Tip

In GDScript you can use $ as a shorthand for get_node(), writing $AnimatedSprite2D instead.

2. Using signals

Signals should be used to call functions on nodes that are higher in the tree or at the same level (i.e. “siblings”).

You can connect a signal in the editor (most often for nodes that exist before the game starts) or in code (for nodes that you’re instancing at runtime). The syntax for connecting a signal is:

signal_name.connect(target_node.target_function)

Looking at this, you may be thinking “Wait, if I’m connecting to a sibling, won’t I need a node paths like ../Sibling?”. While you could do this, it breaks our rule above. The answer to this puzzle is to make sure that connections are made by the common parent.

Following the rule of calling down the tree, a node that’s a common parent to the signaling and receiving nodes will by definition know where they are and be ready after both of them.

Signal example

A very common use case for signals is updating your UI. Whenever the player’s health variable changes, you want to update a Label or ProgressBar display. However, your UI nodes are completely separated from your player (as they should be). The player knows nothing about where those nodes are and how to find them.

Here’s our example setup:

alt +alt

Note that the UI is an instanced scene, we’re just showing the contained nodes. This is where you often see things like get_node("../UI/VBoxContainer/HBoxContainer/Label).text = str(health), which is what we want to avoid.

Instead the player emits a health_changed signal whenever it adds/loses health. We need to send that signal to the UI’s update_health() function, which handles setting the Label value. In the Player script we use this code whenever the player’s health is changed:

health_changed.emit(health)
 

In the UI script we have:

onready var label = $VBoxContainer/HBoxContainer/Label
 
 func update_health(value):
     label.text = str(value)
 

Now we just need to connect the signal to the function. The perfect place to do that is in World, which is the common parent, and knows where both nodes are:

func _ready():
     $Player.health_changed.connect($UI.update_health)
-

3. Using groups

Groups are another way to decouple, especially when you have a lot of similar objects that need to do the same thing. A node can be added to any number of groups and membership can be changed dynamically at any time with add_to_group() and remove_from_group().

A common misconception about groups is that they are some kind of object or array that “contains” node references. Groups are a tagging system. A node is “in” a group if it has that tag assigned from it. The SceneTree keeps track of the tags and has functions like get_nodes_in_group() to help you find all nodes with a particular tag.

Group example

Let’s consider a Galaga-style space shooter where you have a lots of enemies flying around. These enemies may have different types and behaviors. You’d like to add a “smart bomb” upgrade that, when activated, destroys all enemies on the screen. Using groups, you can implement this with a minimal amount of code.

First, add all enemies to an “enemies” group. You can do this in the editor using the “Node” tab:

alt -alt

You can also add nodes to the group in your script:

func _ready():
+

3. Using groups

Groups are another way to decouple, especially when you have a lot of similar objects that need to do the same thing. A node can be added to any number of groups and membership can be changed dynamically at any time with add_to_group() and remove_from_group().

A common misconception about groups is that they are some kind of object or array that “contains” node references. Groups are a tagging system. A node is “in” a group if it has that tag assigned from it. The SceneTree keeps track of the tags and has functions like get_nodes_in_group() to help you find all nodes with a particular tag.

Group example

Let’s consider a Galaga-style space shooter where you have a lots of enemies flying around. These enemies may have different types and behaviors. You’d like to add a “smart bomb” upgrade that, when activated, destroys all enemies on the screen. Using groups, you can implement this with a minimal amount of code.

First, add all enemies to an “enemies” group. You can do this in the editor using the “Node” tab:

alt +alt

You can also add nodes to the group in your script:

func _ready():
     add_to_group("enemies")
 

Let’s assume every enemy has an explode() function that handles what happens when it dies (playing an animation, spawning dropped items, etc). Now that every enemy is in the group, we can implement our smart bomb function like this:

func activate_smart_bomb():
     get_tree().call_group("enemies", "explode")
-

4. Using owner

owner is a Node property that’s set automatically when you save a scene. Every node in that scene will have its owner set to the scene’s root node. This makes for a convenient way to connect child signals up to the main node.

owner example

In a complex UI, you often find yourself with a very deep, nested hierarchy of containers and controls. Nodes that the user interacts with, such as Button, emit signals, and you may want to connect those signals to the script on the UI’s root node.

Here’s an example setup:

alt -alt

The script on the root CenterContainer has the following function, which we want to call whenever any button is pressed:

extends CenterContainer
+

4. Using owner

owner is a Node property that’s set automatically when you save a scene. Every node in that scene will have its owner set to the scene’s root node. This makes for a convenient way to connect child signals up to the main node.

owner example

In a complex UI, you often find yourself with a very deep, nested hierarchy of containers and controls. Nodes that the user interacts with, such as Button, emit signals, and you may want to connect those signals to the script on the UI’s root node.

Here’s an example setup:

alt +alt

The script on the root CenterContainer has the following function, which we want to call whenever any button is pressed:

extends CenterContainer
 
 func _on_button_pressed(button_name):
     print(button_name, " was pressed")
@@ -37,15 +37,17 @@
 

No matter where you place the buttons in the tree - if you add more containers, for example - the CenterContainer remains the owner.

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/tree_ready_order/index.html b/docs/4.x/basics/tree_ready_order/index.html index eef473ce..4f845051 100644 --- a/docs/4.x/basics/tree_ready_order/index.html +++ b/docs/4.x/basics/tree_ready_order/index.html @@ -1,8 +1,8 @@ -Understanding tree order :: Godot 4 Recipes - +Understanding tree order :: Godot 4 Recipes +

Understanding tree order

Problem

You need to understand in what order Godot handles nodes in the scene tree.

Solution

“Tree order” is mentioned often in the Godot docs and in tutorials. However, it is not always obvious to a beginner what is meant by this. Generally speaking, the order in which nodes are handled in the tree is in top-down fashion, starting at the root and going down each branch in turn.

Scene tree order is something that can cause a great deal of confusion for Godot beginners. In this example, we’ll illustrate in what order things happen.

Here’s our sample node setup:

alt -alt

On each node, we have the following script attached:

extends Node
+

Understanding tree order

Problem

You need to understand in what order Godot handles nodes in the scene tree.

Solution

“Tree order” is mentioned often in the Godot docs and in tutorials. However, it is not always obvious to a beginner what is meant by this. Generally speaking, the order in which nodes are handled in the tree is in top-down fashion, starting at the root and going down each branch in turn.

Scene tree order is something that can cause a great deal of confusion for Godot beginners. In this example, we’ll illustrate in what order things happen.

Here’s our sample node setup:

alt +alt

On each node, we have the following script attached:

extends Node
 
 func _init():
     # Note: a Node doesn't have a "name" yet here.
@@ -46,15 +46,17 @@
 

As you can see, all of these nodes printed their messages in tree order, from top to bottom, following branches first - with the exception of the _ready() code.

Here’s a quote from the Node reference:

Called when the node is “ready”, i.e. when both the node and its children have entered the scene tree. If the node has children, their _ready callbacks get triggered first, and the parent node will receive the ready notification afterwards.

This leads to an important rule-of-thumb to remember when setting up your node structure:

Tip

Parent nodes should manage their children, not vice-versa.

This means any code in the parent must be able to fully access any data in its children. For that reason, _ready() must be processed in reverse tree order.

Remember this when trying to access other nodes in _ready(). If you need to go up the tree to a parent (or grandparent), you should probably run that code in the parent rather than the child.

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/basics/understanding_delta/index.html b/docs/4.x/basics/understanding_delta/index.html index 6a07dfbf..bae5c168 100644 --- a/docs/4.x/basics/understanding_delta/index.html +++ b/docs/4.x/basics/understanding_delta/index.html @@ -1,5 +1,5 @@ -Understanding 'delta' :: Godot 4 Recipes - +Understanding 'delta' :: Godot 4 Recipes +

Understanding 'delta'

Problem

The delta or “delta time” parameter is a frequently-misunderstood concept in game development. In this tutorial, we’ll explain how it’s used, the importance of frame-rate independent movement, and practical examples of its use in Godot.

Solution

To illustrate the problem, let’s consider a Sprite node moving across the screen. If our screen is 600 pixels wide and we want the sprite to take 5 seconds to cross the screen, we can use the following calculation to find the necessary speed:

600 pixels / 5 seconds = 120 pixels/second
 

We’ll move the sprite every frame using the _process() function. If the game is running at 60 frames per second, we can find the per-frame movement like so:

120 pixels/second * 1/60 second/frame = 2 pixels/frame
@@ -10,9 +10,9 @@
 
 func _process(delta):
     $Sprite.position += movement
-

Run this code and you’ll see the sprite takes 5 seconds to cross the screen.

alt -alt

Maybe. The trouble begins if there is something else occupying the computer’s time. This is called lag and can come from a variety of sources - the cause could be your code or even other applications running on your computer. If this happens, then the length of a frame might increase. As an extreme example, imagine that the frame rate is halved - each frame took 1/30 instead of 1/60 of a second. Moving at 2 px/frame, it’s now going to take twice as long for the sprite to reach the edge.

alt -alt

Even small frame rate fluctuations will result in inconsistent movement speed. If this were a bullet or other fast-moving object, we wouldn’t want it slowing down like this. We need the movement to be frame rate independent.

Fixing the frame rate problem

When using the _process() function, it automatically includes a parameter called delta that’s passed in from the engine (so does _physics_process(), which is used for physics-related code). This is a floating point value representing the length of time since the previous frame. Typically, this will be approximately 1/60 or 0.0167 seconds.

With this information, we can stop thinking about how much to move each frame, and only consider our desired speed in pixels/second (120 from the above calculation).

Multiplying the engine’s delta value by this number will give us how many pixels to move each frame. The number will automatically adjust if the frame time fluctuates.

# 60 frames/second
+

Run this code and you’ll see the sprite takes 5 seconds to cross the screen.

alt +alt

Maybe. The trouble begins if there is something else occupying the computer’s time. This is called lag and can come from a variety of sources - the cause could be your code or even other applications running on your computer. If this happens, then the length of a frame might increase. As an extreme example, imagine that the frame rate is halved - each frame took 1/30 instead of 1/60 of a second. Moving at 2 px/frame, it’s now going to take twice as long for the sprite to reach the edge.

alt +alt

Even small frame rate fluctuations will result in inconsistent movement speed. If this were a bullet or other fast-moving object, we wouldn’t want it slowing down like this. We need the movement to be frame rate independent.

Fixing the frame rate problem

When using the _process() function, it automatically includes a parameter called delta that’s passed in from the engine (so does _physics_process(), which is used for physics-related code). This is a floating point value representing the length of time since the previous frame. Typically, this will be approximately 1/60 or 0.0167 seconds.

With this information, we can stop thinking about how much to move each frame, and only consider our desired speed in pixels/second (120 from the above calculation).

Multiplying the engine’s delta value by this number will give us how many pixels to move each frame. The number will automatically adjust if the frame time fluctuates.

# 60 frames/second
 120 pixels/second * 1/60 second/frame = 2 pixels/frame
 
 # 30 frames/second
@@ -24,9 +24,9 @@
 
 func _process(delta):
     $Sprite.position += movement * delta
-

Now when running at 30 frames per second, the travel time is consistent:

alt -alt

If the frame rate gets very low, the movement is no longer smooth, but the time remains the same.

alt -alt

Using delta with motion equations

What if your movement is more complex? The concept remains the same. Keep your units in seconds, not frames, and multiply by delta each frame.

Tip

Working in pixels and seconds is much easier to conceptualize too, since it relates to how we measure these quantities in the real world. “Gravity is 100 pixels/second/second, so after the ball falls for 2 seconds, it’s traveling at 200 pixels/second.” If you’re working with frames, then you have to think about acceleration in units of pixels/frame/frame. Go ahead and try - it’s not very natural.

For example, if you are applying a gravity, that’s an acceleration - each frame it will increase the velocity by some amount. As in the above example, the velocity then changes the node’s position.

Try adjusting delta and target_fps in the following code to see the effect:

extends Node2D
+

Now when running at 30 frames per second, the travel time is consistent:

alt +alt

If the frame rate gets very low, the movement is no longer smooth, but the time remains the same.

alt +alt

Using delta with motion equations

What if your movement is more complex? The concept remains the same. Keep your units in seconds, not frames, and multiply by delta each frame.

Tip

Working in pixels and seconds is much easier to conceptualize too, since it relates to how we measure these quantities in the real world. “Gravity is 100 pixels/second/second, so after the ball falls for 2 seconds, it’s traveling at 200 pixels/second.” If you’re working with frames, then you have to think about acceleration in units of pixels/frame/frame. Go ahead and try - it’s not very natural.

For example, if you are applying a gravity, that’s an acceleration - each frame it will increase the velocity by some amount. As in the above example, the velocity then changes the node’s position.

Try adjusting delta and target_fps in the following code to see the effect:

extends Node2D
 
 # Acceleration in pixels/sec/sec.
 var gravity = Vector2(0, 120)
@@ -59,15 +59,17 @@
 

If you don’t use delta when applying acceleration to your velocity, then your acceleration will be subject to fluctuations in frame rate. This can have a_much more subtle effect on movement - it will be inconsistent, but much more difficult to diagnose.

Tip

When using move_and_slide() you still need to apply delta to any other quantities such as gravity, friction, etc.

- - \ No newline at end of file +
+ + \ No newline at end of file diff --git a/docs/4.x/categories/index.html b/docs/4.x/categories/index.html index 7d1d4bc7..f8f84298 100644 --- a/docs/4.x/categories/index.html +++ b/docs/4.x/categories/index.html @@ -1,17 +1,19 @@ -Categories :: Godot 4 Recipes - +Categories :: Godot 4 Recipes +

Categories

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/3d/101_3d_01/index.html b/docs/4.x/g101/3d/101_3d_01/index.html index 315d1d35..018eb368 100644 --- a/docs/4.x/g101/3d/101_3d_01/index.html +++ b/docs/4.x/g101/3d/101_3d_01/index.html @@ -1,5 +1,5 @@ -The 3D Editor :: Godot 4 Recipes - +The 3D Editor :: Godot 4 Recipes +

    The 3D Editor

    In this tutorial, we’ll look at how to start working in 3D in Godot. You’ll learn how to navigate in the 3D editor, how to create and manipulate 3D objects, @@ -15,8 +15,8 @@ much of what you’ve learned working on 2D projects (nodes, scenes, signals, etc.) applies equally well in 3D, there is also a whole new layer of complexity and capabilities. First, you’ll find that there are some additional features -available in the 3D editor window, so we’ll start there:

    Orienting in 3D Space

    When you first open a new project in Godot, you will see the 3D project view:

    alt -alt

    The first thing you should notice is the three colored lines in the center. +available in the 3D editor window, so we’ll start there:

    Orienting in 3D Space

    When you first open a new project in Godot, you will see the 3D project view:

    alt +alt

    The first thing you should notice is the three colored lines in the center. These are the x (red), y (green), and z (blue) axes. The point where they meet is the origin, which has the coordinates (0, 0, 0). You’ll find that this color scheme will also apply elsewhere in the Inspector.

    Note

    Different 3D applications follow different conventions for orientation. @@ -28,12 +28,12 @@ mode, which you can toggle on/off using Shift+F. In this mode, you can use the WASD keys to fly around the scene while aiming with the mouse.

    You can also alter the camera’s view by clicking on the [Perspective] label in the upper-left corner. Here, you can snap the camera to a particular -orientation.

    alt -alt

    Adding 3D Objects

    Now let’s add our first 3D node. Just as all 2D nodes inherit from Node3D, +orientation.

    alt +alt

    Adding 3D Objects

    Now let’s add our first 3D node. Just as all 2D nodes inherit from Node3D, which provides properties such as position and rotation, 3D nodes inherit from Node3D, which provides 3D versions of the same properties. Add one to your scene and you’ll see the following object -appear at the origin:

    alt -alt

    This object is not the node. It is something called a 3D gizmo. Gizmos are +appear at the origin:

    alt +alt

    This object is not the node. It is something called a 3D gizmo. Gizmos are tools that allow you to move and rotate objects in space. The three rings control rotation, while the three arrows move (translate) the object along the three axes. Note that the rings and arrows are color-coded to match the @@ -41,27 +41,27 @@ you find yourself getting lost.

    Tip

    Sometimes you may feel the gizmos are getting in your way. You can click on the mode icons to restrict yourself to only one type of transformation: move, rotate, or scale: -alt -alt

    Global vs. Local Space

    By default, the gizmo controls operate in global space. When you rotate the +alt +alt

    Global vs. Local Space

    By default, the gizmo controls operate in global space. When you rotate the object, the gizmo’s arrows still point along the axes. However, if you click the Use Local Space button, the gizmo will switch to moving the body in -local space.

    alt -alt

    Now when you rotate the object, the gizmo arrows point along the object’s +local space.

    alt +alt

    Now when you rotate the object, the gizmo arrows point along the object’s axes and not the world’s. Switching back and forth between Local and World space can make it much easier to place an object exactly where you want it.

    Transforms

    Look at the Inspector for the Node3D node. In the Transform section, you’ll see properties for Position, Rotation, and Scale. Drag the object around with the gizmo and observe how these values change. Just like in 2D, these properties are relative to the node’s parent.

    Together, these properties make up the node’s transform. When changing the node’s spatial properties in code, you’ll access the transform property, which is a Godot Transform3D object. It has two properties: origin and basis. The origin represents the body’s position, while the basis contains three vectors that define the body’s local coordinate axes - think of the three axis arrows in the gizmo when you’re in Local Space mode.

    You’ll see how to use these properties later in this section.

    Meshes

    Just like a Node2D, a Node3D has no size or appearance of its own. In 2D, you would use a Sprite2D to add a texture to the node. In 3D, you need to add a mesh. A mesh is a mathematical description of a shape. It consists of a collection of points, called vertices. These vertices are connected by lines, -called edges, and multiple edges (at least three) together make a face.

    alt -alt

    For example, a cube is made up of 8 vertices, 12 edges, and 6 faces.

    Adding Meshes

    Typically, meshes are created by using 3D modeling software, such as Blender. +called edges, and multiple edges (at least three) together make a face.

    alt +alt

    For example, a cube is made up of 8 vertices, 12 edges, and 6 faces.

    Adding Meshes

    Typically, meshes are created by using 3D modeling software, such as Blender. You can also find many collections of 3D models available for download, if you’re unable to create your own. However, often you just need a basic shape such as a cube or sphere. In this case, Godot provides a way to create -simple meshes called primitives.

    Add a MeshInstance3D node as a child of the Node3D and in the Inspector, click its Mesh property:

    alt -alt

    Here you can see the list of available primitives. They represent a handy +simple meshes called primitives.

    Add a MeshInstance3D node as a child of the Node3D and in the Inspector, click its Mesh property:

    alt +alt

    Here you can see the list of available primitives. They represent a handy collection of common useful shapes. Select “New BoxMesh” and you’ll see a plain cube appear on the screen.

    Cameras

    Try running the scene with your cube object. Did you see anything? In 3D, you -won’t see anything in the game viewport without adding a Camera3D. Add one to the root node and use the camera’s gizmo to position it pointing towards the cube:

    alt -alt

    The pinkish-purple pyramid shape on the camera is called the fustrum and +won’t see anything in the game viewport without adding a Camera3D. Add one to the root node and use the camera’s gizmo to position it pointing towards the cube:

    alt +alt

    The pinkish-purple pyramid shape on the camera is called the fustrum and represents the camera’s view. Notice the small triangular arrow which represents the camera’s “up” orientation. As you’re moving the camera around, try pressing the Preview button in the upper-left to see what the camera sees. Play the @@ -70,15 +70,17 @@ and how to use more of Godot’s 3D nodes.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/3d/101_3d_02/index.html b/docs/4.x/g101/3d/101_3d_02/index.html index abb0a901..a1b521cc 100644 --- a/docs/4.x/g101/3d/101_3d_02/index.html +++ b/docs/4.x/g101/3d/101_3d_02/index.html @@ -1,27 +1,27 @@ -Importing 3D Objects :: Godot 4 Recipes - +Importing 3D Objects :: Godot 4 Recipes +

    Importing 3D Objects

    In the last part, we started a 3D project and looked at how to navigate and create 3D objects. In this part, you’ll learn how to import existing 3D objects that you’ve made or downloaded and how to use more of Godot’s 3D nodes.

    Importing 3D Objects

    If you’re familiar with 3D modeling software such as Blender, you can make your own models to use in your game. If not, there are many sources where you can download objects or even collections of objects for particular game types. One of our favorite makers of free game art is Kenney.nl.

    For our tutorials, we’re going to use Kenney’s Platformer Kit, which you can download here: https://kenney.nl/assets/platformer-kit

    This kit has a wide selection of objects that we can use to practice our Godot -3D skills. Here’s a sample showing what the kit looks like:

    alt -alt

    Once you’ve downloaded the kit, you’ll find that the objects inside are provided +3D skills. Here’s a sample showing what the kit looks like:

    alt +alt

    Once you’ve downloaded the kit, you’ll find that the objects inside are provided in a variety of different formats. Godot is able to use several of these, but since GLTF is available in this pack, it’s preferred over the others. Drop the GLTF format folder into your Godot project’s folder and rename it to “platformer_kit”.

    3D file formats

    Whether you create your own models or download the, you’ll need them to be saved in a format that Godot can use. Godot supports the following 3D file formats:

    • glTF - supported in both text (.gltf) and binary (.glb) versions
    • DAE (Collada) - an older format that is still supported
    • OBJ (Wavefront) - an older format that is supported, but the format is limited compared to modern options
    • FBX - a commercial format that has limited support

    glTF is the recommended format - it has the most features and is very well supported in Godot.

    When you switch back to your Godot window, you’ll see progress bar while Godot scans the folder and imports all of the objects. Let’s click on one of them to -see what’s going on. In the FileSystem tab, double-click on crate.glb:

    alt -alt

    Here you can see the object will be imported as a scene, with its root type +see what’s going on. In the FileSystem tab, double-click on crate.glb:

    alt +alt

    Here you can see the object will be imported as a scene, with its root type set to Node3D and named “Scene Root”. Let’s change these: set the root type to RigidBody3D and the root name to “Crate”, then click the “Reimport” button.

    Now right-click on “crate.glb” and choose New Inherited Scene. Here we have a classic game object: the crate. The root node of the scene is a RigidBody3D named “Crate” just as we wanted.

    Finally, we need to add a collision shape to the body. While we could do this by adding a CollionShape3D, as you would typically do in 2D, but there’s a quicker way.

    Select the crate2 mesh and you’ll see a Mesh menu appear at the top of the viewport. Click it and select Create Single Convex Collision Sibling. Godot will automatically add a CollionShape3D with a collision shape that matches the mesh.

    Now we’re finished setting up the object. Save your Crate scene and let’s see how we can use it.

    Building a 3D Scene

    Create a new scene with a Node3D root. The first child we’ll add is one to give us a “ground” to stack some crates on. Add a StaticBody3D called “Ground”, and to that add a MeshInstance3D. In the Mesh property, select “New BoxMesh” and then click it to open its properties. Set Size to (10, 0.1, 10) so that we have a nice large surface. However, it would look better if it weren’t plain white.

    Also in the mesh properties is a Material property. Materials are how you define the appearance of an object. Select “New StandardMaterial3D” and then click it to open a large list of properties. To set the color of the mesh, we need the Albedo/Color property. Choose a color, such as brown or dark green.

    If we add a crate, it will fall right through the mesh, so we also need to give it a collision shape. Add a CollisionShape3D to the Ground and choose “New BoxShape3D”. Set the collision box to the same size as the mesh.

    Now instance a few crates in the scene and arrange them in a rough stack. Add a Camera and place it where it has a good view of the crates. Run the scene -and watch your crates go tumbling!

    alt -alt

    Why is the scene so dark? Because there’s no light! By default, Godot doesn’t add any lighting or environment to your scenes, like it does in the editor viewport. This is great when you want to set up your own specific lighting, but for a quick example scene like this, there’s a shortcut.

    Lighting

    There are multiple light nodes available in 3D, which you can use to create a variety of lighting effects. But we’re going to start with DirectionalLight3D. However, instead of adding one manually, we’re going to have Godot use the same one it’s using in the editor window. At the top ove the viewport, there are two icons that control the preview lighting and preview environment. If you click the three dots next to them, you can see their settings.

    alt -alt

    Click the Add Sun to Scene button, and Godot will add a DirectionalLight3D to your scene. Click Add Environment to Scene and it will do the same with the preview sky by adding a WorldEnvironment node.

    Run the scene again, and you’ll be able to see your crates falling.

    Rotating Camera

    Let’s make the camera a little more dynamic by having it slowly orbit around the +and watch your crates go tumbling!

    alt +alt

    Why is the scene so dark? Because there’s no light! By default, Godot doesn’t add any lighting or environment to your scenes, like it does in the editor viewport. This is great when you want to set up your own specific lighting, but for a quick example scene like this, there’s a shortcut.

    Lighting

    There are multiple light nodes available in 3D, which you can use to create a variety of lighting effects. But we’re going to start with DirectionalLight3D. However, instead of adding one manually, we’re going to have Godot use the same one it’s using in the editor window. At the top ove the viewport, there are two icons that control the preview lighting and preview environment. If you click the three dots next to them, you can see their settings.

    alt +alt

    Click the Add Sun to Scene button, and Godot will add a DirectionalLight3D to your scene. Click Add Environment to Scene and it will do the same with the preview sky by adding a WorldEnvironment node.

    Run the scene again, and you’ll be able to see your crates falling.

    Rotating Camera

    Let’s make the camera a little more dynamic by having it slowly orbit around the scene. Select the root node and add a Node3D, which will be located at (0, 0, 0) and name it “CameraHub”. In the scene tree, drag the camera to make it a child of this new node. Now, if the CameraHub rotates around the y axis, it will drag the camera along with it.

    Add a script to the root node and add the following:

    extends Node3D
     
    @@ -33,15 +33,17 @@
     a player-controlled character.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/3d/101_3d_03/index.html b/docs/4.x/g101/3d/101_3d_03/index.html index 8ceea30e..a5c517dc 100644 --- a/docs/4.x/g101/3d/101_3d_03/index.html +++ b/docs/4.x/g101/3d/101_3d_03/index.html @@ -1,5 +1,5 @@ -Creating a 3D Character :: Godot 4 Recipes - +Creating a 3D Character :: Godot 4 Recipes +

    Creating a 3D Character

    In the last part, we covered how to import 3D objects and how to arrange them in a scene. In this installment, we’ll add more objects to the scene, including a user-controlled character.

    Building the Scene

    We’re going to continue using the Kenney Platformer Kit we downloaded in Part 2. Select all the block*.glb files and in the Import tab set their Root Type to StaticBody3D. Uncheck the Root Name property and click Reimport. Select blockLarge.glb and make a new inherited scene. Use the Create Single Convex Collision Sibling option on the mesh using the menu as you did in the last tutorial. Now you can save the scene - I recommend making a separate folder for this, as soon you’re going to have a bunch of scenes representing the differently shaped platform parts.

    Open the scene from the previous step with the “Ground” plane and the crates. Delete the crates and add an instance of the large block. We want to be able @@ -7,12 +7,12 @@ from the “Transform” menu at the top of the Viewport and set Translate Snap to 0.5. Then click on the “Snap Mode” button (or press the Y key). Now duplicate the block a few times and drag them to arrange.

    If you like, go ahead and add scenes for some of the other platform blocks and -arrange them into a pleasing level. Be creative!

    alt -alt

    Adding a Character

    Now we’re going to make a character so we can walk around on the platforms. +arrange them into a pleasing level. Be creative!

    alt +alt

    Adding a Character

    Now we’re going to make a character so we can walk around on the platforms. Open a new scene and start with a CharacterBody3D named “Character”. This PhysicsBody node behaves very much like its 2D equivalent (you’ve already done the 2D tutorials, right?). It has a move_and_slide() method that we’ll use to perform the movement and collision detection.

    Add a capsule-shaped MeshInstance3D and a matching CollionShape3D. Remember, you can add a StandardMaterial3D to the mesh and set its Albedo/Color property to change the color.

    The capsule is nice, but it’s going to be hard to tell what direction it’s facing. Let’s add another mesh, this time with a CylinderMesh3D shape. -Set its Top Radius to 0.2, its Bottom Radius to 0.001 and its Height to 0.5, then its x rotation to -90 degrees. Now you have a nice cone shape. Arrange it so it’s pointing out from the body along the negative z axis. (You can tell which way is negative because the gizmo arrows point in the positive direction).

    alt -alt

    In this picture, we’ve also added two sphere meshes for eyes to give a little more character. Feel free to add whatever details you like.

    Let’s also add a Camera3D to the scene, so it will follow the player around. Position the camera behind and above the character, angling it down a bit. Click the “Preview” button to check the camera’s view.

    Before we add a script, open the “Project Settings” and add the following inputs +Set its Top Radius to 0.2, its Bottom Radius to 0.001 and its Height to 0.5, then its x rotation to -90 degrees. Now you have a nice cone shape. Arrange it so it’s pointing out from the body along the negative z axis. (You can tell which way is negative because the gizmo arrows point in the positive direction).

    alt +alt

    In this picture, we’ve also added two sphere meshes for eyes to give a little more character. Feel free to add whatever details you like.

    Let’s also add a Camera3D to the scene, so it will follow the player around. Position the camera behind and above the character, angling it down a bit. Click the “Preview” button to check the camera’s view.

    Before we add a script, open the “Project Settings” and add the following inputs on the “Input Map” tab:

    Input ActionKey
    move_forwardW
    move_backS
    strafe_rightD
    strafe_leftA
    jumpSpace

    Now let’s add a script to the body.

    extends CharacterBody3D
     
     var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
    @@ -34,13 +34,13 @@
     accelerate in the positive Y direction (downward), call get_input() to
     check for input, and then use move_and_slide() to move in the direction
     of the velocity vector.

    In get_input() we check to see which key is pressed and then move in that -direction. Run the program and test:

    alt -alt

    This is all good, but we need to be able to rotate using the mouse. Add the following code to the character’s script:

    func _unhandled_input(event):
    +direction. Run the program and test:

    alt +alt

    This is all good, but we need to be able to rotate using the mouse. Add the following code to the character’s script:

    func _unhandled_input(event):
         if event is InputEventMouseMotion:
             rotate_y(-event.relative.x * mouse_sensitivity)
     

    This will convert any mouse motion in the x direction into a rotation around -the y axis.

    Run the scene and confirm that moving the mouse rotates the character:

    alt -alt

    However, there’s a problem. No matter which way we’re facing, pressing W +the y axis.

    Run the scene and confirm that moving the mouse rotates the character:

    alt +alt

    However, there’s a problem. No matter which way we’re facing, pressing W moves us along the Z axis of the world. Our movement is using global coordinates, but we need to move in the object’s forward direction.

    The Power of Transforms

    This is where transforms come in. A transform is a mathematical matrix that contains the object’s translation, rotation, and scale information all in one. In Godot it’s stored in the Transform data type. The position information is called the transform.origin and the orientation information is in the transform.basis.

    Remember how the 3D gizmo can be set to “Local Space Mode”? When in this mode, @@ -49,22 +49,24 @@ var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed -

    By multiplying the input vector by the transform.basis, we apply that transformation to the vector. Since the basis represents the object’s rotation, we’ve now converted forward and back to point along the object’s Z axis, and the strafe keys along its X.

    alt -alt

    Jumping

    Let’s add one more movement to the player: jumping.

    Add these lines to the end of get_input():

    if event.is_action_pressed("jump") and is_on_floor():
    +

    By multiplying the input vector by the transform.basis, we apply that transformation to the vector. Since the basis represents the object’s rotation, we’ve now converted forward and back to point along the object’s Z axis, and the strafe keys along its X.

    alt +alt

    Jumping

    Let’s add one more movement to the player: jumping.

    Add these lines to the end of get_input():

    if event.is_action_pressed("jump") and is_on_floor():
         velocity.y = jump_speed
     

    Improving the camera

    You may have noticed that the if the character stands near an obstacle, the camera can “clip” inside the object, which doesn’t look nice. While coding a good 3D camera can be a complex topic on its own, we can use a built-in Godot node to get a pretty good solution.

    Delete the Camera3D from the character scene and add a SpringArm3D. This node can act as a moving arm that holds the camera while detecting collisions. It will move the camera closer if there’s an obstacle.

    In its properties, set Spring Length to 5, and set its Position to (0, 1, 0), which is at the character’s head. Note the yellow line indicating the Spring Length. The camera will move along this line - at its end whenever possible, but moving closer if an obstacle is there.

    Add back a Camera3D as a child of the SpringArm3D, and try running the game again. You can experiment with rotating the spring arm (around its x axis to point down slightly, for example) until you find something you like.

    What about first person?

    If you’re curious how you would do this in first person, see the Basic FPS Character recipe. You’ll notice several similarities with the 3rd person script we wrote above.

    Wrapping Up

    In this tutorial you learned how to build a more complex scene, and how to write movement code for a user-controlled character. You also learned about transforms, which are a very important concept in 3D - you’re going to be using a lot in the future.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/3d/index.html b/docs/4.x/g101/3d/index.html index ae98e27e..eeac5407 100644 --- a/docs/4.x/g101/3d/index.html +++ b/docs/4.x/g101/3d/index.html @@ -1,17 +1,19 @@ -Intro to 3D :: Godot 4 Recipes - +Intro to 3D :: Godot 4 Recipes +
    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/gdscript/gdscript_01/index.html b/docs/4.x/g101/gdscript/gdscript_01/index.html index 866bfaea..6112106f 100644 --- a/docs/4.x/g101/gdscript/gdscript_01/index.html +++ b/docs/4.x/g101/gdscript/gdscript_01/index.html @@ -1,10 +1,10 @@ -Getting started :: Godot 4 Recipes - +Getting started :: Godot 4 Recipes +

    Getting started

    Overview

    Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. For example, a Sprite2D node automatically displays an image, but to move it across the screen, you’ll add a script that tells it how fast, in what direction, and so on.

    You can think of it as the coding version of using the Inspector - GDScript knows all about Godot nodes and how to access them, plus it allows you to change them dynamically.

    GDScript is Godot’s built-in language for scripting and interacting with nodes. The GDScript documentation on the Godot website is a great place to get an overview of the language, and I highly recommend taking the time to read through it.

    Is GDScript Python?

    You’ll often read comments to the effect that “GDScript is based on Python”. That’s somewhat misleading; GDScript uses a syntax that’s modeled on Python’s, but it’s a distinct language that’s optimized for and integrated into the Godot engine. That said, if you already know some Python, you’ll find GDScript looks very familiar.

    Warning

    Many tutorials (and Godot in general) assume that you have at least some programming experience already. If you’ve never coded before, you’ll likely find learning Godot to be a challenge. Learning a game engine is a large task on its own; learning to code at the same time means you’re taking on a lot. If -you find yourself struggling with the code in this section, you may find that working through an introductory Python lesson will help you grasp the basics.

    Structure of a script

    The first line of any GDScript file must be extends <Class>, where <Class> is some existing built-in or user-defined class. For example, if you’re attaching a script to a CharacterBody2D node, then your script would start with extends CharacterBody2D. This states that your script is taking all the functionality of the built-in CharacterBody2D object and extending it with additional functionality created by you.

    In the rest of the script, you can define any number of variables (aka “class properties”) and functions (aka “class methods”).

    Creating a script

    Let’s make our first script. Remember, any node can have a script attached to it.

    Open the editor and add a Sprite2D node to empty scene. Right-click on the new node, and choose “Attach Script”. You can also click the button next to the search box.

    alt -alt

    Next you need to decide where you want the script saved and what to call it. If you’ve named the node, the script will automatically be named to match it (so unless you’ve changed anything this script will likely be called “Sprite.gd”).

    Now the script editor window opens up, and this is your new, empty sprite script. Godot has automatically included some lines of code, as well as some comments describing what they do.

    extends Sprite2D
    +you find yourself struggling with the code in this section, you may find that working through an introductory programming lesson (Python is a good option) will help you grasp the basics.

    Structure of a script

    The first line of any GDScript file must be extends <Class>, where <Class> is some existing built-in or user-defined class. For example, if you’re attaching a script to a CharacterBody2D node, then your script would start with extends CharacterBody2D. This states that your script is taking all the functionality of the built-in CharacterBody2D object and extending it with additional functionality created by you.

    In the rest of the script, you can define any number of variables (aka “class properties”) and functions (aka “class methods”).

    Creating a script

    Let’s make our first script. Remember, any node can have a script attached to it.

    Open the editor and add a Sprite2D node to empty scene. Right-click on the new node, and choose “Attach Script”. You can also click the button next to the search box.

    alt +alt

    Next you need to decide where you want the script saved and what to call it. If you’ve named the node, the script will automatically be named to match it (so unless you’ve changed anything this script will likely be called “sprite2d.gd”).

    Now the script editor window opens up, and this is your new, empty sprite script. Godot has automatically included some lines of code, as well as some comments describing what they do.

    extends Sprite2D
     
     # Called when the node enters the scene tree for the first time.
     func _ready():
    @@ -13,23 +13,25 @@
     # Called every frame. 'delta' is the elapsed time since the previous frame.
     func _process(delta):
         pass
    -

    Since the script was added to a Sprite2D, the first line is automatically set to extends Sprite2D. Because this script extends the Sprite2D class, it will be able to access and manipulate all the properties and methods that a Sprite2D node provides.

    Properties and methods

    Properties and methods are two terms which specifically mean variables and functions that are defined in an object. Programmers tend to use them interchangeably.

    After that is where you’re going to define all the variables you will use in the script, the “member variables”. You define variables with the ‘var’ keyword - as you can see by the comment examples.

    Go ahead and delete the comments and let’s talk about this next piece.

    Now we see a function called _ready(). In GDScript you define a function with the keyword “func”. The _ready() function is a special one that Godot looks for and runs whenever a node is added to the tree, for example when we hit “Play”.

    Let’s say that when the game starts, we want to make sure the sprite goes to a particular location. In the Inspector, we want to set the Position property. Notice that it’s in the section called “Node2D” - that means this is a property that any Node2D type node will have, not just Sprite2Ds.

    How do we set the property in code? One way to find the name of the property is by hovering over it in the Inspector.

    alt -alt

    Godot has a great built-in help/reference tool. Click on “Classes” at the top of the Script window and search for Node2D and you’ll see a help page showing you all the properties and methods the class has available. Looking down a bit you can see position in the “Member Variables” section - that’s the one we want. It also tells us the property is of the type “Vector2”.

    alt -alt

    Let’s go back to the script and use that property:

    func _ready():
    +

    Since the script was added to a Sprite2D, the first line is automatically set to extends Sprite2D. Because this script extends the Sprite2D class, it will be able to access and manipulate all the properties and methods that a Sprite2D node provides.

    Properties and methods

    Properties and methods are two terms which specifically mean variables and functions that are defined in an object. Programmers tend to use the terms interchangeably.

    After that is where you’re going to define all the variables you will use in the script, the “member variables”. You define variables with the ‘var’ keyword - as you can see by the comment examples.

    Go ahead and delete the comments and let’s talk about this next piece.

    Now we see a function called _ready(). In GDScript you define a function with the keyword “func”. The _ready() function is a special one that Godot looks for and runs whenever a node is added to the tree, for example when we hit “Play”.

    Let’s say that when the game starts, we want to make sure the sprite goes to a particular location. In the Inspector, we want to set the Position property. Notice that it’s in the section called “Node2D” - that means this is a property that any Node2D type node will have, not just Sprite2Ds.

    How do we set the property in code? One way to find the name of the property is by hovering over it in the Inspector.

    alt +alt

    Godot has a great built-in help/reference tool. Click on “Classes” at the top of the Script window and search for Node2D and you’ll see a help page showing you all the properties and methods the class has available. Looking down a bit you can see position in the “Member Variables” section - that’s the one we want. It also tells us the property is of the type “Vector2”.

    alt +alt

    Let’s go back to the script and use that property:

    func _ready():
         position = Vector2(100, 150)
    -

    Notice how the editor is making suggestions as you type. Godot uses vectors for lots of things, and we’ll talk more about them later. For now, let’s type Vector2, and the hint tells us to put two floats for x and y.

    Now we have a script that says “When this sprite starts, set its position to (100, 150)”. We can try this out by pressing the “Play Scene” button.

    alt -alt

    Tip

    When first learning to code, beginners often ask “How do you memorize all these commands?” Just like any other skill, it’s not a matter of memorization, it’s about practice. As you use things more, the things you do frequently will “stick” and become automatic. Until then, it’s a great idea to keep the reference docs handy. Use the search function whenever you see something you don’t recognize. If you have multiple monitors, keep a copy of the web docs open on the side for quick reference.

    Wrapping up

    Congratulations on making your first script in GDScript! Before moving on, make sure you understand everything we did in this step. In the next part, we’ll add some more code to move the sprite around the screen.

    + + \ No newline at end of file diff --git a/docs/4.x/g101/gdscript/index.html b/docs/4.x/g101/gdscript/index.html index 5e335b16..2f027fa8 100644 --- a/docs/4.x/g101/gdscript/index.html +++ b/docs/4.x/g101/gdscript/index.html @@ -1,18 +1,20 @@ -Introduction to GDScript :: Godot 4 Recipes - +Introduction to GDScript :: Godot 4 Recipes +

     GDScript

    GDScript is Godot’s built-in scripting language. Its syntax is based on Python, so if you’re familiar with that language, you’ll feel right at home. In this chapter, we’ll introduce the language and get you up to speed with how it works.

    Updating to Godot 4.0

    We’re working on a new version of Godot 101 for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.

    In this section:

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/index.html b/docs/4.x/g101/index.html index 0e5f5413..3731e15f 100644 --- a/docs/4.x/g101/index.html +++ b/docs/4.x/g101/index.html @@ -1,19 +1,21 @@ -Godot 101 :: Godot 4 Recipes - -

     Godot 101

    alt -alt

    Your introduction to the Godot game engine. If you’ve never used a game engine +Godot 101 :: Godot 4 Recipes + +

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/start/101_01/index.html b/docs/4.x/g101/start/101_01/index.html index 7564cde7..89c487fa 100644 --- a/docs/4.x/g101/start/101_01/index.html +++ b/docs/4.x/g101/start/101_01/index.html @@ -1,5 +1,5 @@ -What is Godot? :: Godot 4 Recipes - +What is Godot? :: Godot 4 Recipes +

    What is Godot?

    Game Engines

    Game development is complex and involves a wide variety of knowledge and skills. In order to build a modern game, you need a lot of underlying technology before @@ -27,8 +27,8 @@ be free to download, but the will require some kind of licensing or royalty agreement if you plan to release your game (and especially if your game makes money). You need to carefully read and understand what you’re agreeing to and -what you are and are not allowed to do with the engine.

    Why use Godot?

    alt -alt +what you are and are not allowed to do with the engine.

    Why use Godot?

    alt +alt Click here to download Godot

    In contrast to the above, Godot is completely free and open source, released under the very permissive MIT license. This means there are no fees, hidden costs, or royalties you need to pay. This is in addition to being a fully @@ -40,15 +40,17 @@ free to modify the engine itself - no permission required.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/start/101_02/index.html b/docs/4.x/g101/start/101_02/index.html index 6bb76204..bc4bee13 100644 --- a/docs/4.x/g101/start/101_02/index.html +++ b/docs/4.x/g101/start/101_02/index.html @@ -1,28 +1,30 @@ -The Godot Editor: Finding your way around :: Godot 4 Recipes - +The Godot Editor: Finding your way around :: Godot 4 Recipes +

    The Godot Editor: Finding your way around

    Project Manager

    The Project Manager is the first thing you’ll see when opening Godot.

    alt -alt

    In this window you can see a list of your Godot projects. You can choose an existing project and click “Run” to play the game or click “Edit” to work on it in the Godot editor. Since you probably don’t have any projects yet, let’s start by clicking the “New Project” -button.

    alt -alt

    Here you can give the project a name and create a folder to store it in.

    Note

    Every Godot project is contained in its own folder. This has many benefits, including making it easy to move, share, and backup +

    The Godot Editor: Finding your way around

    Project Manager

    The Project Manager is the first thing you’ll see when opening Godot.

    alt +alt

    In this window you can see a list of your Godot projects. You can choose an existing project and click “Run” to play the game or click “Edit” to work on it in the Godot editor. Since you probably don’t have any projects yet, let’s start by clicking the “New Project” +button.

    alt +alt

    Here you can give the project a name and create a folder to store it in.

    Note

    Every Godot project is contained in its own folder. This has many benefits, including making it easy to move, share, and backup projects. It also means that all the project’s files (images, sounds, etc.) must be in the project folder.

    When you’re naming your project, try to choose a name that describes the project. “New Game Project #23” is not going to help you remember what that project was. You should also think about compatibility: some operating systems are case-sensitive, and some are not. This can lead to problems if you move or share your project from one computer to another. For this reason, many -programmers develop a standardized naming scheme. For example: “No spaces, use ‘_’ between words.”

    Let’s name this new project “getting_started”. Type this name, click Create Folder, and then click Create & Edit.

    You’re now looking at the Godot editor window. This is where you’ll spend most of your time when working in Godot. The editor is divided into sections.

    alt -alt

    • Viewport: This is where you’ll see the parts of your game as you’re working on them.
    • Workspaces: At the center-top, you can switch between working in the 2D, 3D, or Script workspaces. You start in 3D.
    • Playtest Buttons: These buttons let you launch and control your game when testing.
    • Docks/Tabs: On both sides are a number of docks where you can view game items and set their properties.
    • Bottom Panel: Here, you’ll see context-specific information for various tools. The most important one to note first is the Output panel, where you’ll see any error or informational messages when your game is running.

    Project Settings

    Now we’ve talked about the main parts of the Godot window and how they work, let’s spend a little time talking about our Project settings. Usually one of the first tasks when starting a new project is make sure it’s all set up correctly.

    So let’s click on Project in the menu and select Project Settings.

    alt -alt

    This is the Project settings window. On the left is a list of categories. For most projects, the default settings will be fine, and you shouldn’t worry about changing them unless you have a very specific need. For now, we’re just going to look at two of the sections. First, Application/Config.

    In here, you can set your game’s title, choose which scene is the “main scene” (more about that in a bit), and change the icon.

    Second, let’s look at the Display section. This is where you set up your game’s display. width & height let you set the size of the game window. If, for example, you were making a mobile game, you’d want to set this to the resolution and proportions of your target device. There are also settings for scaling, stretching, fullscreen mode, and more. For now, we’ll leave the default size - later on we’ll talk about how to adjust these to get our game running on different devices.

    alt -alt

    There are also some tabs across the top. We’ve been looking at the General tab. I’ll also point out briefly, the Input Map. This is where you can define different input actions for keyboard control, gamepad, mouse, and so on. In your game, you’ll just worry about the action, not what individual key or button was pressed. This is a very powerful and flexible way of handling player input.

    We also have localization options, if you plan to support multiple languages. Autoloading, which we’ll get to later, and plugins. The Godot community has created a variety of useful plugins that you can download and add to supply more features, different tools, and so on.

    We’ll come back to the project settings window later. Let’s close it for now and we’re ready to move on to the next step: working with nodes.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/g101/start/101_03/index.html b/docs/4.x/g101/start/101_03/index.html index 06950986..4ecf5ab3 100644 --- a/docs/4.x/g101/start/101_03/index.html +++ b/docs/4.x/g101/start/101_03/index.html @@ -1,25 +1,27 @@ -Nodes: Godot's building blocks :: Godot 4 Recipes - +Nodes: Godot's building blocks :: Godot 4 Recipes +

    Nodes: Godot's building blocks

    Nodes are the basic building blocks for creating games in Godot. A node is an object that can represent some kind of specialized game function. A given type of node might display graphics, play an animation, or represent a 3D model of an object. The node also contains a collection of properties, allowing you to customize its behavior. Which nodes you add to your project will depend on what functionality you need. It’s a modular system designed to give you flexibility in building your game objects.

    Working with Nodes

    Nodes are objects, in the programming sense. They encapsulate data and behavior, and they can inherit properties from other nodes. Rather than use one of the default suggestions, let’s click the “Add/Create a New Node” button in the scene dock.

    alt -alt

    Here you’ll see the whole hierarchy of node types available in the engine. For example, the nodes with the bluish icons all fall under the “Node2D” category, meaning they will all have the properties of a Node2D. More about that in a moment.

    alt -alt

    The list is long, and it would be frustrating to have to drill down every time to find the node you need. Instead, you can use the search function to find it using a small number of characters. We’re looking for the Sprite2D node, so I’ll just type “sp” and we’ll jump right to it. Click “Create” to add the node.

    alt -alt

    Now we have this Sprite2D node in our Scene dock. Make sure it’s selected, and then look at the Inspector dock on the right side. Over here, you’ll see all the properties of whatever node you have selected. Notice that the properties are organized by where they come from. The Sprite2D node inherits from Node2D, which inherits from CanvasItem, which inherits from the plain old Node.

    alt -alt

    Over in the viewport, the sprite doesn’t look like much. A sprite’s purpose is to display an image, or texture. As you can see in the Inspector, the Texture property is currently empty. Fortunately, every new Godot project comes with an image we can use: the Godot icon. Drag the icon from the Filesystem dock and drop it in the texture property.

    In the Inspector, click to expand the “Transform” section, and type (50, 50) in the Position property.

    alt -alt

    You can also click and drag the sprite around in the viewport, and you’ll see the Position values changing as you move.

    One important property of nodes is that they can be arranged in a parent-child hierarchy. Make sure you have the Sprite2D selected and press the add button again. Add another Sprite2D and also drag the icon into its texture.

    This new sprite is a child of the first. This means that it’s “attached” to its parent. If the parent sprite moves, so will the child. Click on the child sprite and set its Position to (50, 50). Now click and drag the parent sprite to move it around the screen.

    Notice that the Position of the parent is changing as you move it around. Now check the child: it’s still (50, 50). That’s because its “Transform” properties are relative to its parent.

    alt -alt

    Scenes

    Grouping nodes together like this is a powerful tool, enabling you to construct complex objects out of node “building blocks”. For example, a “Player” node in your game might have many child nodes attached to it: a Sprite2D for display, an AnimationPlayer to animate it, a Camera2D to follow it around, and so on.

    alt -alt

    A group of nodes arranged in a “tree” structure like this is called a Scene. In the next part, we’ll look at how you can use scenes to organize your game’s objects into independent parts that all work together. You’ll see this in practice was you work through the examples in later lessons.

    + + \ No newline at end of file diff --git a/docs/4.x/g101/start/index.html b/docs/4.x/g101/start/index.html index e71f5987..0d79e7c8 100644 --- a/docs/4.x/g101/start/index.html +++ b/docs/4.x/g101/start/index.html @@ -1,19 +1,21 @@ -Getting Started :: Godot 4 Recipes - +Getting Started :: Godot 4 Recipes +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_01/index.html b/docs/4.x/games/first_2d/first_2d_01/index.html index e4d2612c..0215aeea 100644 --- a/docs/4.x/games/first_2d/first_2d_01/index.html +++ b/docs/4.x/games/first_2d/first_2d_01/index.html @@ -1,22 +1,24 @@ -Project Setup :: Godot 4 Recipes - +Project Setup :: Godot 4 Recipes +

    Project Setup

    This first game project will guide you through making your first Godot Engine game. While you don’t need any previous experience, it’s expected that you’ve at least read through the Godot 101: Getting Started section. There, you’ll learn about the editor interface and how to get around the Godot UI.

    Why start with 2D?

    In a nutshell, 3D games are much more complex than 2D ones. However, many of the underlying game engine features you’ll need to know are the same. You should stick to 2D until you have a good understanding of Godot’s workflow. At that point, the jump to 3D will feel much easier.

    Open Godot and start a new project. You can name it anything you’d like - we’re going with “Classic Shmup”, since this is a traditional shoot-em-up style game.

    Downloading the art

    You can download the art we’ll be using for the game from itch.io: -Mini Pixel Pack by Grafxkid

    Unzip the art pack and copy it into your project by dropping the folder in the FileSystem tab.

    alt -alt

    Project settings

    Next, we need to set up some project-wide settings. Open Project Settings and check the “Advanced Settings” toggle in the upper-right.

    • In the Display/Window section:

      • Viewport Width & Viewport Height to 240, 320.
      • Window Width Override & Window Height Override to 480, 640.
      • Stretch/Mode to canvas_items.

    These settings will ensure the game is the right size. Because we’re using pixel art, the images themselves are very small, so an old-school resolution like 240x320 is perfect. However, on a modern monitor, that’s a fairly small window, so the other settings let us scale that up proportionally. If you have a 1080p monitor, you can make the override values 720x960 instead. You’ll also be able to resize the window when the game is running.

    • In the Rendering/Textures section under Canvas Textures, set Default Texture Filter to Nearest. This will ensure that our beautiful pixel art stays nice and crisp, looking like the image on the right, not the one on the left:

    alt -alt

    • Click the Input Map tab at the top of the Project Settings window. This is where we can set up the inputs we want to use in the game. In the “Add New Action” box, type the following, hitting <enter> after each to add it to the list of actions: right, left, up, down, shoot. To assign key(s) to each named input, click the + button to its right and press the key on your keyboard. When you’re done, you should have something like this:

    alt -alt

    Feel free to use other keys if you’d rather use a different setup.

    Next steps

    That takes care of setting up - now we’re ready to get started! In the next section, we’ll create the player-controlled spaceship.

    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_02/index.html b/docs/4.x/games/first_2d/first_2d_02/index.html index 8d29c6f6..937461e0 100644 --- a/docs/4.x/games/first_2d/first_2d_02/index.html +++ b/docs/4.x/games/first_2d/first_2d_02/index.html @@ -1,27 +1,29 @@ -Designing the Player Scene :: Godot 4 Recipes - +Designing the Player Scene :: Godot 4 Recipes +

    Designing the Player Scene

    In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.

    Setting up the Ship Scene

    A common part of the Godot workflow is creating scenes. As discussed earlier, a scene in Godot is nothing more than a collection of nodes. In most Godot projects, each game object is configured as a scene, with nodes that provide it with the desired functionality, and optionally some code to customize its behavior.

    Choosing nodes

    The first step is to decide what kind of node to start with. The first node you add to the scene is called the root node. A scene’s root node should generally be the one that primarily defines the game object’s behavior. Then you attach child nodes to add additional functionality.

    So what should our game’s ship be? Let’s break down the requirements, and look at what nodes might be useful to meet them.

    The ship needs to:

    • Move in 2D space. For this, a basic Node2D would suffice, as that’s the node that has position, rotation, and other 2D-related properties. However, it has no appearance.

    • Display an image. Sprite2D is the node for this. Since it’s also a Node2D, we’d still be able to move it around.

    • Detect getting hit. The enemies will be shooting and flying around on the screen, so we’ll need to know when ship is hit. We don’t have a need for solid objects - they’re not going to bounce off each other or transfer momentum - we just need to know when they touch. For this, an Area2D would be perfect. It can detect touching other objects, has positional properties, but it has no appearance of its own.

    Looking at this list, the Area2D provides the main functionality. We can attach a Sprite2D to display the ship image, and then we’ll have everything we need.

    Building the scene

    In the Scene tab, click the + button or the + Other Node button to add the first node. Start typing Area2D and choose it from the list. Once it’s in the Scene tab, click the node’s name to rename it to Player, and press <Ctrl+S> to save the scene.

    Displaying the ship

    With the Player node selected, add another node: a Sprite2D. To keep things organized, let’s rename this node to Ship.

    From the FileSystem tab, drag the Player_ship (16x16).png file from the art pack and drop it in the Texture property of the Inspector.

    alt -alt

    The first thing you’ll notice is that there seem to be three ships! The image from the art pack also includes versions of the ship going to the left/right. We can use this - in the Animation section of the Inspector, set Hframes to 3. Now, changing the Frame property will move between the three different versions. Leave it at 1 for now.

    alt -alt

    Adding a collision shape

    You may also have noticed the yellow warning triangle on the Area2D node. If you click it, you’ll see the warning is telling us that the area doesn’t have a shape. We need to define its shape, and we can do that by adding a CollisionShape2D node as a child of the Player.

    In the Inspector for this node, you’ll see a Shape property that currently shows <empty>. If you click in this box, you’ll see a dropdown that allows you to select from a variety of shapes. Choose New RectangleShape2D and you’ll see a light blue square appear over the ship. You can adjust the size of the shape by dragging the orange circles, or you can click on the shape in the Shape property to expand it and fill in the Size manually.

    alt -alt

    Exhaust

    The ship will look much more dynamic with a little animation. Included in the art pack are some animations of exhaust flames named “Boosters”. There are three: one for each version of the ship (left, forward, and right).

    To display these, select the Ship node and add a child AnimatedSprite2D node and name it “Boosters”.

    In the Inspector, under the Animation section, you’ll find a property called Sprite Frames, which is currently <empty>. Click it to create a New SpriteFrames, then click the SpriteFrames item to open the animation panel at the bottom of the editor window.

    alt -alt

    Double-click the “default” animation to rename it to “forward”. Then, to add the animation images, click the Add frames from sprite sheet button:

    alt -alt

    Choose the Boosters (16 x 16).png image and you’ll see the Select Frames window, allowing you to choose the frames you want.

    alt -alt

    There are only two frames in this animation, but the grid isn’t correct. Change the Size values to match the image sizes: 16 x 16. Then, click both frames to select them and click the Add 2 Frame(s) button.

    alt -alt

    Now that you’ve added the two frames, press the Play button to run the animation. You can also toggle the Autoplay on Load button so that the animation will start automatically.

    alt -alt

    It’s a little slow, so change the speed to 10 FPS.

    Add two more animations by clicking the Add Animation button, naming them left and right.

    alt -alt

    Repeat the process, adding the left and right “Booster” sprite sheets.

    Gun cooldown

    The last node we’ll need to complete the player setup is a Timer to control how fast the player can shoot. Add the Timer as a child of Player and name it GunCooldown. Set its One Shot property to “On”. This means that when the timer ends, it won’t automatically restart. In the player’s code, we’ll start the timer when the player shoots, and they won’t be able to shoot again until the timer runs out.

    Next steps

    That completes the player scene setup. We’ve added the nodes to give the player ship the functionality it will need in the game. In the next section, we’ll add some code to enable the player to control the ship, make it shoot, and detect when it collides with things.

    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_03/index.html b/docs/4.x/games/first_2d/first_2d_03/index.html index c80166f5..8cff662c 100644 --- a/docs/4.x/games/first_2d/first_2d_03/index.html +++ b/docs/4.x/games/first_2d/first_2d_03/index.html @@ -1,19 +1,19 @@ -Coding the Player :: Godot 4 Recipes - +Coding the Player :: Godot 4 Recipes +

    Coding the Player

    In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.

    Adding a script

    Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. Our Player scene displays the ship, defines its collision hitbox, etc., but it can’t move, and nothing would happen if it collided. We’ll write code to add this functionality to the ship.

    Select the Player node and click the Attach script button:

    alt -alt

    You don’t need to change any of the options on the Attach Node Script window, so just click Create and you’ll be taken to the script editor.

    Let’s look at the first line of the script, which has automatically been added.

    extends Area2D
    +

    Coding the Player

    In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.

    Adding a script

    Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. Our Player scene displays the ship, defines its collision hitbox, etc., but it can’t move, and nothing would happen if it collided. We’ll write code to add this functionality to the ship.

    Select the Player node and click the Attach script button:

    alt +alt

    You don’t need to change any of the options on the Attach Node Script window, so just click Create and you’ll be taken to the script editor.

    Let’s look at the first line of the script, which has automatically been added.

    extends Area2D
     

    This line defines what type of object this script should be attached to. It means that the script will have access to all the functionality that an Area2D provides.

    Your extends line should always match the type of node the script is attached to.

    Accessing scripts

    A script on its own doesn’t do much of anything. Scripts define additional functionality for whatever object they’re attached to. You will never be accessing a variable in some script, you’ll be accessing a property of an object, which is defined by that script. This is a very important distinction.

    Movement

    We’ll start by making the ship move around the screen. Let’s start with some code that does the following:

    • Detect what input(s) the player is pressing
    • Move the ship in the direction of the input
    @export var speed = 150
     
     func _process(delta):
         var input = Input.get_vector("left", "right", "up", "down")
         position += input * speed * delta
    -

    Let’s break this down line-by-line:

    • Adding @export in front of a variable allows you to adjust its value in the Inspector.

    alt -alt

    • The _process() function is called once every frame by the engine. Any code we place in this function will be executed every frame.
    • Input.get_vector() checks the pressed state of the four given inputs and produces a vector pointing in that direction.
    • Finally, we move the ship’s position by adding that input vector, scaling it to the desired speed, and multipling by delta.
    +

    Let’s break this down line-by-line:

    • Adding @export in front of a variable allows you to adjust its value in the Inspector.

    alt +alt

    • The _process() function is called once every frame by the engine. Any code we place in this function will be executed every frame.
    • Input.get_vector() checks the pressed state of the four given inputs and produces a vector pointing in that direction.
    • Finally, we move the ship’s position by adding that input vector, scaling it to the desired speed, and multipling by delta.

    Run the scene by clicking the Run Current Scene button, and try moving around.

    alt -alt

    Staying on screen

    One problem we have is that if you keep moving, you’ll go off the screen. We need to lock the player’s position property inside the bounds of the screen rectangle. Add this line at the top of the script:

    @onready var screensize = get_viewport_rect().size
    +Links to more information

    Run the scene by clicking the Run Current Scene button, and try moving around.

    alt +alt

    Staying on screen

    One problem we have is that if you keep moving, you’ll go off the screen. We need to lock the player’s position property inside the bounds of the screen rectangle. Add this line at the top of the script:

    @onready var screensize = get_viewport_rect().size
     

    The @onready here tells Godot not to set the value of the screensize variable until the Player node has entered the scene tree. Effectively, it means “wait until the game starts”, because there’s no window to get the size of until the game is running.

    The next step is to clamp the position within the bounds of that screensize rectangle. Vector2, which is what position is, has a clamp() method we can use. Put this line right after setting the position:

    func _process(delta):
         var input = Input.get_vector("left", "right", "up", "down")
         position += input * speed * delta
    @@ -35,15 +35,17 @@
     

    Once again, play the scene and verify that the images change when moving left/right. Verify that everything works as intended before moving to the next step.

    The next step will be to create the Bullet scene and let the player shoot.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_04/index.html b/docs/4.x/games/first_2d/first_2d_04/index.html index f102d478..fe80cd24 100644 --- a/docs/4.x/games/first_2d/first_2d_04/index.html +++ b/docs/4.x/games/first_2d/first_2d_04/index.html @@ -1,5 +1,5 @@ -Bullet Scene :: Godot 4 Recipes - +Bullet Scene :: Godot 4 Recipes +

    Bullet Scene

    Now that the player can move around the screen, our next step will be to implement shooting

    Reusable objects

    The player will fire many “bullets” during the game, but all of them will be identical. A bullet needs to do the following:

    • Appear just ahead of the player
    • Travel forward until going off the screen
    • Detect collisions with enemies

    Since all bullets will do these same things, we can save ourselves a great deal of work by designing one “prototype” bullet, and using that as the blueprint for creating as many duplicates as we need. Godot’s scene system is ideal for this.

    Bullet scene

    Create a new scene by selecting Scene -> New Scene in the menu, or by clicking the + in the tabs on the top of the viewport.

    Just like we did with the Player scene, we need to consider what nodes we’ll need to make the bullet work. We can again use an Area2D, since that will allow us to detect the bullet hitting things. This means we’ll need a collision shape, and a sprite to display the bullet image. Finally, we need a way to detect when the bullet goes offscreen so we can automatically remove it.

    Here’s the node setup:

    • Area2D - name this Bullet
      • Sprite2D
      • CollisionShape2D
      • VisibleOnScreenNotifier2D

    From the asset pack folder, drop the Player_charged_beam (16 x 16).png image on the Texture of the Sprite2D.

    As with the ship image, there are multiple versions here, so set the *Hframes to 2 so we’ll only see one at a time.

    Set the shape of the CollisionShape2D just like you did earlier in the Player scene.

    Bullet script

    Attach a script to the Bullet node and let’s start with the movement:

    extends Area2D
     
    @@ -10,8 +10,8 @@
     
     func _process(delta):
         position.y += speed * delta
    -

    This should look fairly familiar, as it’s similar to the player script. We’re only changing the position.y since the bullet should travel straight up.

    Note the start() function we defined. That will let us set the bullet’s starting position, since the player will move around and spawn the bullets at different locations.

    Connecting signals

    Now select the Bullet node and then click the Node tab next to the Inspector.

    alt -alt

    This is a list of all the signals this node can emit. Signals are how Godot lets you know that something has happened. In this case, we can use the area_entered signal to tell us whenever this bullet touches another Area2D node.

    Select the area_entered signal and click the Connect… button (you can also double-click the signal name). In the dialog that opens up, just click Connect - we don’t need to change anything there.

    You’ll notice that you’re back in the script editor, looking at bullet.gd, and a new function as been added. It has a green “connected” icon next to its name to show that a signal is connected to it. This function will be called whenever the area touches something, so let’s add some code here:

    func _on_area_entered(area):
    +

    This should look fairly familiar, as it’s similar to the player script. We’re only changing the position.y since the bullet should travel straight up.

    Note the start() function we defined. That will let us set the bullet’s starting position, since the player will move around and spawn the bullets at different locations.

    Connecting signals

    Now select the Bullet node and then click the Node tab next to the Inspector.

    alt +alt

    This is a list of all the signals this node can emit. Signals are how Godot lets you know that something has happened. In this case, we can use the area_entered signal to tell us whenever this bullet touches another Area2D node.

    Select the area_entered signal and click the Connect… button (you can also double-click the signal name). In the dialog that opens up, just click Connect - we don’t need to change anything there.

    You’ll notice that you’re back in the script editor, looking at bullet.gd, and a new function as been added. It has a green “connected” icon next to its name to show that a signal is connected to it. This function will be called whenever the area touches something, so let’s add some code here:

    func _on_area_entered(area):
         if area.is_in_group("enemies"):
             area.explode()
             queue_free()
    @@ -20,15 +20,17 @@
     

    Next steps

    This completes the bullet scene, so now we can go back and add shooting to the player.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_05/index.html b/docs/4.x/games/first_2d/first_2d_05/index.html index abe749cd..c32fe130 100644 --- a/docs/4.x/games/first_2d/first_2d_05/index.html +++ b/docs/4.x/games/first_2d/first_2d_05/index.html @@ -1,5 +1,5 @@ -Shooting :: Godot 4 Recipes - +Shooting :: Godot 4 Recipes +

    Shooting

    The Bullet scene provides us with a reusable object we can instantiate whenever the player shoots.

    Adding to the player

    Let’s head back to the Player script and add a few new variables:

    @export var cooldown = 0.25
     @export var bullet_scene : PackedScene
    @@ -22,19 +22,21 @@
         shoot()
     

    We’ll also need to connect the timeout signal of GunCooldown.

    func _on_gun_cooldown_timeout():
         can_shoot = true
    -

    When the cooldown ends, we can allow shooting again.

    Go ahead and run the scene and try pressing the shoot action.

    alt -alt

    Adding instances to the three

    Notice that we’ve added the new bullets as children of the SceneTree root (get_tree().root), and not to the player ship. This is important because if we made the bullets children of the ship, then they would be “attached” to it when it moves.

    Next steps

    Shooting’s no fun without something to shoot at. We’ll start making the enemies soon, but first we need a scene where we can bring the player, enemies, and other game objects together.

    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_06/index.html b/docs/4.x/games/first_2d/first_2d_06/index.html index 44833e85..b86054ee 100644 --- a/docs/4.x/games/first_2d/first_2d_06/index.html +++ b/docs/4.x/games/first_2d/first_2d_06/index.html @@ -1,20 +1,22 @@ -Main Scene :: Godot 4 Recipes - +Main Scene :: Godot 4 Recipes +

    Main Scene

    Before we can make enemies, powerups, or any other game objects, we need a place where they can all exist together with the player. In most games, this would be called a “level” or “main” scene, and that’s what we’ll call it here.

    Start the scene with a Node2D called “Main” and save it.

    Creating the background

    Add a Sprite2D child. Name this sprite “Background” and add the Space_BG (2 frames) (64 x 64).png as its texture.

    This image has two frames, each 64x64 pixels in size. We’d like the image to tile across the full size of the screen, so start with the following settings:

    • Under Offset set Centered to “off”. This makes the image’s top left corner start at the origin rather than its center.

    • Under Region, turn Enabled “on”, and then set the Rect to a width of 240 and a height of 320. This makes the image stretch to the size of the screen.

    • Under Texture change Repeat to Enabled. This causes the image to repeat over the full size of the screen.

    Now add the player to the scene by selecting the Main node and clicking the Instantiate Child Scene button.

    alt -alt

    Animating the background

    We can make the scene more dynamic by animating the background. While we could do this in code by changing the region_rect property every frame, we’ll use an AnimationPlayer node instead; add one as a child of Main.

    At the bottom of the editor window, you’ll see the Animation panel. There’s a lot of information there, so let’s look at how it’s laid out:

    alt -alt

    Click the Animation button and choose New Animation. You can name the new animation scroll. Set its Length to 2 and toggle the Looping and Autoplay buttons.

    Animations work by adding tracks that represent properties that you want the AnimationPlayer to control. In the timeline of the player, you’ll add keyframes that define what value you want the property to have at that particular time.

    We can add keyframes to the animation by clicking the key icon that now appears next to every property in the Inspector. Make sure the scrubber (the blue indicator on the timeline) is at time 0, then select the Background and click the key next to Region/Rect. You’ll be asked if you want to create a new track and then you’ll see the new track added to the animation panel, with a small dot representing the keyframe you’ve just added. Drag the scrubber to time 2 and then change the y value of the Region/Rect property to 64. Click the key to add another keyframe.

    Now when you press Play on the animation, you should see the background slowly scrolling behind the player.

    Next steps

    The main scene is now ready for us to add enemies. In the next step we’ll make a single enemy scene, as we did with the bullets, and then instantiate that multiple times.

    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_07/index.html b/docs/4.x/games/first_2d/first_2d_07/index.html index afae3d52..eb8b0c84 100644 --- a/docs/4.x/games/first_2d/first_2d_07/index.html +++ b/docs/4.x/games/first_2d/first_2d_07/index.html @@ -1,5 +1,5 @@ -Enemies :: Godot 4 Recipes - +Enemies :: Godot 4 Recipes +

    Enemies

    Now that our enemy can shoot, let’s give them something to shoot at.

    Setting up the scene

    We’ll use an Area2D for the enemy, since we need it to detect overlap - either with the player’s bullets, or with the player itself.

    Here’s are the nodes we’ll need:

    Enemy:  Area2D
          Sprite2D
    @@ -7,8 +7,8 @@
          AnimationPlayer
         MoveTimer:  Timer
         ShootTimer:  Timer
    -

    Select the area node and click the Node tab next to the Inspector. Under Groups, type “enemies” an click Add. Remember the code we wrote on the bullet? It looks for objects in the “enemies” group.

    In the sprite’s Texture, add Bon_Bon (16 x 16).png and set its Animation/Hframes to 4.

    As you’ve done before, add a rectangular collision shape and size it to fit. Enable One Shot on both timer nodes.

    In the AnimationPlayer, add an animation called “bounce” and set it to looping and autoplay. Set the Snap at the bottom of the animation panel to 0.05.

    Select the sprite node and press the key icons next to Texture and Hframes to create tracks for them. We’re doing this because later we’ll add an “explosion” animation that will use different values for these properties.

    Now we’ll key the individual Frames values we want. Start with keying Frames each .1 seconds to values in this order2, 1, 0, 3. Finally, key 0 again and put it immediately after. This will make a “pulsing” animation where the sprite grows and then bounces a little at the end. The animation setup should look like this:

    alt -alt

    Press the play button to see it in action. Feel free to adjust it if you’d like.

    Now add another animation called “explode”. Set its length to 0.4 seconds.

    Change the sprite’s Texture to Explosion (16 x 16).png and keyframe that property. Since this image has a different number of frames than the enemy image, we also need to change Hframes to 6 and keyframe that.

    Now keyframe Frame to 0 at time 0 and to 5 at time 0.4. Play the animation to see it in action.

    Enemy script

    The enemies will spawn at the top of the screen in a grid. After a random amount of time, they’ll descend toward the player and then return to the top if they weren’t destroyed. Periodically, they’ll also shoot at the player.

    Add a script, and start with the variables:

    extends Area2D
    +

    Select the area node and click the Node tab next to the Inspector. Under Groups, type “enemies” an click Add. Remember the code we wrote on the bullet? It looks for objects in the “enemies” group.

    In the sprite’s Texture, add Bon_Bon (16 x 16).png and set its Animation/Hframes to 4.

    As you’ve done before, add a rectangular collision shape and size it to fit. Enable One Shot on both timer nodes.

    In the AnimationPlayer, add an animation called “bounce” and set it to looping and autoplay. Set the Snap at the bottom of the animation panel to 0.05.

    Select the sprite node and press the key icons next to Texture and Hframes to create tracks for them. We’re doing this because later we’ll add an “explosion” animation that will use different values for these properties.

    Now we’ll key the individual Frames values we want. Start with keying Frames each .1 seconds to values in this order2, 1, 0, 3. Finally, key 0 again and put it immediately after. This will make a “pulsing” animation where the sprite grows and then bounces a little at the end. The animation setup should look like this:

    alt +alt

    Press the play button to see it in action. Feel free to adjust it if you’d like.

    Now add another animation called “explode”. Set its length to 0.4 seconds.

    Change the sprite’s Texture to Explosion (16 x 16).png and keyframe that property. Since this image has a different number of frames than the enemy image, we also need to change Hframes to 6 and keyframe that.

    Now keyframe Frame to 0 at time 0 and to 5 at time 0.4. Play the animation to see it in action.

    Enemy script

    The enemies will spawn at the top of the screen in a grid. After a random amount of time, they’ll descend toward the player and then return to the top if they weren’t destroyed. Periodically, they’ll also shoot at the player.

    Add a script, and start with the variables:

    extends Area2D
     
     var start_pos = Vector2.ZERO
     var speed = 0
    @@ -64,15 +64,17 @@
     

    We don’t have a way to display the score yet, but we’ll get to that soon.

    Play the scene and you should see a bunch of enemies appear at the top and periodically fall down the screen. Next, we’ll make them shoot.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_08/index.html b/docs/4.x/games/first_2d/first_2d_08/index.html index b2208c93..2b9dd354 100644 --- a/docs/4.x/games/first_2d/first_2d_08/index.html +++ b/docs/4.x/games/first_2d/first_2d_08/index.html @@ -1,5 +1,5 @@ -Enemy Shooting :: Godot 4 Recipes - +Enemy Shooting :: Godot 4 Recipes +

    Enemy Shooting

    Now that our enemy can shoot, let’s give them something to shoot at.

    Enemy bullet scene

    Make a new EnemyBullet scene just like you made the player bullet earlier. We won’t go into all the steps here, but you can refer back to that part if you’re stuck. The only difference here is that you can use the Enemy_projectile (16 x 16).png image instead.

    The script will be a little bit different:

    extends Area2D
     
    @@ -26,15 +26,17 @@
     

    Play the Main scene again and you should have some random enemy bullets appearing.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_09/index.html b/docs/4.x/games/first_2d/first_2d_09/index.html index 7a3be728..80f19340 100644 --- a/docs/4.x/games/first_2d/first_2d_09/index.html +++ b/docs/4.x/games/first_2d/first_2d_09/index.html @@ -1,13 +1,13 @@ -UI and Score :: Godot 4 Recipes - +UI and Score :: Godot 4 Recipes +

    UI and Score

    The last main piece of our game is the user interface (UI). We need a way to show the player the score and other information. To do this, we’ll use a variety of Control nodes - the nodes Godot provides for building UIs.

    UI scene

    Start the scene with a MarginContainer and name it UI.

    Containers are Control nodes that are designed to control the size and position of their children. Using them makes it easier to position and move Control nodes without having to do it manually. The MarginContainer makes sure its children don’t get too close to the edge.

    In the Inspector under Theme Overrides/Constants set all four Margin values to 10. Then, in the menu bar at the top of the viewport, set the anchors to the Top Wide preset.

    alt -alt

    Next, we’ll add an HBoxContainer. This type of container organizes its children horizontally. Add a TextureProgressBar, which will represent our ship’s shield level. Name it ShieldBar.

    Unfortunately, there’s not a good image in the art pack to use for a progress bar (there is one, but it isn’t formatted in an easy way to work with). Instead, we’ll use the two images below. One is a green bar and the other is a white outline. Save them in your project folder.

    alt -alt -alt -alt

    In the Texture section, drag the foreground image to the Progress and the background image to the Under texture. The first thing you’ll notice is that it’s very small. Let’s first under Layout set Custom Minimum Size to (80, 16). You’ll notice that the orange selection rectangle got bigger, but the image didn’t. Well, we don’t want the image to just stretch, or it would look bad. Instead we’ll check the Nine Patch Stretch box, and then set the four Stretch Margin values to 3.

    You should now see a long, unfilled bar. To see what it looks like when filled, change the Value property in the Range section to anything between 0 and 100.

    alt -alt

    On the right side, we’d like to show the score. Now, we could just use a Label node and add a font, but that’s not very fun. The art pack includes a lovely pixel set of digits that we could use instead. We’ll just need to do a little coding to chop it up and show the corect digit(s).

    Score counter

    Start a new scene and add an HBoxContainer. Name it ScoreCounter then set it to Top Wide and set the Alignment to “End”. Also, set the Theme Overrides/Constants/Separation to 0 (you need to check the box next to the property).

    In this container, we’ll have a string of TextureRect nodes showing each digit. We’ll start by adding one and then duplicating it.

    Name the TextureRect Digit0. Under Texture, select “New AtlasTexture”, then click the box to open it. Drag Number_font (8 x 8).png into the Atlas property, then set the Region to (32, 8, 8, 8). Set Stretch Mode to “Keep Aspect Centered”.

    Select the Digit0 node and press Ctrl-D 7 times to create duplicates of the node. The picture below shows what you should see after this step:

    alt -alt

    Now we’ll add a script to ScoreCounter that will choose the correct Region values for whichever digit it needs to display.

    extends HBoxContainer
    +

    UI and Score

    The last main piece of our game is the user interface (UI). We need a way to show the player the score and other information. To do this, we’ll use a variety of Control nodes - the nodes Godot provides for building UIs.

    UI scene

    Start the scene with a MarginContainer and name it UI.

    Containers are Control nodes that are designed to control the size and position of their children. Using them makes it easier to position and move Control nodes without having to do it manually. The MarginContainer makes sure its children don’t get too close to the edge.

    In the Inspector under Theme Overrides/Constants set all four Margin values to 10. Then, in the menu bar at the top of the viewport, set the anchors to the Top Wide preset.

    alt +alt

    Next, we’ll add an HBoxContainer. This type of container organizes its children horizontally. Add a TextureProgressBar, which will represent our ship’s shield level. Name it ShieldBar.

    Unfortunately, there’s not a good image in the art pack to use for a progress bar (there is one, but it isn’t formatted in an easy way to work with). Instead, we’ll use the two images below. One is a green bar and the other is a white outline. Save them in your project folder.

    alt +alt +alt +alt

    In the Texture section, drag the foreground image to the Progress and the background image to the Under texture. The first thing you’ll notice is that it’s very small. Let’s first under Layout set Custom Minimum Size to (80, 16). You’ll notice that the orange selection rectangle got bigger, but the image didn’t. Well, we don’t want the image to just stretch, or it would look bad. Instead we’ll check the Nine Patch Stretch box, and then set the four Stretch Margin values to 3.

    You should now see a long, unfilled bar. To see what it looks like when filled, change the Value property in the Range section to anything between 0 and 100.

    alt +alt

    On the right side, we’d like to show the score. Now, we could just use a Label node and add a font, but that’s not very fun. The art pack includes a lovely pixel set of digits that we could use instead. We’ll just need to do a little coding to chop it up and show the corect digit(s).

    Score counter

    Start a new scene and add an HBoxContainer. Name it ScoreCounter then set it to Top Wide and set the Alignment to “End”. Also, set the Theme Overrides/Constants/Separation to 0 (you need to check the box next to the property).

    In this container, we’ll have a string of TextureRect nodes showing each digit. We’ll start by adding one and then duplicating it.

    Name the TextureRect Digit0. Under Texture, select “New AtlasTexture”, then click the box to open it. Drag Number_font (8 x 8).png into the Atlas property, then set the Region to (32, 8, 8, 8). Set Stretch Mode to “Keep Aspect Centered”.

    Select the Digit0 node and press Ctrl-D 7 times to create duplicates of the node. The picture below shows what you should see after this step:

    alt +alt

    Now we’ll add a script to ScoreCounter that will choose the correct Region values for whichever digit it needs to display.

    extends HBoxContainer
     
     var digit_coords = {
         1: Vector2(0, 0),
    @@ -65,15 +65,17 @@
     

    Run the game again and check that your shield depletes when you get hit by a bullet or an enemy.

    Next steps

    We’re almost done with the basic functionality. We just need a way to start and end the game.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_10/index.html b/docs/4.x/games/first_2d/first_2d_10/index.html index ae0ffec9..6a3da672 100644 --- a/docs/4.x/games/first_2d/first_2d_10/index.html +++ b/docs/4.x/games/first_2d/first_2d_10/index.html @@ -1,5 +1,5 @@ -Starting and Ending the Game :: Godot 4 Recipes - +Starting and Ending the Game :: Godot 4 Recipes +

    Starting and Ending the Game

    Our last step is to add a start button and a “game over” state to the game.

    Starting the game

    Currently when we run the game, it starts immediately. Let’s add a button to start it.

    In Main as a child of the CanvasLayer, add a CenterContainer and set its layout to Full Rect. Then add a TextureButton child. Name this button Start and add the START (48 x 8).png image as its Normal texture.

    Add a reference at the top of the script:

    @onready var start_button = $CanvasLayer/CenterContainer/Start
     

    Connect this button’s pressed texture to Main and add this code:

    func _on_start_pressed():
    @@ -18,15 +18,17 @@
     

    This will show the “game over” image for 2 seconds, then switch back to the start button so you can play again. Try it out and see if you can play a few games.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/first_2d_end/index.html b/docs/4.x/games/first_2d/first_2d_end/index.html index b0f279ae..4384d1f7 100644 --- a/docs/4.x/games/first_2d/first_2d_end/index.html +++ b/docs/4.x/games/first_2d/first_2d_end/index.html @@ -1,18 +1,20 @@ -Wrapping up :: Godot 4 Recipes - +Wrapping up :: Godot 4 Recipes +

    Wrapping up

    If you’ve been following along, you’ve learned a lot of the fundamentals of building games in Godot. We’re going to end the tutorial here, since we’ve completed the basic game.

    The secret to learning effectively

    Here’s my big secret for getting the most out of tutorials like this and others you may find online. At the end, once you’ve finished building the project, immediately delete it and start over. This time, try and re-create it without looking at the tutorial. If you get stuck, look at just that part, then close it again.

    It may sound repetitive, but that is how we learn: by doing things repeatedly. If you follow this tip, you’ll be amazed at how quickly you level up your gamedev skills.

    Adding to the game

    If you’re feeling comfortable with the techniques used to make this game, then you’re ready to branch out. Try adding a single new feature to this game.

    If you’re stuck coming up with an idea, here are some suggestions:

    • Additional enemy types - there is art for other enemies in the art pack. How do they move and shoot?

    • Waves - make more enemies spawn every time you clear the screen

    • Boss enemies - what if a big enemy appears?

    • Boosts - powerups could appear for the player to collect. There’s some art for those too.

      • Shield recharge - collect these to power up the shield
      • Weapon upgrades - shoot more bullets, patterns, etc.
    • Sound and music - give everything a lot more personality with some sound effects and background music.

    Learning more

    Ready for more? Here are some suggestions for your next learning adventure:

    • Godot 101: Getting started in 3D - if you’re interested in making things in 3D, check out this introduction to Godot’s 3D features.

    • Check out the rest of the content on this website. There are lots of examples, tutorials, and code snippets to help you learn how to make your dream game.

    Download This Project on GitHub

    Download the project code here:

    https://github.com/godotrecipes/8_direction_animation

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/games/first_2d/index.html b/docs/4.x/games/first_2d/index.html index ef025681..738c653f 100644 --- a/docs/4.x/games/first_2d/index.html +++ b/docs/4.x/games/first_2d/index.html @@ -1,19 +1,21 @@ -Your First 2D Game :: Godot 4 Recipes - +Your First 2D Game :: Godot 4 Recipes +

     Your First 2D Game

    Get started with Godot by building a 2D shooter.

    In this series, we’ll start with the basics and build a classic, old-school space shooter.

    Here’s a screenshot of the finished game:

    alt -alt

    In each part of the series, we’ll build a piece of the game, adding features and explaining the process along the way.

    Background

    If you find that you’re struggling with the programming side of things, see these resources:

    Download This Project on GitHub

    Download the project code here:

    https://github.com/godotrecipes/classic_shmup

    + + \ No newline at end of file diff --git a/docs/4.x/games/index.html b/docs/4.x/games/index.html index a1bb2abe..bf0e22fb 100644 --- a/docs/4.x/games/index.html +++ b/docs/4.x/games/index.html @@ -1,17 +1,19 @@ -Game Tutorials :: Godot 4 Recipes - +Game Tutorials :: Godot 4 Recipes +

     Games

    Demo games and tutorials.

    Updating to Godot 4.0

    We’re working on new content for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.

    In this section:

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/images/amazon_button.png b/docs/4.x/images/amazon_button.png new file mode 100644 index 0000000000000000000000000000000000000000..b0806e05575681198b39ed9ff656babe30538536 GIT binary patch literal 30697 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 T1=ByjXZuA`@pI)zqoDr-NGpoI literal 0 HcmV?d00001 diff --git a/docs/4.x/images/book_ad.png b/docs/4.x/images/book_ad.png new file mode 100644 index 0000000000000000000000000000000000000000..2e58e3aa57c2cd09d327d70e904cc66d2a90c4c0 GIT binary patch literal 365540 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

    _jnvDOxI-cAdP!Mw0R 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
    p1)u;>zjS1 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 JnjTbjkBE{Yd9N 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%0A4H``rQX$y)Zwkjt;V9%CYB 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%0A3(_t4luhmVZ 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�v6~?|L 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�xVT;?n_uji~AHzod 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

    R~3*Lgyq6{r^3FyJX$!;G-=H_-5 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 qY`4-rs2q8HV&RjX-SMMvw$Kvzyyjw*P`>?*B`O z=>G)Zj>>=f%lihnF0T7;;kqt4Hv&~yb@&lVSYfG7?wfOey2C#%L4ktd`vCLBO?HEz z+RlvQ5qKK}9lKK{4E8fSSnhzp;Oqf1=$vi+sKWccJ##9!864-`-d2SHc>8@-XMp=2 z5EvL4M+Md99-McBVRk{@5oTsU^-VK#$jrbTRcFSL&Z_{9!1dT4aAw|_8DO)Gtvzuz zb;Nn!c}Hecz%3>*K%O`=&dj_DBfHKpst7cin0aS-1yB~w{+iR;#*rR(W@cET$~YrS z;y%Nuz$Tt%90d}Pe-#)IPD$+E66?CI>(dVy@OsoWTPOA^@^ z-Q7OG{h-w;gbxG~3P_4wC6I(Cpxt3NWUst5ms;7L<#qYohoqwMuK=!vdtwq}Vor02 zCdpdX^%#CDe~&KEB&w&sCw)K3lR$yC&!YcG1D(VqBmv8(0Xa7|-Jq#T*po!JUki3i zRZc}Epnq6k8p@U24OJBYA*}Ufx0|)r>h6X@x=%ubpm4=c)*0^Gr@K-`)cBQbp^EQm zzRPd7`qfWTr>NJ@mheeZ4K#Ga58?dKZEqx9aNqp0Dj`4urKT%nLOZo>N0PL9^kxnwJfNi7wzD@{6b>Ik{2auG_%PyUw=1_j{{d7QfCo^m zoP?2B+iu%PcXRAuyO`8}30nd-PhL)@B@pQU1mIWz1}Fj^Dgpq7;5?kXDFFaPqj3g^ z2q13&Om9qYPNz4=%jeVS%}H}Ky*at~9~;Z(llApveLZbXns4i?6P7U!DB%nS<6pc4 z#6&>E^%$Iw5r|Mk6k!2gDP6oAAOOmfMMQ*#m*;MNSbgw1YF?Ka;ZzW=8R1gKcQL_P z77-C4;)8d^`o!K_ivdgt!h?uL(;pr>dT6(p#H2#~lg+!oz4bU}J?Rn(ODii2i-?F# z@7_g~E@c4#QbO@^y`4`VKIGUIPwU6WpN{*Vz1VeJ&otfWXSyPKSz9Qa;bdSUCRJWd z4rQ#mZ|}#;0UGPmb&R2=d)9r%L)=D0G=C%FyqNYj-d0t$+So9AY3brpe9()ob+I1y z{ol`*lhr_sMMXGooMU^uZ2F~}RmUjpwxi_q*q#}iiik2?9AHUq54=3s^Vo3Xd^!PE z=lj!xYxwXK`*zI5{Vbc?RW2+S;6>@7m;CUshKW)U5g%6Dv-PpPsPq2AmfN}eJbBs6 z-rx0P?xx4IeC?s`Bj&he*<$zYUT7co;n2VAUjNYkzQW&X(C@YCZY!&wYdzL-R6T6t z>2Ol!SJk3i_Q#s3Wi@?&Rh!##UysKcs_2w`@b~n4^>SI6q08q8Ma27Ts0-Vc)rrcg zKdr@TZM)iz-p*BWKlh#ejRDoI-Set2d;R*$>(%t&Pr6yEj&lo(AB&0YQa06EpLp?s z{kc|$g{8IW?bh3Uj^)n2_RQktYvf9fi0CgAKNNqeQeCYypBJsz8Fk4;4Nl6iEOT7j zP_K7uyz70o`|Ru8*w_z1uBpcl9VTxg|Ac6IJoS3(eZ6n>qdP;bR&Ai%0}xnD9?Ro6 z*Lbsv_1>Gc)4tI3c6q&d8JDqa9v8V-#iS?wo38a(FS=IaL8sLAriUDI$Swgue9U5h zsd4Ug);PL~Q z+z2>|7(6#Y!rH41Kt24Krz#B za4ix7gCCk8;H z|EHWZCx=T3K_Kz~fWRM`BY(Js5Q03B02pbGnls!rMG=CSa4!@vBhnn5KxBa8L;Bi0q~UXsVB^~n12Di4==!yDFGS!*T9UA`pm$+x`X6ZTj6khQH_ z_=NQ(aI=2+B3Vh;ldL3JNfwX`$Qj6Mc;X^odtm#NSAL`af34fLl`-eO7Y!+p z02z=02~cE#O$tOn40wd01Hd8xObvSsG`xj} z*<46KrhzqGv@!>pk4M!oQ0^ZQfz~>U?luGI9=9W6kShWbP}5xw(p+eM?hS$ZqEe6x zO%POVccHPbMb&Um0;om+q`<|~%EKQnmNYxU()w5%9g0>q=5+5(05Lo?mbM^)h4Ao~ zMb)`Bo|dLonc!+|YPcruM#G6gZX`*LBze?fKmuh?%4c7WXTM59*hqrp$bH0rp#@l` zVgmeL|F>+HbSu|&&+2}CySwk5;TfSzrhkxz4k@8~?J;zwwZoH)BGe_5LF$k*2`lNKGRA{*<4oM`htq@1w$(pCN?K_JTdLA$Wn6d_ z&i1{}e*lZ^4Y3Z^OIY~Csluu7#OWq-oj8Q<5^mqSSBS$i22stl2Q0a8cc*d78Vg+t zry74VxN*90+g2e?=9;;NZWE`;o-J$b{XRHb1HZLxtF~?1R!X_{F=;azu%*3K263CP z)$QmGTO|j!5zIX2=rw2Gk!-8BZQE8vDy6p8`(y?Urv6eaP1YAr>ZA8sa*7GyeEr{Y zN!FD6zMs9rBeUF(%j?Ha8}2Uusmlze?i?*Uu4S2FJNDkseGg)1WLA+?XRX4%_zB!C zad)>mIGh=Y^9*mKZR6zdhS`g|yE9DethL*_Uc3T-YSs_VdGPk`&9HTdwVYRHSm8W1 z45TG+^2~UqfxEmh!QI-s^Q^_lA#rx%@eX5Jjo&zhyA^AlDejs$^`eopUJ)~bStRcE z4B=m(1$RxH#0w{4NDS|3ChnFvLwDEq?o;@g22O6A!XKQf87_spySr-^?r=6vZu~H- zz}?;5-KFrvDLhu;8z*NhzXBjBl5N|TnR!H3b@yOiYi;JeC1#qWZDx|UwAC#!{l@-) zy?=uDmY5{wF&PGScV)PlCrPqh+qNW0MCRJ(-Ukf5Z#8U1t639_y!+k*FJWjHeQ@vD zQ)2?WzyH7Nk|a6b-|xDv8xI9XW+YT*R#o%#_L!ORAv69_e#X!IjG1w<84Hpl1J8-W zH(l5D`#y-1nURsX@bJ6B4zrhc)9Nv_v^~pZXer7tQg8*|*tw%ntKycBh#kE!7yOEx8_)+12JWJHwo9?Az=-<}%kYyUV;Nxg^h+ z+Yic8nY+d~VOU7>-R8c{4zoQ-lBC+UZCmCZ+4>lBtqqr%`Ih3ITdybYaS zUTeRN?}*F;{V#qu?v^+uEu`#@5vPND;LPU4;l*8kIOHFICGHM^ zvraQ{*!YVxahSNv!r}e_W!X5myR&X{bvMpFJ>Kpb(6X|?-J!fHXqHz6&d%n>+1;$> zi<7up;_h$?k8U7$jM!t)+KDeb5_fxXPP@jyatspJvcL&Ea3XL5pB*G)3|cOG4BTDs z80R(RqSx+Bv&sl4Z-fB#MC-(o}U5ag|H{E;pPJP&iv-in4?Qn9=Y44MB z&N=6tbIv*EoO8}OpR-Rs=Z2m4J~^M$dwcf|y`cxs|9Pq?*ym2)?Sunn(oUS8RKlqz zs4V?)>IuT3N8D6Mx77};vb2ydYKIo1*0S0OoN(x~21`tEqDHMPwG%kuzZNJqi}A(h*SRnj^*G2t*lYN;JC4vaWUT8$GE zR1VzK4ji1&w)#ASV$=?(B>&n82ZR$2t?!_4!U5Ib5x!gPL^*LoI~9_*+5wg1wQ=YX zI3cxg;^2T&ufbYO+KC#q15O+!IQ3(x)DG>0O8Os0J;6A%bR=p}!hsPgX_3aAJbW2^|S1aB7FXf9qB|F+nBW|HT0(7_~#| zh7u0#L=Dz%X}g`k2?v}I4vbiXwV5zNI3R7Q&j2xLr+#GBZ3moMB^g26wsFUw@*jq6 z+lHaC|4&{W6Y!gb-*wvGBERc29DW=8uG8Rm#P3?o&+)rf^Lz0Bd%thMCI%xhjgfq1 zZ}fnS=@^MQld%(nk(kCvOy?`*cDyo=?`iDEBd~LrM)IiGi2*P&Ewb`T2(t6)yr~DW zZ%oF}8Y3}wf{}G2k`Rnc%YuhtGhdyLZn>o+`QCt}^Wd+%2_QC)t;Aq#VlaYC$KbK^ zDm++YXQoWY2;AW?1_5F)f=tI??5rD*`xUlh1;h6qkB9cE!G2&mLGp<7CqNKlddI#Y z@!+f5192k;BQY2O1Y;l|`D%S+3)3P9F^!Qn1|zxUM%)16w!Y7qrOX2{JvKLq!3e}W zCZfpcvcKx%tNVN5Vapr;m=?u!f(T+H24f`6d=WX(7!1IE>|6LAM}5D~ddPQRI7y$6^omn9AuzA#|wm`uqR)a8ODJ1&9~*t`Y+z6YG18iy)XHIFpOVfQFbUw5Fedy3xmJzn)5ly!+PUok#f1-CL6cWu1CbxY4 zhyr)MAPZUKbXlIwo?vkO6SNWKaW^>b`X8A*lY;@^!T>H{sGWe@8xufC2Z#YDSBwBv z7XUJ>^8(*ZGa_6727qIn!OS9aj1{aWKCt z1OuBGDn~ua_(2!&GBhRXXreZ3(@d&|*X}TY%ixt=uu-&bT$`q;P1Xv+D!v=Tn>5!T>a)R6^+; zVdulEc1-NY65oDrtiszP!1ZiqBpGdxi_DP_m3vkhcv!p*`AddxGdZ!luS?PXVdM69 zmrdG{^mbfBNy|)ot8hPan7VDKif~_aKW9n8`gn10It>1N4R-TCb35-udN0_G5}*gQ zC_#wp(<8poG{=~o9gA~>XdQn?e(HbT^Y{Pr{i*LARv!5c|3-iDf8Ja+$7hP&8r;_0 zUK|ST$WWyI0o>T)(4xboOn9MlFx zHl6L0)^56sUH3oJBKyNgAZD5A9^R|mA4|jY-TtrcP<5N(VW$7Wotq|4mYnC{)3E0C z99}qShorrUb~~_WMZN6Itt8si#(m*|#nQbh079tS{M-*29EJbWb_qF2V7tF=EF-5IPeV; zJA!vP02Didm>E}ImJz{@D1I+vvwfoej92NG*G!k(SYtT)F0V6BHxO;tbkolLjZ8<2 z?MjOs$?nF@lWi=5?J7YY_HW*f9snN3BO4`*U|(G;bh>6UHq6r6mC5o6#S<8GbEHZ@ zT8OrDWA{y8c*EXC0C1^z$t~oWlDmip9BsY!t_M;tyz$?DCTsSYLJa6EjVyun|6OVqg^%xcrEss%Gn8);7)A5bR|yO(!BONI>~ zW3SiTRC|O-2B+`RjkN0)iO=_yroYh!AhZGjRDs~S5hKrcfEbKSiY*X8AZ&cElC(Ge zZeN4n`>Pv#BT2GUydGtHn}9D0fcu!^CdcM<6SoQ8WO4)dFo6yo93t-B9VQPNUMXmu zy$d}ab%#>Dx*6^bB1Zs`S#n@?TR>4()rb3z=dNvg5F(NG8#}zb>&cT)f|da6+MvcXR!}{o|HrAL;WQ)BmqSl#V=li1GaUvHPwY-DTfni6uSZTN+Py0)_Lt8LTO`s&D>{g#&i8Cvc2mXH`;PA7+UQ<)`2Tw4LaT64;%V{X zkhN5mj7xOx9Pi7%akHb;5Wd|>^xSc1@hUV=o?N!ojzx$4^Dl?~=g*nrN~tJ-7}o#5 z-&rFdYcsL>oPb2Qm$^$9_>;E)`;(XK^$xrwgRv)y3mA!6#>hLqF{WcA=Aw+jG)7`h z6UBv$#56`?P7}d}jKnks6E4fxyN$PfHGXBU{4YVt=+W|SF@a-HQgB(k2mNLyjk&B{ z-hAv<&9h8i+1NCksxNo#e9Q-vG8W<+d#0~=!s3Yl=|K>yYO2_dPM&-~JTYz>bs^-p zYsD&nU0J!Gcgt{ob_jG1BVC?bLp zjKLT?F|@`=ri`74XfKE&iY?ML64TQN5r$z%-0yJ9AKR<*t2EpyQ9j^byeqQwctd*t zV6X#{QlVVqp(CC*UwcCbKHAZG5xO<-3S>n^G41I%32 zH84YS2TNu$q+xly{GxC4cfZP>-BG}o2n;|%6+^%f1H>LH-%TLqH$|5X3N3&KQGf2nnFbY|&URrVurfNwvu?KweMLaVNdnVpZmI!nK)B$H2u?Sc0!bB4O zeHwr}1OV>fb6+Z(VI9d#51J&u^4ItH8tPOY%fIGua8Q$$$#C+y-SpXE1$rK1492PH z7>och80ktYf)Io8v>1#{48~i@``6A%G~-iXb5&oE~Tm4Il(fe$!p*jtG1Hll6`;X;eq|gbF4)++)o|5bkJ* zx0=`mB@^FF``CZFYcPAt}8LwiyTW=0785CG-G7#0nI%m|O?zD>o@Hn0@LVC3W&+KFHcgoN~f7>t}8 zLpvu1LPD5Mh*3Nt5}*<45CF^t*0AL|6nbgsVWV@%lQBRA>p zY`RnrZ|LsnHe)w$|I>rT;Ro*V#@{Wu|Lse1dQnE1h-`bX_GID*y>jhmdmm^e^fCsZ zId3m!UMqrWrj5ijM%pH3UTY+#nKlyBk{F307>OZ}WKL_0=>@ak>S9juMdq2=Pc+n@L!%g>a_*TWUs6B8Vw)!y+>iGa7OPpLj zA-LtGlx5?Mman3@B~48h@4Sl5*LIsheU?@P7jUhZLC18rB?p)7&5g{WqeuV1|IZGu z5d?6(9)Pi%mf;htMdXuiSR7C9X2nQM{BpV60~C?-hIic%zPGBOH8gT>Z@A^62eyFc zeEsU?r1qi+1#oTkNI(OsPW+a1s@EmEXQ-FY@`m!(u)$_aCl26H<%>Y&d-itTNm#H+ z=T_BGr@g=kA*zmD9T{&mtoQ)@FKa3I5}z4tn55d9JmIEaqAiM4Am>6J5#P1W&Mkk? zn|-&bSJq#6!hicZ{ozh|YU02w&Rl3ln{M+=xTNCgNq1h?dCnc1M*MVpe-Gb5h|W59 z3`dv0%#LmU$j#Uj$L}YoBEZ95W|In^`yGlqkb3xQwv@Mo&zn}v_?RXo_u9&PFrPt7 z=1HJMl$7akBYC@TFgfMq7GLE}zNF*+Z;o;3G$A+7_5<8mgC~5aTfAYqWt&~keC_{W z_xUI5ZYCO>*>E(makKJfArpwY9@&o^1>)+qVTzA&fz=8HAH57?I6e>q4swSvoGk$0 z8{~9`4FCtgI1k7j18{IAMF+s>3u(M&n(ZXpcr?Ok*vSPrIFn_5_X+W?vp75vIXF{n zlmNfxCjh*V1dY!W13x)p2H-NnnPkVW#F?H;LIhO+o*zm-%=gZVl&3>|>KKU;d`!%8 zJI^0~S0&RisyzY#)#-Q{C{=;Ls8FJ3m=6n80HO9G4^gP$DXh<5fEbCeZc~%)h>ys6 zoZ;Yir8+IDnA&mxOaxH5dRBvc=)?0vsX_ojtN=s$e{o#_ABX**Z;Zi6 z%*65KTSO4Wo`_C)f++hdAENAk>?u(M**VPjbb@N{O63yDr7}w863V4=piqDTl*BSi zWrS5vg;IG+Nd*H>Nd*8>L5En*;JTgm{6Rz?o{=_&la0j44Cd?zVhe~ZO81XV|BnqK zGJ7N6)(!i1PZkaW5}%GQa&NQCtOA&-r@ zi0;G5Ws-n~rqPtw6T<^G?!}9Ap6;`#q1~VFz+og#O|M&cY-29raMI#%a(OT&ASs^$ zcyH!(9v@DRWP@uk==r=o-@lubb;pgrwY zW-D-w+oOa+C;>O;aPlmngd-{&%E`g8-YB7P^2AB8{t!y&D4`Hm2=c*-3WW#W{D9eR z7H)F7>0{532R5f0cKZt|CSq_wL9U>NvaJ%p$jLDnACL29UFI@3r=*ip^ULCxb_pd= zf+y$vj~~1j0$Cu&V1Ss5DAv1Ax2Qstq9`c?G1Hcdb^=f+qV0&ZBjqMHE*|KFUTkbHj<(6fvYzp~qX4noa z+zwlY?Ld3Wa2EX0l3kClAbr-VZ6BW@>@P?D28c}K#Nw>E_h zKnaCV0woke2_+P)H~iofmUIGS=s4~O$+eLMmd!~}O4yDn?01T&D554>)7^fDiZY{f zyjVx8O$DA>VK3JguE5Lnh3m`p&WAmQZQVLCZ0mMPfe^0y%vRC#NMz$*Q_Xa#$bt=& zkS~=1!Og*ZCGCoic}I!4gf7so^|ZzztrMPndyME52674Dv;%+`nH3M7Xl!zTbIIo0 zXYOd2h}+24wm`KED!k!?$M?#7BwP@OkPs3=z(@?OF*w4-2H}?SnwXb|oNNVB&Fj8i zZY+cRG}fPvpN2p4RDAySGxYgJ&S!b^d!?p6p1Tm9<6yT-b*RR+0HLrrlt2lEb|?pB@`M;`NU_!)7T8tfG(0)2*dUjput9~4^|r}I z*(gFGTsrB1a#Uzczm{WOY5>?Q3W!4gYvDSwX@Vkv$`Zn=dCw{mgPOlW?5u0MPr-eA zzP_Bx*cOnxXw@{PY-E`rEhd4 zUxA$fdSl~6En(|ERGVjXt)eAIX5(#d zC^wqHyQZG@@doac?T}NTB23$x;m%T3lXh%)4Sq534eYrbgB>rTsnQ@a#S$(|P}nmsq{toGo!&r5gawHD53t z3K&7dG)pBFL?Y8yVC_@EYLS2$i#Tlj|C2HZpvsU zo9CYc4!%V9CS^!);-YcOx`Zv=ajPsV0O1ouu?Jz7TWw!wkez(FRQVe{nLVH?=Y;T;tLi2{(O57Ho--ZRSq&slTPd|(aXhzjAsm+w6I)2MWA`xCau zaYkc`$G?i3>!AcLM+p`-`MIBaFmGDeDFPhmL*8KI!cm=WPFv-901(v+wT2trz|Up) z0w^6^XFX^adnuf4D%X2x&&&Kt^=dQ07IgL|^R@HLnI*;ME(kp&{ zTQy$LIS&FkL5@rBwCUs*J%JlnUJ})6Ib^E>!*jN^Lo;Y#5!w+I zIYDUJK3oFOXUBj|jBF-3nax+08&yO*u^zF05T`Hcolg3nj_)h6xg_~O>gIPke#iI; zHn{;&fvB=Eq}J@l%fxP0_#%~2z=krxhLQ{Lu#$f1C4io-@Djt58`w)FFzc#(8=rdvQnMw4 z-t7riThPRCp#K(31f@X| z?vOMAg-@erM}|eta0;j@OS=1bmHF2R zu7bT-T<2wAZ?H4xa}ofk^}aQ1x5Ti_gSr$a8G2^vwI|h{F5!F;ssiYZO$Ip3JHf4Q zMQ-%5Z>cT_Q2Lt(oOP2@QPs~WMGjxUGa<9MKB=`+nl`W7efg_wQg~ArC0}G2FGhvP` z>FOwmFW#lTyy1{Pbq@+Uuzi!J+UiG{ljZTkSF8ki?bFvEm7OFQta+Jvk*llficYX^v~!B(+TddrC? zmh88SH=8$?6QMK2LBzF_`xPeP(#+ISzRdsl;;3L6~Mu zu1r8ggr3sB&fZSuuWOE(!Kzr0-9wGnRK09rHoW%58h?i_>*v=TBAU% zIBYV-H{5irpZ@RG0wjWozPvMQRE7fkMmWl{0m`oG%MxN3B^nG+ZI}%%Q-h5NQe6-) zfdX+`9rB|OLZ$j3_sNk>OpV0ajtyKP2W5zT?!C|FHdh}C@W8f1AJpYa=?kQ0z1dJ! zRNBwnrd@!TMtNUAV^61OD^Pl(JWb~F6A-bxet4;rwrkT~F|##nQJQ~b!3W^)I4igg zJ_Z=VG8K09M=kw$Ttz2efu!n7C#3_FseAFjUaHxIUQ&YnyPfVG#QhCBh~oY*q_J~& za3k65<=r~ua6$mebF_=DrP&-~wxFsFS3PgXNarahYuUZYU>4|fI?*82)5aJGT*MQp zbM8BzW9S3H4hx+}d@}vP;R3g|YGY=JU>gTckt)6u5R_aV4zm3aaWupv%dmo!VA^{1 zSI~vc1Ogf>iaa;Rn3GhqztT|fD;(+@pp<7a_%ubB%_D&+yFeVQvQ45`4H!UymoAHq z;NyC(|KzYo}G+8;OmRlP| zCYOYi&cWK-05*@%-*}h-5a@wzq<#HyHUQ5FRZUtm8i-U2U<3j{fD;3;0U}R;2MEC2 zzU=XIoO92?(LSf(Fvf61j=;5ZGC;lDp#NeML6VkNL?k^`E>#hgl|%Jwi}oF9r!5g++CyTbEP7+IY@yj>4v^ z53SoPEq=z7W_|j|mOkkteOP8mhdpgmlBw2Rl71ci?BUQ?}H}k7f9~qqN`V zAw_3#ckSV8<>4|N9SY>+^&UW12%#b$#1xE)Ky1FfWhePl}f3p5BBs z1@2Z$QHPM#K>PEXe2O^4ot6Vzh>#7F@JWPPFR153|5ZsS{of``E#xC6=bU2k$H(s;2d2NTR)NKX4_t=GdaysPTf3T^bByPBDKq&zS+@z zo$2-xxZeVAH9ctyGN*wk+9oI`g#ak{ zFet1l24JE?wD$Dx$;=POg->R1H>pC0?xyN3Kh?+P2O%OoS`gvF^oS87W`lLkdS~rD z%q{x&d~i!2Xe-F|s^F-y5r7EVry9^&YXGcIO4&wE)=oS2t*-Or$&-gp4*Ze)@GL!? zql@E6hhGe*IBxg3yu6mw{UbbPa{FN;dpx=p#QNBS_Bngh{sOu6EIsJJ(59lAl)jZT zD~=4ATXjhU00?!736Oh$h)f>mV04#W|cQj0=YD*QuGn9F#^8BF3SFd2b>e<)cj~mO}zj=9(1=2mfs6gb~E>zO-j@!~XBoxpLP6o5W~ z0jO5tOZese2MhkBnzGGqel*j+JBGqDE|OH~h%|3_<-7Go&2QvaJHO0Ykp=0(!*XuS z+%`5IwCHZDWC#4g04Ggzn-e0OG&SVgRIjP6Y-?>Gx|Ve{Od96viRu{Q93Ean;c?_$ z=NTdLzUX++g?!d=^td|5@^sBvaO(2so<$yZ^gm@kH<*ejazVaO-CpFybu~gMN7;f~ zp)5Qs;R0=q<93-pO+gQ z2ZP+E64~Ju@%Zcp*s`j)-YB_CvOv#a`VGv>Yv#>TrCjRWZD#QaADDt&2B3NlV+>Gt zf_pe4a&?D5yo}MbVAfaWdEUHjFfu88Vkdl26hO(Nq_}DEHUWcxRxARoQ?H!XFE_SngIbLWfg%)KTCt@R zn*u8x!nwu_v}1kb8!g*d*o(bIV=JU zfh?-8mUPAnPP>*`Oq`2abPsEfiAxS8gl9sTOU?d>eEo{-Ps+UN`iv}>*RAJ{C_Jjr ztWR4bcS?GXmjh*Iq{K5fYs2ZI(^jWgPngY6XrvG#?c8KA!4oX4S7>8xIQQ}2-N)Zb zJxHhMQI^2zB$jQQSyY13xKgsb(H46ABSL?712x<%E1dw{RdasZe99-EuZH_?&>ecG z+Qx(ZCYslLhNlTzqZuAId4tViV{p=3`U1qNUGmzn)kAJsN!!<|Er2nsX*z2tykF=z zwh$-*;uPM$D;s{#-Kt|#J7b*tqx{1Tr7qz97`|b~0oEMhg(`fGx$BoVvZa9>*ICIKx$r(+uC^fU|DVHyry3;p8y4mf*a zB>`PDhcE-2Fd>=s}n?0O4kU>Il`C zT466yVOTtBcSf(R!2`3+W=_Cm#^t>5IXw%U0GkLV$CMsEf0u;8?+aF{8 z;ReaTHJ}E{6ssqHn%6!l!_XBC(#hIW%ZC!`LhI}0TJ&zNQh8;evoB4--NMfj_`xYP} zsqQX;C*6_pb=Th~I3!fb_t3Fg=Z^VuIdl>|;yLTD|6e77WVN~t*ez{=OgxYKu?TTkvJNiB&CQbaQY(3v$mdzRHvwlP7Ln8-di)qj(1Pw z@7$~9{(`U>a#W0U5)#8ez0QIv+AjRug|Frvo-@{mftkj z`M6arcL!E&z@$UB5}9kj*v<{%2B3>P_1p;Igg;R?f&`FX31wN|>Mir@bV=qp&cV@y zjk8#2v#6#9m{fxjf;LX52&c-1&GAz0<8rqdckkM5Hcvt$rB%xL*81^gNzc}aUHd=- zyV(|;dm#uVG;LV}JPxQrfl%l!M9y_AYMa!xo-mgr$Btj#6Vv&(SNvo}mN@78g=!V> zB@8e6HssNu(&S_%3wEW#i1QQrVS^uUlr%XMod8OmJ}fd#lMleR=9_&>-no_O6+fcJImdOflkI)u zwWm*+MlfUxwxYkdZ(l)n4tfW1wg~yOOfP8`+h4Z_qC3F>GT&JdQP#r&Ir%d|z}1qi z?VQuyGzGIXkrT|Z{>$YqIz@*;6KZF+DU}_xilUwwqqtI^K6< zy*)dvxrJq2k-_r;U`lxfcsUE~M!=mUmE90{8^}4vGm8luo*9nZ>^8?(GnvID=kku@ z)g&a)iBcYhJCjfQS-kf0p+CQKsFRs)Ctyvex{U6?y0L5ExCwOMp}TaH%8*6 zFaFmkv`e9MZUBsoEtw$R$_Z}PC%>iD+9$19MzdlS?VFg}Zk8{KqxAdvD9|k(Cuj39 zSz=4YDpWLQ1$h-5{S~@#$}Klyh855dz?>s z&*_igSNXXw9BMym@9Hr#?%MO%=9J}MUP}aVtm6QH3rJ4p_uS*l?Dg}HeEs|f@rjSV z&TUitRWEiO}Evu51% zUAQ$BfD~Zaks~4^O;nkx_&G&B*^__s=#;d7=xh7X-e(;vhWy0RYdZriDxL>_Mfs2d zmz0X!CJoOGC-Vg>^3yW98La?>RF%S~@N%dYONWEQ6_OH#Y<3Js`+EPqdj=PTmzL7T z9BP*51Eky#U49?vh55w0GB#azgj5B`taVYvNK0feqxgrH7I&$&fzy+xLzI z&kpDO|Ar=!v};0pT+{Ic9 zVzpm6im=(TswA{v0j+$ldgkinWT(2}Vwj;~7>4KwSu;vsmM%j@_!Z<2!kuXGW>XQV zGR*1S3v#ai&1bbfbDmE@00^*C;M7Vezxfm{Hf*V&_io^T2AKr{x`29=FfUM4hS1!%u?|B# z+6WA=(y!9-L_ zdecEmY)LhHeb#h$tiN(^ejn$)<@>wjeH>ZyWaub;Wtdy2>K;p2YGq(6-ZbrRSnSSx zCNoSldv{IWRlC{Dm@(A8Rr~V*XW*bV?v*?Rh2KYQlK_uX6q9q;*sSWU-y&sYWk-RM z337={-~<@lS)2F0e)UGrv!fL?@z{L?G94>VLtf?UJ9 zhOl%qMI0a-w9j(kB^MN1V6QQ=%xg8J4u~lJLv-FAm+eGhHug$%w)iWg{ z#Gs!yZEt)=^F(;O4+jB#PFC!p4+5NSwu=ysyS5w4&g3|sIE^$lX>H8D0LwH@Q^`VZ z9}_>F$k!mtxNJfpuy{Ur^Hjg#i3{-Y&6(8Yg9z3A{tt9KqZ%?kg7awJl}ZRGiJl(r zHUJgM%&BMdt;I}ofZLwOfH?zXCPwwmys)`@URZjKvwV1l&Y*QNQUbtB+TrS7H5GP# zZVk%+^L{k)WLs5UPUfh%HOLj~Rbgz&XSeb|fI67uNVFIr8uP{(2FWhWI^>!bmukdR zr(^D|UlEa0P*utj0N;!Ow^sxCY0Y$ukt|cHQn)-DeY?_%H9)A|)}#-Ul*N)N$Q*OP z73qIJ5~o{-b+QGcSe3tA%(&0Xf(1;dlDECZ;cUH`p4(IBo%wpZ>uKp^QmOScP2W9J zucy!J7S@nbkfvyx=a*_bq||t9am$v&9Msc>XdOBrDd%8LP84Cim5nr!@I69*aQzb$ zCvB-x1X}_cCYPv0hky`u$h3p4GlR@)-^k6CITd}yA2xEH3U{8RiG4J!D*y;+)>)(T z6&lRLH%*DJW!jxSrB`AI4-b3dvUn}giw&o6*-{`v(QhJzj?%G)`0O5NpdUJXBBj5rW491*e>h$A7sESR~;EQdjsvQ$N! zQ&4&y5z(qzeyW@s@QPapY7D_R2h=vzE}dr;P3=M`6S*?$NX5kEVkjHK0zQj3&l@;v zOal;ukr<4f6E4??ieM|)e1gNv+(^g{7ip}5e6d)nF!bo%_P>wR4BIa3xwy(sxq21z zs`*8v=Qs_0-PJ={MRMletDM2o-o;L8>Wzmph#6(0{2Gt2l9mR7z3!cObOn7JDAGGD zv9VQ&mDky31TXM*Y|k-xLBOh7*1fQ(zx|O7oNpUAS{F!#Mc|SQ6i^jzD!@7hFCabV zwa-vnB4wHg`F$fhx>faPyXjN5)gFLG+xwj6whHAWorh;Uc9?VeWH)ev2}xo$ezumQ zsEc(;R2#%Wux8=912zx-PoCBJ5wOzoB+)R_1UhtRI)&cZ0mQus!ImO`Itiww5_Zgd z6t!o5jM`i&KU|uOP+fU)}1k37OIRR9t$Z2TBuD}|E!op`x znIiqv9y#)gm?lW>-<}&Rc;W4(~e*EJZlTgf!u_7BCWHkdq^qG1D$*Z`U_ERV%PW z%an7IQN~H9RPu>ND-|B;tMtET#+dLVbkjvKfi5>f&kensclvDKc04>uqg$Nf-lfu( zxa(+^9;flh$7#b(n#8!a!qEUPp!o6d1=vF6g5sfO`gJ9{YK;rMClh%@1s(^Nt*?Xf zS|{g#$sZe=2E;03+9E1ShvY7jsybYQ5;jkg2MEnY6hq10y7t;&k7*gsNZw&*^xcql zLLY_=7)pT1gPk*nc#`jJG+TFU^=H)c6h*QgDJ)r)NQ<|11$}^3iMUrWb9OLkEBjHTE3(_y z7r1;ac}}H@*d1aBl1OG5fe@8jHgCkyx{^1BQ|kU;76jU}g}e63ZHyRYn#H@xibHir zNtC_Js0hPN173F))Mm51fpZ#KBa2>E>{u*(oNA;4&nYn}9MH?4>e`1|FuFe|Ok$P2 zIQ2CTAnTGnYnnuv^qqi8buSoMCkKVQH=5HL2FX&aAagKTB8KDuD3voVl(=~}me+*G z*Y9Q(8DB2x)e>LR(BO>PGj^VIryP@{W1uA8-+(|sNKp*DYngs>oxDwk_!{QRigHvE zjUoW`CT|UsU`vlyY?*%6Pn@#hr5Ut{)M2K5GPj@R0&kYc5rN>)nL352>a4SDkS!bS z_>9wO9jBO6GxaO{;FtaUVU-Igk42^n-&m32o?T+h?IYphdF_p;ybq1dn4|zKr9iTp zpWErYFoA5~aL9&R3lfO3YiKJh2NDDg)`^BGua3PhG@^$}qE6IIS~!3$ z45ypjjX2BnMDyz@pOHzV-6?eDF0~-{^%rK|}$elFgWo;~M>Id01I7+fEbKZ!4s(BWz$Eil}4$LmziNoOZydz{kd5RD9BY_1{8W_|LZi_ST~(dBK_;l z`nh@f@Y&g0dh1lb>fn{+jpQJ$5pRjoY^P1;$Dz4(Z4N*{q_PrPwvP@2BLG9r9bko! zU)w+dePP0v9$Ku4V}}Lx_^PX z?O)Ts#%n2q+Wr29!m~BLzZuU?5Y`6i^pu6o1lUIGW4Ef6@s^pz{kJu7B^FgYhKdl_ zIE9|33IU?Hi8gVk@UnHBa3Y|A#exEiA1Iqvpaf7}iUM*U-+Yx_y*_)xKk;;~*V_YT zvC%pl_`F#|1z2Tm2l(_i+lSPT`5x}-EJH;{cpzZXeVqKbkMfH;m_0 zOS&)PYw^8mtdXG@_Iz(-gaAWL+^H?qA-}?2B0@r#4zD4GuNg&52aF*G(>de$^v|Bt z>GJT6+16EeKj1-(5Rn{M0%}RRusx^*Ims>UU)%O0`d;JGzt^x~FGPTaea<>s zxE$4?e(^8&TFOFU+{jeBm`)Yj?R_9gB4u$@{MX1PXlf+(4jA9A@*i&u5p=s4a92A>c7Tm&*kQ|7E%TGJ-m+P$T3K zJ4P2)qxLMk1(Oz0`xGcMe z1IiPjPkvUt&ZmWkm2VUjOAr!5LU>L1no*1z0FuuG0#Ks}XJ3EL+Q{V*XFb3<0CAQB zsR6JAR7ho6l6B~(;=fzOd@V9xouiLW&_8{P^Bc7D1Gkxf?78N2WS8S+*8m3v?qy3;Hsl4Lll{Ykk zh{bngZlq6vQQ%@pL1cXrD4}X?KA&W=5Q-TrETA@r1#eolB~iuhGIJmIgdC5mxdD&^ zs4b~&bVq($x-*oqX$wTbr1k(ce!h(iL$C(Sv1`XYueq`Ibagh7*pX}UY@ABhsD`-M zrL9w!QawvVj_-S-Q z&wGfSwYfyP=~BYJcfr->HpY$B1-f36634tO@p?ZGoo(m^w7HuF(}zbJJ<9p@$vpW1 z(%ADvN9lQ7IuR(`QiF517n{LmZxcOE^wr5T&Q`?hC(->&`1zU`gsX6Yn$V)8rWz1qyW}T@YfYOcoya4M>34}1G)n&F+8uDQlHS6vH>DV-oVUKwG z0=}j7zQ#cm*+h4%z)>t4t+Ti#76ZpX-bsMiZQCk+#9q(J8subgHEt5 zIdOiJ^@B3foZJ8kgeHP?$mwBHl@2fOMdYXejJ1v=a%y8^q--BBH2}oX3xlHhNY{!d zC#@$AU9m+2@Bmss)5QqJsE5#XP>~arN&##w&cJ*x@m&(6@gRWBVB^e2Ns%%kuqm+9 z{A4+*00neMXP9?nQd^?FY3}}L2F^0w0}fNc6|?ma4;Sh>O$BFF3&jjEZNtUspYA&M zF+7!%f3qeXfNBes68=O<3No}lhPeBZZBL0k*n3p#11kBItq?%%SG88V*Qat8I!C$X zelcxIIGNQ?317PnNjCB0a**PfK~yhbv|JE-yVMhX1~35inqt(D{2T+))ToQNd>gg) zxl@9J3>hMHyk~BwZ#*+#23sF^zgSbazj`GIbY zV=cXX4wieXD3+>aKhL6Ia^!~nc?AQKPtLP|DuIs+N2pl?LTAas_t%Y1@+2xJ)c(!8 zUGi@TvQ%=R0l^!^ZR1&!LF(xKR}JnfnomN#oIUq^%#R;uX$Z1Lx(C@@j7GY{&F#7N zz$kl1eav_OptPrt$mAJ*g8u`%Du<;*+YtzEr{f%zSD+KT9itZw;Cj=E1wMz~=Q#hh zbU}!rn0x*UW0G3B! z{<J>Gg~P@=z<1F`PJ$g)A-nh%hEdDn4Zq$D8lsVSx^f-&}O^&i3)g zs$xK;dO+3Em^;O)@`Nc|VsxHri{cpjA_7%EmkDotwLEyg-46o0HnxV%sEG|u`*pz< zmU8%p_<=S^zMXj&F}Rdut+shSH>E0T?EP2>qu0EkmR-V$T5HhI@m`*6yxowt9#Uo- z9#B9kC`;;+gH$edE1&(=hfaRiO0H4qObTBNZQrjjR)VqGY`eXEx>hllW6$YT@F8d0 zdDk<^NGiZ~PIuedp?gY@yWhp%M;EdT`$WvxN`0FF5dc>J5zB@G%^YjMF`!~)pcyCd zar_x^bbv)=><7r{|9xIobN6D|Q>ldxeU0QkZs#W-`$h1$1ph2M(d?=^e9}ChU?vBE z=cZW>fGXX^H&R8Fs!&eWPs8~H(DYCf(Z;0!f;CF>+^_!l_`#OB?lUNFzia$qw(Dhr zG3%<)>mX9>tKgaS-$UC+-mgWTb3`T7c4))3Hmi*@lUIV(aJjG)CVcCodyyn$S8?@ zeAr;VD*e|Km%eXr;GDeXGIh@)s!J-RT_Z~K955Kk)9~*<(C52P zn}7O6&epA!Idy}Z!5#1VGF$4de3L`8g9n@UqebgWE`#p=H6wJ*L|sh|Dp^^Wwn|W> zO&{rqFVRtWa{Z260NQ-s4(d;M6wW|*rj?zJ$_H^5@jMgn8+`WI@c43b)HHHQ{l9Zs zGmc4`@toAn1J8vZJbrUcRs>p`RA0hKt+q$Q?qw&t8P|Zqs|{7I zIgDda{>h>l7`7~6X^A~8mdx@TObQgD0Jm+X-W8}qc)YPqDMqK!b}?mJlO8~K=*5ry zB>(f1ef*mFNxK}qS0Y^^_Ex9ei1Gi%@r=X;6gPltL)WM#vcB5}5!CpQHyjSNyS6e!8zNSP}h+BzS{ z^)NF|F_O=0_L@TX;BAqaOj|YLqZa3*>N++1^7f5qa-du$F?8?A3Sd|V`es*#Q7cIio0m{#ilJ@I9p)F?VQGt|Oq{hwIYlj5i+bjAoTay-cSjXsvnLd|Y%Ao${-=$eHg{>;0QP%yAC)v!p84!io^Zlerv8r__f@6-8^aEWJ71>?%TA zsRaND$ddvFhzT({jIlKfV7XgOIn;)cqRov6CgzpYdhTryV;Kv}aaB$z8)dXEXl_qE zmMTOCbK2pQFu>pQ+(XTJu8Q?m<8$}OC++JGhR199%Dl<~AtW5pr*Cd)4|4(XPEG5DUjRhqH zfrNpOeKaMjP!O-$#pw2*Ir7i#8h6|#_wBI7hXzgIyN>*BgLK<`i6h+0SgUq4%f_rj zRgSDcPPq7egO__VZq^U7T^{Jf;LAP2k5)m4bG`&jMGBpCJ)JUDWO=@gXfg_nBJ-M0 z%Ueqy&zl$Xys(BnL*G)FkbDCNVpzC75RC#fWOe%li@d@GmApfh9C|R3p{sJN7RSQa z3Ra%)v?j~9U4A9bfNEp|YFz#0YWhUe#u!`!ylH|y-~;vvJYX%H-LK2`B)Svb!|v?N zf(G}_P7^iyHE^HQFXY2hsWxjPkC{Ecv~#(@c{DlLjB0C9H}ICq z9!L_GIOfEwS^0Z)iLTg&9o+#FUGQ&bfHTW(E1A)+UOS66H(S4x|cIzl_Tqe z6DpwcMmGoMejWs%WcB9;tgrXM@fv1D9n)v`-XeWr%wR5>8)tqQ*4?!a+m9Of*lstw zSnc*>JKJy44$u;z&2Tp`_wZ3@%6;pAP%Q8#^{2!+0!xAib7Eq|W%6$64{Q(x+A!RV z5ULA-%l2SqRRI$=_Gos_s`YRHh0YH(v&IB2qJtbFB9ENGx2yG-eCGLOYhe==3LGNx z{Lz~5(&V+t9h`oFkf>|skSMh5>=e}BrC;U_6oNjyAq4>s%9~kuyE=KrnTxNPcPytB zE}X3$6BCgIfQYC-idI8=R-H4G-IQj!0lW9Wedz7T0&o_-Xwns~*H{W3KHxoDQ0w`@ zye$R}7E6}%X>+W=G<<}VjRv#iMN|U_h=}BfqolfiA038>hS^SxiWFSgG?S7Hdtn0h z4w8;u$Dd9x%EHyaP8&+oI{42+rFnBosWJ!YkYkv zsopc}mQtiSVvR}er}|&n8uWa2U(7gNe@SF-^_F*c&2n8Imb7`JbCkYB|SsfuY^}sq4j{#2FDX2Fg!6Ycb!!wElB2t~)z%@LdPhS!CZ-cCr=|8=Hzo zrG>hBL$o!;@`|LC>a8AHuh1l# z^iqa|xI!^%^T(6wpW8!bd}Lm`>)`MTwi|HKAGGw)bX~I!7lf^o8?bYNtBzx#Z0UQ= zyG=@-tY6_*f_$YSwlG`e9ok^2RR?3qo&ks4Kb; zQeymxSdGd9b!MTV4)Suq35v9r9H`{vs8qq2@<4`$9Jw%yH9=5e2JczV%Rija1fFeXOG`XZm{#We)Vl2SonzSBXUpp=2z3zz?n`tRQ0NZL7~jg%NNB4uTY+H?fe+74w2 z9nh1`tJy3~NGNv~g0mrdGz&LnzIA&b&3AqIBkV&+v50wU0XNIVZ!0Ke37BSJ?OcJh zVPFsqb|==(oh}ZhQ)vU*=I|Y!Sfkd=R=84m*)j{zVg$6jxS+m)eb>y*PNKW$8u#KJ z53^fYGx$m4E&YiM?Nil6hTN+vz~mLQjo+RuCh+m8ih5;b=Jp$iML!>WbP=|9YSU@s zMlA!dPyq2}`Yjv?ZOFI+gn-5m9U_nz=1@YS3WLXz&#G6^h}quF!Ei6BF6M)zGIJwi z*)H~}_L-6=5@!m1ySaZxt6_8~8~>r=U^d8V}YNv={{^}2QE*tO6yx9BbA(wT>zXW9_53}=X4NIQL+ z?ungzON7EJB*o$b#Bw z@HHRj2_D9n&gwpP^x_D6miR($do8yIXi|bRalXrnAE(RWkPOJm3D1_j}Dc z9?ELH^%iKsxAN|tZH~(T^o;2otzT<;D?Uf&Y>IYn+sU9nIc2e?;qBbcI%`u-raM-F z^hTUvNgE-kqD$6n5f76vvDtZN&$`>a+l#GkjKl5Ptl`;WMr6?R>p}m1ed^=x%%Q{; zo3p%v^ozi$01jEiXY0Icc?^lr0T_S=62eiv&$Mgz8w_8M=B!8U zb<{g5uW%r$=AioTv^%;dAAG7^t_pDn#beyv%EbCY2D+p16v3OUCS3*m7D zMkcKri5IO4cP*DU!Qv8bA6Ta(la3n1m1*wx00|9p_<7bqbxnUqgA^~X2nv;;Kmo90 z+;r^z^rBM$LOK0G5ReR1|30B%c%3$gPt)B>1=^fj&&AvC1(hiGvlc5r{?69`l^`Ih zOioq?MSZ_o?x<|-oHi>x)`)@Gq+3{TrcoUjw(erQen#ETGQiv9=D_dE$hP6Ke4rxl zx$u%Pv_)dA1<)q%3jn2_z&qmt@4dC1fbHcgxxo&1K4Irm0~kOlB}o_Qto=UrWakO+ zLpSokFM(os`@t2H6y(5B4*War*%E-bSqxov zrNgR;V%2hOQ<(c|Zqz~zp{NwS^f&kv^zSl|?iaoO_(lFKPJXK=>|qUEIR5u@_{4b! zA#2XLTs7o&F;CRo=NPR+yk`r0<3SN-kiQlux_`b{?akq9U|sScRi!ejG5ksMqnO#zb<+isX zYM%oIcsO6dWThyu79JJ_$cFl%ma>V^G>YU^s0>uzr#-oupIBBMCRs*E#Ud4N0<&B* zwZ+FXhK5k08X(H%HGBxk4cj(7&f-Tj7j49xq!bZJ*u=Fpgq7Rv`4p2@u$!JwkL2-a zL6-$6kWVYUrrRfF5fb1IW*T|y;ftcM74)nFvIDeKm&}+F(Kjz6Cq*) zWZ0ruEqj!1WrROD#c%`%>WJaGm3&B+M5xJ~+gK0~QQ!=DAmV8CMz|5EpitkPe~(|T zw5P1W766{|L1xE+Kn%%B0U^6edRpswZ2l@yh|6uF;2uBIrbUDB>x})`6nd22=<=bb z1J8Zib1{#KefSsv2cjyM2SCxW#H+*jd*>E@kC+zMy8S}8hWxyFW!J!XmmmEG8$aRZ zA=fPgzGRIF9*zYBrni>0^L9_#Govb4S5zjmGVcPyld=kxjXHDg-bEu~vk$#Sco0(M z5XpCJYlEid6jE)IBVQk;%Y!?v3f4nV3UVy2fs^4udt&N$pzk7Pz$+mGIt~s!9G`1= zj@!Rn&9C6PL(G@aM#z!){QuLt#~y7r4#2=t7OB5r<(e=A;60+6na)>BvM>hyp;%ko zvrY`|o+`T!XFVozGk@8ZJiuIbIfP)j5*5d6kN@TGjajvPeJI)?6J3p-kejp)98Q68F;$p`1La8tFJNA45NPpP7IKege_B52z+MxQ;(hWdCVmEVHASxo=4TMAJ2);iz>@3 zdVubc0DF8dxL^L7o8+ZVnKfW2jAan}$#lVJcefR>umzEviZtS{sp*=V9Wl`S#{2QC z?HRh!$K%;dJ(RMj8sP}M2Q)kY_!)Nip1}sHIfA3~s&aUqFfCaGdhag@aH#norO<0tH)0JLOWYqTeS~2 z#WT0epvI>}R9-V_g`&3-_4%eO zfoK$!ftb;nFZqzqdjH(LrEJ8ZE$54YhAL1vStcOiO^hLQF4mt%-Usb|73n2rMrICO z*P+j6nd|zD2VLoGxDUsL9Yao;7aMop%uo(xXWJ!L7Au7;6%hb>1!(lMig(%a4liPC z_j!kp;sBJdv8xQjAyr{#m*ys!Nq5(0Fbk+(?X{Rd>E+*!S=k9_)hL<^n+=+)jcg6) z{-%!HWLnX|3KY;_vg-y`(WptYsEmM!mq%xnVuZ<$_VkC|S*Fd_jxn(|-uYjHa*YF1OTf4IGo8DkFiPNi8-_%#*DVks6N7ntp<48oS*AXAx?*+9Hwjb@D-9m z2s0@TY++Ha@W43izS!^ofLX9R(Cjc+FuP!TllIfOirq7~rR|h6O(wG09U)*;z^0^H)_VPxC&!XfkR;p07Z-{Xv*k3=`w-C zQPe4Ikr5i2gka-VIu#;owc2ikMyg>9|Pokp?eAS+dXG80@0 zCIkrf(k@w^R6$sUuX82y@iS3xmhHflrhLw6}15 zsj`5q{6vz!yHkH}7eA5CPuMM@^RmvT!!BWpC?}Pqz6%0qXKB9r7Z0e2HBrC7wxBrV zg(L!z!6#y5>qj+7Hy*?gU%)-SLGc(d@wpqMwmD;)Ml5s8CbR51(EXe~9Is1*ynnNk zCE8J4Q|BV^xtXO8fE=c91ZttOxwh|{z!cuGNl&hS8kXlXq`XCDZEjA&uDPyBjTvaG z%;?kwmRM}IY&C!yX2-v!Cp98sk6z>a(I_$p!H!xsQ)?*ZGg9{{_NDfRwTx@X9vTp! zMyxqQh`J$27)c{pBxQlQ#E5Swf$H)KFd$#yOZASAvUT+A3#7Y6n~GV1&0eGPt{_<(hZdz5z2A$$A~ zqV94MYJf-M;5lq)2S`z+Fz}Ypuu;0%+aK=Kk9ELh@!$7=A|iSK<#I$a04La%N6emn zq=TuOazyX*Afo%|#g%vWsXPKiC8Hl#A+69e-UHr4JSi5Uq!Fx?ej_-c8Nu?$6V>Yg zBIpWcL=n7dHxBV8Q{naz(ozLrs*nitz)`YE?Ax8 zP2?TeJ5H|O;DpD4y2v;j*Le+XKb*0<1Rs|dwJ*-TuV(CcnypsTgW1apmRNT5I+<@& zgU8N?`-!I_r={E&1_eb!O5RYEy!M=^o*=(GH`V)N2AWW1skSB-f6x~g@gdjMyaRMwUy(ib1 z#|RNSLg8}v6+7ir*2lL{{@|jp3w_9@WLiuiOzIM+_LQBBW|#N?I;M9QzhXXPK`#wf z9O@6U)zbb^kh0zmXk6*~=OOEg7;zH?_i#Ve?+VwaJ;Z|*f^|){Y>DoS6TtLyO zR0cQrnAxzAq4HoblR3C#Ubsu+N|(>?=aS^j&yBUe-f9k`bD35)`X{4DI!aGn=;JB9 zIr+L;%$!OK4VpWq`#W3m*zjzFNCuT$*(>=>=}LgSxbgKq0O-bc@bdLEpy}rfq*HXD zNxbm^YH2uW4=mHtWV)Kb9A#{qdT*Ps6*k3g2xv*`McAO@ya$~utJErepC<-AV5&69Yhb|rQx9E=nmT|l8DDldBJ1f zM%kY%PfScsS}JCKg5?ZZ%m-FH>{q5LMkyJv&D~Az%Ulql)|R$i^ASKWA-@~EK8EE; z6sz6=xq zCn`V}jTLq*G7rliSbtOfBhbyj!vuQ{uP`*Y9y3|3^aoL5VmT-kf{?s(-D?YhDR{i< z&dlZYS@J?LHC-jjh!LF)w`LlacA@Lt#GvQnmuKJ82PvsQC2yn@98x=+^NuSAt)78) ztt#DGCM>x${P#~QN&_uj)?J=}f(Qh_KmsrTAPsy{c9RnZu^<*-RXn6z*0a`!(7uZ5 zv4yQ!&A9%W{rUN>kI$X%+f&wO+~@Wuw7zP-zcOvMmSId0K{{M*Ld32Z@X{lcy-HC| zLLs%T`aWu3cai63KA7-R48&)AtvC~2)&8i7tEOj8&YYf-8m8UMsjp~5VPHFd`cDax zEE~(pf#&k`&#kJrF?U9C4GtBnf{9%fff=N-%sQ;WBo~D$*e=98`2j}1BaPDE5(gkL z*|ye2ina}+nnfyUTSs%#BhF|x$d5Z&wT&rwH8ns)!lL=8F}${{Ci~^#B;g`Qn!o;|AemR-K@pdI#x?T(E!U4T1V3nti#d7u)-sw71;IpMHiL`i6ftqH+8 zifn-nv>4=4c^;f|neF@E%8_Hj9Lch)C{UF_0oXVw)QO5js(t}usES8K!PK4!zrqDh z2w7m%+?ri7&yG06Q(Ezi568c}7OJw6#2}U3f?YUNd=#$m@`1hpDn|jJXq%8`B8ud$ zGlCkR;D+fAZP2q4ODO>Y^C@^rhz=tUkPWi(Y+4s4hU_M@dH#>Mdp_BJV z$2-p$y)#lmubCXDOw-L$2&y#@Y8*MP(W8!dp=Z(vt-RcOg(vV3J$SHw+abb9;?d3< zK-f735(-cd$*lC1tUl3rGt$&%Nwrw^cE#k&bXt#4Q#92k?Z(l;{kgn@V%Bj3$9Hp7 zbSJ4mMCADS5=j-&rziCKn~4R_5`K#-J{VX|E$Tb~^j0+|g^Wz6=EY7~8Ny<+0f zP*8HdTL8OT3T?04Yks6aR;w^#)PBBUpJ!!l!B_efqKT?)*{)IT47VhgQneAw7@1Nw zM$RP$PbDOlJ@ThPm^t8qIdnFpBAe&Jn-vfOz@lKMlmA|b4!7YE93rUn#p<9;#@19` zj@78#uRQx>3E9ydJ78c7Ypw5*1?6-qYGyofJevlI8s4+gh|nZS&$`1s`1?}%5we2@ zA3aFuf;?{gTrYuLZ1r-TazsR1)=P(QbkAgqDNWkLN%DahtZ!AtW4^mv&v9-*DlaG! zlZr#5`BI|Y1^Rnb^LxXX5an!cF!D1bc9A9O=NaGnmSMEo#-`^s%b@r zk<-E#`xR+mcX+F@J0m-zv(`OR82}}drbZZnY^9&sf zsOma6K=@kNxH?wzukz2cjt^f`&9<|(5i6?Jc3VNdx3wj$`P&?yl&cB@1Fo=k_yHA5 zk`ygps@K?DnyzyLiAkXxC7R@6NjhVfaEX_>v22l)%9{OC17U64V+4$$Am}M{meNDP ztg1Xb&k6VKCjQYM`L6TKIl9R_YQL@9uP-I9x#KTyONEH0#FXNLm^RM(%1V_Udgyy% z$(DQA86Lez#C6a?2a|mdcTb)Z*oec8qMXF~1viuD*KvWi+`4nKi8+Ej%|f->R5XfZ3Fg$j%kSa+&iR|qbU^0}dK5Y% z0}7Vl=NO`Z)^u0Qwz0^|8z+OevXMh1M|X6e$WVAJoHz<}s8k*l!X1}IL zStF{F$_}UpKu1oh$DlO=$kXv@(~HUimtib~E+ChoctNPp$qL~H7)qEB&C1>Lr#)=z z@V?(tO{k=|r9;+3h)ChH4wYNE!+Y(`;_QBSAV(+B*cdRPXOSuD*-08h01%}b z31du5x-NPfL5k{!v%qX`ag*o)HB}zS{Qxc^(^W6_dW?-J@Co5$vuX37&FM}bmaUr) zIZHI*WG4o#G)eL#@`@y3D0uGo;sO5M=+AvRdaE5^f~?A$lniS7RQ*wqnNsll-x<%w zX96^72{;G6PUadE!wl+3U2?l+n%#e!5SJC3Qi1aVC8pDJgeXd{FSYJ3med*EpUlc9 zGtcX9`M#R@@n-0Hr)Qom94zEv$$9AVW*T7e(Jsx>8yf|XF4$`KHNoX<3nW1oJ!^Eg z=>1oI(noMU%9kTFny+ThlcY*`T3EZxnY#PGq;`N0=KRsN`Kh+^7kkf7t>tqqU&HxY z^FBlTtzihL<)#_S^jc3nwsy|Yv+ij)tJsb`Yy~Q*&HCvSR?0!~ar?IJt~B=GS6nKF z*UquN|7N`quV3lRo>ZA4Bp_y!1NxRVh|Gy~0MxSBL+dO~fdsfIr;1bql=t^-4Ek-a z4c%G*mxRR=YSODskj$g<3QgL*@e*2o6U)?X9w*OI?8F?-9vB}4f z+wD5u53oe_T2Mf{mXD9w`)8w0_s|s0A!mf;muFMFvdGQMT(Ae_l4j6G=8B0VyvJwh zb5s+CrfFafySd#}Wp=xEjM%72n9$k+%rIZ?!YR%_PJt>s*`8reIdOL9`JA76=;2O>h#1QH%y4OC zw>Z--%eS-m3J%5a}qjEvD$VF76Rtkt$CiX|skD#8>E8Ycw>`_#RS{h=NH7m@K>+ zEX^PS(UaZD$|O{#KqHh@I~S(4oWER4>LIHpRUdRUc6w+BeJ-V3^7e=wTHs;Z*yW5RDXRg4h2_Qazgc zEGhs`7!9OJaGOn$xugrxZZ72>U<9SzBQSVRyq*-smT$I%hV1*#xt856;L33l2`^aY0^n>S7*!d7AhrxJC}Kfa9yKE^ZqH`es;b-6Fe&b#CG> zhkdY~b=LH0uVdo0wzF@YL)UrQcD=FWJY8^29b&>ixy(P+(?1LOpCkI_FlFx+4XP{Z zjuqE6wJr|kdKK6CYv)-=6WMZN7#m->ifDgqntl!<$`9;=0%jGPaw1Si(g94E7qGbg zY`RRs#5DWbOypoZ1cC87^2R~Iaez<+qL^a6H_r5afZ{ASPQE1a9d9oe#atq1nQn(5 z>!IC;1;vN?{-e(=oQ}kCZ`(KD2p&4*Dn+j^9<#OUm6ga;o>v|rx!O|34^+1`%baPXWG0C?}2S`*UpP=JXU8XW}_V4#S! z&WUZ^oLb1{)(qZ~vx3;^#?p4y#Y2xAsOa-5P`h7G1*`3e2lLNqHKDPyw9yNkYyM&+$RMzdpi-T-2EPQz=*;xp`2!1w1f#!M1Ml_(2Mr~!dQE4v^niXx|bk5Y_x zQ)F{?!$EyLgzzmlE7 z5M6Z@M*FT}Tb(z23zk>L{W|_!;_Y#<>H0UDuQ$UVZzt#K#3^UI4yK-TjoP|(Jj>#2 zi*{LtQ~Vj*pVd6QQ@he74Kjxl>|kLQ-YP+a25N0GOxw8t1!@s;z*23ySz7TNm2GPH z2ulJByMM_mn)}2!0CZ>|^3n zyCq&b$vK^c7j$}5kR$&Yp9VtMn!4JJK3WS~D^#R$5srhw0+p#>2-M`?kyMeS6Ml>h(=j2nZ?=C2N^n*+2v%XOuw6 z0`;}MS<%heLD>8XNOaz>+nd)2P}ZXnr&`HnC7ib8x9^vU&3UmiHdx!alW1N)Th=8ymi@DdH}wi*Ue`*We61@QHR!=6pFsIUy8tC9sOJf1+CTd^x&37~H?r zaHG3I&ggV`C-Izkqc+dz574z4x8f%^(*-$Eu>mjT!|U#fdQA;#)Dj?yZ;%)}F&H~@ zdmN$7`EPcDSB+-_cU5os^l<1<6^-=kKozs(qp;2^u~ST|0~MMRA*JPufLQIjilx97 zFqwm^izd8ZXuZ>X1?N`04x-x;TAuZ%qxSK}5??RSVL|IcV@X~memtL+1LsN4eKz#v z7L~a4RqC!>g(Fr_9;8YM(A``$I@&UZ30Uzedd)2v?bCyu6>I@{Ad4DJ?4`>uRY_BlLj%fJ|HVjX<*rkkLWyv<~{0M4FqTr~Txyupp3$-Oi|M0Cl)xZ* zo)8qY**d0twRGw!U{Rd{idiJ~ZlTwtGk9Q~mY18KVo9i>K`xg-rk{K?9;0H~1x%@WEu*w&| zYiRCE=Nii7xmBU6Yx)Wz$1O1hyTe-89uZRyr)ZXROf=hqC2(SDvEw+)6?2jx;B>y8TN2)e5MYI1}mZSmzKN%?>rf?TauJdEUa3JyNw>7qC;E)t zM+E5el2&oWPE2+fDdnu6SN_mO6_RYj*B{!nIu`|~CaEDfH5vSQ3Y-dE(}3x;#-N52 z%nyhzq_f~%KnUg3@`;bF^IQ)1Ipavgq!E&=UWG!PJCY~TSz|#6KJ5I50s~eU+=T6% zemU=1_e1mj@>buUKhwJPdaL!C(^hN{JNqu*oUUnM%2Kl@N6cwPmZPtn#jn@W!$nwq zkVl#;g9w8iZei4~c=^#$-|_{ivPe4ZLB131xUrKFHVi|w{T4NOJm@t=m$OE)AA?ux z0*kp~pQYb#8eLEzkd6)dD@|kFSL14Y9SKv~of3%RxKnYw#8yq@C{7fT zo}Q>!+$Y#RhsKFtkOl|XhO;F*IYFRm^|&~7y2dScnz>b@*)OIG4N#xiepEiC(>k7- z_Qo5d8wIUH$5Uo^x<@ClN)A8}OIRR8WB|(phz>0|?*!1n0uNmvIu< zX888>c^rm?2i0jefOA-l?FNtxx3z^|V7($-oEMo26q|&*iYT!0dEQHe5yBpX22>f9 zS?k9-w%m@=gS(FS*sbQ?dmlFl%PXaLS-9~VOwx@Bqk$ILRL7iIK3wcqMiQ=($6jX- z5h>UG+x(c*7kOH(sC=jB7ZoUlh=hL$o^%DEOPujyZoBk-+Ko>1hHwYauf7*D3nMT0 z?+<8l{r1Zz{7dc+-sl#pnE@_8S8Lt1E4z93*5m7tJb8~G_6oi zlBPy6Y5)lJBx&rOv0`Yw?J>gU1Yx;k=>n7&3M)_?(8Id)+cUdUpiosOAcT_E2*>|L zJR?#XBBcMyaf8_}x|@6D`qX%yH|Eu>+|8kXTYh|a>+?4I&G#Pfr|*{^ZCx$9-ajq- z*1hj}?%w&#=1l$iX^yxRQM?z9ak-BHuFzavBBea0aPw0T?AWpK9Ipk|5XIT{6l%25B9RolB%HkOnb) zVGLywh_#|vae&rPHk+t~1w*EXK~C=>vNvL{1qF2l%;K0Xj`{qcNH3Gf z-fhQl2A~EU7A)RSmgsbcw^c*}K+U><4Ko)uR60W`EcQP4xzBeEZX49Q4(X*UPuk8>IY@c*pxX!>i z+{YF@f98C0T>3Kog1SwM4)-0-haMW^n9c>Tfe@sTdb=AaU|p|1D9|( zuBm{OcZ*C^hl@1K7Qt06m=9%0bK`3ks2$lwojs&j&r)JVisng28`{A`__~9?W(~P| z=IH&Tq|b>0uZFi5TStB4nxRQY)Gm`&B>7KaV9PvQ1bWnQS*S&O$<&BqD2l8GXC@*L z$G33F?xBMtGFXE5rbqS39uW~vG8NQu8Eu*(+^c?uvvlTwFeO3n(PoRn+XfwKitGG3 z`5q}+{+uv7_~6;?-A&;|Zuc~u<|7A8h6xV~9LH9{p0j#%l=3s`Pozg9akzvcaPwJ`7Ayt3{e4I<4i1H|)B2&CUDMv~c zi=d89*y^J;b_i5U0%hp{l?cn+cIkr=(9k^xn+i*-MdDe+EVER@gkAW4R^}v)Z3tuY zvUQOWER@F_BC;Uo3l}dc1diFX4$J!fYqJg?yB|x5Mujybo%fEM0=vww60B_~>guYw z6$XBg^~nrMhNAK^S9gMO95FK^C*%l-2Agcd(gpT;ct2|D^EtZv*1Ui2xT~FTJ()94 zBYFaQ)^?_a98Ns*sZoZ>P=2bJCR0SIhBQ@MC0T`K6`UxJg2Y?E^0xSDhf)kNsqSU3 zG0sW>?6gp=Kw0vr_x+wm;kDN|P-jJAZOa30fhX+^ylJd3V=Z>dxb`?tD4!ut2q_XA zxs&2~%&4*@k=i)AxHH^|K$G5kIF8lO8kA#_{x$tti~Z~tJT=7dCD4B9HZcm4B-%rH zRO?`Hid}O=^2~f*Wz24`nStRU)PMZ5fF)aVK-xM ztsH8@HVcMeJSzJ}fs$fZ7LHbRE)Aqc^y3`1*990KY<*loTJLWbqGDw@U> zCU5jO!yIOsKJWVZzD6DsEpu&-0g%+RYghEx(tBF8V6!0;(>l-05<73E92a&yswsP_zhU?2Ef z{SuYv!l=SJyEW+U9g98AtR<=14E zJglWoYSK`e$GCP6Uq1I{!fT0^>EAFz+MK5fa% zXU?a$BA?;-_|vBQs9jHb=4OGfPx0YI)sx|p*)ozab-bIIH(fW*(Ad@yVl8C^452-x zDOlwqASyV;9UehNqcDu9&{X3XYBhFqZ6{U2()z)9LSY+{3XtV;?wEyzT4a{z0YqCG z%TtDoGqgn^kl5m2LC}HXps?TYlpZ2-(52GV`-*Vq^OadvG7lXA9+S>WP_bwoX%sl+ zVSiL*iHIi9vQupj^9m*5(z(A#5uGj0dV%^g3!P)Fv z$|fU<_S2eUk#S7lcjGFR#+~?l>%d}P1(ghTdL3?es3`E*;eny|hNuE7xkt*)BD<70 zUa7&X6F9^*B1_4<_fEiIABFOoNTzhvsV&kHo-&Q)W(xzmkhlRb zNEL!DqHJ_!N8yLI)B#J0y}%Eg_QjqB5ck=_$HJ_ZCzu8uxX7Pk6&iG2yElst)VF5r zZ7OGI2luoQPVN3$p*JG(2Oae~OkrImQOaoUbUq5_>vNw^=gs}MJ70gMb2IOHJLY#X z=gpO^SOsGJt2*q;1(CMn7DsI7}Y}2QwN8={>wB9SK*QyxM~2)dsp<$7F&6DO*bPzDwnta1Zb4^iX|AYa5?5AQd)c zxeb(#?!z3N#`Qp}(`s@Ud0Vrt;VY1Pc%NI#ufp8v@RXrpGGxo1(C%+phn0Pi^8e@| zxqX#rZ4ydZ888Q~X21=LfExe-1pz5gtV&#&eC74XCT8f=4-;#G`-<-~t<#fut~#ID z8odwP>iFaYL4LnOM3dWcdF4BZ5ZDHoflnnc6jq331|_#y-1PQlsr1^z4{}i9V;H8S zjaR|5r3@)}(0*CqSq*S^+)Y)gl2ZxBPK=QljKpAUVlKtwT7z#Q8w#^hko>w+tv$|# z$AKf!8l|;-((Yr5PhZt#8m5h1EWBo1Y?oHF!Wn2N#VvwM@y$xD-*vdDp6S-e$4D1`Cvtqk6lI? zL4sI-xrmAgQ8d`3U1rc{OBz5~GldKc)FSd}h(%Mc zNea%2Bq9YsDwStEi5i_NuN%0M=J$tqiRH!i$K5h#2PV4&P->)X=yn`zsrGy3pe<3b ztvxtw1vwx!w^gc>;rc8OFymZaFR%OOj-XMe3Ye8jr7~Kemx?Ql?pnL0oyUigD$3J> zs5P*MWTrnrr2|48TJDhg_%!r-O#SjC4VV#Aup=uTw%K)V%Wq-XiJ=%wW_^-bS!56P zl+^m-SkVu53Avc_gE7Z;H3_Iee%^Dp+>V_wn^bQf{8 z1}}DgBGr$l{fKa|c8E0!ppbGs^^Ae>IXG%`T4N*^l&9lU#At(pVu9$MDt&K*Zs1x~ z?#?kF*PSQ?Y|gen)%(>G`0#lCiN?Hb&leZZH>2KPI?rxTy-7*ues|9yw6%Q{#iJ{eb!&4E#TPb&}fL3uw!G&T}iLFJ{-9}ciE2eE{ zeMhdM%pe7yX>Q^j9YTb-qftP642)#a8KughAOhwvG&(49E=CpHn|0MJWjm_#wfVW( zaJ90r8F-)jfuHe}q|{kBr|&+yg>>iP+-pb>#N6p+%=!JVDXKoRPC`i;N|4VXx;Y< z5L~i;pRCC}I4ZeNU|HHnlm7mk^t1j0T$(HkLpTF51;*k8AFr>G%Kbtux^p0`*~#C} z(tcL?7TzB{nAJlJ;i36WoeTX(PFMJZ$E9lE3{bygm~8}TxL`+A8y9FtdBKTusz5(i zfI37+NV4J#qGFBk>zID-*21wmh<$eYymfn?-G28klKl&3cbmbYW|~ppLoU6e>V4}R zp#erPQkIf6Hi1yeX$kw}055}`G^|<7+E#I0hDy5%v~lCciZBuKh6-xC8u8kp=~V?f z7el3Ls&BSn+O<3n1#h- zUVzGUyTDb)5r};?5Y6N(LV04h=oLPT6-(rM1tKa`3SMx1^i(bN=JiRh$+gzBKMuFU zVPNy=JSCqzk|YSRWm@+UPqz4#*oFvb=M1u#Z+pOm6ZqNE z&LKIl-*dD5Rgx$XDk@?G9zrNRc^fTdoNVp-da(D6zuxQq^mV`g`8yjv6r{ij4)GZs zOMJdM!zzfVP7qWWA0o;B5Ufji%x4m88@&!MjCRwKd!;kArV$jj;H zm!JK(@w)Ey`={yc7vkzRX~+N9Kb`p5Z!>-oz6j1WwfxB7W-1x05?R?x>&{ge-9WWk zel62T`IKfaLHaOuDP&w;Van>(?U$>IufAayS7upNKJ1QGi&B^yh}PDixMuKjUa-W5 z(b1dhy9)WuqdPW3z!$qbUf*pGU*`Ah+2dVvL%klT4XWx@HUzsyoWS!PjyUjbxaF-Q zgQ64zx44A}`=z@Ny^OdZ4q-qJ1x`RKxdV>N(isEy2I~RXY_5-3gg|Dd_xe%XqNiur zxtZ$A0!PXgg)`Fw01ZZ#lwYsp^_9u#YxC(TKiHV(hz(ZMOXm>!afK_)pFSdjEpp6@^yrf+I!OzUEpYCYA zelnvp*zRsmc!j@M%1S?qjH5|bK;!{M)&lp01r?+P1TUtZOj`GNnm%ES6nQHHtVBdL z@$K9!Wtyqpt?3dgFoc)23GOq)AB<% z4DqsX!)Au`8wH5-T%iYhx%ARnbhTF)yR6CzlG*IYgpce&JE0vE2$&9)RiNW0aqsJt zh$Kh%PY^Ln2$9h=7lZ#xVP`S653I|u`RX7~Zm;~shO!>{hvZ5_<6KG-N$~bV(w(-hE?)|c~|&&bsb8sb7Fi^fI57H9_m76Q9V=K$ap&y01C=*4qi+?);-(X9Xp z7usn7cW0p**Pc)a=N!GV+dth>RS;C?Q8p_krALnyP!*Z1{mX@+RkD1P^m;j#Fr_}nOraHvzFKWH0iaK;^E$5b#Sc@Mxg9t zd$`IM@~fmAqc|?l`^=|OHYQcH+tQ0^@@ptYy!WgQ^cM_={u_&~2Yy9c*# z915-+no8?;rAORvHp#|OVhBOtaM3<&yL7ji&v?^UukI*}OFDZhKrY7*Wz$J;2+C9K zBA}IdBQ^t5pEW;D-<9`oIQeivf}HZp{Yjd_9i;Zd|6C_fIs7l*#gOTFkz3a%l3!t&-ka$BG0cVukAfVEQE51D ztyDy~lr^)(&D@$1tFg>EzSM&|0RiN2fXz8@?7cf%lMcW(!IHr_BzhW`Ma)K+Z871 zm%X0ZN?b9dKvCU`1DvV*^@qFXtr)9riHr7x#&Bvoe!t7U`Kp<3-$$5Nc}mfFF2b-8 z8t?l9m;tTalC~x|yue5Qp5`Wbf_I3n8E<(tNOg$>ArB=b2}HAq8H{H}OV19K-{VIZ zrJ@p>&E8x4loalbToHMJ3tnM@+v~62>(QYpjjR`Z->qLGSp>^G1g})kA4rb02RCSu zkq9B9&jLPTNCNWW-{)=9g=7ObH)kj|9HESYrgZ)goh zL0C^Y&ku*dY#2J1P-HAfaac_B+5O`5K#Pn;_@Q2I4lQ(q1}LlB?SVBuJYjzj2Z3C9 za!fMSexvC~dI-=X;%%1Bw@^NV_H&rn!~ipVfCTeoiG@l$OSmCaXO_#-ceblXGi;fK zfzn!f0IC4bYmqb9G$=FxMw8OBRL zbP8qSf(J;Fd+F3SbcG>q>sL_GkUO@Ioiq>MiD~o*JyEau5v7fXn3ZT8LIwcOQmO;g zuDu_6;Nf?+JS@{#D@uE1V+#mexY?FC)P}#ru*+k($*i425`~vT11IpRLU~_P%1nfn z6_;;@emwAVY5)C;9!;@D6tSiY03e+gJCh!*tl>OOmK-;~?U7o< zQ9=8Eb_UiQAsDWV%4pRo>=GV=pFfRz4t*m5)1~kaSU7L+Y z_-5o`?CqbD{tSNGF@IuRylh`ote#w1NP*_m;#oe%_YIsNJFM%GyJZ+LiH(pX1XQ3c zv2^8Dbk4ZukKe?p-i$>j6{8$!tpsq6elVzJe}6?93J5tfbH)R(7jNSkR>Ex`kzxjH z9FB4Uxr-VU1f%E}HM(i(V)*bxcORxkfCijR6L{fo7%i`Lugla!AKgKQ5I9VvePkb` ze{$krS=e4BVOq3S4+B$f!-~%M<+Ev{%@lw*F1Ks_9OglEm-JFpY{(E(v5kk?3(Njn z+kzcgg(dt7V4wpXG9{BY9?IUgx;4OqN;QiXY;XSYeA@N@wEynO$*ti>&616XRkib^ zb0Zgpqx)o9<{|eG+H4Z*ErMQZ41?EjK>*^JNh125UB9BbrV{@Ma}}>b3a{o>D(gcp z#$MEH1&XbQuDnuin9^1}`AfmY721Z;yn4-~amarn{$Ee@59%+$Cv2CDkv5nW?6pU! zGIqw9x-#_{)yO|#zsx&rgFG?NEN)y@>2iH9mel}beIgczXgmbvI~)X`)-K=D2I5Akr(Lgqp&(I5fdayPU3!Qs?@tzz?? zqz_YuWx|mzK8+7}xfRU7yW@7Sx1r&OM$OaNkNeg#@$HFz+WpYUZazqh557Nv+>~`% z_aLcZ+x8HPv22l2Ag;}bg2=J(e8mJOhN%kC=Uix|| zF$M3CT*nJ7(t`J@hdg@a@c(=rItmT`LpVxKq=&u3l=zV3WDiV6sgF#)M(VOTuw+|! zLs4UWtwiV*eC4STufz&JPwU+}4^*It5K!F{#l%J?jcumf1M-q{mk{I&vLI?Rq%MOo zQ4CpPuAgJBtQM$NK=e9dfytY&(w~U`qWBLse5dhn;!c-Lt|UZEB~y2IGmA@$h*cfQ zER8K!#pgnIJ*^V_4Y%RiT^??P@$sp)^cn44WxUI_r5X`%fO9rIQgLYSl*Q$a3O5hU z1hKX;H?V%eYUK7h8t~7NYdEG(+%h#zmiD7bIcXBAk9 zZ->k?=KlIT{u=Xtdq}X;zho+V|NO%d|Nr4jo_fDOfKoB(o%a|SKp0w*fcXhkfvSue zqrBLJL)8B&uqeL+5EUxq@qqfwLr_3T zQk|jEfQU#{G^hj};Uw$WAjTYlz?10oD|v#m)8;b8qZ901r98D6hAt!=7rD%vS6*26 z_Vy1x8buI>?S=S0dI*T3QNwrXk8pBmW$_EI)B`@vnOCh#l0=G#C?NEB+f@pv((+Pi zDFvG~CRc#XCAicFYk(saC|Zsmgqg(qq%nhrhaK4FA^t97ugm2}`6fI)U(aa%#=-Y| z@W9@^b#Lok*X-o>=mZ~fHHLnNn)?DF=_$NZgr*~-2&bB3MmIpo|BS^;sS7>9QqVrFGXX-^!lO9b`x1S|r;48TUbXwH;6Hndp;1r(GdR{#MI3+!HCi%jAMI8eB= z>ByL-20PG9e#plR-T>n=tMQcXb6C=?;pI8kSo&xq9)pwV5|-}s8p^brl>%XqzLlAtbW2nacVORuD0OL zy}Ve%rT+SWU%0%!ckjqecB6Aj@4M)~C>n-IkXIH`%q2tV5=E=#)lw%ShW0K;#Bg}5 zt6jr^Hy4yJ@V*ocOj=vv(p7Y|5?T#lZ2BW;%c0u-qn~htrdYWJi?G_0(q@htUnym! zT*F({D2cZP--t|MKm^4)qAEy+flF2O>X7TjtEsWSbfctf<@T~jb~irlSWY{rC6MoL=w;VUoPR$VK^Y937@a_ETy zVSv~+CSZ(~SG~mkK<3yS!+~yY&@?KJGi6;ZdENY=%?|7KjAuC^Xcq97dfN9xdQd&j z37pHkm8_Dh3#-SO0ti8g9!Ta?9~I?+NTA=$nTTjGdn=+EW!(%Jp7moSKs|2#GTBDR zP!S2jMs(D85dFZLkp!I?8z2roIOv_$*UTqi<=ynGy2t1K<>++X$!v{P1DXnR5ugxq z*C3m!wFy^7Dg*`;WT z4hX4#4I46I|;y(2fl_TKI;17suFjksO{S8*D}Yin{I7SLqU)VHx|w#~Rn**J(<{IYeF z`LOlESNaup4|@(0l3CtEfS5Am8vd(i9X>dOC(T5}&{uXmsfI>~yqc7XYHxuWK);d6 zyeVWT#&kVsf~F^eK%E+R-JFjN9(1iCwn+P@|64OWV(Gec@nbZkO|^*>e&whE$&6%) zPqIu?v6djM{gN*&ZfmGsn2m`j+6?^SP3k`)**VeuA3vkLnX3#pCWn7ILtOFn5>rK2 zK7b3!ofC!j1HZ^OzdMckbj}BR_Wn!fUQ4FPGqKJ^B7h1A1rohf!?3LWyk7n$Kj$ED zqX4JADsZ+6XA98s5@0V~tI7h$*ffHiEvL#e_0}tV`C!>4J>ebC;tXK5w_HF4^~nG< zAA>xq2OBqT+!zEjx^?SdbqCG}F${yL#BOP7U3D+m`Iqe-^n7H)Z`|-Eer4~Y%u{Ge z*02^$0pvHWxiH1!y^1nc1wdWF2hK7Ii6@MN7giY^e@aUsD0v!?wCcS*aoy%sRXp&j zAeSFtIfW7qHG67%#GLMutrpGTi0(q*>k5pdS7VITEsNMnhnhmzvR78Wm9sQrlnj{QnC<2nx4?GqCXR+ zL%XGJC&z?_(%d*TRFbtHld%M~)U!*dORb@f-uw<1-$AIG8fKR?Te3@X*R+&UOZC?7 z8-h0=2XqWF+kBU_@L7k0f^8jQ;M#Kx1PDOp1>1e<-gVcuG3W(E1QM_lz7h)5(0ZEP zKbL9_=dZ{Yqqhg?md5Q$iIDco2$;)Br#X1a#&b zM~QfGAs8ENe2+3GwtewT`TOnuWbe5&efH?hgkt}@M-d>1UO?C(7#N~me-tQIyTO{`XIwR)27O)%!3~J$@+)D$ob@4;g>5M<<8}F@ChfK_kB=Kh zSmfc8@PHkfS9IMOZ8i$ZCHFi*6+yU&#Za)45dp5EA}OZQPd=UQ>&QQwX_`M6YlMC?9zLK-M1z4>LxL%IKZ^4U$L7~&`$c1yMtVcnxYq_Q|1@1heQwd~ zw|)r8+({lgOIUobjA$`M0D}Qkv4L85gsWQM1wC+GvEU^xzS9(^Z)LM!Lid zW8Fs)<0Me|#1oRTf5D=9^IE3q>f!dVdtB$3PSIK7ERlJP@(|%s+wH}>-P*bB?$e&$ zBvb8H$nI?k6?3rH2zgAC-r`9YL`5WuSJ3k(dUPO<;IfT4Yx%+DW{@sPX$5K?a znyJh41YnB?05&@SD7C2c;SiBCNSB&q{D@VVhYtu{kr;G;X2Un@UrwxFIp#7T ztEybt08JDSV+Yn_^(vk7EEnew?IRoKgF`0aq+Ka6;*I^Wsw_?M#kCs{BG_;z7?<6H z*1_rr577fm$u_uCgKTWU4C?={aG1r>zMhcv=NpDvLek3bA|Qw&3J~t^taK=WgjVnP z|6)5HoyVpmr2sDml%i~O)O)#;bu8I6_H72u>>Y18toef03%&#~>=xZvn*7XNiw@US zv&AoV)g(9rrJy2C)@qlob)A4%Nm%~(-XPr(KhRvP01PUqT;K({VJ_ME@F+g~wyK;k z-EvxuFWLl|BJFtBX^ySu%@^U+#><(FxRBEF2hYIEp7<(AMM_u*4SxxrLntEx*=Ys2 z734~#uGK=4s#1b6Ns^S5RT8999a(#a>%SbXP1B;ee{`DHhcf4RBL!7VxR*`4FIxC4FFqL5;#Nwxp~j?7$T~@)Fe>GU_viO(<$~a z{WE(r!apMXF#YcA<(TkDa2a4$P}^LUR}R!fEbuDCG92ZdnLF`=8(QSZDYhA&VcxSh z?k#YYJNwd=9-5>30usvp{xN%qN~WADY@OKf1Ti)pVsb(|c3x7LA=6mihY6OV{VFkM z{B~`nlW9e@W5XLHbz1p-$_kfWBSr3*lEqbsxKM!7Y9y|eDi&jGZ5?Smd}%rSv}k@= zq`O!wp0pepXzs<;7BzBW9(X~cEQ!~HNc7205WqoJ=^ny-Pf;r?tns9!Dz3sN`(fCoX!q5nBGxYu#AW z*farwDA`Dsas|+EL5zVa32{aH`56u_6_u4M*|2{D)$7dp*u6t{;A~M&)A@9!pHDw8 z=Uop`tw}~}*rN%*-eBF8(bz*PC72~Z0wq8K&X~#u)w05gwhOoS!x9@(cu%(C&Tr&* zHN3Hb2WMT+-gCG>YKbpD%xPAw8OI%}9HDRquzP8R`186Gw{g2E8^GW``0;#}V45na zlIOWM@Yrrf6b4 zR|ebL8(hU?#}@1w%WHNjR@!jLBss)&HCYA8^ucrhO>i>Hkz%9dY#iM(_;8vOMmfkc zvs#_tU=4>Gy*oa@GFheDk2ftr>d=53TURVod~J=JD_?qFfe2tm!hyEeo+g>f8FyIOCDlAwxXoptDtO|(aa0O+tzKT1z>=*nZ5mljd ztI&(CQ?v`RS(cy*+QU#4iH)cz2uO$aj%L+f%KIdta6q|AL;FzaGl|u5=V(Bo@!{08 z-^UOm@BwfUgf+~;gz6o%AlS5f}K04FUXRBu$ofS(k3^?;b*ni(MK1Nc}(`+DD0??TZWF zUII*;T`7R^cR!4naUv?{Ag`^6u=IRyf4%xXp&RA`Y>dn6>AQmZTYQRP4rxE_%AX2< z`5o5Hik6T!TNEicX+a~1LPf%6)2u3+5S>>fXXVaD0Qz*8675(UAHtS)D1<^_0;31H zX>NP+LI=Obp{-9qtoedZl7x~R`HJGD@?G)QHs)V1Ywy|nnD3t5Jif7n*Nb};-7%WK zl7J!9qRoq28P9wb<2qUDMFgCk+5Pif@jM?b(GhuGJoB?;sP>RaKya1n?%lS`l86

    >jZ)9JU7_98pEK9ZKc$Lnsd(pah;icYp+9dUm4HYU}oa%AU(o-oty$kwIeX%sIP< zv5ek9&nR_@?rOc%`Qj(hrP0d(5gc%;=Fmi)c4T_iJUhhLHHebVVo@j;dpHN1WF7|vGDZ71a@p?kmX{y6q5jpk=-A=8l11Sv~3OCw!g7Z&eoSIp!u zr(eiC&!^vm5&p^+1Y$4Yy#nJ9G-#V-&~?dIP(Y0ByceFc zHfQ6$*E#)an{bP=aY{q}aXfFFu0Q{}_TRRxUo2l}P5O$Q=zpr;9t)o1lG3jrHVja3 z1qdYe5@$wdNuoZ1Q8#?UZzL_tgP%Q5i#_NDvmA3M2WdQ`*ta&{882t;E|Fea3()ld7aD+47dZN5B9qKZz%Mrw4!gDA!-rTQEu#vmpw+VYD3DnAhw3hVK^p2@Riy7i`l~cD%9hOKs^%S^SOX92$qw zLF4d~v1%T@H{wkKFS+zmtmltY+n0LpFOKTas3-C3FGOE&hPbv*vsF>^|D`uu zS6(*g5xi0gK!U^Kcv={Svu+N(Gzm8z0Q~%%v!M%|89{mioQ(Xg@_VitiUpIMSZU~D zK!Djdf;bz1G(K^jWta->XF;C)N1Ho2{nh%msl%XR#zoUIUVk z&bO>X<jb|gnCNQl)^=wkqym2=vX&JPEGr`Cm+Qn@y}&Q;1P-g~AwUnq$K{0B~x z8s%^Yr<L#GsQUy<-qMK(2C`tF-s+gOdTy6#n9UgRS#$jd2Zi`5_^w5y|C2;3oX} zSNP+UJ!I$RDM8|~szcZ|yPUH%?YeW?xK+~F9*&zi<&cm}rCI5QjvruO?A~lp#+}8L z?ZmjuWk1f*b?L>Gp+g-Az*6)SW3BzdsNH<{=VC8rE_Hw7lEH!4j>pB(v|r}K};b%obW>Ux^?8!0{Q^6g^n z=7;mME$HK?c_g29;D^d6=Q9O}Vxvv^0bll_h8(at$vKC9uq7Jl9yMlbTcs>9vCwH0 zEeHSs{lyP(Xv{P7G`1bW94CvGb`IujTBLcuw`&qZreNAFEfcw&rqZa-u&#kzSqL@W zUS!a$n1N;tnr1QPth3oJODE0t*`69`mV^NIY@b!s6;1&)lC)yaQ6ly7YcU{agNv##()PYepY6&j z6A(Q30>F9zJ3z$09-*Xm-m9m|KwT2pCQ&&GfWTE8!UP~>nniEs!$T+TPKH9`Vm|Ly z&_@~(-37L8V$_^AETlkJq&&u{Y|jgdl2l2DvR$mm;L%=is0+0U74^*zwylpl6Th7H zqGk6Cr@h1QprV}^iKoRFoC>Y9F|&+8C;VJXD3?f~_g5)VeE!`00Ha@QB3gcO?9-{2 zx?j=eI0Gzs!(N2``7cIR(ISe%i7c?K1w(go#WobRrZz@ot%Upq|KPQ*k=%##5buB1 z;&aRCouB{9ZS+kYz0w-y%qbre*Ry5Rh1oiEL6-2Yku1?Y#j?ca`x-i$goa9!gj=(n z6OP>aw-`RLW&kR4fLH4^q|yl)!8RlKVJT za}%*1m(=Z`@2bJWL#4`d1qIhV0Ul(prOWqji5b_NNc98H^9T<%V||bEkl9QLVF@p` z{xoL)dI)O>P-PCrbd1Dwz?hC{S_w;<_0{0=cI($*m}X~0et8kktEkxU`Y)qD-$Xud@f^G6 zI@z1_?PWjCJ@;z4F?Nf^6Q57hW@M(w{)YIL=v8B$;%y<3H<9i2ZbSy}^NmaqH=1`x z2^CrvQ?YMC)N&Lc00x9YZ#&qwNTp?}-3xm3|xHQ0$ zysh)hL{Gyj@%AzpQ+&*?rZ}VsT@yk8vM*C-@~yQP6E@?xG?C1gkw74v&t6iMxkuFV zPvaqYhP#QI-6seLd}$Ab6X77!82QX?5r0C*g;_rwRi0l>2lxuoG14}G=@|)U_gF3? z$AF=OtRcV$Fy}X(`4dfX*@Z<1ix!=K@3|Jmi&zj63}Q3lK@KO_8DR$Mkhlv9xPR+}2OTO^55mbG zkX^sJuAa8*$m!U%`S~UyifC5@%+Q~Wt+I>?ow_;QK(2!KJ|Q6CBxBxTt}~lZASh;w zyTWvX;V_4$(98=_!_E+N%N-FHY-b=Ope4A=hcQ3@kSk|h;?`o3P^&?iP&v=d@maa{ z8_!aKA-;ObxPr}1d2SUG7XxoDY%tOth{=rvSJ=n^^dJ9NshYxgA*ugcJr6Fd*kPYqz z?QA#webL?zq5C0xFCR?A4*&G8$3LGK*ITE5T%G=Us?U3=b8OvMc5i><&M#|s3G1zC zeDl$B-{@qhGyx|ddaXgB^u2O^6op*Cu<*HTAL+T6MV=|4GD2h?2?p@he6AxdD;Q@r zGc`;JI}J9R71ir2MukqTm}Lm)n5#olGDETs>)BIE^C|T*6Bxqwxr`Ya=U>N;3>8hmEMa`(3If;~6s+!>-&F{+ zc`#AR2P=qA6Lt?+c+7JJ`&nRPWfNym2Ct2peAV1AItF=a+Lv zw^dQ@2-6q_=YF=hPB?V(vh-o%1Jm9P#RAdeD`hiWvYiX6BB=5NoP3IHZ?|6KD^2{J z7p7n+ejoqSkgSE!WG6UejXHO(_ZWT}B(*1voWj|n{F#Pb;#!-JLn_^Vvh=#`Vc+xl z`3dlAKxGJPLB`sF4uudmhdzlh^I>2HErhUJkb(4D3gmO1iALHoi~u#Q*g=13&{g zgwA(NFz#xn2ZPl@Dyad}e5X+Ysz=APdP+BAqe4e{XRhvW)4QUgLqiKO+#OWsz#i}9>H@cqc5#R1Ujj(4z-qX6&s z{f#cUWPu7gt!&^7WwGN;fE#nbW5uCe5f*PMb9>I5?m1YF2`A1ZIqEBFz2+Mo+Gyec zBZ^bD`cLxM4o!)2ohfuxuBfj%)?VDz+4kiBw0z&pzQwHraj+%G=_W09V=_NM!Zj$V4-FQ zaH&l`4VPHIf{=)XfkgR`8XH6thk0+KbI^DFiy`A`o>3yQg4ZmZJ#*7^fd_FNKwW0A z<$;&oyP6Ri@Ht-8hE{jx3fSiSUAH{{Wkc~swGBl6-}5j|Id&;vD2k_DjDW%Ix5yWkf91m3!=uboke z$(SjEaD>w#aD?aKxA!renez(^004m!^3Z_}JHrcWcts@Z$QI-7*%-5AJDV2KfbL=t ze1al1l;wk8Kci-n&&&_R+?~@jLmB>s1re_Nx;ZEkBW%>q^T}z}Qk}JyVkjHpA#a;K z2r(lPEQGLkBw}}2*}Rfj!z5XjwbA`!!yHyrLD&gr)Z^t*phq~IhD+sBse&xjHp+~+ zZuf0|YNlZv$6+MYu=_|fZ#Ezd3ZoEf?U2M|D5n@p!~W0lj28FUN1*ntT+ipNMe}U2 z-&lNIFFRA}zK#CkY44YF_q!X^#jfFdgf#Lt0m>&Trc@)I3>+sA2iH6Gyep3Tl?I0N z#3ln%9i+lSr1oiYjp&kjDUQdr+;}Gq8Vb*uQrnM2&C}wm&d2Qv<>VKb-}K<7 zA`PSYm~$Y;kU0~8Wm*A4h6p?+AaEIv<#l|F%eXcq)8fvm7(x}-nV;bXsYi`yRH8CpD!qb8KiOknVW1mR3mixk8un@&I;HzDSonU3-lpA&lv-lT$s4 z25f!ubRp;>6*ydU{`vojKiCC1%!G&%Zj_oN9~Ab=K|n<6X^>ZMz45FRrfowGlm@|1 z)74ZWww#rz@oW)OSQhi$yZqKWaSUY!7l^Ja2+!d}ZWnG=@?1M)>N@HzNfKKLo>E`_ z8}|u=R5ZH>9osV(>NY2*jS=xtd4;zlK!rJ!cgV9RKl4BMtBPXH;A=)Tf$?($M-6IF zqlToKA-R~Xa;fK3MLYk@Pil{4Mf(2I{if?P0zUyx=?^&63}#O1%O_u|W!3LT{p z)Og=DWobHKR!5P-Z{-r7jfu%m#)0-eIDV94L{zUal1j^Y}Da6G@aS+ z6d4yf3efdvNQM=NRg?oJ(KWRpVG-CDF&MnbR#MK0!NrfnLkynsQ+O1wiLn{b)a^4R z>HB)REN4hKQ*nO4n2l}GhK9fA+bmYMZcoln9yM0>w)zjKO&Dgm(EPO{Q!x*s$Eg^I23H4MFp)_JJOFWcngwCzO$m+{^WTSVJ2+nZH^!0Jt zO}plU>w*r2Y5-@{bW24P;8d%vk2=qfl+b&zRlq+uU(%A1x=)KbUWc54U7@hau&1 zY|wI2TJ~u0va4x}-I4>Wjtz9A3mjf9Bc45;`wCqKX;lX~Ee!eU!8DSFYx|f^nP<_@ z4}%*YJ0~~?&d)#5Hh9S?6$0rBY${uYofYK%0a_h31u$w*qW}Wol9fwsAPQpa@RHMs zi}VA!{TVcjnkJgCLkl#8A;Aa*R{hfM!?$a9XDku*M*aClyBRl)%yU@8&1rdh8gDF_ zCrhtg=LvDM<$F%grFoa=etYQYk*90!Ss`iCRZs2bwj>;1sj{9d$%(qU|F$tEPn4?< z1lzCDA}-s*4Yk$#CMQ@)r`>}(9IEU>jnm8WR3t1p0~H)hycedE=mT5oa#Q@tX2ICO zzT((tXXdgRfCY^`fMu~-f}6p@FE8bRulkZV!c&oxdwtG=6w<6447JCOu5t<> zx#toPhyatMG3b;bk_@I__(kbh4BQUD;Ox0-(@5B+`&+kbQs?@?>i3K5-Cz9jh2wv} zqDw@ZF5LWaH&kzf@s7@PKf)Zfn=zQ1bjxrKi* z`fVTj-DfgCed&yk&)!yVpYq(ae?=m%)6awc>%QfCpLu_C6aL&abO$6Kvy}-&zS2%w zrd1mrb)U#RxK$OZl>hVIkqSZZ_+YZg#jQnARm@&m;Y|>n79=Y&Tqff~S!`K#I16v#i;qT)Ri3$s0@e2oG`%*h%YVtK)XgMHjSw0_}#GCIAtF9`(kT+=n_NTi`|Fs_FlDIVjeB?KQ_Ih=|uSK zd!FO%xjwPC3-WZC_8n8iviTmfeVW#9jlH{mci!%Skj>J4l?ygUO!+tzAMb(b<*vP4 z4(+W#%)$2R`s|u1?~X$V^2Lq$!j_rg-{@Vu>3vrDh}Ya>IIc`DNU59-nOc2gDzr zl((1R`O6#fhw^&Y3#MeVgI@#=RAQGw8p3_eVXCrcxh~|IxenxJU36|wdm%8M_Moix zSa$hi_C$tI=@ca6Dl*6tj^jAp_Vm+ldN}}P5o~9y2Rm6$!Sjc74ZBA9X3Ts6@6-?T zwLD8O?Ms0MP9XR+mX^2bm9AwAsSX%g}zk z@%hG_=heJD9eMWizJ1X2@xFCE?L7OXQy0CkNaOzaqmKGHyZxLUUx|Ah-4We`$x`f! zm56UT%}EB~;lc(dfu4O)DIAiz+uJFYnhMoOG50TQFluP)1Tz=dBrE&g)Nb0g5Lkj4 zx@D;71i!>f2)FM=2+GZmn7Y$-e0F2J>bh@lh*%Qtd3@A2eDPIBCuCRyu z`=ZQv5g$BA8I$P_Y*o(@wy@R~$SD=?_>v}V7`Jxq-a}ae5akus2h|+%;(*FoQewTo zWD?Ekt8baJE|^z@$AY~fKWh&O-QfWlDt5)|57?LQa)1AxF`wjoKVt`bZ(sy7wlbT; ztRS(*;?O}R?JejrSj@`$6B9cUZulL?eJk$vwD# zhpHB`Oy!FG;I`H zV!#)V;nT*hfM;c={}y%5GrxZ6=;!OlvK%M=ZzJcr^Yv`O_2i6iPfq6YeXvP}PiA@%|Dp&@(zDj^N?81XL?5io)8WK&6tE(B79>3OpA|uH|b~@}Smq6A~w8?kWNx$o+z;u$ zg)j(HD;|f!@C;7*1~W< zkw*dRu#+Y^d}BW;BJxC20DknA+d%z#O9@CbA$SFiF8tcU_YTRwjq28P0#@MnD(XgD z-UW0z{2U@dqA~`X*EcHNWKCHBAI>C__8`e81svfd!+sS&gIog`z~R(fD%ACDmCujj z@jbr3fA{4iUt5|rPsP39T&~Ur96W|bfRW2)U$kUN+gw+!ebUC?kb{puQ9Yg-Jbo|<$e3F@S26v zW1}uoidLXQmHO0v(@wq85(Tz#I1w}NW(pbzp$YBjzj(-le%~2dr!%))H;d<;)NbGU zJb(79wO>z8oa>W(JYCW4KozZ-N8*X29F2}$P5SKl8p(3sp1g)&i>-qxxon}jg%b<94{E*F{qd;>vGsbGuHMK)g(g?veZqb1P1EGGQa5L3GuEr~+4nKm zcc1A6<1Xz`d#*aP{o)O|fJ+n%4H)fM>XEQVPXXEcn(;u(j#y{vg{lC%@ z6g|4HB?O@otN_oIt~g;DiO*qaywW4%iLiB*-Moogyk<#p7UTWA;r{f9um5OXvgvxW z>8I~_-~EN}*&DIp9PgL0E!rim3ZA(u5Kxr%V6}y=JzlU2A3RH%tI2YF_J4E9>Wz;~ zXMG)(sxW)4l-%{fM0k>SLLf)XU_Y)th$pfN0e{ED1?WjYVk?V(Ab;Y?GhJI zb_a2B1TjDY@p^%){X|4AODXO2$P>2lpo}7n%sU3k4Jdu zXEpZ1u?4s)7J%=1bO+E@0}3BBTWM*iwJ0?_gpBv-9tlV<@kPp<UxB0N?gQJpBc^a|98BCvN<-AId?%CL6Ct{0Dw*#VDGL?o!3Pz?`!#fww=UP?>V9?JE6mRsou}Q z%e}D%xy>BRDqsl@EcvxFB|^f&7B9o(K&2?K15iURpDi*-BQ%^Z?f0)>M1@LkviT+l zA(A<76)Z4fOEwUffB6?O#Q!9x|Nk$(C3k*nQlh0T(RrdMb~v<#ExocBy(6YjB+pLC zoBHvX8i`lhv0xaWAw?hYu&B4}6?Yz zGC6!~fB6SZ21`;wxeEzbplHD_xEqKp7KE7N5fj7(R-p++P#fLu?RT{VKs2f&C_9CK zT&iEDR&xPF1WvAkxPRB1Y+(fofGf0Z#{?rL31N*#^d`feJDjf&5e!U)AppLMfrkg! z0JNtvDz!)PFl?0jY2_bL8^uL*XJ--Wz+e=ZnXBD=AFot|1t&6X=G~1e-y0DTBW|?l zf9oAJ)MU-h|HSV1jolbXSB-5D(qn34Gm55SVK0w6W{w$?`B`q;kyYf$f1^)yoGMHa zXPEHrktXI6&kT`L%5*b8DcZ;0$>!23&k3@Ft2T}TEO^fep9Vzvt|U*8yZ{HqHO@y= zf<>1M;DJHnEKU>c@JzG0yT#RQrac$+`BLVa8Mog(^8_lOs6O((?_GmKb#4ZuEOM~; zn*MB0A-yLLt@N}C*kT+ z7{VP~+dss3uQPr*eQUq)ZJai1%Ru$K;SDA`J5bM>gQMWOrD|X8Dq<*5XDyf1#Xxss3`H?0T~%Xn()*^>0G7Gg8Fv)5+F-oyKL{x~l_-lG!XiuiGSfN%CX zN!UUpDS{kaF9AWXwvElz$-)jCZ!eCGf<1}~DW@d4RP=@HQlQ*JPe3V49!+=!5>8R` zf5F(d zOe?qV5m5z(=0}(>n_ld{{J?wCwBfmexivCJ5>Kdkd&DPT=XaV>4+Yu)i-4F>wMm;H zK%FCN4ohCo(o(}Z-q+E*ek8~Mq-4kz5o>6xsG{4C;?%>%G-uDRbjB)+0OL7J zfdkONGJtrs6FW}3#D-J5L>!?4rZ$6A)UWI!lTQdz6{9LIsVab&0CbQzVI;|uBnKQ< zfs6^cPoAUf$8tiri%T5Rny2{z7X_Q^(R#`^-_R8h=u$&1p?drJ1%&4#=KDH%t}M5; zI-v>Rd^{uWJHbWi4nyGBQAR+6D=rhj*m9VOc zNy*^IV0L7bl;^Bs%2iq*K!BvmN-99peRZjYTTrBXvcciN^Aw=E2VFsD++-QTP>Ud? zMsTUTUVz%XfR?-*Y)d=xuzwz+uvJ*qd9o1htM8Im>cOuh`}W$c#1jy zM0T)otL(PTnqqZQNhyzsw#42T^zf`%r{BpnWgLbq-|ea@N_?^%V5}@^RuJ}oQv%)J7@UP z9!`=RC}=umNV zyiin!9M@MBm(O4xjLq03yyqET{h5;AN{wfg8W*X$R5`CuCgqftF^!xf8T+d#hRI0T zX(1!qqD_>{Lkz~j;TIUHd1!sq8N2r*u|1lISWMv{;N2K!y)PR1e~!+-f2^ziKi ze9w3D`qt;BCGL4ugU=t z$qJVBKm{z3FMlCaJ%sfd_X3n94c7}}0>g-x_vG~sIGM}mQ8=)6wWj4CA7KEyxJrF|HJ-zL!5yV-_8wCE7z;Z|MV<+Q=lq zTpqd8SGt>46$ascT6QrEvN$qDfVs1Dl{oWP07#*%cx8VL*2fi`3|2 z(>J@YKGnWG?e^L&>y$cE#`CMNOI&2uxOk6awe-4f{n6}`9m9U{T5T}(p~;qE&1l@k!cW~cU^(t}_+e8?Gt~^WL`9Bv0fXya z?}s6MeWvQs{!ilm&PDpd^K|#V_Vt3yP5rK8&3O||+uvt8mk8#bR=xfx%^y~~%Y<%+6WSE)f!2|K;&mv&?N zj^n`$jVm2mDM7w!VraWHurA{EH^>y%nPc%=-|z7c)GxJw*oj2v`DgAFzWgZ1dGnds zHQuxzX)zMQeAB!TGu0RJSHE9gkY3Mc0QC<;_AMI3iCLz9Ej*}iFC zf#U#BpfbFO<+>u}+$m$f+6_w-%n^B8{cFv~Vb5$kCu8dv>rC}q51vJN zOMHZ+152)~q4P*---s2Ul4RCCTSJD#5DJICO~VY=y3CKlOLmG7?ELai?WdLjpZ=%+ zkNiSd%(0$V`7-yyw znS_7}0P~X``J;>=I=DODbOA-1tSUiBJ*&sxJ{wENj4#KV;|9p@3RKuBB@ITnt%$iq z`Pji`V*+>UURMBrv%k%)S1AC922EYVtt{p1O6^|Fnr{lYj#5nvnL1N)^5UzD@)Cp} z(!E826*~4l0BDER=<3~H%E-_CR>dEOkF5O-4(Ku`u4FChN6+fRdZr{2-0vpZJz{)}N0NYHBlPLA`Fiox2 zOyn+0J-9M9J$Hsxq_J{t4 z9#=X<63@6h=wVJ@e2^$NNcwct;FO24nbYU$R7J1Ok&LE%Z7$F>Xc!rTNHEJBW~)I} z-_Ccnv7C0QB@PMC0^DZNJU5e5JJ%7Pbbr3PlM!nP9nNCnOS4Q+I^Ez=Kr-U(1$Rm8;2yO^cB&QTcaA@p2$}|@yuK`-o%K{=+(FJ0p zISlk?>#U(LQ-I&vFhVNM&po!;C>?z zGPr+q6qIZ4v&s|lW5Yo#jx`$;N91^`$)i6o+n6pH)l2>pxJavt+X^e6S)dJk**3yN zBJzS8vDk&Cz@%53!wucK_-z=H4h+!{9wgFDzevKR54t}CC|VPI()1#lU!CRivHA}U zzW+t|^7pcOfj&3&*(2w--;y#DSz4?eHExsg0>w+BKzRxjM7upAw4|;nBzWiFK^&LE2{zf%ve2^~+ zY8Kp3Iv@a0f*EM&4tKw>($8~~_xlO(NYTJTq?KgbdUSRQov%d;zX~1b;vr2~!;k9@ z-Q-^w${OpcwWarp{m5`-d`9y46|4a`y_;W#Nv7~MMV)NYByG&xsZ3NIvo&O+20$Cz zN+ydoaqWet)2pt0_Lgm2I{oAfFq0M1nF8u>93)Nb$5NJUj5Aij2nI~J%P{2CKMGP> zN2TQfhA3sJzyt&kRe_?2=gBnODk4U!>leN(L%MOY9#2XC;EleO=eXs1TtcM1^uq{q zf9C&o@c{+^k2rCKvKxG#=r0@m^{yY-I$f8Huy;8wbJsT8VXheR8FZ#%UE3WMpi8+_ z6_#zS-la?2)groyUg9^jq`9bEQn#52kdji03?(I+Dy(s>Q2Fr2L=!>aX$;QxxK)QC zK(ba805r5`6l+@200xQ%p)yv6Pi;PGxo%rNo*MJe`iJ&MZ{nioEN-MX;fSFf(z^;O z0OxWW8x>Fl)ThTR8ZX-BqjM-Q29vd+AsD6Z3^&6#q~Lnl&UKqZ6rc;&t2i%?o&WL# zjvajG`^mR|Px|&5Dzo&_3I2ogau@RggjL$MySE?}5iybKbs~_)t4z{Rf?CX?tNUy$ zlbg=crdSZvivcCp06;X?vzTG%6V~qQV@AxKOT%7Kz(w@v+V6pq@8FmTQu5>thk$|c zYNim^Y)=!$m&0z%Odh8{JDn|Z3~D`LYJ;m7SH@NKfu#i*k42-v3!`0^!zwuO7wj~b zoLuPtW0{qiHpy}hJxfcsnf^;NX4bCT`F`Vh{Pu0NPgY#y{N|8O4LUV(YUl_TrHpvs zZ&hb6D}2vXCM_`nuCWJT5V27B8S%Es`5gJ|&HC3~TK@4&g3ZX5396MNTNUNIf$*!) zYfk{7%qe{VCL`+W*&>5^AE(`SjAQ37Pa=0|bWOk1^>XTkuT^j2NE|wMOe^gEO6cT- z<*~a=eKLV;KujFAEj9&ta+*q)1S`ygz13v`98 zsjL3xj{u0Fz-}3rXmS9c@7!l(jo#2&Dw$@w{wE|A9g3w=8~hixNsEwG)zhw-nMA6t zNjks?vBrA(#zzrCbKPB^{-~#Y?7s^`3O$U12GmeBQ=2+#Dqa4mwz5rJRNnSr2_>`- zN3t|^(RST2ge6_41S+k30&kd_8K$chEQmaXhKoWzC`{xjyG>?Ay<1cKyL5G3de5d$ z+@G}Y%g0^+^>qK1+w<$chl%IwMf11c%I9NL8k?58C5M1UeB(mKiV3tl6R94!1dgk@ zr7LumevjnS&V5nc$i`lgSD^@i!KV7z;3^mxxD^8V7|R~b%dwqRDA1f@w)Q~FJ%3Kd z{b+QJWsT$L$02iFFTAeXuj>ae$v%vk$Gl>?OZqlGz46Gpw-f*LVc|~6pKjP|AAi*- zsl6Xr*NZlA;|87h0OMdr%4{{*7q@@^hbOTBkJCN1ynZSE^LKQ=-hRHp3;tL;`YO8L z(@2j`X`G0SUSbgIZB?}(Crqpz02KN%CEa;Zek;+>AUqVa4NX##W(&K5<$ReR37D7~@&_e^MNFcsa}%1RhSFhHzu z8DM*H>@yCh<`|au8fKL-3}I}G$Ts$~H!MFq=iZ8FI~6Vz^+#rz&gTevn$6|8q5tm* z`16g-Ask#?jwN}>EeetnjOez9Nefgu3?{o>*Ya2BLj-$MO4Hv zPh8LlO@%2em5gM%*pwc0T2pt4E85w5#V_-8=2OXx!>Et!Iy(=4^#g}0I@X5~YK$-h^L<~Xr~iM)x~ti-TW8kZ&BXQ|^~AGv^t^2I#D@lB?(|!*{ECchHxi*(T1)3b zft=JgI|GMS+;05}v-)%mB za^IVy-~9}JF-x!Co`r_%U>PDq7AXoj)fcJi)I>8vV5565%8NU=C?&$f>& zUrfl{Vd=0~7m;l?p)t6?R0=2n1i&C_r$oW?o74gaL%lebZ9$fZshNunS8QF3(cld~(BQ#7vN7&&(QjL6wkpMt? zb%9NF2+H#NOF-pv@COvpAQ3EcL?z|#VvaR@!fahsf9|tXL^oUUt<&^=F@F#CK{RXg z7~f9uh}x1sSp}OzE^_p0sXEo(3p0UeedoWm|5hTfir^)5j|P=6O+(4qb9+utx0q#4 z&f#=u4mysM=K=(B(ur~$gd7D0CZga3L^;nVLdjcgH0OcnbJy#y&HU*DA}G|Y9w zn&-lL;$7=PDVn$!2F_tKlkPHej&Y|o7-iM-PocT8NNgRHLrzGw!2-!`RX-X=)`>zrVQCs*=Bovxt7=}V~yT1snA!6Jf7UmNF-qSglGcE4E z$;)xt=Fww0@oeQt&3iF%x1Q^OqKB%%Tn zM25jg)vc*OLA@0lO#K}K7oJti8rz2!ar*p6(~OqX@Jz`qZG#SF2TE&~K1z3uq4>2+ zIMaoY-lI8X;n!Ylou!QEg6`w?Mu;Wmb9^-YH#A>$fwfpjrHp*yVFw;?NH8JJA$eW1 z*A1Ji7tG;>_?fx<#-q4z$_41(kfmgbFZYe;f-MXY5jIjMC`@H47Z4BxUYV6Q{ne+N zGcsk4mb}?v*wR>O#hsc80Dx)2=!{Ri9dJ-#06Q)wOH%A+c0=Kp{Bg76dtJ?Dej+~3 zFY#@o*Q5UWlW4#HSfG1-SU}@@nUuyGLeLhs5|Lt=##Nqa5Gg_dS`4+Y<}fXIrZfJq z+V(2IPbsf4uQ5_ihxV-FT-@htp8Jx|x^)eHBqw`?+tQstVM!M3#5)oWt$`vGRLKr$ zm@6w^x6ng#*pfDa>nFJjgovRSiE37i^9=L#E4Y9Y-f^U*vi0HaolnQ&nJLn$XB9-9 zmCe$mN@orc90;p==a78h5`I|8T&NB(vb=HK=owFcvoSkvpUJp}^D5O@nJHC*%^n%% za1rNYzB8X6&hIm+o!)+i;_NyvE)KbuS!oT#I|PPTU-<-3al$2%ZCPwk)kekj>`UQ2 z^w$Z_--7+W_jUIP&D<5xc!~O~a~I~qR3ruJbs=B+2X;BH;X3Ayj`5itQ)Zqc zfP7L8U;van6PE%a)j_Jl1_VJB%q>jGS+wVMsg`_=4@X&?VU{q(0wY@|!Q8sOfg^$5cM^`)qQKn8?*aE^XZki82utd;+2hNQ7Ay5oz7D zdqiEds|+?%ep76B1SLd*5Y4P5dwF6l^UL~%&zN%g-hq0g3IWUh~}m(VW>)@L3d zsP4vnp2AU_wMK1*whhmeXxDC;4cxV!k#q=(oG&Aw!H~I;PAT|aiHPdltgsKVU3z+n z{cxZ00KH6_4*=E-tTeESDWK=)XiGpyw*Z8r)fNB`jmm4bFt6(x$zUssPC1W9(OrY2 z>ftW!t=!?BM>i+)&PCH$w(MA1{BFUA#L?oG)b<%|Eq53hOsiBY9UzFbvY^`G{M@#! zLru4+-ek6Gco^DWJBfe#DE{()AH!e%TQ~a)ieqDAeJGHaLL-Kr?h;c@N~EOUEK61U zbT^K1&ON$wDtc6MrO5~YI6#S%qa}*0z{|KC5UWa9atO}X(Yo;7>3e#mBlBO)r8Z5; zSna7DTRVvKG)wIPYHaJF65`isTt)PjjMaNj62#5=H?9gt6 zo-NjG+SNio9hP`r=zY(GC5Nt~alO&{?TzQ3<7s=#{hhy0&Ep;dZ+as>@ zDjTHaKXgW;NNOVMF?xDl>8`88_Z(nilm)<+wg|_eVoM-$ZS_rLp7Ka}0(RK`*2NW! z**?ykzXEg1f7%@@5!L+alXMjvLg`DfS8 zziTGYx1TO1M|`IAsY|YXSGQ?hXQ+h()=jRk*L=;OMokS#jp}tflOZ9!#f2gwFjb0< z-^qw#hqr6|uP6WQek}@iOR7_;R3fS~2Xom-Pv1I>Pk=9KEq6crjiBld4-hInC2o-> z01yX2zTlW0LSj7x6kN4UG7YAS{(Lg+`T3*uAI{Tt`17r{*BkA6+SW+X4?K#a2m6f3 z))BIn8aA8X47>Mq{bBg*@MSuFloV>X982o4bo>zXM6eZJNC z`!An{I==qf*nD4fin8r(<6ni!Qo$_{WqDbexWZR(3CRU4P{W0u9!W-4jhdTs_~S3u zNJ>J?@-T2$nDK#W)k9(d=HhBNKuXGLAq%xyWy4nQ_oJ~?V?!E=X*j~FKCrUW0el)2 z0DnF8$#kD)T}_ZA$zU@B06XNq*3z5ouXXlYGR|N`4b)g4jiN#SXVRs|?aCs7a8O3n zXHMUJ&DemiUdnqLpv~0cu{)Q5U;y9DNx1bVn(Y9=Hk+Q(Jq35zZq=8{H>5Hd?nq0v zz)-0a3>NNx`TD1`ujt%gC#`dWSrXDjR0ddBSXg+31XZ|phX%}esiAQWkD4!a@bczU zN2E>zqrLK zX(0)%kl@~|y>&GaITKX(p55jLF8lGXKaR_Pn)a`(Ij@%$MaAOSSdl6|NaJwk&>GnE z>an%_vsb66v{kAOIGMo2(*l$R$3Ci>zZu3=<@Kz>vi9eW+@H)Lk3avyzr9VRUAwkv z8Bd(XREG%V93u{1k5EY&)va{c-4dvGX~7__1&AFZsBB-eG6H(x?)VvL&lIsSCW^ziuz>pu*1jaJE1oNS-xR zQ2^Y+z$T_Of`+Nl)_tkE`yD-osW1c>iH+JQt!PsOlb<-{$xI~{oD8IOZzD&)m;kN z>1(E6UD!VA96&ze5fY#@LbwU=Y`-4QS-OB1=;wCM%pGd*ukcX=X*LbgDW?y^1L^75 zrw(mlCf);)B>+a%QaTiGvBc92Wsj)DhgOa`L=zNf!t+1`&@84m!Fix-W3g9Tg}Z@E z*X6}jE8~Q78+B$#U3yK^3ib0yni|EZ0U*?qWRWGcR#oY{*0$H>#a24YfUb_b*TzKpwB8hfMGBu`omis(#hKA=ki(pqVr|x|ThqFK zeyihm?=SdsZ=U*_H|Mi<{mK8^)R_CbZ%drrvimKO9DW>@2r%LXLz4`;2mlO8;$ZK4X?) z$P$c>#6%GyGJ-c;V+a5md*BRM8I1r&&@y27PR6(YuCF}sm~G2{NlEzr(p3Gdb{qe0JLw1?F2mB zHK-nEK%7~dTdF6PoF8LO+I1qekv3*`V}Cpsio1{@7+2SF#4)4TttIA5aWZfPgZ=u|&C{(tqt+yKHKcNLhp(eB0Kw(u|xuoMKWN)Gq<3^ z!y2|Nd1i`7*1N_pB!!X;qSuE%&ByyH5X8sX!klTr&y|XrtTDT1u3Y?yL$Fo zoC6qQ?$hPj`wE2wETsU_gb@t8n=>@am5KLZy+5kU?DN;Y_h;zX)>xdiQT^!22)0_7 zy&NIvVIeBafoQEqy!(+vc z|9`&jQ8BSxTZ3|**a(pQi2E(wVv6f5Y0g}(pjW|WcsM$oPk3N@mNm$NxGBwi_u2ha zs1+*+wMlsi12O=u8Yp^|AaSMvmyWsR0V3KgS)o~A*7H2om~ci)0`Unn?`~3!Y+LEP z*BzT`q`QdO=BwntrxcYOE;>w5cpv@E=JfBH^RE;QFE95+27pC8pV<*sLH2L zIq}s}&ki7_E2I!rZ;EI^v|i{|KB{YIN%8$DTqPl54)Dfq&zM8xnwOEi8C-t-*DH+l zccd8h&Q-s!Q_MG`H|vo71|^);+x|J+aom-fH*%)UE%K z|N3_G|NF_Y`{Kyof8`XF+w{K-RA{pu7o>5DWw|Am1Z=i<%WI zme8!B6|H3HSm+WK^2o!50RZCw5&#@500Npm1H6854I8A^)#u?}4mDWJE;B^E7tceO z&>&IU8-8q>zK}V?j!Cx8)AxMD^?$ePxPYm9u1?3v9kaPRN~_^wBcee?9?l}>V(SqL zj&zy57MSg;06(((MIgT~m4hyqDt7}g$J7!N8qZCM` zsLrlvehz7(&ztiyJ}F;;2_<9?xv+p`E!XZqG>4Ou#NBcfF2HG)=Y_=&gw2Nb?ypjD z*(r?_mbB9V8PpA^gGQna%(yGrgaal~eoGKH9e^w!J8&isbB$Xz1UX-G+FXCtx)C}L z?lb3aKF`+{^u#wOoa?RwBHL944uV%X&qWpam_=hR`MMouZA{o>F%NXdLukn4J2nU zHhJ|+)u;xjHbBEu{?wD1%A_D;_Z;<>e)mj(QvXKla9P`8|F4NzQuh#a&drU{EzSCk z;mnaO{60w6!12mgR^L9;>Su|dg3naeg7ODtG5 zp3>)21K{?9JQ3$>C!rJs!IYlOf)2BIPh-+cNL# zy*sCmOXkq2%4AH=mAJKV@VDuqXTq7YizyQ7Z^jKGB6+z6o5iWi^++?PL?N7!;~V9o z_Ax+(DqTK--22EEKA$s%U@4yFsuAGW`BoWcc^r#OuhXIuD{krTag-A)^Lh?1i<<&l0=>m z^|LZaAtI{Rse=M{W)+7w*wnUqaVf}I=vLOzly;9LT?Fj{Mu76XNU6017W#sR&uHzl zZ{273bj_j%08$9!S&w@D)>;3me-76VfLLE{SE}oVopQ~u%{Qrl!Qh-uOn5-SWBIpM z{W^(XC;hw6i%3!oPFX#R51S`}y|_y^AKWJ-NEaQ@V$JpZ^K&o6oAz8CVK?oo@li35X!mbYu~cpKf`J~ z<2qQdACIB0%dM_oHsShmYuYrfTFb8 zdjxx#-()a{N<17_4O2Q1F=92=NgN(w4 z6^CIsew%!xC!b~v9i|eHL8%4iC_<1^o_ZoyC=x(B27q!N%TPDJWb|J{vaD8SET{qv zzNBnF9T|mAIn8Np#$fYIDV6pO-D^uOhCY2~{QQE|t2f|AotQ+5uFh_y>+QORg4!nm zn{$pS5i84rVgj*E(7gbcqQKJ}VyE%_C(|0HV=cWkMR8Dgc$wUU)hP_jBQSx4Wkq+&{xi zvlNTNwlTAy8lqx77>Su@Bxb@~k{r&{)(*oK0VIVR*D-c%m6Q`y0o8aejU99ScTf9Q z`PgAe2q0buJIv?Oo6|VNW<`t%i28IM`rKpBdcoJU7QcewW+()J0P9jCYs6`Y^*F4~ z2xXRi()^7O6F1IZQ*MIE8?IchP3R4FW<@$I>;MRs)c)}C zQO_qce*VnuTNXY4Q}pb#ge~uTVsTqOTPpGf9|J=9`-YKMX5&$)v4cYc7_dQWVQiHle~)CNM_;*@C%gJsj~)I;E}cP1PFkVR%Q>LtrfU(t4L#Z*z!?U(n5hRGwE zUHfE-*O><){qIX-LBVE=&vPlZMjQLCIUd$J$`mjdI8{^A+$y5V7Tna&R$eb8XB78FL;XwlOT+OvtGLWlTCaW6Ce z^t*30`^7)M|H7WM{jLxe_FW)^jeUs5g}BKjsQ#2go`^)f1uJfcK?HnlF&G1Emeon+ z=@3ive06rtCM$Z8>xbBJsD_TP9Wh=!^I+|J5TLkK6-bs|{56bm*wQ@gTAX0hcpyTt zwg|f^7OM`D8-y-b7nboydrcd5oym`CXdzDa>I>%}q$h z_M?V#rTH$W>B=rbY=*E(DL^0*lZcl>CQ?K&mJH6T;?DtCn3>7>1TdGAQlmmnHr@7s z0W$-zD@2q0aTd1re9gFZE#V<%{wd~ZK2pn+V`x2z1gJ3XgXG*Neyn$BZw`k_j>MdU zos^~IRM267@}`TxILr$3mz zH}?H>?D@kD*Y%5Rbm~hV*^}13ogL!>ZBm#JU{|&AJn{Zh;Yld@Ru9v+beAXYJ9N=)dp7&@`jK}~D1 z66p#bF>!N1z1%@VfZPi5R5-GJ4Ov@b@nV^C!e7#Cbah?6I5R=ojzzMumpLB82~VAx z5-xko(5_pJEtcwnVp%UUr~XCLIP2Gmh=QOn?1kG!vSqHkIm8md)VFt%?F zf;A|Dd3E)`ri__@y(vA`Cny~6Bh*%n?0L@V#|X`Z*`93n^q4Mw`Szh8o+2OuTY$uS zK_^R-l2MR+*cxzVHvqvg`+h%jALjn{>+?CB{`KD)F3c6nO;|xRiUcB9?Z6drFma6$ z{hN(CG-?(bMj5yeb-@Q~)6=Rcy;dhglpld>0ANnkC3B$<>H?2nJEIUwltN{}8kA&V zVEmn1B5vA4ab%tgocqYtHR@Lr-Uw~HE^I=8;+aV0^JB7$T#>;65!Qh+o1m=ft%7wpz34#@y0g26P?`9@fB zla9!Z=X`~Q$5m816RUHtu=o1(M;f^|Nm|~fPGP8JR&A9bY|a{8SC3=Hn;(%-<7!LU zmTH?_JYzBQY)i6hr30l(rzR*6;WgALxrjrm=9V+*D(DzbJ*pUE&RV^w%0!Tu#JscT zu+Trg;NL8PPKYwok*1%Ij-Wd5F@abUk&d)O^tZO5E5pn?zkzS#yA-FfARL~oCQzK? z826`0iXaNTNsoy0X+@L}!$pP$P^b)7c4-toVs2W(Pc_@SbnQ9qU}|ih#b$rZ$6;O( zeqSY@*B!ki)XX_B)#4aTAK(ePzA$XhP*5JykPzuH7=+%BC+L^>=ld_5Ih8Mk5sOfE z4?O9KYy}U7;pYGU@$WJOLuN#XjoO!z9i6H;Nu|$^S^Qa2j{QfiO-Jl_pCMIxudk|3@ru%5UrEphQIy#WcC5u6G}F0*`~4k|44 z?DpTEVd+j(KMSZblOu*u!Msn;;bi??ZU6Dokg5e1qPJm9D0diw&TpU;$wS6@!HZ4@A-t-;9}rz-0&oY;be46vD6+Hji#F1kJ@}YNxh^ z35;=bw_D3coX~K0vLpQskb0>?fo?+N29MdzG~SRCTWw>|d~)`0mSZ)aHrJ==XAKo? z6BeSB?9n~gP4-Yr{O%%Y4O_w}jH9AH%#2eCbF{44ZBFR0*%=>vWZfCJ+haSC2p5&i@dfl2myL1EUJRcqK4uffnhi;I zrxgPo1AEFkl`AiR3N_X!$>*zkzEk=?r2et)@Ncy0oPScEU+woAcEV1g``&FoE{)&D zCa_f<;&Z>`pFJrU3KbwIJYP7^@=0;)d8_HU*)ahODK99H0xcT#i%$f)WDGT#$+hck zmMB|XQ>Vi!b_?JsB9y3a6gZjYNvs?IupqRmFr87M@*E~)VNnDPND3voi%uF`i_llw z6$O_J^1-iUVS8IWv^#eO(L1nA7u@5U&R-*wsL(l5@U_}&J1~|SZ zx&mw;^~jJQW7o0m4S0JAl8k3U=k79TmBSa#2s5+^mHd`KRBYp2#H04wmYB`d{&e$U znEPWKw+iRa^B%~Bvjvg)v6H$O`ksYGqogg}3U@cYlG3?YhZ+jC*g@i2Sqn@ZmcCEq zZ}7<@B2i?Ng`yC!A}%T>6kVzY)OS|Pbgi~>R;q@ZomHQ*n1!GOCZQ}G`5k3An7pRq z?oc3UF1j_|Q?x6(_yc@?`iTDQobICc%T#|;Ld{kKH5bGEymTOdhmcKwFK{n<9HpTk z?$ht(nE9g}*Wtu3H1LB>5J=&{7usEKc!}2ti?8vuMV$J@rpMuM4MQv#-j}deMF_rr zTqHoIB!2+qeYnhhVdQtP5d#ejsG-bC?df0H6uLt8D%rRTs?jpp6X6&lB$nI z7r#2_=2upC>5D(KE`NOhODsi?^s;#c;~WU-^$w4};A6(Cu&)Avw%$KKHs|?jUQ<|- zRDDQAL8bhaV2`-hJ-b`nzKnC2(x?`;qO~E1Wka2DVJut;9kmTblH=%-@2k(cLrSvx zEUan_l*{%GxY0qS(i{MsW*N47Lud3g04bRGrCO;>+_n-AIk&_XTjZO845Ag89S_az ze5fo}$?-9rx=2b|{-C9>FfW+mp|_7?Rv-(<`O2&AbMGh9R>*}qnm|M(kGP^+<#wt| zP!kFvA<#MfhS)wGi{C&4>EX3s1i>m+jDx5~h}4oCR|%B6P~ZhvP5_d0`yfe<5ZB28d3Hi60T78C z?uH)8_5r{_A5H;ifIH=%Yvb#uz*Pef*Mrp1xY#?LD+3zrTFt!ou=yq!3fE_cS^C;f zJw0V{VFEdyAtJ{!Qb}0Qr_2)(p}6vPZVkI<2ubz`QA_QCqwO$xov4ERDa!>0=P!~^+2s1!cyFWE+6$=k;z z4hN%XMwvT9INik}ny4QW8{-W9LO2{xS7Natd|_D~%H!H(VRN-AWgy_S*}NaLE+~WP zsZ|hQ!d=DEpCn`inQ1C*F=+F6%HFrwdp!LYhti?==?=s9^S83T>8mI{4mrG@;Y6A# zNGe>iVZ%sqs2370>BIY^d|v}E8H-hC^^cyG-(bWgA}I&ZDm%h^$hG-gcRp>tdAhdF zwP3sMLuekYOqke$i9@YhC zES8rRE&{`7);@t-s)dzjte4m+<6-E+8sxw1VLDSqXPYY#0E6?4B~rlN=0X1Ojv<#J z9MJeRfv}{^t|eYw>LKAsj=v;j2)o()vGfr8-@&h z;e&`BSWBa9M$JU8;v~CO{Q9O@@yp?+ZDxWUqbNle3h-EtvKYhX4-)zSV)czXq`BE>hL)WJ*bgaDlEwL<$|yxHEbdd)>ov(axu?+q7mZ9&%JF z^JIw-7mEx98#EllO&~supAzo~_Y$RtC&IP10}q6=eDk4+bq*0JA|mMsK}kVHX;<2d1G=k zMA17BUmVcWKht%qr)+`KDOGWL>E?m$0#)Iq_ee{lgG)qiPzc$EGVZ37IZ1E7dzS@Z zpLi846)EZqITC^0{hnj=7v}CU^SoMPYl1dJZ!1L#@T7Z>LdJ3W?Q_A&2~#mmdSzrZ zUgQ(K#cR`gCUTJ}wiVZ7v~i2$GO3hhD2)rB41yxTACYXqi;=TJ!!aM9C<$>6rAf4! z=d)t+8bJk%;^N;33mWYdbV0=|Dcsc9RbpuucX`KecxM!{HNtXt2J5_$<2Z8Do3+T3 zz?P=)QoqRbdG04)mEOy@;WMTC3{;YOTeU}(jngYh-E;4bseE!`K^H-gN2kBO-s=j) zUa4{niA$T{X|`??+_q&pU1Om}GOAn+h-h7nNaZUf1OjI_ztn>?GE6k6#vL)2Pas}I zgwSC)o8E$j`8(e1S zDxYJjr(^49b(`%M-|X`lr*w1(X*WSZ30#;8LkcjFA|iR#6p@=0ltKPw_bu9s-UBz9HHN*Y9{=TT{ ziMY9r3$}7sqjmRQgP62?oKi8U?5(1sEf_a%fd)(-`S-9GaQZkT8~H6K`sZW&<~E1=bD|9a#>@jR;uO zSwDXQJGy6vFQp&U-OO10sKepw-4(j)4qB29U#{4!tu5~U4c{qrphe4Y8wEy|(GcAa_&vQWsV|Putf%N=j6|X`^gp%8CtF2;WH!wyUTg{v4 z9lW#t%~u+r$$=Ewl0;Gllf&i!5#@QptdfF)bq!0Et_SWvrA*TVxQnz#D$5lD8Wh-8 zZ}1ieMLD=+V^^&(kkSsrwC4$cyg|AY8dtUbPfnlfWoLhqV!pb7+{}{l(fV}_%k!Y{ z!kqc!EyIDn21=DVY|1hVD;t@?ZP*A+WQaCxdOZ`~!nh^=j&!?iF1Kf#JZ&-2t7d7o zJp&;u;$UEaq&#bCfS`4S2M__^=+kh%#G8J3l2yYjIOtqjDeou=Fvf)fFCp=kEVP)W9tllhXF(po(zeZ2@}dP zL}fKzRSp7`-|m)fVrW~Y#8M|TJh>MB2n51tS;(mTFpiZO)AzFVl;}uuw#<>tN6|YG zUY1SM%>LrEef-$Gus`HQk2oKTeFzFfq=-Nua>x)2Kt{0|`TOUg#(KZou zX`XSU3>8R_zOyV`nZfx(-e0aW6Z&Q9l_u0~YmdwJ_Sn-x9(t^cUyeq>ww~0*)oI>Z zXIK=pm{`mgNsU(M%q!~AR4F=sdBfmK1$T%N4b&+0e;AWvc1-&I8C^9trj4^Ob*S9n zU_ebI=`y9~J^R`hdVAB~qVfa`XUGu20sx7UQ~90pyGy!;CFMSNVM|eP%?Z^z>d1X5 z1#2}ZXJ_|Gm)zQ6W4%qDF)4z)q56yoGYHL5^- zVu2hN%zX;O3Ix2^*^4GWKoAs|H0nELPZK}0>D_fEp0`_CK9bTCrrRAF~`zy-XD!>o`qt3>%1>$Y0Ns*~g zK$hNenr!w9DKCh1;ZeG%%b_hiwY^+aCI?^@f-Dh9+ICjXqAF6cl=luWum9WzBPRYC z-wYa2h(5ge|o+)Q;?3AfRm8W=_|kNkMoISJqw3r5artV)2^!F2 zJ>bj)4q;A8l!*=Pvm{k2{ zEtLyItF737EvA6Lp`w8@?N`c>wdjNGpgPIAGgM~;8y?~{7e`rgLQCn&+-Aph znhESQ6MDme@FJQ-PS&Oa51F>3j~#4pSQAQ+QFM+r2N(UrR{3R-Tu3Z$Ekd~Fr|E7u zh$WU+qyVKyv2cO*-KElk(Gsd~TvgP!q7`41z_<^nW5E|6WNcb=>Tj(fjDbenARFE^ zUoP-I%l)gE?M~-BN`0{E;mNuB4~LN!PLU~Y6r`yEfD{7(FqcMJK$yij>j%sAgIE&Etz1|W zpj;{yOQlkFrE>w96yacpyPH$G@7ghxEdm2f#~qWz3dtl!JkB@>!}j_i@zyo{tArA} zn)_hveO!)7%<;{%3(JPz(R1j;UCmDL<~?z5M=w2@Oo9v^7dBMZgz0vH?*rk&CkBkT zRSa_5NxT@73|(iyAHoxKLUS=h1HeSbgN|}`I8y+#xzTJjp%zL55s`GjL8LKF5s7+m zhhVl0h!#-js%=6WOEWb-HG>dtP|ULTZqEFU$h>)~ekz6I-9&*xd=P?Z5eqF8#(pUv z0>n^c2ePgk2+^`PLI`k3&;+6UQe5jNCVKOA KBN4;FtJRh32)95Z~K$Z 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�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 literal 0 HcmV?d00001 diff --git a/docs/4.x/index.html b/docs/4.x/index.html index 4e497eaf..04064b05 100644 --- a/docs/4.x/index.html +++ b/docs/4.x/index.html @@ -1,20 +1,22 @@ -Home :: Godot 4 Recipes - +Home :: Godot 4 Recipes +

     Godot Recipes

    Godot’s nodes are your ingredients. What can you cook up?

    On this site you’ll find a collection of solutions and examples to help you make whatever game system you need.

    Godot 4.0

    Godot 4.0 has been released!
    Godot 4.0 is the latest stable release version of the engine.
    There is a limited amount of learning material available, so if you’re looking to make a game or learn the engine, we recommend you stick with 3.x for now. Don’t worry - what you learn will still apply when you’re ready to move to the newer version!
    This site has lots of learning material for Godot 3 - but not all of it has been updated for version 4 yet. You can click the ribbon in the top-right to toggle the Godot Recipes version, or click the button below:

    -Godot 3 Recipes

    Are you ready to learn game development? Whether it’s as a hobby or working towards your dream career, there’s never been a better time to get started. Modern programming languages and tools have made it easier than ever to build high-quality games and distribute them to the world. One of these tools is the Godot game engine. For beginners, it offers a friendly way to learn gamedev techniques. For experienced developers, it’s a powerful, customizable and open tool for bringing your visions to life.

    alt -alt

    On this site you’ll find a gentle introduction to the Godot game engine, as well as a wide variety of gamedev tips and techniques. Feel free to browse the categories in the sidebar and see what catches your interest.

    If you’re new to Godot, start here: What is Godot?.

    How to use this site

    Beginners

    If you’re new to game development, start with the “Godot 101: Basics” section. There you’ll find an introduction to the Godot application, and a step-by-step guide to creating your first project. There is a lot of material to absorb here. Don’t feel discouraged if you feel you don’t get it at first. Repetition is the key to learning complex topics; the more you work with Godot’s features, the more familiar and easy they will start to feel.

    Info

    It’s assumed that you have at least some general programming experience. If you’re completely new to programming, click here for tips on how to get started.

    Experienced Developers

    If you’re an experienced developer and/or you’re familiar with other modern game engine(s), feel free to explore the menu on the left. You’ll find a number of useful guides and tutorials to show you how to do things the “Godot Way”. Code samples and example projects are available for all articles.

    + + \ No newline at end of file diff --git a/docs/4.x/index.json b/docs/4.x/index.json index 18b24f43..e0170e87 100644 --- a/docs/4.x/index.json +++ b/docs/4.x/index.json @@ -1 +1 @@ -[{"content":" Godot 101 Your introduction to the Godot game engine. If you’ve never used a game engine before, or if you’re just new to Godot, this is the place to start.\nIn this section: Getting Started Introduction to GDScript Intro to 3D See also: Game Tutorials/Your First 2D Game ","description":"","tags":null,"title":"Godot 101","uri":"/godot_recipes/4.x/g101/index.html"},{"content":"Problem You’ve tried adding an AudioStreamPlayer to your mob/coin/etc. to play when the object dies or is collected. But the problem is that when you remove the object, the audio player goes with it, chopping off the sound. You need an easier way to manage playing audio.\nSolution We’ll solve this problem with a node that is available from anywhere in the SceneTree. This node manages a set of AudioStreamPlayer nodes and a queue of sound streams to play.\nCreate a new script in the script editor.\nextends Node var num_players = 8 var bus = \"master\" var available = [] # The available players. var queue = [] # The queue of sounds to play. func _ready(): # Create the pool of AudioStreamPlayer nodes. for i in num_players: var p = AudioStreamPlayer.new() add_child(p) available.append(p) p.finished.connect(_on_stream_finished.bind(p)) p.bus = bus func _on_stream_finished(stream): # When finished playing a stream, make the player available again. available.append(stream) func play(sound_path): queue.append(sound_path) func _process(delta): # Play a queued sound if any players are available. if not queue.empty() and not available.empty(): available[0].stream = load(queue.pop_front()) available[0].play() available.pop_front() Set this script as an autoload in Project Settings. Give it an easily recognizable name, such as “AudioStreamManager”.\nAnywhere in your project that you want to play a sound, use:\nAudioStreamManager.play(\"res://path/to/sound\") Note This audio manager is adapted with thanks from [SFXPlayer by TheDuriel] (https://github.com/TheDuriel/DurielsGodotUtilities).\nExample project Below you can download an example project showing the use of the audio manager node. This project reads a folder full of audio files and generates a grid of buttons. Click the button to play the sound.\nAt the top, you can see the audio manager’s live statistics.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/audio_manager\n","description":"","tags":null,"title":"Audio Manager","uri":"/godot_recipes/4.x/audio/audio_manager/index.html"},{"content":"Problem You want to detect when an object enters or exits the screen.\nSolution The engine provides a node for this: VisibleOnScreenNotifier2D. Attach this node to your object, and you’ll be able to use its screen_entered and screen_exited signals. *\nExample 1 Consider a projectile that travels in a straight line after it’s fired. If we continue firing, eventually we’ll have a large number of objects for the engine to track, event though they’re offscreen, which can cause lag.\nHere’s the movement code for the projectile:\nextends Area2D var velocity = Vector2(500, 0) func _process(delta): position += velocity * delta To have the projectile automatically deleted when it moves offscreen, add a VisibleOnScreenNotifier2D and connect its screen_exited signal.\nfunc _on_VisibleOnScreenNotifier2D_screen_exited(): queue_free() Example 2 We have an enemy that performs some actions, such as moving along a path or playing an animation. On a large map with many enemies, only a few of them will be onscreen at the same time. We can disable the enemy’s actions while it’s offscreen using VisibleOnScreenNotifier2D.\nPartial code:\nvar active = false func _process(delta): if active: play_animation() move() func _on_VisibleOnScreenNotifier2D_screen_entered(): active = true func _on_VisibleOnScreenNotifier2D_screen_exited(): active = false ","description":"","tags":null,"title":"Entering/Exiting the screen","uri":"/godot_recipes/4.x/2d/enter_exit_screen/index.html"},{"content":"Here you can find the most recently added recipes:\nMultitarget Camera Character to Rigid Body Interaction CharacterBody3D: Align with Surface CharacterBody3D: Movement Arcade-style Car Pathfinding on a 2D Grid Migrating from 3.x Shooting with Raycasts Basic FPS Character RigidBody2D: Drag and Drop 2D Car Steering 3D Healthbars Grid-based Movement Arcade-style 3D Spaceship Interpolated Camera Platform Character ","description":"","tags":null,"title":"Fresh Recipes","uri":"/godot_recipes/4.x/recent/index.html"},{"content":"Overview Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. For example, a Sprite2D node automatically displays an image, but to move it across the screen, you’ll add a script that tells it how fast, in what direction, and so on.\nYou can think of it as the coding version of using the Inspector - GDScript knows all about Godot nodes and how to access them, plus it allows you to change them dynamically.\nGDScript is Godot’s built-in language for scripting and interacting with nodes. The GDScript documentation on the Godot website is a great place to get an overview of the language, and I highly recommend taking the time to read through it.\nIs GDScript Python?\nYou’ll often read comments to the effect that “GDScript is based on Python”. That’s somewhat misleading; GDScript uses a syntax that’s modeled on Python’s, but it’s a distinct language that’s optimized for and integrated into the Godot engine. That said, if you already know some Python, you’ll find GDScript looks very familiar.\nWarning Many tutorials (and Godot in general) assume that you have at least some programming experience already. If you’ve never coded before, you’ll likely find learning Godot to be a challenge. Learning a game engine is a large task on its own; learning to code at the same time means you’re taking on a lot. If you find yourself struggling with the code in this section, you may find that working through an introductory Python lesson will help you grasp the basics.\nStructure of a script The first line of any GDScript file must be extends \u003cClass\u003e, where \u003cClass\u003e is some existing built-in or user-defined class. For example, if you’re attaching a script to a CharacterBody2D node, then your script would start with extends CharacterBody2D. This states that your script is taking all the functionality of the built-in CharacterBody2D object and extending it with additional functionality created by you.\nIn the rest of the script, you can define any number of variables (aka “class properties”) and functions (aka “class methods”).\nCreating a script Let’s make our first script. Remember, any node can have a script attached to it.\nOpen the editor and add a Sprite2D node to empty scene. Right-click on the new node, and choose “Attach Script”. You can also click the button next to the search box.\nNext you need to decide where you want the script saved and what to call it. If you’ve named the node, the script will automatically be named to match it (so unless you’ve changed anything this script will likely be called “Sprite.gd”).\nNow the script editor window opens up, and this is your new, empty sprite script. Godot has automatically included some lines of code, as well as some comments describing what they do.\nextends Sprite2D # Called when the node enters the scene tree for the first time. func _ready(): pass # Replace with function body. # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): pass Since the script was added to a Sprite2D, the first line is automatically set to extends Sprite2D. Because this script extends the Sprite2D class, it will be able to access and manipulate all the properties and methods that a Sprite2D node provides.\nProperties and methods Properties and methods are two terms which specifically mean variables and functions that are defined in an object. Programmers tend to use them interchangeably.\nAfter that is where you’re going to define all the variables you will use in the script, the “member variables”. You define variables with the ‘var’ keyword - as you can see by the comment examples.\nGo ahead and delete the comments and let’s talk about this next piece.\nNow we see a function called _ready(). In GDScript you define a function with the keyword “func”. The _ready() function is a special one that Godot looks for and runs whenever a node is added to the tree, for example when we hit “Play”.\nLet’s say that when the game starts, we want to make sure the sprite goes to a particular location. In the Inspector, we want to set the Position property. Notice that it’s in the section called “Node2D” - that means this is a property that any Node2D type node will have, not just Sprite2Ds.\nHow do we set the property in code? One way to find the name of the property is by hovering over it in the Inspector.\nGodot has a great built-in help/reference tool. Click on “Classes” at the top of the Script window and search for Node2D and you’ll see a help page showing you all the properties and methods the class has available. Looking down a bit you can see position in the “Member Variables” section - that’s the one we want. It also tells us the property is of the type “Vector2”.\nLet’s go back to the script and use that property:\nfunc _ready(): position = Vector2(100, 150) Notice how the editor is making suggestions as you type. Godot uses vectors for lots of things, and we’ll talk more about them later. For now, let’s type Vector2, and the hint tells us to put two floats for x and y.\nNow we have a script that says “When this sprite starts, set its position to (100, 150)”. We can try this out by pressing the “Play Scene” button.\nTip When first learning to code, beginners often ask “How do you memorize all these commands?” Just like any other skill, it’s not a matter of memorization, it’s about practice. As you use things more, the things you do frequently will “stick” and become automatic. Until then, it’s a great idea to keep the reference docs handy. Use the search function whenever you see something you don’t recognize. If you have multiple monitors, keep a copy of the web docs open on the side for quick reference.\nWrapping up Congratulations on making your first script in GDScript! Before moving on, make sure you understand everything we did in this step. In the next part, we’ll add some more code to move the sprite around the screen.\n","description":"","tags":null,"title":"Getting started","uri":"/godot_recipes/4.x/g101/gdscript/gdscript_01/index.html"},{"content":"Getting Started Have you downloaded Godot yet? You can get it here:\nhttps://godotengine.org\nUpdating to Godot 4.0 We’re working on a new version of Godot 101 for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.\nIn this section: What is Godot? The Godot Editor: Finding your way around Nodes: Godot's building blocks ","description":"","tags":null,"title":"Getting Started","uri":"/godot_recipes/4.x/g101/start/index.html"},{"content":"Linear Interpolation, or its commonly-used abbreviation lerp, is a term that comes up often in game development. If you’ve never come across it before it can seem mysterious and highly-technical, but as you’ll see in this tutorial, it’s actually a straightforward concept with a wide variety of applications in game programming.\nNumeric Interpolation The core formula for linear interpolation is this:\nfunc lerp(a, b, t): return (1 - t) * a + t * b In this formula, a and b represent the two values and t is the amount of interpolation, typically expressed as a value between 0 (which returns a), and 1 (which returns b). The function finds a value the given amount between the two. For example:\nx = lerp(0, 1, 0.75) # x is 0.75 x = lerp(0, 100, 0.5) # x is 50 x = lerp(10, 75, 0.3) # x is 29.5 x = lerp(30, 2, 0.75) # x is 9 It’s called linear interpolation because the path between the two points is a straight line.\nYou can animate a node’s properties with lerp(). For example, if you divide the elapsed time by the desired duration, you’ll get a value between zero and one you can use to alter a property smoothly over time. This script scales a sprite up to five times its starting size while fading it out (using modulate.a) over two seconds:\nextends Sprite2D var time = 0 var duration = 2 # length of the effect func _process(delta): if time \u003c duration: time += delta modulate.a = lerp(1, 0, time / duration) scale = Vector2.ONE * lerp(1, 5, time / duration) Vector interpolation You can also interpolate between vectors. Both Vector2 and Vector3 provide linear_interpolate() methods for this.\nFor example, to find a vector that’s halfway between a Spatial node’s forward and left direction vectors:\nvar forward = -transform.basis.z var left = transform.basis.x var forward_left = forward.linear_interpolate(left, 0.5) The following example moves a Sprite node towards the mouse click position. Each frame the node moves 10% of the way to the target. This results in an “approach” effect, where the object’s speed becomes slower the closer it gets to the target.\nextends Sprite2D var target func _input(event): if event is InputEventMouseButton and event.pressed: target = event.position func _process(delta): if target: position = position.linear_interpolate(target, 0.1) For more advanced applications of interpolation, see Tween.\n","description":"","tags":null,"title":"Interpolation","uri":"/godot_recipes/4.x/math/interpolation/index.html"},{"content":"Problem You need to make a 2D platform-style character.\nSolution New developers are often surprised at how complex a platform character can be to program. Godot provides some built-in tools to assist, but there are as many solutions as there are games. In this tutorial, we won’t be going in-depth with features like double-jumps, crouching, wall-jumps, or animation. Here we’ll discuss the fundamentals of platformer movement. See the rest of the recipes for other solutions.\nTip While it’s possible to use RigidBody2D to make a platform character, we’ll be focusing on CharacterBody2D. Kinematic bodies are well-suited for platformers, where you are less interested in realistic physics than in responsive, arcade feel.\nStart with a CharacterBody2D node, and add a Sprite2D and CollisionShape2D to it.\nAttach the following script to the root node of the character. Note that we’re using input actions we’ve defined in the InputMap: \"walk_right\", \"walk_left\", and \"jump\". See InputActions.\nextends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 func _physics_process(delta): # Add gravity every frame velocity.y += gravity * delta # Input affects x axis only velocity.x = Input.get_axis(\"walk_left\", \"walk_right\") * speed move_and_slide() # Only allow jumping when on the ground if Input.is_action_just_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed The values used for speed, gravity, and jump_speed depend greatly on the size of your player sprite. The player’s texture in this example is 108x208 pixels. If your sprite is smaller, you’ll want to use smaller values. We also want high values so that everything feels fast and responsive. A low gravity results in a floaty-feeling game while a high value means you’re quickly back on the ground and ready to jump again.\nNote that we’re checking is_on_floor() after using move_and_slide(). The move_and_slide() function sets the value of this method, so it’s important not to check it before, or you’ll be getting the value from the previous frame.\nFriction and acceleration The above code is a great start, and you can use it as the foundation for a wide variety of platform controllers. One problem it has, though, is the instantaneous movement. For a more natural feel, it’s better if the character has to accelerate up to its max speed and that it coasts to a stop when there is no input.\nOne way to add this behavior is to use linear interpolation (“lerp”). When moving, we will lerp between the current speed and the max speed and while stopping we’ll lerp between the current speed and 0. Adjusting the lerp amount will give us a variety of movement styles.\nTip For an overview of linear interpolation, see Gamedev Math: Interpolation.\nextends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 @export_range(0.0, 1.0) var friction = 0.1 @export_range(0.0 , 1.0) var acceleration = 0.25 func _physics_process(delta): velocity.y += gravity * delta var dir = Input.get_axis(\"walk_left\", \"walk_right\") if dir != 0: velocity.x = lerp(velocity.x, dir * speed, acceleration) else: velocity.x = lerp(velocity.x, 0.0, friction) move_and_slide() if Input.is_action_just_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed Try changing the values for friction and acceleration to see how they affect the game’s feel. An ice level, for example, could use very low values, making it harder to maneuver.\nConclusion This code gives you a starting point for building your own platformer controller. For more advanced platforming features such as wall jumps, see the other recipes in this section.\nDownload This Project Download the project code here: https://github.com/godotrecipes/2d_platform_basic\n","description":"","tags":null,"title":"Platform character","uri":"/godot_recipes/4.x/2d/platform_character/index.html"},{"content":"This first game project will guide you through making your first Godot Engine game. While you don’t need any previous experience, it’s expected that you’ve at least read through the Godot 101: Getting Started section. There, you’ll learn about the editor interface and how to get around the Godot UI.\nWhy start with 2D? In a nutshell, 3D games are much more complex than 2D ones. However, many of the underlying game engine features you’ll need to know are the same. You should stick to 2D until you have a good understanding of Godot’s workflow. At that point, the jump to 3D will feel much easier.\nOpen Godot and start a new project. You can name it anything you’d like - we’re going with “Classic Shmup”, since this is a traditional shoot-em-up style game.\nDownloading the art You can download the art we’ll be using for the game from itch.io: Mini Pixel Pack by Grafxkid\nUnzip the art pack and copy it into your project by dropping the folder in the FileSystem tab.\nProject settings Next, we need to set up some project-wide settings. Open Project Settings and check the “Advanced Settings” toggle in the upper-right.\nIn the Display/Window section:\nViewport Width \u0026 Viewport Height to 240, 320. Window Width Override \u0026 Window Height Override to 480, 640. Stretch/Mode to canvas_items. These settings will ensure the game is the right size. Because we’re using pixel art, the images themselves are very small, so an old-school resolution like 240x320 is perfect. However, on a modern monitor, that’s a fairly small window, so the other settings let us scale that up proportionally. If you have a 1080p monitor, you can make the override values 720x960 instead. You’ll also be able to resize the window when the game is running.\nIn the Rendering/Textures section under Canvas Textures, set Default Texture Filter to Nearest. This will ensure that our beautiful pixel art stays nice and crisp, looking like the image on the right, not the one on the left: Click the Input Map tab at the top of the Project Settings window. This is where we can set up the inputs we want to use in the game. In the “Add New Action” box, type the following, hitting \u003center\u003e after each to add it to the list of actions: right, left, up, down, shoot. To assign key(s) to each named input, click the + button to its right and press the key on your keyboard. When you’re done, you should have something like this: Feel free to use other keys if you’d rather use a different setup.\nNext steps That takes care of setting up - now we’re ready to get started! In the next section, we’ll create the player-controlled spaceship.\n","description":"","tags":null,"title":"Project Setup","uri":"/godot_recipes/4.x/games/first_2d/first_2d_01/index.html"},{"content":"Problem You want to allow the player to “wrap around” the screen, teleporting from one side of the screen to the other. This is a common feature, especially in old-school 2D games (think Pac-man).\nSolution Get your screen (viewport) size\n@onready var screen_size = get_viewport_rect().size get_viewport_rect() is available to any CanvasItem derived node.\nCompare your player’s position\nif position.x \u003e screen_size.x: position.x = 0 if position.x \u003c 0: position.x = screen_size.x if position.y \u003e screen_size.y: position.y = 0 if position.y \u003c 0: position.y = screen_size.y Note that this is using the node’s position, which is usually the center of your sprite and/or body.\nSimplifying with wrapf()\nThe above code can be simplified using GDScript’s wrapf() function, which “loops” a value between the given limits.\nposition.x = wrapf(position.x, 0, screen_size.x) position.y = wrapf(position.y, 0, screen_size.y) ","description":"","tags":null,"title":"Screen wrap","uri":"/godot_recipes/4.x/2d/screen_wrap/index.html"},{"content":"Problem You want to use a spritesheet containing 2D animations.\nSolution Spritesheets are a common way for 2D animations to be distributed. In a spritesheet, all of the animation frames are packed into a single image.\nFor this demo, we’ll be using the excellent “Adventurer” sprite by Elthen. You can get this and lots of other great art athttps://elthen.itch.io/.\nWarning Make sure the images in your spritesheet are laid out in a constant-sized grid. This will enable Godot to automatically slice them. If they’re packed irregularly, you will not be able to use the following technique.\nNode setup This animation technique uses a Sprite2D node to display the texture, and then we animate the changing frames with AnimationPlayer. This can work with any 2D node, but for this demo, we’ll use a CharacterBody2D.\nAdd the following nodes to your scene:\nCharacterBody2D: Player Sprite2D CollisionShape2D AnimationPlayer Drag the spritesheet texture into the Texture property of the Sprite2D. You’ll see the entire spritesheet displayed in the viewport. To slice it up into individual frames, expand the “Animation” section in the Inspector and set the Hframes to 13 and Vframes to 8. Hframes and Vframes are the number of horizontal and vertical frames in your spritesheet.\nTry changing the Frame property to see the image change. This is the property we’ll be animating.\nAdding animations Select the AnimationPlayer and click the “Animation” button followed by “New\" . Name the new animation “idle”. Set the animation length to 2 and click the “Loop” button so that our animation will repeat (see below).\nWith the scrubber at time 0, select the Sprite2D node. Set its Animation/Frame to 0, then click the key icon next to the value.\nIf you try playing the animation, you’ll see it doesn’t appear to do anything. That’s because the last frame (12) looks the same as the first (0), but we’re not seeing any of the frames in-between (1-11). To fix this, change the “Update Mode” of the track from its default value of “Discrete” to “Continuous”. You can find this button at the end of the track on the right side.\nNote that this will only work for spritesheets where the frames are already in order. If they are not, you’ll have to keyframe each Frame seperately along the timeline.\nFeel free to add the other animations yourself. For example, the “jump” animation is on frames 65 through 70.\nRelated recipes Platform character ","description":"","tags":null,"title":"Spritesheet animation","uri":"/godot_recipes/4.x/animation/spritesheet_animation/index.html"},{"content":"In this tutorial, we’ll look at how to start working in 3D in Godot. You’ll learn how to navigate in the 3D editor, how to create and manipulate 3D objects, and how to work with some of Godot’s essential 3D nodes, such as cameras and lighting.\nAre you ready? A word of warning: 3D development can be quite a bit more complex than working in 2D. While many of the same principles apply - such as working with nodes, writing scripts, and handling logic/data - 3D brings with it a number of other considerations. For this reason, it’s a good idea to stick to 2D for your first few projects, moving to 3D once you have a good understanding of the game development process. This tutorial will assume you have completed at least an introductory Godot 2D project, such as the one in the [official Godot tutorial] (https://docs.godotengine.org/en/stable/getting_started/step_by_step/your_first_game.html).\nGetting Started in 3D One of Godot’s strengths is its ability to handle both 2D and 3D games. While much of what you’ve learned working on 2D projects (nodes, scenes, signals, etc.) applies equally well in 3D, there is also a whole new layer of complexity and capabilities. First, you’ll find that there are some additional features available in the 3D editor window, so we’ll start there:\nOrienting in 3D Space When you first open a new project in Godot, you will see the 3D project view:\nThe first thing you should notice is the three colored lines in the center. These are the x (red), y (green), and z (blue) axes. The point where they meet is the origin, which has the coordinates (0, 0, 0). You’ll find that this color scheme will also apply elsewhere in the Inspector.\nNote Different 3D applications follow different conventions for orientation. Godot uses Y-Up orientation, so that when looking at the axes, if x is pointing to the left/right, then y is up/down, and z is forward/back. Some other popular 3D software uses Z-UP. It’s good to keep this in mind when moving between applications.\nNavigation in 3D is performed using the mouse and keyboard. Here are the basic controls for the view camera:\nMousewheel up/down: zoom in/out Middle button + drag: orbit camera around current target Shift + middle button + drag: pan camera Right-click + drag: rotate camera in place In addition, if you’re familiar with popular 3D games, you might prefer Freelook mode, which you can toggle on/off using Shift+F. In this mode, you can use the WASD keys to fly around the scene while aiming with the mouse.\nYou can also alter the camera’s view by clicking on the [Perspective] label in the upper-left corner. Here, you can snap the camera to a particular orientation.\nAdding 3D Objects Now let’s add our first 3D node. Just as all 2D nodes inherit from Node3D, which provides properties such as position and rotation, 3D nodes inherit from Node3D, which provides 3D versions of the same properties. Add one to your scene and you’ll see the following object appear at the origin:\nThis object is not the node. It is something called a 3D gizmo. Gizmos are tools that allow you to move and rotate objects in space. The three rings control rotation, while the three arrows move (translate) the object along the three axes. Note that the rings and arrows are color-coded to match the axis colors.\nTake a few minutes to experiment and get familiar with the gizmo. Use Undo if you find yourself getting lost.\nTip Sometimes you may feel the gizmos are getting in your way. You can click on the mode icons to restrict yourself to only one type of transformation: move, rotate, or scale: Global vs. Local Space By default, the gizmo controls operate in global space. When you rotate the object, the gizmo’s arrows still point along the axes. However, if you click the Use Local Space button, the gizmo will switch to moving the body in local space.\nNow when you rotate the object, the gizmo arrows point along the object’s axes and not the world’s. Switching back and forth between Local and World space can make it much easier to place an object exactly where you want it.\nTransforms Look at the Inspector for the Node3D node. In the Transform section, you’ll see properties for Position, Rotation, and Scale. Drag the object around with the gizmo and observe how these values change. Just like in 2D, these properties are relative to the node’s parent.\nTogether, these properties make up the node’s transform. When changing the node’s spatial properties in code, you’ll access the transform property, which is a Godot Transform3D object. It has two properties: origin and basis. The origin represents the body’s position, while the basis contains three vectors that define the body’s local coordinate axes - think of the three axis arrows in the gizmo when you’re in Local Space mode.\nYou’ll see how to use these properties later in this section.\nMeshes Just like a Node2D, a Node3D has no size or appearance of its own. In 2D, you would use a Sprite2D to add a texture to the node. In 3D, you need to add a mesh. A mesh is a mathematical description of a shape. It consists of a collection of points, called vertices. These vertices are connected by lines, called edges, and multiple edges (at least three) together make a face.\nFor example, a cube is made up of 8 vertices, 12 edges, and 6 faces.\nAdding Meshes Typically, meshes are created by using 3D modeling software, such as Blender. You can also find many collections of 3D models available for download, if you’re unable to create your own. However, often you just need a basic shape such as a cube or sphere. In this case, Godot provides a way to create simple meshes called primitives.\nAdd a MeshInstance3D node as a child of the Node3D and in the Inspector, click its Mesh property:\nHere you can see the list of available primitives. They represent a handy collection of common useful shapes. Select “New BoxMesh” and you’ll see a plain cube appear on the screen.\nCameras Try running the scene with your cube object. Did you see anything? In 3D, you won’t see anything in the game viewport without adding a Camera3D. Add one to the root node and use the camera’s gizmo to position it pointing towards the cube:\nThe pinkish-purple pyramid shape on the camera is called the fustrum and represents the camera’s view. Notice the small triangular arrow which represents the camera’s “up” orientation. As you’re moving the camera around, try pressing the Preview button in the upper-left to see what the camera sees. Play the scene to verify everything is working as expected.\nWrapping Up In this tutorial you learned how to use Godot’s 3D editor, how to add 3D nodes such as Node3D, MeshInstance3D, and Camera3D, and how to use gizmos to place your objects. You also learned a bunch of new terminology. Hopefully you’re not overwhelmed.\nIn the next part, we’ll look at how to build a 3D scene by importing 3D assets and how to use more of Godot’s 3D nodes.\n","description":"","tags":null,"title":"The 3D Editor","uri":"/godot_recipes/4.x/g101/3d/101_3d_01/index.html"},{"content":"Problem You’re making a 2D top-down game, and you want to control a character’s movement.\nSolution For this solution, we’ll assume you have the following input actions defined:\nAction Name Key(s) \"up\" W,↑ \"down\" S,↓ \"right\" D,→ \"left\" A,← \"click\" Mouse button 1 We will also assume you’re using a CharacterBody2D node.\nWe can solve this problem in many ways, depending on what type of behavior you’re looking for.\nOption 1: 8-way movement In this scenario, the player uses the four directional keys to move (including diagonals).\nextends CharacterBody2D var speed = 400 # speed in pixels/sec func _physics_process(delta): var direction = Input.get_vector(\"left\", \"right\", \"up\", \"down\") velocity = direction * speed move_and_slide() Option 2: Rotate and move In this scenario, the left/right actions rotate the character and up/down move the character forward and back in whatever direction it’s facing. This is sometimes referred to as “Asteroids-style” movement.\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec var rotation_speed = 1.5 # turning speed in radians/sec func _physics_process(delta): var move_input = Input.get_axis(\"down\", \"up\") var rotation_direction = Input.get_axis(\"left\", \"right\") velocity = transform.x * move_input * speed rotation += rotation_direction * rotation_speed * delta move_and_slide() Note Godot considers an angle of 0 degrees to be pointing along the x axis. This means that a node’s forward direction (transform.x) is to the right. You should ensure that your character’s sprite is also drawn pointing to the right.\nOption 3: Aim with mouse Similar to option 2, but this time the character rotation is controlled with the mouse (ie the character always points towards the mouse). Forward/back movement is done with the keys as before.\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec func _physics_process(delta): look_at(get_global_mouse_position()) var move_input = Input.get_axis(\"down\", \"up\") velocity = transform.x * move_input * speed move_and_slide() Option 4: Click and move In this option, the character moves to the clicked location.\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec var target = null func _input(event): if event.is_action_pressed(\"click\"): target = get_global_mouse_position() func _physics_process(delta): if target: # look_at(target) velocity = position.direction_to(target) * speed if position.distance_to(target) \u003c 10: velocity = Vector2.ZERO move_and_slide() Note that we stop moving if we get close to the target position. If you don’t do this, the character will “jiggle” back and forth as it moves a little bit past the target, moves back, goes a little past it, and so on. Optionally, you can use look_at() to face in the direction of movement.\nDownload This Project Download the project code here: https://github.com/godotrecipes/topdown_movement\n","description":"","tags":null,"title":"Top-down movement","uri":"/godot_recipes/4.x/2d/topdown_movement/index.html"},{"content":"Problem You need to understand in what order Godot handles nodes in the scene tree.\nSolution “Tree order” is mentioned often in the Godot docs and in tutorials. However, it is not always obvious to a beginner what is meant by this. Generally speaking, the order in which nodes are handled in the tree is in top-down fashion, starting at the root and going down each branch in turn.\nScene tree order is something that can cause a great deal of confusion for Godot beginners. In this example, we’ll illustrate in what order things happen.\nHere’s our sample node setup:\nOn each node, we have the following script attached:\nextends Node func _init(): # Note: a Node doesn't have a \"name\" yet here. print(\"TestRoot init\") func _enter_tree(): print(name + \" enter tree\") func _ready(): print(name + \" ready\") # This ensures we only print *once* in process(). var test = true func _process(delta): if test: print(name + \" process\") test = false Before we talk about the results, let’s review what each of these callback functions represents:\n_init() is called when the object is first created. It now exists in the computer’s memory.\n_enter_tree() is called when the node first enters the tree. This can be when instancing or when add_child() is used, for example.\n_ready() is called when the node and its children have all been added to the tree and are ready.\n_process() is called every frame (typically 60 times per second) on every node in the tree.\nIf we ran this on a single node all by itself, the order would be as you might expect:\nTestRoot init TestRoot enter tree TestRoot ready TestRoot process Once we add children to the mix, it becomes a bit more complex, and probably needs some clarification:\nTestRoot init TestChild1 init TestChild3 init TestChild2 init TestRoot enter tree TestChild1 enter tree TestChild3 enter tree TestChild2 enter tree TestChild3 ready TestChild1 ready TestChild2 ready TestRoot ready TestRoot process TestChild1 process TestChild3 process TestChild2 process As you can see, all of these nodes printed their messages in tree order, from top to bottom, following branches first - with the exception of the _ready() code.\nHere’s a quote from the Node reference:\nCalled when the node is “ready”, i.e. when both the node and its children have entered the scene tree. If the node has children, their _ready callbacks get triggered first, and the parent node will receive the ready notification afterwards.\nThis leads to an important rule-of-thumb to remember when setting up your node structure:\nTip Parent nodes should manage their children, not vice-versa.\nThis means any code in the parent must be able to fully access any data in its children. For that reason, _ready() must be processed in reverse tree order.\nRemember this when trying to access other nodes in _ready(). If you need to go up the tree to a parent (or grandparent), you should probably run that code in the parent rather than the child.\nRelated recipes Understanding node paths ","description":"","tags":null,"title":"Understanding tree order","uri":"/godot_recipes/4.x/basics/tree_ready_order/index.html"},{"content":"Game Engines Game development is complex and involves a wide variety of knowledge and skills. In order to build a modern game, you need a lot of underlying technology before you can make the actual game itself. Imagine if you had to build your own computer and write your own operating system before you could even start programming. Game development would be a lot like that if you truly had to start from scratch and build everything you needed.\nIn addition, there are a number of common needs every game has. For example, no matter what your game is, it’s going to need to draw things on the screen. If the code to do that has already been written, it makes more sense to reuse it that to create it all over again for every game. This is where game engines come in.\nA game engine is a collection of tools and technologies designed to assist in developing games. This allows you to focus more on building your game, and less on reinventing the wheel. Here are some of the features a good game engine will provide:\nRendering (2D/3D) “Rendering” is the process of displaying your game on the player’s screen. A good rendering pipeline needs to work with modern GPU features, high resolution displays, and effects like lighting and perspective, while maintaining a high frame rate.\nPhysics Building an accurate and usable physics engine is an enormous task. Most games require some sort of collision detection and response, and many need simulated physics (ie. friction, inertia, etc.), but few developers want to take on the task of writing one.\nPlatform Support In today’s market, you want to be able to release your game on multiple platforms, such as mobile, web, PC, and/or console. A game engine lets you build your game once and export it to one or more platforms.\nDevelopment Environment All of these tools are brought together in a single application, combining everything into one environment so you don’t have to learn a new workflow for every new project.\nThere are dozens of popular game engines to choose from today, such as Unity, Unreal, and GameMaker Studio, to name a few. It is important to remember that the majority of popular engines are commercial products. They may or may not be free to download, but the will require some kind of licensing or royalty agreement if you plan to release your game (and especially if your game makes money). You need to carefully read and understand what you’re agreeing to and what you are and are not allowed to do with the engine.\nWhy use Godot? Click here to download Godot\nIn contrast to the above, Godot is completely free and open source, released under the very permissive MIT license. This means there are no fees, hidden costs, or royalties you need to pay. This is in addition to being a fully featured modern game engine.\nAs a developer, the benefits are great. Because it’s unencumbered by commercial licensing, you have complete control over exactly how and where your game is distributed. In addition, Godot’s open source nature also means there is a much greater level of transparency than you’ll find with commercial engines. For example, if you find a particular feature doesn’t quite meet your needs, you’re free to modify the engine itself - no permission required.\n","description":"","tags":null,"title":"What is Godot?","uri":"/godot_recipes/4.x/g101/start/101_01/index.html"},{"content":" Your First 2D Game Get started with Godot by building a 2D shooter. In this series, we’ll start with the basics and build a classic, old-school space shooter.\nHere’s a screenshot of the finished game:\nIn each part of the series, we’ll build a piece of the game, adding features and explaining the process along the way.\nBackground If you find that you’re struggling with the programming side of things, see these resources:\nGodot 101: Introduction to GDScript - tutorial on this website. Godot Official Documentation - official tutorial resources Download This Project on GitHub Download the project code here:\nhttps://github.com/godotrecipes/classic_shmup\n","description":"","tags":null,"title":"Your First 2D Game","uri":"/godot_recipes/4.x/games/first_2d/index.html"},{"content":"Problem You need to make a first-person shooter (FPS) character.\nSolution Start with a CharacterBody3D node, and add a CollisionShape3D to it. The CapsuleShape3D collision shape is the most common choice. Depending on your world setup, you may want to add additional shapes here, but for the purposes of this example, we’ll stick to the basics.\nWe’ll leave all the sizing at the default values, meaning the capsule will be 2 meters high. Move it up by 1.0 m to align its bottom with the ground.\nNext, add a Camera3D as a child of the body and move it up about 1.6 m.\nWhere’s the body? For this example, we’ll leave the character “bodyless” - meaning we’re not adding a mesh to display for the player’s body. Depending on your setup, you may or may not need to see the player’s body.\nAttach a script to the body and start by defining some properties:\nextends CharacterBody3D var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var speed = 5 var jump_speed = 5 var mouse_sensitivity = 0.002 The _physics_process() function is the place to handle movement. Note that Input.get_vector() returns a 2-dimensional vector based on the combination of the forward/back/left/right keys. We want to use this vector to set the x and z components of the body’s velocity (because y is handled by gravity). Multiplying this vector by the body’s basis ensures we account for rotation - forward should always be the body’s forward vector.\nfunc _physics_process(delta): velocity.y += -gravity * delta var input = Input.get_vector(\"left\", \"right\", \"forward\", \"back\") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed move_and_slide() if is_on_floor() and Input.is_action_just_pressed(\"jump\"): velocity.y = jump_speed Don’t forget to add the input actions to your Input Map using the keys/inputs you prefer (W/A/S/D is typical, or you can use joystick axes if you prefer a controller).\nAdd the player to a “World” scene where you’ve created some StaticBody3D nodes for the floor and some walls.\nWhen you try to move, you’ll notice you can move forward/back and left/right, but you can’t rotate. That’s what we’ll handle next.\nMouse control in 3D First, we need the player to rotate left/right when we move the mouse the same way. Mouse input is represented in 2D, relative to the screen, so we need the x movement of the mouse to rotate the player’s body around its y (vertical) axis. The mouse_sensitivity property we defined above lets us adjust how many pixels of mouse movement translate to a degree of rotation.\nfunc _input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) Try the code again, and you’ll see that you can now rotate with the mouse. However, you may find your mouse running outside the game window. This is the perfect time to add some code to capture your mouse. See Input: Capturing the Mouse for details.\nOur updated code then becomes\nfunc _input(event): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) Finally, to look up/down, we’ll use the y motion of the mouse to tilt the camera. We don’t want it to turn completely upside-down, though, so we’ll clamp() the rotation to a reasonable value of 70 degrees.\nfunc _input(event): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) $Camera3D.rotate_x(-event.relative.y * mouse_sensitivity) $Camera3D.rotation.x = clampf($Camera3D.rotation.x, -deg_to_rad(70), deg_to_rad(70)) Holding a weapon An FPS character typically has a 3D mesh of a weapon positioned in front. Setting this up can be easy with a couple of Godot editor tricks.\nAdd your weapon mesh as a child of the Camera3D. Then, in the editor view menu, choose “2 Viewports” and set one of them to preview the camera. Then, you can move around the weapon and easily see how it will look from the player’s perspective.\nTo add a little personality, try using an AnimationPlayer to animate the weapon’s position from side-to-side as the player moves.\nRelated recipes Input: Capturing the Mouse Download This Project Download the project code here: https://github.com/godotrecipes/basic_fps\n","description":"","tags":[],"title":"Basic FPS Character","uri":"/godot_recipes/4.x/3d/basic_fps/index.html"},{"content":" Basics Basic Godot tips and tricks that apply to any project.\nIn this section: Understanding tree order Node communication (the right way) Understanding node paths Understanding 'delta' Saving/loading data Migrating from 3.x ","description":"","tags":null,"title":"Basics","uri":"/godot_recipes/4.x/basics/index.html"},{"content":"In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.\nSetting up the Ship Scene A common part of the Godot workflow is creating scenes. As discussed earlier, a scene in Godot is nothing more than a collection of nodes. In most Godot projects, each game object is configured as a scene, with nodes that provide it with the desired functionality, and optionally some code to customize its behavior.\nChoosing nodes The first step is to decide what kind of node to start with. The first node you add to the scene is called the root node. A scene’s root node should generally be the one that primarily defines the game object’s behavior. Then you attach child nodes to add additional functionality.\nSo what should our game’s ship be? Let’s break down the requirements, and look at what nodes might be useful to meet them.\nThe ship needs to:\nMove in 2D space. For this, a basic Node2D would suffice, as that’s the node that has position, rotation, and other 2D-related properties. However, it has no appearance.\nDisplay an image. Sprite2D is the node for this. Since it’s also a Node2D, we’d still be able to move it around.\nDetect getting hit. The enemies will be shooting and flying around on the screen, so we’ll need to know when ship is hit. We don’t have a need for solid objects - they’re not going to bounce off each other or transfer momentum - we just need to know when they touch. For this, an Area2D would be perfect. It can detect touching other objects, has positional properties, but it has no appearance of its own.\nLooking at this list, the Area2D provides the main functionality. We can attach a Sprite2D to display the ship image, and then we’ll have everything we need.\nBuilding the scene In the Scene tab, click the + button or the + Other Node button to add the first node. Start typing Area2D and choose it from the list. Once it’s in the Scene tab, click the node’s name to rename it to Player, and press \u003cCtrl+S\u003e to save the scene.\nDisplaying the ship With the Player node selected, add another node: a Sprite2D. To keep things organized, let’s rename this node to Ship.\nFrom the FileSystem tab, drag the Player_ship (16x16).png file from the art pack and drop it in the Texture property of the Inspector.\nThe first thing you’ll notice is that there seem to be three ships! The image from the art pack also includes versions of the ship going to the left/right. We can use this - in the Animation section of the Inspector, set Hframes to 3. Now, changing the Frame property will move between the three different versions. Leave it at 1 for now.\nAdding a collision shape You may also have noticed the yellow warning triangle on the Area2D node. If you click it, you’ll see the warning is telling us that the area doesn’t have a shape. We need to define its shape, and we can do that by adding a CollisionShape2D node as a child of the Player.\nIn the Inspector for this node, you’ll see a Shape property that currently shows \u003cempty\u003e. If you click in this box, you’ll see a dropdown that allows you to select from a variety of shapes. Choose New RectangleShape2D and you’ll see a light blue square appear over the ship. You can adjust the size of the shape by dragging the orange circles, or you can click on the shape in the Shape property to expand it and fill in the Size manually.\nExhaust The ship will look much more dynamic with a little animation. Included in the art pack are some animations of exhaust flames named “Boosters”. There are three: one for each version of the ship (left, forward, and right).\nTo display these, select the Ship node and add a child AnimatedSprite2D node and name it “Boosters”.\nIn the Inspector, under the Animation section, you’ll find a property called Sprite Frames, which is currently \u003cempty\u003e. Click it to create a New SpriteFrames, then click the SpriteFrames item to open the animation panel at the bottom of the editor window.\nDouble-click the “default” animation to rename it to “forward”. Then, to add the animation images, click the Add frames from sprite sheet button:\nChoose the Boosters (16 x 16).png image and you’ll see the Select Frames window, allowing you to choose the frames you want.\nThere are only two frames in this animation, but the grid isn’t correct. Change the Size values to match the image sizes: 16 x 16. Then, click both frames to select them and click the Add 2 Frame(s) button.\nNow that you’ve added the two frames, press the Play button to run the animation. You can also toggle the Autoplay on Load button so that the animation will start automatically.\nIt’s a little slow, so change the speed to 10 FPS.\nAdd two more animations by clicking the Add Animation button, naming them left and right.\nRepeat the process, adding the left and right “Booster” sprite sheets.\nGun cooldown The last node we’ll need to complete the player setup is a Timer to control how fast the player can shoot. Add the Timer as a child of Player and name it GunCooldown. Set its One Shot property to “On”. This means that when the timer ends, it won’t automatically restart. In the player’s code, we’ll start the timer when the player shoots, and they won’t be able to shoot again until the timer runs out.\nNext steps That completes the player scene setup. We’ve added the nodes to give the player ship the functionality it will need in the game. In the next section, we’ll add some code to enable the player to control the ship, make it shoot, and detect when it collides with things.\n","description":"","tags":null,"title":"Designing the Player Scene","uri":"/godot_recipes/4.x/games/first_2d/first_2d_02/index.html"},{"content":"Problem You need a 2D character that moves in a grid pattern.\nSolution Grid- or tile-based movement means the character’s position is restricted. They can only stand on a particular tile - never between two tiles.\nCharacter setup Here are the nodes we’ll use for the player:\nArea2D (“Player”): Using an Area2D means we can detect overlap (for picking up objects or colliding with enemies). Sprite2D: You can use a sprite sheet here (we’ll set up the animation below). CollisionShape2D: Don’t make the hitbox too big. Since the player will be standing on the center of a tile, overlaps will be from the center. RayCast2D: For checking if movement is possible in the given direction. AnimationPlayer: For playing the character’s walk animation(s). Add some input actions to the Input Map. We’ll use “up”, “down”, “left”, and “right” for this example.\nBasic movement We’ll start by setting up the tile-by-tile movement, without any animations or interpolation.\nextends Area2D var tile_size = 64 var inputs = {\"right\": Vector2.RIGHT, \"left\": Vector2.LEFT, \"up\": Vector2.UP, \"down\": Vector2.DOWN} tile_size should be set to match the size of your tiles. In a larger project, this can be set by your main scene when instancing the player. We’re using 64x64 tiles in the example below.\nThe inputs dictionary maps the input action names to direction vectors. Make sure you have the names spelled the same here and in the Input Map (capitalization counts!).\nfunc _ready(): position = position.snapped(Vector2.ONE * tile_size) position += Vector2.ONE * tile_size/2 snapped() allows us to “round” the position to the nearest tile increment, and adding a half-tile amount makes sure the player is centered on the tile.\nfunc _unhandled_input(event): for dir in inputs.keys(): if event.is_action_pressed(dir): move(dir) func move(dir): position += inputs[dir] * tile_size Here’s the actual movement code. When an input event occurs, we check the four directions to see which one matched, then pass it to move() to change the position.\nCollision Now we can add some obstacles. You can add StaticBody2Ds to manually add some obstacles (enable snapping to make sure they’re aligned with the grid) or use a TileMap (with collisions defined), as in the example below.\nWe’ll use the RayCast2D to determine whether a move to the next tile is allowed.\nonready var ray = $RayCast2D func move(dir): ray.target_position = inputs[dir] * tile_size ray.force_raycast_update() if !ray.is_colliding(): position += inputs[dir] * tile_size When changing a raycast’s target_position property, the physics engine won’t recalculate its collisions until the next physics frame. force_raycast_update() lets you update the ray’s state immediately. If it’s not colliding, then we allow the move.\nNote Another common method is to use 4 separate raycasts, one for each direction.\nAnimating movement Lastly we can interpolate the position between tiles, giving a smooth feel to the movement. We’ll use the Tween node to animate the position property.\nvar animation_speed = 3 var moving = false Add a reference to the Tween node and a variable to set our movement speed.\nfunc _unhandled_input(event): if moving: return for dir in inputs.keys(): if event.is_action_pressed(dir): move(dir) We’ll ignore any input while the tween is running and remove the direct position change so that the tween can handle it.\nfunc move(dir): ray.target_position = inputs[dir] * tile_size ray.force_raycast_update() if !ray.is_colliding(): #position += inputs[dir] * tile_size var tween = create_tween() tween.tween_property(self, \"position\", position + inputs[dir] * tile_size, 1.0/animation_speed).set_trans(Tween.TRANS_SINE) moving = true await tween.finished moving = false Experiment with different tween transitions for different movement effects.\nDownload This Project Download the project code here: https://github.com/godotrecipes/2d_grid_movement/\n","description":"","tags":null,"title":"Grid-based movement","uri":"/godot_recipes/4.x/2d/grid_movement/index.html"},{"content":"In the last part, we started a 3D project and looked at how to navigate and create 3D objects. In this part, you’ll learn how to import existing 3D objects that you’ve made or downloaded and how to use more of Godot’s 3D nodes.\nImporting 3D Objects If you’re familiar with 3D modeling software such as Blender, you can make your own models to use in your game. If not, there are many sources where you can download objects or even collections of objects for particular game types. One of our favorite makers of free game art is Kenney.nl.\nFor our tutorials, we’re going to use Kenney’s Platformer Kit, which you can download here: https://kenney.nl/assets/platformer-kit\nThis kit has a wide selection of objects that we can use to practice our Godot 3D skills. Here’s a sample showing what the kit looks like:\nOnce you’ve downloaded the kit, you’ll find that the objects inside are provided in a variety of different formats. Godot is able to use several of these, but since GLTF is available in this pack, it’s preferred over the others. Drop the GLTF format folder into your Godot project’s folder and rename it to “platformer_kit”.\n3D file formats Whether you create your own models or download the, you’ll need them to be saved in a format that Godot can use. Godot supports the following 3D file formats:\nglTF - supported in both text (.gltf) and binary (.glb) versions DAE (Collada) - an older format that is still supported OBJ (Wavefront) - an older format that is supported, but the format is limited compared to modern options FBX - a commercial format that has limited support glTF is the recommended format - it has the most features and is very well supported in Godot.\nWhen you switch back to your Godot window, you’ll see progress bar while Godot scans the folder and imports all of the objects. Let’s click on one of them to see what’s going on. In the FileSystem tab, double-click on crate.glb:\nHere you can see the object will be imported as a scene, with its root type set to Node3D and named “Scene Root”. Let’s change these: set the root type to RigidBody3D and the root name to “Crate”, then click the “Reimport” button.\nNow right-click on “crate.glb” and choose New Inherited Scene. Here we have a classic game object: the crate. The root node of the scene is a RigidBody3D named “Crate” just as we wanted.\nFinally, we need to add a collision shape to the body. While we could do this by adding a CollionShape3D, as you would typically do in 2D, but there’s a quicker way.\nSelect the crate2 mesh and you’ll see a Mesh menu appear at the top of the viewport. Click it and select Create Single Convex Collision Sibling. Godot will automatically add a CollionShape3D with a collision shape that matches the mesh.\nNow we’re finished setting up the object. Save your Crate scene and let’s see how we can use it.\nBuilding a 3D Scene Create a new scene with a Node3D root. The first child we’ll add is one to give us a “ground” to stack some crates on. Add a StaticBody3D called “Ground”, and to that add a MeshInstance3D. In the Mesh property, select “New BoxMesh” and then click it to open its properties. Set Size to (10, 0.1, 10) so that we have a nice large surface. However, it would look better if it weren’t plain white.\nAlso in the mesh properties is a Material property. Materials are how you define the appearance of an object. Select “New StandardMaterial3D” and then click it to open a large list of properties. To set the color of the mesh, we need the Albedo/Color property. Choose a color, such as brown or dark green.\nIf we add a crate, it will fall right through the mesh, so we also need to give it a collision shape. Add a CollisionShape3D to the Ground and choose “New BoxShape3D”. Set the collision box to the same size as the mesh.\nNow instance a few crates in the scene and arrange them in a rough stack. Add a Camera and place it where it has a good view of the crates. Run the scene and watch your crates go tumbling!\nWhy is the scene so dark? Because there’s no light! By default, Godot doesn’t add any lighting or environment to your scenes, like it does in the editor viewport. This is great when you want to set up your own specific lighting, but for a quick example scene like this, there’s a shortcut.\nLighting There are multiple light nodes available in 3D, which you can use to create a variety of lighting effects. But we’re going to start with DirectionalLight3D. However, instead of adding one manually, we’re going to have Godot use the same one it’s using in the editor window. At the top ove the viewport, there are two icons that control the preview lighting and preview environment. If you click the three dots next to them, you can see their settings.\nClick the Add Sun to Scene button, and Godot will add a DirectionalLight3D to your scene. Click Add Environment to Scene and it will do the same with the preview sky by adding a WorldEnvironment node.\nRun the scene again, and you’ll be able to see your crates falling.\nRotating Camera Let’s make the camera a little more dynamic by having it slowly orbit around the scene. Select the root node and add a Node3D, which will be located at (0, 0, 0) and name it “CameraHub”. In the scene tree, drag the camera to make it a child of this new node. Now, if the CameraHub rotates around the y axis, it will drag the camera along with it.\nAdd a script to the root node and add the following:\nextends Node3D func _process(delta): $CameraHub.rotate_y(0.6 * delta) Run the scene to see what happens.\nWrapping Up In this tutorial you learned how to import 3D objects from outside sources, and how to combine them into a simple scene. We also investigated lights and moving cameras.\nIn the next part, we’ll look at how to build a more complex scene and include a player-controlled character.\n","description":"","tags":null,"title":"Importing 3D Objects","uri":"/godot_recipes/4.x/g101/3d/101_3d_02/index.html"},{"content":"Problem You want to understand Godot’s “input action” system.\nSolution Let’s say you’re making a top-down character and you write code using InputActionKey that uses the arrow keys for movement. You’ll quickly find that many players prefer to use “WASD” style controls. You can go back into your code and add the additional key checks, but this would result in duplicated/redundant code.\nInput actions can help to make your code more configurable. Rather than hard-coding specific keys, you’ll be able to modify and customize them without changing the code.\nCreating inputs You define input actions in the “Project Settings” under the “Input Map” tab. Here, you can create new actions and/or assign inputs to them.\nYou’ll see when you click on the tab there are already some default actions configured. They are all named “ui_*” to indicate that they are the default interface actions. “Tab” for next UI element, for example.\nGenerally speaking, you should create your own actions for your game, rather than use the existing ones.\nFor this example, let’s say you want to allow the player to control the game with the keyboard or the mouse. They need to be able to shoot by pressing either the left mouse button or the spacebar.\nCreate the new action “shoot” by typing the name in the “Action” field at the top and clicking “Add” (or pressing enter). Scroll to the bottom and you’ll see the new action has been added to the list.\nNow you can assign inputs to this action by clicking the “+” sign to the right. Inputs can be keys, mouse buttons, or joy/gamepad inputs. Choose “Key” and you can press the key on the keyboard you want to assign - let’s press the spacebar - and click “OK”.\nClick “+” to add another input, and this time choose “Mouse Button”. The default of “Device 0” and “Left Button” is fine, but you can select others if you like.\nUsing input actions You can check for the action either by polling the Input singleton every frame:\nfunc _process(delta): if Input.is_action_pressed(\"shoot\"): # This will execute every frame as long as the input is held. This is best for continuous actions - i.e. those you want to check constantly, such as movement.\nIf instead you want to detect the action at the moment it occurs, you can use the _input() or _unhandled_input() callbacks:\nfunc _unhandled_input(event): if event.is_action_pressed(\"shoot\"): # This will run once on the frame when the action is first pressed There are several functions you can use for checking input state:\nis_action_pressed(): This function returns true if the action is currently in the pressed state.\nis_action_released(): This function returns true if the action is not In the pressed state.\nis_action_just_pressed() / is_action_just_released(): These methods work like the above, but only return true on the single frame after the event occurs. This is useful for non-recurring actions like shooting or jumping where the user needs to let go and then press the key again to repeat the action.\nRelated Recipes Inputs: Introduction ","description":"","tags":null,"title":"Input Actions","uri":"/godot_recipes/4.x/input/input_actions/index.html"},{"content":"Problem You need a 3D camera that smoothly follows a target (interpolates).\nSolution Info Godot’s built-in InterpolatedCamera node is deprecated and will be removed in the release of Godot 4.0.\nAttach the script below to a Camera3D node in your scene. The three export properties let you choose:\nlerp_speed - the camera’s movement speed. Lower values result in a “lazier” camera. target_path - choose the camera’s target node. offset - position of the camera relative to the target. See below for some examples of the camera in action.\nextends Camera3D @export var lerp_speed = 3.0 @export var target_path : NodePath @export var offset = Vector3.ZERO var target = null func _ready(): if target_path: target = get_node(target_path) func _physics_process(delta): if !target: return var target_xform = target.global_transform.translated_local(offset) global_transform = global_transform.interpolate_with(target_xform, lerp_speed * delta) look_at(target.global_transform.origin, target.transform.basis.y) In the _physics_process() function we interpolate the camera’s position with the target’s (plus offset).\nExamples lerp_speed: 3.0 offset: (0, 7, 5) ","description":"","tags":null,"title":"Interpolated Camera","uri":"/godot_recipes/4.x/3d/interpolated_camera/index.html"},{"content":" GDScript GDScript is Godot’s built-in scripting language. Its syntax is based on Python, so if you’re familiar with that language, you’ll feel right at home. In this chapter, we’ll introduce the language and get you up to speed with how it works.\nUpdating to Godot 4.0 We’re working on a new version of Godot 101 for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.\nIn this section: Getting started ","description":"","tags":null,"title":"Introduction to GDScript","uri":"/godot_recipes/4.x/g101/gdscript/index.html"},{"content":" Know Your Nodes In the “Know Your Nodes” series, we go in-depth with a single one of Godot’s nodes. Learn what makes it tick and see some examples of how it’s used.\nIn this section: RayCast2D ","description":"","tags":null,"title":"Know Your Nodes","uri":"/godot_recipes/4.x/kyn/index.html"},{"content":"Problem You want to detect mouse input.\nSolution InputEventMouse is the base class for mouse events. It contains position and global_position properties. Inheriting from it are two classes: InputEventMouseButton and InputEventMouseMotion.\nNote You can assign mouse button events in the InputMap, so you can use them with is_action_pressed().\nInputEventMouseButton @GlobalScope.ButtonList contains a list of BUTTON_* constants for each possible button, which will be reported in the event’s button_index property. Note that the scrollwheel also counts as a button - two buttons, to be precise, with both BUTTON_WHEEL_UP and BUTTON_WHEEL_DOWN being separate events.\nTip Unlike regular buttons, mouse wheel clicks only produce pressed events. There is no concept of a mouse wheel click being “released”.\nfunc _unhandled_input(event): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT: if event.pressed: print(\"Left button was clicked at \", event.position) else: print(\"Left button was released\") if event.button_index == BUTTON_WHEEL_DOWN: print(\"Wheel down\") InputEventMouseMotion These events occur whenever the mouse moves. You can find the distance moved (in screen coordinates) with the relative property.\nHere’s an example using mouse movement to rotate a 3D character:\n# Converts mouse movement (pixels) to rotation (radians). var mouse_sensitivity = 0.002 func _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) ","description":"","tags":null,"title":"Mouse Input","uri":"/godot_recipes/4.x/input/mouse_input/index.html"},{"content":" Info Many thanks to @TheDuriel on the Godot Discord for the original diagram that inspired this article. Save this and keep it handy.\nProblem Your project has started getting complex. You have multiple scenes, instances, and a lot of nodes. You’ve probably found yourself writing code like the following:\nget_node(\"../../SomeNode/SomeOtherNode\") get_parent().get_parent().get_node(\"SomeNode\") get_tree().get_root().get_node(\"SomeNode/SomeOtherNode\") If you do this, you’ll soon find that node references like this break easily. As soon as you change one thing about your scene tree, none of those references may be valid anymore.\nCommunication between nodes and scenes doesn’t have to be complicated. There is a better way.\nSolution As a general rule, nodes should manage their children, not the other way around. If you’re using get_parent() or get_node(\"..\"), then you’re probably headed for trouble. Node paths like this are brittle, meaning they can break easily. The three main problems with this arrangement:\nYou can’t test a scene independently. If you run the scene by itself or in a test scene that doesn’t have the exact same node setup, get_node() will cause a crash.\nYou can’t change things easily. If you decide to rearrange or redesign your tree, paths will no longer be valid.\nReady order is children-first, parent-last. This means that trying to access a parent’s property in a node’s _ready() can fail because the parent isn’t ready yet.\nTip See Understanding tree order for an explanation of how nodes enter the tree and become ready.\nGenerally speaking, a node or scene should be able to be instanced anywhere in your game, and it should make no assumptions about what its parent is going to be.\nWe’ll go into detailed examples later in this tutorial, but for now, here’s the “golden rule” of node communication:\nCall down, signal up.\nIf a node is calling a child (i.e. going “down” the tree), then get_node() is appropriate.\nIf a node needs to communicate “up” the tree, it should probably use a signal.\nIf you keep this rule in mind when designing your scene setup, you’ll be well on your way to a maintainable, well-organized project. And you’ll avoid using the cumbersome node paths that lead to problems.\nNow, let’s look at each of these strategies along with some examples.\n1. Using get_node() get_node() traverses the scene tree using a given path to find the named node.\nTip See Understanding node paths for a more detailed explanation of node paths.\nget_node() example Let’s consider the following common configuration:\nThe script in the Player node needs to notify the AnimatedSprite2D which animation to play, based on the player’s movement. In this situation, get_node() works well:\nextends CharacterBody2D func _process(delta): if speed \u003e 0: get_node(\"AnimatedSprite2D\").play(\"run\") else: get_node(\"AnimatedSprite2D\").play(\"idle\") Tip In GDScript you can use $ as a shorthand for get_node(), writing $AnimatedSprite2D instead.\n2. Using signals Signals should be used to call functions on nodes that are higher in the tree or at the same level (i.e. “siblings”).\nYou can connect a signal in the editor (most often for nodes that exist before the game starts) or in code (for nodes that you’re instancing at runtime). The syntax for connecting a signal is:\nsignal_name.connect(target_node.target_function)\nLooking at this, you may be thinking “Wait, if I’m connecting to a sibling, won’t I need a node paths like ../Sibling?”. While you could do this, it breaks our rule above. The answer to this puzzle is to make sure that connections are made by the common parent.\nFollowing the rule of calling down the tree, a node that’s a common parent to the signaling and receiving nodes will by definition know where they are and be ready after both of them.\nSignal example A very common use case for signals is updating your UI. Whenever the player’s health variable changes, you want to update a Label or ProgressBar display. However, your UI nodes are completely separated from your player (as they should be). The player knows nothing about where those nodes are and how to find them.\nHere’s our example setup:\nNote that the UI is an instanced scene, we’re just showing the contained nodes. This is where you often see things like get_node(\"../UI/VBoxContainer/HBoxContainer/Label).text = str(health), which is what we want to avoid.\nInstead the player emits a health_changed signal whenever it adds/loses health. We need to send that signal to the UI’s update_health() function, which handles setting the Label value. In the Player script we use this code whenever the player’s health is changed:\nhealth_changed.emit(health) In the UI script we have:\nonready var label = $VBoxContainer/HBoxContainer/Label func update_health(value): label.text = str(value) Now we just need to connect the signal to the function. The perfect place to do that is in World, which is the common parent, and knows where both nodes are:\nfunc _ready(): $Player.health_changed.connect($UI.update_health) 3. Using groups Groups are another way to decouple, especially when you have a lot of similar objects that need to do the same thing. A node can be added to any number of groups and membership can be changed dynamically at any time with add_to_group() and remove_from_group().\nA common misconception about groups is that they are some kind of object or array that “contains” node references. Groups are a tagging system. A node is “in” a group if it has that tag assigned from it. The SceneTree keeps track of the tags and has functions like get_nodes_in_group() to help you find all nodes with a particular tag.\nGroup example Let’s consider a Galaga-style space shooter where you have a lots of enemies flying around. These enemies may have different types and behaviors. You’d like to add a “smart bomb” upgrade that, when activated, destroys all enemies on the screen. Using groups, you can implement this with a minimal amount of code.\nFirst, add all enemies to an “enemies” group. You can do this in the editor using the “Node” tab:\nYou can also add nodes to the group in your script:\nfunc _ready(): add_to_group(\"enemies\") Let’s assume every enemy has an explode() function that handles what happens when it dies (playing an animation, spawning dropped items, etc). Now that every enemy is in the group, we can implement our smart bomb function like this:\nfunc activate_smart_bomb(): get_tree().call_group(\"enemies\", \"explode\") 4. Using owner owner is a Node property that’s set automatically when you save a scene. Every node in that scene will have its owner set to the scene’s root node. This makes for a convenient way to connect child signals up to the main node.\nowner example In a complex UI, you often find yourself with a very deep, nested hierarchy of containers and controls. Nodes that the user interacts with, such as Button, emit signals, and you may want to connect those signals to the script on the UI’s root node.\nHere’s an example setup:\nThe script on the root CenterContainer has the following function, which we want to call whenever any button is pressed:\nextends CenterContainer func _on_button_pressed(button_name): print(button_name, \" was pressed\") The buttons here are instances of a Button scene, representing an object which may contain dynamic code that sets the button’s text or other properties. Or perhaps you have buttons that are dynamically added/removed from the container depending on the game state. Regardless, all we need to connect the button’s signal is the following:\nextends Button func _ready(): pressed.connect(owner._on_button_pressed.bind(name)) No matter where you place the buttons in the tree - if you add more containers, for example - the CenterContainer remains the owner.\nRelated recipes Understanding tree order Understanding node paths ","description":"","tags":null,"title":"Node communication (the right way)","uri":"/godot_recipes/4.x/basics/node_communication/index.html"},{"content":"Problem You want to shoot projectiles from your player/mob/etc..\nSolution Setting up the bullet First, we’ll set up a “bullet” object that we can instance. Here are the nodes we’ll use:\nArea2D: Bullet Sprite2D CollisionShape2D For the Sprite2D’s texture, you can use any image you like. Here’s an example one:\nSet up the nodes and configure the sprite and collision shape. If your texture is oriented pointing up, like the one above, make sure to rotate the Sprite node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.\nAdd a script and connect the Area2D’s body_entered signal.\nextends Area2D var speed = 750 func _physics_process(delta): position += transform.x * speed * delta func _on_Bullet_body_entered(body): if body.is_in_group(\"mobs\"): body.queue_free() queue_free() For this example, we’ll remove the bullet if it hits anything at all. We’ll also delete anything tagged in the “mobs” group that it hits.\nShooting We need to set up a spawn location for the bullets. Add a Marker2D and place it where you want the bullets to spawn. Here’s an example, placed at the barrel of the gun. I’ve named it “Muzzle”.\nNotice that as the player rotates, the Muzzle’s transform remains oriented the same way relative to the gun. This will be very convenient when spawning the bullets, as they can use the transform to get the proper position and direction. We just set the new bullet’s transform equal to the muzzle’s.\nTip This will work for any character type, not just the “rotate-and-move” style shown here. Just attach the Marker2D where you want the bullets to spawn.\nIn the character’s script we add a variable to hold the bullet scene for instancing:\nexport var Bullet : PackedScene And check for our defined input action:\nif Input.is_action_just_pressed(\"shoot\"): shoot() Now in our shoot() function we can instance a bullet and add it to the tree. A common mistake is to add the bullet as a child of the player:\nfunc shoot(): var b = Bullet.instantiate() add_child(b) b.transform = $Muzzle.transform The problem here is that since the bullets are children of the player, they are affected when the player moves or rotates.\nTo fix this, we should make sure the bullets are added to the world instead. In this case, we’ll use owner, which refers to the root node of the scene the player is in. Note that we also need to use the muzzle’s global transform, or else the bullet would not be where we expected.\nfunc shoot(): var b = Bullet.instantiate() owner.add_child(b) b.transform = $Muzzle.global_transform Related recipes Gamedev Math: transforms Download This Project Download the project code here: https://github.com/godotrecipes/2d_shooting\n","description":"","tags":null,"title":"Shooting projectiles","uri":"/godot_recipes/4.x/2d/2d_shooting/index.html"},{"content":"Project Manager The Project Manager is the first thing you’ll see when opening Godot.\nIn this window you can see a list of your Godot projects. You can choose an existing project and click “Run” to play the game or click “Edit” to work on it in the Godot editor. Since you probably don’t have any projects yet, let’s start by clicking the “New Project” button.\nHere you can give the project a name and create a folder to store it in.\nNote Every Godot project is contained in its own folder. This has many benefits, including making it easy to move, share, and backup projects. It also means that all the project’s files (images, sounds, etc.) must be in the project folder.\nWhen you’re naming your project, try to choose a name that describes the project. “New Game Project #23” is not going to help you remember what that project was. You should also think about compatibility: some operating systems are case-sensitive, and some are not. This can lead to problems if you move or share your project from one computer to another. For this reason, many programmers develop a standardized naming scheme. For example: “No spaces, use ‘_’ between words.”\nLet’s name this new project “getting_started”. Type this name, click Create Folder, and then click Create \u0026 Edit.\nYou’re now looking at the Godot editor window. This is where you’ll spend most of your time when working in Godot. The editor is divided into sections.\nViewport: This is where you’ll see the parts of your game as you’re working on them. Workspaces: At the center-top, you can switch between working in the 2D, 3D, or Script workspaces. You start in 3D. Playtest Buttons: These buttons let you launch and control your game when testing. Docks/Tabs: On both sides are a number of docks where you can view game items and set their properties. Bottom Panel: Here, you’ll see context-specific information for various tools. The most important one to note first is the Output panel, where you’ll see any error or informational messages when your game is running. Project Settings Now we’ve talked about the main parts of the Godot window and how they work, let’s spend a little time talking about our Project settings. Usually one of the first tasks when starting a new project is make sure it’s all set up correctly.\nSo let’s click on Project in the menu and select Project Settings.\nThis is the Project settings window. On the left is a list of categories. For most projects, the default settings will be fine, and you shouldn’t worry about changing them unless you have a very specific need. For now, we’re just going to look at two of the sections. First, Application/Config.\nIn here, you can set your game’s title, choose which scene is the “main scene” (more about that in a bit), and change the icon.\nSecond, let’s look at the Display section. This is where you set up your game’s display. width \u0026 height let you set the size of the game window. If, for example, you were making a mobile game, you’d want to set this to the resolution and proportions of your target device. There are also settings for scaling, stretching, fullscreen mode, and more. For now, we’ll leave the default size - later on we’ll talk about how to adjust these to get our game running on different devices.\nThere are also some tabs across the top. We’ve been looking at the General tab. I’ll also point out briefly, the Input Map. This is where you can define different input actions for keyboard control, gamepad, mouse, and so on. In your game, you’ll just worry about the action, not what individual key or button was pressed. This is a very powerful and flexible way of handling player input.\nWe also have localization options, if you plan to support multiple languages. Autoloading, which we’ll get to later, and plugins. The Godot community has created a variety of useful plugins that you can download and add to supply more features, different tools, and so on.\nWe’ll come back to the project settings window later. Let’s close it for now and we’re ready to move on to the next step: working with nodes.\n","description":"","tags":null,"title":"The Godot Editor: Finding your way around","uri":"/godot_recipes/4.x/g101/start/101_02/index.html"},{"content":"Problem It’s probably the most common problem seen in the Godot help channels: an invalid node reference. Most often, it appears as the following error message:\nInvalid get index ‘position’ (on base: ’null instance’).\nSolution It’s that last part, the “null instance”, that’s the source of this problem, and the main source of confusion for Godot beginners.\nThe way to avoid this problem is to understand the concept of node paths.\nUnderstanding node paths The scene tree is made of nodes, which are connected together in parent-child relationships. A node path is the path it takes to get from one node to another by moving through this tree.\nAs an example, let’s take a simple “Player” scene:\nThe script for this scene is on the Player node. If the script needs to call play() on the AnimatedSprite node, it needs a reference to that node:\nget_node(\"AnimatedSprite\").play() The argument of the get_node() function is a string representing the path to the desired node. In this case, it’s a child of the node the script is on. If the path you give it is invalid, you’ll get the dreaded null instance error (as well as “Node not found”).\nGetting a node reference with get_node() is such a common situation that GDScript has a shortcut for it:\n$AnimatedSprite.play() Info get_node() returns a reference to the desired node.\nLet’s look at a more complex scene tree:\nIf the script on Main needs to access ScoreLabel it can do so with this path:\nget_node(\"HUD/ScoreLabel\").text = \"0\" # or using the shortcut: $HUD/ScoreLabel.text = \"0\" Tip When using $ notation, the Godot editor will autocomplete paths for you. You can also right-click on a node in the Scene tab and choose “Copy Node Path”.\nWhat if the node you want to access is higher in the tree? You can use get_parent() or \"..\" to reference the parent node. In the above example tree, to get the Player node from the ScoreLabel:\nget_node(\"../../Player\") Let’s break that down. The path \"../../Player\" means “get the node that’s up one level (HUD), then one more level (Main), then its child Player”.\nTip Does this seem familiar? Node paths work exactly like directory paths in your operating system. The / character indicates the parent-child relationship, and .. means “up one level”.\nRelative vs absolute paths The above examples all use relative paths - meaning they start at the current node and follow the path to the destination. Node paths can also be absolute, starting from the root node of the scene.\nFor example, the absolute path to the player node is:\nget_node(\"/root/Main/Player\") /root, which can also be accessed with get_tree().root is not the root node of your scene. It’s the Viewport node that is always present by default in the SceneTree.\nA warning While the above examples work just fine, there are some things you should be aware of that may cause problems later. Imagine the following situation: the Player node has a health property, which you want to display in a HealthBar node somewhere in your UI. You might write something like this in the player’s script:\nfunc take_damage(amount): health -= amount get_node(\"../Main/UI/HealthBar\").text = str(health) While this may work fine at first, it is brittle, meaning it can break easily. There are two main problems with this kind of arrangement:\nYou can’t test the player scene independently. If you run the player scene by itself or in a test scene that doesn’t have a UI, the get_node() line will cause a crash. You can’t change your UI. If you decide to rearrange or redesign your UI, the path will no longer be valid and you have to change it. For this reason, you should try to avoid using node paths that go up the scene tree. In the above situation, if the player instead emitted a signal when the health changed, the UI could listen for that signal to update itself. You could then rearrange and separate nodes without fear of breaking your game.\nWrapping up Once you understand how to use node paths, you’ll see how easy it is to reference any node you need. And put a stop to seeing those null instance error messages.\n","description":"","tags":null,"title":"Understanding node paths","uri":"/godot_recipes/4.x/basics/getting_nodes/index.html"},{"content":"Before reading this, make sure you have an understanding of vectors and how they’re used in game development. If you don’t, I recommend you read this introduction I wrote for the Godot documentation: Vector Math.\n2D Transforms In 2D space, we use the familiar X-Y coordinate plane. Remember that in Godot, as in most computer graphics applications, the Y axis points downward:\nTo begin, let’s consider this spaceship floating in space:\nThe ship is pointing in the same direction as the X axis. If we wanted it to move forward, we could add to its X coordinate and it would move to the right:\nposition += Vector2(10, 0) But what happens when the ship rotates?\nHow do we move the ship forward now? If you remember Trigonometry from school, you might be starting to think about angles, sine and cosine and doing something like position += Vector2(10 * cos(angle), 10 * sin(angle)). While this would work, there’s a much more convenient way: the Transform.\nLet’s look at the rotated ship again, but this time, let’s also imagine that the ship has its own X and Y axes that it carries with it, independent of the global axes:\nThese “local” axes are contained in the object’s transform.\nKnowing this, we can move the ship forward by moving it along its own X axis and we won’t have to worry about angles and trig functions. To do this in Godot, we can use the transform property, which is available to all Node2D derived nodes.\nposition += transform.x * 10 This code says “Add the transform’s x vector multiplied by 10.” Let’s break down what that means. The transform contains x and y properties that represent those local axes. They are unit vectors, which means their length is 1. Another term for unit vector is direction vector. They tell us the direction the ship’s x axis is pointing. We then multiply by 10 to scale it to a longer distance.\nTip The transform property of a node is relative to its parent node. If you need to get the global value, it’s available in global_transform.\nIn addition to the local axes, the transform also contains a component called the origin. The origin represents the translation, or change in position.\nIn this picture, the blue vector is the transform.origin. It is equal to the object’s position vector.\nConverting Between Local and Global Space You can convert coordinates from local to global by applying the transform. For convenience, Node2D and Spatial include helper functions for this: to_local() and to_global():\nvar global_position = to_global(local_position) Let’s use the example of an object in the 2D plane and convert mouse clicks (global space) into coordinates relative to the object:\nextends Sprite func _unhandled_input(event): if event is InputEventMouseButton and event.pressed: if event.button_index == BUTTON_LEFT: printt(event.position, to_local(event.position)) See the Transform2D docs for a list of the available properties and methods.\n3D Transforms In 3D space, the concept of transforms applies in the same way as in 2D. In fact, it becomes even more necessary, as using angles in 3D can lead to a variety of problems, as we’ll see in a bit.\n3D nodes inherit from the base node Node3D, which contains the transform information. The 3D transform requires more information than the 2D version. Position is still held in the origin property, but rotation is in a property called basis, which contains three unit vectors representing the body’s local X, Y, and Z axes.\nWhen you select a 3D node in the editor, the gizmo that appears allows you to manipulate the transform.\nLocal Space Mode In the editor, you can see and manipulate the body’s local orientation by clicking the “Local Space Mode” button. When in this mode, the 3 colored axis lines represent the body’s local basis axes.\nAs in 2D, we can use the local axes to move an object forward. In Godot’s 3D orientation (Y-up), this means that by default the body’s -Z axis is the forward direction. To move forward:\nposition += -transform.basis.z * speed * delta Tip Godot has default vector values defined, for example: Vector3.FORWARD == Vector3(0, 0, -1). See Vector2 and Vector3 for details.\n","description":"","tags":null,"title":"Transforms","uri":"/godot_recipes/4.x/math/transforms/index.html"},{"content":" 2D Tips, tricks, and tutorials on the 2D side of game development.\nIn this section: Entering/Exiting the screen Platform character Screen wrap Top-down movement Grid-based movement Shooting projectiles Car steering 8-Directional Movement/Animation Using Y-Sort Moving Platforms Pathfinding on a 2D Grid Multitarget Camera ","description":"","tags":null,"title":"2D","uri":"/godot_recipes/4.x/2d/index.html"},{"content":"Problem You want to hide the mouse cursor and keep the mouse from leaving the game window. This is common in many 3D games (and some 2D ones).\nSolution You can set the mouse state using Input.mouse_mode. There are four possible mouse modes:\nMOUSE_MODE_VISIBLE: The mouse is visible and can move freely into and out of the window. This is the default state.\nMOUSE_MODE_HIDDEN: The mouse cursor is invisible, but the mouse can still move outside the window.\nMOUSE_MODE_CAPTURED: The mouse cursor is hidden and the mouse is unable to leave the game window.\nMOUSE_MODE_CONFINED: The mouse is visible, but cannot leave the game window.\n“Captured” is the most commonly used option. You can set the mouse mode at runtime using:\nfunc _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED When the mouse is captured, mouse input events will still be passed as normal. However, you will find there is a problem. If you want to close the game or switch to another window, you can’t. For this reason, you will want to also include a way to “release” the mouse. For example, to release when the player pressed the Escape key:\nfunc _input(event): if event.is_action_pressed(\"ui_cancel\"): Input.mouse_mode = Input.MOUSE_MODE_VISIBLE So that the game doesn’t respond to mouse movement when you’re in another window, you can test for the capture state in your character controller using:\nif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: Once the mouse is released, that leaves the need to re-capture it to continue playing. Assuming you have an event in the Input Map for a mouse click, you can do the following:\nif event.is_action_pressed(\"click\"): if Input.mouse_mode == Input.MOUSE_MODE_VISIBLE: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED Since you may also be using a mouse click to shoot or perform some other action, it’s probably a good idea to stop the event from propagating. Add this after setting the mouse mode:\nget_tree().set_input_as_handled() ","description":"","tags":null,"title":"Capturing the Mouse","uri":"/godot_recipes/4.x/input/mouse_capture/index.html"},{"content":"Problem You need to create a 2D top-down car controller.\nSolution When approaching this problem, beginners often wind up creating something that handles nothing like a real car. Some common mistakes you’ll find in amateur car games:\nA car doesn’t rotate around its center. Put another way, a car’s rear wheels don’t slide side-to-side. (Unless it’s drifting, but we’ll talk about that later.) A car can only turn when it’s moving - it can’t spin in place. A car isn’t a train; it’s not on rails. Turning at high speeds should involve some sliding (drifting). There are many approaches to 2D car physics, mainly depending on how “realistic” you want to be. For this solution, we’re going for an “arcade” level of realism, meaning we’ll prioritize action over realism.\nNote The method below is based on the algorithm found here: http://engineeringdotnet.blogspot.com/2010/04/simple-2d-car-physics-in-games.html\nThe recipe below is broken into 5 parts, each adding a different feature to the car’s movement. Feel free to mix-and-match for your needs.\nScene setup Here’s the car scene setup:\nCharacterBody2D Sprite2D CollisionShape2D Camera2D Add whatever sprite texture you like. For this demo, we’ll use art from Kenney’s Racing Pack. CapsuleShape2D is a good choice for the collision, so that the car won’t have sharp corners to get caught on obstacles.\nWe’ll also use four input actions: “steer_right”, “steer_left”, “accelerate”, and “brake” - set them to whatever key inputs you prefer.\nPart 1: Movement The first step is to code the movement based on the algorithm described above.\nStart with a few variables:\nextends CharacterBody2D var wheel_base = 70 # Distance from front to rear wheel var steering_angle = 15 # Amount that front wheel turns, in degrees var steer_direction Set wheelbase to a value that works with your sprite.\nsteer_direction will be the amount that the wheels are turned.\nNote Since we’re using keyboard controls, turning is all-or-nothing. If you’re using an analog joystick, you can instead vary this value based on the distance the stick moves.\nfunc _physics_process(delta): get_input() calculate_steering(delta) move_and_slide() Each frame, we need to check for input and calculate steering. Then we pass the resulting velocity to move_and_slide(). We’ll define those two function next:\nfunc get_input(): var turn = Input.get_axis(\"steer_left\", \"steer_right\") steer_direction = turn * deg_to_rad(steering_angle) velocity = Vector2.ZERO if Input.is_action_pressed(\"accelerate\"): velocity = transform.x * 500 Here we check for user input and set the velocity. Note: the speed of 500 is temporary so that we can test movement. We’ll address it in the next part.\nHere is where we implement the algorithm from the link:\nfunc calculate_steering(delta): # 1. Find the wheel positions var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 # 2. Move the wheels forward rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_direction) * delta # 3. Find the new direction vector var new_heading = rear_wheel.direction_to(front_wheel) # 4. Set the velocity and rotation to the new direction velocity = new_heading * velocity.length() rotation = new_heading.angle() Run the project and the car should move and turn. It’s still very unnatural though - the car starts and stops instantly. To fix that, we’ll add acceleration into the calculation.\nPart 2: Acceleration We’ll need another setting variable and one to track the car’s overall acceleration:\nvar engine_power = 900 # Forward acceleration force. var acceleration = Vector2.ZERO Change the input code to apply acceleration instead of directly changing the car’s velocity.\nfunc get_input(): var turn = Input.get_axis(\"steer_left\", \"steer_right\") steer_direction = turn * deg_to_rad(steering_angle) if Input.is_action_pressed(\"accelerate\"): acceleration = transform.x * engine_power Once we’ve got our acceleration, we can apply it to the velocity like so:\nfunc _physics_process(delta): acceleration = Vector2.ZERO get_input() calculate_steering(delta) velocity += acceleration * delta move_and_slide() Now when you run, the car should gradually increase its speed. Careful: we don’t have any way to slow down yet!\nPart 3: Friction/drag A car experiences two different deceleration forces: friction and drag.\nFriction is the force applied by the ground. It’s very high if driving on sand, but very low if driving on ice. Friction is proportional to velocity - the faster you’re going the stronger the force.\nDrag is the force resulting from wind resistance. It’s based on the car’s cross-section - a large truck or van experiences more drag than a sleek race car. Drag is proportional to the velocity squared.\nThis means that friction is more significant when moving slowly, but drag becomes dominant at high speeds. We’ll add both of these forces to our calculation. As a bonus, the values of these quantities will also give our car a maximum speed - the point where the force from the engine can’t overcome the drag force any longer.\nHere are our starting values for these quantities:\nvar friction = -55 var drag = -0.06 As you can see in this graph, these values mean that at a speed of 600 the drag force overcomes the friction force.\nYou can play with the values here to see how they change: https://www.desmos.com/calculator/e4ayu3xkip\nIn _physics_process() we’ll call a function to calculate the current friction and apply it to the acceleration force.\nfunc _physics_process(delta): acceleration = Vector2.ZERO get_input() apply_friction(delta) calculate_steering(delta) velocity += acceleration * delta velocity = move_and_slide(velocity) func apply_friction(delta): if acceleration == Vector2.ZERO and velocity.length() \u003c 50: velocity = Vector2.ZERO var friction_force = velocity * friction * delta var drag_force = velocity * velocity.length() * drag * delta acceleration += drag_force + friction_force First, we’ll set a minimum speed. This will ensure that the car doesn’t keep creeping forward at very low speeds as friction never quite brings the velocity to zero.\nThen we calculate the two forces and add them to the total acceleration. Since they’re both negative, they’ll affect the car in the opposite direction.\nPart 4: Reverse/Brake We’ll need two more settings variables:\nvar braking = -450 var max_speed_reverse = 250 Add the input to get_input():\nif Input.is_action_pressed(\"brake\"): acceleration = transform.x * braking This is fine for coming to a stop, but we also want to be able to put the car in reverse. Currently, that won’t work, because the acceleration is always being applied in the “heading” direction, which is forward. When we’re reversing, we need to accelerate backward.\nfunc calculate_steering(delta): var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_angle) * delta var new_heading = (front_wheel - rear_wheel).normalized() var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = new_heading * velocity.length() if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) rotation = new_heading.angle() We can find whether we’re accelerating forward or backward using the dot product. If the two vectors are aligned, the result will be greater than 0. If the movement is in the opposite direction the car’s facing, then the dot product will be less than 0 and we must be moving backward.\nPart 5: Drift/slide We could stop here and you’d have a satisfactory driving experience. However, the car still feels like it’s “on rails”. Even at top speed, the turns are perfect, as if the tires have perfect “grip”.\nAt high speeds (or even low ones, if desired), the turning force should cause the tires to slip and result in a fishtailing/sliding motion.\nvar slip_speed = 400 # Speed where traction is reduced var traction_fast = 2.5 # High-speed traction var traction_slow = 10 # Low-speed traction We’ll apply these values when calculating the steering. Currently, the velocity is instantly set to the new heading. Instead, we’ll use interpolation - lerp() - to cause it to only “turn” partway towards the new direction. The “traction” values will determine how “sticky” the tires are.\nfunc calculate_steering(delta): var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_angle) * delta var new_heading = (front_wheel - rear_wheel).normalized() # choose which traction value to use - at lower speeds, slip should be low var traction = traction_slow if velocity.length() \u003e slip_speed: traction = traction_fast var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = lerp(velocity, new_heading * velocity.length(), traction * delta) if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) rotation = new_heading.angle() Here, we select which traction value to use and apply lerp() to the velocity.\nAdjustments At this point, we have a large number of settings that control the car’s behavior. Adjusting them can drastically change how the car drives. To make experimenting with different values easier, download the project for this recipe below. When you run the game, you’ll see a set of sliders you can use to change the car’s behavior as you drive (press \u003cTab\u003e to show/hide the slider panel).\nRelated recipes Gamedev Math: Interpolation Download This Project Download the project code here: https://github.com/godotrecipes/2d_car_steering\n","description":"","tags":null,"title":"Car steering","uri":"/godot_recipes/4.x/2d/car_steering/index.html"},{"content":"In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.\nAdding a script Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. Our Player scene displays the ship, defines its collision hitbox, etc., but it can’t move, and nothing would happen if it collided. We’ll write code to add this functionality to the ship.\nSelect the Player node and click the Attach script button:\nYou don’t need to change any of the options on the Attach Node Script window, so just click Create and you’ll be taken to the script editor.\nLet’s look at the first line of the script, which has automatically been added.\nextends Area2D This line defines what type of object this script should be attached to. It means that the script will have access to all the functionality that an Area2D provides.\nYour extends line should always match the type of node the script is attached to.\nAccessing scripts A script on its own doesn’t do much of anything. Scripts define additional functionality for whatever object they’re attached to. You will never be accessing a variable in some script, you’ll be accessing a property of an object, which is defined by that script. This is a very important distinction.\nMovement We’ll start by making the ship move around the screen. Let’s start with some code that does the following:\nDetect what input(s) the player is pressing Move the ship in the direction of the input @export var speed = 150 func _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") position += input * speed * delta Let’s break this down line-by-line:\nAdding @export in front of a variable allows you to adjust its value in the Inspector. The _process() function is called once every frame by the engine. Any code we place in this function will be executed every frame. Input.get_vector() checks the pressed state of the four given inputs and produces a vector pointing in that direction. Finally, we move the ship’s position by adding that input vector, scaling it to the desired speed, and multipling by delta. Links to more information Understanding vectors: Vector Math What is delta? Understanding delta Run the scene by clicking the Run Current Scene button, and try moving around.\nStaying on screen One problem we have is that if you keep moving, you’ll go off the screen. We need to lock the player’s position property inside the bounds of the screen rectangle. Add this line at the top of the script:\n@onready var screensize = get_viewport_rect().size The @onready here tells Godot not to set the value of the screensize variable until the Player node has entered the scene tree. Effectively, it means “wait until the game starts”, because there’s no window to get the size of until the game is running.\nThe next step is to clamp the position within the bounds of that screensize rectangle. Vector2, which is what position is, has a clamp() method we can use. Put this line right after setting the position:\nfunc _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") position += input * speed * delta position = position.clamp(Vector2.ZERO, screensize) Run the scene again and try moving off the edges. You’ll notice that half of the ship still goes off screen. This is because the ship’s position is the center of the Sprite2D. Since we know our ship is 16x16, we can change the clamp() to include 8 extra pixels:\nposition = position.clamp(Vector2(8, 8), screensize - Vector2(8, 8)) Matching animation to direction Now that the ship is moving, we can choose the “tilted” ship images when moving left or right, as well as the matching “Booster” animation.\nTo tell which direction we’re moving, we can check the x value of the input vector. Depending on whether it’s positive (right), negative (left), or zero (not moving), we can choose the frame value of the Sprite2D and the animation of the AnimatedSprite2D.\nfunc _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") if input.x \u003e 0: $Ship.frame = 2 $Ship/Boosters.animation = \"right\" elif input.x \u003c 0: $Ship.frame = 0 $Ship/Boosters.animation = \"left\" else: $Ship.frame = 1 $Ship/Boosters.animation = \"forward\" position += input * speed * delta position = position.clamp(Vector2(8, 8), screensize-Vector2(8, 8)) Once again, play the scene and verify that the images change when moving left/right. Verify that everything works as intended before moving to the next step.\nThe next step will be to create the Bullet scene and let the player shoot.\n","description":"","tags":null,"title":"Coding the Player","uri":"/godot_recipes/4.x/games/first_2d/first_2d_03/index.html"},{"content":"In the last part, we covered how to import 3D objects and how to arrange them in a scene. In this installment, we’ll add more objects to the scene, including a user-controlled character.\nBuilding the Scene We’re going to continue using the Kenney Platformer Kit we downloaded in Part 2. Select all the block*.glb files and in the Import tab set their Root Type to StaticBody3D. Uncheck the Root Name property and click Reimport. Select blockLarge.glb and make a new inherited scene. Use the Create Single Convex Collision Sibling option on the mesh using the menu as you did in the last tutorial. Now you can save the scene - I recommend making a separate folder for this, as soon you’re going to have a bunch of scenes representing the differently shaped platform parts.\nOpen the scene from the previous step with the “Ground” plane and the crates. Delete the crates and add an instance of the large block. We want to be able to place these blocks so that they line up. To do this, select “Configure Snap” from the “Transform” menu at the top of the Viewport and set Translate Snap to 0.5. Then click on the “Snap Mode” button (or press the Y key). Now duplicate the block a few times and drag them to arrange.\nIf you like, go ahead and add scenes for some of the other platform blocks and arrange them into a pleasing level. Be creative!\nAdding a Character Now we’re going to make a character so we can walk around on the platforms. Open a new scene and start with a CharacterBody3D named “Character”. This PhysicsBody node behaves very much like its 2D equivalent (you’ve already done the 2D tutorials, right?). It has a move_and_slide() method that we’ll use to perform the movement and collision detection.\nAdd a capsule-shaped MeshInstance3D and a matching CollionShape3D. Remember, you can add a StandardMaterial3D to the mesh and set its Albedo/Color property to change the color.\nThe capsule is nice, but it’s going to be hard to tell what direction it’s facing. Let’s add another mesh, this time with a CylinderMesh3D shape. Set its Top Radius to 0.2, its Bottom Radius to 0.001 and its Height to 0.5, then its x rotation to -90 degrees. Now you have a nice cone shape. Arrange it so it’s pointing out from the body along the negative z axis. (You can tell which way is negative because the gizmo arrows point in the positive direction).\nIn this picture, we’ve also added two sphere meshes for eyes to give a little more character. Feel free to add whatever details you like.\nLet’s also add a Camera3D to the scene, so it will follow the player around. Position the camera behind and above the character, angling it down a bit. Click the “Preview” button to check the camera’s view.\nBefore we add a script, open the “Project Settings” and add the following inputs on the “Input Map” tab:\nInput Action Key move_forward W move_back S strafe_right D strafe_left A jump Space Now let’s add a script to the body.\nextends CharacterBody3D var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var speed = 4.0 # movement speed var jump_speed = 6.0 # determines jump height var mouse_sensitivity = 0.002 # turning speed func get_input(): var input = Input.get_vector(\"strafe_left\", \"strafe_right\", \"move_forward\", \"move_back\") velocity.x = input.x * speed velocity.z = input.y * speed func _physics_process(delta): velocity.y += -gravity * delta get_input() move_and_slide() The code in _physics_process() is pretty straightforward: add gravity to accelerate in the positive Y direction (downward), call get_input() to check for input, and then use move_and_slide() to move in the direction of the velocity vector.\nIn get_input() we check to see which key is pressed and then move in that direction. Run the program and test:\nThis is all good, but we need to be able to rotate using the mouse. Add the following code to the character’s script:\nfunc _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) This will convert any mouse motion in the x direction into a rotation around the y axis.\nRun the scene and confirm that moving the mouse rotates the character:\nHowever, there’s a problem. No matter which way we’re facing, pressing W moves us along the Z axis of the world. Our movement is using global coordinates, but we need to move in the object’s forward direction.\nThe Power of Transforms This is where transforms come in. A transform is a mathematical matrix that contains the object’s translation, rotation, and scale information all in one. In Godot it’s stored in the Transform data type. The position information is called the transform.origin and the orientation information is in the transform.basis.\nRemember how the 3D gizmo can be set to “Local Space Mode”? When in this mode, the gizmo’s X/Y/Z axes point along the object’s axes. This is the same as the basis of the transform. The basis contains three Vector3 objects called x, y, and z that represent these directions. We can use this to ensure that pressing the W key will always move us in the object’s forward direction.\nChange the get_input() function like so:\nfunc get_input(): var input = Input.get_vector(\"strafe_left\", \"strafe_right\", \"move_forward\", \"move_back\") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed By multiplying the input vector by the transform.basis, we apply that transformation to the vector. Since the basis represents the object’s rotation, we’ve now converted forward and back to point along the object’s Z axis, and the strafe keys along its X.\nJumping Let’s add one more movement to the player: jumping.\nAdd these lines to the end of get_input():\nif event.is_action_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed Improving the camera You may have noticed that the if the character stands near an obstacle, the camera can “clip” inside the object, which doesn’t look nice. While coding a good 3D camera can be a complex topic on its own, we can use a built-in Godot node to get a pretty good solution.\nDelete the Camera3D from the character scene and add a SpringArm3D. This node can act as a moving arm that holds the camera while detecting collisions. It will move the camera closer if there’s an obstacle.\nIn its properties, set Spring Length to 5, and set its Position to (0, 1, 0), which is at the character’s head. Note the yellow line indicating the Spring Length. The camera will move along this line - at its end whenever possible, but moving closer if an obstacle is there.\nAdd back a Camera3D as a child of the SpringArm3D, and try running the game again. You can experiment with rotating the spring arm (around its x axis to point down slightly, for example) until you find something you like.\nWhat about first person? If you’re curious how you would do this in first person, see the Basic FPS Character recipe. You’ll notice several similarities with the 3rd person script we wrote above.\nWrapping Up In this tutorial you learned how to build a more complex scene, and how to write movement code for a user-controlled character. You also learned about transforms, which are a very important concept in 3D - you’re going to be using a lot in the future.\n","description":"","tags":null,"title":"Creating a 3D Character","uri":"/godot_recipes/4.x/g101/3d/101_3d_03/index.html"},{"content":"Nodes are the basic building blocks for creating games in Godot. A node is an object that can represent some kind of specialized game function. A given type of node might display graphics, play an animation, or represent a 3D model of an object. The node also contains a collection of properties, allowing you to customize its behavior. Which nodes you add to your project will depend on what functionality you need. It’s a modular system designed to give you flexibility in building your game objects.\nWorking with Nodes Nodes are objects, in the programming sense. They encapsulate data and behavior, and they can inherit properties from other nodes. Rather than use one of the default suggestions, let’s click the “Add/Create a New Node” button in the scene dock.\nHere you’ll see the whole hierarchy of node types available in the engine. For example, the nodes with the bluish icons all fall under the “Node2D” category, meaning they will all have the properties of a Node2D. More about that in a moment.\nThe list is long, and it would be frustrating to have to drill down every time to find the node you need. Instead, you can use the search function to find it using a small number of characters. We’re looking for the Sprite2D node, so I’ll just type “sp” and we’ll jump right to it. Click “Create” to add the node.\nNow we have this Sprite2D node in our Scene dock. Make sure it’s selected, and then look at the Inspector dock on the right side. Over here, you’ll see all the properties of whatever node you have selected. Notice that the properties are organized by where they come from. The Sprite2D node inherits from Node2D, which inherits from CanvasItem, which inherits from the plain old Node.\nOver in the viewport, the sprite doesn’t look like much. A sprite’s purpose is to display an image, or texture. As you can see in the Inspector, the Texture property is currently empty. Fortunately, every new Godot project comes with an image we can use: the Godot icon. Drag the icon from the Filesystem dock and drop it in the texture property.\nIn the Inspector, click to expand the “Transform” section, and type (50, 50) in the Position property.\nYou can also click and drag the sprite around in the viewport, and you’ll see the Position values changing as you move.\nOne important property of nodes is that they can be arranged in a parent-child hierarchy. Make sure you have the Sprite2D selected and press the add button again. Add another Sprite2D and also drag the icon into its texture.\nThis new sprite is a child of the first. This means that it’s “attached” to its parent. If the parent sprite moves, so will the child. Click on the child sprite and set its Position to (50, 50). Now click and drag the parent sprite to move it around the screen.\nNotice that the Position of the parent is changing as you move it around. Now check the child: it’s still (50, 50). That’s because its “Transform” properties are relative to its parent.\nScenes Grouping nodes together like this is a powerful tool, enabling you to construct complex objects out of node “building blocks”. For example, a “Player” node in your game might have many child nodes attached to it: a Sprite2D for display, an AnimationPlayer to animate it, a Camera2D to follow it around, and so on.\nA group of nodes arranged in a “tree” structure like this is called a Scene. In the next part, we’ll look at how you can use scenes to organize your game’s objects into independent parts that all work together. You’ll see this in practice was you work through the examples in later lessons.\n","description":"","tags":null,"title":"Nodes: Godot's building blocks","uri":"/godot_recipes/4.x/g101/start/101_03/index.html"},{"content":"Problem You need to implement shooting in an FPS, but moving individual projectiles is impractical.\nSolution Game physics engines often break down when trying to handle very fast-moving objects. The solution is to cast a ray from the shooter’s location and detect the first thing that would be hit.\nThere are two ways to approach raycasting in Godot: the RayCast3D node, or directly casting a ray in space using the physics engine. While they can both accomplish the same thing, each has its uses. The node method tends to be best for situations where you continuously want to check for collisions - a downward-facing ray to check if you’re on the floor, for example.\nWe’ll use the second method, querying the physics state, because we want to know, at the moment we press the “shoot” key, whether we’ve hit anything.\nNote This recipe assumes you already have a working FPS character controller and a world to move around in. If you don’t, see the Basic FPS Character recipe first.\nTo display what we’ve hit, add a CanvasLayer with a Label node to the FPSPlayer scene.\nWe’ll add an input check in the _input() function, which we’re already using to handle mouse input.\nif event.is_action_pressed(\"shoot\"): shoot() Then we’ll define the shoot() method. Whenever it’s called, we want to build a PhysicsRayQueryParameters3D object, which defines the start (position of the camera) and end (position of the camera projected forward by 100 meters) points of the ray. We’ll pass this to the physics engine using the direct_space_state of the world. If we get a returned value (a dictionary containing data about the collision), we’ll update the label so we can see what kind of object we hit.\nfunc shoot(): var space = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create($Camera3D.global_position, $Camera3D.global_position - $Camera3D.global_transform.basis.z * 100) var collision = space.intersect_ray(query) if collision: $CanvasLayer/Label.text = collision.collider.name else: $CanvasLayer/Label.text = \"\" Related recipes Basic FPS Character Download This Project Download the project code here: https://github.com/godotrecipes/3d_shoot_raycasts\n","description":"","tags":[],"title":"Shooting with Raycasts","uri":"/godot_recipes/4.x/3d/shooting_raycasts/index.html"},{"content":"Problem The delta or “delta time” parameter is a frequently-misunderstood concept in game development. In this tutorial, we’ll explain how it’s used, the importance of frame-rate independent movement, and practical examples of its use in Godot.\nSolution To illustrate the problem, let’s consider a Sprite node moving across the screen. If our screen is 600 pixels wide and we want the sprite to take 5 seconds to cross the screen, we can use the following calculation to find the necessary speed:\n600 pixels / 5 seconds = 120 pixels/second We’ll move the sprite every frame using the _process() function. If the game is running at 60 frames per second, we can find the per-frame movement like so:\n120 pixels/second * 1/60 second/frame = 2 pixels/frame Tip Notice the units are consistent in all the calculations above. Always pay attention to the units in your calculations - it’ll save you from making mistakes.\nHere’s the necessary code:\nextends Node2D # Desired movement in pixels/frame var movement = Vector2(2, 0) func _process(delta): $Sprite.position += movement Run this code and you’ll see the sprite takes 5 seconds to cross the screen.\nMaybe. The trouble begins if there is something else occupying the computer’s time. This is called lag and can come from a variety of sources - the cause could be your code or even other applications running on your computer. If this happens, then the length of a frame might increase. As an extreme example, imagine that the frame rate is halved - each frame took 1/30 instead of 1/60 of a second. Moving at 2 px/frame, it’s now going to take twice as long for the sprite to reach the edge.\nEven small frame rate fluctuations will result in inconsistent movement speed. If this were a bullet or other fast-moving object, we wouldn’t want it slowing down like this. We need the movement to be frame rate independent.\nFixing the frame rate problem When using the _process() function, it automatically includes a parameter called delta that’s passed in from the engine (so does _physics_process(), which is used for physics-related code). This is a floating point value representing the length of time since the previous frame. Typically, this will be approximately 1/60 or 0.0167 seconds.\nWith this information, we can stop thinking about how much to move each frame, and only consider our desired speed in pixels/second (120 from the above calculation).\nMultiplying the engine’s delta value by this number will give us how many pixels to move each frame. The number will automatically adjust if the frame time fluctuates.\n# 60 frames/second 120 pixels/second * 1/60 second/frame = 2 pixels/frame # 30 frames/second 120 pixels/second * 1/30 second/frame = 4 pixels/frame Note that if the frame rate decreases by half (meaning the frame time doubles), then our per-frame movement must also double to keep the desired speed.\nLet’s change the code to use this calculation:\nextends Node2D # Desired movement in pixels/second. var movement = Vector2(120, 0) func _process(delta): $Sprite.position += movement * delta Now when running at 30 frames per second, the travel time is consistent:\nIf the frame rate gets very low, the movement is no longer smooth, but the time remains the same.\nUsing delta with motion equations What if your movement is more complex? The concept remains the same. Keep your units in seconds, not frames, and multiply by delta each frame.\nTip Working in pixels and seconds is much easier to conceptualize too, since it relates to how we measure these quantities in the real world. “Gravity is 100 pixels/second/second, so after the ball falls for 2 seconds, it’s traveling at 200 pixels/second.” If you’re working with frames, then you have to think about acceleration in units of pixels/frame/frame. Go ahead and try - it’s not very natural.\nFor example, if you are applying a gravity, that’s an acceleration - each frame it will increase the velocity by some amount. As in the above example, the velocity then changes the node’s position.\nTry adjusting delta and target_fps in the following code to see the effect:\nextends Node2D # Acceleration in pixels/sec/sec. var gravity = Vector2(0, 120) # Acceleration in pixels/frame/frame. var gravity_frame = Vector2(0, .033) # Velocity in pixels/sec or pixels/frame. var velocity = Vector2.ZERO var use_delta = false var target_fps = 60 func _ready(): Engine.target_fps = target_fps func _process(delta): if use_delta: velocity += gravity * delta $Sprite.position += velocity * delta else: velocity += gravity_frame $Sprite.position += velocity Note that we’re multiplying by our timestep each frame to update both velocity and position. Any quantity that is updated every frame should be multiplied by delta to ensure it changes independent or frame rate.\nUsing kinematic functions In the above examples, we’ve used a Sprite to keep things simple, updating the position every frame. If you’re using a kinematic body (in 2D or 3D), you’ll instead be using one of its movement methods. Specifically in the case of move_and_slide(), there tends to be some confusion, because it uses the velocity vector, not the position. This means you won’t multiply your velocity by delta to find distance - the function does that for you. But you will still need to apply it on any other calculations, such as the acceleration. For example:\n# Sprite movement code: velocity += gravity * delta position += velocity * delta # Kinematic body movement code: velocity += gravity * delta move_and_slide() If you don’t use delta when applying acceleration to your velocity, then your acceleration will be subject to fluctuations in frame rate. This can have a_much more subtle effect on movement - it will be inconsistent, but much more difficult to diagnose.\nTip When using move_and_slide() you still need to apply delta to any other quantities such as gravity, friction, etc.\nRelated Recipes ","description":"","tags":null,"title":"Understanding 'delta'","uri":"/godot_recipes/4.x/basics/understanding_delta/index.html"},{"content":" 3D Tips, tricks, and tutorials on the 3D side of game development.\nIn this section: Basic FPS Character Interpolated Camera Shooting with Raycasts CharacterBody3D: Movement 3D Unit Healthbars Rolling Cube Arcade-style Spaceship Arcade-style Car Click to move CharacterBody3D: Align with Surface ","description":"","tags":null,"title":"3D","uri":"/godot_recipes/4.x/3d/index.html"},{"content":"Problem You need a 2D character that has 8-directional movement, including animation.\nSolution For our example, we’ll use the Isometric Mini-Crusader, which contains 8-directional animations for idle, run, attack, and several other states.\nThe animations are organized in folders, with a separate image for each frame. We’ll use an AnimatedSprite2D and we’ll name each animation based on its direction. For example, idle0 pointing to the right and going clockwise to idle7.\nWhen our character moves, it will pick an animation based on the direction of movement:\nWe’ll use the mouse to move - the character will always face the mouse and run in that direction when we click the mouse button.\nTo choose which animation to play, we need to get the mouse direction and map it to this same range of 0-7. get_local_mouse_position() gives us the position of the mouse relative to the character. We can then use snappedf() to snap the angle of the mouse vector to the closest multiple of 45° (PI/4 radians) giving the following result:\nDivide each value by 45° (PI/4 radians), and we have:\nFinally, we need to map the resulting range to 0-7 using the wrapi() function, and we’ll have our correct values. Adding that value to the end of the animation name (“idle”, “run”, etc) gives us the correct animation:\nfunc _physics_process(delta): current_animation = \"idle\" var mouse = get_local_mouse_position() angle = snappedf(mouse.angle(), PI/4) / (PI/4) angle = wrapi(int(angle), 0, 8) if Input.is_action_pressed(\"left_mouse\") and mouse.length() \u003e 10: current_animation = \"run\" velocity = mouse.normalized() * speed move_and_slide() $AnimatedSprite2D.animation = current_animation + str(a) Testing the movement, we see this:\nKeyboard input If you’re using keyboard controls instead of mouse, you can get the angle of movement based on which keys are being held. The rest of the process works in the same way.\nfunc _process(delta): current_animation = \"idle\" var input_dir = Input.get_vector(\"left\", \"right\", \"up\", \"down\") if input_dir.length() != 0: angle = input_dir.angle() / (PI/4) angle = wrapi(int(a), 0, 8) current_animation = \"run\" velocity = input_dir * speed move_and_slide() $AnimatedSprite2D.play(current_animation + str(angle)) Download This Project Download the project code here: https://github.com/godotrecipes/8_direction_animation\n","description":"","tags":null,"title":"8-Directional Movement/Animation","uri":"/godot_recipes/4.x/2d/8_direction/index.html"},{"content":"Now that the player can move around the screen, our next step will be to implement shooting\nReusable objects The player will fire many “bullets” during the game, but all of them will be identical. A bullet needs to do the following:\nAppear just ahead of the player Travel forward until going off the screen Detect collisions with enemies Since all bullets will do these same things, we can save ourselves a great deal of work by designing one “prototype” bullet, and using that as the blueprint for creating as many duplicates as we need. Godot’s scene system is ideal for this.\nBullet scene Create a new scene by selecting Scene -\u003e New Scene in the menu, or by clicking the + in the tabs on the top of the viewport.\nJust like we did with the Player scene, we need to consider what nodes we’ll need to make the bullet work. We can again use an Area2D, since that will allow us to detect the bullet hitting things. This means we’ll need a collision shape, and a sprite to display the bullet image. Finally, we need a way to detect when the bullet goes offscreen so we can automatically remove it.\nHere’s the node setup:\nArea2D - name this Bullet Sprite2D CollisionShape2D VisibleOnScreenNotifier2D From the asset pack folder, drop the Player_charged_beam (16 x 16).png image on the Texture of the Sprite2D.\nAs with the ship image, there are multiple versions here, so set the *Hframes to 2 so we’ll only see one at a time.\nSet the shape of the CollisionShape2D just like you did earlier in the Player scene.\nBullet script Attach a script to the Bullet node and let’s start with the movement:\nextends Area2D @export var speed = -250 func start(pos): position = pos func _process(delta): position.y += speed * delta This should look fairly familiar, as it’s similar to the player script. We’re only changing the position.y since the bullet should travel straight up.\nNote the start() function we defined. That will let us set the bullet’s starting position, since the player will move around and spawn the bullets at different locations.\nConnecting signals Now select the Bullet node and then click the Node tab next to the Inspector.\nThis is a list of all the signals this node can emit. Signals are how Godot lets you know that something has happened. In this case, we can use the area_entered signal to tell us whenever this bullet touches another Area2D node.\nSelect the area_entered signal and click the Connect… button (you can also double-click the signal name). In the dialog that opens up, just click Connect - we don’t need to change anything there.\nYou’ll notice that you’re back in the script editor, looking at bullet.gd, and a new function as been added. It has a green “connected” icon next to its name to show that a signal is connected to it. This function will be called whenever the area touches something, so let’s add some code here:\nfunc _on_area_entered(area): if area.is_in_group(\"enemies\"): area.explode() queue_free() Here we’ll check if the bullet hit an enemy (more about that later), and if it did, we tell the enemy to explode and then delete the bullet.\nDo the same thing to connect the screen_exited signal of the VisibleOnScreenNotifier2D.\nfunc _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() Next steps This completes the bullet scene, so now we can go back and add shooting to the player.\n","description":"","tags":null,"title":"Bullet Scene","uri":"/godot_recipes/4.x/games/first_2d/first_2d_04/index.html"},{"content":"Problem You need a player-controlled 3D character body.\nSolution For this recipe, we’ll be using this adorable tank model:\nYou can grab this model on Itch.io: https://gtibo.itch.io/mini-tank or use any other model you’d like. We won’t be doing anything that’s tank-specific here.\nIn the case of this asset, the download includes an OBJ file, and we’ll find it more convenient if we import it as a scene:\nWe can add the model to the scene, but we’ll need a couple of additional nodes:\nFor the collision shape, we’re just going to use a BoxShape aligned and sized with the tank’s treads. CamPos is a Position3D we’ll use to place our following camera. It’s placed behind and above the tank, angled down.\nWe’ve also rotated the individual MeshInstance nodes 180 degrees around the Y axis. This is because they were modeled facing towards +Z, but -Z is the forward direction in Godot, and we don’t want our tank to look like it’s backwards.\nBefore we add a script, open the “Project Settings” and add the following inputs on the “Input Map” tab:\nInput Action Key forward W back S right D left A Now let’s add a script, starting with the required variables:\nextends CharacterBody3D @export var speed = 4.0 @export var turn_speed = 0.8 speed is the tank’s movement speed (forward and back), while rot_speed defines how fast it can turn.\nTip Declaring properties with @export makes it easy to adjust them in the Inspector.\nUsing the move_and_slide() method makes our movement code quite simple:\nfunc _physics_process(delta): velocity.y -= gravity * delta get_input(delta) move_and_slide() With this code, we add the downward acceleration of gravity to the current velocity, get the user’s input (more about that below), and call move_and_slide().\nNext we need to define get_input(), where we’ll process and apply the input actions:\nfunc get_input(delta): var vy = velocity.y velocity = Vector3.ZERO var move = Input.get_axis(\"back\", \"forward\") var turn = Input.get_axis(\"right\", \"left\") velocity += -transform.basis.z * move * speed rotate_y(turn_speed * turn * delta) velocity.y = vy Let’s examine this more closely. Player input should affect horizontal movement: forward/back along the ground, and rotation around the tank’s center. Movement in the Y direction should only be affected by gravity, which means we don’t want to set it to 0 every frame. This is why we’re using the vy variable to temporarily hold that value while we assign a new velocity vector for the horizontal movement, then add it back in at the end.\nFor the forward and back movement, we’re using transform.basis.z so that we’ll move in our body’s local forward direction.\nHere’s the tank in action. We’ve made a test scene with a StaticBody3D plane for the ground and an Camera3D using the Interpolated Camera recipe.\nWrapping up This is the basis of movement for any kind of kinematic character. From here you can add jumping, shooting, AI behavior, etc. See the related recipes for examples that build on this recipe.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/characterbody3d_examples\nRelated recipes Intro to 3D Input Actions ","description":"","tags":null,"title":"CharacterBody3D: Movement","uri":"/godot_recipes/4.x/3d/characterbody3d_examples/index.html"},{"content":"Problem You need a “homing missile” - a projectile that will seek a moving target.\nSolution For this example, we’ll use an Area2D node for the projectile. Areas are typically good choices for bullets because we need to detect when they contact something. If you also need a bullet that bounces/ricochets, one of the PhysicsBody type node might be a better choice.\nThe node setup and behavior of the missile is the same you would use for a “dumb” bullet. If you’re creating many bullet types, you can use inheritance to base all your projectiles on the same core setup.\nThe nodes we’ll use:\nArea2D: Missile Sprite2D CollisionShape2D Timer: Lifetime For the texture, you can use any image you like. Here’s an example one:\nSet up the nodes and configure the sprite’s texture and the collision shape. Make sure to rotate the Sprite2D node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.\nAdd a script and connect the Area2D’s body_entered signal and the Timer’s timeout signal.\nHere’s the starting script:\nextends Area2D export var speed = 350 var velocity = Vector2.ZERO var acceleration = Vector2.ZERO func start(_transform): global_transform = _transform velocity = transform.x * speed func _physics_process(delta): velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta func _on_Missile_body_entered(body): queue_free() func _on_Lifetime_timeout(): queue_free() This creates a “dumb” rocket that travels in a straight line when fired. To use this projectile, instance it and call its start() method with the desired Transform2D to set its position and direction.\nSee the related recipes section below for more information.\nTo change the behavior to seek a target, we’ll use the acceleration. However, we don’t want the missile to “turn on a dime”, so we’ll add a variable to control its “steering” force. This will give the missile a turning radius that can be adjusted for different behavior. We also need a target variable so that the missile knows what to chase. We’ll set that in start() as well:\nexport var steer_force = 50.0 var target = null func start(_transform, _target): target = _target ... To change the missile’s direction to move toward the target, it needs to accelerate in that direction (acceleration is change in velocity). The missile “wants” to move straight towards the target, but its current velocity is pointing in a different direction. Using a little vector math, we can find that difference:\nThe green arrow represents the needed change in velocity (i.e. acceleration). However, if we turn instantly, that will look unnatural, so the “steering” vector’s length needs to be limited. This is the purpose of the steer_force variable.\nThis is the function to calculate that acceleration. Note that if there’s no target, there will be no steering, so the missile remains traveling in a straight line.\nfunc seek(): var steer = Vector2.ZERO if target: var desired = (target.position - position).normalized() * speed steer = (desired - velocity).normalized() * steer_force return steer Finally, the resulting steer force must be applied in _physics_process():\nfunc _physics_process(delta): acceleration += seek() velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta Here’s an example of the results, with a little extra visual flair such as particle smoke and explosions:\nHere’s the full script, including the above effects. See related recipes for details.\nextends Area2D export var speed = 350 export var steer_force = 50.0 var velocity = Vector2.ZERO var acceleration = Vector2.ZERO var target = null func start(_transform, _target): global_transform = _transform rotation += rand_range(-0.09, 0.09) velocity = transform.x * speed target = _target func seek(): var steer = Vector2.ZERO if target: var desired = (target.position - position).normalized() * speed steer = (desired - velocity).normalized() * steer_force return steer func _physics_process(delta): acceleration += seek() velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta func _on_Missile_body_entered(body): explode() func _on_Lifetime_timeout(): explode() func explode(): $Particles2D.emitting = false set_physics_process(false) $AnimationPlayer.play(\"explode\") await $AnimationPlayer.animation_finished queue_free() Related recipes Spritesheet animation Top-down character Transforms ","description":"","tags":null,"title":"Homing missile","uri":"/godot_recipes/4.x/ai/homing_missile/index.html"},{"content":"Problem You want to pick up and move rigid bodies with the mouse.\nSolution Working with rigid bodies can be tricky. Godot’s physics engine controls their movements, and interfering with that can often lead to unexpected results. The key is to make use of the body’s mode property. This applies equally well in 2D or 3D.\nBody setup We’ll start with our rigid body object, adding a Sprite2D and CollisionShape2D. You can also add a PhysicsMaterial if you want to set Bounce and Friction properties.\nWe’re going to use the rigid body’s freeze property to remove it from the control of the physics engine while we’re dragging it. Since we still want it to be movable, we need to set the Freeze Mode to “Kinematic”, rather than the default value of “Static”.\nPlace the body in a group called “pickable”. We’ll use this to allow for multiple instances of the pickable object in the main scene. Attach a script to the body and connect the its _input_event signal.\nextends RigidBody2D signal clicked var held = false func _on_input_event(viewport, event, shape_idx): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: print(\"clicked\") clicked.emit(self) We’ll emit a signal when a mouse click is detected, including a reference to the body. Since there can be many bodies, we’ll let the main scene manage whether a body can be dragged or if there’s already one in the held state.\nIf the body is being dragged, we update its position to follow the mouse.\nfunc _physics_process(delta): if held: global_transform.origin = get_global_mouse_position() Finally, these are the two functions to call when the body is picked up and dropped. Changing the freeze to true removes the body from physics engine processing. Note that other objects can still collide with it. If you don’t want that, you can disable the collision_layer and/or collision_mask here as well. Just remember to re-enable them when dropping.\nfunc pickup(): if held: return freeze = true held = true func drop(impulse=Vector2.ZERO): if held: freeze = false apply_central_impulse(impulse) held = false In the drop function, after we change freeze back to `false, the body will return to the physics engine’s control. By passing in an optional impulse value, we can add the ability to “throw” the object on release.\nMain scene Create a main scene with some static body obstacles or a TileMap and instance a few copies of the pickable body.\nHere’s the script for the main scene. We start by connecting the clicked signal on any pickable bodies that are in the scene.\nextends Node2D var held_object = null func _ready(): for node in get_tree().get_nodes_in_group(\"pickable\"): node.clicked.connect(_on_pickable_clicked) Next, we have the function we connect the signal to. The connected function sets held_object so that we know something is currently being dragged, and calls the body’s pickup() method.\nfunc _on_pickable_clicked(object): if !held_object: object.pickup() held_object = object Lastly, when the mouse is released during dragging, we can perform the reverse actions.\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if held_object and !event.pressed: held_object.drop(Input.get_last_mouse_velocity()) held_object = null Note the use of get_last_mouse_velocity() to pass the impulse to the object - be careful with this! You may find yourself launching the rigid bodies at high speeds, especially if the bodies have low mass values. It’s probably a good idea to scale this to a reasonable value and clamp() it to some maximum. Experiment to find out what works for you.\nDownload This Project Download the project code here: https://github.com/godotrecipes/rigidbody_drag_drop\nRelated recipes ","description":"","tags":null,"title":"RigidBody2D: Drag and Drop","uri":"/godot_recipes/4.x/physics/rigidbody_drag_drop/index.html"},{"content":"Problem You need to save and load local data between game sessions.\nSolution Godot’s file I/O (input/output) system is based around the FileAccess object. You open a file by calling open().\nvar file = FileAccess.open(\"user://myfile.name\", File.READ) Warning User data should only be stored in the user:// path. While res:// can be used when running from the editor, when your project is exported, the res:// path becomes read-only.\nThe second argument after the file path is the “Mode Flag”, which can be one of the following:\nFileAccess.READ - Open for reading. FileAccess.WRITE - Open for writing. Creates the file if it doesn’t exist and truncates if it does. FileAccess.READ_WRITE - Open for reading and writing. Doesn’t truncate the file. FileAccess.WRITE_READ - Open for reading/writing. Creates the file if it doesn’t exist and truncates if it does. Storing data You can save data using its specific data type (store_float(), store_string(), etc.), or using the generic store_var(), which will use Godot’s built-in serialization to encode your data, including complex data like objects (more on this later).\nLet’s start with a small example: saving the player’s high score. We can write a function that we can call whenever the score needs to be saved:\nvar save_path = \"user://score.save\" func save_score(): var file = FileAccess.open(save_path, FileAccess.WRITE) file.store_var(highscore) We’re saving our score, but we need to be able to load it when the game starts:\nfunc load_score(): if FileAccess.file_exists(save_path): print(\"file found\") var file = FileAccess.open(save_path, FileAccess.READ) highscore = file.get_var() else: print(\"file not found\") highscore = 0 Don’t forget to check for the file’s existence before attempting to read from it - it may not be there! If that’s the case, you can use a default value.\nYou can store_var() and get_var() as many times as you need for any number of values.\nSaving Resources The above technique works great when all you need to save are a few values. For more complex situations, you can save your data in a Resource, just like Godot does. Godot saves all its data Resources as .tres files (Animations, TileSets, Shaders, etc.) and you can too!\nTo save and load Resources, use the ResourceSaver and ResourceLoader Godot classes.\nFor this example, let’s say you have all the data about your character’s stats stored in a Resource like this:\nextends Resource class_name PlayerData var level = 1 var experience = 100 var strength = 5 var intelligence = 3 var charisma = 2 You can then save and load like so:\nfunc load_character_data(): if ResourceLoader.exists(save_path): return load(save_path) return null func save_character_data(data): ResourceSaver.save(data, save_path) Resources can contain subresources, so you could have your player’s inventory Resource included as well, and so on.\nWhat about JSON? I see it very often (and some readers may be asking it already): “What if I want to use JSON to save my data?” This is my response:\nDon’t use JSON for your save files!\nWhile Godot has JSON support, saving game data is not what JSON is for. JSON is a data interchange format - its purpose is to allow systems using different data formats and/or languages to exchange data between each other.\nThis means JSON has limitations that are negatives for you when it comes to saving your game data. JSON doesn’t support many data types (no int vs. float, for example) so you have to do a lot of converting and validating to try and save/load your data. It’s cumbersome and time consuming.\nDon’t waste your time. Using Godot’s built-in serialization, you can store native Godot objects - Nodes, Resources, even Scenes - without any effort, which means less code and fewer errors.\nThere’s a reason that Godot itself doesn’t use JSON for saving scenes and resources.\nWrapping up This article just scratches the surface of what you can do with FileAccess. For the full list of available FileAccess methods, see the FileAccess documentation.\n","description":"","tags":null,"title":"Saving/loading data","uri":"/godot_recipes/4.x/basics/file_io/index.html"},{"content":"Problem Many 2D games use a “3/4 view” perspective, giving the impression that the camera is looking at the world at an angle. To make this work, objects that are “farther” away need to be rendered behind “nearer” objects. In practice, that means we want to “y-sort” - making the drawing order tied to the object’s y coordinate. The higher on the screen, the farther away and therefore lower the render order.\nHere’s an example of the problem:\nThese objects are being drawn in the default render order: tree order. They are arranged like this in the scene tree:\nSolution Godot has a built-in option to change the render order: on any CanvasItem node (Node2D or Control), we can enable the Y Sort Enabled property. When this is enabled, all child nodes are then y-sorted.\nIn the above example, we can enable the property on the TileMap node. However, there’s still a problem:\nThe draw order is based on each object’s y coordinate. By default, that is the object’s center:\nSince we want to give the impression that the objects are on the “ground”, we can solve this by offsetting each object’s sprite so that the object’s position is aligned with the bottom of the sprite:\nNow things look a lot better:\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/using_ysort\n","description":"","tags":null,"title":"Using Y-Sort","uri":"/godot_recipes/4.x/2d/using_ysort/index.html"},{"content":"Problem You want a floating “healthbar” for your 3D game objects (mobs, characters, etc.).\nSolution For this solution, we’re going to re-use a 2D healthbar based on a TextureProgressBar node. It’s already set up with textures and code for updating the value and color. If you already have something similar, feel free to use it here. In the example, we’ll name this scene “Healthbar2D”.\nIf you need some assets, here are the three images used in the bar:\nNote Re-using existing objects can save you a lot of time. Don’t re-invent the wheel everytime you need a healthbar, camera, or other common object.\nProject setup For our example “mob”, we’ll start with a CharacterBody3D node. It’s programmed to spawn and travel in a straight line. It also has the following code to handle damage:\nfunc _on_input_event(camera, event, position, normal, shape_idx): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: health -= 1 if health \u003c= 0: queue_free() Clicking on a unit deals one damage. Do ten damage, and the unit is destroyed. Now we need a visual representation of that using our 2D bar.\n2D in 3D We can display a 2D image in 3D using a Sprite3D. Add one to a new scene and name it “Healthbar3D”. First, we’ll get it configured and sized, so set the Texture property to the green bar image.\nThe Sprite3D acts like any other 3D object - as we pan the camera around, our perspective on it changes. However, we want the healthbar to always “face” toward the camera so that we can see it.\nIn the Inspector, under Flags, set Billboard to “Enabled”.\nNow try moving the camera to confirm that the texture is always facing you.\nAdd an instance of this scene to the Mob scene and position the bar above the mob’s body.\nViewport texture We don’t want the Sprite3D to show a static texture - we want it to display the 2D TextureProgressBar. We can do that using a SubViewport node, which can export a texture.\nAdd a SubViewport as a child of the Sprite3D. In the Inspector set Transparent BG to On.\nWe also need to set the size of the viewport to match the size of the healthbar texture, which is (200, 26).\nInstance the HealthBar2D as a child of the Viewport. Your scene should look like this:\nIf the SubViewport were not a child of the Sprite3D, we could set it as the sprite’s texture directly in the Inspector. Since it’s a child, it won’t be ready at the right time, so we’ll need to set it in a script attached to the Sprite3D:\nextends Sprite3D func _ready(): texture = $SubViewport.get_texture() Connecting it all together In the mob’s _on_input_event() method, add the following after reducing the health:\n$HealthBar3D.update(health, max_health) Add the following to HealthBar3D.gd:\nfunc update_health(_value, max_value): $SubViewport/HealthBar2D.update_health(_value) This calls the update method that already exists on the 2D bar, setting the progress bar’s value and selecting the bar color:\nfunc update_health(_value, max_value): value = _value if value \u003c max_value: show() texture_progress = bar_green if value \u003c 0.75 * max_value: texture_progress = bar_yellow if value \u003c 0.45 * max_value: texture_progress = bar_red Click on the mobs to see the health bars change.\nWrapping up You can use this technique to display any other Node2D or Control nodes, such as Label, VideoStreamPlayer, etc. You can even use the SubViewport to “project” an entire 2D game in 3D space.\nDownload This Project Download the project code here: https://github.com/godotrecipes/3d_object_healthbars\n","description":"","tags":null,"title":"3D Unit Healthbars","uri":"/godot_recipes/4.x/3d/healthbars/index.html"},{"content":" Animation Using Godot’s animation system.\nIn this section: Spritesheet animation ","description":"","tags":null,"title":"Animation","uri":"/godot_recipes/4.x/animation/index.html"},{"content":"Problem You want your character body to interact with rigid bodies.\nSolution Note This recipe applies equally well in both 2D and 3D nodes.\nBy default, a CharacterBody2D moved with move_and_slide() or move_and_collide() will not push any RigidBody2D it collides with. The rigid body doesn’t react at all, and behaves just like a StaticBody2D.\nIn some cases, this might be all you need. However, if you want to be able to push the bodies, you’ll need to make some changes.\nFor this example, we’ll use the 2D character described in the Platform character recipe. This example uses the most common movement method for character bodies: move_and_slide(). If you’re using move_and_collide(), you’ll need to adjust the examples below accordingly.\nYou have two options when deciding how to interact with rigid bodies:\nYou can just push them, ignoring physics. If you’re familiar with Godot 3.x, this is equivalent to the “infinite inertia” option. You can give them a push based on the character’s imagined “mass” and velocity. This will give you a “realistic” result - pushing heavy bodies a little, and lighter bodies a lot. We’ll try out both options below.\nInfinite Inertia This option has its pros and cons. The biggest pro is, you don’t need any extra code. You just need to correctly set the collision layers/masks of the objects. For this example, we’ve defined three physics layers:\nFor the rigid body, we’ve placed it on the “items” layer (layer 3), and left the mask at the default (masking all layers):\nThen, we’ve placed the player on the “player” layer (layer 2), and configured the mask to ignore the “items”:\nRunning the game, we now see we can push the boxes around. Note that the mass of the box doesn’t matter - they’ll all be pushed the same.\nHere, you can also see the downside of this option. Because the physics of the boxes is being ignored, they can clip through walls and you can’t jump on top of them.\nFor some games, this will be fine. If you want to prevent the clipping, you’ll need to go with option 2.\nApplying impulses To give the colliding body a “push” we’ll need to apply an impulse. An impulse is an instantaneous “kick” - think of a bat hitting a ball. This is as opposed to a force, which is a continuous “push” on an object.\n# This represents the player's inertia. var push_force = 80.0 func _physics_process(delta): # after calling move_and_slide() for i in get_slide_collision_count(): var c = get_slide_collision(i) if c.get_collider() is RigidBody2D: c.get_collider().apply_central_impulse(-c.get_normal() * push_force) The collision normal points out of the rigid body, so we reverse it to point away from the character and apply the push_force factor. Now pushing works again, but it won’t force the rigid bodies through walls:\nYou’ll need to adjust the push_force in relation to the mass of your rigid bodies. Too high a force will still cause clipping, while too low will prevent pushing at all.\nExperiment to find the settings that work for your particular game.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/character_vs_rigid\nRelated recipes Platform character ","description":"","tags":null,"title":"Character to Rigid Body Interaction","uri":"/godot_recipes/4.x/physics/character_vs_rigid/index.html"},{"content":" Input Handling input - from keyboard and mouse to game controllers and touchscreens.\nIn this section: Input Actions Mouse Input Capturing the Mouse Mouse: Drag-select multiple units ","description":"","tags":null,"title":"Input","uri":"/godot_recipes/4.x/input/index.html"},{"content":" Intro to 3D A gentle introduction to the 3D side of Godot development.\nIn this section: The 3D Editor Importing 3D Objects Creating a 3D Character ","description":"","tags":null,"title":"Intro to 3D","uri":"/godot_recipes/4.x/g101/3d/index.html"},{"content":"This is an evolving list of the main changes and “gotchas” to look out for if you’re transitioning to 4.0.\nNew Names One of the biggest changes in Godot 4 is a whole bunch of renaming - of nodes, functions, and property names. Most of it is done to make things consistent or clear. Here are a few of the biggest ones to watch out for:\n2D/3D nodes - In Godot 3.x, 2D nodes had the “2D” suffix, but 3D nodes had none. This has been made consistent - they all now have “2D” or “3D” suffixes. For example: RigidBody2D vs. RigidBody3D.\nAlso in the category of 3D, the Spatial node is renamed to Node3D to match.\nOne of the most popular nodes, KinematicBody, has been renamed to CharacterBody2D/CharacterBody3D. See below for further changes with this node’s API.\nPackedScene’s instance() function has been renamed to instantiate().\nThe position and global_position properties replace translation and global_translation in 3D, making them consistent with 2D.\nSignals and Callables Working with signals is much more streamlined in 4.0. Signal is a native type now, so you’ll be using fewer strings, meaning you get autocomplete and error checking. This applies to functions as well, which can now be directly referenced rather than using strings.\nHere’s an example of defining, connecting, and emitting a signal.\nextends Node signal my_signal func _ready(): my_signal.connect(signal_handler) func _input(event): if event.is_action_pressed(\"ui_select\"): my_signal.emit() func signal_handler(): print(\"signal received\") Tweens If you started using SceneTreeTween in Godot 3.5, then you’ll be familiar with Godot 4.0’s Tween usage.\nTween is no longer a node. Instead, you create one-off tween animation objects whenever you need them. Once you get used to it, it’s a lot more powerful and easier to use than the old method.\nAnimatedSprite[2D|3D] The biggest change that catches people who are familiar with the 3.x version of this node is that the playing property is gone. It’s now much more consistent with AnimationPlayer’s usage - to automatically play an animation, you can toggle autoplay in the SpriteFrames panel. In code, use play() and stop() to control playback.\nCharacterBody[2D|3D] The biggest change in this node is in using move_and_slide(). It no longer takes any parameters - they are all now built-in properties. This includes a native velocity property, so you no longer need to declare your own.\nFor detailed examples of using these nodes, see Platform Character and/or Basic FPS Character.\nTileMap The TileMap node is completely overhauled for 4.0. Just about everything, from how you create TileSets to how you draw and interact with tiles is 100% new.\nOur “Using TileMaps” guide is coming soon.\nRNG There are a few changes to GDScript’s built-in random number generator functions:\nYou no longer need to call randomize() - this is automatic. If you do want repeatable “randomness”, use seed() to set it to a preselected value.\nrand_range() is now replaced with either randf_range() (for floats) or randi_range() (for ints).\nRaycasting When casting rays in code, there’s a new API. PhysicsDirectSpaceState[2D|3D].intersect_ray() now takes a special object as a parameter. This object specifies the ray properties. For example, to cast a ray in 3D:\nvar space = get_world_3d().direct_space_state var ray = PhysicsRayQueryParameters3D.create(position, destination) var collision = space.intersect_ray(ray) if collision: print(\"ray collided\") ","description":"","tags":null,"title":"Migrating from 3.x","uri":"/godot_recipes/4.x/basics/migrating/index.html"},{"content":"Problem You need moving platforms in your 2D platformer.\nSolution There are several ways to approach this problem. In this recipe, we’ll use AnimatableBody2Ds for our platform and move it with a Tween. This allows for a variety of movement styles while minimizing the amount of code we need to write.\nInfo You can also implement this moving platform technique using an AnimationPlayer rather than a tween. Much of the setup will be the same, but rather than tween code, you’ll animate the body’s position property.\nSetting up We’ll start with a basic platformer setup using the Platform character recipe. The basic movement from that recipe will work fine with the platforms. If you’ve modified it or used your own, everything should still work the same.\nCreating the platform The platform scene contains the following nodes:\nNode2D (“MovingPlatform”): The Node2D parent is there to act as the “anchor” or start point for the platform. We’ll animate the platform’s position relative to this parent node. AnimatableBody2D: This represents the platform itself. This is the node that will move. Sprite2D: You can use a sprite sheet here, individual images, or even a TileMap. CollisionShape2D: Don’t make the hitbox too big, or the player will appear to be “hovering” off the edge of the platform. Set up the Sprite2D’s Texture and the collision shape appropriately. In the AnimatableBody2D, set the Sync to Physics property “On”. Since we’re moving the body in code, this ensures that it’s moved during the physics step, keeping it in sync with the player and other physics bodies.\nNow add a script to the root Node2D:\nextends Node2D @export var offset = Vector2(0, -320) @export var duration = 5.0 func _ready(): start_tween() func start_tween(): var tween = get_tree().create_tween().set_process_mode(Tween.TWEEN_PROCESS_PHYSICS) tween.set_loops().set_parallel(false) tween.tween_property($AnimatableBody2d, \"position\", offset, duration / 2) tween.tween_property($AnimatableBody2d, \"position\", Vector2.ZERO, duration / 2) We’ve used a few of Tween’s options here to make everything work smoothly:\nset_process_mode(): ensures that all movement takes place during the physics processing step. set_loops(): this makes the tween repeat. set_parallel(false): by default, all tween_property() changes would happen at that same time. This makes the two happen one after another: moving to one end of the offset, then back to the start. Using the two exported properties, you can adjust the platform’s movement. Set the offset to determine where the tween moves relative to its starting point, and the duration to determine how long it takes to complete the cycle.\nAdd some platforms in your level/world and try them out:\nDownload This Project Download the project code here: https://github.com/godotrecipes/2d_moving_platforms\nRelated recipes Platform character Like video? Coming soon\n","description":"","tags":null,"title":"Moving Platforms","uri":"/godot_recipes/4.x/2d/moving_platforms/index.html"},{"content":"Problem You have a grid-based environment and you’d like to set up pathfinding to allow navigation.\nSolution Godot provides a number of methods for pathfinding. For this recipe, we’ll consider the A* algorithm.\nAbout A* A* is a widely-used algorithm for finding the shortest path between two points. It can be used in any graph-based data structure, not just a grid.\nAStarGrid2D is a specialized version of Godot’s more generic AStar2D class. Because it’s specialized for using with a grid, it’s quicker and easier to set up because you don’t have to manually add all the individual grid cells and their connections.\nSetting up the Grid The most important configuration decision is the size of the cells and the size of the grid itself. We’ll use (64, 64) for this example, and we’ll use the window size to determine how many cells fit on the screen, but everything will work the same regardless of cell size.\nAdd this code to a Node2D.\nextends Node2D @export var cell_size = Vector2i(64, 64) var astar_grid = AStarGrid2D.new() var grid_size func _ready(): initialize_grid() func initialize_grid(): grid_size = Vector2i(get_viewport_rect().size) / cell_size astar_grid.size = grid_size astar_grid.cell_size = cell_size astar_grid.offset = cell_size / 2 astar_grid.update() In this code, we divide the size of the screen by the cell_size to calculate how big the whole grid will be. This lets us set the size property of the AStarGrid2D.\nThe offset property will come into play when we ask for a path between two points. Using cell_size / 2 means the path will be calculated from the center of each cell rather than the corners.\nFinally, we need to call update() after setting or changing any of the AStarGrid2D’s properties.\nDrawing the Grid For the purposes of this demo, we’ll draw the grid on the screen in code. In a game application, you’ll probably have a TileMap or some other visual representation of your world.\nHere’s some code to draw the grid:\nfunc _draw(): draw_grid() func draw_grid(): for x in grid_size.x + 1: draw_line(Vector2(x * cell_size.x, 0), Vector2(x * cell_size.x, grid_size.y * cell_size.y), Color.DARK_GRAY, 2.0) for y in grid_size.y + 1: draw_line(Vector2(0, y * cell_size.y), Vector2(grid_size.x * cell_size.x, y * cell_size.y), Color.DARK_GRAY, 2.0) This gives us a nice visual of the grid:\nDrawing the Path In order to find a path, we need a start and end point. Add these variables at the top of the script:\nvar start = Vector2i.ZERO var end = Vector2i(5, 5) And a couple of lines in _draw() to show them:\ndraw_rect(Rect2(start * cell_size, cell_size), Color.GREEN_YELLOW) draw_rect(Rect2(end * cell_size, cell_size), Color.ORANGE_RED) We can find the path between the two points using the get_point_path() method, but we also need to visualize it. We can use a Line2D, so add one to the scene.\nHere’s how we can get the path, and add the resulting points to the Line2D:\nfunc update_path(): $Line2D.points = PackedVector2Array(astar_grid.get_point_path(start, end)) Here’s the result:\nNote that we have a diagonal line between the two points. This is because, by default, the path will use diagonals. This can be modified by changing the diagonal_mode:\nDIAGONAL_MODE_ALWAYS - The default value, uses diagonals. DIAGONAL_MODE_NEVER - All movement is orthogonal. DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE - This allows diagonals, but prevents the path going “between” diagonally placed obstacles. DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES - This allows diagonals only in “open” areas, not near obstacles. Modifying this property can give you very different results, so make sure to experiment based on your setup. Let’s add this in the initialize_grid() function:\nastar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER Now we only have orthogonal moves:\nAdding Obstacles We can also add obstacles to the grid. By marking a cell as “solid”, the path will not include that cell. A cell can be toggled solid/not solid by using the set_point_solid() function.\nLet’s add some code to draw our walls (when they exist), by finding any solid cells and coloring them in:\nfunc fill_walls(): for x in grid_size.x: for y in grid_size.y: if astar_grid.is_point_solid(Vector2i(x, y)): draw_rect(Rect2(x * cell_size.x, y * cell_size.y, cell_size.x, cell_size.y), Color.DARK_GRAY) Call this function in _draw().\nThen, we can use the mouse to click on cells and toggle their state:\nfunc _input(event): if event is InputEventMouseButton: # Add/remove wall if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: var pos = Vector2i(event.position) / cell_size if astar_grid.is_in_boundsv(pos): astar_grid.set_point_solid(pos, not astar_grid.is_point_solid(pos)) update_path() queue_redraw() Note that we’re checking is_in_boundsv() first - this will prevent errors from being thrown if we click outside the grid boundaries.\nNow we can see the effect of obstacles on the path:\nChoosing a Heuristic A big factor that affects the resulting path is what heuristic you choose to use. The term “heuristic” refers to a “best guess”, and in the context of pathfinding just means: what direction should we try first when moving toward the goal?\nFor example, the Euclidean distance uses the Pythagorean theorem to estimate the path to try:\nWhile Manhattan distance only considers distance in N/S or E/W directions:\nAnd the Octile heuristic results in a path like this:\nYou can choose the heuristic using this property:\nastar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_OCTILE Which of these works best (results in the most pleasing paths) depends on the nature of your environment. Is it mostly wide-open spaces with few obstacles scattered around? Or is it a maze of twisty passages? Make sure to experiment with your specific project.\nDownload the example project below to experiment with this setup yourself. In addition to placing walls, you can use the right/middle mouse buttons to move the end/start locations.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/grid_pathfinding\n","description":"","tags":null,"title":"Pathfinding on a 2D Grid","uri":"/godot_recipes/4.x/2d/grid_pathfinding/index.html"},{"content":"The Bullet scene provides us with a reusable object we can instantiate whenever the player shoots.\nAdding to the player Let’s head back to the Player script and add a few new variables:\n@export var cooldown = 0.25 @export var bullet_scene : PackedScene var can_shoot = true The two @export variables let you configure them in the Inspector so that you can adjust the cooldown time. Set the bullet_scene by clicking the property and choosing the bullet.tscn file.\ncan_shoot is what programmers call a flag - a Boolean variable that controls a certain condition. In this case it determines whether the player is allowed to shoot or not. During the cooldown period, this variable will be false.\nNext, we’ll add a start() function similar to the one we made for the Bullet. This will let us set initial values for the player, as well as resetting them when the game restarts.\nfunc _ready(): start() func start(): position = Vector2(screensize.x / 2, screensize.y - 64) $GunCooldown.wait_time = cooldown This places the player at the bottom center of the screen - a good place to start. It also ensures that the cooldown timer has the correct wait time.\nThe shoot() function will be called whenever we press the “shoot” input.\nfunc shoot(): if not can_shoot: return can_shoot = false $GunCooldown.start() var b = bullet_scene.instantiate() get_tree().root.add_child(b) b.start(position + Vector2(0, -8)) The first thing this function does is check if the player is allowed to shoot. If it isn’t, return will end the function immediately.\nIf the player is allowed to shoot, then we set the flag to false, and start the cooldown timer. Then we create a new bullet and add it to the game, calling its start() function to make sure it’s placed in the correct position (just above the player’s ship).\nWe can call this function when the player is pressing the key. Add this to the end of the _process() function, after the position.clamp() line:\nif Input.is_action_pressed(\"shoot\"): shoot() We’ll also need to connect the timeout signal of GunCooldown.\nfunc _on_gun_cooldown_timeout(): can_shoot = true When the cooldown ends, we can allow shooting again.\nGo ahead and run the scene and try pressing the shoot action.\nAdding instances to the three Notice that we’ve added the new bullets as children of the SceneTree root (get_tree().root), and not to the player ship. This is important because if we made the bullets children of the ship, then they would be “attached” to it when it moves.\nNext steps Shooting’s no fun without something to shoot at. We’ll start making the enemies soon, but first we need a scene where we can bring the player, enemies, and other game objects together.\n","description":"","tags":null,"title":"Shooting","uri":"/godot_recipes/4.x/games/first_2d/first_2d_05/index.html"},{"content":" UI Building user interfaces.\nIn this section: Level Select Menu ","description":"","tags":null,"title":"UI","uri":"/godot_recipes/4.x/ui/index.html"},{"content":" Gamedev Math Math is a big part of game development. Some of it you may remember from school, or it may be something you’ve never encountered before. Here you’ll find guides to help you get up to speed and examples of how these concepts are applied to making games.\nIn this section: Interpolation Transforms Vectors: Using Dot and Cross Product ","description":"","tags":null,"title":"Gamedev Math","uri":"/godot_recipes/4.x/math/index.html"},{"content":" AI/Behavior Automated behavior and (sometimes) smarter entities.\nIn this section: Homing missile ","description":"","tags":null,"title":"AI/Behavior","uri":"/godot_recipes/4.x/ai/index.html"},{"content":"Before we can make enemies, powerups, or any other game objects, we need a place where they can all exist together with the player. In most games, this would be called a “level” or “main” scene, and that’s what we’ll call it here.\nStart the scene with a Node2D called “Main” and save it.\nCreating the background Add a Sprite2D child. Name this sprite “Background” and add the Space_BG (2 frames) (64 x 64).png as its texture.\nThis image has two frames, each 64x64 pixels in size. We’d like the image to tile across the full size of the screen, so start with the following settings:\nUnder Offset set Centered to “off”. This makes the image’s top left corner start at the origin rather than its center.\nUnder Region, turn Enabled “on”, and then set the Rect to a width of 240 and a height of 320. This makes the image stretch to the size of the screen.\nUnder Texture change Repeat to Enabled. This causes the image to repeat over the full size of the screen.\nNow add the player to the scene by selecting the Main node and clicking the Instantiate Child Scene button.\nAnimating the background We can make the scene more dynamic by animating the background. While we could do this in code by changing the region_rect property every frame, we’ll use an AnimationPlayer node instead; add one as a child of Main.\nAt the bottom of the editor window, you’ll see the Animation panel. There’s a lot of information there, so let’s look at how it’s laid out:\nClick the Animation button and choose New Animation. You can name the new animation scroll. Set its Length to 2 and toggle the Looping and Autoplay buttons.\nAnimations work by adding tracks that represent properties that you want the AnimationPlayer to control. In the timeline of the player, you’ll add keyframes that define what value you want the property to have at that particular time.\nWe can add keyframes to the animation by clicking the key icon that now appears next to every property in the Inspector. Make sure the scrubber (the blue indicator on the timeline) is at time 0, then select the Background and click the key next to Region/Rect. You’ll be asked if you want to create a new track and then you’ll see the new track added to the animation panel, with a small dot representing the keyframe you’ve just added. Drag the scrubber to time 2 and then change the y value of the Region/Rect property to 64. Click the key to add another keyframe.\nNow when you press Play on the animation, you should see the background slowly scrolling behind the player.\nNext steps The main scene is now ready for us to add enemies. In the next step we’ll make a single enemy scene, as we did with the bullets, and then instantiate that multiple times.\n","description":"","tags":null,"title":"Main Scene","uri":"/godot_recipes/4.x/games/first_2d/first_2d_06/index.html"},{"content":" Physics Learn how to use Godot’s physics nodes.\nIn this section: RigidBody2D: Drag and Drop Character to Rigid Body Interaction Asteroids-style Physics (using RigidBody2D) ","description":"","tags":null,"title":"Physics","uri":"/godot_recipes/4.x/physics/index.html"},{"content":"Problem You want to make a rolling cube in 3D.\nSolution Rolling a cube is trickier than it seems. You can’t just rotate the cube around its center:\nInstead, the cube needs to be rotated around its bottom edge.\nHere’s the tricky part: which bottom edge? It depends on which direction the cube is rolling.\nIn preparing this recipe, I experimented with a few different solutions to this problem:\nPure math - calculating and applying rotation transforms AnimationPlayer - using animations to key the rotations and offsets Helper nodes - using Spatial(s) as rotation helpers They all worked fine, but I found the last option the most flexible and easiest to adapt, so that’s what we’ll do here.\nNode setup Cube: CharacterBody3D Pivot: Node3D Mesh: MeshInstance3D Collision: CollisionShape3D Tip You can do this with RigidBody3D, CharacterBody3D, or Area3D as your collision node. There will be minor differences in how you handle movement. Which node you choose should depend on what other behavior you want in your game. For this recipe, we’re only concerned with the movement.\nBy default, everything is centered at (0, 0, 0) so the first thing we’re going to do is offset everything so that the bottom center of the cube is the CharacterBody3D’s position.\nThe default size of a BoxMesh3D is (1, 1, 1), so do this, move the mesh and collision nodes both up to (0, 0.5, 0), leaving the rest where they are. Now when you select the root node, its position will be the bottom of the cube:\nNow when you want to roll the cube, you’ll need to move the Pivot 0.5 in the direction you want to move. Since the mesh is attached, you need to move it the opposite amount. For example, to roll to the right (+X), you’ll end up with this:\nNow the pivot node is at the correct edge and rotating it will also rotate the mesh.\nMovement script The movement is broken in to 3 steps:\nStep 1 Here we apply the two offsets shown above: shift the Pivot in the direction of movement, and shift the Mesh in the opposite direction.\nStep 2 In this step we animate the rotation. We find the axis of rotation using the cross product of the direction and the down vector. Then we use a Tween to animate rotating the pivot’s transform.\nStep 3 Finally, once the animation has finished, we need to reset everything so that it’s ready to happen again. In the end, we want to have the cube moved 1 unit in the chosen direction (for a cube of size 1) and have the pivot and mesh back at their original positions.\nextends CharacterBody3D @onready var pivot = $Pivot @onready var mesh = $Pivot/MeshInstance3D var cube_size = 1.0 var speed = 4.0 var rolling = false func _physics_process(delta): var forward = Vector3.FORWARD if Input.is_action_pressed(\"ui_up\"): roll(forward) if Input.is_action_pressed(\"ui_down\"): roll(-forward) if Input.is_action_pressed(\"ui_right\"): roll(forward.cross(Vector3.UP)) if Input.is_action_pressed(\"ui_left\"): roll(-forward.cross(Vector3.UP)) func roll(dir): # Do nothing if we're currently rolling. if rolling: return rolling = true # Step 1: Offset the pivot. pivot.translate(dir * cube_size / 2) mesh.global_translate(-dir * cube_size / 2) # Step 2: Animate the rotation. var axis = dir.cross(Vector3.DOWN) var tween = create_tween() tween.tween_property(pivot, \"transform\", pivot.transform.rotated_local(axis, PI/2), 1 / speed) await tween.finished # Step 3: Finalize the movement and reset the offset. transform.origin += dir * cube_size var b = mesh.global_transform.basis pivot.transform = Transform3D.IDENTITY mesh.position = Vector3(0, cube_size / 2, 0) mesh.global_transform.basis = b rolling = false If your cube’s texture isn’t symmetrical, you may notice that it’s resetting after every roll. To preserve the rotation of the mesh, add the following:\nIn Step 1:\nChange mesh.translate(-dir) to mesh.global_translate(-dir).\nIn Step 3:\nAdd two lines to keep the mesh rotation after reset:\n# Step 3: Finalize the movement and reset the offset. transform.origin += dir * cube_size var b = mesh.global_transform.basis # Save the mesh rotation. pivot.transform = Transform3D.IDENTITY mesh.position = Vector3(0, cube_size / 2, 0) mesh.global_transform.basis = b # Restore the mesh rotation. Checking for collisions If you plan to have obstacles in your game, you can check for collisions before moving (similar to any other grid-based movement scheme). Add a raycast check before Step 1 of the move:\n# Cast a ray before moving to check for obstacles var space = get_world_3d().direct_space_state var ray = PhysicsRayQueryParameters3D.create(mesh.global_position, mesh.global_position + dir * cube_size, collision_mask, [self]) var collision = space.intersect_ray(ray) if collision: return Note You could also use a RayCast3D node. Just remember to call force_raycast_update() before checking.\nPlaying with transitions You can add a lot of “personality” to the cube’s rolling behavior by changing which TransitionType you use. The default is Tween.TRANS_LINEAR, which results in a constant speed throughout the movement.\nBy setting a different transition type, you can get a very different feel. For example:\nvar tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN) Download This Project Download the project code here: https://github.com/godotrecipes/rolling_cube\nRelated recipes Transforms ","description":"","tags":null,"title":"Rolling Cube","uri":"/godot_recipes/4.x/3d/rolling_cube/index.html"},{"content":" Audio Helpful recipes for adding sound effects and music to your game.\nIn this section: Audio Manager ","description":"","tags":null,"title":"Audio","uri":"/godot_recipes/4.x/audio/index.html"},{"content":"Now that our enemy can shoot, let’s give them something to shoot at.\nSetting up the scene We’ll use an Area2D for the enemy, since we need it to detect overlap - either with the player’s bullets, or with the player itself.\nHere’s are the nodes we’ll need:\nEnemy: Area2D Sprite2D CollisionShape2D AnimationPlayer MoveTimer: Timer ShootTimer: Timer Select the area node and click the Node tab next to the Inspector. Under Groups, type “enemies” an click Add. Remember the code we wrote on the bullet? It looks for objects in the “enemies” group.\nIn the sprite’s Texture, add Bon_Bon (16 x 16).png and set its Animation/Hframes to 4.\nAs you’ve done before, add a rectangular collision shape and size it to fit. Enable One Shot on both timer nodes.\nIn the AnimationPlayer, add an animation called “bounce” and set it to looping and autoplay. Set the Snap at the bottom of the animation panel to 0.05.\nSelect the sprite node and press the key icons next to Texture and Hframes to create tracks for them. We’re doing this because later we’ll add an “explosion” animation that will use different values for these properties.\nNow we’ll key the individual Frames values we want. Start with keying Frames each .1 seconds to values in this order2, 1, 0, 3. Finally, key 0 again and put it immediately after. This will make a “pulsing” animation where the sprite grows and then bounces a little at the end. The animation setup should look like this:\nPress the play button to see it in action. Feel free to adjust it if you’d like.\nNow add another animation called “explode”. Set its length to 0.4 seconds.\nChange the sprite’s Texture to Explosion (16 x 16).png and keyframe that property. Since this image has a different number of frames than the enemy image, we also need to change Hframes to 6 and keyframe that.\nNow keyframe Frame to 0 at time 0 and to 5 at time 0.4. Play the animation to see it in action.\nEnemy script The enemies will spawn at the top of the screen in a grid. After a random amount of time, they’ll descend toward the player and then return to the top if they weren’t destroyed. Periodically, they’ll also shoot at the player.\nAdd a script, and start with the variables:\nextends Area2D var start_pos = Vector2.ZERO var speed = 0 @onready var screensize = get_viewport_rect().size The start_pos variable is going to keep track of the enemy’s starting position so that after it moves, it can return to its original location. We’ll set it when the enemy is spawned and we call its start() function.\nfunc start(pos): speed = 0 position = Vector2(pos.x, -pos.y) start_pos = pos await get_tree().create_timer(randf_range(0.25, 0.55)).timeout var tween = create_tween().set_trans(Tween.TRANS_BACK) tween.tween_property(self, \"position:y\", start_pos.y, 1.4) await tween.finished $MoveTimer.wait_time = randf_range(5, 20) $MoveTimer.start() $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() When we spawn our enemies we’ll call this function and pass it a position vector representing where on the screen the enemy should go. Note that we actually spawn it above the top of the screen (negative y value). This is so that we can animate it coming onto the screen using a tween. We also randomize the two timers so that all enemies won’t be moving and shooting at the same time.\nConnect both of the timers’ timeout signals.\nfunc _on_timer_timeout(): speed = randf_range(75, 100) func _on_shoot_timer_timeout(): $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() We can start moving when the timer runs out, and we’ll also shoot, but we haven’t made a bullet yet, so that part will come later. Now that we’re changing the speed, we can move using it.\nfunc _process(delta): position.y += speed * delta if position.y \u003e screensize.y + 32: start(start_pos) Now if the speed isn’t 0, we’ll see the enemy move down the screen. When it goes off the bottom, we start it all over again.\nWe’ve already written the code in the bullet scene that calls explode() on the enemies it hits, so let’s add that too.\nfunc explode(): speed = 0 $AnimationPlayer.play(\"explode\") set_deferred(\"monitoring\", false) died.emit(5) await $AnimationPlayer.animation_finished queue_free() In this function, we stop moving, play the explosion animation, and then delete the enemy when it’s finished. The set_deferred() call makes sure to turn off monitoring on the enemy. This is so that while the enemy is exploding, another bullet can’t hit it again.\nAdd the died signal at the top of the script:\nsignal died We’ll use that signal to let the main scene know that the player just earned some points.\nSpawning enemies Now let’s go to the Main scene and add these enemies to the game. Add a script to Main and start by loading the enemy scene:\nextends Node2D var enemy = preload(\"res://enemy.tscn\") var score = 0 Spawning enemies ordinarily won’t happen until we’ve pressed the “Start” button to begin the game, but since we haven’t made that yet, we’ll just spawn them immediately:\nfunc _ready(): spawn_enemies() func spawn_enemies(): for x in range(9): for y in range(3): var e = enemy.instantiate() var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16) add_child(e) e.start(pos) e.died.connect(_on_enemy_died) This makes 27 enemies and positions them in a grid in the top half of the screen. We also make sure to connect the died signal of each, so we need to create that function:\nfunc _on_enemy_died(value): score += value We don’t have a way to display the score yet, but we’ll get to that soon.\nPlay the scene and you should see a bunch of enemies appear at the top and periodically fall down the screen. Next, we’ll make them shoot.\n","description":"","tags":null,"title":"Enemies","uri":"/godot_recipes/4.x/games/first_2d/first_2d_07/index.html"},{"content":"Problem You want to click-and-drag to select multiple units, RTS style.\nSolution Realtime strategy (RTS) games often require giving orders to many units at once. A typical style of selecting multiple units is to click-and-drag a box around them. Once the units are selected, clicking on the map commands them to move.\nHere’s an example of what we’re going for:\nUnit setup To test this out, we’ll need some basic RTS-style units. They are set up to move towards a target and to avoid running into each other. We won’t go into too much detail on them in this tutorial. The unit script is commented if you’d like to use it as a base for creating your own RTS units. See below for a link to download the project.\nWorld setup Processing the unit selection will happen in the world. We’ll start with a Node2D called “World” and add a few Unit instances in it. Attach a script to the World node and add the following variables:\nextends Node2D var dragging = false # Are we currently dragging? var selected = [] # Array of selected units. var drag_start = Vector2.ZERO # Location where drag began. var select_rect = RectangleShape2D.new() # Collision shape for drag box. Note that once we’ve drawn the box, we’ll need a way to find what units are inside it. The RectangleShape2D will allow us to query the physics engine and see what we collided with.\nDrawing the box We’ll be using the left mouse button for this. Clicking starts a drag and then letting go ends it. During dragging, we’ll draw the rectangle for visibility.\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: # If the mouse was clicked and nothing is selected, start dragging if selected.size() == 0: dragging = true drag_start = event.position # If the mouse is released and is dragging, stop dragging elif dragging: dragging = false queue_redraw() if event is InputEventMouseMotion and dragging: queue_redraw() func _draw(): if dragging: draw_rect(Rect2(drag_start, get_global_mouse_position() - drag_start), Color.YELLOW, false, 2.0) Selecting the units Now that we’ve got a selection box, we need to find the units that are inside it. When we release the button and the drag ends, we must query the physics space to find the units. Note that the units are CharacterBody2D, but Area2D or other bodies would work as well.\nWe’ll use PhysicsDirectSpaceState2D.intersect_shape() to find the units. This requires a shape (our rectangle) and a transform (our location). See Godot docs for details.\nelif dragging: dragging = false queue_redraw() var drag_end = event.position select_rect.extents = abs(drag_end - drag_start) / 2 We start by recording the location when we released the button, and use that to set the RectangleShape2D’s extents (remember: extents are measured from the rectangle’s center, so they’re half the full width/height).\nvar space = get_world_2d().direct_space_state var query = PhysicsShapeQueryParameters2D.new() q.shape = select_rect q.collision_mask = 2 # Units are on collision layer 2 q.transform = Transform2D(0, (drag_end + drag_start) / 2) selected = space.intersect_shape(query) Now we get a reference to the physics state and set up our shape query using PhysicsShapeQueryParameters2D, assigning it our shape, and using the center of the dragged area as the origin for the query’s transform. Our result after calling intersect_shape() is an array of dictionaries, which looks like this:\n[{ \"rid\": RID(4093103833089), \"collider_id\": 32145147326, \"collider\": Unit2:\u003cCharacterBody2D#32145147326\u003e, \"shape\": 0 }, { \"rid\": RID(4123168604162), \"collider_id\": 32229033411, \"collider\": Unit3:\u003cCharacterBody2D#32229033411\u003e, \"shape\": 0 }] Each of those collider items is a reference to a unit, so we can use this to notify them that they’ve been selected, activating the outline shader:\nfor item in selected: item.collider.selected = true Commanding the units Finally, we can command the selected units to move by clicking somewhere on the screen:\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: # If the mouse was clicked and nothing is selected, start dragging if selected.size() == 0: dragging = true drag_start = event.position # Otherwise a click tells the selected units to move else: for item in selected: item.collider.target = event.position item.collider.selected = false selected = [] The else clause here triggers if we click the mouse when selected is greater than 0. Each item’s target is set, and we make sure to deselect the units so we can start again.\nWrapping up This technique can be expanded to a wide range of RTS or other game styles. Download the full project below and use it as a base for your own game.\nDownload This Project Download the project code here: https://github.com/godotrecipes/multi_unit_select\nRelated recipes Mouse Input ","description":"","tags":null,"title":"Mouse: Drag-select multiple units","uri":"/godot_recipes/4.x/input/multi_unit_select/index.html"},{"content":"Now that our enemy can shoot, let’s give them something to shoot at.\nEnemy bullet scene Make a new EnemyBullet scene just like you made the player bullet earlier. We won’t go into all the steps here, but you can refer back to that part if you’re stuck. The only difference here is that you can use the Enemy_projectile (16 x 16).png image instead.\nThe script will be a little bit different:\nextends Area2D @export var speed = 150 func start(pos): position = pos func _process(delta): position.y += speed * delta Connect the screen_exited and area_entered signals of the VisibleOnScreenNotifier2D and Area2D, respectively:\nfunc _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() func _on_area_entered(area): if area.name == \"Ship\": queue_free() Notice that we’re detecting the hit on the player, but it’s not doing anything yet. We’ll come back to that once we add a way for the player to take damage.\nAdding shooting to the enemy At the top of the enemy’s script, load the new bullet:\nvar bullet_scene = preload(\"res://enemy_bullet.tscn\") Then update the shooting function:\nfunc _on_shoot_timer_timeout(): var b = bullet_scene.instantiate() get_tree().root.add_child(b) b.start(position) $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() Play the Main scene again and you should have some random enemy bullets appearing.\n","description":"","tags":null,"title":"Enemy Shooting","uri":"/godot_recipes/4.x/games/first_2d/first_2d_08/index.html"},{"content":"Problem You want to make a 3D spaceship that flies in an arcade/cinematic way. You’re not looking for realistic physics, but more of a dog-fighting, “Star Wars”-style of spaceflight.\nSolution To accomplish this, we’ll use a CharacterBody3D for the ship. The three axis inputs (pitch, roll, and yaw) will rotate the body’s basis around the corresponding axis. The direction of motion will always point forward.\nNote You can do this with RigidBody3D and get the same results. See the example project linked below, which includes a rigid body version as well.\nAssets Spaceship models are from this asset pack:\nUltimate Spaceships Pack by Quaternius\nI’ve chosen the “Executioner” ship model:\nFeel free to choose your favorite design.\nSetup Select the gltf file of the ship you want, and click the Import tab. Change the Root Type to CharacterBody3D and click “Reimport”. Then double-click the gltf and you’ll have a new inherited scene with a CharacterBody3D root and a MeshInstance child. Add a CollisionShape3D to the body.\nIn Project Settings -\u003e Input Map, set up the following inputs:\nroll_right / roll_left pitch_up / pitch_down yaw_right / yaw_left throttle_up / throttle_down You can assign keys or controller inputs. Analog stick inputs will work best.\nMovement To start the script, let’s handle the forward movement. Pressing the throttle buttons smoothly increases/decreases the speed.\nextends CharacterBody @export var max_speed = 50.0 @export var acceleration = 0.6 var forward_speed = 0 func get_input(delta): if Input.is_action_pressed(\"throttle_up\"): forward_speed = lerp(forward_speed, max_speed, acceleration * delta) if Input.is_action_pressed(\"throttle_down\"): forward_speed = lerp(forward_speed, 0, acceleration * delta) func _physics_process(delta): get_input(delta) velocity = -transform.basis.z * forward_speed move_and_collide(velocity * delta) Make a test scene with a Camera3D to try it out. You can use a stationary camera or a chase camera. Check that the ship accelerates and slows before moving on to the next step.\nRotation Now we can handle rotation in the three axes. Add the following variables at the top of the script:\n@export var pitch_speed = 1.5 @export var roll_speed = 1.9 @export var yaw_speed = 1.25 var pitch_input = 0.0 var roll_input = 0.0 var yaw_input = 0.0 The three axis speeds will affect the “handling” of the ship. Experiment to find values the work for you and your desired flight style.\nNext, add these lines to get_input() to capture the three axis inputs:\npitch_input = Input.get_axis(\"pitch_down\", \"pitch_up\") roll_input = Input.get_axis(\"roll_right\", \"roll_left\") yaw_input = Input.get_axis(\"yaw_right\", \"yaw_left\") Finally, we need to rotate the ship’s Basis according to the inputs. Note how each input affects one axis of rotation:\ntransform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta) transform.basis = transform.basis.orthonormalized() Improvements Currently the rotations are a little to “sharp”. The ship starts and stops rotating instantly, which feels a bit too unnatural. We can solve this with lerp(), and by adding one more configuration variable to set how “floaty” we’d like the controls to be:\n@export var input_response = 8.0 Change the three axis inputs in get_input() to the following:\npitch_input = lerp(pitch_input, Input.get_axis(\"pitch_down\", \"pitch_up\"), input_response * delta) roll_input = lerp(roll_input, Input.get_axis(\"roll_right\", \"roll_left\"), input_response * delta) yaw_input = lerp(yaw_input, Input.get_axis(\"yaw_right\", \"yaw_left\"), input_response * delta) Now when stopping or changing direction, there’s a little bit of inertia.\nLinking roll/yaw One problem with this control scheme is that it’s awkward. Having to use a separate stick for the yaw input makes it difficult to control, especially when also shooting and using other controls. Many games solve this by linking the roll input to also apply a small amount of yaw. To do this, change the yaw_speed to around 1/4 to 1/2 of the roll_speed.\nIn the get_input() function, change the line getting yaw_input to the following:\nyaw_input = roll_input This is another fun place to experiment by changing the roll and yaw speeds. For example, what if yaw was primary and roll smaller? What if other axes were linked? If your game has different ships, you can give them different values for variety in flight styles/performance.\nWrapping up That’s it, now you can fly! This controller is a great start for whatever space-based game you might have in mind. Add some other ships, and a few effects, and you’re ready go:\nFull script Here’s the complete script:\nextends CharacterBody3D @export var max_speed = 50.0 @export var acceleration = 0.6 @export var pitch_speed = 1.5 @export var roll_speed = 1.9 @export var yaw_speed = 1.25 # Set lower for linked roll/yaw @export var input_response = 8.0 var forward_speed = 0.0 var pitch_input = 0.0 var roll_input = 0.0 var yaw_input = 0.0 func get_input(delta): if Input.is_action_pressed(\"throttle_up\"): forward_speed = lerp(forward_speed, max_speed, acceleration * delta) if Input.is_action_pressed(\"throttle_down\"): forward_speed = lerp(forward_speed, 0.0, acceleration * delta) pitch_input = lerp(pitch_input, Input.get_axis(\"pitch_down\", \"pitch_up\"), input_response * delta) roll_input = lerp(roll_input, Input.get_axis(\"roll_right\", \"roll_left\"), input_response * delta) # yaw_input = lerp(yaw_input, Input.get_axis(\"yaw_right\", \"yaw_left\"), input_response * delta) yaw_input = roll_input func _physics_process(delta): get_input(delta) transform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta) transform.basis = transform.basis.orthonormalized() velocity = -transform.basis.z * forward_speed move_and_collide(velocity * delta) Related recipes Interpolated Camera Download This Project Download the project code here: https://github.com/godotrecipes/3d_spaceship\n","description":"","tags":[],"title":"Arcade-style Spaceship","uri":"/godot_recipes/4.x/3d/spaceship/index.html"},{"content":"The last main piece of our game is the user interface (UI). We need a way to show the player the score and other information. To do this, we’ll use a variety of Control nodes - the nodes Godot provides for building UIs.\nUI scene Start the scene with a MarginContainer and name it UI.\nContainers are Control nodes that are designed to control the size and position of their children. Using them makes it easier to position and move Control nodes without having to do it manually. The MarginContainer makes sure its children don’t get too close to the edge.\nIn the Inspector under Theme Overrides/Constants set all four Margin values to 10. Then, in the menu bar at the top of the viewport, set the anchors to the Top Wide preset.\nNext, we’ll add an HBoxContainer. This type of container organizes its children horizontally. Add a TextureProgressBar, which will represent our ship’s shield level. Name it ShieldBar.\nUnfortunately, there’s not a good image in the art pack to use for a progress bar (there is one, but it isn’t formatted in an easy way to work with). Instead, we’ll use the two images below. One is a green bar and the other is a white outline. Save them in your project folder.\nIn the Texture section, drag the foreground image to the Progress and the background image to the Under texture. The first thing you’ll notice is that it’s very small. Let’s first under Layout set Custom Minimum Size to (80, 16). You’ll notice that the orange selection rectangle got bigger, but the image didn’t. Well, we don’t want the image to just stretch, or it would look bad. Instead we’ll check the Nine Patch Stretch box, and then set the four Stretch Margin values to 3.\nYou should now see a long, unfilled bar. To see what it looks like when filled, change the Value property in the Range section to anything between 0 and 100.\nOn the right side, we’d like to show the score. Now, we could just use a Label node and add a font, but that’s not very fun. The art pack includes a lovely pixel set of digits that we could use instead. We’ll just need to do a little coding to chop it up and show the corect digit(s).\nScore counter Start a new scene and add an HBoxContainer. Name it ScoreCounter then set it to Top Wide and set the Alignment to “End”. Also, set the Theme Overrides/Constants/Separation to 0 (you need to check the box next to the property).\nIn this container, we’ll have a string of TextureRect nodes showing each digit. We’ll start by adding one and then duplicating it.\nName the TextureRect Digit0. Under Texture, select “New AtlasTexture”, then click the box to open it. Drag Number_font (8 x 8).png into the Atlas property, then set the Region to (32, 8, 8, 8). Set Stretch Mode to “Keep Aspect Centered”.\nSelect the Digit0 node and press Ctrl-D 7 times to create duplicates of the node. The picture below shows what you should see after this step:\nNow we’ll add a script to ScoreCounter that will choose the correct Region values for whichever digit it needs to display.\nextends HBoxContainer var digit_coords = { 1: Vector2(0, 0), 2: Vector2(8, 0), 3: Vector2(16, 0), 4: Vector2(24, 0), 5: Vector2(32, 0), 6: Vector2(0, 8), 7: Vector2(8, 8), 8: Vector2(16, 8), 9: Vector2(24, 8), 0: Vector2(32, 8) } func display_digits(n): var s = \"%08d\" % n for i in 8: get_child(i).texture.region = Rect2(digit_coords[int(s[i])], Vector2(8, 8)) We start by making a list of the coordinates in the image where each digit is found. Then, display_digits() will format the number to an 8 digit number (for example, 258 would become \"00000258\"). Then, for each digit, we can apply the correct coordinates from the array.\nScripting the UI Go back to the UI scene and add the ScoreCounter to the HBoxContainer, then add a script to UI.\nextends MarginContainer @onready var shield_bar = $HBoxContainer/ShieldBar @onready var score_counter = $HBoxContainer/ScoreCounter func update_score(value): score_counter.display_digits(value) func update_shield(max_value, value): shield_bar.max_value = max_value shield_bar.value = value We’ll call these functions from Main whenever we need to update the score or the shield.\nAdding the UI to main Now in the Main scene add a CanvasLayer node, and instance the UI as its child. The CanvasLayer node creates another drawing layer, so our UI will be drawn on top of the rest of the game.\nChange this function in main.gd:\nfunc _on_enemy_died(value): score += value $CanvasLayer/UI.update_score(score) Run the game and see that your score goes up when shooting enemies.\nPlayer shield We can also add the shield to the player’s script. Add these new lines at the top of ship.gd:\nsignal died signal shield_changed @export var max_shield = 10 var shield = max_shield: set = set_shield This set = syntax tells Godot that we want to call the set_shield() function whenever the shield variable has its value set.\nfunc set_shield(value): shield = min(max_shield, value) shield_changed.emit(max_shield, shield) if shield \u003c= 0: hide() died.emit() We can also connect the ship’s area_entered signal so that we can detect when an enemy hits the ship:\nfunc _on_area_entered(area): if area.is_in_group(\"enemies\"): area.explode() shield -= max_shield / 2 And in the enemy bullet, add some damage to the shield when it hits:\nfunc _on_area_entered(area): if area.name == \"Ship\": queue_free() area.shield -= 1 Run the game again and check that your shield depletes when you get hit by a bullet or an enemy.\nNext steps We’re almost done with the basic functionality. We just need a way to start and end the game.\n","description":"","tags":null,"title":"UI and Score","uri":"/godot_recipes/4.x/games/first_2d/first_2d_09/index.html"},{"content":"Our last step is to add a start button and a “game over” state to the game.\nStarting the game Currently when we run the game, it starts immediately. Let’s add a button to start it.\nIn Main as a child of the CanvasLayer, add a CenterContainer and set its layout to Full Rect. Then add a TextureButton child. Name this button Start and add the START (48 x 8).png image as its Normal texture.\nAdd a reference at the top of the script:\n@onready var start_button = $CanvasLayer/CenterContainer/Start Connect this button’s pressed texture to Main and add this code:\nfunc _on_start_pressed(): start_button.hide() new_game() The new_game() function handles starting the game, so change _ready() so that it no longer spawns enemies, but just ensures the button is showing:\nfunc _ready(): start_button.show() #\tspawn_enemies() Now the button should show when you run the scene, and pressing it starts the game.\nEnding the game Add a TextureRect as a child of the CenterContainer and name the node GameOver. Use the GAME_OVER (72 x 8).png image. It will overlap with the start button, but that’s ok, we’re only ever going to show one at a time.\nAdd another reference at the top of the script:\n@onready var game_over = $CanvasLayer/CenterContainer/GameOver And add game_over.hide() to _ready().\nConnect the ship’s died signal in Main.\nfunc _on_ship_died(): get_tree().call_group(\"enemies\", \"queue_free\") game_over.show() await get_tree().create_timer(2).timeout game_over.hide() start_button.show() This will show the “game over” image for 2 seconds, then switch back to the start button so you can play again. Try it out and see if you can play a few games.\n","description":"","tags":null,"title":"Starting and Ending the Game","uri":"/godot_recipes/4.x/games/first_2d/first_2d_10/index.html"},{"content":"Problem You need a dynamic camera that moves and zooms to keep multiple objects on screen at the same time.\nAn example might be in a 2 player game, keeping both players on-screen as they move farther and closer together, like so:\nSolution In a single-player game, you’re probably used to attaching the camera to the player, so that it automatically follows them. We can’t really do this here because we have 2 (or more) players or other game objects that we want to keep on the screen at all times.\nWe need our camera to do 3 things:\nAdd/remove any number of targets. Keep the camera’s position centered at the midpoint of the targets. Adjust the camera’s zoom to keep all targets on screen. Create a new scene with a Camera2D and attach a script. We’ll add this camera to our game once we’re done.\nLet’s break down how the script works.\nNote You can see the full script at the end of the article.\nHere’s how the script starts:\nextends Camera2D @export var move_speed = 30 # camera position lerp speed @export var zoom_speed = 3.0 # camera zoom lerp speed @export var min_zoom = 5.0 # camera won't zoom closer than this @export var max_zoom = 0.5 # camera won't zoom farther than this @export var margin = Vector2(400, 200) # include some buffer area around targets var targets = [] # Array of targets to be tracked. @onready var screen_size = get_viewport_rect().size These settings will let you adjust the camera’s behavior. We’ll lerp() all camera changes, setting the move/zoom speeds to low values will introduce some delay in the camera “catching up” to sudden changes.\nMaximum and minimum zoom values will also depend on the size of objects in your game and how close or far you want to get. Adjust to suit.\nThe margin property is going to add some extra space around the targets so they’re not right on the edge of the viewable area.\nLastly, we have our array of targets and we get the viewport size so that we can properly calculate the scale.\nfunc add_target(t): if not t in targets: targets.append(t) func remove_target(t): if t in targets: targets.erase(t) For adding and removing targets, we have two helper functions. You can use these during gameplay to change what targets are being tracked (“Player 3 has entered the game!”). Note that we don’t want to have the same target tracked twice, so we reject it if it’s already there.\nMost of the functionality happens in _process(). First, moving the camera:\nfunc _process(delta): if !targets: return # Keep the camera centered between the targets var p = Vector2.ZERO for target in targets: p += target.position p /= targets.size() position = lerp(position, p, move_speed * delta) Here, we loop through the targets’ positions and find the common center. Using lerp() we make sure it moves there smoothly.\nNext, we’ll handle the zoom:\n# Find the zoom that will contain all targets var r = Rect2(position, Vector2.ONE) for target in targets: r = r.expand(target.position) r = r.grow_individual(margin.x, margin.y, margin.x, margin.y) var z if r.size.x \u003e r.size.y * screen_size.aspect(): z = 1 / clamp(r.size.x / screen_size.x, min_zoom, max_zoom) else: z = 1 / clamp(r.size.y / screen_size.y, min_zoom, max_zoom) zoom = lerp(zoom, Vector2.ONE * z, zoom_speed) The key functionality here comes from Rect2. We want to find a rectangle that encloses all the targets, which we can get with the expand() method. We then grow the rect by the margin.\nHere you can see the rectangle being drawn (press “Tab” in the demo project to enable this drawing):\nThen, depending whether the rectangle is wider or taller (relative to the screen’s aspect ratio), we find the scale and clamp it in the max/min range we’ve defined.\nFull script extends Camera2D @export var move_speed = 30 # camera position lerp speed @export var zoom_speed = 3.0 # camera zoom lerp speed @export var min_zoom = 5.0 # camera won't zoom closer than this @export var max_zoom = 0.5 # camera won't zoom farther than this @export var margin = Vector2(400, 200) # include some buffer area around targets var targets = [] @onready var screen_size = get_viewport_rect().size func _process(delta): if !targets: return # Keep the camera centered among all targets var p = Vector2.ZERO for target in targets: p += target.position p /= targets.size() position = lerp(position, p, move_speed * delta) # Find the zoom that will contain all targets var r = Rect2(position, Vector2.ONE) for target in targets: r = r.expand(target.position) r = r.grow_individual(margin.x, margin.y, margin.x, margin.y) var z if r.size.x \u003e r.size.y * screen_size.aspect(): z = 1 / clamp(r.size.x / screen_size.x, max_zoom, min_zoom) else: z = 1 / clamp(r.size.y / screen_size.y, max_zoom, min_zoom) zoom = lerp(zoom, Vector2.ONE * z, zoom_speed * delta) # For debug get_parent().draw_cam_rect(r) func add_target(t): if not t in targets: targets.append(t) func remove_target(t): if t in targets: targets.remove(t) Download This Project Download the project’s example code here: https://github.com/godotrecipes/multitarget_camera\n","description":"","tags":null,"title":"Multitarget Camera","uri":"/godot_recipes/4.x/2d/multi_target_camera/index.html"},{"content":"Problem You want to make an arcade-style car game, so you’re looking for simplicity over realistic physics. In this recipe, you’ll learn how to make a fun, driveable car using a rolling sphere.\nSolution There are a lot of ways to make a driving game. Different games need different levels of realism. If you’re trying to make a light, arcade-style car, you don’t need all of the features that Godot’s VehicleBody3D node provides, such as supension, independently modeled wheels, etc.\nInstead, we’re going to use a single RigidBody3D sphere to handle the driving physics. The sphere will be invisible, and the car mesh will be placed at the sphere’s location, making it look like it’s the car that’s driving.\nAs you can see in the preview clip above, the result looks remarkably good (and feels great to play!). Read on, and you’ll see that the amount of code required is also surprisingly small.\nInputs For control, we’re going to add four inputs to the Input Map:\naccelerate brake steer_left steer_right You can use keyboard input, game controller, or both. However, we recommend going with the analog stick for better steering.\nNode setup The car is made with two main nodes: a RigidBody3D sphere for the physics, and a MeshInstance3D to display the car body. Here’s the scene layout:\nRigidBody3D (Car) CollisionShape3D (Sphere) CarMesh (Imported model) Here’s how these nodes will interact: pressing “accelerate” will apply a force on the RigidBody3D in the direction the CarMesh is facing, while the turning inputs will rotate the CarMesh. As the ball rolls, it will carry the car mesh along with it (we’ll ignore the ball’s rotation).\nCarMesh Here’s the car model we’ll use:\nNote You can find this and other car models in Kenney’s “Car Kit”, available here: https://kenney.nl/assets/car-kit. Download the whole kit; you can use any of them that you choose. Note that this kit includes the models in multiple formats - you won’t need all of them for your project. GLTF is the recommended format for use with Godot.\nIf you use the GLTF models, you shouldn’t have adjust anything in the import settings.\nHere’s what the node tree looks like when importing the “suv” model:\nNote that the wheels \u0026 body are separate meshes. This will make it easy to add some visual appeal - like turning the wheels when steering.\nBall Add a sphere shape to the CollisionShape3D. We’re using a radius of 1 here, but you’ll want to experiment with the size of the ball to get different driving behaviors.\nHere’s how to adjust the settings on the body:\nAngular Damp: 10 - this property will have a huge effect on the driving feel. A higher value will bring the car to a stop much faster. Gravity Scale: 5 - Default gravity in Godot (9.8) feels a bit floaty, especially when going for an action feel. This will really matter if you plan to have jumps, hills, etc. in your world. You can set this globally in the Project Settings instead, if you prefer. Physics Material/Bounce: 0.1 - Playing around with this value can be a lot of fun. Be careful going above 0.5, though! For the demo, we’ve also added a spherical mesh to the collision shape for debugging purposes. You don’t need this, but it helps when troubleshooting to have a visual of the ball rolling.\nRayCast Finally, add a RayCast3D node as a child of the CarMesh. Set its Target Position to (0, -1, 0).\nWe’re going to use this for ground detection. When the car’s in the air, steering and acceleration won’t work. We can also use it to align the car mesh to a slope (if your game’s track isn’t flat).\nNow we’re ready to start coding.\nScript We’ll begin the script with some node references we’ll need:\nextends RigidBody3D @onready var car_mesh = $CarMesh @onready var body_mesh = $CarMesh/suv2 @onready var ground_ray = $CarMesh/RayCast3D @onready var right_wheel = $CarMesh/suv2/wheel_frontRight @onready var left_wheel = $CarMesh/suv2/wheel_frontLeft Next, some variables configuring the car’s behavior. See the comments describing each one’s purpose.\n# Where to place the car mesh relative to the sphere var sphere_offset = Vector3.DOWN # Engine power var acceleration = 35.0 # Turn amount, in degrees var steering = 18.0 # How quickly the car turns var turn_speed = 4.0 # Below this speed, the car doesn't turn var turn_stop_limit = 0.75 # Variables for input values var speed_input = 0 var turn_input = 0 You can @export these if you’d like to adjust them from the Inspector.\nIn _physics_process() we add a force to the body based on the direction the car is pointing, as well as keeping the car mesh positioned at the ball’s position:\nfunc _physics_process(delta): car_mesh.position = position + sphere_offset if ground_ray.is_colliding(): apply_central_force(-car_mesh.global_transform.basis.z * speed_input) The next step is to get the inputs, but we’ll also check if the ray is colliding with the ground first:\nfunc _process(delta): if not ground_ray.is_colliding(): return speed_input = Input.get_axis(\"brake\", \"accelerate\") * acceleration turn_input = Input.get_axis(\"steer_right\", \"steer_left\") * deg_to_rad(steering) right_wheel.rotation.y = turn_input left_wheel.rotation.y = turn_input Tip At this point, you can try it out. You should be able to accelerate forward and back (but not steer yet).\nNext, still in the _process() function, we’ll rotate the car mesh based on the rotation input. We’ll use slerp() (spherical linear interpolation) to do this smoothly:\n# rotate car mesh if linear_velocity.length() \u003e turn_stop_limit: var new_basis = car_mesh.global_transform.basis.rotated(car_mesh.global_transform.basis.y, turn_input) car_mesh.global_transform.basis = car_mesh.global_transform.basis.slerp(new_basis, turn_speed * delta) car_mesh.global_transform = car_mesh.global_transform.orthonormalized() Warning Because of floating point imprecision, repeatedly rotating a transform will eventually cause it to become distorted. The scale can drift or the axes can become no-perpendicular. In any script where you’re regularly rotating a transform, it’s a good idea to use orthonormalized() to correct any error before it accumulates.\nYou should try playing again at this point. You’ll be able to control the car and drive around, and everything works pretty much as expected. However, there are a few more things to add that will improve the “feel” of the driving.\nFinal touches 1. Align with slopes FIX THIS\nIf you’ve tried driving on a slope, you’ve seen that the car mesh doesn’t tilt at all, it always remains level. That looks unnatural, so let’s use the process described in KinematicBody: Align with Surface to fix that.\nAdd this code after rotating the mesh in _process():\nif ground_ray.is_colliding(): var n = ground_ray.get_collision_normal() var xform = align_with_y(car_mesh.global_transform, n) car_mesh.global_transform = car_mesh.global_transform.interpolate_with(xform, 10.0 * delta) And the align function (notice how we’re using orthonormalized() again?):\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform.orthonormalized() 2. Turn the wheels It looks nice if the front wheels turn when you steer. Add some references to the front wheel meshes at the top of the script:\n@onready var right_wheel = $CarMesh/suv2/wheel_frontRight @onready var left_wheel = $CarMesh/suv2/wheel_frontLeft And right after getting input, add the following:\n# rotate wheels for effect right_wheel.rotation.y = rotate_input left_wheel.rotation.y = rotate_input 3. Tilt the body This one adds lots of visual appeal. We’re going to tilt the car’s body based on the speed of the turn. Add a variable at the top of the script:\nvar body_tilt = 35 The smaller this number, the more extreme the tilt effect will be. Between 35 and 40 works well for the SUV model.\nNow add the following right after rotating the car mesh (in the if statement):\n# tilt body for effect var t = -rotate_input * ball.linear_velocity.length() / body_tilt body_mesh.rotation.z = lerp(body_mesh.rotation.z, t, 10 * delta) Observe the difference:\nCredits The demo project seen here uses the following open-source/creative commons assets:\nCars: Kenney Car Kit by Kenney Track: Modular Racekart Track by Keith at Fertile Soil Productions Download This Project Download the project code here: https://github.com/godotrecipes/3d_car_sphere\nRelated recipes Input Actions ","description":"","tags":null,"title":"Arcade-style Car","uri":"/godot_recipes/4.x/3d/3d_sphere_car/index.html"},{"content":"Problem You want to use a RigidBody2D to create a semi-realistic spaceship, a la Asteroids.\nSolution Using RigidBody2D can be tricky. Because it’s controlled by Godot’s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc, and we’ll refer to it as we work through this example.\nFor this example, we’ll use the following node setup:\nRigidBody2D (Ship) Sprite2D CollisionShape2D Sprite orientation Don’t forget to orient your sprite correctly. An object that is not rotated should be pointing along the +X axis, i.e. to the right. If your sprite’s art is drawn facing in another direction, rotate the Sprite2D (not the parent body) to align it correctly.\nWe’ll use the following inputs in the Input Map:\nInput Key thrust w or ↑ rotate_right d or → rotate_left a or ← Add a script to the body, and let’s define some variables:\nextends RigidBody2D @export var engine_power = 800 @export var spin_power = 10000 var thrust = Vector2.ZERO var rotation_dir = 0 The first two variables are how we’ll control the ship’s “handling”. engine_power is going to affect acceleration and top speed. spin_power controls how fast the ship rotates.\nthrust and rotation_dir are going to be set by pressing the inputs. Let’s do that next:\nfunc get_input(): thrust = Vector2.ZERO if Input.is_action_pressed(\"thrust\"): thrust = transform.x * engine_power rotation_dir = Input.get_axis(\"rotate_left\", \"rotate_right\") If we’re pressing the \"thrust\" input, we’ll set the thrust vector to the ship’s forward direction, while rotation_dir will be +/-1 based on the rotate inputs.\nWe can start flying by applying those values in _physics_process():\nfunc _physics_process(_delta): get_input() constant_force = thrust constant_torque = rotation_dir * spin_power It works, but you’ll notice that it’s very hard to control. The rotation is too fast, and it accelerates to a high speed before going offscreen. This is where we want to break from “real” space physics. In space, there’s no friction, but our Asteroids-style ship will be a lot easier to control if it coasted to a stop when we’re not thrusting. We can control this with damping.\nIn the RigidBody2D properties, you’ll find Linear/Damp and Angular/Damp. Set these to 1 and 2 respectively, and they’ll slow the movement/rotation as well as causing them to stop.\nFeel free to experiment with these values and how they interact with the engine_power and spin_power\nScreen wrapping Wrapping around the screen is really teleportation: when the ship goes off the right side of the screen, you teleport it to the left side. However, if you just tried to change the position, you’d find that it instantly snapped back. This is because the physics engine is trying to control the position as well.\nThe solution to this is to use the _integrate_forces() callback of the rigid body. In this function, you can safely update the physics properties of the object without conflicting with what the physics engine is doing.\nLet’s get the screensize at the top of the script:\n@onready var screensize = get_viewport_rect().size Then add the new function:\nfunc _integrate_forces(state): var xform = state.transform xform.origin.x = wrapf(xform.origin.x, 0, screensize.x) xform.origin.y = wrapf(xform.origin.y, 0, screensize.y) state.transform = xform As you can see, the _integrate_forces() function includes a parameter called state. This object is the PhysicsDirectBodyState2D of our body. It contains all of the current physics properties such as the forces, velocity, position, etc.\nFrom the state, we grab the current transform, modify it to wrap around the screen using wrapf(), and then set it back to the current state.\nHere’s how it looks:\nWarping Let’s look at one more example of using _integrate_forces() to alter the body’s state without issues. Let’s add a “warp” mechanic - when the player presses the \"warp\" input, the ship will teleport to a random spot on the screen.\nFirst, we’ll add a new variable for this:\nvar teleport_pos = null Then, in get_input(), we’ll set a random position:\nif Input.is_action_just_pressed(\"warp\"): teleport_pos = Vector2(randf_range(0, screensize.x), randf_range(0, screensize.y)) Finally, in _integrate_forces(), if there’s a teleport_position set, we’ll use it and then clear it:\nif teleport_pos: physics_state.transform.origin = teleport_pos teleport_pos = null Download This Project Download the project’s example code here: https://github.com/godotrecipes/asteroids_physics\n","description":"","tags":null,"title":"Asteroids-style Physics (using RigidBody2D)","uri":"/godot_recipes/4.x/physics/asteroids_physics/index.html"},{"content":"Problem You want to move a 3D object to a clicked position.\nSolution We’ll start with a flat plane for our world. Our actor will move on this plane.\nThe actor for this demo is a triangular prism mesh:\nHere is the code for the movement. If given a target, the object will turn and move toward it.\nextends CharacterBody3D @export var speed = 5 @export var gravity = -5 var target = Vector3.ZERO func _physics_process(delta): velocity.y += gravity * delta if target: look_at(target, Vector3.UP) rotation.x = 0 velocity = -transform.basis.z * speed if transform.origin.distance_to(target) \u003c .5: target = Vector3.ZERO velocity = Vector3.ZERO move_and_slide() We’ve also added a MeshInstance3D called “Marker” to the scene. This will be moved to indicate the clicked position.\nMouse -\u003e 3D Now we need a way to map mouse position into our 3D world. If you imagine the screen as a window into the 3D world, the mouse is trapped on the glass. To select something in 3D, we must project a ray from our eye (the camera), through the mouse’s position and into the world.\nWhile this can be done manually using the Camera3D’s project_ray methods, we can take advantage of the fact that CollisionObject3D nodes do this automatically. All we need to do is connect our StaticBody3D ground’s input_event signal:\nfunc _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx): if event is InputEventMouseButton and event.pressed: $Marker.transform.origin = click_position $Player.target = click_position We set the position of the marker and the Player’s target to the clicked position:\nWrapping up You can use this technique to detect clicks on any objects in your 3D world.\nRelated recipes Like video? ","description":"","tags":null,"title":"Click to move","uri":"/godot_recipes/4.x/3d/click_to_move/index.html"},{"content":"Problem Your game needs a “level select” menu, where the user can choose from a grid of options.\nSolution As shown in the example above, we’ll make a scrolling grid of level “boxes” that the player can choose from. Let’s start with the individual level boxes:\n1: Level box Here’s the node setup:\nLevelBox: PanelContainer Label MarginContainer TextureRect The TextureRect is for displaying the lock icon, and the Label for displaying the level number. When one is showing, the other is hidden.\nYou can style these as you like, here’s an example:\nMake sure to set the LevelBox’s Custom Minimum Size in the Inspector. We’re using (110, 110) in the example, but it depends on what size layout you’re going for.\nNow add a script and connect the gui_input signal.\n@tool extends PanelContainer signal level_selected @export var locked = true: set = set_locked @export var level_num = 1: set = set_level @onready var lock = $MarginContainer/Lock @onready var label = $Label func set_locked(value): locked = value if not is_inside_tree(): await ready lock.visible = value label.visible = not value func set_level(value): level_num = value if not is_inside_tree(): await ready label.text = str(level_num) func _on_gui_input(event): if locked: return if event is InputEventMouseButton and event.pressed: level_selected.emit(level_num) print(\"Clicked level \", level_num) We’re using @tool here so that we can make changes to the properties in the inspector and see them right away, without running the scene. Go ahead and try clicking the Locked property and verify that you see the lock appear/disappear.\nSince we don’t have actual levels to load in this project, the print() statement can help test that the click is being detected.\n2: Grid Once you have the box scene completed, add a new scene with a GridContainer. Add any number of LevelBox instances under it, making sure to set the Columns value. Here’s one with 6 columns:\nIn this example Theme Overrides/Constants/H Separation and V Separation are set to 10.\nSave this scene as LevelGrid. In the menu, we’ll use multiple instances to display the desired number of levels.\n3: Menu screen Now we can put together the final menu.\nHere’s the basic layout we’re going for:\nWe’ll create it with these nodes:\nLevelMenu: MarginContainer VBoxContainer Title: Label HBoxContainer BackButton: TextureButton ClipControl: Control NextButton: TextureButton Adjust the node properties:\nLevelMenu Theme Overrides/Constants/Margins: 20 VBoxContainer Theme Overrides/Constants/Separation: 50 Title Style the font however you like BackButton / NextButton Ignore Texture Size: On Stretch Mode: Keep Centered Layout/Container Sizing/Horizontal/Expand: On ClipControl Layout/Clip Contents: On Layout/Custom Minimum Size: (710, 350) (size of the LevelGrid) The ClipControl node is where the grid goes. Enabling Clip Contents means that if the contents are larger than the control, they’ll be cropped. That will allow us to make a horizontally scrolling set of grids. Add an HBoxContainer called GridBox to ClipControl, and instance 3 (or more) LevelGrids inside it.\nMake sure to set Theme Overrides/Constants/Separation to 0.\nYour layout should look something like this (we’ve disabled Clip Contents in order to show what’s happening):\nWith Clip Content, the three grids are all there, but the ClipControl only shows one at a time.\nNow, to scroll the menu, we need to shift the GridBox by 710 pixels to the left/right.\n110 (width of each LevelBox) * 6 (grid columns) + 10 (grid spacing) * 5 == 710 Info You may be wondering why we’re not using a ScrollContainer here. You certainly can, but we don’t want continuous scrolling, and we don’t want to see a scrollbar.\nAdd a script to the LevelMenu and connect the pressed signals of the two buttons.\nextends MarginContainer var num_grids = 1 var current_grid = 1 var grid_width = 710 @onready var gridbox = $VBoxContainer/HBoxContainer/ClipControl/GridBox func _ready(): # Number all the level boxes and unlock them # Replace with your game's level/unlocks/etc. # You can also connect the \"level_selected\" signals here num_grids = gridbox.get_child_count() for grid in gridbox.get_children(): for box in grid.get_children(): var num = box.get_position_in_parent() + 1 + 18 * grid.get_position_in_parent() box.level_num = num box.locked = false func _on_BackButton_pressed(): if current_grid \u003e 1: current_grid -= 1 gridbox.rect_position.x += grid_width func _on_NextButton_pressed(): if current_grid \u003c num_grids: current_grid += 1 gridbox.rect_position.x -= grid_width When you run the scene, try clicking the “Next” and “Back” buttons and verify that it’s scrolling as expected. Clicking the individual level boxes should print to the console.\nDownload the example project to see the whole thing in action, including some tweens for the scrolling action (because tweens make everything better).\nDownload This Project Download the project code here: https://github.com/godotrecipes/ui_level_select\n","description":"","tags":null,"title":"Level Select Menu","uri":"/godot_recipes/4.x/ui/level_select/index.html"},{"content":"Problem You’d like to understand what is meant by dot product and cross product.\nSolution In this recipe we’ll introduce the concept of vector dot product and cross product and how they might be used.\nDot product Dot product is an operation on two vectors that returns a scalar. It is often visualized as the projection of vector A onto vector B:\nThis is the formula for calculating the dot product:\nWhere θ is the angle between the two vectors and ||A|| is the magnitude of A.\nThis is very useful when both vectors are normalized (i.e. their magnitudes are 1), then the formula simplifies to:\nThis shows that the dot product is directly related to the angle between the two vectors. Since cos(0) == 1 and cos(180) == -1, the result of the dot product can tell you how closely aligned two vectors are:\nSee below for how we can apply this fact in a practical example.\nCross product The cross product of two vectors is a third vector that is perpendicular to both of them. Its magnitude is related to their magnitudes and the angle between them.\nOnce again, if we’re using normalized vectors, the result is simplified: it will be directly related to the angle and its magnitude will range from -1 to 1.\nNote Since the cross product is perpendicular to both vectors, we would need to be working in 3D. In most 2D frameworks, including Godot, the 2D Vector2.cross() method returns a scalar value representing the result’s magnitude.\nPractical applications Consider this animation, showing how the results of Vector2.dot() and Vector2.cross() change in relation to the changing angle:\nThis demonstrates two common applications of these methods. If the red vector is our object’s forward direction, and the green shows the direction towards another object:\nDot product: Using the result, we can tell if the object is in front of (\u003e 0) or behind (\u003c 0) us. Cross product: Using the result, we can tell if the object is to the left (\u003e 0) or right (\u003c 0). ","description":"","tags":null,"title":"Vectors: Using Dot and Cross Product","uri":"/godot_recipes/4.x/math/dot_cross_product/index.html"},{"content":"Problem You need your character body to align with the surface or terrain.\nSolution This recipe builds on the basic CharacterBody3D controller described in the CharacterBody3D: Movement recipe, so read that one first.\nFirst, we’ve added some terrain to the scene. You can download the terrain from here: https://fertile-soil-productions.itch.io/modular-terrain-pack. This is low-poly terrain, but you can use or make any terrain you like for this technique.\nAs you can see, the movement still works with the terrain, but the tank seems to “float” above the slopes because it doesn’t change its orientation.\nInstead, we need to rotate the tank so that its treads are aligned with the ground, even as the slope changes. To do that, we need to know which way is up.\nSurface normals A surface normal is a unit vector (“normal vector” and “unit vector” mean the same thing) perpendicular to a surface. It shows which way the surface is facing. In the case of a mesh, every surface has a normal pointing outward.\nIn Godot, when a body collides, you can get the normal of the collision. This will be the colliding body’s normal at the point of contact.\nOnce we have the surface normal, we need to align the tank’s Y axis with it. Note that we can’t use Transform3D.looking_at(), because that will align the -Z (forward) axis with the normal.\nTo do this, we’ll use the following function:\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform Given a transform and a new Y direction vector, this function returns the transform rotated so that its basis.y is aligned with the given normal.\nNote If you’re unfamiliar with the cross product or other vector math, there’s a great vector math intro in the Godot Docs.\nWe can update the tank’s movement code to call this function when it collides with a surface:\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) move_and_slide() for i in get_slide_count(): var c = get_slide_collision(i) global_transform = align_with_y(global_transform, c.get_normal()) This doesn’t work quite as expected:\nThe problem is that the tank’s collision shape could be colliding with more than one of the terrain’s faces. Also, move_and_slide() can result in more than one collision in a single frame. This leads to the jittering. We need to choose one face and stick with it.\nAdd a RayCast3D child to the tank and set its Target Position to (0, -1, 0).\nSince this raycast is pointing down from the exact center of the tank, we’ll align with the individual surface that it collides with - the one directly beneath the tank.\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) move_and_slide(v) var n = $RayCast3D.get_collision_normal() global_transform = align_with_y(global_transform, n) This is much better, but because we are instantly snapping to the new alignment every time the tank crosses an edge, it still looks a little jarring:\nWe can solve this last problem by interpolating to the new transform rather than snapping immediately to it.\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) velocity = move_and_slide_with_snap(velocity, Vector3.DOWN*2, Vector3.UP, true) var n = $RayCast.get_collision_normal() var xform = align_with_y(global_transform, n) global_transform = global_transform.interpolate_with(xform, 12 * delta) The result is much smoother and more pleasing:\nYou can get even better results with two raycasts - one at the front and one at the back. Get the average normal from them:\nvar n = ($FrontRay.get_collision_normal() + $RearRay.get_collision_normal()) / 2.0 Feel free to experiment with the interpolation amount. We found 12 to work well in this situation, but you might find a higher or lower value works better for your setup.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/characterbody3d_examples\nRelated recipes CharacterBody3D: Movement Math: Interpolation Math: Transforms ","description":"","tags":null,"title":"CharacterBody3D: Align with Surface","uri":"/godot_recipes/4.x/3d/3d_align_surface/index.html"},{"content":" Games Demo games and tutorials.\nUpdating to Godot 4.0 We’re working on new content for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.\nIn this section: Your First 2D Game ","description":"","tags":null,"title":"Game Tutorials","uri":"/godot_recipes/4.x/games/index.html"},{"content":"If you’ve been following along, you’ve learned a lot of the fundamentals of building games in Godot. We’re going to end the tutorial here, since we’ve completed the basic game.\nThe secret to learning effectively Here’s my big secret for getting the most out of tutorials like this and others you may find online. At the end, once you’ve finished building the project, immediately delete it and start over. This time, try and re-create it without looking at the tutorial. If you get stuck, look at just that part, then close it again.\nIt may sound repetitive, but that is how we learn: by doing things repeatedly. If you follow this tip, you’ll be amazed at how quickly you level up your gamedev skills.\nAdding to the game If you’re feeling comfortable with the techniques used to make this game, then you’re ready to branch out. Try adding a single new feature to this game.\nIf you’re stuck coming up with an idea, here are some suggestions:\nAdditional enemy types - there is art for other enemies in the art pack. How do they move and shoot?\nWaves - make more enemies spawn every time you clear the screen\nBoss enemies - what if a big enemy appears?\nBoosts - powerups could appear for the player to collect. There’s some art for those too.\nShield recharge - collect these to power up the shield Weapon upgrades - shoot more bullets, patterns, etc. Sound and music - give everything a lot more personality with some sound effects and background music.\nLearning more Ready for more? Here are some suggestions for your next learning adventure:\nGodot 101: Getting started in 3D - if you’re interested in making things in 3D, check out this introduction to Godot’s 3D features.\nCheck out the rest of the content on this website. There are lots of examples, tutorials, and code snippets to help you learn how to make your dream game.\nDownload This Project on GitHub Download the project code here:\nhttps://github.com/godotrecipes/8_direction_animation\n","description":"","tags":null,"title":"Wrapping up","uri":"/godot_recipes/4.x/games/first_2d/first_2d_end/index.html"},{"content":" Godot Recipes Godot’s nodes are your ingredients. What can you cook up?\nOn this site you’ll find a collection of solutions and examples to help you make whatever game system you need.\nGodot 4.0 Godot 4.0 has been released!\nGodot 4.0 is the latest stable release version of the engine.\nThere is a limited amount of learning material available, so if you’re looking to make a game or learn the engine, we recommend you stick with 3.x for now. Don’t worry - what you learn will still apply when you’re ready to move to the newer version!\nThis site has lots of learning material for Godot 3 - but not all of it has been updated for version 4 yet. You can click the ribbon in the top-right to toggle the Godot Recipes version, or click the button below:\nGodot 3 Recipes Are you ready to learn game development? Whether it’s as a hobby or working towards your dream career, there’s never been a better time to get started. Modern programming languages and tools have made it easier than ever to build high-quality games and distribute them to the world. One of these tools is the Godot game engine. For beginners, it offers a friendly way to learn gamedev techniques. For experienced developers, it’s a powerful, customizable and open tool for bringing your visions to life.\nOn this site you’ll find a gentle introduction to the Godot game engine, as well as a wide variety of gamedev tips and techniques. Feel free to browse the categories in the sidebar and see what catches your interest.\nIf you’re new to Godot, start here: What is Godot?.\nHow to use this site Beginners If you’re new to game development, start with the “Godot 101: Basics” section. There you’ll find an introduction to the Godot application, and a step-by-step guide to creating your first project. There is a lot of material to absorb here. Don’t feel discouraged if you feel you don’t get it at first. Repetition is the key to learning complex topics; the more you work with Godot’s features, the more familiar and easy they will start to feel.\nInfo It’s assumed that you have at least some general programming experience. If you’re completely new to programming, click here for tips on how to get started.\nExperienced Developers If you’re an experienced developer and/or you’re familiar with other modern game engine(s), feel free to explore the menu on the left. You’ll find a number of useful guides and tutorials to show you how to do things the “Godot Way”. Code samples and example projects are available for all articles.\n","description":"","tags":null,"title":"Home","uri":"/godot_recipes/4.x/index.html"},{"content":"","description":"","tags":null,"title":"Categories","uri":"/godot_recipes/4.x/categories/index.html"},{"content":"RayCast2D Raycasting is a common technique in game development. “Casting a ray” means extending a line from a point until it collides with something or reaches its limit.\nNode properties Add a RayCast2D node and take a look at the Inspector:\nHere are the main properties you’ll need to understand:\nEnabled Turn this off to disabled the raycast work.\nExclude Parent This property causes the ray to ignore collisions with the parent object. Enabled by default.\nTarget Position This is the destination point of the ray. Note: This is in local coordinates.\nAlso, take note of the Collide With section. By default the ray will only detect bodies, so you’ll need to go here if you want to detect areas as well or instead.\nUseful functions You can see the full list of the node’s functions in the API Documentation. Here are the some of the most useful ones:\nis_colliding() Boolean function, lets you know if the ray is colliding with something.\nget_collision_point() If the ray is colliding, this will return the position of the collision (in global coordinates).\nget_collider() If the ray is colliding, this function will return a reference to the colliding object.\nget_collision_normal() Another useful piece of information, this is the normal of the collided object at the point of collision.\nExample uses There are many uses for raycasts: visibility (can A see B, or is there an obstacle between?), proximity (am I close to a wall/ground/obstacle?), etc. Here are a couple of practical examples in use:\n1. Shooting Fast-moving projectiles often have the problem of “tunneling” through obstacles - they are moving too fast for the collision to be detected in a single frame. As an alternative, you can use a Raycast2D to represent the path (or a laser, etc.).\nHere’s a player sprite with a raycast attached to the end of the gun. The target_position is set to (250, 0).\nWhen the player shoots, you check to see if the ray is colliding with something:\nfunc _input(event): if event.is_action_pressed(\"shoot\"): if $RayCast2D.is_colliding(): print($RayCast2D.get_collider().name) 2. Edge detection Consider a platformer enemy that walks on platforms, but you don’t want it to fall off the edges. Add two downward-pointing raycasts to the mob like so:\nIn the mob’s script, check for when the ray stops colliding. That means you’ve found the edge and should turn around:\nfunc _physics_process(delta): velocity.y += gravity * delta if not $RayRight.is_colliding(): dir = -1 if not $RayLeft.is_colliding(): dir = 1 velocity.x = dir * speed $AnimatedSprite.flip_h = velocity.x \u003e 0 velocity = move_and_slide(velocity, Vector2.UP) Here’s what it looks like in action:\n","description":"","tags":null,"title":"RayCast2D","uri":"/godot_recipes/4.x/kyn/raycast2d/index.html"},{"content":"","description":"","tags":null,"title":"Tags","uri":"/godot_recipes/4.x/tags/index.html"}] \ No newline at end of file +[{"content":" Godot 101 Your introduction to the Godot game engine. If you’ve never used a game engine before, or if you’re just new to Godot, this is the place to start.\nIn this section: Getting Started Introduction to GDScript Intro to 3D See also: Game Tutorials/Your First 2D Game ","description":"","tags":null,"title":"Godot 101","uri":"/godot_recipes/4.x/g101/index.html"},{"content":"Problem You’ve tried adding an AudioStreamPlayer to your mob/coin/etc. to play when the object dies or is collected. But the problem is that when you remove the object, the audio player goes with it, chopping off the sound. You need an easier way to manage playing audio.\nSolution We’ll solve this problem with a node that is available from anywhere in the SceneTree. This node manages a set of AudioStreamPlayer nodes and a queue of sound streams to play.\nCreate a new script in the script editor.\nextends Node var num_players = 8 var bus = \"master\" var available = [] # The available players. var queue = [] # The queue of sounds to play. func _ready(): # Create the pool of AudioStreamPlayer nodes. for i in num_players: var p = AudioStreamPlayer.new() add_child(p) available.append(p) p.finished.connect(_on_stream_finished.bind(p)) p.bus = bus func _on_stream_finished(stream): # When finished playing a stream, make the player available again. available.append(stream) func play(sound_path): queue.append(sound_path) func _process(delta): # Play a queued sound if any players are available. if not queue.empty() and not available.empty(): available[0].stream = load(queue.pop_front()) available[0].play() available.pop_front() Set this script as an autoload in Project Settings. Give it an easily recognizable name, such as “AudioStreamManager”.\nAnywhere in your project that you want to play a sound, use:\nAudioStreamManager.play(\"res://path/to/sound\") Note This audio manager is adapted with thanks from [SFXPlayer by TheDuriel] (https://github.com/TheDuriel/DurielsGodotUtilities).\nExample project Below you can download an example project showing the use of the audio manager node. This project reads a folder full of audio files and generates a grid of buttons. Click the button to play the sound.\nAt the top, you can see the audio manager’s live statistics.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/audio_manager\n","description":"","tags":null,"title":"Audio Manager","uri":"/godot_recipes/4.x/audio/audio_manager/index.html"},{"content":"Problem You want to detect when an object enters or exits the screen.\nSolution The engine provides a node for this: VisibleOnScreenNotifier2D. Attach this node to your object, and you’ll be able to use its screen_entered and screen_exited signals. *\nExample 1 Consider a projectile that travels in a straight line after it’s fired. If we continue firing, eventually we’ll have a large number of objects for the engine to track, event though they’re offscreen, which can cause lag.\nHere’s the movement code for the projectile:\nextends Area2D var velocity = Vector2(500, 0) func _process(delta): position += velocity * delta To have the projectile automatically deleted when it moves offscreen, add a VisibleOnScreenNotifier2D and connect its screen_exited signal.\nfunc _on_VisibleOnScreenNotifier2D_screen_exited(): queue_free() Example 2 We have an enemy that performs some actions, such as moving along a path or playing an animation. On a large map with many enemies, only a few of them will be onscreen at the same time. We can disable the enemy’s actions while it’s offscreen using VisibleOnScreenNotifier2D.\nPartial code:\nvar active = false func _process(delta): if active: play_animation() move() func _on_VisibleOnScreenNotifier2D_screen_entered(): active = true func _on_VisibleOnScreenNotifier2D_screen_exited(): active = false ","description":"","tags":null,"title":"Entering/Exiting the screen","uri":"/godot_recipes/4.x/2d/enter_exit_screen/index.html"},{"content":"Here you can find the most recently added recipes:\nMultitarget Camera Character to Rigid Body Interaction CharacterBody3D: Align with Surface CharacterBody3D: Movement Arcade-style Car Pathfinding on a 2D Grid Migrating from 3.x Shooting with Raycasts Basic FPS Character RigidBody2D: Drag and Drop 2D Car Steering 3D Healthbars Grid-based Movement Arcade-style 3D Spaceship Interpolated Camera Platform Character ","description":"","tags":null,"title":"Fresh Recipes","uri":"/godot_recipes/4.x/recent/index.html"},{"content":"Overview Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. For example, a Sprite2D node automatically displays an image, but to move it across the screen, you’ll add a script that tells it how fast, in what direction, and so on.\nYou can think of it as the coding version of using the Inspector - GDScript knows all about Godot nodes and how to access them, plus it allows you to change them dynamically.\nGDScript is Godot’s built-in language for scripting and interacting with nodes. The GDScript documentation on the Godot website is a great place to get an overview of the language, and I highly recommend taking the time to read through it.\nIs GDScript Python?\nYou’ll often read comments to the effect that “GDScript is based on Python”. That’s somewhat misleading; GDScript uses a syntax that’s modeled on Python’s, but it’s a distinct language that’s optimized for and integrated into the Godot engine. That said, if you already know some Python, you’ll find GDScript looks very familiar.\nWarning Many tutorials (and Godot in general) assume that you have at least some programming experience already. If you’ve never coded before, you’ll likely find learning Godot to be a challenge. Learning a game engine is a large task on its own; learning to code at the same time means you’re taking on a lot. If you find yourself struggling with the code in this section, you may find that working through an introductory programming lesson (Python is a good option) will help you grasp the basics.\nStructure of a script The first line of any GDScript file must be extends \u003cClass\u003e, where \u003cClass\u003e is some existing built-in or user-defined class. For example, if you’re attaching a script to a CharacterBody2D node, then your script would start with extends CharacterBody2D. This states that your script is taking all the functionality of the built-in CharacterBody2D object and extending it with additional functionality created by you.\nIn the rest of the script, you can define any number of variables (aka “class properties”) and functions (aka “class methods”).\nCreating a script Let’s make our first script. Remember, any node can have a script attached to it.\nOpen the editor and add a Sprite2D node to empty scene. Right-click on the new node, and choose “Attach Script”. You can also click the button next to the search box.\nNext you need to decide where you want the script saved and what to call it. If you’ve named the node, the script will automatically be named to match it (so unless you’ve changed anything this script will likely be called “sprite2d.gd”).\nNow the script editor window opens up, and this is your new, empty sprite script. Godot has automatically included some lines of code, as well as some comments describing what they do.\nextends Sprite2D # Called when the node enters the scene tree for the first time. func _ready(): pass # Replace with function body. # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): pass Since the script was added to a Sprite2D, the first line is automatically set to extends Sprite2D. Because this script extends the Sprite2D class, it will be able to access and manipulate all the properties and methods that a Sprite2D node provides.\nProperties and methods Properties and methods are two terms which specifically mean variables and functions that are defined in an object. Programmers tend to use the terms interchangeably.\nAfter that is where you’re going to define all the variables you will use in the script, the “member variables”. You define variables with the ‘var’ keyword - as you can see by the comment examples.\nGo ahead and delete the comments and let’s talk about this next piece.\nNow we see a function called _ready(). In GDScript you define a function with the keyword “func”. The _ready() function is a special one that Godot looks for and runs whenever a node is added to the tree, for example when we hit “Play”.\nLet’s say that when the game starts, we want to make sure the sprite goes to a particular location. In the Inspector, we want to set the Position property. Notice that it’s in the section called “Node2D” - that means this is a property that any Node2D type node will have, not just Sprite2Ds.\nHow do we set the property in code? One way to find the name of the property is by hovering over it in the Inspector.\nGodot has a great built-in help/reference tool. Click on “Classes” at the top of the Script window and search for Node2D and you’ll see a help page showing you all the properties and methods the class has available. Looking down a bit you can see position in the “Member Variables” section - that’s the one we want. It also tells us the property is of the type “Vector2”.\nLet’s go back to the script and use that property:\nfunc _ready(): position = Vector2(100, 150) Notice how the editor is making suggestions as you type. Godot uses vectors for lots of things, and we’ll talk more about them later. For now, let’s type Vector2, and the hint tells us to put two floats for x and y.\nNow we have a script that says “When this sprite starts, set its position to (100, 150)”. We can try this out by pressing the “Play Scene” button.\nLearning tip When first learning to code, beginners often ask “How do you memorize all these commands?” Just like any other skill, it’s not a matter of memorization, it’s about practice. As you use things more, the things you do frequently will “stick” and become automatic. Until then, it’s a great idea to keep the reference docs handy. Use the search function whenever you see something you don’t recognize. If you have multiple monitors, keep a copy of the web docs open on the side for quick reference.\nWrapping up Congratulations on making your first script in GDScript! Before moving on, make sure you understand everything we did in this step. In the next part, we’ll add some more code to move the sprite around the screen.\n","description":"","tags":null,"title":"Getting started","uri":"/godot_recipes/4.x/g101/gdscript/gdscript_01/index.html"},{"content":"Getting Started Have you downloaded Godot yet? You can get it here:\nhttps://godotengine.org\nUpdating to Godot 4.0 We’re working on a new version of Godot 101 for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.\nIn this section: What is Godot? The Godot Editor: Finding your way around Nodes: Godot's building blocks ","description":"","tags":null,"title":"Getting Started","uri":"/godot_recipes/4.x/g101/start/index.html"},{"content":"Linear Interpolation, or its commonly-used abbreviation lerp, is a term that comes up often in game development. If you’ve never come across it before it can seem mysterious and highly-technical, but as you’ll see in this tutorial, it’s actually a straightforward concept with a wide variety of applications in game programming.\nNumeric Interpolation The core formula for linear interpolation is this:\nfunc lerp(a, b, t): return (1 - t) * a + t * b In this formula, a and b represent the two values and t is the amount of interpolation, typically expressed as a value between 0 (which returns a), and 1 (which returns b). The function finds a value the given amount between the two. For example:\nx = lerp(0, 1, 0.75) # x is 0.75 x = lerp(0, 100, 0.5) # x is 50 x = lerp(10, 75, 0.3) # x is 29.5 x = lerp(30, 2, 0.75) # x is 9 It’s called linear interpolation because the path between the two points is a straight line.\nYou can animate a node’s properties with lerp(). For example, if you divide the elapsed time by the desired duration, you’ll get a value between zero and one you can use to alter a property smoothly over time. This script scales a sprite up to five times its starting size while fading it out (using modulate.a) over two seconds:\nextends Sprite2D var time = 0 var duration = 2 # length of the effect func _process(delta): if time \u003c duration: time += delta modulate.a = lerp(1, 0, time / duration) scale = Vector2.ONE * lerp(1, 5, time / duration) Vector interpolation You can also interpolate between vectors. Both Vector2 and Vector3 provide linear_interpolate() methods for this.\nFor example, to find a vector that’s halfway between a Spatial node’s forward and left direction vectors:\nvar forward = -transform.basis.z var left = transform.basis.x var forward_left = forward.linear_interpolate(left, 0.5) The following example moves a Sprite node towards the mouse click position. Each frame the node moves 10% of the way to the target. This results in an “approach” effect, where the object’s speed becomes slower the closer it gets to the target.\nextends Sprite2D var target func _input(event): if event is InputEventMouseButton and event.pressed: target = event.position func _process(delta): if target: position = position.linear_interpolate(target, 0.1) For more advanced applications of interpolation, see Tween.\n","description":"","tags":null,"title":"Interpolation","uri":"/godot_recipes/4.x/math/interpolation/index.html"},{"content":"Problem You need to make a 2D platform-style character.\nSolution New developers are often surprised at how complex a platform character can be to program. Godot provides some built-in tools to assist, but there are as many solutions as there are games. In this tutorial, we won’t be going in-depth with features like double-jumps, crouching, wall-jumps, or animation. Here we’ll discuss the fundamentals of platformer movement. See the rest of the recipes for other solutions.\nTip While it’s possible to use RigidBody2D to make a platform character, we’ll be focusing on CharacterBody2D. Kinematic bodies are well-suited for platformers, where you are less interested in realistic physics than in responsive, arcade feel.\nStart with a CharacterBody2D node, and add a Sprite2D and CollisionShape2D to it.\nAttach the following script to the root node of the character. Note that we’re using input actions we’ve defined in the InputMap: \"walk_right\", \"walk_left\", and \"jump\". See InputActions.\nextends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 func _physics_process(delta): # Add gravity every frame velocity.y += gravity * delta # Input affects x axis only velocity.x = Input.get_axis(\"walk_left\", \"walk_right\") * speed move_and_slide() # Only allow jumping when on the ground if Input.is_action_just_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed The values used for speed, gravity, and jump_speed depend greatly on the size of your player sprite. The player’s texture in this example is 108x208 pixels. If your sprite is smaller, you’ll want to use smaller values. We also want high values so that everything feels fast and responsive. A low gravity results in a floaty-feeling game while a high value means you’re quickly back on the ground and ready to jump again.\nNote that we’re checking is_on_floor() after using move_and_slide(). The move_and_slide() function sets the value of this method, so it’s important not to check it before, or you’ll be getting the value from the previous frame.\nFriction and acceleration The above code is a great start, and you can use it as the foundation for a wide variety of platform controllers. One problem it has, though, is the instantaneous movement. For a more natural feel, it’s better if the character has to accelerate up to its max speed and that it coasts to a stop when there is no input.\nOne way to add this behavior is to use linear interpolation (“lerp”). When moving, we will lerp between the current speed and the max speed and while stopping we’ll lerp between the current speed and 0. Adjusting the lerp amount will give us a variety of movement styles.\nTip For an overview of linear interpolation, see Gamedev Math: Interpolation.\nextends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 @export_range(0.0, 1.0) var friction = 0.1 @export_range(0.0 , 1.0) var acceleration = 0.25 func _physics_process(delta): velocity.y += gravity * delta var dir = Input.get_axis(\"walk_left\", \"walk_right\") if dir != 0: velocity.x = lerp(velocity.x, dir * speed, acceleration) else: velocity.x = lerp(velocity.x, 0.0, friction) move_and_slide() if Input.is_action_just_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed Try changing the values for friction and acceleration to see how they affect the game’s feel. An ice level, for example, could use very low values, making it harder to maneuver.\nConclusion This code gives you a starting point for building your own platformer controller. For more advanced platforming features such as wall jumps, see the other recipes in this section.\nDownload This Project Download the project code here: https://github.com/godotrecipes/2d_platform_basic\n","description":"","tags":null,"title":"Platform character","uri":"/godot_recipes/4.x/2d/platform_character/index.html"},{"content":"This first game project will guide you through making your first Godot Engine game. While you don’t need any previous experience, it’s expected that you’ve at least read through the Godot 101: Getting Started section. There, you’ll learn about the editor interface and how to get around the Godot UI.\nWhy start with 2D? In a nutshell, 3D games are much more complex than 2D ones. However, many of the underlying game engine features you’ll need to know are the same. You should stick to 2D until you have a good understanding of Godot’s workflow. At that point, the jump to 3D will feel much easier.\nOpen Godot and start a new project. You can name it anything you’d like - we’re going with “Classic Shmup”, since this is a traditional shoot-em-up style game.\nDownloading the art You can download the art we’ll be using for the game from itch.io: Mini Pixel Pack by Grafxkid\nUnzip the art pack and copy it into your project by dropping the folder in the FileSystem tab.\nProject settings Next, we need to set up some project-wide settings. Open Project Settings and check the “Advanced Settings” toggle in the upper-right.\nIn the Display/Window section:\nViewport Width \u0026 Viewport Height to 240, 320. Window Width Override \u0026 Window Height Override to 480, 640. Stretch/Mode to canvas_items. These settings will ensure the game is the right size. Because we’re using pixel art, the images themselves are very small, so an old-school resolution like 240x320 is perfect. However, on a modern monitor, that’s a fairly small window, so the other settings let us scale that up proportionally. If you have a 1080p monitor, you can make the override values 720x960 instead. You’ll also be able to resize the window when the game is running.\nIn the Rendering/Textures section under Canvas Textures, set Default Texture Filter to Nearest. This will ensure that our beautiful pixel art stays nice and crisp, looking like the image on the right, not the one on the left: Click the Input Map tab at the top of the Project Settings window. This is where we can set up the inputs we want to use in the game. In the “Add New Action” box, type the following, hitting \u003center\u003e after each to add it to the list of actions: right, left, up, down, shoot. To assign key(s) to each named input, click the + button to its right and press the key on your keyboard. When you’re done, you should have something like this: Feel free to use other keys if you’d rather use a different setup.\nNext steps That takes care of setting up - now we’re ready to get started! In the next section, we’ll create the player-controlled spaceship.\n","description":"","tags":null,"title":"Project Setup","uri":"/godot_recipes/4.x/games/first_2d/first_2d_01/index.html"},{"content":"Problem You want to allow the player to “wrap around” the screen, teleporting from one side of the screen to the other. This is a common feature, especially in old-school 2D games (think Pac-man).\nSolution Get your screen (viewport) size\n@onready var screen_size = get_viewport_rect().size get_viewport_rect() is available to any CanvasItem derived node.\nCompare your player’s position\nif position.x \u003e screen_size.x: position.x = 0 if position.x \u003c 0: position.x = screen_size.x if position.y \u003e screen_size.y: position.y = 0 if position.y \u003c 0: position.y = screen_size.y Note that this is using the node’s position, which is usually the center of your sprite and/or body.\nSimplifying with wrapf()\nThe above code can be simplified using GDScript’s wrapf() function, which “loops” a value between the given limits.\nposition.x = wrapf(position.x, 0, screen_size.x) position.y = wrapf(position.y, 0, screen_size.y) ","description":"","tags":null,"title":"Screen wrap","uri":"/godot_recipes/4.x/2d/screen_wrap/index.html"},{"content":"Problem You want to use a spritesheet containing 2D animations.\nSolution Spritesheets are a common way for 2D animations to be distributed. In a spritesheet, all of the animation frames are packed into a single image.\nFor this demo, we’ll be using the excellent “Adventurer” sprite by Elthen. You can get this and lots of other great art athttps://elthen.itch.io/.\nWarning Make sure the images in your spritesheet are laid out in a constant-sized grid. This will enable Godot to automatically slice them. If they’re packed irregularly, you will not be able to use the following technique.\nNode setup This animation technique uses a Sprite2D node to display the texture, and then we animate the changing frames with AnimationPlayer. This can work with any 2D node, but for this demo, we’ll use a CharacterBody2D.\nAdd the following nodes to your scene:\nCharacterBody2D: Player Sprite2D CollisionShape2D AnimationPlayer Drag the spritesheet texture into the Texture property of the Sprite2D. You’ll see the entire spritesheet displayed in the viewport. To slice it up into individual frames, expand the “Animation” section in the Inspector and set the Hframes to 13 and Vframes to 8. Hframes and Vframes are the number of horizontal and vertical frames in your spritesheet.\nTry changing the Frame property to see the image change. This is the property we’ll be animating.\nAdding animations Select the AnimationPlayer and click the “Animation” button followed by “New\" . Name the new animation “idle”. Set the animation length to 2 and click the “Loop” button so that our animation will repeat (see below).\nWith the scrubber at time 0, select the Sprite2D node. Set its Animation/Frame to 0, then click the key icon next to the value.\nIf you try playing the animation, you’ll see it doesn’t appear to do anything. That’s because the last frame (12) looks the same as the first (0), but we’re not seeing any of the frames in-between (1-11). To fix this, change the “Update Mode” of the track from its default value of “Discrete” to “Continuous”. You can find this button at the end of the track on the right side.\nNote that this will only work for spritesheets where the frames are already in order. If they are not, you’ll have to keyframe each Frame seperately along the timeline.\nFeel free to add the other animations yourself. For example, the “jump” animation is on frames 65 through 70.\nRelated recipes Platform character ","description":"","tags":null,"title":"Spritesheet animation","uri":"/godot_recipes/4.x/animation/spritesheet_animation/index.html"},{"content":"In this tutorial, we’ll look at how to start working in 3D in Godot. You’ll learn how to navigate in the 3D editor, how to create and manipulate 3D objects, and how to work with some of Godot’s essential 3D nodes, such as cameras and lighting.\nAre you ready? A word of warning: 3D development can be quite a bit more complex than working in 2D. While many of the same principles apply - such as working with nodes, writing scripts, and handling logic/data - 3D brings with it a number of other considerations. For this reason, it’s a good idea to stick to 2D for your first few projects, moving to 3D once you have a good understanding of the game development process. This tutorial will assume you have completed at least an introductory Godot 2D project, such as the one in the [official Godot tutorial] (https://docs.godotengine.org/en/stable/getting_started/step_by_step/your_first_game.html).\nGetting Started in 3D One of Godot’s strengths is its ability to handle both 2D and 3D games. While much of what you’ve learned working on 2D projects (nodes, scenes, signals, etc.) applies equally well in 3D, there is also a whole new layer of complexity and capabilities. First, you’ll find that there are some additional features available in the 3D editor window, so we’ll start there:\nOrienting in 3D Space When you first open a new project in Godot, you will see the 3D project view:\nThe first thing you should notice is the three colored lines in the center. These are the x (red), y (green), and z (blue) axes. The point where they meet is the origin, which has the coordinates (0, 0, 0). You’ll find that this color scheme will also apply elsewhere in the Inspector.\nNote Different 3D applications follow different conventions for orientation. Godot uses Y-Up orientation, so that when looking at the axes, if x is pointing to the left/right, then y is up/down, and z is forward/back. Some other popular 3D software uses Z-UP. It’s good to keep this in mind when moving between applications.\nNavigation in 3D is performed using the mouse and keyboard. Here are the basic controls for the view camera:\nMousewheel up/down: zoom in/out Middle button + drag: orbit camera around current target Shift + middle button + drag: pan camera Right-click + drag: rotate camera in place In addition, if you’re familiar with popular 3D games, you might prefer Freelook mode, which you can toggle on/off using Shift+F. In this mode, you can use the WASD keys to fly around the scene while aiming with the mouse.\nYou can also alter the camera’s view by clicking on the [Perspective] label in the upper-left corner. Here, you can snap the camera to a particular orientation.\nAdding 3D Objects Now let’s add our first 3D node. Just as all 2D nodes inherit from Node3D, which provides properties such as position and rotation, 3D nodes inherit from Node3D, which provides 3D versions of the same properties. Add one to your scene and you’ll see the following object appear at the origin:\nThis object is not the node. It is something called a 3D gizmo. Gizmos are tools that allow you to move and rotate objects in space. The three rings control rotation, while the three arrows move (translate) the object along the three axes. Note that the rings and arrows are color-coded to match the axis colors.\nTake a few minutes to experiment and get familiar with the gizmo. Use Undo if you find yourself getting lost.\nTip Sometimes you may feel the gizmos are getting in your way. You can click on the mode icons to restrict yourself to only one type of transformation: move, rotate, or scale: Global vs. Local Space By default, the gizmo controls operate in global space. When you rotate the object, the gizmo’s arrows still point along the axes. However, if you click the Use Local Space button, the gizmo will switch to moving the body in local space.\nNow when you rotate the object, the gizmo arrows point along the object’s axes and not the world’s. Switching back and forth between Local and World space can make it much easier to place an object exactly where you want it.\nTransforms Look at the Inspector for the Node3D node. In the Transform section, you’ll see properties for Position, Rotation, and Scale. Drag the object around with the gizmo and observe how these values change. Just like in 2D, these properties are relative to the node’s parent.\nTogether, these properties make up the node’s transform. When changing the node’s spatial properties in code, you’ll access the transform property, which is a Godot Transform3D object. It has two properties: origin and basis. The origin represents the body’s position, while the basis contains three vectors that define the body’s local coordinate axes - think of the three axis arrows in the gizmo when you’re in Local Space mode.\nYou’ll see how to use these properties later in this section.\nMeshes Just like a Node2D, a Node3D has no size or appearance of its own. In 2D, you would use a Sprite2D to add a texture to the node. In 3D, you need to add a mesh. A mesh is a mathematical description of a shape. It consists of a collection of points, called vertices. These vertices are connected by lines, called edges, and multiple edges (at least three) together make a face.\nFor example, a cube is made up of 8 vertices, 12 edges, and 6 faces.\nAdding Meshes Typically, meshes are created by using 3D modeling software, such as Blender. You can also find many collections of 3D models available for download, if you’re unable to create your own. However, often you just need a basic shape such as a cube or sphere. In this case, Godot provides a way to create simple meshes called primitives.\nAdd a MeshInstance3D node as a child of the Node3D and in the Inspector, click its Mesh property:\nHere you can see the list of available primitives. They represent a handy collection of common useful shapes. Select “New BoxMesh” and you’ll see a plain cube appear on the screen.\nCameras Try running the scene with your cube object. Did you see anything? In 3D, you won’t see anything in the game viewport without adding a Camera3D. Add one to the root node and use the camera’s gizmo to position it pointing towards the cube:\nThe pinkish-purple pyramid shape on the camera is called the fustrum and represents the camera’s view. Notice the small triangular arrow which represents the camera’s “up” orientation. As you’re moving the camera around, try pressing the Preview button in the upper-left to see what the camera sees. Play the scene to verify everything is working as expected.\nWrapping Up In this tutorial you learned how to use Godot’s 3D editor, how to add 3D nodes such as Node3D, MeshInstance3D, and Camera3D, and how to use gizmos to place your objects. You also learned a bunch of new terminology. Hopefully you’re not overwhelmed.\nIn the next part, we’ll look at how to build a 3D scene by importing 3D assets and how to use more of Godot’s 3D nodes.\n","description":"","tags":null,"title":"The 3D Editor","uri":"/godot_recipes/4.x/g101/3d/101_3d_01/index.html"},{"content":"Problem You’re making a 2D top-down game, and you want to control a character’s movement.\nSolution For this solution, we’ll assume you have the following input actions defined:\nAction Name Key(s) \"up\" W,↑ \"down\" S,↓ \"right\" D,→ \"left\" A,← \"click\" Mouse button 1 We will also assume you’re using a CharacterBody2D node.\nWe can solve this problem in many ways, depending on what type of behavior you’re looking for.\nOption 1: 8-way movement In this scenario, the player uses the four directional keys to move (including diagonals).\nextends CharacterBody2D var speed = 400 # speed in pixels/sec func _physics_process(delta): var direction = Input.get_vector(\"left\", \"right\", \"up\", \"down\") velocity = direction * speed move_and_slide() Option 2: Rotate and move In this scenario, the left/right actions rotate the character and up/down move the character forward and back in whatever direction it’s facing. This is sometimes referred to as “Asteroids-style” movement.\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec var rotation_speed = 1.5 # turning speed in radians/sec func _physics_process(delta): var move_input = Input.get_axis(\"down\", \"up\") var rotation_direction = Input.get_axis(\"left\", \"right\") velocity = transform.x * move_input * speed rotation += rotation_direction * rotation_speed * delta move_and_slide() Note Godot considers an angle of 0 degrees to be pointing along the x axis. This means that a node’s forward direction (transform.x) is to the right. You should ensure that your character’s sprite is also drawn pointing to the right.\nOption 3: Aim with mouse Similar to option 2, but this time the character rotation is controlled with the mouse (ie the character always points towards the mouse). Forward/back movement is done with the keys as before.\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec func _physics_process(delta): look_at(get_global_mouse_position()) var move_input = Input.get_axis(\"down\", \"up\") velocity = transform.x * move_input * speed move_and_slide() Option 4: Click and move In this option, the character moves to the clicked location.\nextends CharacterBody2D var speed = 400 # move speed in pixels/sec var target = null func _input(event): if event.is_action_pressed(\"click\"): target = get_global_mouse_position() func _physics_process(delta): if target: # look_at(target) velocity = position.direction_to(target) * speed if position.distance_to(target) \u003c 10: velocity = Vector2.ZERO move_and_slide() Note that we stop moving if we get close to the target position. If you don’t do this, the character will “jiggle” back and forth as it moves a little bit past the target, moves back, goes a little past it, and so on. Optionally, you can use look_at() to face in the direction of movement.\nDownload This Project Download the project code here: https://github.com/godotrecipes/topdown_movement\n","description":"","tags":null,"title":"Top-down movement","uri":"/godot_recipes/4.x/2d/topdown_movement/index.html"},{"content":"Problem You need to understand in what order Godot handles nodes in the scene tree.\nSolution “Tree order” is mentioned often in the Godot docs and in tutorials. However, it is not always obvious to a beginner what is meant by this. Generally speaking, the order in which nodes are handled in the tree is in top-down fashion, starting at the root and going down each branch in turn.\nScene tree order is something that can cause a great deal of confusion for Godot beginners. In this example, we’ll illustrate in what order things happen.\nHere’s our sample node setup:\nOn each node, we have the following script attached:\nextends Node func _init(): # Note: a Node doesn't have a \"name\" yet here. print(\"TestRoot init\") func _enter_tree(): print(name + \" enter tree\") func _ready(): print(name + \" ready\") # This ensures we only print *once* in process(). var test = true func _process(delta): if test: print(name + \" process\") test = false Before we talk about the results, let’s review what each of these callback functions represents:\n_init() is called when the object is first created. It now exists in the computer’s memory.\n_enter_tree() is called when the node first enters the tree. This can be when instancing or when add_child() is used, for example.\n_ready() is called when the node and its children have all been added to the tree and are ready.\n_process() is called every frame (typically 60 times per second) on every node in the tree.\nIf we ran this on a single node all by itself, the order would be as you might expect:\nTestRoot init TestRoot enter tree TestRoot ready TestRoot process Once we add children to the mix, it becomes a bit more complex, and probably needs some clarification:\nTestRoot init TestChild1 init TestChild3 init TestChild2 init TestRoot enter tree TestChild1 enter tree TestChild3 enter tree TestChild2 enter tree TestChild3 ready TestChild1 ready TestChild2 ready TestRoot ready TestRoot process TestChild1 process TestChild3 process TestChild2 process As you can see, all of these nodes printed their messages in tree order, from top to bottom, following branches first - with the exception of the _ready() code.\nHere’s a quote from the Node reference:\nCalled when the node is “ready”, i.e. when both the node and its children have entered the scene tree. If the node has children, their _ready callbacks get triggered first, and the parent node will receive the ready notification afterwards.\nThis leads to an important rule-of-thumb to remember when setting up your node structure:\nTip Parent nodes should manage their children, not vice-versa.\nThis means any code in the parent must be able to fully access any data in its children. For that reason, _ready() must be processed in reverse tree order.\nRemember this when trying to access other nodes in _ready(). If you need to go up the tree to a parent (or grandparent), you should probably run that code in the parent rather than the child.\nRelated recipes Understanding node paths ","description":"","tags":null,"title":"Understanding tree order","uri":"/godot_recipes/4.x/basics/tree_ready_order/index.html"},{"content":"Game Engines Game development is complex and involves a wide variety of knowledge and skills. In order to build a modern game, you need a lot of underlying technology before you can make the actual game itself. Imagine if you had to build your own computer and write your own operating system before you could even start programming. Game development would be a lot like that if you truly had to start from scratch and build everything you needed.\nIn addition, there are a number of common needs every game has. For example, no matter what your game is, it’s going to need to draw things on the screen. If the code to do that has already been written, it makes more sense to reuse it that to create it all over again for every game. This is where game engines come in.\nA game engine is a collection of tools and technologies designed to assist in developing games. This allows you to focus more on building your game, and less on reinventing the wheel. Here are some of the features a good game engine will provide:\nRendering (2D/3D) “Rendering” is the process of displaying your game on the player’s screen. A good rendering pipeline needs to work with modern GPU features, high resolution displays, and effects like lighting and perspective, while maintaining a high frame rate.\nPhysics Building an accurate and usable physics engine is an enormous task. Most games require some sort of collision detection and response, and many need simulated physics (ie. friction, inertia, etc.), but few developers want to take on the task of writing one.\nPlatform Support In today’s market, you want to be able to release your game on multiple platforms, such as mobile, web, PC, and/or console. A game engine lets you build your game once and export it to one or more platforms.\nDevelopment Environment All of these tools are brought together in a single application, combining everything into one environment so you don’t have to learn a new workflow for every new project.\nThere are dozens of popular game engines to choose from today, such as Unity, Unreal, and GameMaker Studio, to name a few. It is important to remember that the majority of popular engines are commercial products. They may or may not be free to download, but the will require some kind of licensing or royalty agreement if you plan to release your game (and especially if your game makes money). You need to carefully read and understand what you’re agreeing to and what you are and are not allowed to do with the engine.\nWhy use Godot? Click here to download Godot\nIn contrast to the above, Godot is completely free and open source, released under the very permissive MIT license. This means there are no fees, hidden costs, or royalties you need to pay. This is in addition to being a fully featured modern game engine.\nAs a developer, the benefits are great. Because it’s unencumbered by commercial licensing, you have complete control over exactly how and where your game is distributed. In addition, Godot’s open source nature also means there is a much greater level of transparency than you’ll find with commercial engines. For example, if you find a particular feature doesn’t quite meet your needs, you’re free to modify the engine itself - no permission required.\n","description":"","tags":null,"title":"What is Godot?","uri":"/godot_recipes/4.x/g101/start/101_01/index.html"},{"content":" Your First 2D Game Get started with Godot by building a 2D shooter. In this series, we’ll start with the basics and build a classic, old-school space shooter.\nHere’s a screenshot of the finished game:\nIn each part of the series, we’ll build a piece of the game, adding features and explaining the process along the way.\nBackground If you find that you’re struggling with the programming side of things, see these resources:\nGodot 101: Introduction to GDScript - tutorial on this website. Godot Official Documentation - official tutorial resources Download This Project on GitHub Download the project code here:\nhttps://github.com/godotrecipes/classic_shmup\n","description":"","tags":null,"title":"Your First 2D Game","uri":"/godot_recipes/4.x/games/first_2d/index.html"},{"content":"Problem You need to make a first-person shooter (FPS) character.\nSolution Start with a CharacterBody3D node, and add a CollisionShape3D to it. The CapsuleShape3D collision shape is the most common choice. Depending on your world setup, you may want to add additional shapes here, but for the purposes of this example, we’ll stick to the basics.\nWe’ll leave all the sizing at the default values, meaning the capsule will be 2 meters high. Move it up by 1.0 m to align its bottom with the ground.\nNext, add a Camera3D as a child of the body and move it up about 1.6 m.\nWhere’s the body? For this example, we’ll leave the character “bodyless” - meaning we’re not adding a mesh to display for the player’s body. Depending on your setup, you may or may not need to see the player’s body.\nAttach a script to the body and start by defining some properties:\nextends CharacterBody3D var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var speed = 5 var jump_speed = 5 var mouse_sensitivity = 0.002 The _physics_process() function is the place to handle movement. Note that Input.get_vector() returns a 2-dimensional vector based on the combination of the forward/back/left/right keys. We want to use this vector to set the x and z components of the body’s velocity (because y is handled by gravity). Multiplying this vector by the body’s basis ensures we account for rotation - forward should always be the body’s forward vector.\nfunc _physics_process(delta): velocity.y += -gravity * delta var input = Input.get_vector(\"left\", \"right\", \"forward\", \"back\") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed move_and_slide() if is_on_floor() and Input.is_action_just_pressed(\"jump\"): velocity.y = jump_speed Don’t forget to add the input actions to your Input Map using the keys/inputs you prefer (W/A/S/D is typical, or you can use joystick axes if you prefer a controller).\nAdd the player to a “World” scene where you’ve created some StaticBody3D nodes for the floor and some walls.\nWhen you try to move, you’ll notice you can move forward/back and left/right, but you can’t rotate. That’s what we’ll handle next.\nMouse control in 3D First, we need the player to rotate left/right when we move the mouse the same way. Mouse input is represented in 2D, relative to the screen, so we need the x movement of the mouse to rotate the player’s body around its y (vertical) axis. The mouse_sensitivity property we defined above lets us adjust how many pixels of mouse movement translate to a degree of rotation.\nfunc _input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) Try the code again, and you’ll see that you can now rotate with the mouse. However, you may find your mouse running outside the game window. This is the perfect time to add some code to capture your mouse. See Input: Capturing the Mouse for details.\nOur updated code then becomes\nfunc _input(event): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) Finally, to look up/down, we’ll use the y motion of the mouse to tilt the camera. We don’t want it to turn completely upside-down, though, so we’ll clamp() the rotation to a reasonable value of 70 degrees.\nfunc _input(event): if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: rotate_y(-event.relative.x * mouse_sensitivity) $Camera3D.rotate_x(-event.relative.y * mouse_sensitivity) $Camera3D.rotation.x = clampf($Camera3D.rotation.x, -deg_to_rad(70), deg_to_rad(70)) Holding a weapon An FPS character typically has a 3D mesh of a weapon positioned in front. Setting this up can be easy with a couple of Godot editor tricks.\nAdd your weapon mesh as a child of the Camera3D. Then, in the editor view menu, choose “2 Viewports” and set one of them to preview the camera. Then, you can move around the weapon and easily see how it will look from the player’s perspective.\nTo add a little personality, try using an AnimationPlayer to animate the weapon’s position from side-to-side as the player moves.\nRelated recipes Input: Capturing the Mouse Download This Project Download the project code here: https://github.com/godotrecipes/basic_fps\n","description":"","tags":[],"title":"Basic FPS Character","uri":"/godot_recipes/4.x/3d/basic_fps/index.html"},{"content":" Basics Basic Godot tips and tricks that apply to any project.\nIn this section: Understanding tree order Node communication (the right way) Understanding node paths Understanding 'delta' Saving/loading data Migrating from 3.x ","description":"","tags":null,"title":"Basics","uri":"/godot_recipes/4.x/basics/index.html"},{"content":"In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.\nSetting up the Ship Scene A common part of the Godot workflow is creating scenes. As discussed earlier, a scene in Godot is nothing more than a collection of nodes. In most Godot projects, each game object is configured as a scene, with nodes that provide it with the desired functionality, and optionally some code to customize its behavior.\nChoosing nodes The first step is to decide what kind of node to start with. The first node you add to the scene is called the root node. A scene’s root node should generally be the one that primarily defines the game object’s behavior. Then you attach child nodes to add additional functionality.\nSo what should our game’s ship be? Let’s break down the requirements, and look at what nodes might be useful to meet them.\nThe ship needs to:\nMove in 2D space. For this, a basic Node2D would suffice, as that’s the node that has position, rotation, and other 2D-related properties. However, it has no appearance.\nDisplay an image. Sprite2D is the node for this. Since it’s also a Node2D, we’d still be able to move it around.\nDetect getting hit. The enemies will be shooting and flying around on the screen, so we’ll need to know when the ship is hit. We don’t have a need for solid objects - they’re not going to bounce off each other or transfer momentum - we just need to know when they touch. For this, an Area2D would be perfect. It can detect touching other objects, has positional properties, but it has no appearance of its own.\nLooking at this list, the Area2D provides the main functionality. We can attach a Sprite2D to display the ship image, and then we’ll have everything we need.\nBuilding the scene In the Scene tab, click the + button or the + Other Node button to add the first node. Start typing Area2D and choose it from the list. Once it’s in the Scene tab, click the node’s name to rename it to Player, and press \u003cCtrl+S\u003e to save the scene.\nDisplaying the ship With the Player node selected, add another node: a Sprite2D. To keep things organized, let’s rename this node to Ship.\nFrom the FileSystem tab, drag the Player_ship (16x16).png file from the art pack and drop it in the Texture property of the Inspector.\nThe first thing you’ll notice is that there seem to be three ships! The image from the art pack also includes versions of the ship going to the left/right. We can use this - in the Animation section of the Inspector, set Hframes to 3. Now, changing the Frame property will move between the three different versions. Leave it at 1 for now.\nAdding a collision shape You may also have noticed the yellow warning triangle on the Area2D node. If you click it, you’ll see the warning is telling us that the area doesn’t have a shape. We need to define its shape, and we can do that by adding a CollisionShape2D node as a child of the Player.\nIn the Inspector for this node, you’ll see a Shape property that currently shows \u003cempty\u003e. If you click in this box, you’ll see a dropdown that allows you to select from a variety of shapes. Choose New RectangleShape2D and you’ll see a light blue square appear over the ship. You can adjust the size of the shape by dragging the orange circles, or you can click on the shape in the Shape property to expand it and fill in the Size manually.\nExhaust The ship will look much more dynamic with a little animation. Included in the art pack are some animations of exhaust flames named “Boosters”. There are three: one for each version of the ship (left, forward, and right).\nTo display these, select the Ship node and add a child AnimatedSprite2D node and name it “Boosters”.\nIn the Inspector, under the Animation section, you’ll find a property called Sprite Frames, which is currently \u003cempty\u003e. Click it to create a New SpriteFrames, then click the SpriteFrames item to open the animation panel at the bottom of the editor window.\nDouble-click the “default” animation to rename it to “forward”. Then, to add the animation images, click the Add frames from sprite sheet button:\nChoose the Boosters (16 x 16).png image and you’ll see the Select Frames window, allowing you to choose the frames you want.\nThere are only two frames in this animation, but the grid isn’t correct. Change the Size values to match the image sizes: 16 x 16. Then, click both frames to select them and click the Add 2 Frame(s) button.\nNow that you’ve added the two frames, press the Play button to run the animation. You can also toggle the Autoplay on Load button so that the animation will start automatically.\nIt’s a little slow, so change the speed to 10 FPS.\nAdd two more animations by clicking the Add Animation button, naming them left and right.\nRepeat the process, adding the left and right “Booster” sprite sheets.\nGun cooldown The last node we’ll need to complete the player setup is a Timer to control how fast the player can shoot. Add the Timer as a child of Player and name it GunCooldown. Set its One Shot property to “On”. This means that when the timer ends, it won’t automatically restart. In the player’s code, we’ll start the timer when the player shoots, and they won’t be able to shoot again until the timer runs out.\nNext steps That completes the player scene setup. We’ve added the nodes to give the player ship the functionality it will need in the game. In the next section, we’ll add some code to enable the player to control the ship, make it shoot, and detect when it collides with things.\n","description":"","tags":null,"title":"Designing the Player Scene","uri":"/godot_recipes/4.x/games/first_2d/first_2d_02/index.html"},{"content":"Problem You need a 2D character that moves in a grid pattern.\nSolution Grid- or tile-based movement means the character’s position is restricted. They can only stand on a particular tile - never between two tiles.\nCharacter setup Here are the nodes we’ll use for the player:\nArea2D (“Player”): Using an Area2D means we can detect overlap (for picking up objects or colliding with enemies). Sprite2D: You can use a sprite sheet here (we’ll set up the animation below). CollisionShape2D: Don’t make the hitbox too big. Since the player will be standing on the center of a tile, overlaps will be from the center. RayCast2D: For checking if movement is possible in the given direction. AnimationPlayer: For playing the character’s walk animation(s). Add some input actions to the Input Map. We’ll use “up”, “down”, “left”, and “right” for this example.\nBasic movement We’ll start by setting up the tile-by-tile movement, without any animations or interpolation.\nextends Area2D var tile_size = 64 var inputs = {\"right\": Vector2.RIGHT, \"left\": Vector2.LEFT, \"up\": Vector2.UP, \"down\": Vector2.DOWN} tile_size should be set to match the size of your tiles. In a larger project, this can be set by your main scene when instancing the player. We’re using 64x64 tiles in the example below.\nThe inputs dictionary maps the input action names to direction vectors. Make sure you have the names spelled the same here and in the Input Map (capitalization counts!).\nfunc _ready(): position = position.snapped(Vector2.ONE * tile_size) position += Vector2.ONE * tile_size/2 snapped() allows us to “round” the position to the nearest tile increment, and adding a half-tile amount makes sure the player is centered on the tile.\nfunc _unhandled_input(event): for dir in inputs.keys(): if event.is_action_pressed(dir): move(dir) func move(dir): position += inputs[dir] * tile_size Here’s the actual movement code. When an input event occurs, we check the four directions to see which one matched, then pass it to move() to change the position.\nCollision Now we can add some obstacles. You can add StaticBody2Ds to manually add some obstacles (enable snapping to make sure they’re aligned with the grid) or use a TileMap (with collisions defined), as in the example below.\nWe’ll use the RayCast2D to determine whether a move to the next tile is allowed.\nonready var ray = $RayCast2D func move(dir): ray.target_position = inputs[dir] * tile_size ray.force_raycast_update() if !ray.is_colliding(): position += inputs[dir] * tile_size When changing a raycast’s target_position property, the physics engine won’t recalculate its collisions until the next physics frame. force_raycast_update() lets you update the ray’s state immediately. If it’s not colliding, then we allow the move.\nNote Another common method is to use 4 separate raycasts, one for each direction.\nAnimating movement Lastly we can interpolate the position between tiles, giving a smooth feel to the movement. We’ll use the Tween node to animate the position property.\nvar animation_speed = 3 var moving = false Add a reference to the Tween node and a variable to set our movement speed.\nfunc _unhandled_input(event): if moving: return for dir in inputs.keys(): if event.is_action_pressed(dir): move(dir) We’ll ignore any input while the tween is running and remove the direct position change so that the tween can handle it.\nfunc move(dir): ray.target_position = inputs[dir] * tile_size ray.force_raycast_update() if !ray.is_colliding(): #position += inputs[dir] * tile_size var tween = create_tween() tween.tween_property(self, \"position\", position + inputs[dir] * tile_size, 1.0/animation_speed).set_trans(Tween.TRANS_SINE) moving = true await tween.finished moving = false Experiment with different tween transitions for different movement effects.\nDownload This Project Download the project code here: https://github.com/godotrecipes/2d_grid_movement/\n","description":"","tags":null,"title":"Grid-based movement","uri":"/godot_recipes/4.x/2d/grid_movement/index.html"},{"content":"In the last part, we started a 3D project and looked at how to navigate and create 3D objects. In this part, you’ll learn how to import existing 3D objects that you’ve made or downloaded and how to use more of Godot’s 3D nodes.\nImporting 3D Objects If you’re familiar with 3D modeling software such as Blender, you can make your own models to use in your game. If not, there are many sources where you can download objects or even collections of objects for particular game types. One of our favorite makers of free game art is Kenney.nl.\nFor our tutorials, we’re going to use Kenney’s Platformer Kit, which you can download here: https://kenney.nl/assets/platformer-kit\nThis kit has a wide selection of objects that we can use to practice our Godot 3D skills. Here’s a sample showing what the kit looks like:\nOnce you’ve downloaded the kit, you’ll find that the objects inside are provided in a variety of different formats. Godot is able to use several of these, but since GLTF is available in this pack, it’s preferred over the others. Drop the GLTF format folder into your Godot project’s folder and rename it to “platformer_kit”.\n3D file formats Whether you create your own models or download the, you’ll need them to be saved in a format that Godot can use. Godot supports the following 3D file formats:\nglTF - supported in both text (.gltf) and binary (.glb) versions DAE (Collada) - an older format that is still supported OBJ (Wavefront) - an older format that is supported, but the format is limited compared to modern options FBX - a commercial format that has limited support glTF is the recommended format - it has the most features and is very well supported in Godot.\nWhen you switch back to your Godot window, you’ll see progress bar while Godot scans the folder and imports all of the objects. Let’s click on one of them to see what’s going on. In the FileSystem tab, double-click on crate.glb:\nHere you can see the object will be imported as a scene, with its root type set to Node3D and named “Scene Root”. Let’s change these: set the root type to RigidBody3D and the root name to “Crate”, then click the “Reimport” button.\nNow right-click on “crate.glb” and choose New Inherited Scene. Here we have a classic game object: the crate. The root node of the scene is a RigidBody3D named “Crate” just as we wanted.\nFinally, we need to add a collision shape to the body. While we could do this by adding a CollionShape3D, as you would typically do in 2D, but there’s a quicker way.\nSelect the crate2 mesh and you’ll see a Mesh menu appear at the top of the viewport. Click it and select Create Single Convex Collision Sibling. Godot will automatically add a CollionShape3D with a collision shape that matches the mesh.\nNow we’re finished setting up the object. Save your Crate scene and let’s see how we can use it.\nBuilding a 3D Scene Create a new scene with a Node3D root. The first child we’ll add is one to give us a “ground” to stack some crates on. Add a StaticBody3D called “Ground”, and to that add a MeshInstance3D. In the Mesh property, select “New BoxMesh” and then click it to open its properties. Set Size to (10, 0.1, 10) so that we have a nice large surface. However, it would look better if it weren’t plain white.\nAlso in the mesh properties is a Material property. Materials are how you define the appearance of an object. Select “New StandardMaterial3D” and then click it to open a large list of properties. To set the color of the mesh, we need the Albedo/Color property. Choose a color, such as brown or dark green.\nIf we add a crate, it will fall right through the mesh, so we also need to give it a collision shape. Add a CollisionShape3D to the Ground and choose “New BoxShape3D”. Set the collision box to the same size as the mesh.\nNow instance a few crates in the scene and arrange them in a rough stack. Add a Camera and place it where it has a good view of the crates. Run the scene and watch your crates go tumbling!\nWhy is the scene so dark? Because there’s no light! By default, Godot doesn’t add any lighting or environment to your scenes, like it does in the editor viewport. This is great when you want to set up your own specific lighting, but for a quick example scene like this, there’s a shortcut.\nLighting There are multiple light nodes available in 3D, which you can use to create a variety of lighting effects. But we’re going to start with DirectionalLight3D. However, instead of adding one manually, we’re going to have Godot use the same one it’s using in the editor window. At the top ove the viewport, there are two icons that control the preview lighting and preview environment. If you click the three dots next to them, you can see their settings.\nClick the Add Sun to Scene button, and Godot will add a DirectionalLight3D to your scene. Click Add Environment to Scene and it will do the same with the preview sky by adding a WorldEnvironment node.\nRun the scene again, and you’ll be able to see your crates falling.\nRotating Camera Let’s make the camera a little more dynamic by having it slowly orbit around the scene. Select the root node and add a Node3D, which will be located at (0, 0, 0) and name it “CameraHub”. In the scene tree, drag the camera to make it a child of this new node. Now, if the CameraHub rotates around the y axis, it will drag the camera along with it.\nAdd a script to the root node and add the following:\nextends Node3D func _process(delta): $CameraHub.rotate_y(0.6 * delta) Run the scene to see what happens.\nWrapping Up In this tutorial you learned how to import 3D objects from outside sources, and how to combine them into a simple scene. We also investigated lights and moving cameras.\nIn the next part, we’ll look at how to build a more complex scene and include a player-controlled character.\n","description":"","tags":null,"title":"Importing 3D Objects","uri":"/godot_recipes/4.x/g101/3d/101_3d_02/index.html"},{"content":"Problem You want to understand Godot’s “input action” system.\nSolution Let’s say you’re making a top-down character and you write code using InputActionKey that uses the arrow keys for movement. You’ll quickly find that many players prefer to use “WASD” style controls. You can go back into your code and add the additional key checks, but this would result in duplicated/redundant code.\nInput actions can help to make your code more configurable. Rather than hard-coding specific keys, you’ll be able to modify and customize them without changing the code.\nCreating inputs You define input actions in the “Project Settings” under the “Input Map” tab. Here, you can create new actions and/or assign inputs to them.\nYou’ll see when you click on the tab there are already some default actions configured. They are all named “ui_*” to indicate that they are the default interface actions. “Tab” for next UI element, for example.\nGenerally speaking, you should create your own actions for your game, rather than use the existing ones.\nFor this example, let’s say you want to allow the player to control the game with the keyboard or the mouse. They need to be able to shoot by pressing either the left mouse button or the spacebar.\nCreate the new action “shoot” by typing the name in the “Action” field at the top and clicking “Add” (or pressing enter). Scroll to the bottom and you’ll see the new action has been added to the list.\nNow you can assign inputs to this action by clicking the “+” sign to the right. Inputs can be keys, mouse buttons, or joy/gamepad inputs. Choose “Key” and you can press the key on the keyboard you want to assign - let’s press the spacebar - and click “OK”.\nClick “+” to add another input, and this time choose “Mouse Button”. The default of “Device 0” and “Left Button” is fine, but you can select others if you like.\nUsing input actions You can check for the action either by polling the Input singleton every frame:\nfunc _process(delta): if Input.is_action_pressed(\"shoot\"): # This will execute every frame as long as the input is held. This is best for continuous actions - i.e. those you want to check constantly, such as movement.\nIf instead you want to detect the action at the moment it occurs, you can use the _input() or _unhandled_input() callbacks:\nfunc _unhandled_input(event): if event.is_action_pressed(\"shoot\"): # This will run once on the frame when the action is first pressed There are several functions you can use for checking input state:\nis_action_pressed(): This function returns true if the action is currently in the pressed state.\nis_action_released(): This function returns true if the action is not In the pressed state.\nis_action_just_pressed() / is_action_just_released(): These methods work like the above, but only return true on the single frame after the event occurs. This is useful for non-recurring actions like shooting or jumping where the user needs to let go and then press the key again to repeat the action.\nRelated Recipes Inputs: Introduction ","description":"","tags":null,"title":"Input Actions","uri":"/godot_recipes/4.x/input/input_actions/index.html"},{"content":"Problem You need a 3D camera that smoothly follows a target (interpolates).\nSolution Info Godot’s built-in InterpolatedCamera node is deprecated and will be removed in the release of Godot 4.0.\nAttach the script below to a Camera3D node in your scene. The three export properties let you choose:\nlerp_speed - the camera’s movement speed. Lower values result in a “lazier” camera. target_path - choose the camera’s target node. offset - position of the camera relative to the target. See below for some examples of the camera in action.\nextends Camera3D @export var lerp_speed = 3.0 @export var target_path : NodePath @export var offset = Vector3.ZERO var target = null func _ready(): if target_path: target = get_node(target_path) func _physics_process(delta): if !target: return var target_xform = target.global_transform.translated_local(offset) global_transform = global_transform.interpolate_with(target_xform, lerp_speed * delta) look_at(target.global_transform.origin, target.transform.basis.y) In the _physics_process() function we interpolate the camera’s position with the target’s (plus offset).\nExamples lerp_speed: 3.0 offset: (0, 7, 5) ","description":"","tags":null,"title":"Interpolated Camera","uri":"/godot_recipes/4.x/3d/interpolated_camera/index.html"},{"content":" GDScript GDScript is Godot’s built-in scripting language. Its syntax is based on Python, so if you’re familiar with that language, you’ll feel right at home. In this chapter, we’ll introduce the language and get you up to speed with how it works.\nUpdating to Godot 4.0 We’re working on a new version of Godot 101 for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.\nIn this section: Getting started ","description":"","tags":null,"title":"Introduction to GDScript","uri":"/godot_recipes/4.x/g101/gdscript/index.html"},{"content":" Know Your Nodes In the “Know Your Nodes” series, we go in-depth with a single one of Godot’s nodes. Learn what makes it tick and see some examples of how it’s used.\nIn this section: RayCast2D ","description":"","tags":null,"title":"Know Your Nodes","uri":"/godot_recipes/4.x/kyn/index.html"},{"content":"Problem You want to detect mouse input.\nSolution InputEventMouse is the base class for mouse events. It contains position and global_position properties. Inheriting from it are two classes: InputEventMouseButton and InputEventMouseMotion.\nNote You can assign mouse button events in the InputMap, so you can use them with is_action_pressed().\nInputEventMouseButton @GlobalScope.ButtonList contains a list of BUTTON_* constants for each possible button, which will be reported in the event’s button_index property. Note that the scrollwheel also counts as a button - two buttons, to be precise, with both BUTTON_WHEEL_UP and BUTTON_WHEEL_DOWN being separate events.\nTip Unlike regular buttons, mouse wheel clicks only produce pressed events. There is no concept of a mouse wheel click being “released”.\nfunc _unhandled_input(event): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT: if event.pressed: print(\"Left button was clicked at \", event.position) else: print(\"Left button was released\") if event.button_index == BUTTON_WHEEL_DOWN: print(\"Wheel down\") InputEventMouseMotion These events occur whenever the mouse moves. You can find the distance moved (in screen coordinates) with the relative property.\nHere’s an example using mouse movement to rotate a 3D character:\n# Converts mouse movement (pixels) to rotation (radians). var mouse_sensitivity = 0.002 func _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) ","description":"","tags":null,"title":"Mouse Input","uri":"/godot_recipes/4.x/input/mouse_input/index.html"},{"content":" Info Many thanks to @TheDuriel on the Godot Discord for the original diagram that inspired this article. Save this and keep it handy.\nProblem Your project has started getting complex. You have multiple scenes, instances, and a lot of nodes. You’ve probably found yourself writing code like the following:\nget_node(\"../../SomeNode/SomeOtherNode\") get_parent().get_parent().get_node(\"SomeNode\") get_tree().get_root().get_node(\"SomeNode/SomeOtherNode\") If you do this, you’ll soon find that node references like this break easily. As soon as you change one thing about your scene tree, none of those references may be valid anymore.\nCommunication between nodes and scenes doesn’t have to be complicated. There is a better way.\nSolution As a general rule, nodes should manage their children, not the other way around. If you’re using get_parent() or get_node(\"..\"), then you’re probably headed for trouble. Node paths like this are brittle, meaning they can break easily. The three main problems with this arrangement:\nYou can’t test a scene independently. If you run the scene by itself or in a test scene that doesn’t have the exact same node setup, get_node() will cause a crash.\nYou can’t change things easily. If you decide to rearrange or redesign your tree, paths will no longer be valid.\nReady order is children-first, parent-last. This means that trying to access a parent’s property in a node’s _ready() can fail because the parent isn’t ready yet.\nTip See Understanding tree order for an explanation of how nodes enter the tree and become ready.\nGenerally speaking, a node or scene should be able to be instanced anywhere in your game, and it should make no assumptions about what its parent is going to be.\nWe’ll go into detailed examples later in this tutorial, but for now, here’s the “golden rule” of node communication:\nCall down, signal up.\nIf a node is calling a child (i.e. going “down” the tree), then get_node() is appropriate.\nIf a node needs to communicate “up” the tree, it should probably use a signal.\nIf you keep this rule in mind when designing your scene setup, you’ll be well on your way to a maintainable, well-organized project. And you’ll avoid using the cumbersome node paths that lead to problems.\nNow, let’s look at each of these strategies along with some examples.\n1. Using get_node() get_node() traverses the scene tree using a given path to find the named node.\nTip See Understanding node paths for a more detailed explanation of node paths.\nget_node() example Let’s consider the following common configuration:\nThe script in the Player node needs to notify the AnimatedSprite2D which animation to play, based on the player’s movement. In this situation, get_node() works well:\nextends CharacterBody2D func _process(delta): if speed \u003e 0: get_node(\"AnimatedSprite2D\").play(\"run\") else: get_node(\"AnimatedSprite2D\").play(\"idle\") Tip In GDScript you can use $ as a shorthand for get_node(), writing $AnimatedSprite2D instead.\n2. Using signals Signals should be used to call functions on nodes that are higher in the tree or at the same level (i.e. “siblings”).\nYou can connect a signal in the editor (most often for nodes that exist before the game starts) or in code (for nodes that you’re instancing at runtime). The syntax for connecting a signal is:\nsignal_name.connect(target_node.target_function)\nLooking at this, you may be thinking “Wait, if I’m connecting to a sibling, won’t I need a node paths like ../Sibling?”. While you could do this, it breaks our rule above. The answer to this puzzle is to make sure that connections are made by the common parent.\nFollowing the rule of calling down the tree, a node that’s a common parent to the signaling and receiving nodes will by definition know where they are and be ready after both of them.\nSignal example A very common use case for signals is updating your UI. Whenever the player’s health variable changes, you want to update a Label or ProgressBar display. However, your UI nodes are completely separated from your player (as they should be). The player knows nothing about where those nodes are and how to find them.\nHere’s our example setup:\nNote that the UI is an instanced scene, we’re just showing the contained nodes. This is where you often see things like get_node(\"../UI/VBoxContainer/HBoxContainer/Label).text = str(health), which is what we want to avoid.\nInstead the player emits a health_changed signal whenever it adds/loses health. We need to send that signal to the UI’s update_health() function, which handles setting the Label value. In the Player script we use this code whenever the player’s health is changed:\nhealth_changed.emit(health) In the UI script we have:\nonready var label = $VBoxContainer/HBoxContainer/Label func update_health(value): label.text = str(value) Now we just need to connect the signal to the function. The perfect place to do that is in World, which is the common parent, and knows where both nodes are:\nfunc _ready(): $Player.health_changed.connect($UI.update_health) 3. Using groups Groups are another way to decouple, especially when you have a lot of similar objects that need to do the same thing. A node can be added to any number of groups and membership can be changed dynamically at any time with add_to_group() and remove_from_group().\nA common misconception about groups is that they are some kind of object or array that “contains” node references. Groups are a tagging system. A node is “in” a group if it has that tag assigned from it. The SceneTree keeps track of the tags and has functions like get_nodes_in_group() to help you find all nodes with a particular tag.\nGroup example Let’s consider a Galaga-style space shooter where you have a lots of enemies flying around. These enemies may have different types and behaviors. You’d like to add a “smart bomb” upgrade that, when activated, destroys all enemies on the screen. Using groups, you can implement this with a minimal amount of code.\nFirst, add all enemies to an “enemies” group. You can do this in the editor using the “Node” tab:\nYou can also add nodes to the group in your script:\nfunc _ready(): add_to_group(\"enemies\") Let’s assume every enemy has an explode() function that handles what happens when it dies (playing an animation, spawning dropped items, etc). Now that every enemy is in the group, we can implement our smart bomb function like this:\nfunc activate_smart_bomb(): get_tree().call_group(\"enemies\", \"explode\") 4. Using owner owner is a Node property that’s set automatically when you save a scene. Every node in that scene will have its owner set to the scene’s root node. This makes for a convenient way to connect child signals up to the main node.\nowner example In a complex UI, you often find yourself with a very deep, nested hierarchy of containers and controls. Nodes that the user interacts with, such as Button, emit signals, and you may want to connect those signals to the script on the UI’s root node.\nHere’s an example setup:\nThe script on the root CenterContainer has the following function, which we want to call whenever any button is pressed:\nextends CenterContainer func _on_button_pressed(button_name): print(button_name, \" was pressed\") The buttons here are instances of a Button scene, representing an object which may contain dynamic code that sets the button’s text or other properties. Or perhaps you have buttons that are dynamically added/removed from the container depending on the game state. Regardless, all we need to connect the button’s signal is the following:\nextends Button func _ready(): pressed.connect(owner._on_button_pressed.bind(name)) No matter where you place the buttons in the tree - if you add more containers, for example - the CenterContainer remains the owner.\nRelated recipes Understanding tree order Understanding node paths ","description":"","tags":null,"title":"Node communication (the right way)","uri":"/godot_recipes/4.x/basics/node_communication/index.html"},{"content":"Problem You want a rigid body to rotate smoothly to look at a target.\nSolution Using RigidBody2D can be tricky. Because it’s controlled by Godot’s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc.\nTo rotate a body, we need to apply a rotational force - a torque. Once the body is rotating, we want the torque to get smaller as we get closer to the final rotation.\nThis is the perfect situation to use the dot product. Its sign will tell us whether the target is to the left/right, and its magnitude will tell us how far away from the target direction we’re pointing.\nTip See Vectors: Using Dot and Cross Product for a brief review of the dot product.\nextends RigidBody2D var angular_force = 50000 var target = position + Vector2.RIGHT func _physics_process(delta): var dir = transform.y.dot(position.direction_to(target)) constant_torque = dir * angular_force You may be wondering why we’re using the transform.y here, when transform.x is the body’s forward vector. Using transform.x, the dot product would be at its maximum when the body is directly pointing at the target, but we want the torque to be zero at that point. Using transform.y means that our torque will be higher when we’re not aligned with the target.\nSkip the Rigid Body Entirely You can avoid all of this entirely by not rotating your rigid body at all! Instead, change the child sprite’s rotation to point at the target. You can use lerp() or a Tween to make the rotation as smooth as you wish.\nIn many cases, this will be a great solution. Remember, the underlying body’s orientation doesn’t have to match the attached sprite!\nRelated recipes Vectors: Using Dot and Cross Product ","description":"","tags":null,"title":"RigidBody2D: Look at Target","uri":"/godot_recipes/4.x/physics/smooth_rigid_rotate/index.html"},{"content":"Problem You want to shoot projectiles from your player/mob/etc..\nSolution Setting up the bullet First, we’ll set up a “bullet” object that we can instance. Here are the nodes we’ll use:\nArea2D: Bullet Sprite2D CollisionShape2D For the Sprite2D’s texture, you can use any image you like. Here’s an example one:\nSet up the nodes and configure the sprite and collision shape. If your texture is oriented pointing up, like the one above, make sure to rotate the Sprite node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.\nAdd a script and connect the Area2D’s body_entered signal.\nextends Area2D var speed = 750 func _physics_process(delta): position += transform.x * speed * delta func _on_Bullet_body_entered(body): if body.is_in_group(\"mobs\"): body.queue_free() queue_free() For this example, we’ll remove the bullet if it hits anything at all. We’ll also delete anything tagged in the “mobs” group that it hits.\nShooting We need to set up a spawn location for the bullets. Add a Marker2D and place it where you want the bullets to spawn. Here’s an example, placed at the barrel of the gun. I’ve named it “Muzzle”.\nNotice that as the player rotates, the Muzzle’s transform remains oriented the same way relative to the gun. This will be very convenient when spawning the bullets, as they can use the transform to get the proper position and direction. We just set the new bullet’s transform equal to the muzzle’s.\nTip This will work for any character type, not just the “rotate-and-move” style shown here. Just attach the Marker2D where you want the bullets to spawn.\nIn the character’s script we add a variable to hold the bullet scene for instancing:\n@export var Bullet : PackedScene And check for our defined input action:\nif Input.is_action_just_pressed(\"shoot\"): shoot() Now in our shoot() function we can instance a bullet and add it to the tree. A common mistake is to add the bullet as a child of the player:\nfunc shoot(): var b = Bullet.instantiate() add_child(b) b.transform = $Muzzle.transform The problem here is that since the bullets are children of the player, they are affected when the player moves or rotates.\nTo fix this, we should make sure the bullets are added to the world instead. In this case, we’ll use owner, which refers to the root node of the scene the player is in. Note that we also need to use the muzzle’s global transform, or else the bullet would not be where we expected.\nfunc shoot(): var b = Bullet.instantiate() owner.add_child(b) b.transform = $Muzzle.global_transform Related recipes Gamedev Math: transforms Download This Project Download the project code here: https://github.com/godotrecipes/2d_shooting\n","description":"","tags":null,"title":"Shooting projectiles","uri":"/godot_recipes/4.x/2d/2d_shooting/index.html"},{"content":"Project Manager The Project Manager is the first thing you’ll see when opening Godot.\nIn this window you can see a list of your Godot projects. You can choose an existing project and click “Run” to play the game or click “Edit” to work on it in the Godot editor. Since you probably don’t have any projects yet, let’s start by clicking the “New Project” button.\nHere you can give the project a name and create a folder to store it in.\nNote Every Godot project is contained in its own folder. This has many benefits, including making it easy to move, share, and backup projects. It also means that all the project’s files (images, sounds, etc.) must be in the project folder.\nWhen you’re naming your project, try to choose a name that describes the project. “New Game Project #23” is not going to help you remember what that project was. You should also think about compatibility: some operating systems are case-sensitive, and some are not. This can lead to problems if you move or share your project from one computer to another. For this reason, many programmers develop a standardized naming scheme. For example: “No spaces, use ‘_’ between words.”\nLet’s name this new project “getting_started”. Type this name, click Create Folder, and then click Create \u0026 Edit.\nYou’re now looking at the Godot editor window. This is where you’ll spend most of your time when working in Godot. The editor is divided into sections.\nViewport: This is where you’ll see the parts of your game as you’re working on them. Workspaces: At the center-top, you can switch between working in the 2D, 3D, or Script workspaces. You start in 3D. Playtest Buttons: These buttons let you launch and control your game when testing. Docks/Tabs: On both sides are a number of docks where you can view game items and set their properties. Bottom Panel: Here, you’ll see context-specific information for various tools. The most important one to note first is the Output panel, where you’ll see any error or informational messages when your game is running. Project Settings Now we’ve talked about the main parts of the Godot window and how they work, let’s spend a little time talking about our Project settings. Usually one of the first tasks when starting a new project is make sure it’s all set up correctly.\nSo let’s click on Project in the menu and select Project Settings.\nThis is the Project settings window. On the left is a list of categories. For most projects, the default settings will be fine, and you shouldn’t worry about changing them unless you have a very specific need. For now, we’re just going to look at two of the sections. First, Application/Config.\nIn here, you can set your game’s title, choose which scene is the “main scene” (more about that in a bit), and change the icon.\nSecond, let’s look at the Display section. This is where you set up your game’s display. width \u0026 height let you set the size of the game window. If, for example, you were making a mobile game, you’d want to set this to the resolution and proportions of your target device. There are also settings for scaling, stretching, fullscreen mode, and more. For now, we’ll leave the default size - later on we’ll talk about how to adjust these to get our game running on different devices.\nThere are also some tabs across the top. We’ve been looking at the General tab. I’ll also point out briefly, the Input Map. This is where you can define different input actions for keyboard control, gamepad, mouse, and so on. In your game, you’ll just worry about the action, not what individual key or button was pressed. This is a very powerful and flexible way of handling player input.\nWe also have localization options, if you plan to support multiple languages. Autoloading, which we’ll get to later, and plugins. The Godot community has created a variety of useful plugins that you can download and add to supply more features, different tools, and so on.\nWe’ll come back to the project settings window later. Let’s close it for now and we’re ready to move on to the next step: working with nodes.\n","description":"","tags":null,"title":"The Godot Editor: Finding your way around","uri":"/godot_recipes/4.x/g101/start/101_02/index.html"},{"content":"Problem It’s probably the most common problem seen in the Godot help channels: an invalid node reference. Most often, it appears as the following error message:\nInvalid get index ‘position’ (on base: ’null instance’).\nSolution It’s that last part, the “null instance”, that’s the source of this problem, and the main source of confusion for Godot beginners.\nThe way to avoid this problem is to understand the concept of node paths.\nUnderstanding node paths The scene tree is made of nodes, which are connected together in parent-child relationships. A node path is the path it takes to get from one node to another by moving through this tree.\nAs an example, let’s take a simple “Player” scene:\nThe script for this scene is on the Player node. If the script needs to call play() on the AnimatedSprite node, it needs a reference to that node:\nget_node(\"AnimatedSprite\").play() The argument of the get_node() function is a string representing the path to the desired node. In this case, it’s a child of the node the script is on. If the path you give it is invalid, you’ll get the dreaded null instance error (as well as “Node not found”).\nGetting a node reference with get_node() is such a common situation that GDScript has a shortcut for it:\n$AnimatedSprite.play() Info get_node() returns a reference to the desired node.\nLet’s look at a more complex scene tree:\nIf the script on Main needs to access ScoreLabel it can do so with this path:\nget_node(\"HUD/ScoreLabel\").text = \"0\" # or using the shortcut: $HUD/ScoreLabel.text = \"0\" Tip When using $ notation, the Godot editor will autocomplete paths for you. You can also right-click on a node in the Scene tab and choose “Copy Node Path”.\nWhat if the node you want to access is higher in the tree? You can use get_parent() or \"..\" to reference the parent node. In the above example tree, to get the Player node from the ScoreLabel:\nget_node(\"../../Player\") Let’s break that down. The path \"../../Player\" means “get the node that’s up one level (HUD), then one more level (Main), then its child Player”.\nTip Does this seem familiar? Node paths work exactly like directory paths in your operating system. The / character indicates the parent-child relationship, and .. means “up one level”.\nRelative vs absolute paths The above examples all use relative paths - meaning they start at the current node and follow the path to the destination. Node paths can also be absolute, starting from the root node of the scene.\nFor example, the absolute path to the player node is:\nget_node(\"/root/Main/Player\") /root, which can also be accessed with get_tree().root is not the root node of your scene. It’s the Viewport node that is always present by default in the SceneTree.\nA warning While the above examples work just fine, there are some things you should be aware of that may cause problems later. Imagine the following situation: the Player node has a health property, which you want to display in a HealthBar node somewhere in your UI. You might write something like this in the player’s script:\nfunc take_damage(amount): health -= amount get_node(\"../Main/UI/HealthBar\").text = str(health) While this may work fine at first, it is brittle, meaning it can break easily. There are two main problems with this kind of arrangement:\nYou can’t test the player scene independently. If you run the player scene by itself or in a test scene that doesn’t have a UI, the get_node() line will cause a crash. You can’t change your UI. If you decide to rearrange or redesign your UI, the path will no longer be valid and you have to change it. For this reason, you should try to avoid using node paths that go up the scene tree. In the above situation, if the player instead emitted a signal when the health changed, the UI could listen for that signal to update itself. You could then rearrange and separate nodes without fear of breaking your game.\nWrapping up Once you understand how to use node paths, you’ll see how easy it is to reference any node you need. And put a stop to seeing those null instance error messages.\n","description":"","tags":null,"title":"Understanding node paths","uri":"/godot_recipes/4.x/basics/getting_nodes/index.html"},{"content":"Before reading this, make sure you have an understanding of vectors and how they’re used in game development. If you don’t, I recommend you read this introduction I wrote for the Godot documentation: Vector Math.\n2D Transforms In 2D space, we use the familiar X-Y coordinate plane. Remember that in Godot, as in most computer graphics applications, the Y axis points downward:\nTo begin, let’s consider this spaceship floating in space:\nThe ship is pointing in the same direction as the X axis. If we wanted it to move forward, we could add to its X coordinate and it would move to the right:\nposition += Vector2(10, 0) But what happens when the ship rotates?\nHow do we move the ship forward now? If you remember Trigonometry from school, you might be starting to think about angles, sine and cosine and doing something like position += Vector2(10 * cos(angle), 10 * sin(angle)). While this would work, there’s a much more convenient way: the Transform.\nLet’s look at the rotated ship again, but this time, let’s also imagine that the ship has its own X and Y axes that it carries with it, independent of the global axes:\nThese “local” axes are contained in the object’s transform.\nKnowing this, we can move the ship forward by moving it along its own X axis and we won’t have to worry about angles and trig functions. To do this in Godot, we can use the transform property, which is available to all Node2D derived nodes.\nposition += transform.x * 10 This code says “Add the transform’s x vector multiplied by 10.” Let’s break down what that means. The transform contains x and y properties that represent those local axes. They are unit vectors, which means their length is 1. Another term for unit vector is direction vector. They tell us the direction the ship’s x axis is pointing. We then multiply by 10 to scale it to a longer distance.\nTip The transform property of a node is relative to its parent node. If you need to get the global value, it’s available in global_transform.\nIn addition to the local axes, the transform also contains a component called the origin. The origin represents the translation, or change in position.\nIn this picture, the blue vector is the transform.origin. It is equal to the object’s position vector.\nConverting Between Local and Global Space You can convert coordinates from local to global by applying the transform. For convenience, Node2D and Spatial include helper functions for this: to_local() and to_global():\nvar global_position = to_global(local_position) Let’s use the example of an object in the 2D plane and convert mouse clicks (global space) into coordinates relative to the object:\nextends Sprite func _unhandled_input(event): if event is InputEventMouseButton and event.pressed: if event.button_index == BUTTON_LEFT: printt(event.position, to_local(event.position)) See the Transform2D docs for a list of the available properties and methods.\n3D Transforms In 3D space, the concept of transforms applies in the same way as in 2D. In fact, it becomes even more necessary, as using angles in 3D can lead to a variety of problems, as we’ll see in a bit.\n3D nodes inherit from the base node Node3D, which contains the transform information. The 3D transform requires more information than the 2D version. Position is still held in the origin property, but rotation is in a property called basis, which contains three unit vectors representing the body’s local X, Y, and Z axes.\nWhen you select a 3D node in the editor, the gizmo that appears allows you to manipulate the transform.\nLocal Space Mode In the editor, you can see and manipulate the body’s local orientation by clicking the “Local Space Mode” button. When in this mode, the 3 colored axis lines represent the body’s local basis axes.\nAs in 2D, we can use the local axes to move an object forward. In Godot’s 3D orientation (Y-up), this means that by default the body’s -Z axis is the forward direction. To move forward:\nposition += -transform.basis.z * speed * delta Tip Godot has default vector values defined, for example: Vector3.FORWARD == Vector3(0, 0, -1). See Vector2 and Vector3 for details.\n","description":"","tags":null,"title":"Transforms","uri":"/godot_recipes/4.x/math/transforms/index.html"},{"content":" 2D Tips, tricks, and tutorials on the 2D side of game development.\nIn this section: Entering/Exiting the screen Platform character Screen wrap Top-down movement Grid-based movement Shooting projectiles Car steering 8-Directional Movement/Animation Using Y-Sort Moving Platforms Pathfinding on a 2D Grid Multitarget Camera ","description":"","tags":null,"title":"2D","uri":"/godot_recipes/4.x/2d/index.html"},{"content":"Problem You need to add actions to the InputMap at runtime.\nSolution Typically, you’ll add actions to the InputMap via Project Settings, as shown in Recipe: Input Actions. However, you may find yourself needing to add one or more actions directly in a script. The InputMap singleton has methods to help you do this.\nHere’s an example that would add a new action called “attack” using the space key:\nfunc _ready(): InputMap.add_action(\"attack\") var ev = InputEventKey.new() ev.keycode = KEY_SPACE InputMap.action_add_event(\"attack\", ev) If you also wanted to add the left mouse button to the same action:\nev = InputEventMouseButton.new() ev.button_index = MOUSE_BUTTON_LEFT InputMap.action_add_event(\"attack\", ev) Note InputMap.add_action() will produce an error if the action already exists. You should check first with InputMap.has_action() before attempting to add a new action.\nPractical Example Let’s say you’ve made the platform character from Recipe: Platform character and you want to re-use it in another project. If you saved the scene, script, and assets in a single folder, you need only copy that folder to your new project. But you’d still need to edit the Input Map in order for the inputs to work.\nInstead, you could add the following code to the player script and be sure that the necessary input actions will be added automatically:\nvar controls = {\"walk_right\": [KEY_RIGHT, KEY_D], \"walk_left\": [KEY_LEFT, KEY_A], \"jump\": [KEY_UP, KEY_W, KEY_SPACE]} func _ready(): add_inputs() func add_inputs(): var ev for action in controls: if not InputMap.has_action(action): InputMap.add_action(action) for key in controls[action]: ev = InputEventKey.new() ev.keycode = key InputMap.action_add_event(action, ev) Related recipes Input Actions Platform Character ","description":"","tags":null,"title":"Adding Input Actions in code","uri":"/godot_recipes/4.x/input/custom_actions/index.html"},{"content":"Problem You want to hide the mouse cursor and keep the mouse from leaving the game window. This is common in many 3D games (and some 2D ones).\nSolution You can set the mouse state using Input.mouse_mode. There are four possible mouse modes:\nMOUSE_MODE_VISIBLE: The mouse is visible and can move freely into and out of the window. This is the default state.\nMOUSE_MODE_HIDDEN: The mouse cursor is invisible, but the mouse can still move outside the window.\nMOUSE_MODE_CAPTURED: The mouse cursor is hidden and the mouse is unable to leave the game window.\nMOUSE_MODE_CONFINED: The mouse is visible, but cannot leave the game window.\n“Captured” is the most commonly used option. You can set the mouse mode at runtime using:\nfunc _ready(): Input.mouse_mode = Input.MOUSE_MODE_CAPTURED When the mouse is captured, mouse input events will still be passed as normal. However, you will find there is a problem. If you want to close the game or switch to another window, you can’t. For this reason, you will want to also include a way to “release” the mouse. For example, to release when the player pressed the Escape key:\nfunc _input(event): if event.is_action_pressed(\"ui_cancel\"): Input.mouse_mode = Input.MOUSE_MODE_VISIBLE So that the game doesn’t respond to mouse movement when you’re in another window, you can test for the capture state in your character controller using:\nif Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: Once the mouse is released, that leaves the need to re-capture it to continue playing. Assuming you have an event in the Input Map for a mouse click, you can do the following:\nif event.is_action_pressed(\"click\"): if Input.mouse_mode == Input.MOUSE_MODE_VISIBLE: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED Since you may also be using a mouse click to shoot or perform some other action, it’s probably a good idea to stop the event from propagating. Add this after setting the mouse mode:\nget_tree().set_input_as_handled() ","description":"","tags":null,"title":"Capturing the Mouse","uri":"/godot_recipes/4.x/input/mouse_capture/index.html"},{"content":"Problem You need to create a 2D top-down car controller.\nSolution When approaching this problem, beginners often wind up creating something that handles nothing like a real car. Some common mistakes you’ll find in amateur car games:\nA car doesn’t rotate around its center. Put another way, a car’s rear wheels don’t slide side-to-side. (Unless it’s drifting, but we’ll talk about that later.) A car can only turn when it’s moving - it can’t spin in place. A car isn’t a train; it’s not on rails. Turning at high speeds should involve some sliding (drifting). There are many approaches to 2D car physics, mainly depending on how “realistic” you want to be. For this solution, we’re going for an “arcade” level of realism, meaning we’ll prioritize action over realism.\nNote The method below is based on the algorithm found here: http://engineeringdotnet.blogspot.com/2010/04/simple-2d-car-physics-in-games.html\nThe recipe below is broken into 5 parts, each adding a different feature to the car’s movement. Feel free to mix-and-match for your needs.\nScene setup Here’s the car scene setup:\nCharacterBody2D Sprite2D CollisionShape2D Camera2D Add whatever sprite texture you like. For this demo, we’ll use art from Kenney’s Racing Pack. CapsuleShape2D is a good choice for the collision, so that the car won’t have sharp corners to get caught on obstacles.\nWe’ll also use four input actions: “steer_right”, “steer_left”, “accelerate”, and “brake” - set them to whatever key inputs you prefer.\nPart 1: Movement The first step is to code the movement based on the algorithm described above.\nStart with a few variables:\nextends CharacterBody2D var wheel_base = 70 # Distance from front to rear wheel var steering_angle = 15 # Amount that front wheel turns, in degrees var steer_direction Set wheelbase to a value that works with your sprite.\nsteer_direction will be the amount that the wheels are turned.\nNote Since we’re using keyboard controls, turning is all-or-nothing. If you’re using an analog joystick, you can instead vary this value based on the distance the stick moves.\nfunc _physics_process(delta): get_input() calculate_steering(delta) move_and_slide() Each frame, we need to check for input and calculate steering. Then we pass the resulting velocity to move_and_slide(). We’ll define those two function next:\nfunc get_input(): var turn = Input.get_axis(\"steer_left\", \"steer_right\") steer_direction = turn * deg_to_rad(steering_angle) velocity = Vector2.ZERO if Input.is_action_pressed(\"accelerate\"): velocity = transform.x * 500 Here we check for user input and set the velocity. Note: the speed of 500 is temporary so that we can test movement. We’ll address it in the next part.\nHere is where we implement the algorithm from the link:\nfunc calculate_steering(delta): # 1. Find the wheel positions var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 # 2. Move the wheels forward rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_direction) * delta # 3. Find the new direction vector var new_heading = rear_wheel.direction_to(front_wheel) # 4. Set the velocity and rotation to the new direction velocity = new_heading * velocity.length() rotation = new_heading.angle() Run the project and the car should move and turn. It’s still very unnatural though - the car starts and stops instantly. To fix that, we’ll add acceleration into the calculation.\nPart 2: Acceleration We’ll need another setting variable and one to track the car’s overall acceleration:\nvar engine_power = 900 # Forward acceleration force. var acceleration = Vector2.ZERO Change the input code to apply acceleration instead of directly changing the car’s velocity.\nfunc get_input(): var turn = Input.get_axis(\"steer_left\", \"steer_right\") steer_direction = turn * deg_to_rad(steering_angle) if Input.is_action_pressed(\"accelerate\"): acceleration = transform.x * engine_power Once we’ve got our acceleration, we can apply it to the velocity like so:\nfunc _physics_process(delta): acceleration = Vector2.ZERO get_input() calculate_steering(delta) velocity += acceleration * delta move_and_slide() Now when you run, the car should gradually increase its speed. Careful: we don’t have any way to slow down yet!\nPart 3: Friction/drag A car experiences two different deceleration forces: friction and drag.\nFriction is the force applied by the ground. It’s very high if driving on sand, but very low if driving on ice. Friction is proportional to velocity - the faster you’re going the stronger the force.\nDrag is the force resulting from wind resistance. It’s based on the car’s cross-section - a large truck or van experiences more drag than a sleek race car. Drag is proportional to the velocity squared.\nThis means that friction is more significant when moving slowly, but drag becomes dominant at high speeds. We’ll add both of these forces to our calculation. As a bonus, the values of these quantities will also give our car a maximum speed - the point where the force from the engine can’t overcome the drag force any longer.\nHere are our starting values for these quantities:\nvar friction = -55 var drag = -0.06 As you can see in this graph, these values mean that at a speed of 600 the drag force overcomes the friction force.\nYou can play with the values here to see how they change: https://www.desmos.com/calculator/e4ayu3xkip\nIn _physics_process() we’ll call a function to calculate the current friction and apply it to the acceleration force.\nfunc _physics_process(delta): acceleration = Vector2.ZERO get_input() apply_friction(delta) calculate_steering(delta) velocity += acceleration * delta velocity = move_and_slide(velocity) func apply_friction(delta): if acceleration == Vector2.ZERO and velocity.length() \u003c 50: velocity = Vector2.ZERO var friction_force = velocity * friction * delta var drag_force = velocity * velocity.length() * drag * delta acceleration += drag_force + friction_force First, we’ll set a minimum speed. This will ensure that the car doesn’t keep creeping forward at very low speeds as friction never quite brings the velocity to zero.\nThen we calculate the two forces and add them to the total acceleration. Since they’re both negative, they’ll affect the car in the opposite direction.\nPart 4: Reverse/Brake We’ll need two more settings variables:\nvar braking = -450 var max_speed_reverse = 250 Add the input to get_input():\nif Input.is_action_pressed(\"brake\"): acceleration = transform.x * braking This is fine for coming to a stop, but we also want to be able to put the car in reverse. Currently, that won’t work, because the acceleration is always being applied in the “heading” direction, which is forward. When we’re reversing, we need to accelerate backward.\nfunc calculate_steering(delta): var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_angle) * delta var new_heading = (front_wheel - rear_wheel).normalized() var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = new_heading * velocity.length() if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) rotation = new_heading.angle() We can find whether we’re accelerating forward or backward using the dot product. If the two vectors are aligned, the result will be greater than 0. If the movement is in the opposite direction the car’s facing, then the dot product will be less than 0 and we must be moving backward.\nPart 5: Drift/slide We could stop here and you’d have a satisfactory driving experience. However, the car still feels like it’s “on rails”. Even at top speed, the turns are perfect, as if the tires have perfect “grip”.\nAt high speeds (or even low ones, if desired), the turning force should cause the tires to slip and result in a fishtailing/sliding motion.\nvar slip_speed = 400 # Speed where traction is reduced var traction_fast = 2.5 # High-speed traction var traction_slow = 10 # Low-speed traction We’ll apply these values when calculating the steering. Currently, the velocity is instantly set to the new heading. Instead, we’ll use interpolation - lerp() - to cause it to only “turn” partway towards the new direction. The “traction” values will determine how “sticky” the tires are.\nfunc calculate_steering(delta): var rear_wheel = position - transform.x * wheel_base / 2.0 var front_wheel = position + transform.x * wheel_base / 2.0 rear_wheel += velocity * delta front_wheel += velocity.rotated(steer_angle) * delta var new_heading = (front_wheel - rear_wheel).normalized() # choose which traction value to use - at lower speeds, slip should be low var traction = traction_slow if velocity.length() \u003e slip_speed: traction = traction_fast var d = new_heading.dot(velocity.normalized()) if d \u003e 0: velocity = lerp(velocity, new_heading * velocity.length(), traction * delta) if d \u003c 0: velocity = -new_heading * min(velocity.length(), max_speed_reverse) rotation = new_heading.angle() Here, we select which traction value to use and apply lerp() to the velocity.\nAdjustments At this point, we have a large number of settings that control the car’s behavior. Adjusting them can drastically change how the car drives. To make experimenting with different values easier, download the project for this recipe below. When you run the game, you’ll see a set of sliders you can use to change the car’s behavior as you drive (press \u003cTab\u003e to show/hide the slider panel).\nRelated recipes Gamedev Math: Interpolation Download This Project Download the project code here: https://github.com/godotrecipes/2d_car_steering\n","description":"","tags":null,"title":"Car steering","uri":"/godot_recipes/4.x/2d/car_steering/index.html"},{"content":"In the last section, we configured the project and downloaded the game art. Now we’re ready to start coding - starting with the player-controlled ship.\nAdding a script Writing scripts and attaching them to nodes and other objects is how you build behavior and game mechanics into your game. Our Player scene displays the ship, defines its collision hitbox, etc., but it can’t move, and nothing would happen if it collided. We’ll write code to add this functionality to the ship.\nSelect the Player node and click the Attach script button:\nYou don’t need to change any of the options on the Attach Node Script window, so just click Create and you’ll be taken to the script editor.\nLet’s look at the first line of the script, which has automatically been added.\nextends Area2D This line defines what type of object this script should be attached to. It means that the script will have access to all the functionality that an Area2D provides.\nYour extends line should always match the type of node the script is attached to.\nAccessing scripts A script on its own doesn’t do much of anything. Scripts define additional functionality for whatever object they’re attached to. You will never be accessing a variable in some script, you’ll be accessing a property of an object, which is defined by that script. This is a very important distinction.\nMovement We’ll start by making the ship move around the screen. Let’s start with some code that does the following:\nDetect what input(s) the player is pressing Move the ship in the direction of the input @export var speed = 150 func _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") position += input * speed * delta Let’s break this down line-by-line:\nAdding @export in front of a variable allows you to adjust its value in the Inspector. The _process() function is called once every frame by the engine. Any code we place in this function will be executed every frame. Input.get_vector() checks the pressed state of the four given inputs and produces a vector pointing in that direction. Finally, we move the ship’s position by adding that input vector, scaling it to the desired speed, and multipling by delta. Links to more information Understanding vectors: Vector Math What is delta? Understanding delta Run the scene by clicking the Run Current Scene button, and try moving around.\nStaying on screen One problem we have is that if you keep moving, you’ll go off the screen. We need to lock the player’s position property inside the bounds of the screen rectangle. Add this line at the top of the script:\n@onready var screensize = get_viewport_rect().size The @onready here tells Godot not to set the value of the screensize variable until the Player node has entered the scene tree. Effectively, it means “wait until the game starts”, because there’s no window to get the size of until the game is running.\nThe next step is to clamp the position within the bounds of that screensize rectangle. Vector2, which is what position is, has a clamp() method we can use. Put this line right after setting the position:\nfunc _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") position += input * speed * delta position = position.clamp(Vector2.ZERO, screensize) Run the scene again and try moving off the edges. You’ll notice that half of the ship still goes off screen. This is because the ship’s position is the center of the Sprite2D. Since we know our ship is 16x16, we can change the clamp() to include 8 extra pixels:\nposition = position.clamp(Vector2(8, 8), screensize - Vector2(8, 8)) Matching animation to direction Now that the ship is moving, we can choose the “tilted” ship images when moving left or right, as well as the matching “Booster” animation.\nTo tell which direction we’re moving, we can check the x value of the input vector. Depending on whether it’s positive (right), negative (left), or zero (not moving), we can choose the frame value of the Sprite2D and the animation of the AnimatedSprite2D.\nfunc _process(delta): var input = Input.get_vector(\"left\", \"right\", \"up\", \"down\") if input.x \u003e 0: $Ship.frame = 2 $Ship/Boosters.animation = \"right\" elif input.x \u003c 0: $Ship.frame = 0 $Ship/Boosters.animation = \"left\" else: $Ship.frame = 1 $Ship/Boosters.animation = \"forward\" position += input * speed * delta position = position.clamp(Vector2(8, 8), screensize-Vector2(8, 8)) Once again, play the scene and verify that the images change when moving left/right. Verify that everything works as intended before moving to the next step.\nThe next step will be to create the Bullet scene and let the player shoot.\n","description":"","tags":null,"title":"Coding the Player","uri":"/godot_recipes/4.x/games/first_2d/first_2d_03/index.html"},{"content":"In the last part, we covered how to import 3D objects and how to arrange them in a scene. In this installment, we’ll add more objects to the scene, including a user-controlled character.\nBuilding the Scene We’re going to continue using the Kenney Platformer Kit we downloaded in Part 2. Select all the block*.glb files and in the Import tab set their Root Type to StaticBody3D. Uncheck the Root Name property and click Reimport. Select blockLarge.glb and make a new inherited scene. Use the Create Single Convex Collision Sibling option on the mesh using the menu as you did in the last tutorial. Now you can save the scene - I recommend making a separate folder for this, as soon you’re going to have a bunch of scenes representing the differently shaped platform parts.\nOpen the scene from the previous step with the “Ground” plane and the crates. Delete the crates and add an instance of the large block. We want to be able to place these blocks so that they line up. To do this, select “Configure Snap” from the “Transform” menu at the top of the Viewport and set Translate Snap to 0.5. Then click on the “Snap Mode” button (or press the Y key). Now duplicate the block a few times and drag them to arrange.\nIf you like, go ahead and add scenes for some of the other platform blocks and arrange them into a pleasing level. Be creative!\nAdding a Character Now we’re going to make a character so we can walk around on the platforms. Open a new scene and start with a CharacterBody3D named “Character”. This PhysicsBody node behaves very much like its 2D equivalent (you’ve already done the 2D tutorials, right?). It has a move_and_slide() method that we’ll use to perform the movement and collision detection.\nAdd a capsule-shaped MeshInstance3D and a matching CollionShape3D. Remember, you can add a StandardMaterial3D to the mesh and set its Albedo/Color property to change the color.\nThe capsule is nice, but it’s going to be hard to tell what direction it’s facing. Let’s add another mesh, this time with a CylinderMesh3D shape. Set its Top Radius to 0.2, its Bottom Radius to 0.001 and its Height to 0.5, then its x rotation to -90 degrees. Now you have a nice cone shape. Arrange it so it’s pointing out from the body along the negative z axis. (You can tell which way is negative because the gizmo arrows point in the positive direction).\nIn this picture, we’ve also added two sphere meshes for eyes to give a little more character. Feel free to add whatever details you like.\nLet’s also add a Camera3D to the scene, so it will follow the player around. Position the camera behind and above the character, angling it down a bit. Click the “Preview” button to check the camera’s view.\nBefore we add a script, open the “Project Settings” and add the following inputs on the “Input Map” tab:\nInput Action Key move_forward W move_back S strafe_right D strafe_left A jump Space Now let’s add a script to the body.\nextends CharacterBody3D var gravity = ProjectSettings.get_setting(\"physics/3d/default_gravity\") var speed = 4.0 # movement speed var jump_speed = 6.0 # determines jump height var mouse_sensitivity = 0.002 # turning speed func get_input(): var input = Input.get_vector(\"strafe_left\", \"strafe_right\", \"move_forward\", \"move_back\") velocity.x = input.x * speed velocity.z = input.y * speed func _physics_process(delta): velocity.y += -gravity * delta get_input() move_and_slide() The code in _physics_process() is pretty straightforward: add gravity to accelerate in the positive Y direction (downward), call get_input() to check for input, and then use move_and_slide() to move in the direction of the velocity vector.\nIn get_input() we check to see which key is pressed and then move in that direction. Run the program and test:\nThis is all good, but we need to be able to rotate using the mouse. Add the following code to the character’s script:\nfunc _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity) This will convert any mouse motion in the x direction into a rotation around the y axis.\nRun the scene and confirm that moving the mouse rotates the character:\nHowever, there’s a problem. No matter which way we’re facing, pressing W moves us along the Z axis of the world. Our movement is using global coordinates, but we need to move in the object’s forward direction.\nThe Power of Transforms This is where transforms come in. A transform is a mathematical matrix that contains the object’s translation, rotation, and scale information all in one. In Godot it’s stored in the Transform data type. The position information is called the transform.origin and the orientation information is in the transform.basis.\nRemember how the 3D gizmo can be set to “Local Space Mode”? When in this mode, the gizmo’s X/Y/Z axes point along the object’s axes. This is the same as the basis of the transform. The basis contains three Vector3 objects called x, y, and z that represent these directions. We can use this to ensure that pressing the W key will always move us in the object’s forward direction.\nChange the get_input() function like so:\nfunc get_input(): var input = Input.get_vector(\"strafe_left\", \"strafe_right\", \"move_forward\", \"move_back\") var movement_dir = transform.basis * Vector3(input.x, 0, input.y) velocity.x = movement_dir.x * speed velocity.z = movement_dir.z * speed By multiplying the input vector by the transform.basis, we apply that transformation to the vector. Since the basis represents the object’s rotation, we’ve now converted forward and back to point along the object’s Z axis, and the strafe keys along its X.\nJumping Let’s add one more movement to the player: jumping.\nAdd these lines to the end of get_input():\nif event.is_action_pressed(\"jump\") and is_on_floor(): velocity.y = jump_speed Improving the camera You may have noticed that the if the character stands near an obstacle, the camera can “clip” inside the object, which doesn’t look nice. While coding a good 3D camera can be a complex topic on its own, we can use a built-in Godot node to get a pretty good solution.\nDelete the Camera3D from the character scene and add a SpringArm3D. This node can act as a moving arm that holds the camera while detecting collisions. It will move the camera closer if there’s an obstacle.\nIn its properties, set Spring Length to 5, and set its Position to (0, 1, 0), which is at the character’s head. Note the yellow line indicating the Spring Length. The camera will move along this line - at its end whenever possible, but moving closer if an obstacle is there.\nAdd back a Camera3D as a child of the SpringArm3D, and try running the game again. You can experiment with rotating the spring arm (around its x axis to point down slightly, for example) until you find something you like.\nWhat about first person? If you’re curious how you would do this in first person, see the Basic FPS Character recipe. You’ll notice several similarities with the 3rd person script we wrote above.\nWrapping Up In this tutorial you learned how to build a more complex scene, and how to write movement code for a user-controlled character. You also learned about transforms, which are a very important concept in 3D - you’re going to be using a lot in the future.\n","description":"","tags":null,"title":"Creating a 3D Character","uri":"/godot_recipes/4.x/g101/3d/101_3d_03/index.html"},{"content":"Nodes are the basic building blocks for creating games in Godot. A node is an object that can represent some kind of specialized game function. A given type of node might display graphics, play an animation, or represent a 3D model of an object. The node also contains a collection of properties, allowing you to customize its behavior. Which nodes you add to your project will depend on what functionality you need. It’s a modular system designed to give you flexibility in building your game objects.\nWorking with Nodes Nodes are objects, in the programming sense. They encapsulate data and behavior, and they can inherit properties from other nodes. Rather than use one of the default suggestions, let’s click the “Add/Create a New Node” button in the scene dock.\nHere you’ll see the whole hierarchy of node types available in the engine. For example, the nodes with the bluish icons all fall under the “Node2D” category, meaning they will all have the properties of a Node2D. More about that in a moment.\nThe list is long, and it would be frustrating to have to drill down every time to find the node you need. Instead, you can use the search function to find it using a small number of characters. We’re looking for the Sprite2D node, so I’ll just type “sp” and we’ll jump right to it. Click “Create” to add the node.\nNow we have this Sprite2D node in our Scene dock. Make sure it’s selected, and then look at the Inspector dock on the right side. Over here, you’ll see all the properties of whatever node you have selected. Notice that the properties are organized by where they come from. The Sprite2D node inherits from Node2D, which inherits from CanvasItem, which inherits from the plain old Node.\nOver in the viewport, the sprite doesn’t look like much. A sprite’s purpose is to display an image, or texture. As you can see in the Inspector, the Texture property is currently empty. Fortunately, every new Godot project comes with an image we can use: the Godot icon. Drag the icon from the Filesystem dock and drop it in the texture property.\nIn the Inspector, click to expand the “Transform” section, and type (50, 50) in the Position property.\nYou can also click and drag the sprite around in the viewport, and you’ll see the Position values changing as you move.\nOne important property of nodes is that they can be arranged in a parent-child hierarchy. Make sure you have the Sprite2D selected and press the add button again. Add another Sprite2D and also drag the icon into its texture.\nThis new sprite is a child of the first. This means that it’s “attached” to its parent. If the parent sprite moves, so will the child. Click on the child sprite and set its Position to (50, 50). Now click and drag the parent sprite to move it around the screen.\nNotice that the Position of the parent is changing as you move it around. Now check the child: it’s still (50, 50). That’s because its “Transform” properties are relative to its parent.\nScenes Grouping nodes together like this is a powerful tool, enabling you to construct complex objects out of node “building blocks”. For example, a “Player” node in your game might have many child nodes attached to it: a Sprite2D for display, an AnimationPlayer to animate it, a Camera2D to follow it around, and so on.\nA group of nodes arranged in a “tree” structure like this is called a Scene. In the next part, we’ll look at how you can use scenes to organize your game’s objects into independent parts that all work together. You’ll see this in practice was you work through the examples in later lessons.\n","description":"","tags":null,"title":"Nodes: Godot's building blocks","uri":"/godot_recipes/4.x/g101/start/101_03/index.html"},{"content":"Problem You need to implement shooting in an FPS, but moving individual projectiles is impractical.\nSolution Game physics engines often break down when trying to handle very fast-moving objects. The solution is to cast a ray from the shooter’s location and detect the first thing that would be hit.\nThere are two ways to approach raycasting in Godot: the RayCast3D node, or directly casting a ray in space using the physics engine. While they can both accomplish the same thing, each has its uses. The node method tends to be best for situations where you continuously want to check for collisions - a downward-facing ray to check if you’re on the floor, for example.\nWe’ll use the second method, querying the physics state, because we want to know, at the moment we press the “shoot” key, whether we’ve hit anything.\nNote This recipe assumes you already have a working FPS character controller and a world to move around in. If you don’t, see the Basic FPS Character recipe first.\nTo display what we’ve hit, add a CanvasLayer with a Label node to the FPSPlayer scene.\nWe’ll add an input check in the _input() function, which we’re already using to handle mouse input.\nif event.is_action_pressed(\"shoot\"): shoot() Then we’ll define the shoot() method. Whenever it’s called, we want to build a PhysicsRayQueryParameters3D object, which defines the start (position of the camera) and end (position of the camera projected forward by 100 meters) points of the ray. We’ll pass this to the physics engine using the direct_space_state of the world. If we get a returned value (a dictionary containing data about the collision), we’ll update the label so we can see what kind of object we hit.\nfunc shoot(): var space = get_world_3d().direct_space_state var query = PhysicsRayQueryParameters3D.create($Camera3D.global_position, $Camera3D.global_position - $Camera3D.global_transform.basis.z * 100) var collision = space.intersect_ray(query) if collision: $CanvasLayer/Label.text = collision.collider.name else: $CanvasLayer/Label.text = \"\" Related recipes Basic FPS Character Download This Project Download the project code here: https://github.com/godotrecipes/3d_shoot_raycasts\n","description":"","tags":[],"title":"Shooting with Raycasts","uri":"/godot_recipes/4.x/3d/shooting_raycasts/index.html"},{"content":"Problem The delta or “delta time” parameter is a frequently-misunderstood concept in game development. In this tutorial, we’ll explain how it’s used, the importance of frame-rate independent movement, and practical examples of its use in Godot.\nSolution To illustrate the problem, let’s consider a Sprite node moving across the screen. If our screen is 600 pixels wide and we want the sprite to take 5 seconds to cross the screen, we can use the following calculation to find the necessary speed:\n600 pixels / 5 seconds = 120 pixels/second We’ll move the sprite every frame using the _process() function. If the game is running at 60 frames per second, we can find the per-frame movement like so:\n120 pixels/second * 1/60 second/frame = 2 pixels/frame Tip Notice the units are consistent in all the calculations above. Always pay attention to the units in your calculations - it’ll save you from making mistakes.\nHere’s the necessary code:\nextends Node2D # Desired movement in pixels/frame var movement = Vector2(2, 0) func _process(delta): $Sprite.position += movement Run this code and you’ll see the sprite takes 5 seconds to cross the screen.\nMaybe. The trouble begins if there is something else occupying the computer’s time. This is called lag and can come from a variety of sources - the cause could be your code or even other applications running on your computer. If this happens, then the length of a frame might increase. As an extreme example, imagine that the frame rate is halved - each frame took 1/30 instead of 1/60 of a second. Moving at 2 px/frame, it’s now going to take twice as long for the sprite to reach the edge.\nEven small frame rate fluctuations will result in inconsistent movement speed. If this were a bullet or other fast-moving object, we wouldn’t want it slowing down like this. We need the movement to be frame rate independent.\nFixing the frame rate problem When using the _process() function, it automatically includes a parameter called delta that’s passed in from the engine (so does _physics_process(), which is used for physics-related code). This is a floating point value representing the length of time since the previous frame. Typically, this will be approximately 1/60 or 0.0167 seconds.\nWith this information, we can stop thinking about how much to move each frame, and only consider our desired speed in pixels/second (120 from the above calculation).\nMultiplying the engine’s delta value by this number will give us how many pixels to move each frame. The number will automatically adjust if the frame time fluctuates.\n# 60 frames/second 120 pixels/second * 1/60 second/frame = 2 pixels/frame # 30 frames/second 120 pixels/second * 1/30 second/frame = 4 pixels/frame Note that if the frame rate decreases by half (meaning the frame time doubles), then our per-frame movement must also double to keep the desired speed.\nLet’s change the code to use this calculation:\nextends Node2D # Desired movement in pixels/second. var movement = Vector2(120, 0) func _process(delta): $Sprite.position += movement * delta Now when running at 30 frames per second, the travel time is consistent:\nIf the frame rate gets very low, the movement is no longer smooth, but the time remains the same.\nUsing delta with motion equations What if your movement is more complex? The concept remains the same. Keep your units in seconds, not frames, and multiply by delta each frame.\nTip Working in pixels and seconds is much easier to conceptualize too, since it relates to how we measure these quantities in the real world. “Gravity is 100 pixels/second/second, so after the ball falls for 2 seconds, it’s traveling at 200 pixels/second.” If you’re working with frames, then you have to think about acceleration in units of pixels/frame/frame. Go ahead and try - it’s not very natural.\nFor example, if you are applying a gravity, that’s an acceleration - each frame it will increase the velocity by some amount. As in the above example, the velocity then changes the node’s position.\nTry adjusting delta and target_fps in the following code to see the effect:\nextends Node2D # Acceleration in pixels/sec/sec. var gravity = Vector2(0, 120) # Acceleration in pixels/frame/frame. var gravity_frame = Vector2(0, .033) # Velocity in pixels/sec or pixels/frame. var velocity = Vector2.ZERO var use_delta = false var target_fps = 60 func _ready(): Engine.target_fps = target_fps func _process(delta): if use_delta: velocity += gravity * delta $Sprite.position += velocity * delta else: velocity += gravity_frame $Sprite.position += velocity Note that we’re multiplying by our timestep each frame to update both velocity and position. Any quantity that is updated every frame should be multiplied by delta to ensure it changes independent or frame rate.\nUsing kinematic functions In the above examples, we’ve used a Sprite to keep things simple, updating the position every frame. If you’re using a kinematic body (in 2D or 3D), you’ll instead be using one of its movement methods. Specifically in the case of move_and_slide(), there tends to be some confusion, because it uses the velocity vector, not the position. This means you won’t multiply your velocity by delta to find distance - the function does that for you. But you will still need to apply it on any other calculations, such as the acceleration. For example:\n# Sprite movement code: velocity += gravity * delta position += velocity * delta # Kinematic body movement code: velocity += gravity * delta move_and_slide() If you don’t use delta when applying acceleration to your velocity, then your acceleration will be subject to fluctuations in frame rate. This can have a_much more subtle effect on movement - it will be inconsistent, but much more difficult to diagnose.\nTip When using move_and_slide() you still need to apply delta to any other quantities such as gravity, friction, etc.\nRelated Recipes ","description":"","tags":null,"title":"Understanding 'delta'","uri":"/godot_recipes/4.x/basics/understanding_delta/index.html"},{"content":" 3D Tips, tricks, and tutorials on the 3D side of game development.\nIn this section: Basic FPS Character Interpolated Camera Shooting with Raycasts CharacterBody3D: Movement 3D Unit Healthbars Rolling Cube Arcade-style Spaceship Arcade-style Car Click to move CharacterBody3D: Align with Surface ","description":"","tags":null,"title":"3D","uri":"/godot_recipes/4.x/3d/index.html"},{"content":"Problem You need a 2D character that has 8-directional movement, including animation.\nSolution For our example, we’ll use the Isometric Mini-Crusader, which contains 8-directional animations for idle, run, attack, and several other states.\nThe animations are organized in folders, with a separate image for each frame. We’ll use an AnimatedSprite2D and we’ll name each animation based on its direction. For example, idle0 pointing to the right and going clockwise to idle7.\nWhen our character moves, it will pick an animation based on the direction of movement:\nWe’ll use the mouse to move - the character will always face the mouse and run in that direction when we click the mouse button.\nTo choose which animation to play, we need to get the mouse direction and map it to this same range of 0-7. get_local_mouse_position() gives us the position of the mouse relative to the character. We can then use snappedf() to snap the angle of the mouse vector to the closest multiple of 45° (PI/4 radians) giving the following result:\nDivide each value by 45° (PI/4 radians), and we have:\nFinally, we need to map the resulting range to 0-7 using the wrapi() function, and we’ll have our correct values. Adding that value to the end of the animation name (“idle”, “run”, etc) gives us the correct animation:\nfunc _physics_process(delta): current_animation = \"idle\" var mouse = get_local_mouse_position() angle = snappedf(mouse.angle(), PI/4) / (PI/4) angle = wrapi(int(angle), 0, 8) if Input.is_action_pressed(\"left_mouse\") and mouse.length() \u003e 10: current_animation = \"run\" velocity = mouse.normalized() * speed move_and_slide() $AnimatedSprite2D.animation = current_animation + str(a) Testing the movement, we see this:\nKeyboard input If you’re using keyboard controls instead of mouse, you can get the angle of movement based on which keys are being held. The rest of the process works in the same way.\nfunc _process(delta): current_animation = \"idle\" var input_dir = Input.get_vector(\"left\", \"right\", \"up\", \"down\") if input_dir.length() != 0: angle = input_dir.angle() / (PI/4) angle = wrapi(int(a), 0, 8) current_animation = \"run\" velocity = input_dir * speed move_and_slide() $AnimatedSprite2D.play(current_animation + str(angle)) Download This Project Download the project code here: https://github.com/godotrecipes/8_direction_animation\n","description":"","tags":null,"title":"8-Directional Movement/Animation","uri":"/godot_recipes/4.x/2d/8_direction/index.html"},{"content":"Now that the player can move around the screen, our next step will be to implement shooting\nReusable objects The player will fire many “bullets” during the game, but all of them will be identical. A bullet needs to do the following:\nAppear just ahead of the player Travel forward until going off the screen Detect collisions with enemies Since all bullets will do these same things, we can save ourselves a great deal of work by designing one “prototype” bullet, and using that as the blueprint for creating as many duplicates as we need. Godot’s scene system is ideal for this.\nBullet scene Create a new scene by selecting Scene -\u003e New Scene in the menu, or by clicking the + in the tabs on the top of the viewport.\nJust like we did with the Player scene, we need to consider what nodes we’ll need to make the bullet work. We can again use an Area2D, since that will allow us to detect the bullet hitting things. This means we’ll need a collision shape, and a sprite to display the bullet image. Finally, we need a way to detect when the bullet goes offscreen so we can automatically remove it.\nHere’s the node setup:\nArea2D - name this Bullet Sprite2D CollisionShape2D VisibleOnScreenNotifier2D From the asset pack folder, drop the Player_charged_beam (16 x 16).png image on the Texture of the Sprite2D.\nAs with the ship image, there are multiple versions here, so set the *Hframes to 2 so we’ll only see one at a time.\nSet the shape of the CollisionShape2D just like you did earlier in the Player scene.\nBullet script Attach a script to the Bullet node and let’s start with the movement:\nextends Area2D @export var speed = -250 func start(pos): position = pos func _process(delta): position.y += speed * delta This should look fairly familiar, as it’s similar to the player script. We’re only changing the position.y since the bullet should travel straight up.\nNote the start() function we defined. That will let us set the bullet’s starting position, since the player will move around and spawn the bullets at different locations.\nConnecting signals Now select the Bullet node and then click the Node tab next to the Inspector.\nThis is a list of all the signals this node can emit. Signals are how Godot lets you know that something has happened. In this case, we can use the area_entered signal to tell us whenever this bullet touches another Area2D node.\nSelect the area_entered signal and click the Connect… button (you can also double-click the signal name). In the dialog that opens up, just click Connect - we don’t need to change anything there.\nYou’ll notice that you’re back in the script editor, looking at bullet.gd, and a new function as been added. It has a green “connected” icon next to its name to show that a signal is connected to it. This function will be called whenever the area touches something, so let’s add some code here:\nfunc _on_area_entered(area): if area.is_in_group(\"enemies\"): area.explode() queue_free() Here we’ll check if the bullet hit an enemy (more about that later), and if it did, we tell the enemy to explode and then delete the bullet.\nDo the same thing to connect the screen_exited signal of the VisibleOnScreenNotifier2D.\nfunc _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() Next steps This completes the bullet scene, so now we can go back and add shooting to the player.\n","description":"","tags":null,"title":"Bullet Scene","uri":"/godot_recipes/4.x/games/first_2d/first_2d_04/index.html"},{"content":"Problem You need a player-controlled 3D character body.\nSolution For this recipe, we’ll be using this adorable tank model:\nYou can grab this model on Itch.io: https://gtibo.itch.io/mini-tank or use any other model you’d like. We won’t be doing anything that’s tank-specific here.\nIn the case of this asset, the download includes an OBJ file, and we’ll find it more convenient if we import it as a scene:\nWe can add the model to the scene, but we’ll need a couple of additional nodes:\nFor the collision shape, we’re just going to use a BoxShape aligned and sized with the tank’s treads. CamPos is a Position3D we’ll use to place our following camera. It’s placed behind and above the tank, angled down.\nWe’ve also rotated the individual MeshInstance nodes 180 degrees around the Y axis. This is because they were modeled facing towards +Z, but -Z is the forward direction in Godot, and we don’t want our tank to look like it’s backwards.\nBefore we add a script, open the “Project Settings” and add the following inputs on the “Input Map” tab:\nInput Action Key forward W back S right D left A Now let’s add a script, starting with the required variables:\nextends CharacterBody3D @export var speed = 4.0 @export var turn_speed = 0.8 speed is the tank’s movement speed (forward and back), while rot_speed defines how fast it can turn.\nTip Declaring properties with @export makes it easy to adjust them in the Inspector.\nUsing the move_and_slide() method makes our movement code quite simple:\nfunc _physics_process(delta): velocity.y -= gravity * delta get_input(delta) move_and_slide() With this code, we add the downward acceleration of gravity to the current velocity, get the user’s input (more about that below), and call move_and_slide().\nNext we need to define get_input(), where we’ll process and apply the input actions:\nfunc get_input(delta): var vy = velocity.y velocity = Vector3.ZERO var move = Input.get_axis(\"back\", \"forward\") var turn = Input.get_axis(\"right\", \"left\") velocity += -transform.basis.z * move * speed rotate_y(turn_speed * turn * delta) velocity.y = vy Let’s examine this more closely. Player input should affect horizontal movement: forward/back along the ground, and rotation around the tank’s center. Movement in the Y direction should only be affected by gravity, which means we don’t want to set it to 0 every frame. This is why we’re using the vy variable to temporarily hold that value while we assign a new velocity vector for the horizontal movement, then add it back in at the end.\nFor the forward and back movement, we’re using transform.basis.z so that we’ll move in our body’s local forward direction.\nHere’s the tank in action. We’ve made a test scene with a StaticBody3D plane for the ground and an Camera3D using the Interpolated Camera recipe.\nWrapping up This is the basis of movement for any kind of kinematic character. From here you can add jumping, shooting, AI behavior, etc. See the related recipes for examples that build on this recipe.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/characterbody3d_examples\nRelated recipes Intro to 3D Input Actions ","description":"","tags":null,"title":"CharacterBody3D: Movement","uri":"/godot_recipes/4.x/3d/characterbody3d_examples/index.html"},{"content":"Problem You need a “homing missile” - a projectile that will seek a moving target.\nSolution For this example, we’ll use an Area2D node for the projectile. Areas are typically good choices for bullets because we need to detect when they contact something. If you also need a bullet that bounces/ricochets, one of the PhysicsBody type node might be a better choice.\nThe node setup and behavior of the missile is the same you would use for a “dumb” bullet. If you’re creating many bullet types, you can use inheritance to base all your projectiles on the same core setup.\nThe nodes we’ll use:\nArea2D: Missile Sprite2D CollisionShape2D Timer: Lifetime For the texture, you can use any image you like. Here’s an example one:\nSet up the nodes and configure the sprite’s texture and the collision shape. Make sure to rotate the Sprite2D node by 90° so that it’s pointing to the right, ensuring it matches the parent’s “forward” direction.\nAdd a script and connect the Area2D’s body_entered signal and the Timer’s timeout signal.\nHere’s the starting script:\nextends Area2D export var speed = 350 var velocity = Vector2.ZERO var acceleration = Vector2.ZERO func start(_transform): global_transform = _transform velocity = transform.x * speed func _physics_process(delta): velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta func _on_Missile_body_entered(body): queue_free() func _on_Lifetime_timeout(): queue_free() This creates a “dumb” rocket that travels in a straight line when fired. To use this projectile, instance it and call its start() method with the desired Transform2D to set its position and direction.\nSee the related recipes section below for more information.\nTo change the behavior to seek a target, we’ll use the acceleration. However, we don’t want the missile to “turn on a dime”, so we’ll add a variable to control its “steering” force. This will give the missile a turning radius that can be adjusted for different behavior. We also need a target variable so that the missile knows what to chase. We’ll set that in start() as well:\nexport var steer_force = 50.0 var target = null func start(_transform, _target): target = _target ... To change the missile’s direction to move toward the target, it needs to accelerate in that direction (acceleration is change in velocity). The missile “wants” to move straight towards the target, but its current velocity is pointing in a different direction. Using a little vector math, we can find that difference:\nThe green arrow represents the needed change in velocity (i.e. acceleration). However, if we turn instantly, that will look unnatural, so the “steering” vector’s length needs to be limited. This is the purpose of the steer_force variable.\nThis is the function to calculate that acceleration. Note that if there’s no target, there will be no steering, so the missile remains traveling in a straight line.\nfunc seek(): var steer = Vector2.ZERO if target: var desired = (target.position - position).normalized() * speed steer = (desired - velocity).normalized() * steer_force return steer Finally, the resulting steer force must be applied in _physics_process():\nfunc _physics_process(delta): acceleration += seek() velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta Here’s an example of the results, with a little extra visual flair such as particle smoke and explosions:\nHere’s the full script, including the above effects. See related recipes for details.\nextends Area2D export var speed = 350 export var steer_force = 50.0 var velocity = Vector2.ZERO var acceleration = Vector2.ZERO var target = null func start(_transform, _target): global_transform = _transform rotation += rand_range(-0.09, 0.09) velocity = transform.x * speed target = _target func seek(): var steer = Vector2.ZERO if target: var desired = (target.position - position).normalized() * speed steer = (desired - velocity).normalized() * steer_force return steer func _physics_process(delta): acceleration += seek() velocity += acceleration * delta velocity = velocity.clamped(speed) rotation = velocity.angle() position += velocity * delta func _on_Missile_body_entered(body): explode() func _on_Lifetime_timeout(): explode() func explode(): $Particles2D.emitting = false set_physics_process(false) $AnimationPlayer.play(\"explode\") await $AnimationPlayer.animation_finished queue_free() Related recipes Spritesheet animation Top-down character Transforms ","description":"","tags":null,"title":"Homing missile","uri":"/godot_recipes/4.x/ai/homing_missile/index.html"},{"content":"Problem You want to pick up and move rigid bodies with the mouse.\nSolution Working with rigid bodies can be tricky. Godot’s physics engine controls their movements, and interfering with that can often lead to unexpected results. The key is to make use of the body’s mode property. This applies equally well in 2D or 3D.\nBody setup We’ll start with our rigid body object, adding a Sprite2D and CollisionShape2D. You can also add a PhysicsMaterial if you want to set Bounce and Friction properties.\nWe’re going to use the rigid body’s freeze property to remove it from the control of the physics engine while we’re dragging it. Since we still want it to be movable, we need to set the Freeze Mode to “Kinematic”, rather than the default value of “Static”.\nPlace the body in a group called “pickable”. We’ll use this to allow for multiple instances of the pickable object in the main scene. Attach a script to the body and connect the its _input_event signal.\nextends RigidBody2D signal clicked var held = false func _on_input_event(viewport, event, shape_idx): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: print(\"clicked\") clicked.emit(self) We’ll emit a signal when a mouse click is detected, including a reference to the body. Since there can be many bodies, we’ll let the main scene manage whether a body can be dragged or if there’s already one in the held state.\nIf the body is being dragged, we update its position to follow the mouse.\nfunc _physics_process(delta): if held: global_transform.origin = get_global_mouse_position() Finally, these are the two functions to call when the body is picked up and dropped. Changing the freeze to true removes the body from physics engine processing. Note that other objects can still collide with it. If you don’t want that, you can disable the collision_layer and/or collision_mask here as well. Just remember to re-enable them when dropping.\nfunc pickup(): if held: return freeze = true held = true func drop(impulse=Vector2.ZERO): if held: freeze = false apply_central_impulse(impulse) held = false In the drop function, after we change freeze back to `false, the body will return to the physics engine’s control. By passing in an optional impulse value, we can add the ability to “throw” the object on release.\nMain scene Create a main scene with some static body obstacles or a TileMap and instance a few copies of the pickable body.\nHere’s the script for the main scene. We start by connecting the clicked signal on any pickable bodies that are in the scene.\nextends Node2D var held_object = null func _ready(): for node in get_tree().get_nodes_in_group(\"pickable\"): node.clicked.connect(_on_pickable_clicked) Next, we have the function we connect the signal to. The connected function sets held_object so that we know something is currently being dragged, and calls the body’s pickup() method.\nfunc _on_pickable_clicked(object): if !held_object: object.pickup() held_object = object Lastly, when the mouse is released during dragging, we can perform the reverse actions.\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if held_object and !event.pressed: held_object.drop(Input.get_last_mouse_velocity()) held_object = null Note the use of get_last_mouse_velocity() to pass the impulse to the object - be careful with this! You may find yourself launching the rigid bodies at high speeds, especially if the bodies have low mass values. It’s probably a good idea to scale this to a reasonable value and clamp() it to some maximum. Experiment to find out what works for you.\nDownload This Project Download the project code here: https://github.com/godotrecipes/rigidbody_drag_drop\nRelated recipes ","description":"","tags":null,"title":"RigidBody2D: Drag and Drop","uri":"/godot_recipes/4.x/physics/rigidbody_drag_drop/index.html"},{"content":"Problem You need to save and load local data between game sessions.\nSolution Godot’s file I/O (input/output) system is based around the FileAccess object. You open a file by calling open().\nvar file = FileAccess.open(\"user://myfile.name\", File.READ) Warning User data should only be stored in the user:// path. While res:// can be used when running from the editor, when your project is exported, the res:// path becomes read-only.\nThe second argument after the file path is the “Mode Flag”, which can be one of the following:\nFileAccess.READ - Open for reading. FileAccess.WRITE - Open for writing. Creates the file if it doesn’t exist and truncates if it does. FileAccess.READ_WRITE - Open for reading and writing. Doesn’t truncate the file. FileAccess.WRITE_READ - Open for reading/writing. Creates the file if it doesn’t exist and truncates if it does. Storing data You can save data using its specific data type (store_float(), store_string(), etc.), or using the generic store_var(), which will use Godot’s built-in serialization to encode your data, including complex data like objects (more on this later).\nLet’s start with a small example: saving the player’s high score. We can write a function that we can call whenever the score needs to be saved:\nvar save_path = \"user://score.save\" func save_score(): var file = FileAccess.open(save_path, FileAccess.WRITE) file.store_var(highscore) We’re saving our score, but we need to be able to load it when the game starts:\nfunc load_score(): if FileAccess.file_exists(save_path): print(\"file found\") var file = FileAccess.open(save_path, FileAccess.READ) highscore = file.get_var() else: print(\"file not found\") highscore = 0 Don’t forget to check for the file’s existence before attempting to read from it - it may not be there! If that’s the case, you can use a default value.\nYou can store_var() and get_var() as many times as you need for any number of values.\nSaving Resources The above technique works great when all you need to save are a few values. For more complex situations, you can save your data in a Resource, just like Godot does. Godot saves all its data Resources as .tres files (Animations, TileSets, Shaders, etc.) and you can too!\nTo save and load Resources, use the ResourceSaver and ResourceLoader Godot classes.\nFor this example, let’s say you have all the data about your character’s stats stored in a Resource like this:\nextends Resource class_name PlayerData var level = 1 var experience = 100 var strength = 5 var intelligence = 3 var charisma = 2 You can then save and load like so:\nfunc load_character_data(): if ResourceLoader.exists(save_path): return load(save_path) return null func save_character_data(data): ResourceSaver.save(data, save_path) Resources can contain subresources, so you could have your player’s inventory Resource included as well, and so on.\nWhat about JSON? I see it very often (and some readers may be asking it already): “What if I want to use JSON to save my data?” This is my response:\nDon’t use JSON for your save files!\nWhile Godot has JSON support, saving game data is not what JSON is for. JSON is a data interchange format - its purpose is to allow systems using different data formats and/or languages to exchange data between each other.\nThis means JSON has limitations that are negatives for you when it comes to saving your game data. JSON doesn’t support many data types (no int vs. float, for example) so you have to do a lot of converting and validating to try and save/load your data. It’s cumbersome and time consuming.\nDon’t waste your time. Using Godot’s built-in serialization, you can store native Godot objects - Nodes, Resources, even Scenes - without any effort, which means less code and fewer errors.\nThere’s a reason that Godot itself doesn’t use JSON for saving scenes and resources.\nWrapping up This article just scratches the surface of what you can do with FileAccess. For the full list of available FileAccess methods, see the FileAccess documentation.\n","description":"","tags":null,"title":"Saving/loading data","uri":"/godot_recipes/4.x/basics/file_io/index.html"},{"content":"Problem Many 2D games use a “3/4 view” perspective, giving the impression that the camera is looking at the world at an angle. To make this work, objects that are “farther” away need to be rendered behind “nearer” objects. In practice, that means we want to “y-sort” - making the drawing order tied to the object’s y coordinate. The higher on the screen, the farther away and therefore lower the render order.\nHere’s an example of the problem:\nThese objects are being drawn in the default render order: tree order. They are arranged like this in the scene tree:\nSolution Godot has a built-in option to change the render order: on any CanvasItem node (Node2D or Control), we can enable the Y Sort Enabled property. When this is enabled, all child nodes are then y-sorted.\nIn the above example, we can enable the property on the TileMap node. However, there’s still a problem:\nThe draw order is based on each object’s y coordinate. By default, that is the object’s center:\nSince we want to give the impression that the objects are on the “ground”, we can solve this by offsetting each object’s sprite so that the object’s position is aligned with the bottom of the sprite:\nNow things look a lot better:\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/using_ysort\n","description":"","tags":null,"title":"Using Y-Sort","uri":"/godot_recipes/4.x/2d/using_ysort/index.html"},{"content":"Problem You want a floating “healthbar” for your 3D game objects (mobs, characters, etc.).\nSolution For this solution, we’re going to re-use a 2D healthbar based on a TextureProgressBar node. It’s already set up with textures and code for updating the value and color. If you already have something similar, feel free to use it here. In the example, we’ll name this scene “Healthbar2D”.\nIf you need some assets, here are the three images used in the bar:\nNote Re-using existing objects can save you a lot of time. Don’t re-invent the wheel everytime you need a healthbar, camera, or other common object.\nProject setup For our example “mob”, we’ll start with a CharacterBody3D node. It’s programmed to spawn and travel in a straight line. It also has the following code to handle damage:\nfunc _on_input_event(camera, event, position, normal, shape_idx): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: health -= 1 if health \u003c= 0: queue_free() Clicking on a unit deals one damage. Do ten damage, and the unit is destroyed. Now we need a visual representation of that using our 2D bar.\n2D in 3D We can display a 2D image in 3D using a Sprite3D. Add one to a new scene and name it “Healthbar3D”. First, we’ll get it configured and sized, so set the Texture property to the green bar image.\nThe Sprite3D acts like any other 3D object - as we pan the camera around, our perspective on it changes. However, we want the healthbar to always “face” toward the camera so that we can see it.\nIn the Inspector, under Flags, set Billboard to “Enabled”.\nNow try moving the camera to confirm that the texture is always facing you.\nAdd an instance of this scene to the Mob scene and position the bar above the mob’s body.\nViewport texture We don’t want the Sprite3D to show a static texture - we want it to display the 2D TextureProgressBar. We can do that using a SubViewport node, which can export a texture.\nAdd a SubViewport as a child of the Sprite3D. In the Inspector set Transparent BG to On.\nWe also need to set the size of the viewport to match the size of the healthbar texture, which is (200, 26).\nInstance the HealthBar2D as a child of the Viewport. Your scene should look like this:\nIf the SubViewport were not a child of the Sprite3D, we could set it as the sprite’s texture directly in the Inspector. Since it’s a child, it won’t be ready at the right time, so we’ll need to set it in a script attached to the Sprite3D:\nextends Sprite3D func _ready(): texture = $SubViewport.get_texture() Connecting it all together In the mob’s _on_input_event() method, add the following after reducing the health:\n$HealthBar3D.update(health, max_health) Add the following to HealthBar3D.gd:\nfunc update_health(_value, max_value): $SubViewport/HealthBar2D.update_health(_value) This calls the update method that already exists on the 2D bar, setting the progress bar’s value and selecting the bar color:\nfunc update_health(_value, max_value): value = _value if value \u003c max_value: show() texture_progress = bar_green if value \u003c 0.75 * max_value: texture_progress = bar_yellow if value \u003c 0.45 * max_value: texture_progress = bar_red Click on the mobs to see the health bars change.\nWrapping up You can use this technique to display any other Node2D or Control nodes, such as Label, VideoStreamPlayer, etc. You can even use the SubViewport to “project” an entire 2D game in 3D space.\nDownload This Project Download the project code here: https://github.com/godotrecipes/3d_object_healthbars\n","description":"","tags":null,"title":"3D Unit Healthbars","uri":"/godot_recipes/4.x/3d/healthbars/index.html"},{"content":" Animation Using Godot’s animation system.\nIn this section: Spritesheet animation ","description":"","tags":null,"title":"Animation","uri":"/godot_recipes/4.x/animation/index.html"},{"content":"Problem You want your character body to interact with rigid bodies.\nSolution Note This recipe applies equally well in both 2D and 3D nodes.\nBy default, a CharacterBody2D moved with move_and_slide() or move_and_collide() will not push any RigidBody2D it collides with. The rigid body doesn’t react at all, and behaves just like a StaticBody2D.\nIn some cases, this might be all you need. However, if you want to be able to push the bodies, you’ll need to make some changes.\nFor this example, we’ll use the 2D character described in the Platform character recipe. This example uses the most common movement method for character bodies: move_and_slide(). If you’re using move_and_collide(), you’ll need to adjust the examples below accordingly.\nYou have two options when deciding how to interact with rigid bodies:\nYou can just push them, ignoring physics. If you’re familiar with Godot 3.x, this is equivalent to the “infinite inertia” option. You can give them a push based on the character’s imagined “mass” and velocity. This will give you a “realistic” result - pushing heavy bodies a little, and lighter bodies a lot. We’ll try out both options below.\nInfinite Inertia This option has its pros and cons. The biggest pro is, you don’t need any extra code. You just need to correctly set the collision layers/masks of the objects. For this example, we’ve defined three physics layers:\nFor the rigid body, we’ve placed it on the “items” layer (layer 3), and left the mask at the default (masking all layers):\nThen, we’ve placed the player on the “player” layer (layer 2), and configured the mask to ignore the “items”:\nRunning the game, we now see we can push the boxes around. Note that the mass of the box doesn’t matter - they’ll all be pushed the same.\nHere, you can also see the downside of this option. Because the physics of the boxes is being ignored, they can clip through walls and you can’t jump on top of them.\nFor some games, this will be fine. If you want to prevent the clipping, you’ll need to go with option 2.\nApplying impulses To give the colliding body a “push” we’ll need to apply an impulse. An impulse is an instantaneous “kick” - think of a bat hitting a ball. This is as opposed to a force, which is a continuous “push” on an object.\n# This represents the player's inertia. var push_force = 80.0 func _physics_process(delta): # after calling move_and_slide() for i in get_slide_collision_count(): var c = get_slide_collision(i) if c.get_collider() is RigidBody2D: c.get_collider().apply_central_impulse(-c.get_normal() * push_force) The collision normal points out of the rigid body, so we reverse it to point away from the character and apply the push_force factor. Now pushing works again, but it won’t force the rigid bodies through walls:\nYou’ll need to adjust the push_force in relation to the mass of your rigid bodies. Too high a force will still cause clipping, while too low will prevent pushing at all.\nExperiment to find the settings that work for your particular game.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/character_vs_rigid\nRelated recipes Platform character Watch Video ","description":"","tags":null,"title":"Character to Rigid Body Interaction","uri":"/godot_recipes/4.x/physics/character_vs_rigid/index.html"},{"content":" Input Handling input - from keyboard and mouse to game controllers and touchscreens.\nIn this section: Input Actions Mouse Input Adding Input Actions in code Capturing the Mouse Mouse: Drag-select multiple units ","description":"","tags":null,"title":"Input","uri":"/godot_recipes/4.x/input/index.html"},{"content":" Intro to 3D A gentle introduction to the 3D side of Godot development.\nIn this section: The 3D Editor Importing 3D Objects Creating a 3D Character ","description":"","tags":null,"title":"Intro to 3D","uri":"/godot_recipes/4.x/g101/3d/index.html"},{"content":"This is an evolving list of the main changes and “gotchas” to look out for if you’re transitioning to 4.0.\nNew Names One of the biggest changes in Godot 4 is a whole bunch of renaming - of nodes, functions, and property names. Most of it is done to make things consistent or clear. Here are a few of the biggest ones to watch out for:\n2D/3D nodes - In Godot 3.x, 2D nodes had the “2D” suffix, but 3D nodes had none. This has been made consistent - they all now have “2D” or “3D” suffixes. For example: RigidBody2D vs. RigidBody3D.\nAlso in the category of 3D, the Spatial node is renamed to Node3D to match.\nOne of the most popular nodes, KinematicBody, has been renamed to CharacterBody2D/CharacterBody3D. See below for further changes with this node’s API.\nPackedScene’s instance() function has been renamed to instantiate().\nThe position and global_position properties replace translation and global_translation in 3D, making them consistent with 2D.\nSignals and Callables Working with signals is much more streamlined in 4.0. Signal is a native type now, so you’ll be using fewer strings, meaning you get autocomplete and error checking. This applies to functions as well, which can now be directly referenced rather than using strings.\nHere’s an example of defining, connecting, and emitting a signal.\nextends Node signal my_signal func _ready(): my_signal.connect(signal_handler) func _input(event): if event.is_action_pressed(\"ui_select\"): my_signal.emit() func signal_handler(): print(\"signal received\") Tweens If you started using SceneTreeTween in Godot 3.5, then you’ll be familiar with Godot 4.0’s Tween usage.\nTween is no longer a node. Instead, you create one-off tween animation objects whenever you need them. Once you get used to it, it’s a lot more powerful and easier to use than the old method.\nAnimatedSprite[2D|3D] The biggest change that catches people who are familiar with the 3.x version of this node is that the playing property is gone. It’s now much more consistent with AnimationPlayer’s usage - to automatically play an animation, you can toggle autoplay in the SpriteFrames panel. In code, use play() and stop() to control playback.\nCharacterBody[2D|3D] The biggest change in this node is in using move_and_slide(). It no longer takes any parameters - they are all now built-in properties. This includes a native velocity property, so you no longer need to declare your own.\nFor detailed examples of using these nodes, see Platform Character and/or Basic FPS Character.\nTileMap The TileMap node is completely overhauled for 4.0. Just about everything, from how you create TileSets to how you draw and interact with tiles is 100% new.\nOur “Using TileMaps” guide is coming soon.\nRNG There are a few changes to GDScript’s built-in random number generator functions:\nYou no longer need to call randomize() - this is automatic. If you do want repeatable “randomness”, use seed() to set it to a preselected value.\nrand_range() is now replaced with either randf_range() (for floats) or randi_range() (for ints).\nRaycasting When casting rays in code, there’s a new API. PhysicsDirectSpaceState[2D|3D].intersect_ray() now takes a special object as a parameter. This object specifies the ray properties. For example, to cast a ray in 3D:\nvar space = get_world_3d().direct_space_state var ray = PhysicsRayQueryParameters3D.create(position, destination) var collision = space.intersect_ray(ray) if collision: print(\"ray collided\") ","description":"","tags":null,"title":"Migrating from 3.x","uri":"/godot_recipes/4.x/basics/migrating/index.html"},{"content":"Problem You need moving platforms in your 2D platformer.\nSolution There are several ways to approach this problem. In this recipe, we’ll use AnimatableBody2Ds for our platform and move it with a Tween. This allows for a variety of movement styles while minimizing the amount of code we need to write.\nInfo You can also implement this moving platform technique using an AnimationPlayer rather than a tween. Much of the setup will be the same, but rather than tween code, you’ll animate the body’s position property.\nSetting up We’ll start with a basic platformer setup using the Platform character recipe. The basic movement from that recipe will work fine with the platforms. If you’ve modified it or used your own, everything should still work the same.\nCreating the platform The platform scene contains the following nodes:\nNode2D (“MovingPlatform”): The Node2D parent is there to act as the “anchor” or start point for the platform. We’ll animate the platform’s position relative to this parent node. AnimatableBody2D: This represents the platform itself. This is the node that will move. Sprite2D: You can use a sprite sheet here, individual images, or even a TileMap. CollisionShape2D: Don’t make the hitbox too big, or the player will appear to be “hovering” off the edge of the platform. Set up the Sprite2D’s Texture and the collision shape appropriately. In the AnimatableBody2D, set the Sync to Physics property “On”. Since we’re moving the body in code, this ensures that it’s moved during the physics step, keeping it in sync with the player and other physics bodies.\nNow add a script to the root Node2D:\nextends Node2D @export var offset = Vector2(0, -320) @export var duration = 5.0 func _ready(): start_tween() func start_tween(): var tween = get_tree().create_tween().set_process_mode(Tween.TWEEN_PROCESS_PHYSICS) tween.set_loops().set_parallel(false) tween.tween_property($AnimatableBody2d, \"position\", offset, duration / 2) tween.tween_property($AnimatableBody2d, \"position\", Vector2.ZERO, duration / 2) We’ve used a few of Tween’s options here to make everything work smoothly:\nset_process_mode(): ensures that all movement takes place during the physics processing step. set_loops(): this makes the tween repeat. set_parallel(false): by default, all tween_property() changes would happen at that same time. This makes the two happen one after another: moving to one end of the offset, then back to the start. Using the two exported properties, you can adjust the platform’s movement. Set the offset to determine where the tween moves relative to its starting point, and the duration to determine how long it takes to complete the cycle.\nAdd some platforms in your level/world and try them out:\nDownload This Project Download the project code here: https://github.com/godotrecipes/2d_moving_platforms\nRelated recipes Platform character Like video? Coming soon\n","description":"","tags":null,"title":"Moving Platforms","uri":"/godot_recipes/4.x/2d/moving_platforms/index.html"},{"content":"Problem You have a grid-based environment and you’d like to set up pathfinding to allow navigation.\nSolution Godot provides a number of methods for pathfinding. For this recipe, we’ll consider the A* algorithm.\nAbout A* A* is a widely-used algorithm for finding the shortest path between two points. It can be used in any graph-based data structure, not just a grid.\nAStarGrid2D is a specialized version of Godot’s more generic AStar2D class. Because it’s specialized for using with a grid, it’s quicker and easier to set up because you don’t have to manually add all the individual grid cells and their connections.\nSetting up the Grid The most important configuration decision is the size of the cells and the size of the grid itself. We’ll use (64, 64) for this example, and we’ll use the window size to determine how many cells fit on the screen, but everything will work the same regardless of cell size.\nAdd this code to a Node2D.\nextends Node2D @export var cell_size = Vector2i(64, 64) var astar_grid = AStarGrid2D.new() var grid_size func _ready(): initialize_grid() func initialize_grid(): grid_size = Vector2i(get_viewport_rect().size) / cell_size astar_grid.size = grid_size astar_grid.cell_size = cell_size astar_grid.offset = cell_size / 2 astar_grid.update() In this code, we divide the size of the screen by the cell_size to calculate how big the whole grid will be. This lets us set the size property of the AStarGrid2D.\nThe offset property will come into play when we ask for a path between two points. Using cell_size / 2 means the path will be calculated from the center of each cell rather than the corners.\nFinally, we need to call update() after setting or changing any of the AStarGrid2D’s properties.\nDrawing the Grid For the purposes of this demo, we’ll draw the grid on the screen in code. In a game application, you’ll probably have a TileMap or some other visual representation of your world.\nHere’s some code to draw the grid:\nfunc _draw(): draw_grid() func draw_grid(): for x in grid_size.x + 1: draw_line(Vector2(x * cell_size.x, 0), Vector2(x * cell_size.x, grid_size.y * cell_size.y), Color.DARK_GRAY, 2.0) for y in grid_size.y + 1: draw_line(Vector2(0, y * cell_size.y), Vector2(grid_size.x * cell_size.x, y * cell_size.y), Color.DARK_GRAY, 2.0) This gives us a nice visual of the grid:\nDrawing the Path In order to find a path, we need a start and end point. Add these variables at the top of the script:\nvar start = Vector2i.ZERO var end = Vector2i(5, 5) And a couple of lines in _draw() to show them:\ndraw_rect(Rect2(start * cell_size, cell_size), Color.GREEN_YELLOW) draw_rect(Rect2(end * cell_size, cell_size), Color.ORANGE_RED) We can find the path between the two points using the get_point_path() method, but we also need to visualize it. We can use a Line2D, so add one to the scene.\nHere’s how we can get the path, and add the resulting points to the Line2D:\nfunc update_path(): $Line2D.points = PackedVector2Array(astar_grid.get_point_path(start, end)) Here’s the result:\nNote that we have a diagonal line between the two points. This is because, by default, the path will use diagonals. This can be modified by changing the diagonal_mode:\nDIAGONAL_MODE_ALWAYS - The default value, uses diagonals. DIAGONAL_MODE_NEVER - All movement is orthogonal. DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE - This allows diagonals, but prevents the path going “between” diagonally placed obstacles. DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES - This allows diagonals only in “open” areas, not near obstacles. Modifying this property can give you very different results, so make sure to experiment based on your setup. Let’s add this in the initialize_grid() function:\nastar_grid.diagonal_mode = AStarGrid2D.DIAGONAL_MODE_NEVER Now we only have orthogonal moves:\nAdding Obstacles We can also add obstacles to the grid. By marking a cell as “solid”, the path will not include that cell. A cell can be toggled solid/not solid by using the set_point_solid() function.\nLet’s add some code to draw our walls (when they exist), by finding any solid cells and coloring them in:\nfunc fill_walls(): for x in grid_size.x: for y in grid_size.y: if astar_grid.is_point_solid(Vector2i(x, y)): draw_rect(Rect2(x * cell_size.x, y * cell_size.y, cell_size.x, cell_size.y), Color.DARK_GRAY) Call this function in _draw().\nThen, we can use the mouse to click on cells and toggle their state:\nfunc _input(event): if event is InputEventMouseButton: # Add/remove wall if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: var pos = Vector2i(event.position) / cell_size if astar_grid.is_in_boundsv(pos): astar_grid.set_point_solid(pos, not astar_grid.is_point_solid(pos)) update_path() queue_redraw() Note that we’re checking is_in_boundsv() first - this will prevent errors from being thrown if we click outside the grid boundaries.\nNow we can see the effect of obstacles on the path:\nChoosing a Heuristic A big factor that affects the resulting path is what heuristic you choose to use. The term “heuristic” refers to a “best guess”, and in the context of pathfinding just means: what direction should we try first when moving toward the goal?\nFor example, the Euclidean distance uses the Pythagorean theorem to estimate the path to try:\nWhile Manhattan distance only considers distance in N/S or E/W directions:\nAnd the Octile heuristic results in a path like this:\nYou can choose the heuristic using this property:\nastar_grid.default_estimate_heuristic = AStarGrid2D.HEURISTIC_OCTILE Which of these works best (results in the most pleasing paths) depends on the nature of your environment. Is it mostly wide-open spaces with few obstacles scattered around? Or is it a maze of twisty passages? Make sure to experiment with your specific project.\nDownload the example project below to experiment with this setup yourself. In addition to placing walls, you can use the right/middle mouse buttons to move the end/start locations.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/grid_pathfinding\n","description":"","tags":null,"title":"Pathfinding on a 2D Grid","uri":"/godot_recipes/4.x/2d/grid_pathfinding/index.html"},{"content":"The Bullet scene provides us with a reusable object we can instantiate whenever the player shoots.\nAdding to the player Let’s head back to the Player script and add a few new variables:\n@export var cooldown = 0.25 @export var bullet_scene : PackedScene var can_shoot = true The two @export variables let you configure them in the Inspector so that you can adjust the cooldown time. Set the bullet_scene by clicking the property and choosing the bullet.tscn file.\ncan_shoot is what programmers call a flag - a Boolean variable that controls a certain condition. In this case it determines whether the player is allowed to shoot or not. During the cooldown period, this variable will be false.\nNext, we’ll add a start() function similar to the one we made for the Bullet. This will let us set initial values for the player, as well as resetting them when the game restarts.\nfunc _ready(): start() func start(): position = Vector2(screensize.x / 2, screensize.y - 64) $GunCooldown.wait_time = cooldown This places the player at the bottom center of the screen - a good place to start. It also ensures that the cooldown timer has the correct wait time.\nThe shoot() function will be called whenever we press the “shoot” input.\nfunc shoot(): if not can_shoot: return can_shoot = false $GunCooldown.start() var b = bullet_scene.instantiate() get_tree().root.add_child(b) b.start(position + Vector2(0, -8)) The first thing this function does is check if the player is allowed to shoot. If it isn’t, return will end the function immediately.\nIf the player is allowed to shoot, then we set the flag to false, and start the cooldown timer. Then we create a new bullet and add it to the game, calling its start() function to make sure it’s placed in the correct position (just above the player’s ship).\nWe can call this function when the player is pressing the key. Add this to the end of the _process() function, after the position.clamp() line:\nif Input.is_action_pressed(\"shoot\"): shoot() We’ll also need to connect the timeout signal of GunCooldown.\nfunc _on_gun_cooldown_timeout(): can_shoot = true When the cooldown ends, we can allow shooting again.\nGo ahead and run the scene and try pressing the shoot action.\nAdding instances to the three Notice that we’ve added the new bullets as children of the SceneTree root (get_tree().root), and not to the player ship. This is important because if we made the bullets children of the ship, then they would be “attached” to it when it moves.\nNext steps Shooting’s no fun without something to shoot at. We’ll start making the enemies soon, but first we need a scene where we can bring the player, enemies, and other game objects together.\n","description":"","tags":null,"title":"Shooting","uri":"/godot_recipes/4.x/games/first_2d/first_2d_05/index.html"},{"content":" UI Building user interfaces.\nIn this section: Level Select Menu ","description":"","tags":null,"title":"UI","uri":"/godot_recipes/4.x/ui/index.html"},{"content":" Gamedev Math Math is a big part of game development. Some of it you may remember from school, or it may be something you’ve never encountered before. Here you’ll find guides to help you get up to speed and examples of how these concepts are applied to making games.\nIn this section: Interpolation Transforms Vectors: Using Dot and Cross Product ","description":"","tags":null,"title":"Gamedev Math","uri":"/godot_recipes/4.x/math/index.html"},{"content":" AI/Behavior Automated behavior and (sometimes) smarter entities.\nIn this section: Homing missile ","description":"","tags":null,"title":"AI/Behavior","uri":"/godot_recipes/4.x/ai/index.html"},{"content":"Before we can make enemies, powerups, or any other game objects, we need a place where they can all exist together with the player. In most games, this would be called a “level” or “main” scene, and that’s what we’ll call it here.\nStart the scene with a Node2D called “Main” and save it.\nCreating the background Add a Sprite2D child. Name this sprite “Background” and add the Space_BG (2 frames) (64 x 64).png as its texture.\nThis image has two frames, each 64x64 pixels in size. We’d like the image to tile across the full size of the screen, so start with the following settings:\nUnder Offset set Centered to “off”. This makes the image’s top left corner start at the origin rather than its center.\nUnder Region, turn Enabled “on”, and then set the Rect to a width of 240 and a height of 320. This makes the image stretch to the size of the screen.\nUnder Texture change Repeat to Enabled. This causes the image to repeat over the full size of the screen.\nNow add the player to the scene by selecting the Main node and clicking the Instantiate Child Scene button.\nAnimating the background We can make the scene more dynamic by animating the background. While we could do this in code by changing the region_rect property every frame, we’ll use an AnimationPlayer node instead; add one as a child of Main.\nAt the bottom of the editor window, you’ll see the Animation panel. There’s a lot of information there, so let’s look at how it’s laid out:\nClick the Animation button and choose New Animation. You can name the new animation scroll. Set its Length to 2 and toggle the Looping and Autoplay buttons.\nAnimations work by adding tracks that represent properties that you want the AnimationPlayer to control. In the timeline of the player, you’ll add keyframes that define what value you want the property to have at that particular time.\nWe can add keyframes to the animation by clicking the key icon that now appears next to every property in the Inspector. Make sure the scrubber (the blue indicator on the timeline) is at time 0, then select the Background and click the key next to Region/Rect. You’ll be asked if you want to create a new track and then you’ll see the new track added to the animation panel, with a small dot representing the keyframe you’ve just added. Drag the scrubber to time 2 and then change the y value of the Region/Rect property to 64. Click the key to add another keyframe.\nNow when you press Play on the animation, you should see the background slowly scrolling behind the player.\nNext steps The main scene is now ready for us to add enemies. In the next step we’ll make a single enemy scene, as we did with the bullets, and then instantiate that multiple times.\n","description":"","tags":null,"title":"Main Scene","uri":"/godot_recipes/4.x/games/first_2d/first_2d_06/index.html"},{"content":" Physics Learn how to use Godot’s physics nodes.\nIn this section: RigidBody2D: Look at Target RigidBody2D: Drag and Drop Character to Rigid Body Interaction Asteroids-style Physics (using RigidBody2D) ","description":"","tags":null,"title":"Physics","uri":"/godot_recipes/4.x/physics/index.html"},{"content":"Problem You want to make a rolling cube in 3D.\nSolution Rolling a cube is trickier than it seems. You can’t just rotate the cube around its center:\nInstead, the cube needs to be rotated around its bottom edge.\nHere’s the tricky part: which bottom edge? It depends on which direction the cube is rolling.\nIn preparing this recipe, I experimented with a few different solutions to this problem:\nPure math - calculating and applying rotation transforms AnimationPlayer - using animations to key the rotations and offsets Helper nodes - using Spatial(s) as rotation helpers They all worked fine, but I found the last option the most flexible and easiest to adapt, so that’s what we’ll do here.\nNode setup Cube: CharacterBody3D Pivot: Node3D Mesh: MeshInstance3D Collision: CollisionShape3D Tip You can do this with RigidBody3D, CharacterBody3D, or Area3D as your collision node. There will be minor differences in how you handle movement. Which node you choose should depend on what other behavior you want in your game. For this recipe, we’re only concerned with the movement.\nBy default, everything is centered at (0, 0, 0) so the first thing we’re going to do is offset everything so that the bottom center of the cube is the CharacterBody3D’s position.\nThe default size of a BoxMesh3D is (1, 1, 1), so do this, move the mesh and collision nodes both up to (0, 0.5, 0), leaving the rest where they are. Now when you select the root node, its position will be the bottom of the cube:\nNow when you want to roll the cube, you’ll need to move the Pivot 0.5 in the direction you want to move. Since the mesh is attached, you need to move it the opposite amount. For example, to roll to the right (+X), you’ll end up with this:\nNow the pivot node is at the correct edge and rotating it will also rotate the mesh.\nMovement script The movement is broken in to 3 steps:\nStep 1 Here we apply the two offsets shown above: shift the Pivot in the direction of movement, and shift the Mesh in the opposite direction.\nStep 2 In this step we animate the rotation. We find the axis of rotation using the cross product of the direction and the down vector. Then we use a Tween to animate rotating the pivot’s transform.\nStep 3 Finally, once the animation has finished, we need to reset everything so that it’s ready to happen again. In the end, we want to have the cube moved 1 unit in the chosen direction (for a cube of size 1) and have the pivot and mesh back at their original positions.\nextends CharacterBody3D @onready var pivot = $Pivot @onready var mesh = $Pivot/MeshInstance3D var cube_size = 1.0 var speed = 4.0 var rolling = false func _physics_process(delta): var forward = Vector3.FORWARD if Input.is_action_pressed(\"ui_up\"): roll(forward) if Input.is_action_pressed(\"ui_down\"): roll(-forward) if Input.is_action_pressed(\"ui_right\"): roll(forward.cross(Vector3.UP)) if Input.is_action_pressed(\"ui_left\"): roll(-forward.cross(Vector3.UP)) func roll(dir): # Do nothing if we're currently rolling. if rolling: return rolling = true # Step 1: Offset the pivot. pivot.translate(dir * cube_size / 2) mesh.global_translate(-dir * cube_size / 2) # Step 2: Animate the rotation. var axis = dir.cross(Vector3.DOWN) var tween = create_tween() tween.tween_property(pivot, \"transform\", pivot.transform.rotated_local(axis, PI/2), 1 / speed) await tween.finished # Step 3: Finalize the movement and reset the offset. transform.origin += dir * cube_size var b = mesh.global_transform.basis pivot.transform = Transform3D.IDENTITY mesh.position = Vector3(0, cube_size / 2, 0) mesh.global_transform.basis = b rolling = false If your cube’s texture isn’t symmetrical, you may notice that it’s resetting after every roll. To preserve the rotation of the mesh, add the following:\nIn Step 1:\nChange mesh.translate(-dir) to mesh.global_translate(-dir).\nIn Step 3:\nAdd two lines to keep the mesh rotation after reset:\n# Step 3: Finalize the movement and reset the offset. transform.origin += dir * cube_size var b = mesh.global_transform.basis # Save the mesh rotation. pivot.transform = Transform3D.IDENTITY mesh.position = Vector3(0, cube_size / 2, 0) mesh.global_transform.basis = b # Restore the mesh rotation. Checking for collisions If you plan to have obstacles in your game, you can check for collisions before moving (similar to any other grid-based movement scheme). Add a raycast check before Step 1 of the move:\n# Cast a ray before moving to check for obstacles var space = get_world_3d().direct_space_state var ray = PhysicsRayQueryParameters3D.create(mesh.global_position, mesh.global_position + dir * cube_size, collision_mask, [self]) var collision = space.intersect_ray(ray) if collision: return Note You could also use a RayCast3D node. Just remember to call force_raycast_update() before checking.\nPlaying with transitions You can add a lot of “personality” to the cube’s rolling behavior by changing which TransitionType you use. The default is Tween.TRANS_LINEAR, which results in a constant speed throughout the movement.\nBy setting a different transition type, you can get a very different feel. For example:\nvar tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_IN) Download This Project Download the project code here: https://github.com/godotrecipes/rolling_cube\nRelated recipes Transforms ","description":"","tags":null,"title":"Rolling Cube","uri":"/godot_recipes/4.x/3d/rolling_cube/index.html"},{"content":" Audio Helpful recipes for adding sound effects and music to your game.\nIn this section: Audio Manager ","description":"","tags":null,"title":"Audio","uri":"/godot_recipes/4.x/audio/index.html"},{"content":"Now that our enemy can shoot, let’s give them something to shoot at.\nSetting up the scene We’ll use an Area2D for the enemy, since we need it to detect overlap - either with the player’s bullets, or with the player itself.\nHere’s are the nodes we’ll need:\nEnemy: Area2D Sprite2D CollisionShape2D AnimationPlayer MoveTimer: Timer ShootTimer: Timer Select the area node and click the Node tab next to the Inspector. Under Groups, type “enemies” an click Add. Remember the code we wrote on the bullet? It looks for objects in the “enemies” group.\nIn the sprite’s Texture, add Bon_Bon (16 x 16).png and set its Animation/Hframes to 4.\nAs you’ve done before, add a rectangular collision shape and size it to fit. Enable One Shot on both timer nodes.\nIn the AnimationPlayer, add an animation called “bounce” and set it to looping and autoplay. Set the Snap at the bottom of the animation panel to 0.05.\nSelect the sprite node and press the key icons next to Texture and Hframes to create tracks for them. We’re doing this because later we’ll add an “explosion” animation that will use different values for these properties.\nNow we’ll key the individual Frames values we want. Start with keying Frames each .1 seconds to values in this order2, 1, 0, 3. Finally, key 0 again and put it immediately after. This will make a “pulsing” animation where the sprite grows and then bounces a little at the end. The animation setup should look like this:\nPress the play button to see it in action. Feel free to adjust it if you’d like.\nNow add another animation called “explode”. Set its length to 0.4 seconds.\nChange the sprite’s Texture to Explosion (16 x 16).png and keyframe that property. Since this image has a different number of frames than the enemy image, we also need to change Hframes to 6 and keyframe that.\nNow keyframe Frame to 0 at time 0 and to 5 at time 0.4. Play the animation to see it in action.\nEnemy script The enemies will spawn at the top of the screen in a grid. After a random amount of time, they’ll descend toward the player and then return to the top if they weren’t destroyed. Periodically, they’ll also shoot at the player.\nAdd a script, and start with the variables:\nextends Area2D var start_pos = Vector2.ZERO var speed = 0 @onready var screensize = get_viewport_rect().size The start_pos variable is going to keep track of the enemy’s starting position so that after it moves, it can return to its original location. We’ll set it when the enemy is spawned and we call its start() function.\nfunc start(pos): speed = 0 position = Vector2(pos.x, -pos.y) start_pos = pos await get_tree().create_timer(randf_range(0.25, 0.55)).timeout var tween = create_tween().set_trans(Tween.TRANS_BACK) tween.tween_property(self, \"position:y\", start_pos.y, 1.4) await tween.finished $MoveTimer.wait_time = randf_range(5, 20) $MoveTimer.start() $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() When we spawn our enemies we’ll call this function and pass it a position vector representing where on the screen the enemy should go. Note that we actually spawn it above the top of the screen (negative y value). This is so that we can animate it coming onto the screen using a tween. We also randomize the two timers so that all enemies won’t be moving and shooting at the same time.\nConnect both of the timers’ timeout signals.\nfunc _on_timer_timeout(): speed = randf_range(75, 100) func _on_shoot_timer_timeout(): $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() We can start moving when the timer runs out, and we’ll also shoot, but we haven’t made a bullet yet, so that part will come later. Now that we’re changing the speed, we can move using it.\nfunc _process(delta): position.y += speed * delta if position.y \u003e screensize.y + 32: start(start_pos) Now if the speed isn’t 0, we’ll see the enemy move down the screen. When it goes off the bottom, we start it all over again.\nWe’ve already written the code in the bullet scene that calls explode() on the enemies it hits, so let’s add that too.\nfunc explode(): speed = 0 $AnimationPlayer.play(\"explode\") set_deferred(\"monitoring\", false) died.emit(5) await $AnimationPlayer.animation_finished queue_free() In this function, we stop moving, play the explosion animation, and then delete the enemy when it’s finished. The set_deferred() call makes sure to turn off monitoring on the enemy. This is so that while the enemy is exploding, another bullet can’t hit it again.\nAdd the died signal at the top of the script:\nsignal died We’ll use that signal to let the main scene know that the player just earned some points.\nSpawning enemies Now let’s go to the Main scene and add these enemies to the game. Add a script to Main and start by loading the enemy scene:\nextends Node2D var enemy = preload(\"res://enemy.tscn\") var score = 0 Spawning enemies ordinarily won’t happen until we’ve pressed the “Start” button to begin the game, but since we haven’t made that yet, we’ll just spawn them immediately:\nfunc _ready(): spawn_enemies() func spawn_enemies(): for x in range(9): for y in range(3): var e = enemy.instantiate() var pos = Vector2(x * (16 + 8) + 24, 16 * 4 + y * 16) add_child(e) e.start(pos) e.died.connect(_on_enemy_died) This makes 27 enemies and positions them in a grid in the top half of the screen. We also make sure to connect the died signal of each, so we need to create that function:\nfunc _on_enemy_died(value): score += value We don’t have a way to display the score yet, but we’ll get to that soon.\nPlay the scene and you should see a bunch of enemies appear at the top and periodically fall down the screen. Next, we’ll make them shoot.\n","description":"","tags":null,"title":"Enemies","uri":"/godot_recipes/4.x/games/first_2d/first_2d_07/index.html"},{"content":"Problem You want to click-and-drag to select multiple units, RTS style.\nSolution Realtime strategy (RTS) games often require giving orders to many units at once. A typical style of selecting multiple units is to click-and-drag a box around them. Once the units are selected, clicking on the map commands them to move.\nHere’s an example of what we’re going for:\nUnit setup To test this out, we’ll need some basic RTS-style units. They are set up to move towards a target and to avoid running into each other. We won’t go into too much detail on them in this tutorial. The unit script is commented if you’d like to use it as a base for creating your own RTS units. See below for a link to download the project.\nWorld setup Processing the unit selection will happen in the world. We’ll start with a Node2D called “World” and add a few Unit instances in it. Attach a script to the World node and add the following variables:\nextends Node2D var dragging = false # Are we currently dragging? var selected = [] # Array of selected units. var drag_start = Vector2.ZERO # Location where drag began. var select_rect = RectangleShape2D.new() # Collision shape for drag box. Note that once we’ve drawn the box, we’ll need a way to find what units are inside it. The RectangleShape2D will allow us to query the physics engine and see what we collided with.\nDrawing the box We’ll be using the left mouse button for this. Clicking starts a drag and then letting go ends it. During dragging, we’ll draw the rectangle for visibility.\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: # If the mouse was clicked and nothing is selected, start dragging if selected.size() == 0: dragging = true drag_start = event.position # If the mouse is released and is dragging, stop dragging elif dragging: dragging = false queue_redraw() if event is InputEventMouseMotion and dragging: queue_redraw() func _draw(): if dragging: draw_rect(Rect2(drag_start, get_global_mouse_position() - drag_start), Color.YELLOW, false, 2.0) Selecting the units Now that we’ve got a selection box, we need to find the units that are inside it. When we release the button and the drag ends, we must query the physics space to find the units. Note that the units are CharacterBody2D, but Area2D or other bodies would work as well.\nWe’ll use PhysicsDirectSpaceState2D.intersect_shape() to find the units. This requires a shape (our rectangle) and a transform (our location). See Godot docs for details.\nelif dragging: dragging = false queue_redraw() var drag_end = event.position select_rect.extents = abs(drag_end - drag_start) / 2 We start by recording the location when we released the button, and use that to set the RectangleShape2D’s extents (remember: extents are measured from the rectangle’s center, so they’re half the full width/height).\nvar space = get_world_2d().direct_space_state var query = PhysicsShapeQueryParameters2D.new() q.shape = select_rect q.collision_mask = 2 # Units are on collision layer 2 q.transform = Transform2D(0, (drag_end + drag_start) / 2) selected = space.intersect_shape(query) Now we get a reference to the physics state and set up our shape query using PhysicsShapeQueryParameters2D, assigning it our shape, and using the center of the dragged area as the origin for the query’s transform. Our result after calling intersect_shape() is an array of dictionaries, which looks like this:\n[{ \"rid\": RID(4093103833089), \"collider_id\": 32145147326, \"collider\": Unit2:\u003cCharacterBody2D#32145147326\u003e, \"shape\": 0 }, { \"rid\": RID(4123168604162), \"collider_id\": 32229033411, \"collider\": Unit3:\u003cCharacterBody2D#32229033411\u003e, \"shape\": 0 }] Each of those collider items is a reference to a unit, so we can use this to notify them that they’ve been selected, activating the outline shader:\nfor item in selected: item.collider.selected = true Commanding the units Finally, we can command the selected units to move by clicking somewhere on the screen:\nfunc _unhandled_input(event): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT: if event.pressed: # If the mouse was clicked and nothing is selected, start dragging if selected.size() == 0: dragging = true drag_start = event.position # Otherwise a click tells the selected units to move else: for item in selected: item.collider.target = event.position item.collider.selected = false selected = [] The else clause here triggers if we click the mouse when selected is greater than 0. Each item’s target is set, and we make sure to deselect the units so we can start again.\nWrapping up This technique can be expanded to a wide range of RTS or other game styles. Download the full project below and use it as a base for your own game.\nDownload This Project Download the project code here: https://github.com/godotrecipes/multi_unit_select\nRelated recipes Mouse Input ","description":"","tags":null,"title":"Mouse: Drag-select multiple units","uri":"/godot_recipes/4.x/input/multi_unit_select/index.html"},{"content":"Now that our enemy can shoot, let’s give them something to shoot at.\nEnemy bullet scene Make a new EnemyBullet scene just like you made the player bullet earlier. We won’t go into all the steps here, but you can refer back to that part if you’re stuck. The only difference here is that you can use the Enemy_projectile (16 x 16).png image instead.\nThe script will be a little bit different:\nextends Area2D @export var speed = 150 func start(pos): position = pos func _process(delta): position.y += speed * delta Connect the screen_exited and area_entered signals of the VisibleOnScreenNotifier2D and Area2D, respectively:\nfunc _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() func _on_area_entered(area): if area.name == \"Ship\": queue_free() Notice that we’re detecting the hit on the player, but it’s not doing anything yet. We’ll come back to that once we add a way for the player to take damage.\nAdding shooting to the enemy At the top of the enemy’s script, load the new bullet:\nvar bullet_scene = preload(\"res://enemy_bullet.tscn\") Then update the shooting function:\nfunc _on_shoot_timer_timeout(): var b = bullet_scene.instantiate() get_tree().root.add_child(b) b.start(position) $ShootTimer.wait_time = randf_range(4, 20) $ShootTimer.start() Play the Main scene again and you should have some random enemy bullets appearing.\n","description":"","tags":null,"title":"Enemy Shooting","uri":"/godot_recipes/4.x/games/first_2d/first_2d_08/index.html"},{"content":"Problem You want to make a 3D spaceship that flies in an arcade/cinematic way. You’re not looking for realistic physics, but more of a dog-fighting, “Star Wars”-style of spaceflight.\nSolution To accomplish this, we’ll use a CharacterBody3D for the ship. The three axis inputs (pitch, roll, and yaw) will rotate the body’s basis around the corresponding axis. The direction of motion will always point forward.\nNote You can do this with RigidBody3D and get the same results. See the example project linked below, which includes a rigid body version as well.\nAssets Spaceship models are from this asset pack:\nUltimate Spaceships Pack by Quaternius\nI’ve chosen the “Executioner” ship model:\nFeel free to choose your favorite design.\nSetup Select the gltf file of the ship you want, and click the Import tab. Change the Root Type to CharacterBody3D and click “Reimport”. Then double-click the gltf and you’ll have a new inherited scene with a CharacterBody3D root and a MeshInstance child. Add a CollisionShape3D to the body.\nIn Project Settings -\u003e Input Map, set up the following inputs:\nroll_right / roll_left pitch_up / pitch_down yaw_right / yaw_left throttle_up / throttle_down You can assign keys or controller inputs. Analog stick inputs will work best.\nMovement To start the script, let’s handle the forward movement. Pressing the throttle buttons smoothly increases/decreases the speed.\nextends CharacterBody @export var max_speed = 50.0 @export var acceleration = 0.6 var forward_speed = 0 func get_input(delta): if Input.is_action_pressed(\"throttle_up\"): forward_speed = lerp(forward_speed, max_speed, acceleration * delta) if Input.is_action_pressed(\"throttle_down\"): forward_speed = lerp(forward_speed, 0, acceleration * delta) func _physics_process(delta): get_input(delta) velocity = -transform.basis.z * forward_speed move_and_collide(velocity * delta) Make a test scene with a Camera3D to try it out. You can use a stationary camera or a chase camera. Check that the ship accelerates and slows before moving on to the next step.\nRotation Now we can handle rotation in the three axes. Add the following variables at the top of the script:\n@export var pitch_speed = 1.5 @export var roll_speed = 1.9 @export var yaw_speed = 1.25 var pitch_input = 0.0 var roll_input = 0.0 var yaw_input = 0.0 The three axis speeds will affect the “handling” of the ship. Experiment to find values the work for you and your desired flight style.\nNext, add these lines to get_input() to capture the three axis inputs:\npitch_input = Input.get_axis(\"pitch_down\", \"pitch_up\") roll_input = Input.get_axis(\"roll_right\", \"roll_left\") yaw_input = Input.get_axis(\"yaw_right\", \"yaw_left\") Finally, we need to rotate the ship’s Basis according to the inputs. Note how each input affects one axis of rotation:\ntransform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta) transform.basis = transform.basis.orthonormalized() Improvements Currently the rotations are a little to “sharp”. The ship starts and stops rotating instantly, which feels a bit too unnatural. We can solve this with lerp(), and by adding one more configuration variable to set how “floaty” we’d like the controls to be:\n@export var input_response = 8.0 Change the three axis inputs in get_input() to the following:\npitch_input = lerp(pitch_input, Input.get_axis(\"pitch_down\", \"pitch_up\"), input_response * delta) roll_input = lerp(roll_input, Input.get_axis(\"roll_right\", \"roll_left\"), input_response * delta) yaw_input = lerp(yaw_input, Input.get_axis(\"yaw_right\", \"yaw_left\"), input_response * delta) Now when stopping or changing direction, there’s a little bit of inertia.\nLinking roll/yaw One problem with this control scheme is that it’s awkward. Having to use a separate stick for the yaw input makes it difficult to control, especially when also shooting and using other controls. Many games solve this by linking the roll input to also apply a small amount of yaw. To do this, change the yaw_speed to around 1/4 to 1/2 of the roll_speed.\nIn the get_input() function, change the line getting yaw_input to the following:\nyaw_input = roll_input This is another fun place to experiment by changing the roll and yaw speeds. For example, what if yaw was primary and roll smaller? What if other axes were linked? If your game has different ships, you can give them different values for variety in flight styles/performance.\nWrapping up That’s it, now you can fly! This controller is a great start for whatever space-based game you might have in mind. Add some other ships, and a few effects, and you’re ready go:\nFull script Here’s the complete script:\nextends CharacterBody3D @export var max_speed = 50.0 @export var acceleration = 0.6 @export var pitch_speed = 1.5 @export var roll_speed = 1.9 @export var yaw_speed = 1.25 # Set lower for linked roll/yaw @export var input_response = 8.0 var forward_speed = 0.0 var pitch_input = 0.0 var roll_input = 0.0 var yaw_input = 0.0 func get_input(delta): if Input.is_action_pressed(\"throttle_up\"): forward_speed = lerp(forward_speed, max_speed, acceleration * delta) if Input.is_action_pressed(\"throttle_down\"): forward_speed = lerp(forward_speed, 0.0, acceleration * delta) pitch_input = lerp(pitch_input, Input.get_axis(\"pitch_down\", \"pitch_up\"), input_response * delta) roll_input = lerp(roll_input, Input.get_axis(\"roll_right\", \"roll_left\"), input_response * delta) # yaw_input = lerp(yaw_input, Input.get_axis(\"yaw_right\", \"yaw_left\"), input_response * delta) yaw_input = roll_input func _physics_process(delta): get_input(delta) transform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta) transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta) transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta) transform.basis = transform.basis.orthonormalized() velocity = -transform.basis.z * forward_speed move_and_collide(velocity * delta) Related recipes Interpolated Camera Download This Project Download the project code here: https://github.com/godotrecipes/3d_spaceship\n","description":"","tags":[],"title":"Arcade-style Spaceship","uri":"/godot_recipes/4.x/3d/spaceship/index.html"},{"content":"The last main piece of our game is the user interface (UI). We need a way to show the player the score and other information. To do this, we’ll use a variety of Control nodes - the nodes Godot provides for building UIs.\nUI scene Start the scene with a MarginContainer and name it UI.\nContainers are Control nodes that are designed to control the size and position of their children. Using them makes it easier to position and move Control nodes without having to do it manually. The MarginContainer makes sure its children don’t get too close to the edge.\nIn the Inspector under Theme Overrides/Constants set all four Margin values to 10. Then, in the menu bar at the top of the viewport, set the anchors to the Top Wide preset.\nNext, we’ll add an HBoxContainer. This type of container organizes its children horizontally. Add a TextureProgressBar, which will represent our ship’s shield level. Name it ShieldBar.\nUnfortunately, there’s not a good image in the art pack to use for a progress bar (there is one, but it isn’t formatted in an easy way to work with). Instead, we’ll use the two images below. One is a green bar and the other is a white outline. Save them in your project folder.\nIn the Texture section, drag the foreground image to the Progress and the background image to the Under texture. The first thing you’ll notice is that it’s very small. Let’s first under Layout set Custom Minimum Size to (80, 16). You’ll notice that the orange selection rectangle got bigger, but the image didn’t. Well, we don’t want the image to just stretch, or it would look bad. Instead we’ll check the Nine Patch Stretch box, and then set the four Stretch Margin values to 3.\nYou should now see a long, unfilled bar. To see what it looks like when filled, change the Value property in the Range section to anything between 0 and 100.\nOn the right side, we’d like to show the score. Now, we could just use a Label node and add a font, but that’s not very fun. The art pack includes a lovely pixel set of digits that we could use instead. We’ll just need to do a little coding to chop it up and show the corect digit(s).\nScore counter Start a new scene and add an HBoxContainer. Name it ScoreCounter then set it to Top Wide and set the Alignment to “End”. Also, set the Theme Overrides/Constants/Separation to 0 (you need to check the box next to the property).\nIn this container, we’ll have a string of TextureRect nodes showing each digit. We’ll start by adding one and then duplicating it.\nName the TextureRect Digit0. Under Texture, select “New AtlasTexture”, then click the box to open it. Drag Number_font (8 x 8).png into the Atlas property, then set the Region to (32, 8, 8, 8). Set Stretch Mode to “Keep Aspect Centered”.\nSelect the Digit0 node and press Ctrl-D 7 times to create duplicates of the node. The picture below shows what you should see after this step:\nNow we’ll add a script to ScoreCounter that will choose the correct Region values for whichever digit it needs to display.\nextends HBoxContainer var digit_coords = { 1: Vector2(0, 0), 2: Vector2(8, 0), 3: Vector2(16, 0), 4: Vector2(24, 0), 5: Vector2(32, 0), 6: Vector2(0, 8), 7: Vector2(8, 8), 8: Vector2(16, 8), 9: Vector2(24, 8), 0: Vector2(32, 8) } func display_digits(n): var s = \"%08d\" % n for i in 8: get_child(i).texture.region = Rect2(digit_coords[int(s[i])], Vector2(8, 8)) We start by making a list of the coordinates in the image where each digit is found. Then, display_digits() will format the number to an 8 digit number (for example, 258 would become \"00000258\"). Then, for each digit, we can apply the correct coordinates from the array.\nScripting the UI Go back to the UI scene and add the ScoreCounter to the HBoxContainer, then add a script to UI.\nextends MarginContainer @onready var shield_bar = $HBoxContainer/ShieldBar @onready var score_counter = $HBoxContainer/ScoreCounter func update_score(value): score_counter.display_digits(value) func update_shield(max_value, value): shield_bar.max_value = max_value shield_bar.value = value We’ll call these functions from Main whenever we need to update the score or the shield.\nAdding the UI to main Now in the Main scene add a CanvasLayer node, and instance the UI as its child. The CanvasLayer node creates another drawing layer, so our UI will be drawn on top of the rest of the game.\nChange this function in main.gd:\nfunc _on_enemy_died(value): score += value $CanvasLayer/UI.update_score(score) Run the game and see that your score goes up when shooting enemies.\nPlayer shield We can also add the shield to the player’s script. Add these new lines at the top of ship.gd:\nsignal died signal shield_changed @export var max_shield = 10 var shield = max_shield: set = set_shield This set = syntax tells Godot that we want to call the set_shield() function whenever the shield variable has its value set.\nfunc set_shield(value): shield = min(max_shield, value) shield_changed.emit(max_shield, shield) if shield \u003c= 0: hide() died.emit() We can also connect the ship’s area_entered signal so that we can detect when an enemy hits the ship:\nfunc _on_area_entered(area): if area.is_in_group(\"enemies\"): area.explode() shield -= max_shield / 2 And in the enemy bullet, add some damage to the shield when it hits:\nfunc _on_area_entered(area): if area.name == \"Ship\": queue_free() area.shield -= 1 Run the game again and check that your shield depletes when you get hit by a bullet or an enemy.\nNext steps We’re almost done with the basic functionality. We just need a way to start and end the game.\n","description":"","tags":null,"title":"UI and Score","uri":"/godot_recipes/4.x/games/first_2d/first_2d_09/index.html"},{"content":"Our last step is to add a start button and a “game over” state to the game.\nStarting the game Currently when we run the game, it starts immediately. Let’s add a button to start it.\nIn Main as a child of the CanvasLayer, add a CenterContainer and set its layout to Full Rect. Then add a TextureButton child. Name this button Start and add the START (48 x 8).png image as its Normal texture.\nAdd a reference at the top of the script:\n@onready var start_button = $CanvasLayer/CenterContainer/Start Connect this button’s pressed texture to Main and add this code:\nfunc _on_start_pressed(): start_button.hide() new_game() The new_game() function handles starting the game, so change _ready() so that it no longer spawns enemies, but just ensures the button is showing:\nfunc _ready(): start_button.show() #\tspawn_enemies() Now the button should show when you run the scene, and pressing it starts the game.\nEnding the game Add a TextureRect as a child of the CenterContainer and name the node GameOver. Use the GAME_OVER (72 x 8).png image. It will overlap with the start button, but that’s ok, we’re only ever going to show one at a time.\nAdd another reference at the top of the script:\n@onready var game_over = $CanvasLayer/CenterContainer/GameOver And add game_over.hide() to _ready().\nConnect the ship’s died signal in Main.\nfunc _on_ship_died(): get_tree().call_group(\"enemies\", \"queue_free\") game_over.show() await get_tree().create_timer(2).timeout game_over.hide() start_button.show() This will show the “game over” image for 2 seconds, then switch back to the start button so you can play again. Try it out and see if you can play a few games.\n","description":"","tags":null,"title":"Starting and Ending the Game","uri":"/godot_recipes/4.x/games/first_2d/first_2d_10/index.html"},{"content":"Problem You need a dynamic camera that moves and zooms to keep multiple objects on screen at the same time.\nAn example might be in a 2 player game, keeping both players on-screen as they move farther and closer together, like so:\nSolution In a single-player game, you’re probably used to attaching the camera to the player, so that it automatically follows them. We can’t really do this here because we have 2 (or more) players or other game objects that we want to keep on the screen at all times.\nWe need our camera to do 3 things:\nAdd/remove any number of targets. Keep the camera’s position centered at the midpoint of the targets. Adjust the camera’s zoom to keep all targets on screen. Create a new scene with a Camera2D and attach a script. We’ll add this camera to our game once we’re done.\nLet’s break down how the script works.\nNote You can see the full script at the end of the article.\nHere’s how the script starts:\nextends Camera2D @export var move_speed = 30 # camera position lerp speed @export var zoom_speed = 3.0 # camera zoom lerp speed @export var min_zoom = 5.0 # camera won't zoom closer than this @export var max_zoom = 0.5 # camera won't zoom farther than this @export var margin = Vector2(400, 200) # include some buffer area around targets var targets = [] # Array of targets to be tracked. @onready var screen_size = get_viewport_rect().size These settings will let you adjust the camera’s behavior. We’ll lerp() all camera changes, so setting the move/zoom speeds to lower values will introduce some delay in the camera “catching up” to sudden changes.\nMaximum and minimum zoom values will also depend on the size of objects in your game and how close or far you want to get. Adjust to suit.\nThe margin property is going to add some extra space around the targets so they’re not right on the edge of the viewable area.\nLastly, we have our array of targets and we get the viewport size so that we can properly calculate the scale.\nfunc add_target(t): if not t in targets: targets.append(t) func remove_target(t): if t in targets: targets.erase(t) For adding and removing targets, we have two helper functions. You can use these during gameplay to change what targets are being tracked (“Player 3 has entered the game!”). Note that we don’t want to have the same target tracked twice, so we reject it if it’s already there.\nMost of the functionality happens in _process(). First, moving the camera:\nfunc _process(delta): if !targets: return # Keep the camera centered between the targets var p = Vector2.ZERO for target in targets: p += target.position p /= targets.size() position = lerp(position, p, move_speed * delta) Here, we loop through the targets’ positions and find the common center. Using lerp() we make sure it moves there smoothly.\nNext, we’ll handle the zoom:\n# Find the zoom that will contain all targets var r = Rect2(position, Vector2.ONE) for target in targets: r = r.expand(target.position) r = r.grow_individual(margin.x, margin.y, margin.x, margin.y) var z if r.size.x \u003e r.size.y * screen_size.aspect(): z = 1 / clamp(r.size.x / screen_size.x, min_zoom, max_zoom) else: z = 1 / clamp(r.size.y / screen_size.y, min_zoom, max_zoom) zoom = lerp(zoom, Vector2.ONE * z, zoom_speed) The key functionality here comes from Rect2. We want to find a rectangle that encloses all the targets, which we can get with the expand() method. We then grow the rect by the margin.\nHere you can see the rectangle being drawn (press “Tab” in the demo project to enable this drawing):\nThen, depending whether the rectangle is wider or taller (relative to the screen’s aspect ratio), we find the scale and clamp it in the max/min range we’ve defined.\nFull script extends Camera2D @export var move_speed = 30 # camera position lerp speed @export var zoom_speed = 3.0 # camera zoom lerp speed @export var min_zoom = 5.0 # camera won't zoom closer than this @export var max_zoom = 0.5 # camera won't zoom farther than this @export var margin = Vector2(400, 200) # include some buffer area around targets var targets = [] @onready var screen_size = get_viewport_rect().size func _process(delta): if !targets: return # Keep the camera centered among all targets var p = Vector2.ZERO for target in targets: p += target.position p /= targets.size() position = lerp(position, p, move_speed * delta) # Find the zoom that will contain all targets var r = Rect2(position, Vector2.ONE) for target in targets: r = r.expand(target.position) r = r.grow_individual(margin.x, margin.y, margin.x, margin.y) var z if r.size.x \u003e r.size.y * screen_size.aspect(): z = 1 / clamp(r.size.x / screen_size.x, max_zoom, min_zoom) else: z = 1 / clamp(r.size.y / screen_size.y, max_zoom, min_zoom) zoom = lerp(zoom, Vector2.ONE * z, zoom_speed * delta) # For debug get_parent().draw_cam_rect(r) func add_target(t): if not t in targets: targets.append(t) func remove_target(t): if t in targets: targets.remove(t) Download This Project Download the project’s example code here: https://github.com/godotrecipes/multitarget_camera\n","description":"","tags":null,"title":"Multitarget Camera","uri":"/godot_recipes/4.x/2d/multi_target_camera/index.html"},{"content":"Problem You want to make an arcade-style car game, so you’re looking for simplicity over realistic physics. In this recipe, you’ll learn how to make a fun, driveable car using a rolling sphere.\nSolution There are a lot of ways to make a driving game. Different games need different levels of realism. If you’re trying to make a light, arcade-style car, you don’t need all of the features that Godot’s VehicleBody3D node provides, such as supension, independently modeled wheels, etc.\nInstead, we’re going to use a single RigidBody3D sphere to handle the driving physics. The sphere will be invisible, and the car mesh will be placed at the sphere’s location, making it look like it’s the car that’s driving.\nAs you can see in the preview clip above, the result looks remarkably good (and feels great to play!). Read on, and you’ll see that the amount of code required is also surprisingly small.\nInputs For control, we’re going to add four inputs to the Input Map:\naccelerate brake steer_left steer_right You can use keyboard input, game controller, or both. However, we recommend going with the analog stick for better steering.\nNode setup The car is made with two main nodes: a RigidBody3D sphere for the physics, and a MeshInstance3D to display the car body. Here’s the scene layout:\nRigidBody3D (Car) CollisionShape3D (Sphere) CarMesh (Imported model) Here’s how these nodes will interact: pressing “accelerate” will apply a force on the RigidBody3D in the direction the CarMesh is facing, while the turning inputs will rotate the CarMesh. As the ball rolls, it will carry the car mesh along with it (we’ll ignore the ball’s rotation).\nCarMesh Here’s the car model we’ll use:\nNote You can find this and other car models in Kenney’s “Car Kit”, available here: https://kenney.nl/assets/car-kit. Download the whole kit; you can use any of them that you choose. Note that this kit includes the models in multiple formats - you won’t need all of them for your project. GLTF is the recommended format for use with Godot.\nIf you use the GLTF models, you shouldn’t have adjust anything in the import settings.\nHere’s what the node tree looks like when importing the “suv” model:\nNote that the wheels \u0026 body are separate meshes. This will make it easy to add some visual appeal - like turning the wheels when steering.\nBall Add a sphere shape to the CollisionShape3D. We’re using a radius of 1 here, but you’ll want to experiment with the size of the ball to get different driving behaviors.\nHere’s how to adjust the settings on the body:\nAngular Damp: 10 - this property will have a huge effect on the driving feel. A higher value will bring the car to a stop much faster. Gravity Scale: 5 - Default gravity in Godot (9.8) feels a bit floaty, especially when going for an action feel. This will really matter if you plan to have jumps, hills, etc. in your world. You can set this globally in the Project Settings instead, if you prefer. Physics Material/Bounce: 0.1 - Playing around with this value can be a lot of fun. Be careful going above 0.5, though! For the demo, we’ve also added a spherical mesh to the collision shape for debugging purposes. You don’t need this, but it helps when troubleshooting to have a visual of the ball rolling.\nRayCast Finally, add a RayCast3D node as a child of the CarMesh. Set its Target Position to (0, -1, 0).\nWe’re going to use this for ground detection. When the car’s in the air, steering and acceleration won’t work. We can also use it to align the car mesh to a slope (if your game’s track isn’t flat).\nNow we’re ready to start coding.\nScript We’ll begin the script with some node references we’ll need:\nextends RigidBody3D @onready var car_mesh = $CarMesh @onready var body_mesh = $CarMesh/suv2 @onready var ground_ray = $CarMesh/RayCast3D @onready var right_wheel = $CarMesh/suv2/wheel_frontRight @onready var left_wheel = $CarMesh/suv2/wheel_frontLeft Next, some variables configuring the car’s behavior. See the comments describing each one’s purpose.\n# Where to place the car mesh relative to the sphere var sphere_offset = Vector3.DOWN # Engine power var acceleration = 35.0 # Turn amount, in degrees var steering = 18.0 # How quickly the car turns var turn_speed = 4.0 # Below this speed, the car doesn't turn var turn_stop_limit = 0.75 # Variables for input values var speed_input = 0 var turn_input = 0 You can @export these if you’d like to adjust them from the Inspector.\nIn _physics_process() we add a force to the body based on the direction the car is pointing, as well as keeping the car mesh positioned at the ball’s position:\nfunc _physics_process(delta): car_mesh.position = position + sphere_offset if ground_ray.is_colliding(): apply_central_force(-car_mesh.global_transform.basis.z * speed_input) The next step is to get the inputs, but we’ll also check if the ray is colliding with the ground first:\nfunc _process(delta): if not ground_ray.is_colliding(): return speed_input = Input.get_axis(\"brake\", \"accelerate\") * acceleration turn_input = Input.get_axis(\"steer_right\", \"steer_left\") * deg_to_rad(steering) right_wheel.rotation.y = turn_input left_wheel.rotation.y = turn_input Tip At this point, you can try it out. You should be able to accelerate forward and back (but not steer yet).\nNext, still in the _process() function, we’ll rotate the car mesh based on the rotation input. We’ll use slerp() (spherical linear interpolation) to do this smoothly:\n# rotate car mesh if linear_velocity.length() \u003e turn_stop_limit: var new_basis = car_mesh.global_transform.basis.rotated(car_mesh.global_transform.basis.y, turn_input) car_mesh.global_transform.basis = car_mesh.global_transform.basis.slerp(new_basis, turn_speed * delta) car_mesh.global_transform = car_mesh.global_transform.orthonormalized() Warning Because of floating point imprecision, repeatedly rotating a transform will eventually cause it to become distorted. The scale can drift or the axes can become no-perpendicular. In any script where you’re regularly rotating a transform, it’s a good idea to use orthonormalized() to correct any error before it accumulates.\nYou should try playing again at this point. You’ll be able to control the car and drive around, and everything works pretty much as expected. However, there are a few more things to add that will improve the “feel” of the driving.\nFinal touches 1. Align with slopes FIX THIS\nIf you’ve tried driving on a slope, you’ve seen that the car mesh doesn’t tilt at all, it always remains level. That looks unnatural, so let’s use the process described in KinematicBody: Align with Surface to fix that.\nAdd this code after rotating the mesh in _process():\nif ground_ray.is_colliding(): var n = ground_ray.get_collision_normal() var xform = align_with_y(car_mesh.global_transform, n) car_mesh.global_transform = car_mesh.global_transform.interpolate_with(xform, 10.0 * delta) And the align function (notice how we’re using orthonormalized() again?):\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform.orthonormalized() 2. Turn the wheels It looks nice if the front wheels turn when you steer. Add some references to the front wheel meshes at the top of the script:\n@onready var right_wheel = $CarMesh/suv2/wheel_frontRight @onready var left_wheel = $CarMesh/suv2/wheel_frontLeft And right after getting input, add the following:\n# rotate wheels for effect right_wheel.rotation.y = rotate_input left_wheel.rotation.y = rotate_input 3. Tilt the body This one adds lots of visual appeal. We’re going to tilt the car’s body based on the speed of the turn. Add a variable at the top of the script:\nvar body_tilt = 35 The smaller this number, the more extreme the tilt effect will be. Between 35 and 40 works well for the SUV model.\nNow add the following right after rotating the car mesh (in the if statement):\n# tilt body for effect var t = -rotate_input * ball.linear_velocity.length() / body_tilt body_mesh.rotation.z = lerp(body_mesh.rotation.z, t, 10 * delta) Observe the difference:\nCredits The demo project seen here uses the following open-source/creative commons assets:\nCars: Kenney Car Kit by Kenney Track: Modular Racekart Track by Keith at Fertile Soil Productions Download This Project Download the project code here: https://github.com/godotrecipes/3d_car_sphere\nRelated recipes Input Actions ","description":"","tags":null,"title":"Arcade-style Car","uri":"/godot_recipes/4.x/3d/3d_sphere_car/index.html"},{"content":"Problem You want to use a RigidBody2D to create a semi-realistic spaceship, a la Asteroids.\nSolution Using RigidBody2D can be tricky. Because it’s controlled by Godot’s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc, and we’ll refer to it as we work through this example.\nFor this example, we’ll use the following node setup:\nRigidBody2D (Ship) Sprite2D CollisionShape2D Sprite orientation Don’t forget to orient your sprite correctly. An object that is not rotated should be pointing along the +X axis, i.e. to the right. If your sprite’s art is drawn facing in another direction, rotate the Sprite2D (not the parent body) to align it correctly.\nWe’ll use the following inputs in the Input Map:\nInput Key thrust w or ↑ rotate_right d or → rotate_left a or ← Add a script to the body, and let’s define some variables:\nextends RigidBody2D @export var engine_power = 800 @export var spin_power = 10000 var thrust = Vector2.ZERO var rotation_dir = 0 The first two variables are how we’ll control the ship’s “handling”. engine_power is going to affect acceleration and top speed. spin_power controls how fast the ship rotates.\nthrust and rotation_dir are going to be set by pressing the inputs. Let’s do that next:\nfunc get_input(): thrust = Vector2.ZERO if Input.is_action_pressed(\"thrust\"): thrust = transform.x * engine_power rotation_dir = Input.get_axis(\"rotate_left\", \"rotate_right\") If we’re pressing the \"thrust\" input, we’ll set the thrust vector to the ship’s forward direction, while rotation_dir will be +/-1 based on the rotate inputs.\nWe can start flying by applying those values in _physics_process():\nfunc _physics_process(_delta): get_input() constant_force = thrust constant_torque = rotation_dir * spin_power It works, but you’ll notice that it’s very hard to control. The rotation is too fast, and it accelerates to a high speed before going offscreen. This is where we want to break from “real” space physics. In space, there’s no friction, but our Asteroids-style ship will be a lot easier to control if it coasted to a stop when we’re not thrusting. We can control this with damping.\nIn the RigidBody2D properties, you’ll find Linear/Damp and Angular/Damp. Set these to 1 and 2 respectively, and they’ll slow the movement/rotation as well as causing them to stop.\nFeel free to experiment with these values and how they interact with the engine_power and spin_power\nScreen wrapping Wrapping around the screen is really teleportation: when the ship goes off the right side of the screen, you teleport it to the left side. However, if you just tried to change the position, you’d find that it instantly snapped back. This is because the physics engine is trying to control the position as well.\nThe solution to this is to use the _integrate_forces() callback of the rigid body. In this function, you can safely update the physics properties of the object without conflicting with what the physics engine is doing.\nLet’s get the screensize at the top of the script:\n@onready var screensize = get_viewport_rect().size Then add the new function:\nfunc _integrate_forces(state): var xform = state.transform xform.origin.x = wrapf(xform.origin.x, 0, screensize.x) xform.origin.y = wrapf(xform.origin.y, 0, screensize.y) state.transform = xform As you can see, the _integrate_forces() function includes a parameter called state. This object is the PhysicsDirectBodyState2D of our body. It contains all of the current physics properties such as the forces, velocity, position, etc.\nFrom the state, we grab the current transform, modify it to wrap around the screen using wrapf(), and then set it back to the current state.\nHere’s how it looks:\nWarping Let’s look at one more example of using _integrate_forces() to alter the body’s state without issues. Let’s add a “warp” mechanic - when the player presses the \"warp\" input, the ship will teleport to a random spot on the screen.\nFirst, we’ll add a new variable for this:\nvar teleport_pos = null Then, in get_input(), we’ll set a random position:\nif Input.is_action_just_pressed(\"warp\"): teleport_pos = Vector2(randf_range(0, screensize.x), randf_range(0, screensize.y)) Finally, in _integrate_forces(), if there’s a teleport_position set, we’ll use it and then clear it:\nif teleport_pos: physics_state.transform.origin = teleport_pos teleport_pos = null Download This Project Download the project’s example code here: https://github.com/godotrecipes/asteroids_physics\n","description":"","tags":null,"title":"Asteroids-style Physics (using RigidBody2D)","uri":"/godot_recipes/4.x/physics/asteroids_physics/index.html"},{"content":"Problem You want to move a 3D object to a clicked position.\nSolution We’ll start with a flat plane for our world. Our actor will move on this plane.\nThe actor for this demo is a triangular prism mesh:\nHere is the code for the movement. If given a target, the object will turn and move toward it.\nextends CharacterBody3D @export var speed = 5 @export var gravity = -5 var target = Vector3.ZERO func _physics_process(delta): velocity.y += gravity * delta if target: look_at(target, Vector3.UP) rotation.x = 0 velocity = -transform.basis.z * speed if transform.origin.distance_to(target) \u003c .5: target = Vector3.ZERO velocity = Vector3.ZERO move_and_slide() We’ve also added a MeshInstance3D called “Marker” to the scene. This will be moved to indicate the clicked position.\nMouse -\u003e 3D Now we need a way to map mouse position into our 3D world. If you imagine the screen as a window into the 3D world, the mouse is trapped on the glass. To select something in 3D, we must project a ray from our eye (the camera), through the mouse’s position and into the world.\nWhile this can be done manually using the Camera3D’s project_ray methods, we can take advantage of the fact that CollisionObject3D nodes do this automatically. All we need to do is connect our StaticBody3D ground’s input_event signal:\nfunc _on_StaticBody_input_event(camera, event, click_position, click_normal, shape_idx): if event is InputEventMouseButton and event.pressed: $Marker.transform.origin = click_position $Player.target = click_position We set the position of the marker and the Player’s target to the clicked position:\nWrapping up You can use this technique to detect clicks on any objects in your 3D world.\nRelated recipes Like video? ","description":"","tags":null,"title":"Click to move","uri":"/godot_recipes/4.x/3d/click_to_move/index.html"},{"content":"Problem Your game needs a “level select” menu, where the user can choose from a grid of options.\nSolution As shown in the example above, we’ll make a scrolling grid of level “boxes” that the player can choose from. Let’s start with the individual level boxes:\n1: Level box Here’s the node setup:\nLevelBox: PanelContainer Label MarginContainer TextureRect The TextureRect is for displaying the lock icon, and the Label for displaying the level number. When one is showing, the other is hidden.\nYou can style these as you like, here’s an example:\nMake sure to set the LevelBox’s Custom Minimum Size in the Inspector. We’re using (110, 110) in the example, but it depends on what size layout you’re going for.\nNow add a script and connect the gui_input signal.\n@tool extends PanelContainer signal level_selected @export var locked = true: set = set_locked @export var level_num = 1: set = set_level @onready var lock = $MarginContainer/Lock @onready var label = $Label func set_locked(value): locked = value if not is_inside_tree(): await ready lock.visible = value label.visible = not value func set_level(value): level_num = value if not is_inside_tree(): await ready label.text = str(level_num) func _on_gui_input(event): if locked: return if event is InputEventMouseButton and event.pressed: level_selected.emit(level_num) print(\"Clicked level \", level_num) We’re using @tool here so that we can make changes to the properties in the inspector and see them right away, without running the scene. Go ahead and try clicking the Locked property and verify that you see the lock appear/disappear.\nSince we don’t have actual levels to load in this project, the print() statement can help test that the click is being detected.\n2: Grid Once you have the box scene completed, add a new scene with a GridContainer. Add any number of LevelBox instances under it, making sure to set the Columns value. Here’s one with 6 columns:\nIn this example Theme Overrides/Constants/H Separation and V Separation are set to 10.\nSave this scene as LevelGrid. In the menu, we’ll use multiple instances to display the desired number of levels.\n3: Menu screen Now we can put together the final menu.\nHere’s the basic layout we’re going for:\nWe’ll create it with these nodes:\nLevelMenu: MarginContainer VBoxContainer Title: Label HBoxContainer BackButton: TextureButton ClipControl: Control NextButton: TextureButton Adjust the node properties:\nLevelMenu Theme Overrides/Constants/Margins: 20 VBoxContainer Theme Overrides/Constants/Separation: 50 Title Style the font however you like BackButton / NextButton Ignore Texture Size: On Stretch Mode: Keep Centered Layout/Container Sizing/Horizontal/Expand: On ClipControl Layout/Clip Contents: On Layout/Custom Minimum Size: (710, 350) (size of the LevelGrid) The ClipControl node is where the grid goes. Enabling Clip Contents means that if the contents are larger than the control, they’ll be cropped. That will allow us to make a horizontally scrolling set of grids. Add an HBoxContainer called GridBox to ClipControl, and instance 3 (or more) LevelGrids inside it.\nMake sure to set Theme Overrides/Constants/Separation to 0.\nYour layout should look something like this (we’ve disabled Clip Contents in order to show what’s happening):\nWith Clip Content, the three grids are all there, but the ClipControl only shows one at a time.\nNow, to scroll the menu, we need to shift the GridBox by 710 pixels to the left/right.\n110 (width of each LevelBox) * 6 (grid columns) + 10 (grid spacing) * 5 == 710 Info You may be wondering why we’re not using a ScrollContainer here. You certainly can, but we don’t want continuous scrolling, and we don’t want to see a scrollbar.\nAdd a script to the LevelMenu and connect the pressed signals of the two buttons.\nextends MarginContainer var num_grids = 1 var current_grid = 1 var grid_width = 710 @onready var gridbox = $VBoxContainer/HBoxContainer/ClipControl/GridBox func _ready(): # Number all the level boxes and unlock them # Replace with your game's level/unlocks/etc. # You can also connect the \"level_selected\" signals here num_grids = gridbox.get_child_count() for grid in gridbox.get_children(): for box in grid.get_children(): var num = box.get_position_in_parent() + 1 + 18 * grid.get_position_in_parent() box.level_num = num box.locked = false func _on_BackButton_pressed(): if current_grid \u003e 1: current_grid -= 1 gridbox.rect_position.x += grid_width func _on_NextButton_pressed(): if current_grid \u003c num_grids: current_grid += 1 gridbox.rect_position.x -= grid_width When you run the scene, try clicking the “Next” and “Back” buttons and verify that it’s scrolling as expected. Clicking the individual level boxes should print to the console.\nDownload the example project to see the whole thing in action, including some tweens for the scrolling action (because tweens make everything better).\nDownload This Project Download the project code here: https://github.com/godotrecipes/ui_level_select\n","description":"","tags":null,"title":"Level Select Menu","uri":"/godot_recipes/4.x/ui/level_select/index.html"},{"content":"Problem You’d like to understand what is meant by dot product and cross product.\nSolution In this recipe we’ll introduce the concept of vector dot product and cross product and how they might be used.\nDot product Dot product is an operation on two vectors that returns a scalar. It is often visualized as the projection of vector A onto vector B:\nThis is the formula for calculating the dot product:\nWhere θ is the angle between the two vectors and ||A|| is the magnitude of A.\nThis is very useful when both vectors are normalized (i.e. their magnitudes are 1), then the formula simplifies to:\nThis shows that the dot product is directly related to the angle between the two vectors. Since cos(0) == 1 and cos(180) == -1, the result of the dot product can tell you how closely aligned two vectors are:\nSee below for how we can apply this fact in a practical example.\nCross product The cross product of two vectors is a third vector that is perpendicular to both of them. Its magnitude is related to their magnitudes and the angle between them.\nOnce again, if we’re using normalized vectors, the result is simplified: it will be directly related to the angle and its magnitude will range from -1 to 1.\nNote Since the cross product is perpendicular to both vectors, we would need to be working in 3D. In most 2D frameworks, including Godot, the 2D Vector2.cross() method returns a scalar value representing the result’s magnitude.\nPractical applications Consider this animation, showing how the results of Vector2.dot() and Vector2.cross() change in relation to the changing angle:\nThis demonstrates two common applications of these methods. If the red vector is our object’s forward direction, and the green shows the direction towards another object:\nDot product: Using the result, we can tell if the object is in front of (\u003e 0) or behind (\u003c 0) us. Cross product: Using the result, we can tell if the object is to the left (\u003e 0) or right (\u003c 0). ","description":"","tags":null,"title":"Vectors: Using Dot and Cross Product","uri":"/godot_recipes/4.x/math/dot_cross_product/index.html"},{"content":"Problem You need your character body to align with the surface or terrain.\nSolution This recipe builds on the basic CharacterBody3D controller described in the CharacterBody3D: Movement recipe, so read that one first.\nFirst, we’ve added some terrain to the scene. You can download the terrain from here: https://fertile-soil-productions.itch.io/modular-terrain-pack. This is low-poly terrain, but you can use or make any terrain you like for this technique.\nAs you can see, the movement still works with the terrain, but the tank seems to “float” above the slopes because it doesn’t change its orientation.\nInstead, we need to rotate the tank so that its treads are aligned with the ground, even as the slope changes. To do that, we need to know which way is up.\nSurface normals A surface normal is a unit vector (“normal vector” and “unit vector” mean the same thing) perpendicular to a surface. It shows which way the surface is facing. In the case of a mesh, every surface has a normal pointing outward.\nIn Godot, when a body collides, you can get the normal of the collision. This will be the colliding body’s normal at the point of contact.\nOnce we have the surface normal, we need to align the tank’s Y axis with it. Note that we can’t use Transform3D.looking_at(), because that will align the -Z (forward) axis with the normal.\nTo do this, we’ll use the following function:\nfunc align_with_y(xform, new_y): xform.basis.y = new_y xform.basis.x = -xform.basis.z.cross(new_y) xform.basis = xform.basis.orthonormalized() return xform Given a transform and a new Y direction vector, this function returns the transform rotated so that its basis.y is aligned with the given normal.\nNote If you’re unfamiliar with the cross product or other vector math, there’s a great vector math intro in the Godot Docs.\nWe can update the tank’s movement code to call this function when it collides with a surface:\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) move_and_slide() for i in get_slide_count(): var c = get_slide_collision(i) global_transform = align_with_y(global_transform, c.get_normal()) This doesn’t work quite as expected:\nThe problem is that the tank’s collision shape could be colliding with more than one of the terrain’s faces. Also, move_and_slide() can result in more than one collision in a single frame. This leads to the jittering. We need to choose one face and stick with it.\nAdd a RayCast3D child to the tank and set its Target Position to (0, -1, 0).\nSince this raycast is pointing down from the exact center of the tank, we’ll align with the individual surface that it collides with - the one directly beneath the tank.\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) move_and_slide(v) var n = $RayCast3D.get_collision_normal() global_transform = align_with_y(global_transform, n) This is much better, but because we are instantly snapping to the new alignment every time the tank crosses an edge, it still looks a little jarring:\nWe can solve this last problem by interpolating to the new transform rather than snapping immediately to it.\nfunc _physics_process(delta): velocity += gravity * delta get_input(delta) velocity = move_and_slide_with_snap(velocity, Vector3.DOWN*2, Vector3.UP, true) var n = $RayCast.get_collision_normal() var xform = align_with_y(global_transform, n) global_transform = global_transform.interpolate_with(xform, 12 * delta) The result is much smoother and more pleasing:\nYou can get even better results with two raycasts - one at the front and one at the back. Get the average normal from them:\nvar n = ($FrontRay.get_collision_normal() + $RearRay.get_collision_normal()) / 2.0 Feel free to experiment with the interpolation amount. We found 12 to work well in this situation, but you might find a higher or lower value works better for your setup.\nDownload This Project Download the project’s example code here: https://github.com/godotrecipes/characterbody3d_examples\nRelated recipes CharacterBody3D: Movement Math: Interpolation Math: Transforms ","description":"","tags":null,"title":"CharacterBody3D: Align with Surface","uri":"/godot_recipes/4.x/3d/3d_align_surface/index.html"},{"content":" Games Demo games and tutorials.\nUpdating to Godot 4.0 We’re working on new content for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available.\nIn this section: Your First 2D Game ","description":"","tags":null,"title":"Game Tutorials","uri":"/godot_recipes/4.x/games/index.html"},{"content":"If you’ve been following along, you’ve learned a lot of the fundamentals of building games in Godot. We’re going to end the tutorial here, since we’ve completed the basic game.\nThe secret to learning effectively Here’s my big secret for getting the most out of tutorials like this and others you may find online. At the end, once you’ve finished building the project, immediately delete it and start over. This time, try and re-create it without looking at the tutorial. If you get stuck, look at just that part, then close it again.\nIt may sound repetitive, but that is how we learn: by doing things repeatedly. If you follow this tip, you’ll be amazed at how quickly you level up your gamedev skills.\nAdding to the game If you’re feeling comfortable with the techniques used to make this game, then you’re ready to branch out. Try adding a single new feature to this game.\nIf you’re stuck coming up with an idea, here are some suggestions:\nAdditional enemy types - there is art for other enemies in the art pack. How do they move and shoot?\nWaves - make more enemies spawn every time you clear the screen\nBoss enemies - what if a big enemy appears?\nBoosts - powerups could appear for the player to collect. There’s some art for those too.\nShield recharge - collect these to power up the shield Weapon upgrades - shoot more bullets, patterns, etc. Sound and music - give everything a lot more personality with some sound effects and background music.\nLearning more Ready for more? Here are some suggestions for your next learning adventure:\nGodot 101: Getting started in 3D - if you’re interested in making things in 3D, check out this introduction to Godot’s 3D features.\nCheck out the rest of the content on this website. There are lots of examples, tutorials, and code snippets to help you learn how to make your dream game.\nDownload This Project on GitHub Download the project code here:\nhttps://github.com/godotrecipes/8_direction_animation\n","description":"","tags":null,"title":"Wrapping up","uri":"/godot_recipes/4.x/games/first_2d/first_2d_end/index.html"},{"content":" Godot Recipes Godot’s nodes are your ingredients. What can you cook up?\nOn this site you’ll find a collection of solutions and examples to help you make whatever game system you need.\nGodot 4.0 Godot 4.0 has been released!\nGodot 4.0 is the latest stable release version of the engine.\nThere is a limited amount of learning material available, so if you’re looking to make a game or learn the engine, we recommend you stick with 3.x for now. Don’t worry - what you learn will still apply when you’re ready to move to the newer version!\nThis site has lots of learning material for Godot 3 - but not all of it has been updated for version 4 yet. You can click the ribbon in the top-right to toggle the Godot Recipes version, or click the button below:\nGodot 3 Recipes Are you ready to learn game development? Whether it’s as a hobby or working towards your dream career, there’s never been a better time to get started. Modern programming languages and tools have made it easier than ever to build high-quality games and distribute them to the world. One of these tools is the Godot game engine. For beginners, it offers a friendly way to learn gamedev techniques. For experienced developers, it’s a powerful, customizable and open tool for bringing your visions to life.\nOn this site you’ll find a gentle introduction to the Godot game engine, as well as a wide variety of gamedev tips and techniques. Feel free to browse the categories in the sidebar and see what catches your interest.\nIf you’re new to Godot, start here: What is Godot?.\nHow to use this site Beginners If you’re new to game development, start with the “Godot 101: Basics” section. There you’ll find an introduction to the Godot application, and a step-by-step guide to creating your first project. There is a lot of material to absorb here. Don’t feel discouraged if you feel you don’t get it at first. Repetition is the key to learning complex topics; the more you work with Godot’s features, the more familiar and easy they will start to feel.\nInfo It’s assumed that you have at least some general programming experience. If you’re completely new to programming, click here for tips on how to get started.\nExperienced Developers If you’re an experienced developer and/or you’re familiar with other modern game engine(s), feel free to explore the menu on the left. You’ll find a number of useful guides and tutorials to show you how to do things the “Godot Way”. Code samples and example projects are available for all articles.\n","description":"","tags":null,"title":"Home","uri":"/godot_recipes/4.x/index.html"},{"content":"","description":"","tags":null,"title":"Categories","uri":"/godot_recipes/4.x/categories/index.html"},{"content":"RayCast2D Raycasting is a common technique in game development. “Casting a ray” means extending a line from a point until it collides with something or reaches its limit.\nNode properties Add a RayCast2D node and take a look at the Inspector:\nHere are the main properties you’ll need to understand:\nEnabled Turn this off to disabled the raycast work.\nExclude Parent This property causes the ray to ignore collisions with the parent object. Enabled by default.\nTarget Position This is the destination point of the ray. Note: This is in local coordinates.\nAlso, take note of the Collide With section. By default the ray will only detect bodies, so you’ll need to go here if you want to detect areas as well or instead.\nUseful functions You can see the full list of the node’s functions in the API Documentation. Here are the some of the most useful ones:\nis_colliding() Boolean function, lets you know if the ray is colliding with something.\nget_collision_point() If the ray is colliding, this will return the position of the collision (in global coordinates).\nget_collider() If the ray is colliding, this function will return a reference to the colliding object.\nget_collision_normal() Another useful piece of information, this is the normal of the collided object at the point of collision.\nExample uses There are many uses for raycasts: visibility (can A see B, or is there an obstacle between?), proximity (am I close to a wall/ground/obstacle?), etc. Here are a couple of practical examples in use:\n1. Shooting Fast-moving projectiles often have the problem of “tunneling” through obstacles - they are moving too fast for the collision to be detected in a single frame. As an alternative, you can use a Raycast2D to represent the path (or a laser, etc.).\nHere’s a player sprite with a raycast attached to the end of the gun. The target_position is set to (250, 0).\nWhen the player shoots, you check to see if the ray is colliding with something:\nfunc _input(event): if event.is_action_pressed(\"shoot\"): if $RayCast2D.is_colliding(): print($RayCast2D.get_collider().name) 2. Edge detection Consider a platformer enemy that walks on platforms, but you don’t want it to fall off the edges. Add two downward-pointing raycasts to the mob like so:\nIn the mob’s script, check for when the ray stops colliding. That means you’ve found the edge and should turn around:\nfunc _physics_process(delta): velocity.y += gravity * delta if not $RayRight.is_colliding(): dir = -1 if not $RayLeft.is_colliding(): dir = 1 velocity.x = dir * speed $AnimatedSprite.flip_h = velocity.x \u003e 0 velocity = move_and_slide(velocity, Vector2.UP) Here’s what it looks like in action:\n","description":"","tags":null,"title":"RayCast2D","uri":"/godot_recipes/4.x/kyn/raycast2d/index.html"},{"content":"","description":"","tags":null,"title":"Tags","uri":"/godot_recipes/4.x/tags/index.html"}] \ No newline at end of file diff --git a/docs/4.x/index.xml b/docs/4.x/index.xml index e8182183..b4bbd5d8 100644 --- a/docs/4.x/index.xml +++ b/docs/4.x/index.xml @@ -6,11 +6,11 @@ In this section: RayCast2D2Dhttp In this section: Entering/Exiting the screen Platform character Screen wrap Top-down movement Grid-based movement Shooting projectiles Car steering 8-Directional Movement/Animation Using Y-Sort Moving Platforms Pathfinding on a 2D Grid Multitarget Camera3Dhttps://godotrecipes.com/godot_recipes/4.x/3d/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/3d/index.html 3D Tips, tricks, and tutorials on the 3D side of game development. In this section: Basic FPS Character Interpolated Camera Shooting with Raycasts CharacterBody3D: Movement 3D Unit Healthbars Rolling Cube Arcade-style Spaceship Arcade-style Car Click to move CharacterBody3D: Align with SurfaceAnimationhttps://godotrecipes.com/godot_recipes/4.x/animation/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/animation/index.html Animation Using Godot&rsquo;s animation system. In this section: Spritesheet animationInputhttps://godotrecipes.com/godot_recipes/4.x/input/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/input/index.html Input Handling input - from keyboard and mouse to game controllers and touchscreens. -In this section: Input Actions Mouse Input Capturing the Mouse Mouse: Drag-select multiple unitsUIhttps://godotrecipes.com/godot_recipes/4.x/ui/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/ui/index.html UI Building user interfaces. +In this section: Input Actions Mouse Input Adding Input Actions in code Capturing the Mouse Mouse: Drag-select multiple unitsUIhttps://godotrecipes.com/godot_recipes/4.x/ui/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/ui/index.html UI Building user interfaces. In this section: Level Select MenuGamedev Mathhttps://godotrecipes.com/godot_recipes/4.x/math/index.htmlTue, 09 Apr 2019 19:49:14 -0700https://godotrecipes.com/godot_recipes/4.x/math/index.html Gamedev Math Math is a big part of game development. Some of it you may remember from school, or it may be something you&rsquo;ve never encountered before. Here you&rsquo;ll find guides to help you get up to speed and examples of how these concepts are applied to making games. In this section: Interpolation Transforms Vectors: Using Dot and Cross ProductAI/Behaviorhttps://godotrecipes.com/godot_recipes/4.x/ai/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/ai/index.html AI/Behavior Automated behavior and (sometimes) smarter entities. In this section: Homing missilePhysicshttps://godotrecipes.com/godot_recipes/4.x/physics/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/physics/index.html Physics Learn how to use Godot&rsquo;s physics nodes. -In this section: RigidBody2D: Drag and Drop Character to Rigid Body Interaction Asteroids-style Physics (using RigidBody2D)Audiohttps://godotrecipes.com/godot_recipes/4.x/audio/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/audio/index.html Audio Helpful recipes for adding sound effects and music to your game. +In this section: RigidBody2D: Look at Target RigidBody2D: Drag and Drop Character to Rigid Body Interaction Asteroids-style Physics (using RigidBody2D)Audiohttps://godotrecipes.com/godot_recipes/4.x/audio/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/audio/index.html Audio Helpful recipes for adding sound effects and music to your game. In this section: Audio ManagerGame Tutorialshttps://godotrecipes.com/godot_recipes/4.x/games/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/games/index.html Games Demo games and tutorials. Updating to Godot 4.0 We&rsquo;re working on new content for Godot 4.0. In the meantime, we recommend new learners stick with Godot 3.x, which has a lot more resources and learning materials available. In this section: Your First 2D Game \ No newline at end of file diff --git a/docs/4.x/input/custom_actions/index.html b/docs/4.x/input/custom_actions/index.html new file mode 100644 index 00000000..8fc8b5a7 --- /dev/null +++ b/docs/4.x/input/custom_actions/index.html @@ -0,0 +1,44 @@ +Adding Input Actions in code :: Godot 4 Recipes + +

    Adding Input Actions in code

    Problem

    You need to add actions to the InputMap at runtime.

    Solution

    Typically, you’ll add actions to the InputMap via Project Settings, as shown in Recipe: Input Actions. However, you may find yourself needing to add one or more actions directly in a script. The InputMap singleton has methods to help you do this.

    Here’s an example that would add a new action called “attack” using the space key:

    func _ready():
    +    InputMap.add_action("attack")
    +    var ev = InputEventKey.new()
    +    ev.keycode = KEY_SPACE
    +    InputMap.action_add_event("attack", ev)
    +

    If you also wanted to add the left mouse button to the same action:

    ev = InputEventMouseButton.new()
    +ev.button_index = MOUSE_BUTTON_LEFT
    +InputMap.action_add_event("attack", ev)
    +
    Note

    InputMap.add_action() will produce an error if the action already exists. You should check first with InputMap.has_action() before attempting to add a new action.

    Practical Example

    Let’s say you’ve made the platform character from Recipe: Platform character and you want to re-use it in another project. If you saved the scene, script, and assets in a single folder, you need only copy that folder to your new project. But you’d still need to edit the Input Map in order for the inputs to work.

    Instead, you could add the following code to the player script and be sure that the necessary input actions will be added automatically:

    var controls = {"walk_right": [KEY_RIGHT, KEY_D],
    +                "walk_left": [KEY_LEFT, KEY_A],
    +                "jump": [KEY_UP, KEY_W, KEY_SPACE]}
    +
    +func _ready():
    +    add_inputs()
    +
    +func add_inputs():
    +    var ev
    +    for action in controls:
    +        if not InputMap.has_action(action):
    +            InputMap.add_action(action)
    +        for key in controls[action]:
    +            ev = InputEventKey.new()
    +            ev.keycode = key
    +            InputMap.action_add_event(action, ev)
    +
    + + \ No newline at end of file diff --git a/docs/4.x/input/index.html b/docs/4.x/input/index.html index 026bbf01..63f648eb 100644 --- a/docs/4.x/input/index.html +++ b/docs/4.x/input/index.html @@ -1,17 +1,19 @@ -Input :: Godot 4 Recipes - -
    + + \ No newline at end of file diff --git a/docs/4.x/input/index.xml b/docs/4.x/input/index.xml index aeda0e9a..f61fb747 100644 --- a/docs/4.x/input/index.xml +++ b/docs/4.x/input/index.xml @@ -3,7 +3,10 @@ Solution Let&rsquo;s say you&rsquo;re making a top-down character and yo Input actions can help to make your code more configurable.Mouse Inputhttps://godotrecipes.com/godot_recipes/4.x/input/mouse_input/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/input/mouse_input/index.htmlProblem You want to detect mouse input. Solution InputEventMouse is the base class for mouse events. It contains position and global_position properties. Inheriting from it are two classes: InputEventMouseButton and InputEventMouseMotion. Note You can assign mouse button events in the InputMap, so you can use them with is_action_pressed(). -InputEventMouseButton @GlobalScope.ButtonList contains a list of BUTTON_* constants for each possible button, which will be reported in the event’s button_index property. Note that the scrollwheel also counts as a button - two buttons, to be precise, with both BUTTON_WHEEL_UP and BUTTON_WHEEL_DOWN being separate events.Capturing the Mousehttps://godotrecipes.com/godot_recipes/4.x/input/mouse_capture/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/input/mouse_capture/index.htmlProblem You want to hide the mouse cursor and keep the mouse from leaving the game window. This is common in many 3D games (and some 2D ones). +InputEventMouseButton @GlobalScope.ButtonList contains a list of BUTTON_* constants for each possible button, which will be reported in the event’s button_index property. Note that the scrollwheel also counts as a button - two buttons, to be precise, with both BUTTON_WHEEL_UP and BUTTON_WHEEL_DOWN being separate events.Adding Input Actions in codehttps://godotrecipes.com/godot_recipes/4.x/input/custom_actions/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/input/custom_actions/index.htmlProblem You need to add actions to the InputMap at runtime. +Solution Typically, you&rsquo;ll add actions to the InputMap via Project Settings, as shown in Recipe: Input Actions. However, you may find yourself needing to add one or more actions directly in a script. The InputMap singleton has methods to help you do this. +Here&rsquo;s an example that would add a new action called &ldquo;attack&rdquo; using the space key: +func _ready(): InputMap.Capturing the Mousehttps://godotrecipes.com/godot_recipes/4.x/input/mouse_capture/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/input/mouse_capture/index.htmlProblem You want to hide the mouse cursor and keep the mouse from leaving the game window. This is common in many 3D games (and some 2D ones). Solution You can set the mouse state using Input.mouse_mode. There are four possible mouse modes: MOUSE_MODE_VISIBLE: The mouse is visible and can move freely into and out of the window. This is the default state. MOUSE_MODE_HIDDEN: The mouse cursor is invisible, but the mouse can still move outside the window.Mouse: Drag-select multiple unitshttps://godotrecipes.com/godot_recipes/4.x/input/multi_unit_select/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/input/multi_unit_select/index.htmlProblem You want to click-and-drag to select multiple units, RTS style. diff --git a/docs/4.x/input/input_actions/index.html b/docs/4.x/input/input_actions/index.html index 62b0a7f5..6631b198 100644 --- a/docs/4.x/input/input_actions/index.html +++ b/docs/4.x/input/input_actions/index.html @@ -1,5 +1,5 @@ -Input Actions :: Godot 4 Recipes - +Input Actions :: Godot 4 Recipes +

    Input Actions

    Problem

    You want to understand Godot’s “input action” system.

    Solution

    Let’s say you’re making a top-down character and you write code using InputActionKey that uses the arrow keys for movement. You’ll quickly find that many players prefer to use “WASD” style controls. You can go back into your code and add the additional key checks, but this would result in duplicated/redundant code.

    Input actions can help to make your code more configurable. Rather than hard-coding specific keys, you’ll be able to modify and customize them without changing the code.

    Creating inputs

    You define input actions in the “Project Settings” under the “Input Map” tab. Here, you can create new actions and/or assign inputs to them.

    You’ll see when you click on the tab there are already some default actions configured. They are all named “ui_*” to indicate that they are the default interface actions. “Tab” for next UI element, for example.

    Generally speaking, you should create your own actions for your game, rather than use the existing ones.

    For this example, let’s say you want to allow the player to control the game with the keyboard or the mouse. They need to be able to shoot by pressing either the left mouse button or the spacebar.

    Create the new action “shoot” by typing the name in the “Action” field at the top and clicking “Add” (or pressing enter). Scroll to the bottom and you’ll see the new action has been added to the list.

    Now you can assign inputs to this action by clicking the “+” sign to the right. Inputs can be keys, mouse buttons, or joy/gamepad inputs. Choose “Key” and you can press the key on the keyboard you want to assign - let’s press the spacebar - and click “OK”.

    Click “+” to add another input, and this time choose “Mouse Button”. The default of “Device 0” and “Left Button” is fine, but you can select others if you like.

    Using input actions

    You can check for the action either by polling the Input singleton every frame:

    func _process(delta):
         if Input.is_action_pressed("shoot"):
    @@ -10,15 +10,17 @@
     

    There are several functions you can use for checking input state:

    • is_action_pressed(): This function returns true if the action is currently in the pressed state.

    • is_action_released(): This function returns true if the action is not In the pressed state.

    • is_action_just_pressed() / is_action_just_released(): These methods work like the above, but only return true on the single frame after the event occurs. This is useful for non-recurring actions like shooting or jumping where the user needs to let go and then press the key again to repeat the action.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/input/mouse_capture/index.html b/docs/4.x/input/mouse_capture/index.html index 7f59ddc6..18a3888e 100644 --- a/docs/4.x/input/mouse_capture/index.html +++ b/docs/4.x/input/mouse_capture/index.html @@ -1,5 +1,5 @@ -Capturing the Mouse :: Godot 4 Recipes - +Capturing the Mouse :: Godot 4 Recipes +

    Capturing the Mouse

    Problem

    You want to hide the mouse cursor and keep the mouse from leaving the game window. This is common in many 3D games (and some 2D ones).

    Solution

    You can set the mouse state using Input.mouse_mode. There are four possible mouse modes:

    • MOUSE_MODE_VISIBLE: The mouse is visible and can move freely into and out of the window. This is the default state.

    • MOUSE_MODE_HIDDEN: The mouse cursor is invisible, but the mouse can still move outside the window.

    • MOUSE_MODE_CAPTURED: The mouse cursor is hidden and the mouse is unable to leave the game window.

    • MOUSE_MODE_CONFINED: The mouse is visible, but cannot leave the game window.

    “Captured” is the most commonly used option. You can set the mouse mode at runtime using:

    func _ready():
         Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
    @@ -14,15 +14,17 @@
     
    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/input/mouse_input/index.html b/docs/4.x/input/mouse_input/index.html index bd07f86e..208f2260 100644 --- a/docs/4.x/input/mouse_input/index.html +++ b/docs/4.x/input/mouse_input/index.html @@ -1,5 +1,5 @@ -Mouse Input :: Godot 4 Recipes - +Mouse Input :: Godot 4 Recipes +

    Mouse Input

    Problem

    You want to detect mouse input.

    Solution

    InputEventMouse is the base class for mouse events. It contains position and global_position properties. Inheriting from it are two classes: InputEventMouseButton and InputEventMouseMotion.

    Note

    You can assign mouse button events in the InputMap, so you can use them with is_action_pressed().

    InputEventMouseButton

    @GlobalScope.ButtonList contains a list of BUTTON_* constants for each possible button, which will be reported in the event’s button_index property. Note that the scrollwheel also counts as a button - two buttons, to be precise, with both BUTTON_WHEEL_UP and BUTTON_WHEEL_DOWN being separate events.

    Tip

    Unlike regular buttons, mouse wheel clicks only produce pressed events. There is no concept of a mouse wheel click being “released”.

    func _unhandled_input(event):
         if event is InputEventMouseButton:
    @@ -19,15 +19,17 @@
     
    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/input/multi_unit_select/index.html b/docs/4.x/input/multi_unit_select/index.html index 6a7df3db..b2828e48 100644 --- a/docs/4.x/input/multi_unit_select/index.html +++ b/docs/4.x/input/multi_unit_select/index.html @@ -1,8 +1,8 @@ -Mouse: Drag-select multiple units :: Godot 4 Recipes - +Mouse: Drag-select multiple units :: Godot 4 Recipes +

    Mouse: Drag-select multiple units

    Problem

    You want to click-and-drag to select multiple units, RTS style.

    Solution

    Realtime strategy (RTS) games often require giving orders to many units at once. A typical style of selecting multiple units is to click-and-drag a box around them. Once the units are selected, clicking on the map commands them to move.

    Here’s an example of what we’re going for:

    alt -alt

    Unit setup

    To test this out, we’ll need some basic RTS-style units. They are set up to move towards a target and to avoid running into each other. We won’t go into too much detail on them in this tutorial. The unit script is commented if you’d like to use it as a base for creating your own RTS units. See below for a link to download the project.

    World setup

    Processing the unit selection will happen in the world. We’ll start with a Node2D called “World” and add a few Unit instances in it. Attach a script to the World node and add the following variables:

    extends Node2D
    +

    Mouse: Drag-select multiple units

    Problem

    You want to click-and-drag to select multiple units, RTS style.

    Solution

    Realtime strategy (RTS) games often require giving orders to many units at once. A typical style of selecting multiple units is to click-and-drag a box around them. Once the units are selected, clicking on the map commands them to move.

    Here’s an example of what we’re going for:

    alt +alt

    Unit setup

    To test this out, we’ll need some basic RTS-style units. They are set up to move towards a target and to avoid running into each other. We won’t go into too much detail on them in this tutorial. The unit script is commented if you’d like to use it as a base for creating your own RTS units. See below for a link to download the project.

    World setup

    Processing the unit selection will happen in the world. We’ll start with a Node2D called “World” and add a few Unit instances in it. Attach a script to the World node and add the following variables:

    extends Node2D
     
     var dragging = false  # Are we currently dragging?
     var selected = []  # Array of selected units.
    @@ -41,8 +41,8 @@
     { "rid": RID(4123168604162), "collider_id": 32229033411, "collider": Unit3:<CharacterBody2D#32229033411>, "shape": 0 }]
     

    Each of those collider items is a reference to a unit, so we can use this to notify them that they’ve been selected, activating the outline shader:

        for item in selected:
             item.collider.selected = true
    -

    alt -alt

    Commanding the units

    Finally, we can command the selected units to move by clicking somewhere on the screen:

    func _unhandled_input(event):
    +

    alt +alt

    Commanding the units

    Finally, we can command the selected units to move by clicking somewhere on the screen:

    func _unhandled_input(event):
         if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
             if event.pressed:
                 # If the mouse was clicked and nothing is selected, start dragging
    @@ -58,15 +58,17 @@
     

    The else clause here triggers if we click the mouse when selected is greater than 0. Each item’s target is set, and we make sure to deselect the units so we can start again.

    Wrapping up

    This technique can be expanded to a wide range of RTS or other game styles. Download the full project below and use it as a base for your own game.

    Download This Project

    Download the project code here: https://github.com/godotrecipes/multi_unit_select

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/kyn/index.html b/docs/4.x/kyn/index.html index 1e9ff3c7..22283727 100644 --- a/docs/4.x/kyn/index.html +++ b/docs/4.x/kyn/index.html @@ -1,17 +1,19 @@ -Know Your Nodes :: Godot 4 Recipes - +Know Your Nodes :: Godot 4 Recipes +

     Know Your Nodes

    In the “Know Your Nodes” series, we go in-depth with a single one of Godot’s nodes. Learn what makes it tick and see some examples of how it’s used.

    In this section:

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/kyn/raycast2d/index.html b/docs/4.x/kyn/raycast2d/index.html index afef14d5..063466e1 100644 --- a/docs/4.x/kyn/raycast2d/index.html +++ b/docs/4.x/kyn/raycast2d/index.html @@ -1,14 +1,14 @@ -RayCast2D :: Godot 4 Recipes - +RayCast2D :: Godot 4 Recipes +

    RayCast2D

    RayCast2D

    Raycasting is a common technique in game development. “Casting a ray” means extending a line from a point until it collides with something or reaches its limit.

    Node properties

    Add a RayCast2D node and take a look at the Inspector:

    alt -alt

    Here are the main properties you’ll need to understand:

    • Enabled

    Turn this off to disabled the raycast work.

    • Exclude Parent

    This property causes the ray to ignore collisions with the parent object. Enabled by default.

    • Target Position

    This is the destination point of the ray. Note: This is in local coordinates.

    Also, take note of the Collide With section. By default the ray will only detect bodies, so you’ll need to go here if you want to detect areas as well or instead.

    Useful functions

    You can see the full list of the node’s functions in the API Documentation. Here are the some of the most useful ones:

    • is_colliding()

    Boolean function, lets you know if the ray is colliding with something.

    • get_collision_point()

    If the ray is colliding, this will return the position of the collision (in global coordinates).

    • get_collider()

    If the ray is colliding, this function will return a reference to the colliding object.

    • get_collision_normal()

    Another useful piece of information, this is the normal of the collided object at the point of collision.

    Example uses

    There are many uses for raycasts: visibility (can A see B, or is there an obstacle between?), proximity (am I close to a wall/ground/obstacle?), etc. Here are a couple of practical examples in use:

    1. Shooting

    Fast-moving projectiles often have the problem of “tunneling” through obstacles - they are moving too fast for the collision to be detected in a single frame. As an alternative, you can use a Raycast2D to represent the path (or a laser, etc.).

    Here’s a player sprite with a raycast attached to the end of the gun. The target_position is set to (250, 0).

    alt -alt

    When the player shoots, you check to see if the ray is colliding with something:

    func _input(event):
    +

    RayCast2D

    RayCast2D

    Raycasting is a common technique in game development. “Casting a ray” means extending a line from a point until it collides with something or reaches its limit.

    Node properties

    Add a RayCast2D node and take a look at the Inspector:

    alt +alt

    Here are the main properties you’ll need to understand:

    • Enabled

    Turn this off to disabled the raycast work.

    • Exclude Parent

    This property causes the ray to ignore collisions with the parent object. Enabled by default.

    • Target Position

    This is the destination point of the ray. Note: This is in local coordinates.

    Also, take note of the Collide With section. By default the ray will only detect bodies, so you’ll need to go here if you want to detect areas as well or instead.

    Useful functions

    You can see the full list of the node’s functions in the API Documentation. Here are the some of the most useful ones:

    • is_colliding()

    Boolean function, lets you know if the ray is colliding with something.

    • get_collision_point()

    If the ray is colliding, this will return the position of the collision (in global coordinates).

    • get_collider()

    If the ray is colliding, this function will return a reference to the colliding object.

    • get_collision_normal()

    Another useful piece of information, this is the normal of the collided object at the point of collision.

    Example uses

    There are many uses for raycasts: visibility (can A see B, or is there an obstacle between?), proximity (am I close to a wall/ground/obstacle?), etc. Here are a couple of practical examples in use:

    1. Shooting

    Fast-moving projectiles often have the problem of “tunneling” through obstacles - they are moving too fast for the collision to be detected in a single frame. As an alternative, you can use a Raycast2D to represent the path (or a laser, etc.).

    Here’s a player sprite with a raycast attached to the end of the gun. The target_position is set to (250, 0).

    alt +alt

    When the player shoots, you check to see if the ray is colliding with something:

    func _input(event):
         if event.is_action_pressed("shoot"):
             if $RayCast2D.is_colliding():
                 print($RayCast2D.get_collider().name)
    -

    2. Edge detection

    Consider a platformer enemy that walks on platforms, but you don’t want it to fall off the edges. Add two downward-pointing raycasts to the mob like so:

    alt -alt

    In the mob’s script, check for when the ray stops colliding. That means you’ve found the edge and should turn around:

    func _physics_process(delta):
    +

    2. Edge detection

    Consider a platformer enemy that walks on platforms, but you don’t want it to fall off the edges. Add two downward-pointing raycasts to the mob like so:

    alt +alt

    In the mob’s script, check for when the ray stops colliding. That means you’ve found the edge and should turn around:

    func _physics_process(delta):
         velocity.y += gravity * delta
         if not $RayRight.is_colliding():
             dir = -1
    @@ -17,19 +17,21 @@
         velocity.x = dir * speed
         $AnimatedSprite.flip_h = velocity.x > 0
         velocity = move_and_slide(velocity, Vector2.UP)
    -

    Here’s what it looks like in action:

    alt -alt

    + + \ No newline at end of file diff --git a/docs/4.x/math/dot_cross_product/index.html b/docs/4.x/math/dot_cross_product/index.html index f49f5c9d..40a742e2 100644 --- a/docs/4.x/math/dot_cross_product/index.html +++ b/docs/4.x/math/dot_cross_product/index.html @@ -1,24 +1,26 @@ -Vectors: Using Dot and Cross Product :: Godot 4 Recipes - +Vectors: Using Dot and Cross Product :: Godot 4 Recipes +

    Vectors: Using Dot and Cross Product

    Problem

    You’d like to understand what is meant by dot product and cross product.

    Solution

    In this recipe we’ll introduce the concept of vector dot product and cross product and how they might be used.

    Dot product

    Dot product is an operation on two vectors that returns a scalar. It is often visualized as the projection of vector A onto vector B:

    alt -alt

    This is the formula for calculating the dot product:

    alt -alt

    Where θ is the angle between the two vectors and ||A|| is the magnitude of A.

    This is very useful when both vectors are normalized (i.e. their magnitudes are 1), then the formula simplifies to:

    alt -alt

    This shows that the dot product is directly related to the angle between the two vectors. Since cos(0) == 1 and cos(180) == -1, the result of the dot product can tell you how closely aligned two vectors are:

    alt -alt

    See below for how we can apply this fact in a practical example.

    Cross product

    The cross product of two vectors is a third vector that is perpendicular to both of them. Its magnitude is related to their magnitudes and the angle between them.

    alt -alt

    Once again, if we’re using normalized vectors, the result is simplified: it will be directly related to the angle and its magnitude will range from -1 to 1.

    Note

    Since the cross product is perpendicular to both vectors, we would need to be working in 3D. In most 2D frameworks, including Godot, the 2D Vector2.cross() method returns a scalar value representing the result’s magnitude.

    Practical applications

    Consider this animation, showing how the results of Vector2.dot() and Vector2.cross() change in relation to the changing angle:

    alt -alt

    This demonstrates two common applications of these methods. If the red vector is our object’s forward direction, and the green shows the direction towards another object:

    • Dot product: Using the result, we can tell if the object is in front of (> 0) or behind (< 0) us.
    • Cross product: Using the result, we can tell if the object is to the left (> 0) or right (< 0).
    + + \ No newline at end of file diff --git a/docs/4.x/math/index.html b/docs/4.x/math/index.html index a7a1fa1c..3625ebde 100644 --- a/docs/4.x/math/index.html +++ b/docs/4.x/math/index.html @@ -1,20 +1,22 @@ -Gamedev Math :: Godot 4 Recipes - +Gamedev Math :: Godot 4 Recipes +

     Gamedev Math

    Math is a big part of game development. Some of it you may remember from school, or it may be something you’ve never encountered before. Here you’ll find guides to help you get up to speed and examples of how these concepts are applied to making games.

    In this section:

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/math/interpolation/index.html b/docs/4.x/math/interpolation/index.html index 9b480910..5371d436 100644 --- a/docs/4.x/math/interpolation/index.html +++ b/docs/4.x/math/interpolation/index.html @@ -1,5 +1,5 @@ -Interpolation :: Godot 4 Recipes - +Interpolation :: Godot 4 Recipes +

    Interpolation

    Linear Interpolation, or its commonly-used abbreviation lerp, is a term that comes up often in game development. If you’ve never come across it before it can seem mysterious and highly-technical, but as you’ll see in this tutorial, it’s actually a straightforward concept with a wide variety of applications in game programming.

    Numeric Interpolation

    The core formula for linear interpolation is this:

    func lerp(a, b, t):
         return (1 - t) * a + t * b
    @@ -34,15 +34,17 @@
     

    For more advanced applications of interpolation, see Tween.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/math/transforms/index.html b/docs/4.x/math/transforms/index.html index 10d7e04e..a990d5ea 100644 --- a/docs/4.x/math/transforms/index.html +++ b/docs/4.x/math/transforms/index.html @@ -1,38 +1,40 @@ -Transforms :: Godot 4 Recipes - +Transforms :: Godot 4 Recipes +

    Transforms

    Before reading this, make sure you have an understanding of vectors and how they’re used in game development. If you don’t, I recommend you read this introduction I wrote for the Godot documentation: -Vector Math.

    2D Transforms

    In 2D space, we use the familiar X-Y coordinate plane. Remember that in Godot, as in most computer graphics applications, the Y axis points downward:

    alt -alt

    To begin, let’s consider this spaceship floating in space:

    alt -alt

    The ship is pointing in the same direction as the X axis. If we wanted it to move forward, we could add to its X coordinate and it would move to the right:

    position += Vector2(10, 0)
    -

    But what happens when the ship rotates?

    alt -alt

    How do we move the ship forward now? If you remember Trigonometry from school, you might be starting to think about angles, sine and cosine and doing something like position += Vector2(10 * cos(angle), 10 * sin(angle)). While this would work, there’s a much more convenient way: the Transform.

    Let’s look at the rotated ship again, but this time, let’s also imagine that the ship has its own X and Y axes that it carries with it, independent of the global axes:

    alt -alt

    These “local” axes are contained in the object’s transform.

    Knowing this, we can move the ship forward by moving it along its own X axis and we won’t have to worry about angles and trig functions. To do this in Godot, we can use the transform property, which is available to all Node2D derived nodes.

        position += transform.x * 10
    -

    This code says “Add the transform’s x vector multiplied by 10.” Let’s break down what that means. The transform contains x and y properties that represent those local axes. They are unit vectors, which means their length is 1. Another term for unit vector is direction vector. They tell us the direction the ship’s x axis is pointing. We then multiply by 10 to scale it to a longer distance.

    Tip

    The transform property of a node is relative to its parent node. If you need to get the global value, it’s available in global_transform.

    In addition to the local axes, the transform also contains a component called the origin. The origin represents the translation, or change in position.

    In this picture, the blue vector is the transform.origin. It is equal to the object’s position vector.

    alt -alt

    Converting Between Local and Global Space

    You can convert coordinates from local to global by applying the transform. For convenience, Node2D and Spatial include helper functions for this: to_local() and to_global():

        var global_position = to_global(local_position)
    +Vector Math.

    2D Transforms

    In 2D space, we use the familiar X-Y coordinate plane. Remember that in Godot, as in most computer graphics applications, the Y axis points downward:

    alt +alt

    To begin, let’s consider this spaceship floating in space:

    alt +alt

    The ship is pointing in the same direction as the X axis. If we wanted it to move forward, we could add to its X coordinate and it would move to the right:

    position += Vector2(10, 0)
    +

    But what happens when the ship rotates?

    alt +alt

    How do we move the ship forward now? If you remember Trigonometry from school, you might be starting to think about angles, sine and cosine and doing something like position += Vector2(10 * cos(angle), 10 * sin(angle)). While this would work, there’s a much more convenient way: the Transform.

    Let’s look at the rotated ship again, but this time, let’s also imagine that the ship has its own X and Y axes that it carries with it, independent of the global axes:

    alt +alt

    These “local” axes are contained in the object’s transform.

    Knowing this, we can move the ship forward by moving it along its own X axis and we won’t have to worry about angles and trig functions. To do this in Godot, we can use the transform property, which is available to all Node2D derived nodes.

        position += transform.x * 10
    +

    This code says “Add the transform’s x vector multiplied by 10.” Let’s break down what that means. The transform contains x and y properties that represent those local axes. They are unit vectors, which means their length is 1. Another term for unit vector is direction vector. They tell us the direction the ship’s x axis is pointing. We then multiply by 10 to scale it to a longer distance.

    Tip

    The transform property of a node is relative to its parent node. If you need to get the global value, it’s available in global_transform.

    In addition to the local axes, the transform also contains a component called the origin. The origin represents the translation, or change in position.

    In this picture, the blue vector is the transform.origin. It is equal to the object’s position vector.

    alt +alt

    Converting Between Local and Global Space

    You can convert coordinates from local to global by applying the transform. For convenience, Node2D and Spatial include helper functions for this: to_local() and to_global():

        var global_position = to_global(local_position)
     

    Let’s use the example of an object in the 2D plane and convert mouse clicks (global space) into coordinates relative to the object:

    extends Sprite
     
     func _unhandled_input(event):
         if event is InputEventMouseButton and event.pressed:
             if event.button_index == BUTTON_LEFT:
                 printt(event.position, to_local(event.position))
    -

    See the Transform2D docs for a list of the available properties and methods.

    3D Transforms

    In 3D space, the concept of transforms applies in the same way as in 2D. In fact, it becomes even more necessary, as using angles in 3D can lead to a variety of problems, as we’ll see in a bit.

    3D nodes inherit from the base node Node3D, which contains the transform information. The 3D transform requires more information than the 2D version. Position is still held in the origin property, but rotation is in a property called basis, which contains three unit vectors representing the body’s local X, Y, and Z axes.

    When you select a 3D node in the editor, the gizmo that appears allows you to manipulate the transform.

    alt -alt

    Local Space Mode

    In the editor, you can see and manipulate the body’s local orientation by clicking the “Local Space Mode” button. -alt -alt +

    See the Transform2D docs for a list of the available properties and methods.

    3D Transforms

    In 3D space, the concept of transforms applies in the same way as in 2D. In fact, it becomes even more necessary, as using angles in 3D can lead to a variety of problems, as we’ll see in a bit.

    3D nodes inherit from the base node Node3D, which contains the transform information. The 3D transform requires more information than the 2D version. Position is still held in the origin property, but rotation is in a property called basis, which contains three unit vectors representing the body’s local X, Y, and Z axes.

    When you select a 3D node in the editor, the gizmo that appears allows you to manipulate the transform.

    alt +alt

    Local Space Mode

    In the editor, you can see and manipulate the body’s local orientation by clicking the “Local Space Mode” button. +alt +alt When in this mode, the 3 colored axis lines represent the body’s local basis axes.

    As in 2D, we can use the local axes to move an object forward. In Godot’s 3D orientation (Y-up), this means that by default the body’s -Z axis is the forward direction. To move forward:

        position += -transform.basis.z * speed * delta
     
    Tip

    Godot has default vector values defined, for example: Vector3.FORWARD == Vector3(0, 0, -1). See Vector2 and Vector3 for details.

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/physics/asteroids_physics/index.html b/docs/4.x/physics/asteroids_physics/index.html index dc07d6ad..0dca84f8 100644 --- a/docs/4.x/physics/asteroids_physics/index.html +++ b/docs/4.x/physics/asteroids_physics/index.html @@ -1,5 +1,5 @@ -Asteroids-style Physics (using RigidBody2D) :: Godot 4 Recipes - +Asteroids-style Physics (using RigidBody2D) :: Godot 4 Recipes +

    Asteroids-style Physics (using RigidBody2D)

    Problem

    You want to use a RigidBody2D to create a semi-realistic spaceship, a la Asteroids.

    Solution

    Using RigidBody2D can be tricky. Because it’s controlled by Godot’s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc, and we’ll refer to it as we work through this example.

    For this example, we’ll use the following node setup:

     RigidBody2D (Ship)
           Sprite2D
    @@ -26,26 +26,28 @@
         xform.origin.x = wrapf(xform.origin.x, 0, screensize.x)
         xform.origin.y = wrapf(xform.origin.y, 0, screensize.y)
         state.transform = xform
    -

    As you can see, the _integrate_forces() function includes a parameter called state. This object is the PhysicsDirectBodyState2D of our body. It contains all of the current physics properties such as the forces, velocity, position, etc.

    From the state, we grab the current transform, modify it to wrap around the screen using wrapf(), and then set it back to the current state.

    Here’s how it looks:

    alt -alt

    Warping

    Let’s look at one more example of using _integrate_forces() to alter the body’s state without issues. Let’s add a “warp” mechanic - when the player presses the "warp" input, the ship will teleport to a random spot on the screen.

    First, we’ll add a new variable for this:

    var teleport_pos = null
    +

    As you can see, the _integrate_forces() function includes a parameter called state. This object is the PhysicsDirectBodyState2D of our body. It contains all of the current physics properties such as the forces, velocity, position, etc.

    From the state, we grab the current transform, modify it to wrap around the screen using wrapf(), and then set it back to the current state.

    Here’s how it looks:

    alt +alt

    Warping

    Let’s look at one more example of using _integrate_forces() to alter the body’s state without issues. Let’s add a “warp” mechanic - when the player presses the "warp" input, the ship will teleport to a random spot on the screen.

    First, we’ll add a new variable for this:

    var teleport_pos = null
     

    Then, in get_input(), we’ll set a random position:

        if Input.is_action_just_pressed("warp"):
             teleport_pos = Vector2(randf_range(0, screensize.x), randf_range(0, screensize.y))
     

    Finally, in _integrate_forces(), if there’s a teleport_position set, we’ll use it and then clear it:

        if teleport_pos:
             physics_state.transform.origin = teleport_pos
             teleport_pos = null
    -

    alt -alt

    Download This Project

    Download the project’s example code here: https://github.com/godotrecipes/asteroids_physics

    + + \ No newline at end of file diff --git a/docs/4.x/physics/character_vs_rigid/index.html b/docs/4.x/physics/character_vs_rigid/index.html index d0da6e81..b74c3146 100644 --- a/docs/4.x/physics/character_vs_rigid/index.html +++ b/docs/4.x/physics/character_vs_rigid/index.html @@ -1,12 +1,12 @@ -Character to Rigid Body Interaction :: Godot 4 Recipes - +Character to Rigid Body Interaction :: Godot 4 Recipes +

    Character to Rigid Body Interaction

    Problem

    You want your character body to interact with rigid bodies.

    Solution

    Note

    This recipe applies equally well in both 2D and 3D nodes.

    By default, a CharacterBody2D moved with move_and_slide() or move_and_collide() will not push any RigidBody2D it collides with. The rigid body doesn’t react at all, and behaves just like a StaticBody2D.

    alt -alt

    In some cases, this might be all you need. However, if you want to be able to push the bodies, you’ll need to make some changes.

    For this example, we’ll use the 2D character described in the Platform character recipe. This example uses the most common movement method for character bodies: move_and_slide(). If you’re using move_and_collide(), you’ll need to adjust the examples below accordingly.

    You have two options when deciding how to interact with rigid bodies:

    1. You can just push them, ignoring physics. If you’re familiar with Godot 3.x, this is equivalent to the “infinite inertia” option.
    2. You can give them a push based on the character’s imagined “mass” and velocity. This will give you a “realistic” result - pushing heavy bodies a little, and lighter bodies a lot.

    We’ll try out both options below.

    Infinite Inertia

    This option has its pros and cons. The biggest pro is, you don’t need any extra code. You just need to correctly set the collision layers/masks of the objects. For this example, we’ve defined three physics layers:

    alt -alt

    For the rigid body, we’ve placed it on the “items” layer (layer 3), and left the mask at the default (masking all layers):

    alt -alt

    Then, we’ve placed the player on the “player” layer (layer 2), and configured the mask to ignore the “items”:

    alt -alt

    Running the game, we now see we can push the boxes around. Note that the mass of the box doesn’t matter - they’ll all be pushed the same.

    alt -alt

    Here, you can also see the downside of this option. Because the physics of the boxes is being ignored, they can clip through walls and you can’t jump on top of them.

    For some games, this will be fine. If you want to prevent the clipping, you’ll need to go with option 2.

    Applying impulses

    To give the colliding body a “push” we’ll need to apply an impulse. An impulse is an instantaneous “kick” - think of a bat hitting a ball. This is as opposed to a force, which is a continuous “push” on an object.

    # This represents the player's inertia.
    +

    Character to Rigid Body Interaction

    Problem

    You want your character body to interact with rigid bodies.

    Solution

    Note

    This recipe applies equally well in both 2D and 3D nodes.

    By default, a CharacterBody2D moved with move_and_slide() or move_and_collide() will not push any RigidBody2D it collides with. The rigid body doesn’t react at all, and behaves just like a StaticBody2D.

    alt +alt

    In some cases, this might be all you need. However, if you want to be able to push the bodies, you’ll need to make some changes.

    For this example, we’ll use the 2D character described in the Platform character recipe. This example uses the most common movement method for character bodies: move_and_slide(). If you’re using move_and_collide(), you’ll need to adjust the examples below accordingly.

    You have two options when deciding how to interact with rigid bodies:

    1. You can just push them, ignoring physics. If you’re familiar with Godot 3.x, this is equivalent to the “infinite inertia” option.
    2. You can give them a push based on the character’s imagined “mass” and velocity. This will give you a “realistic” result - pushing heavy bodies a little, and lighter bodies a lot.

    We’ll try out both options below.

    Infinite Inertia

    This option has its pros and cons. The biggest pro is, you don’t need any extra code. You just need to correctly set the collision layers/masks of the objects. For this example, we’ve defined three physics layers:

    alt +alt

    For the rigid body, we’ve placed it on the “items” layer (layer 3), and left the mask at the default (masking all layers):

    alt +alt

    Then, we’ve placed the player on the “player” layer (layer 2), and configured the mask to ignore the “items”:

    alt +alt

    Running the game, we now see we can push the boxes around. Note that the mass of the box doesn’t matter - they’ll all be pushed the same.

    alt +alt

    Here, you can also see the downside of this option. Because the physics of the boxes is being ignored, they can clip through walls and you can’t jump on top of them.

    For some games, this will be fine. If you want to prevent the clipping, you’ll need to go with option 2.

    Applying impulses

    To give the colliding body a “push” we’ll need to apply an impulse. An impulse is an instantaneous “kick” - think of a bat hitting a ball. This is as opposed to a force, which is a continuous “push” on an object.

    # This represents the player's inertia.
     var push_force = 80.0
     
     func _physics_process(delta):
    @@ -15,19 +15,21 @@
             var c = get_slide_collision(i)
             if c.get_collider() is RigidBody2D:
                 c.get_collider().apply_central_impulse(-c.get_normal() * push_force)
    -

    The collision normal points out of the rigid body, so we reverse it to point away from the character and apply the push_force factor. Now pushing works again, but it won’t force the rigid bodies through walls:

    alt -alt

    You’ll need to adjust the push_force in relation to the mass of your rigid bodies. Too high a force will still cause clipping, while too low will prevent pushing at all.

    Experiment to find the settings that work for your particular game.

    Download This Project

    Download the project’s example code here: https://github.com/godotrecipes/character_vs_rigid

    + + \ No newline at end of file diff --git a/docs/4.x/physics/index.html b/docs/4.x/physics/index.html index 201ef325..78741bee 100644 --- a/docs/4.x/physics/index.html +++ b/docs/4.x/physics/index.html @@ -1,17 +1,19 @@ -Physics :: Godot 4 Recipes - -
    + + \ No newline at end of file diff --git a/docs/4.x/physics/index.xml b/docs/4.x/physics/index.xml index 07fb29ee..b3f98ad3 100644 --- a/docs/4.x/physics/index.xml +++ b/docs/4.x/physics/index.xml @@ -1,4 +1,6 @@ -Physics on Godot 4 Recipeshttps://godotrecipes.com/godot_recipes/4.x/physics/index.htmlRecent content in Physics on Godot 4 RecipesHugo -- gohugo.ioen-usRigidBody2D: Drag and Drophttps://godotrecipes.com/godot_recipes/4.x/physics/rigidbody_drag_drop/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/physics/rigidbody_drag_drop/index.htmlProblem You want to pick up and move rigid bodies with the mouse. +Physics on Godot 4 Recipeshttps://godotrecipes.com/godot_recipes/4.x/physics/index.htmlRecent content in Physics on Godot 4 RecipesHugo -- gohugo.ioen-usRigidBody2D: Look at Targethttps://godotrecipes.com/godot_recipes/4.x/physics/smooth_rigid_rotate/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/physics/smooth_rigid_rotate/index.htmlProblem You want a rigid body to rotate smoothly to look at a target. +Solution Using RigidBody2D can be tricky. Because it&rsquo;s controlled by Godot&rsquo;s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc. +To rotate a body, we need to apply a rotational force - a torque. Once the body is rotating, we want the torque to get smaller as we get closer to the final rotation.RigidBody2D: Drag and Drophttps://godotrecipes.com/godot_recipes/4.x/physics/rigidbody_drag_drop/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/physics/rigidbody_drag_drop/index.htmlProblem You want to pick up and move rigid bodies with the mouse. Solution Working with rigid bodies can be tricky. Godot&rsquo;s physics engine controls their movements, and interfering with that can often lead to unexpected results. The key is to make use of the body&rsquo;s mode property. This applies equally well in 2D or 3D. Body setup We&rsquo;ll start with our rigid body object, adding a Sprite2D and CollisionShape2D. You can also add a PhysicsMaterial if you want to set Bounce and Friction properties.Character to Rigid Body Interactionhttps://godotrecipes.com/godot_recipes/4.x/physics/character_vs_rigid/index.htmlMon, 01 Jan 0001 00:00:00 +0000https://godotrecipes.com/godot_recipes/4.x/physics/character_vs_rigid/index.htmlProblem You want your character body to interact with rigid bodies. Solution Note This recipe applies equally well in both 2D and 3D nodes. diff --git a/docs/4.x/physics/rigidbody_drag_drop/index.html b/docs/4.x/physics/rigidbody_drag_drop/index.html index d292887b..dadb45b6 100644 --- a/docs/4.x/physics/rigidbody_drag_drop/index.html +++ b/docs/4.x/physics/rigidbody_drag_drop/index.html @@ -1,5 +1,5 @@ -RigidBody2D: Drag and Drop :: Godot 4 Recipes - +RigidBody2D: Drag and Drop :: Godot 4 Recipes +

    RigidBody2D: Drag and Drop

    Problem

    You want to pick up and move rigid bodies with the mouse.

    Solution

    Working with rigid bodies can be tricky. Godot’s physics engine controls their movements, and interfering with that can often lead to unexpected results. The key is to make use of the body’s mode property. This applies equally well in 2D or 3D.

    Body setup

    We’ll start with our rigid body object, adding a Sprite2D and CollisionShape2D. You can also add a PhysicsMaterial if you want to set Bounce and Friction properties.

    We’re going to use the rigid body’s freeze property to remove it from the control of the physics engine while we’re dragging it. Since we still want it to be movable, we need to set the Freeze Mode to “Kinematic”, rather than the default value of “Static”.

    Place the body in a group called “pickable”. We’ll use this to allow for multiple instances of the pickable object in the main scene. Attach a script to the body and connect the its _input_event signal.

    extends RigidBody2D
     
    @@ -45,15 +45,17 @@
     

    Note the use of get_last_mouse_velocity() to pass the impulse to the object - be careful with this! You may find yourself launching the rigid bodies at high speeds, especially if the bodies have low mass values. It’s probably a good idea to scale this to a reasonable value and clamp() it to some maximum. Experiment to find out what works for you.

    Download This Project

    Download the project code here: https://github.com/godotrecipes/rigidbody_drag_drop

    - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/physics/smooth_rigid_rotate/index.html b/docs/4.x/physics/smooth_rigid_rotate/index.html new file mode 100644 index 00000000..b142cf42 --- /dev/null +++ b/docs/4.x/physics/smooth_rigid_rotate/index.html @@ -0,0 +1,28 @@ +RigidBody2D: Look at Target :: Godot 4 Recipes + +

    RigidBody2D: Look at Target

    Problem

    You want a rigid body to rotate smoothly to look at a target.

    Solution

    Using RigidBody2D can be tricky. Because it’s controlled by Godot’s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc.

    To rotate a body, we need to apply a rotational force - a torque. Once the body is rotating, we want the torque to get smaller as we get closer to the final rotation.

    This is the perfect situation to use the dot product. Its sign will tell us whether the target is to the left/right, and its magnitude will tell us how far away from the target direction we’re pointing.

    Tip

    See Vectors: Using Dot and Cross Product for a brief review of the dot product.

    extends RigidBody2D
    +
    +var angular_force = 50000
    +var target = position + Vector2.RIGHT
    +
    +func _physics_process(delta):
    +    var dir = transform.y.dot(position.direction_to(target))
    +    constant_torque = dir * angular_force
    +

    You may be wondering why we’re using the transform.y here, when transform.x is the body’s forward vector. Using transform.x, the dot product would be at its maximum when the body is directly pointing at the target, but we want the torque to be zero at that point. Using transform.y means that our torque will be higher when we’re not aligned with the target.

    Skip the Rigid Body Entirely

    You can avoid all of this entirely by not rotating your rigid body at all! Instead, change the child sprite’s rotation to point at the target. You can use lerp() or a Tween to make the rotation as smooth as you wish.

    In many cases, this will be a great solution. Remember, the underlying body’s orientation doesn’t have to match the attached sprite!

    + + \ No newline at end of file diff --git a/docs/4.x/recent/index.html b/docs/4.x/recent/index.html index f507ea2d..07ae56c2 100644 --- a/docs/4.x/recent/index.html +++ b/docs/4.x/recent/index.html @@ -1,17 +1,19 @@ -Fresh Recipes :: Godot 4 Recipes - +Fresh Recipes :: Godot 4 Recipes + - - \ No newline at end of file +
    + + \ No newline at end of file diff --git a/docs/4.x/sitemap.xml b/docs/4.x/sitemap.xml index af408b49..f2a2c7cc 100644 --- a/docs/4.x/sitemap.xml +++ b/docs/4.x/sitemap.xml @@ -1 +1 @@ -https://godotrecipes.com/godot_recipes/4.x/g101/index.html2019-04-09T19:48:00-07:00https://godotrecipes.com/godot_recipes/4.x/audio/audio_manager/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/enter_exit_screen/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/recent/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/gdscript/gdscript_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/interpolation/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/platform_character/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/screen_wrap/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/animation/spritesheet_animation/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/101_3d_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/topdown_movement/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/tree_ready_order/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/101_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/basic_fps/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_02/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/grid_movement/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/101_3d_02/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/input_actions/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/interpolated_camera/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/gdscript/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/kyn/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/mouse_input/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/node_communication/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/2d_shooting/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/101_02/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/getting_nodes/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/transforms/index.html2019-04-09T19:49:14-07:00https://godotrecipes.com/godot_recipes/4.x/2d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/mouse_capture/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/car_steering/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_03/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/101_3d_03/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/101_03/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/shooting_raycasts/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/understanding_delta/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/8_direction/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_04/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/characterbody3d_examples/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/ai/homing_missile/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/rigidbody_drag_drop/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/file_io/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/using_ysort/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/healthbars/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/animation/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/character_vs_rigid/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/migrating/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/moving_platforms/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/grid_pathfinding/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_05/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/ui/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/index.html2019-04-09T19:49:14-07:00https://godotrecipes.com/godot_recipes/4.x/ai/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_06/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/rolling_cube/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/audio/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_07/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/multi_unit_select/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_08/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/spaceship/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_09/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_10/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/multi_target_camera/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/3d_sphere_car/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/asteroids_physics/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/click_to_move/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/ui/level_select/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/dot_cross_product/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/3d_align_surface/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_end/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/index.html2019-04-09T22:57:31-07:00https://godotrecipes.com/godot_recipes/4.x/categories/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/kyn/raycast2d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/tags/index.html \ No newline at end of file +https://godotrecipes.com/godot_recipes/4.x/g101/index.html2019-04-09T19:48:00-07:00https://godotrecipes.com/godot_recipes/4.x/audio/audio_manager/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/enter_exit_screen/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/recent/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/gdscript/gdscript_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/interpolation/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/platform_character/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/screen_wrap/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/animation/spritesheet_animation/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/101_3d_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/topdown_movement/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/tree_ready_order/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/101_01/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/basic_fps/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_02/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/grid_movement/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/101_3d_02/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/input_actions/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/interpolated_camera/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/gdscript/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/kyn/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/mouse_input/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/node_communication/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/smooth_rigid_rotate/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/2d_shooting/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/101_02/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/getting_nodes/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/transforms/index.html2019-04-09T19:49:14-07:00https://godotrecipes.com/godot_recipes/4.x/2d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/custom_actions/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/mouse_capture/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/car_steering/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_03/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/101_3d_03/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/start/101_03/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/shooting_raycasts/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/understanding_delta/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/8_direction/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_04/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/characterbody3d_examples/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/ai/homing_missile/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/rigidbody_drag_drop/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/file_io/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/using_ysort/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/healthbars/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/animation/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/character_vs_rigid/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/g101/3d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/basics/migrating/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/moving_platforms/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/grid_pathfinding/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_05/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/ui/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/index.html2019-04-09T19:49:14-07:00https://godotrecipes.com/godot_recipes/4.x/ai/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_06/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/rolling_cube/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/audio/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_07/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/input/multi_unit_select/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_08/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/spaceship/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_09/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_10/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/2d/multi_target_camera/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/3d_sphere_car/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/physics/asteroids_physics/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/click_to_move/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/ui/level_select/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/math/dot_cross_product/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/3d/3d_align_surface/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/games/first_2d/first_2d_end/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/index.html2019-04-09T22:57:31-07:00https://godotrecipes.com/godot_recipes/4.x/categories/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/kyn/raycast2d/index.htmlhttps://godotrecipes.com/godot_recipes/4.x/tags/index.html \ No newline at end of file diff --git a/docs/4.x/tags/index.html b/docs/4.x/tags/index.html index 4ac526ae..222be25f 100644 --- a/docs/4.x/tags/index.html +++ b/docs/4.x/tags/index.html @@ -1,17 +1,19 @@ -Tags :: Godot 4 Recipes - +Tags :: Godot 4 Recipes +

    Tags

      - - \ No newline at end of file +
      + + \ No newline at end of file diff --git a/docs/4.x/ui/index.html b/docs/4.x/ui/index.html index e5a4bbfb..910941b4 100644 --- a/docs/4.x/ui/index.html +++ b/docs/4.x/ui/index.html @@ -1,17 +1,19 @@ -UI :: Godot 4 Recipes - +UI :: Godot 4 Recipes +
      - - \ No newline at end of file +
      + + \ No newline at end of file diff --git a/docs/4.x/ui/level_select/index.html b/docs/4.x/ui/level_select/index.html index 9bf43138..a881fa14 100644 --- a/docs/4.x/ui/level_select/index.html +++ b/docs/4.x/ui/level_select/index.html @@ -1,13 +1,13 @@ -Level Select Menu :: Godot 4 Recipes - +Level Select Menu :: Godot 4 Recipes +

      Level Select Menu

      Problem

      Your game needs a “level select” menu, where the user can choose from a grid of options.

      alt -alt

      Solution

      As shown in the example above, we’ll make a scrolling grid of level “boxes” that the player can choose from. Let’s start with the individual level boxes:

      1: Level box

      Here’s the node setup:

      LevelBox:  PanelContainer
      +

      Level Select Menu

      Problem

      Your game needs a “level select” menu, where the user can choose from a grid of options.

      alt +alt

      Solution

      As shown in the example above, we’ll make a scrolling grid of level “boxes” that the player can choose from. Let’s start with the individual level boxes:

      1: Level box

      Here’s the node setup:

      LevelBox:  PanelContainer
            Label
            MarginContainer
                TextureRect
      -

      The TextureRect is for displaying the lock icon, and the Label for displaying the level number. When one is showing, the other is hidden.

      You can style these as you like, here’s an example:

      alt -alt

      Make sure to set the LevelBox’s Custom Minimum Size in the Inspector. We’re using (110, 110) in the example, but it depends on what size layout you’re going for.

      Now add a script and connect the gui_input signal.

      @tool
      +

      The TextureRect is for displaying the lock icon, and the Label for displaying the level number. When one is showing, the other is hidden.

      You can style these as you like, here’s an example:

      alt +alt

      Make sure to set the LevelBox’s Custom Minimum Size in the Inspector. We’re using (110, 110) in the example, but it depends on what size layout you’re going for.

      Now add a script and connect the gui_input signal.

      @tool
       extends PanelContainer
       
       signal level_selected
      @@ -40,17 +40,17 @@
           if event is InputEventMouseButton and event.pressed:
               level_selected.emit(level_num)
               print("Clicked level ", level_num)
      -

      We’re using @tool here so that we can make changes to the properties in the inspector and see them right away, without running the scene. Go ahead and try clicking the Locked property and verify that you see the lock appear/disappear.

      Since we don’t have actual levels to load in this project, the print() statement can help test that the click is being detected.

      2: Grid

      Once you have the box scene completed, add a new scene with a GridContainer. Add any number of LevelBox instances under it, making sure to set the Columns value. Here’s one with 6 columns:

      alt -alt

      In this example Theme Overrides/Constants/H Separation and V Separation are set to 10.

      Save this scene as LevelGrid. In the menu, we’ll use multiple instances to display the desired number of levels.

      3: Menu screen

      Now we can put together the final menu.

      Here’s the basic layout we’re going for:

      alt -alt

      We’ll create it with these nodes:

      LevelMenu: MarginContainer
      +

      We’re using @tool here so that we can make changes to the properties in the inspector and see them right away, without running the scene. Go ahead and try clicking the Locked property and verify that you see the lock appear/disappear.

      Since we don’t have actual levels to load in this project, the print() statement can help test that the click is being detected.

      2: Grid

      Once you have the box scene completed, add a new scene with a GridContainer. Add any number of LevelBox instances under it, making sure to set the Columns value. Here’s one with 6 columns:

      alt +alt

      In this example Theme Overrides/Constants/H Separation and V Separation are set to 10.

      Save this scene as LevelGrid. In the menu, we’ll use multiple instances to display the desired number of levels.

      3: Menu screen

      Now we can put together the final menu.

      Here’s the basic layout we’re going for:

      alt +alt

      We’ll create it with these nodes:

      LevelMenu: MarginContainer
            VBoxContainer
               Title:  Label
                HBoxContainer
                   BackButton:  TextureButton
                   ClipControl:  Control
                   NextButton:  TextureButton
      -

      Adjust the node properties:

      • LevelMenu
        • Theme Overrides/Constants/Margins: 20
      • VBoxContainer
        • Theme Overrides/Constants/Separation: 50
      • Title
        • Style the font however you like
      • BackButton / NextButton
        • Ignore Texture Size: On
        • Stretch Mode: Keep Centered
        • Layout/Container Sizing/Horizontal/Expand: On
      • ClipControl
        • Layout/Clip Contents: On
        • Layout/Custom Minimum Size: (710, 350) (size of the LevelGrid)

      The ClipControl node is where the grid goes. Enabling Clip Contents means that if the contents are larger than the control, they’ll be cropped. That will allow us to make a horizontally scrolling set of grids. Add an HBoxContainer called GridBox to ClipControl, and instance 3 (or more) LevelGrids inside it.

      Make sure to set Theme Overrides/Constants/Separation to 0.

      Your layout should look something like this (we’ve disabled Clip Contents in order to show what’s happening):

      alt -alt

      With Clip Content, the three grids are all there, but the ClipControl only shows one at a time.

      Now, to scroll the menu, we need to shift the GridBox by 710 pixels to the left/right.

      110 (width of each LevelBox)
      +

      Adjust the node properties:

      • LevelMenu
        • Theme Overrides/Constants/Margins: 20
      • VBoxContainer
        • Theme Overrides/Constants/Separation: 50
      • Title
        • Style the font however you like
      • BackButton / NextButton
        • Ignore Texture Size: On
        • Stretch Mode: Keep Centered
        • Layout/Container Sizing/Horizontal/Expand: On
      • ClipControl
        • Layout/Clip Contents: On
        • Layout/Custom Minimum Size: (710, 350) (size of the LevelGrid)

      The ClipControl node is where the grid goes. Enabling Clip Contents means that if the contents are larger than the control, they’ll be cropped. That will allow us to make a horizontally scrolling set of grids. Add an HBoxContainer called GridBox to ClipControl, and instance 3 (or more) LevelGrids inside it.

      Make sure to set Theme Overrides/Constants/Separation to 0.

      Your layout should look something like this (we’ve disabled Clip Contents in order to show what’s happening):

      alt +alt

      With Clip Content, the three grids are all there, but the ClipControl only shows one at a time.

      Now, to scroll the menu, we need to shift the GridBox by 710 pixels to the left/right.

      110 (width of each LevelBox)
           * 6 (grid columns)
           + 10 (grid spacing) * 5
           == 710
      @@ -85,15 +85,17 @@
       

      When you run the scene, try clicking the “Next” and “Back” buttons and verify that it’s scrolling as expected. Clicking the individual level boxes should print to the console.

      Download the example project to see the whole thing in action, including some tweens for the scrolling action (because tweens make everything better).

      Download This Project

      Download the project code here: https://github.com/godotrecipes/ui_level_select

      - - \ No newline at end of file +
      + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 84c16f21..c20ece5f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -2,11 +2,11 @@ - + -

      Please follow this link.

      +

      Please follow this link.

      \ No newline at end of file diff --git a/src-4/content/2D/2d_align_surface.md b/src-4/content/2D/2d_align_surface.md index 9962fc42..75c9a49f 100644 --- a/src-4/content/2D/2d_align_surface.md +++ b/src-4/content/2D/2d_align_surface.md @@ -2,7 +2,6 @@ title: "CharacterBody2D: align with surface" weight: 5 draft: true -ghcommentid: 24 --- ## Problem @@ -103,7 +102,7 @@ func _physics_process(delta): ## Related recipes -- [Platform character](http://kidscancode.org/godot_recipes/2d/platform_character) +- [Platform character](/godot_recipes/4.x/2d/platform_character) - [Using KinematicBody2D](/godot_recipes/4.x/physics/godot3_kinematic2d/) diff --git a/src-4/content/2D/2d_shooting.md b/src-4/content/2D/2d_shooting.md index 05671bd6..c855c426 100644 --- a/src-4/content/2D/2d_shooting.md +++ b/src-4/content/2D/2d_shooting.md @@ -60,7 +60,7 @@ This will work for any character type, not just the "rotate-and-move" style show In the character's script we add a variable to hold the bullet scene for instancing: ```gdscript -export var Bullet : PackedScene +@export var Bullet : PackedScene ``` And check for our defined input action: diff --git a/src-4/content/2D/multi_target_camera.md b/src-4/content/2D/multi_target_camera.md index 17e9731a..8018746b 100644 --- a/src-4/content/2D/multi_target_camera.md +++ b/src-4/content/2D/multi_target_camera.md @@ -46,7 +46,7 @@ var targets = [] # Array of targets to be tracked. @onready var screen_size = get_viewport_rect().size ``` -These settings will let you adjust the camera's behavior. We'll `lerp()` all camera changes, setting the move/zoom speeds to low values will introduce some delay in the camera "catching up" to sudden changes. +These settings will let you adjust the camera's behavior. We'll `lerp()` all camera changes, so setting the move/zoom speeds to lower values will introduce some delay in the camera "catching up" to sudden changes. Maximum and minimum zoom values will also depend on the size of objects in your game and how close or far you want to get. Adjust to suit. diff --git a/src-4/content/_index.md b/src-4/content/_index.md index 533b4cd0..8eecb223 100644 --- a/src-4/content/_index.md +++ b/src-4/content/_index.md @@ -20,7 +20,7 @@ This site has lots of learning material for Godot 3 - but not all of it has been Are you ready to learn game development? Whether it's as a hobby or working towards your dream career, there's never been a better time to get started. Modern programming languages and tools have made it easier than ever to build high-quality games and distribute them to the world. One of these tools is the Godot game engine. For beginners, it offers a friendly way to learn gamedev techniques. For experienced developers, it's a powerful, customizable and _open_ tool for bringing your visions to life. -![alt](/godot_recipes/4.x/img/godot3_logo.png?width=250) +![alt](/godot_recipes/4.x/img/godot3_logo.png?width=400px) On this site you'll find a gentle introduction to the Godot game engine, as well as a wide variety of gamedev tips and techniques. Feel free to browse the categories in the sidebar and see what catches your interest. diff --git a/src-4/content/ai/tilemap_navigation.md b/src-4/content/ai/tilemap_navigation.md new file mode 100644 index 00000000..1fcca3ab --- /dev/null +++ b/src-4/content/ai/tilemap_navigation.md @@ -0,0 +1,12 @@ +--- +title: "TileMap Navigation" +weight: 11 +draft: true +--- + +## Problem + + + +## Solution + diff --git a/src-4/content/g101/3d/101_3d_01.md b/src-4/content/g101/3d/101_3d_01.md index e34acde4..35d2addb 100644 --- a/src-4/content/g101/3d/101_3d_01.md +++ b/src-4/content/g101/3d/101_3d_01.md @@ -1,9 +1,9 @@ -+++ -title = "The 3D Editor" -weight = 1 -draft = false -pre = "01. " -+++ +--- +title: "The 3D Editor" +weight: 1 +draft: false +pre: "01. " +--- In this tutorial, we'll look at how to start working in 3D in Godot. You'll learn how to navigate in the 3D editor, how to create and manipulate 3D objects, diff --git a/src-4/content/g101/3d/101_3d_02.md b/src-4/content/g101/3d/101_3d_02.md index dda42d08..9c62da97 100644 --- a/src-4/content/g101/3d/101_3d_02.md +++ b/src-4/content/g101/3d/101_3d_02.md @@ -1,9 +1,9 @@ -+++ -title = "Importing 3D Objects" -weight = 2 -draft = false -pre = "02. " -+++ +--- +title: "Importing 3D Objects" +weight: 2 +draft: false +pre: "02. " +--- In the last part, we started a 3D project and looked at how to navigate and create 3D objects. In this part, you'll learn how to import existing 3D objects that you've made or downloaded and how to use more of Godot's 3D nodes. diff --git a/src-4/content/g101/3d/101_3d_03.md b/src-4/content/g101/3d/101_3d_03.md index 9524af98..2d83c941 100644 --- a/src-4/content/g101/3d/101_3d_03.md +++ b/src-4/content/g101/3d/101_3d_03.md @@ -1,9 +1,9 @@ -+++ -title = "Creating a 3D Character" -weight = 3 -draft = false -pre = "03. " -+++ +--- +title: "Creating a 3D Character" +weight: 3 +draft: false +pre: "03. " +--- In the last part, we covered how to import 3D objects and how to arrange them in a scene. In this installment, we'll add more objects to the scene, including a user-controlled character. diff --git a/src-4/content/g101/3d/101_3d_04.md b/src-4/content/g101/3d/101_3d_04.md index c901d558..7001a483 100644 --- a/src-4/content/g101/3d/101_3d_04.md +++ b/src-4/content/g101/3d/101_3d_04.md @@ -1,10 +1,9 @@ - -+++ -title = "Using Areas" -weight = 4 -draft = true -pre = "04. " -+++ +--- +title: "Using Areas" +weight: 4 +draft: true +pre: "04. " +--- In the last part, we learned about transforms, a very important concept in 3D that records an object's local orientation in space, and used it to move our KinematicBody character. This time, we're going to look at another type of 3D node: the {{< gd-icon Area3D >}}`Area3D`. diff --git a/src-4/content/g101/gdscript/gdscript_01.md b/src-4/content/g101/gdscript/gdscript_01.md index b7c97773..171e1f5e 100644 --- a/src-4/content/g101/gdscript/gdscript_01.md +++ b/src-4/content/g101/gdscript/gdscript_01.md @@ -21,12 +21,12 @@ You'll often read comments to the effect that "GDScript is based on Python". Tha {{% notice warning %}} Many tutorials (and Godot in general) assume that you have at least *some* programming experience already. If you've never coded before, you'll likely find learning Godot to be a challenge. Learning a game engine is a large task on its own; learning to code at the same time means you're taking on a lot. If -you find yourself struggling with the code in this section, you may find that working through an introductory Python lesson will help you grasp the basics. +you find yourself struggling with the code in this section, you may find that working through an introductory programming lesson (Python is a good option) will help you grasp the basics. {{% /notice %}} ## Structure of a script -The first line of any GDScript file must be `extends `, where `` is some existing built-in or user-defined class. For example, if you're attaching a script to a `CharacterBody2D` node, then your script would start with `extends CharacterBody2D`. This states that your script is taking all the functionality of the built-in `CharacterBody2D` object and *extending* it with additional functionality created by you. +The first line of any GDScript file must be `extends `, where `` is some existing built-in or user-defined class. For example, if you're attaching a script to a {{< gd-icon CharacterBody2D >}}`CharacterBody2D` node, then your script would start with `extends CharacterBody2D`. This states that your script is taking all the functionality of the built-in `CharacterBody2D` object and *extending* it with additional functionality created by you. In the rest of the script, you can define any number of variables (aka "class properties") and functions (aka "class methods"). @@ -34,11 +34,11 @@ In the rest of the script, you can define any number of variables (aka "class pr Let's make our first script. Remember, any node can have a script attached to it. -Open the editor and add a `Sprite2D` node to empty scene. Right-click on the new node, and choose "Attach Script". You can also click the button next to the search box. +Open the editor and add a {{< gd-icon Sprite2D >}}`Sprite2D` node to empty scene. Right-click on the new node, and choose "Attach Script". You can also click the button next to the search box. ![alt](/godot_recipes/4.x/img/gds_01_attach.png?width=250) -Next you need to decide where you want the script saved and what to call it. If you've named the node, the script will automatically be named to match it (so unless you've changed anything this script will likely be called "Sprite.gd"). +Next you need to decide where you want the script saved and what to call it. If you've named the node, the script will automatically be named to match it (so unless you've changed anything this script will likely be called "sprite2d.gd"). Now the script editor window opens up, and this is your new, empty sprite script. Godot has automatically included some lines of code, as well as some comments describing what they do. @@ -57,7 +57,7 @@ func _process(delta): Since the script was added to a {{< gd-icon Sprite2D >}}`Sprite2D`, the first line is automatically set to `extends Sprite2D`. Because this script extends the {{< gd-icon Sprite2D >}}`Sprite2D` class, it will be able to access and manipulate all the properties and methods that a {{< gd-icon Sprite2D >}}`Sprite2D` node provides. {{% notice title="Properties and methods" style="note" %}} -*Properties* and *methods* are two terms which specifically mean *variables* and *functions* that are defined in an object. Programmers tend to use them interchangeably. +*Properties* and *methods* are two terms which specifically mean *variables* and *functions* that are defined in an object. Programmers tend to use the terms interchangeably. {{% /notice %}} After that is where you're going to define all the variables you will use in the script, the "member variables". You define variables with the 'var' keyword - as you can see by the comment examples. @@ -66,7 +66,7 @@ Go ahead and delete the comments and let's talk about this next piece. Now we see a function called `_ready()`. In GDScript you define a function with the keyword "func". The `_ready()` function is a special one that Godot looks for and runs whenever a node is added to the tree, for example when we hit "Play". -Let's say that when the game starts, we want to make sure the sprite goes to a particular location. In the Inspector, we want to set the _Position_ property. Notice that it's in the section called "Node2D" - that means this is a property that *any* {{< gd-icon Node2D >}}`Node2D` type node will have, not just {{< gd-icon Sprite2D >}}`Sprite2D`s. +Let's say that when the game starts, we want to make sure the sprite goes to a particular location. In the Inspector, we want to set the **Position** property. Notice that it's in the section called "Node2D" - that means this is a property that *any* {{< gd-icon Node2D >}}`Node2D` type node will have, not just {{< gd-icon Sprite2D >}}`Sprite2D`s. How do we set the property in code? One way to find the name of the property is by hovering over it in the Inspector. @@ -89,7 +89,7 @@ Now we have a script that says "When this sprite starts, set its position to `(1 ![alt](/godot_recipes/4.x/img/gds_01_03.png) -{{% notice tip %}} +{{% notice style="tip" title="Learning tip" %}} When first learning to code, beginners often ask "How do you memorize all these commands?" Just like any other skill, it's not a matter of memorization, it's about practice. As you use things more, the things you do frequently will "stick" and become automatic. Until then, it's a great idea to keep the reference docs handy. Use the search function whenever you see something you don't recognize. If you have multiple monitors, keep a copy of the [web docs](https://docs.godotengine.org/en/latest/) open on the side for quick reference. {{% /notice %}} diff --git a/src-4/content/g101/gdscript/gdscript_02.md b/src-4/content/g101/gdscript/gdscript_02.md index e4f35544..23ab19bc 100644 --- a/src-4/content/g101/gdscript/gdscript_02.md +++ b/src-4/content/g101/gdscript/gdscript_02.md @@ -65,7 +65,7 @@ func _process(delta): We've taken this quantity and made it a variable. You **declare** a variable by using the keyword `var`, giving the variable a name, and then assigning a value with `=`. -It's often more convenient to set values at the top where they're easy to find. We'll also find it more convenient later, when we start making multiples of this sprite. +It's often more convenient to declare variables at the top where they're easy to find. We'll also find it more convenient later, when we start making multiples of this sprite. ## Randomizing the movement @@ -78,8 +78,21 @@ func _ready(): velocity = velocity * randf_range(100, 400) ``` -Here, we're first setting `velocity` to point to the right, and then rotating it a random amount. Then we multiply that by another random number to give it a random speed. Try running the scene a couple of times and you'll see the sprite go in different directions. +That's a lot, so let's break it down: +1. First, we're setting `velocity` to point to the right. `Vector2.RIGHT` is a built-in *constant* that represents the vector `(1, 0)`. + +1. Next, we're using `Vector2`'s `rotated()` method to rotate the vector. In the parentheses of `rotated()` it expects an angle. + +1. Since we want to rotate a random amount, we use `randf_range()` to get a random number between `0` and `TAU`. + + {{% expand title="About angles" %}} + When working with angles, rather than _degrees_, GDScript (like most programming languages) uses _radians_. In radians, a full rotation is equal to `2 * PI` (or `TAU`) - equivalent to `360` degrees. + {{% /expand %}} + +1. Finally, we multiply `velocity` by another random number to give it a random speed. + +Try running the scene a couple of times and you'll see the sprite move in different directions. diff --git a/src-4/content/games/first_2d/first_2d_02.md b/src-4/content/games/first_2d/first_2d_02.md index 8e38041a..c197e9aa 100644 --- a/src-4/content/games/first_2d/first_2d_02.md +++ b/src-4/content/games/first_2d/first_2d_02.md @@ -23,7 +23,7 @@ The ship needs to: * _Display an image_. {{< gd-icon Sprite2D >}}`Sprite2D` is the node for this. Since it's also a {{< gd-icon Node2D >}}`Node2D`, we'd still be able to move it around. -* _Detect getting hit_. The enemies will be shooting and flying around on the screen, so we'll need to know when ship is hit. We don't have a need for solid objects - they're not going to bounce off each other or transfer momentum - we just need to know when they touch. For this, an {{< gd-icon Area2D >}}`Area2D` would be perfect. It can detect touching other objects, has positional properties, but it has no appearance of its own. +* _Detect getting hit_. The enemies will be shooting and flying around on the screen, so we'll need to know when the ship is hit. We don't have a need for solid objects - they're not going to bounce off each other or transfer momentum - we just need to know when they touch. For this, an {{< gd-icon Area2D >}}`Area2D` would be perfect. It can detect touching other objects, has positional properties, but it has no appearance of its own. Looking at this list, the {{< gd-icon Area2D >}}`Area2D` provides the main functionality. We can attach a {{< gd-icon Sprite2D >}}`Sprite2D` to display the ship image, and then we'll have everything we need. diff --git a/src-4/content/input/custom_actions.md b/src-4/content/input/custom_actions.md new file mode 100644 index 00000000..92015c96 --- /dev/null +++ b/src-4/content/input/custom_actions.md @@ -0,0 +1,68 @@ +--- +title: "Adding Input Actions in code" +weight: 3 +draft: false +--- + +## Problem + +You need to add actions to the InputMap at runtime. + +## Solution + +Typically, you'll add actions to the InputMap via _Project Settings_, as shown in [Recipe: Input Actions](/godot_recipes/4.x/input/input_actions/). However, you may find yourself needing to add one or more actions directly in a script. The [InputMap singleton](https://docs.godotengine.org/en/latest/classes/class_inputmap.html) has methods to help you do this. + +Here's an example that would add a new action called "attack" using the space key: + +```gdscript +func _ready(): + InputMap.add_action("attack") + var ev = InputEventKey.new() + ev.keycode = KEY_SPACE + InputMap.action_add_event("attack", ev) +``` + +If you also wanted to add the left mouse button to the same action: + +```gdscript +ev = InputEventMouseButton.new() +ev.button_index = MOUSE_BUTTON_LEFT +InputMap.action_add_event("attack", ev) +``` + +{{% notice note %}} +`InputMap.add_action()` will produce an error if the action already exists. You should check first with `InputMap.has_action()` before attempting to add a new action. +{{% /notice %}} + +### Practical Example + +Let's say you've made the platform character from [Recipe: Platform character](/godot_recipes/4.x/2d/platform_character/) and you want to re-use it in another project. If you saved the scene, script, and assets in a single folder, you need only copy that folder to your new project. But you'd still need to edit the Input Map in order for the inputs to work. + +Instead, you could add the following code to the player script and be sure that the necessary input actions will be added automatically: + +```gdscript +var controls = {"walk_right": [KEY_RIGHT, KEY_D], + "walk_left": [KEY_LEFT, KEY_A], + "jump": [KEY_UP, KEY_W, KEY_SPACE]} + +func _ready(): + add_inputs() + +func add_inputs(): + var ev + for action in controls: + if not InputMap.has_action(action): + InputMap.add_action(action) + for key in controls[action]: + ev = InputEventKey.new() + ev.keycode = key + InputMap.action_add_event(action, ev) +``` + +## Related recipes + +- [Input Actions](/godot_recipes/4.x/input/input_actions/) +- [Platform Character](/godot_recipes/4.x/2d/platform_character/) + + diff --git a/src-4/content/physics/character_vs_rigid.md b/src-4/content/physics/character_vs_rigid.md index 187bc99a..c98cec56 100644 --- a/src-4/content/physics/character_vs_rigid.md +++ b/src-4/content/physics/character_vs_rigid.md @@ -82,3 +82,6 @@ Download the project's example code here: [https://github.com/godotrecipes/chara ## Related recipes - [Platform character](/godot_recipes/4.x/2d/platform_character/) + +## Watch Video +{{< youtube SJuScDavstM >}} diff --git a/src-4/content/physics/smooth_rigid_rotate.md b/src-4/content/physics/smooth_rigid_rotate.md new file mode 100644 index 00000000..ee85fcf2 --- /dev/null +++ b/src-4/content/physics/smooth_rigid_rotate.md @@ -0,0 +1,44 @@ +--- +title: "RigidBody2D: Look at Target" +weight: 2 +draft: false +--- + +## Problem + +You want a rigid body to rotate smoothly to look at a target. + +## Solution + +Using {{< gd-icon RigidBody2D >}}`RigidBody2D` can be tricky. Because it's controlled by Godot's physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the [RigidBody2D API doc](https://docs.godotengine.org/en/stable/classes/class_rigidbody2d.html). + +To rotate a body, we need to apply a rotational force - a *torque*. Once the body is rotating, we want the torque to get smaller as we get closer to the final rotation. + +This is the perfect situation to use the *dot product*. Its sign will tell us whether the target is to the left/right, and its magnitude will tell us how far away from the target direction we're pointing. + +{{% notice style="tip" title="" %}} +See [Vectors: Using Dot and Cross Product](/godot_recipes/4.x/math/dot_cross_product/) for a brief review of the dot product. +{{% /notice %}} + +```gdscript +extends RigidBody2D + +var angular_force = 50000 +var target = position + Vector2.RIGHT + +func _physics_process(delta): + var dir = transform.y.dot(position.direction_to(target)) + constant_torque = dir * angular_force +``` + +You may be wondering why we're using the `transform.y` here, when `transform.x` is the body's forward vector. Using `transform.x`, the dot product would be at its maximum when the body is directly pointing at the target, but we want the torque to be zero at that point. Using `transform.y` means that our torque will be higher when we're *not* aligned with the target. + +### Skip the Rigid Body Entirely + +You can avoid all of this entirely by not rotating your rigid body at all! Instead, change the child sprite's `rotation` to point at the target. You can use `lerp()` or a {{< gd-icon Tween >}}`Tween` to make the rotation as smooth as you wish. + +In many cases, this will be a great solution. Remember, the underlying body's orientation doesn't have to match the attached sprite! + +## Related recipes + +- [Vectors: Using Dot and Cross Product](/godot_recipes/4.x/math/dot_cross_product/index.html) diff --git a/themes_shared/hugo-theme-relearn-main/layouts/partials/book-ad.html b/themes_shared/hugo-theme-relearn-main/layouts/partials/book-ad.html new file mode 100644 index 00000000..d3cb432a --- /dev/null +++ b/themes_shared/hugo-theme-relearn-main/layouts/partials/book-ad.html @@ -0,0 +1,12 @@ +
      + +Learn Godot with my new book: + + + + + + + + +
      \ No newline at end of file diff --git a/themes_shared/hugo-theme-relearn-main/layouts/partials/menu.html b/themes_shared/hugo-theme-relearn-main/layouts/partials/menu.html index cb638c2c..7f5e4a9f 100644 --- a/themes_shared/hugo-theme-relearn-main/layouts/partials/menu.html +++ b/themes_shared/hugo-theme-relearn-main/layouts/partials/menu.html @@ -13,6 +13,7 @@ {{ partial "logo.html" . }} + {{- if not .Site.Params.disableSearch }} {{ partial "search.html" . }} {{- end }} @@ -42,6 +43,7 @@ {{- $page := . }} {{- $disableShortcutsTitle := .Site.Params.DisableShortcutsTitle }} {{- with .Site.Menus.shortcuts }} + {{ partial "book-ad.html" . }}