Skip to the content

Module 1: What a game engine is, and meeting Godot

Module 2: The editor, scenes and nodes

Module 3: GDScript from zero

Module 4: Functions, classes and types in GDScript

Module 5: Moving in 2D: input and the game loop

Module 6: Physics bodies and collisions

Module 7: Signals: nodes talking to each other

Module 8: Instancing, groups and scene composition

Module 9: Sprites, animation and tweens

Module 10: User interfaces with Control nodes

Module 11: Tile maps, cameras and levels

Module 12: Sound, particles and game feel

Module 13: Game state, resources and saving

Module 14: Enemies: state machines and pathfinding

Module 15: Into 3D: nodes, meshes, lights and cameras

Module 16: A 3D character: movement, physics and the camera

Module 17: Shaders and visual effects

Module 18: Architecture: autoloads, custom resources and patterns

Module 19: Performance, debugging and shipping

Module 20: Expert Godot

Answer key

  1. 1. What a game engine is, and meeting Godot · Each frame has a time budget · QuizA game runs at 30 frames per second. Roughly how long does each frame have?C. 33.3 msOne second is 1000 ms, shared between 30 frames: 1000 ÷ 30 ≈ 33.3 ms each. The tempting 16.7 ms is the budget at 60 fps; doubling the frame rate halves the time each frame gets.
  2. 1. What a game engine is, and meeting Godot · An engine does the work every game needs · QuizWhich of these would you normally write yourself, rather than get from the engine?C. The rule that touching a coin adds 10 to the scoreDrawing, collision detection and input are the same in every game, so the engine provides them. The rule that a coin is worth 10 points is your game’s own design: only you can write it. That split, the engine for the general and you for the particular, is the whole point of an engine.
  3. 1. What a game engine is, and meeting Godot · Godot is free, open source and yours to keep · QuizYou sell your Godot game for 10 dollars and it sells well. What do you owe the Godot project?C. Nothing, apart from including its licence noticeThe MIT licence is permissive: no royalties, no fees, and your own code stays yours and private. Some licences, such as the GPL, would make you share your source, and some commercial engines take a share above a revenue limit; MIT does neither.
  4. 1. What a game engine is, and meeting Godot · You write games in GDScript, or in C# · QuizYou are new to programming and want to make a small 2D game that runs in a web browser. Which is the better first choice?B. GDScript, in the standard downloadGDScript is made for Godot, needs nothing extra, is what the docs teach, and exports to the web. C# is a fine language, but its speed rarely matters for a small game, and its web support in Godot 4 has trailed GDScript’s. C++ is possible through GDExtension (module 20), but nobody needs it to start.
  5. 1. What a game engine is, and meeting Godot · Installing Godot takes one download · Put in orderPut the steps for getting Godot running in order.1. Open the download page on godotengine.org 2. Choose the latest stable Godot 4 for your operating system (the standard edition, not .NET) 3. Unzip the download into a folder you will find again 4. Open the Godot program in that folder 5. See the Project Manager, which lists your projectsPick the stable release rather than a preview, and the standard edition unless you plan to write C#. There is nothing to install, so unzipping is the last step before running it. The first window you see is always the Project Manager, not the editor.
  6. 1. What a game engine is, and meeting Godot · The Project Manager makes and opens projects · QuizYour first game is a 2D puzzle you want people to play in their browser. Which renderer fits?C. CompatibilityBrowsers draw through WebGL 2, which only the Compatibility renderer uses. Forward+ is the most capable, but it needs desktop graphics features a browser does not offer. You can change the renderer later in the project settings, so this is not a choice you are stuck with.
  7. 1. What a game engine is, and meeting Godot · Your first scene says hello · QuizThe first time you press F5, Godot asks you to choose a main scene. Why?A. F5 runs the whole game, which starts from one scene, and none has been chosen yetA game can have many scenes (a menu, levels, a game-over screen), so Godot needs to know which one opens first. Your choice is saved in the project settings. F6 is different: it runs only the scene you are editing, which is useful for testing one piece.
  8. 2. The editor, scenes and nodes · Workspaces switch what the viewport shows · QuizYour script prints “Game started”. Where do you see it?B. In the Output panel at the bottomprint writes to the Output panel while the game runs from the editor. The Inspector shows properties, not messages. Errors land in Output too, and in more detail in the Debugger tab, so these two panels are where to look first when something goes wrong.
  9. 2. The editor, scenes and nodes · A node is one building block with one job · QuizUsing the tree above, which of these has a position it inherits from Node2D?B. Camera2DCamera2D sits under Node2D, so it inherits everything a Node2D has: position, rotation and scale. Label is a Control, which places itself differently (with a rectangle, module 10). Timer is a plain Node: it has no place in space at all, because it doesn’t need one.
  10. 2. The editor, scenes and nodes · Nodes form a tree, and children follow their parent · QuizA Sprite2D is a child of Player. The sprite’s position is (0, -20). Player moves from (100, 300) to (150, 300). Where is the sprite on screen now?C. (150, 280)A child’s position is relative to its parent: (0, -20) means “20 pixels above the player”, wherever the player is. The player is at (150, 300), so the sprite is at (150, 280). Its own position property still reads (0, -20); its global position is (150, 280). Remember y points down in 2D, so -20 is up.
  11. 2. The editor, scenes and nodes · The Inspector edits a node’s properties · Put in orderPut the steps for building a simple player scene in order.1. Make a new scene (Scene, then New Scene) 2. Choose Other Node and pick CharacterBody2D as the root, renamed Player 3. Add a Sprite2D child and drag an image into its Texture in the Inspector 4. Add a CollisionShape2D child and give it a new RectangleShape2D in Shape 5. Save the scene as player.tscnThe root comes first because every other node hangs under it. The sprite and the shape are children so they move with the body; their order doesn’t matter much, but both need their resource set in the Inspector (an empty shape does nothing and shows a warning). Saving last turns the tree into a reusable scene.
  12. 2. The editor, scenes and nodes · Run the whole game, or just this scene · QuizYour main scene is the title menu. You are editing level_3.tscn and want to try it without clicking through the menu. What do you press?B. F6F6 runs the current scene directly. F5 always starts from the main scene, so you would land on the menu. F8 stops the game. A scene that only works when started from somewhere else is a sign it depends on things it shouldn’t.
  13. 2. The editor, scenes and nodes · res:// is the project, user:// is the player’s · QuizWhere should your game write the player’s save file?B. user://save.datuser:// is meant for exactly this, and it exists on every platform, phones and the web included. Writing to res:// may seem to work in the editor, where it is just a folder, but fails once exported, because the project is packed and read-only. A fixed path like C:/ only exists on some Windows computers.
  14. 3. GDScript from zero · Variables remember values · QuizWhat does print(7 / 2) show?B. 37 and 2 are both ints, so the division is between whole numbers and the fraction is dropped: 3. Godot even warns you about it (“integer division”). For 3.5, make one side a float: 7 / 2.0 or 7.0 / 2. This catches out almost everyone once, usually when working out a percentage.
  15. 3. GDScript from zero · Type hints let Godot catch mistakes · QuizWhat type does var gold := 50 give gold?B. int:= takes the type of the starting value, and 50, with no decimal point, is an int. If gold could ever be fractional you would write 50.0. Plain var gold = 50 (no colon) is the one with no fixed type.
  16. 3. GDScript from zero · Operators calculate and compare · Fill the blanksAdd 25 to the score, then print Score: 125 using a format string.1: +=, 2: %d, 3: %+= adds to the variable in place, the short form of score = score + 25. In a format string %d stands for a whole number (%s for any value), and the % between the string and the value fills it in. Writing "Score: " + score fails: + can’t join a String and an int without str().
  17. 3. GDScript from zero · if, elif and else make decisions · Find the bugGodot won’t run this script. Which line is wrong?Line 4: if coins >= 10:One = assigns a value; two, ==, compares. A condition must compare, so if coins = 10: is an error. coins == 10 would run, but >= 10 is better: with 11 coins the door should still open. Asking “is it at least?” instead of “is it exactly?” avoids a whole family of bugs.
  18. 3. GDScript from zero · match picks one branch from many values · QuizWith the function above, what does react("potion") print?B. Healed"potion" is one of the two values in the second pattern, so that branch runs, and match stops there. GDScript’s match never “falls through” into the next branch, unlike switch in some languages; _ only runs when nothing above it matched.
  19. 3. GDScript from zero · for and while repeat work · QuizWhat does for i in range(2, 10, 3): print(i) print?A. 2, 5, 8It starts at 2 and adds 3 each time: 2, 5, 8. The next would be 11, but the end, 10, is never reached or included. The end of a range is always left out, which is why range(3) gives 0, 1, 2: three numbers, starting from 0.
  20. 3. GDScript from zero · Arrays hold lists; dictionaries look things up · Fill the blanksAdd "key" to the inventory, then print how many keys the shop has in stock (0 if it has no entry).1: append, 2: get, 3: 0append adds to the end of an array. get(key, default) reads a dictionary safely: the value if the key is there, the default if not. Here it prints 2. Reading stock["key"] would work too, but crashes the day the key goes missing.
  21. 3. GDScript from zero · Functions name a job and can give back an answer · Fill the blanksFinish is_rich, which takes a number of coins and answers whether it is 100 or more.1: func, 2: int, 3: bool, 4: returnA function starts with func. The parameter is a whole number, int, and the answer is true or false, so it returns bool. A comparison like coins >= 100 already is a bool, so it can be returned directly, with no if needed.
  22. 4. Functions, classes and types in GDScript · Every script is a class · QuizTwo player nodes in a level both use player.gd. One calls collect(5). What happens to the other one’s coins?B. Nothing: each node has its own copy of coinsThe script is the blueprint; each node built from it is a separate instance with its own properties. That is why you can have ten enemies from one script, each with its own health. Data shared by all instances needs something else, such as a static var or an autoload (module 13).
  23. 4. Functions, classes and types in GDScript · Inheritance and super reuse a parent’s code · QuizEnemy._ready() starts the enemy’s patrol. Boss extends Enemy and has this _ready. Does the boss patrol?B. No: this _ready replaces the parent’s; it needs super() to run itIn Godot 4, overriding _ready replaces the parent’s: the engine calls only the child’s. Add super() as the first line to run the parent’s setup, then the boss’s own. Godot 3 called both automatically, so old tutorials leave super() out, and code ported from them loses behaviour silently.
  24. 4. Functions, classes and types in GDScript · Enums name a fixed set of choices · Fill the blanksMake an enum of the four directions, a variable of that type facing up, and turn it to the left.1: enum, 2: Direction, 3: Direction.LEFTenum Name { … } declares it, and Name is then a type. The values are written with the enum’s name in front, Direction.LEFT, so they can’t be confused with another enum’s LEFT. Using a plain int and remembering that 2 means left works, until someone forgets.
  25. 4. Functions, classes and types in GDScript · @export shows a property in the Inspector; @onready waits for the tree · Find the bugGodot flags an error in this script, and it can’t work when the scene starts. Which line is the cause?Line 3: @onready var label: Label = $ScoreLabelA plain property gets its value when the script’s object is created, before the node has its children or is in the scene tree, so $ScoreLabel would find nothing. Godot 4 knows this and reports it as an error: a default value that uses $ won’t find nodes before _ready() is called. @onready runs the line just before _ready, when the children are there. Any property that uses $ needs @onready.
  26. 4. Functions, classes and types in GDScript · Static typing finds bugs before you play · Quizbody is a StaticBody2D (a wall). What is body as Player?B. nullas converts only when the value really is that type; otherwise it gives null. Using the result without checking would then fail on null. That is why the code above checks body is Player first: is answers true or false without converting anything.
  27. 4. Functions, classes and types in GDScript · Godot calls your functions at set moments · Put in orderA node is created and added to the game. Put its callbacks in the order Godot calls them.1. _init() 2. _enter_tree() 3. _ready() 4. The first _process(delta)Creating the object runs _init. Adding it to the tree runs _enter_tree, and once it and its children are all in, _ready. Only then does it take part in frames. Code in _init can’t use $Child, because there is no tree yet: that is what _ready is for.
  28. 4. Functions, classes and types in GDScript · Godot calls your functions at set moments · QuizA Level node has a child Player. Both have _ready. Which runs first?B. Player._ready, because children are ready first_ready means “I and everything under me are ready”, so Godot readies the children before the parent. When Level._ready runs, it can safely use its Player. _enter_tree goes the other way: parent first, then children.
  29. 4. Functions, classes and types in GDScript · null, and nodes that are gone · Find the bugThe turret crashes the moment its target is destroyed. Which line needs to change?Line 6: if is_instance_valid(target): look_at(target.global_position)After the target is freed, target still refers to the deleted object, and reading its global_position stops the game. Guard the line with is_instance_valid(target), which is false both when it was never set and when it has been freed. (In Godot 4, target != null is also false for a freed object, but is_instance_valid says what you mean, and it is the check the docs use.) Setting target = null when it dies works too, but needs someone to remember to do it.
  30. 4. Functions, classes and types in GDScript · Functions are values: Callables and lambdas · QuizWhat does print([1, 2, 3].map(func(n): return n * n)) print?B. [1, 4, 9]map calls the lambda once per item and collects what it returns into a new array: 1×1, 2×2, 3×3. The original array is unchanged. Adding them up to 14 would be reduce; doubling would be n * 2.
  31. 5. Moving in 2D: input and the game loop · Ask Input whether an action is held, pressed or released · QuizThe player holds the jump button for half a second, and the character jumps about 30 times. Which call was probably used?A. is_action_pressedis_action_pressed is true on every frame the button is held: at 60 fps, half a second is 30 frames, so 30 jumps. A jump happens once per press, so it wants is_action_just_pressed. Held actions (running, a machine gun) want is_action_pressed.
  32. 5. Moving in 2D: input and the game loop · get_axis and get_vector turn input into a direction · QuizWhy use get_vector rather than reading two get_axis values into a Vector2 yourself?B. It keeps the length at most 1, so moving diagonally isn’t faster, and applies a dead zoneRight plus down made by hand is (1, 1), whose length is √2 ≈ 1.41, so diagonal movement would be 41% faster. get_vector limits the length to 1 and handles the stick’s dead zone. It does not force length 1: no input stays (0, 0), and a half-tilted stick stays half, which is what you want for walking slowly.
  33. 5. Moving in 2D: input and the game loop · Positions and directions are Vector2s · Fill the blanksMake the enemy step 50 pixels towards the player, and print how far apart they are.1: direction_to, 2: +=, 3: distance_todirection_to gives a direction of length 1, so multiplying by 50 makes a step of exactly 50 pixels, whatever the distance. Subtracting positions, player.position - position, points the right way too, but its length is the whole distance: you would jump straight onto the player.
  34. 5. Moving in 2D: input and the game loop · Multiply by delta to move per second, not per frame · QuizA ship moves with position.x += 200.0 * delta. The game drops from 60 to 30 fps. How far does it move in one second?B. 200 pixelsAt 30 fps delta is about 0.033, twice as big as at 60 fps, so each step is twice as long and there are half as many: 30 × 200 × 0.033 ≈ 200 pixels. That is the point of delta: speeds in pixels per second, the same everywhere.
  35. 5. Moving in 2D: input and the game loop · Velocity and acceleration make movement feel weighty · Fill the blanksAccelerate towards the wanted velocity, then move by the velocity, both per second.1: move_toward, 2: delta, 3: velocitymove_toward(target, amount) moves the vector towards target by at most amount. Acceleration is per second, so the amount for this frame is acceleration * delta. Then the position moves by the velocity, also times delta. Forgetting either delta makes the feel change with the frame rate.
  36. 5. Moving in 2D: input and the game loop · rotation is in radians, and transform.x is forward · QuizA ship drawn facing right has rotation = PI / 2. Which way is transform.x pointing on screen?B. DownA quarter turn from facing right, turning clockwise as positive angles do on a screen whose y points down, faces down: transform.x is (0, 1). In a maths textbook, with y up, the same angle points up, which is why this one surprises people.
  37. 5. Moving in 2D: input and the game loop · Wrapping round the edges of the screen · Find the bugThe ship wraps correctly left and right, but going down it reappears at the top far too early. Which line is wrong?Line 4: position.y = wrapf(position.y, 0.0, screen.y)The vertical position must wrap at the screen’s height, screen.y, not its width. On a 1280 × 720 window, wrapping y at 1280 means the ship flies 560 pixels below the bottom before it comes back. Copy-pasted lines that differ by one letter are where this kind of bug lives.
  38. 6. Physics bodies and collisions · A body collides with the shapes you give it · QuizYou add a StaticBody2D floor with a Sprite2D, but the player falls straight through it. What is most likely missing?B. A CollisionShape2D child with a shapeThe sprite is only a picture; the physics engine knows nothing about it. Without a shape, the body has nothing to collide with, and Godot shows a yellow warning triangle beside it in the Scene dock. Static bodies need no script: they simply exist.
  39. 6. Physics bodies and collisions · CharacterBody2D moves with velocity and move_and_slide · Find the bugThis top-down character crawls at a tiny fraction of its speed. Which line is wrong?Line 8: velocity = direction * speedvelocity is pixels per second, and move_and_slide already multiplies by the step’s time. Multiplying by delta too makes it 60 times slower. The rule from module 5 still holds, but here Godot applies it for you. (Godot 3 passed velocity as an argument, move_and_slide(velocity); in Godot 4 it takes none.)
  40. 6. Physics bodies and collisions · The jump’s height comes from its speed and gravity · QuizYou double jump_velocity and keep gravity the same. What happens to the height of the jump?B. It becomes four times as highHeight is v22g: the speed is squared, so twice the speed gives four times the height (and twice the time in the air). To make a jump twice as high, multiply the speed by 2≈1.41. Many platformers also raise gravity while falling, for a snappier landing.
  41. 6. Physics bodies and collisions · After moving, ask what the body touched · QuizWhere in _physics_process does is_on_floor() give the answer from this step’s movement?B. After move_and_slide()is_on_floor() is set by move_and_slide(). Before the call it still reports the previous step, which is usually fine for deciding whether to jump (the platformer above does that). Code that reacts to landing, like playing a sound, goes after the call.
  42. 6. Physics bodies and collisions · A RigidBody2D is moved by the physics engine · QuizYou set a RigidBody2D’s position every frame to follow the mouse, and it jitters and pushes through things. Why?B. You and the physics engine are both moving it, and the engine can’t solve collisions for a teleportA rigid body belongs to the physics engine. Setting its position teleports it each frame, so the engine finds it overlapping things and shoves back. Push it with impulses and forces instead, or, if your code should be in charge, use a CharacterBody2D or AnimatableBody2D. For direct control of a rigid body’s state, override _integrate_forces(state).
  43. 6. Physics bodies and collisions · An Area2D notices what enters it · Fill the blanksSpikes hurt any player who touches them. Connect the right signal and check the body’s type.1: body_entered, 2: isThe player is a CharacterBody2D, a body, so the signal is body_entered (area_entered is for other areas, such as a sword’s hitbox). is checks the type before calling a method only a Player has; without it, an enemy walking onto the spikes would cause an error.
  44. 6. Physics bodies and collisions · Layers say what you are; masks say what you look for · QuizWith the table above, an enemy walks over a coin. Does the coin’s body_entered fire?B. No: the coin’s mask only has layer 1, and the enemy is on layer 3An area notices the bodies whose layer is in the area’s mask. The coin looks only for layer 1, the player, so enemies pass over coins without picking them up. The enemy’s own mask doesn’t matter for this. In code, set_collision_mask_value(3, true) turns on one numbered layer of the mask.
  45. 6. Physics bodies and collisions · A raycast asks what lies along a line · Fill the blanksThe guard can see the player if a ray from its eyes to the player hits the player first, not a wall.1: target_position, 2: is_colliding, 3: get_collidertarget_position is relative to the ray, so the player’s global position is converted with to_local. A ray normally updates once per physics step; force_raycast_update() checks right now, after moving it. If a wall is in the way, the wall is what it hits, so get_collider() isn’t the player. The ray’s mask must include both walls and the player for this to work.
  46. 7. Signals: nodes talking to each other · Nodes come with signals for their own events · QuizYou want an explosion to delete itself once its one-shot animation, played by an AnimationPlayer, is over. Which signal fits?B. AnimationPlayer.animation_finishedanimation_finished fires exactly when the animation ends, whatever its length. A Timer set to the same length works until someone changes the animation and forgets the timer. tree_exited fires after the node is removed, too late to be the cause of it.
  47. 7. Signals: nodes talking to each other · Connecting a signal in the editor · Put in orderPut the steps for connecting a button’s pressed signal in the editor in order.1. Select the StartButton node in the Scene dock 2. Open the Signals dock (before Godot 4.6, the Node dock’s Signals tab) 3. Double-click pressed() 4. Pick the node whose script should receive it (the menu’s root) and click Connect 5. Write the game’s logic in the new _on_start_button_pressed() functionYou start from the node that emits, because signals are listed on their emitter. The dialog then asks who receives: that node must have a script, which gets the new handler. Godot names it _on_ plus the node and the signal, a convention worth keeping when you connect in code too.
  48. 7. Signals: nodes talking to each other · Connecting in code: signal.connect(handler) · Fill the blanksConnect the start button’s pressed signal to _on_start_pressed in code.1: pressed, 2: connect, 3: _on_start_pressedThe signal, then .connect, then the handler as a Callable: its name with no brackets. Writing _on_start_pressed() with brackets would call it right away and pass its result, nothing, to connect, which is an error. Connect in _ready, so it happens once.
  49. 7. Signals: nodes talking to each other · A handler takes the signal’s arguments · Find the bugWhen the player walks into this area, Godot reports an error instead of printing. Which line is wrong?Line 6: func _on_body_entered(_body: Node2D) -> void:body_entered passes one argument, the body, so the handler must take one, even if it doesn’t use it. A name starting with _ tells Godot it is unused on purpose, which silences the warning. The error only comes when the signal is emitted, which is why it is easy to miss.
  50. 7. Signals: nodes talking to each other · Your own signals: declare, then emit · Fill the blanksDeclare a coin_collected signal that carries the new total, and emit it when a coin is collected.1: signal, 2: emit, 3: coinssignal declares it, with its arguments typed like a function’s parameters. .emit() sends it, passing the values in the same order: here the new total. Emitting the new value saves every listener from reaching back into the player to read it.
  51. 7. Signals: nodes talking to each other · A Timer counts down and emits timeout · QuizA Timer has wait_time 2, one_shot off and autostart on. How many times does it emit timeout in the first 7 seconds?B. 3It starts with the scene and, not being one-shot, restarts after every timeout: at 2, 4 and 6 seconds. The next would be at 8. The first timeout comes after one full wait, not at 0, which is worth remembering for a spawner that should spawn at once.
  52. 7. Signals: nodes talking to each other · await pauses a function until a signal comes · QuizWhat does this print, in order?B. A, C, Bwait_then_print pauses at its await and hands control back, so _ready prints C straight away; one second later the timer fires and B is printed. To make _ready wait for it, write await wait_then_print(). Forgetting that await makes a function return early is the usual surprise.
  53. 7. Signals: nodes talking to each other · Call down, signal up · QuizThe player’s script does get_node("/root/Level/HUD").show_coins(coins). What is the main problem?B. The player breaks if the HUD is renamed, moved, or absent, such as when the player scene runs aloneThe player now depends on one exact layout of one level. Rename the HUD, put the player in another level, or press F6 on player.tscn, and it fails. Emitting coin_collected and letting the level connect it removes the dependency: the player only says what happened.
  54. 8. Instancing, groups and scene composition · preload, load, or an exported slot · QuizYour code works out the next level’s file name from a number. Which do you use to get it?B. load, because the path is only known while runningpreload happens when the script is loaded, before any of your code runs, so its path must be written out as fixed text; Godot refuses a variable there. load runs at that line, with whatever path you built. Godot caches loaded resources, so loading the same path twice doesn’t read the disk twice.
  55. 8. Instancing, groups and scene composition · Spawning a bullet at the gun’s muzzle · QuizWhat goes wrong if shoot() calls add_child(bullet), making each bullet a child of the ship?B. The bullets move and turn with the ship, as children follow their parentA child’s position is relative to its parent (module 2), so when the ship turns, every bullet in flight swings round with it, like on a stick. Bullets, effects and dropped items belong to the world, so they go into the level, or a container node in it such as Projectiles.
  56. 8. Instancing, groups and scene composition · queue_free deletes a node safely · QuizAn enemy has a Sprite2D, a CollisionShape2D and a Health child. You call queue_free() on the enemy. What happens to the children?B. They are freed with itFreeing a node frees its whole subtree, which is why one queue_free() cleans up a complex enemy. Something you want to keep, like a dropped key, should be spawned into the level as its own node, not left as a child of the enemy.
  57. 8. Instancing, groups and scene composition · Inherited scenes share a base and change the details · QuizIn bat.tscn, inherited from enemy.tscn, you want to remove the Shadow node the base has. What happens?B. You can’t delete inherited nodes; hide it, or move it out of the baseAn inherited scene can add and change, but not remove what it inherits. You can switch the node off (visible = false, or disable it), but if half the enemies don’t want a shadow, it doesn’t belong in the base. That limit is one reason to prefer the next idea, composition.
  58. 8. Instancing, groups and scene composition · Groups tag nodes so you can find them all · Find the bugThe “freeze all” power-up does nothing, with no error. Enemies add themselves to "enemies". Which line is wrong?Line 7: get_tree().call_group("enemies", "freeze")There is no group called "enemy", so the call reaches nobody, and that isn’t an error: an empty group is a valid answer. Group names are plain strings, so typos fail silently. Keep the names in one place, such as a constant (const ENEMIES := &"enemies"), and use it everywhere.
  59. 8. Instancing, groups and scene composition · Finding nodes: paths and unique names · QuizA script uses $Panel/ScoreLabel. You put ScoreLabel inside a new MarginContainer in Panel. What happens, and what avoids it?B. The path no longer finds it; marking it as a unique name and using %ScoreLabel would keep workingThe path now ends at Panel/MarginContainer/ScoreLabel, so $Panel/ScoreLabel fails when the script runs. Godot doesn’t rewrite paths in code. A unique name only needs the name, not the route, as long as it stays in the same scene. Use it for the handful of nodes a script reaches into.
  60. 8. Instancing, groups and scene composition · An enemy spawner, from the parts of this module · Put in orderPut the life of a spawned enemy in order.1. The scene is loaded once as a PackedScene 2. instantiate() builds a new enemy 3. add_child() puts it in the tree, and its _ready runs 4. It is placed at a spawn point with global_position 5. It is defeated and calls queue_free()Load once, instantiate many times. The node joins the game, and runs _ready, only when added to the tree; setting its global position right after puts it in the right place before the frame is drawn. Its life ends with queue_free, which also frees its children.
  61. 9. Sprites, animation and tweens · Pixel art needs the nearest filter · QuizYour 16 × 16 pixel-art hero, scaled up 4 times, looks soft and blurry. What fixes it?B. Setting the default texture filter to NearestThe blur comes from linear filtering blending pixels as the image is enlarged. Nearest keeps each pixel a crisp square. Redrawing at 64 × 64 would also look sharp, but it is four times the work and no longer pixel art. For pixel-perfect movement, also look at the 2D snapping settings in Rendering, 2D.
  62. 9. Sprites, animation and tweens · A sprite sheet holds frames in a grid · QuizA sheet has hframes = 8. Which frame is in row 2, column 3, both counted from 0?B. 19Frames are numbered row by row: each full row is 8 frames, so row 2 starts at 2 × 8 = 16, and column 3 is 16 + 3 = 19. Getting 26 means you swapped them (3 × 8 + 2). The rule is: frame = row × hframes + column.
  63. 9. Sprites, animation and tweens · AnimatedSprite2D plays named animations · Fill the blanksPlay the "hurt" animation once, wait for it to end, then go back to "idle".1: play, 2: await, 3: animation_finishedplay starts the animation and await pauses this function until the sprite emits animation_finished. That signal only fires for animations that don’t loop, so turn looping off for hurt in the SpriteFrames panel, or the function waits forever.
  64. 9. Sprites, animation and tweens · Flip the sprite to face where you move · Find the bugThe character faces left while running left, but snaps back to face right the moment it stops. Which line causes it?Line 2: if velocity.x != 0.0: sprite.flip_h = velocity.x < 0.0When the character stops, velocity.x is 0, and 0 < 0 is false, so flip_h turns off and it faces right. Update the facing only while there is horizontal movement; when standing still, leave it as it was. The fix is a one-line if, which GDScript allows for a single statement.
  65. 9. Sprites, animation and tweens · AnimationPlayer animates any property over time · QuizA sword’s hitbox should only hurt during the 0.1 seconds the blade swings. What is the neatest way?B. A property track on the hitbox’s CollisionShape2D disabled, keyed off, on, off in the attack animationKeying the hitbox inside the same animation keeps it exactly in step with the picture: slow the animation down or change its timing and the hitbox follows. A separate timer has to be kept in step by hand, and drifts the first time someone edits the animation.
  66. 9. Sprites, animation and tweens · AnimationTree manages animations as a state machine · QuizWith the state machine above, the character is in idle and the code calls playback.travel("land"). What happens?B. It goes through jump to reach land, the only way theretravel follows the transitions, finding the shortest path through them, so it plays jump on the way to land. That is the point of a state machine: the graph forbids nonsense like landing without jumping. To skip there directly, there is playback.start("land").
  67. 9. Sprites, animation and tweens · A tween animates a value from code · Find the bugGodot reports that the tweened property doesn’t exist, and the sprite never fades. Which line is wrong?Line 3: tween.tween_property($Sprite2D, "modulate:a", 0.0, 0.5)Property paths in Godot use a colon to reach a part of a property: "modulate:a", "position:x". With a dot, Godot looks for a property literally called modulate.a, finds none, and the tween does nothing. Plain properties need no colon: "modulate".
  68. 9. Sprites, animation and tweens · Easing decides how a motion speeds up and slows down · QuizA pause menu slides in from the side. Which ease makes it feel responsive, arriving quickly and settling gently?B. EASE_OUTEASE_OUT moves most at the start, so the menu reacts at once, and slows into place. EASE_IN starts slowly, which reads as lag on something the player asked for; it suits things leaving, like a panel sliding away. Linear motion looks mechanical. Most interface motion is ease-out.
  69. 10. User interfaces with Control nodes · Control nodes are rectangles that lay themselves out · QuizWhich of these should be built from Control nodes?A. A health bar in the corner of the screen; D. A health bar floating above an enemy’s headAnything that is interface, a bar, a label, a button, is a Control, wherever it is drawn. A bar over an enemy is still a ProgressBar, just a child of the enemy so it follows it. The enemy and the crate are game objects, Node2Ds with bodies.
  70. 10. User interfaces with Control nodes · Anchors pin edges to the parent; offsets add pixels · QuizA button has both left and right anchors at 0.5 and offsets of -100 and 100. The window gets wider. What happens to the button?A. It stays 200 pixels wide, centred horizontallyBoth edges are pinned to the middle of the parent (0.5) and pushed 100 pixels either side, so the button is 200 wide and stays centred as the middle moves. For a button that stretches, set the anchors apart, such as 0.1 and 0.9: then its width is a share of the parent’s.
  71. 10. User interfaces with Control nodes · Containers arrange their children for you · QuizYou try to drag a button inside a VBoxContainer to a new spot, and it snaps back. Why?B. The container sets its children’s positions and sizes, overriding yoursInside a container, the container is in charge of layout: position, and size along its direction. You change the result through the container (its separation, its order of children) and through each child’s size flags and minimum size, which is the next slide. To place something freely, keep it out of containers.
  72. 10. User interfaces with Control nodes · Size flags and minimum sizes guide the container · Fill the blanksIn a row, make the name label take all the spare width, and the button at least 120 pixels wide.1: SIZE_EXPAND_FILL, 2: custom_minimum_sizeSIZE_EXPAND_FILL is Expand and Fill together: ask for spare space and stretch into it. Only Expand would reserve the space but leave the label at its own width inside it. A minimum size of 0 for the height lets the container decide that. Both are easier to set in the Inspector, under Layout, but read the same way in code.
  73. 10. User interfaces with Control nodes · A HUD lives on a CanvasLayer · QuizYour score label drifts off screen as the camera follows the player. What fixes it?B. Put the label under a CanvasLayerA CanvasLayer is drawn independently of the camera, which is exactly what screen-fixed UI needs. Making it a child of the camera, or moving it every frame, can be made to work, but breaks with zoom, smoothing and screen shake. A CanvasLayer handles all of them.
  74. 10. User interfaces with Control nodes · Focus makes menus work with keys and gamepads · Find the bugThe menu works with the mouse, but a gamepad can’t select anything. There is no error, only a warning. Which line is wrong?Line 5: %PlayButton.grab_focus()Without brackets, grab_focus is just the method as a value, a Callable, which is then thrown away: nothing is called. Godot warns about a “standalone expression” that does nothing. With () the button takes focus, and the gamepad has somewhere to start. A menu that only works with a mouse is a common bug in games on consoles and handhelds.
  75. 10. User interfaces with Control nodes · Pausing the tree, and what keeps running · Find the bugPressing pause freezes the game and shows the menu, but pressing it again does nothing. Which line is wrong?Line 4: process_mode = Node.PROCESS_MODE_ALWAYSA pausable node stops receiving input once the game is paused, so the menu never hears the second press. PROCESS_MODE_ALWAYS keeps it listening while paused and while playing, which a menu that both opens and closes needs. (WHEN_PAUSED would miss the first press, because the game isn’t paused yet.) Set it in the Inspector, under Process, to see it at a glance.
  76. 10. User interfaces with Control nodes · Themes style every control at once · QuizYou add a full-screen ColorRect over the menu for a fade effect. Now no button can be clicked. What is the cause?B. The ColorRect catches the clicks first; its mouse_filter should be IgnoreControls on top get mouse events first, and by default most controls Stop them, so the clicks never reach the buttons below. Set the overlay’s mouse_filter to Ignore so clicks pass through. Pass handles the event and lets parents see it too. Labels default to Ignore, which is why text over a button doesn’t block it.
  77. 11. Tile maps, cameras and levels · Making a TileSet from a tile sheet · Put in orderPut the steps for setting up tiles in order.1. Add a TileMapLayer node to the level scene 2. Create a new TileSet in its Tile Set property 3. Set the TileSet’s tile size to match the sheet 4. Drag the tile sheet into the TileSet panel and let Godot cut it 5. Switch to the TileMap panel and paint cells in the viewportThe node comes first because the TileSet lives in one of its properties. The tile size has to be right before the sheet is cut, or every tile is sliced in the wrong place. Painting comes last, once there are tiles to paint with. The TileSet panel edits the palette; the TileMap panel paints with it.
  78. 11. Tile maps, cameras and levels · Painting cells, in layers · QuizTiles are 16 × 16 pixels and the layer sits at the origin. What does ground.local_to_map(Vector2(40, 20)) return?B. Vector2i(2, 1)The cell is the position divided by the tile size, rounded down: 40 / 16 = 2.5 → 2, and 20 / 16 = 1.25 → 1. Cells are whole numbers, a Vector2i, never fractions. local_to_map expects a position in the layer’s own space, which is why the code above calls to_local() first: if the layer were moved or scaled, a global position would land on the wrong cell.
  79. 11. Tile maps, cameras and levels · Terrains pick the right edge tile for you · QuizWhat do you mark on each tile so that terrain painting can choose tiles?B. Which of its sides and corners belong to the terrain (its peering bits)Terrain painting compares each cell’s neighbours with each tile’s peering bits: “my top side is grass, my bottom is dirt”. The tile whose bits match the neighbours wins. Collisions and custom data are separate per-tile properties that terrains never look at.
  80. 11. Tile maps, cameras and levels · Tiles that collide, and tiles that carry data · Fill the blanksRead the damage custom data of the cell at pos (a global position).1: local_to_map, 2: get_cell_tile_data, 3: null, 4: get_custom_dataPosition to cell with local_to_map, cell to its TileData with get_cell_tile_data, then the value with get_custom_data, by the custom data layer’s name. The null check matters: an empty cell has no tile data, and calling a method on null stops the game with an error.
  81. 11. Tile maps, cameras and levels · A camera that follows the player · QuizYou want the camera closer, so things look bigger. Which zoom do you set in Godot 4?B. Vector2(2, 2)In Godot 4, zoom is magnification: 2 means twice as big, so you see half as much of the world. Godot 3 had it the other way round (2 meant zoomed out), so old tutorials will mislead you here. Negative zoom flips the view and is never what you want.
  82. 11. Tile maps, cameras and levels · Keep the camera inside the level · Find the bugThe camera is meant to follow the player, but the screen stays still while the player walks off it. Which line is wrong?Line 3: Camera2D (a child of Player)A node moves with its parent. Here the camera is the player’s sibling, a child of the level, so it stays where the level put it. Move it under Player and it follows with no code. (The other way, a script setting global_position = player.global_position every frame, works too, and helps when the camera must stop following, say during a cutscene.)
  83. 11. Tile maps, cameras and levels · Parallax: far things move slower · QuizThe camera moves 100 pixels to the right. How far does a Parallax2D with scroll_scale = Vector2(0.25, 1) scroll on screen?A. 25 pixels, so it looks farther away than the levelScroll scale multiplies the camera’s movement: 100 × 0.25 = 25. Slower means farther away, which is the whole effect. Above 1 a layer moves faster than the level and looks nearer than the player; 0 never moves at all, like a sky.
  84. 11. Tile maps, cameras and levels · Y-sort: lower on screen draws in front · QuizWith Y-sort on, the player’s head draws over the trunk of a tree while their feet are clearly behind it. What’s the likely cause?B. The player’s origin is at the centre of the sprite instead of at the feetY-sort compares the nodes’ origins. With the player’s origin halfway up the body, they count as “lower” than the tree while their feet are still behind its base. Put the origin where the character touches the ground (offset the sprite up, keeping the node at the feet), and do the same for the tree, and the order matches what the eye expects.
  85. 11. Tile maps, cameras and levels · Moving from one level to the next · Fill the blanksFinish the door: export a scene path and change to it when the player enters.1: export_file, 2: change_scene_to_file, 3: call_deferred@export_file shows a file picker in the Inspector, limited by the filter to scenes. change_scene_to_file is a method of the SceneTree, reached with get_tree(). Calling it through call_deferred runs it once the current physics callback has finished, which avoids “can’t change this state while flushing queries” errors.
  86. 12. Sound, particles and game feel · Playing a sound · Find the bugThe coin disappears when picked up, but its chime is never heard. Which line is wrong?Line 8: hide(); set_deferred("monitoring", false); await chime.finished; queue_free()Chime, the node playing the sound, is a child of the coin, so freeing the coin frees Chime in the same frame, and the sound stops before it is heard. Hide the coin and stop it detecting (so it can’t be collected twice), wait for the sound’s finished signal, then free it. Another common fix is to play pickup sounds from a player that outlives the coin, such as one in an autoload.
  87. 12. Sound, particles and game feel · Audio buses: a mixing desk for your game · Fill the blanksSet the Music bus from a slider value between 0 and 1.1: get_bus_index, 2: set_bus_volume_db, 3: linear_to_dbBuses are found by name with AudioServer.get_bus_index, then set with set_bus_volume_db. Passing the slider’s 0.5 straight in would set +0.5 dB, louder than normal, instead of about −6 dB. At 0, linear_to_db gives minus infinity, which is silent; many games also call set_bus_mute there to be sure.
  88. 12. Sound, particles and game feel · Audio buses: a mixing desk for your game · QuizA bus is at 0 dB. You set it to −12 dB. Roughly how big is the signal’s amplitude now?B. A quarterEach −6 dB halves the amplitude, so −12 dB halves it twice: a quarter (10−12/20≈0.25). Decibels add where amplitudes multiply, which is why they’re used for volume. Silence is far lower, near −80 dB.
  89. 12. Sound, particles and game feel · Music that loops and fades · QuizThe music player is in each level scene. Every time the player goes through a door, the music starts again from the beginning. Why?B. Changing scene frees the old level, and its music player with it; the new level starts its ownchange_scene_to_file frees every node in the old scene, so the player and its playback position are gone. To keep music going across levels, the player must live outside the levels: in an autoload, which the next module covers, with a fade_to() like the one above.
  90. 12. Sound, particles and game feel · Particles: many tiny sprites at once · QuizYou want a puff of dust when the player lands: 12 particles, all at once, then nothing. Which settings?A. One Shot on, Explosiveness 1, Emitting set to true on landingOne Shot makes it stop after one cycle; Explosiveness 1 releases all 12 at the same moment instead of spreading them out; setting emitting = true when the player lands fires it, and setting it again later fires another puff. With One Shot off it would leak dust forever, like a chimney.
  91. 12. Sound, particles and game feel · Screen shake, done gently · Fill the blanksAn enemy hit should add 0.4 trauma to the camera.1: get_camera_2d, 2: add_trauma, 3: 0.4get_viewport().get_camera_2d() finds the active 2D camera, wherever it is in the tree, so the enemy doesn’t need a path to it. Then call the method the camera script defines. For a bigger game, a signal on an event bus (module 18) keeps the enemy from knowing about cameras at all.
  92. 12. Sound, particles and game feel · Hit stop: freeze for a few frames · Find the bughit_stop(0.08) should freeze the game for 0.08 seconds, but it freezes for more than a second. Which line is wrong?Line 3: await get_tree().create_timer(seconds, true, false, true).timeoutA timer normally runs in game time, which time_scale has slowed to 5 %. So 0.08 seconds of game time takes 0.08 / 0.05 = 1.6 real seconds. Passing ignore_time_scale = true (the fourth argument) makes the timer count real time. Keep hit stops short: 0.05 to 0.15 seconds feels punchy; more feels like lag.
  93. 12. Sound, particles and game feel · Easing, squash and stretch · QuizA menu panel should slide in quickly and settle softly into place. Which easing fits best?C. Ease out (starts fast, ends slow)Ease out moves most of the way at once, so the panel feels instant, then slows into its final spot, so it doesn’t slam. Ease in does the opposite and feels sluggish at the start and abrupt at the end; it suits things leaving the screen. Linear looks mechanical.
  94. 12. Sound, particles and game feel · Let players turn the juice down · QuizEnemies glow red when they are about to attack. What makes that warning accessible to more players?B. Add a second cue that isn’t colour: a shape over the enemy, or a soundAbout one man in twelve has some colour vision deficiency, and red and green are the usual pair that look alike. Any single colour fails someone. A second channel, an exclamation mark, a wind-up animation, a sound, carries the warning to everyone, and usually makes the game clearer for all players.
  95. 13. Game state, resources and saving · Using an autoload, and not overusing it · QuizWhat happens to an autoload node when the game calls change_scene_to_file()?B. Nothing: it stays in the tree with its variables as they wereChanging scene only replaces the current scene, one child of the root. Autoloads are other children of the root, so they are untouched: same node, same variables, no second _ready(). That is exactly why they hold state across levels.
  96. 13. Game state, resources and saving · Exporting resources, and the shared-copy trap · Find the bugFive slimes share slime_stats.tres. Hitting one slime lowers the health of all five. Which line causes it?Line 6: stats = stats.duplicate()All five stats variables point at the one resource loaded from the file, so line 9 changes it for everyone. Give each slime its own copy when it starts, with stats = stats.duplicate(). Alternatively tick Local to Scene on the resource, which makes each instanced scene get a copy. Better still, keep data that changes during play (current health) in the node, and the resource for what doesn’t (max health).
  97. 13. Game state, resources and saving · Where a game may write: user:// · QuizAn exported game saves to res://save.json. What happens?B. The write fails: res:// is read-only in an exported gameIn the editor res:// is a normal folder, so this seems to work, which is why the bug usually turns up only after exporting. In the exported game, res:// is inside the packed game files and can’t be written: FileAccess.open returns null. Always save to user://.
  98. 13. Game state, resources and saving · Settings in a ConfigFile · Fill the blanksRead the fullscreen setting, defaulting to false if it isn’t there.1: load, 2: OK, 3: get_value, 4: falseload returns an Error, and OK means it worked; on the first run there is no file and it returns ERR_FILE_NOT_FOUND. The third argument of get_value is the default, used when the key is missing, which keeps old settings files working after you add a new option.
  99. 13. Game state, resources and saving · A save file in JSON · QuizYou save {"coins": 3} as JSON and load it back. What type is data["coins"]?B. floatJSON has one kind of number, and Godot parses every number as a float, so 3 comes back as 3.0. Assigning that to a typed int variable usually converts it, but comparing or printing it can surprise you, so convert with int() on purpose. Godot types such as Vector2 don’t survive JSON at all: store x and y as two numbers.
  100. 13. Game state, resources and saving · Saving resources, and why not to load untrusted ones · QuizYour game lets players share level files they built with an in-game editor. Which format is the safe choice?B. JSON parsed with JSON.parse_string, checked before useShared files come from strangers, so they must be pure data. JSON can’t hold code; at worst it holds wrong values, which you check (is it a dictionary, are the numbers in range) before building the level. Loading someone else’s .tres can run their script on your player’s computer.
  101. 13. Game state, resources and saving · Designing saves that last · Put in orderPut loading a save in a safe order.1. Check the save file exists 2. Read its text and parse the JSON 3. Check the result is a dictionary 4. Migrate it to the current version 5. Copy its values into the game state 6. Change to the saved levelEach step guards the next: no file, nothing to parse; broken JSON, nothing to check; the wrong shape, nothing to migrate. Migrating before copying means the rest of the game only ever sees the current format. The scene change comes last, so the new level reads state that is already loaded.
  102. 14. Enemies: state machines and pathfinding · An enemy is always in one state · QuizWhat is the main thing a state machine gives an enemy that a heap of if statements doesn’t?B. Only one behaviour is active at a time, and every change of behaviour is an explicit transitionThe value is clarity: you can’t chase and attack at once, and to find out why the guard gave up you look at one transition. Speed and intelligence don’t come from the pattern; pathfinding is a separate tool, used inside a state such as Chase.
  103. 14. Enemies: state machines and pathfinding · A state machine with an enum and match · Fill the blanksAdd a FLEE state that runs away from the target when health is low.1: FLEE, 2: match, 3: State.FLEEA new state is a new enum value, a new match branch, and at least one transition into it, here from Chase when health drops. direction_to from the target to the slime points away from the target. Forgetting the transition is the usual mistake: the branch exists but nothing ever enters it.
  104. 14. Enemies: state machines and pathfinding · Doing something once on entering a state · QuizInside the Chase branch, which runs every physics frame, the code calls sprite.play("run"). What does the player see?A. The run animation, playing normallyThis one is forgiving: AnimatedSprite2D.play() does nothing new if that animation is already playing, so it runs normally. But many things aren’t forgiving: starting a Timer every frame keeps resetting it, and playing a sound every frame stacks it 60 times a second. That is why “once on entering” belongs in change_state, not in the per-frame code.
  105. 14. Enemies: state machines and pathfinding · Seeing the player: an area, then a ray · Find the bugThe enemies are children of an Enemies node placed at (400, 0). Their rays point at the wrong spot and line of sight is nonsense. Which line is wrong?Line 4: global_position, player.global_position, WALLS)position is relative to the node’s parent; the physics space works in global coordinates. While every parent sits at the origin the two match, so the bug hides, then appears the moment a parent moves. Use global_position for anything you hand to the physics server. A RayCast2D node is the other way to do this: its target_position is local to it, and it updates every physics frame.
  106. 14. Enemies: state machines and pathfinding · Steering: seek, flee and arrive · Fill the blanksWrite flee: steer away from threat at full speed.1: direction_to, 2: global_position, 3: move_towarda.direction_to(b) is the unit vector from a towards b, so from the threat towards the enemy points away from the threat. The same move_toward line as seek keeps the turn smooth. Writing global_position.direction_to(threat) would be seek, the opposite.
  107. 14. Enemies: state machines and pathfinding · Navigation: finding a way around walls · Put in orderPut setting up a navigating enemy in order.1. Add a NavigationRegion2D to the level 2. Draw the walkable outline and bake the navigation mesh 3. Add a NavigationAgent2D as a child of the enemy 4. Set the agent’s target_position to where the enemy should go 5. Each physics frame, move towards get_next_path_position()The walkable area must exist (region, then a baked mesh) before any agent can plan on it. The agent belongs to the enemy, then gets a target, and only then is there a path for get_next_path_position() to follow. Forgetting to bake is the classic mistake: the agent finds no path and the enemy stands still.
  108. 14. Enemies: state machines and pathfinding · Avoidance: crowds that don’t pile up · QuizAvoidance is on, _on_velocity_computed is connected and calls move_and_slide(), but the enemy never moves. The physics code sets velocity (the body’s) towards the next point. What’s wrong?B. It sets the body’s velocity instead of agent.velocity, so the agent never computes a safe velocityThe agent only emits velocity_computed after it is given a wanted velocity through agent.velocity. Setting the body’s own velocity skips the agent entirely, and then the callback overwrites it with whatever it had, zero. Two variables with the same name on two nodes is an easy mix-up.
  109. 14. Enemies: state machines and pathfinding · Cooldowns with timers · Find the bugThis enemy attacks once and never again. Which line is wrong?Line 9: cooldown.start() # moved inside the if, after hit_player()start() restarts the timer from the full wait time. Called every frame, it never gets to 0, so timeout never fires and can_attack stays false. Start the timer once, when the attack happens. Restarting a timer by accident is one of the most common Godot bugs, and the “enter once” idea from earlier is the cure.
  110. 14. Enemies: state machines and pathfinding · Grid pathfinding with AStarGrid2D · QuizA programmer marks the wall cells solid, then sets region and calls update(). Paths now go straight through walls. Why?A. update() rebuilds the grid for the new region and clears the solid cellsupdate() builds the grid’s points from region, cell_size and the other settings, and that resets every cell to walkable. So set the shape, call update(), and only then mark solids. When the map changes during play (a door opens), call set_point_solid(cell, false) on just that cell; no update() needed.
  111. 15. Into 3D: nodes, meshes, lights and cameras · 3D is the same tree with one more axis · QuizIn a 3D scene, which node shows a box you can see?B. MeshInstance3D with a BoxMeshA MeshInstance3D draws a mesh, and BoxMesh is a mesh Godot builds for you. A plain Node3D has a place but nothing to draw, and a CollisionShape3D is for physics: it is invisible in the game. A Sprite2D is a 2D node and does not live in 3D space at all.
  112. 15. Into 3D: nodes, meshes, lights and cameras · Y is up, and forward is minus Z · QuizA Camera3D has no rotation. Which way does it look?B. Along -zEvery node in Godot 3D looks along its own -z, which is why Vector3.FORWARD is (0, 0, -1). The tempting answer is +z, because the axis is called “z”, but +z points out of the screen at you.
  113. 15. Into 3D: nodes, meshes, lights and cameras · A transform is position, rotation and scale · Fill the blanksPut the lamp 3 metres up, and turn it 90 degrees around Y.1: position, 2: 3.0, 3: deg_to_radposition is relative to the parent; y is up, so 3 metres up is (0, 3, 0). rotation is in radians, so convert with deg_to_rad, or write rotation_degrees.y = 90.0. Writing rotation.y = 90.0 is the usual mistake: that is 90 radians, more than 14 full turns, which lands at a strange angle.
  114. 15. Into 3D: nodes, meshes, lights and cameras · The basis tells you which way a node faces · QuizA turret calls look_at(target.global_position, Vector3.UP), and the target stands exactly above it. What goes wrong?B. Godot reports an error, because the direction and the up vector are parallellook_at needs to know which way is up to build the basis, and it cannot if the direction to look is the same line as up: there is no single answer for which way is “right”. Godot prints an error and the node keeps its rotation. Pass another up vector, such as Vector3.FORWARD, in that case, or skip the call when the target is straight overhead.
  115. 15. Into 3D: nodes, meshes, lights and cameras · Meshes: primitive shapes to start with · Find the bugNo ball appears, and Godot complains about a type. Which line is wrong?Line 8: add_child(ball)The sphere is a resource: data that describes a shape, not a node. Only nodes go in the scene tree, so add_child(sphere) is an error (a SphereMesh is not a Node) and the ball, the node that would draw it, is never added. Add ball. Keep the two ideas apart: a resource describes, a node puts it in the world. (height is the full height, so a round sphere has height = 2 * radius.)
  116. 15. Into 3D: nodes, meshes, lights and cameras · Importing models made in other tools · Put in orderPut the steps for using a character model from Blender in order.1. Export it from Blender as a .glb file 2. Copy the file into the Godot project folder 3. Let Godot import it, and check the import settings 4. Make a new scene with a CharacterBody3D root 5. Drag the imported model in as a child, and add a collision shapeThe model becomes a scene only once Godot imports it, and that import is where scale and animations are set. Your own scene then wraps it: the body, collision and script are yours, and the model is just its look, so a new version of the model can replace it without losing your work.
  117. 15. Into 3D: nodes, meshes, lights and cameras · Materials: what a surface is made of · Fill the blanksMake a dull, rusty metal: fully metallic, quite rough.1: StandardMaterial3D, 2: metallic, 3: roughness, 4: material_overridemetallic and roughness both go from 0 to 1. Metal is 1; a rough surface scatters light so it has no sharp highlight. material_override replaces every surface’s material on that one node. A material is a resource, so if several nodes share it, changing it in code changes them all: call duplicate() first when you want one node to look different.
  118. 15. Into 3D: nodes, meshes, lights and cameras · Three kinds of light · QuizYou move a DirectionalLight3D 100 metres to the left without turning it. What changes in the scene?B. Nothing: a directional light only cares about its rotationA directional light stands for something so far away that its rays are parallel, like the sun, so only the direction matters. To make it morning or evening, rotate it. An OmniLight3D is the opposite: only its position matters, and it lights only what is inside its omni_range.
  119. 15. Into 3D: nodes, meshes, lights and cameras · The environment: sky, ambient light and tone · QuizYour level has a sun and a sky, but the sides of buildings facing away from the sun are pitch black. What is the first thing to check?A. The ambient light in the EnvironmentAmbient light is what lights the parts no light reaches directly. If it is off or set to a black colour, shadows are black. Set its source to the sky (or a colour) and raise its energy a little. The camera and collision have nothing to do with how bright a surface is drawn.
  120. 15. Into 3D: nodes, meshes, lights and cameras · The camera decides what you see · Find the bugPlayers say the mountains in the distance pop in and out as they walk. Which line causes it?Line 6: far = 2000.0Anything farther than far is not drawn, so mountains a few hundred metres away vanish and reappear as the player moves near the 50-metre edge. Raise far to cover the level (the default is 4000). Do not push near toward 0 to compensate: a tiny near with a huge far wastes the depth buffer’s precision and makes distant surfaces flicker against each other.
  121. 16. A 3D character: movement, physics and the camera · A character is a CharacterBody3D with a shape and a look · QuizWhy is a capsule the usual collision shape for a walking character?B. Its rounded bottom slides over small steps and edges instead of catching on themA box’s flat bottom and sharp edges catch on the lip of every step and seam between floor pieces; a capsule’s round end rides over them. Any shape works, and the shape is never drawn in the game (only in the editor, or with Visible Collision Shapes in the Debug menu). A CharacterBody3D has no mass at all: you move it yourself.
  122. 16. A 3D character: movement, physics and the camera · Gravity pulls velocity down, a jump sets it up · QuizPlayers want the jump twice as high. What should jump_velocity be, starting from 4.5?B. About 6.4The height of a jump is h=v22g, so it grows with the square of the speed. Twice the height needs 2 times the speed: 4.5×1.414≈6.4. Doubling the speed to 9.0 quadruples the height. Halving gravity also doubles the height, but it makes the whole jump floaty and slow, a different feel.
  123. 16. A 3D character: movement, physics and the camera · Move relative to where the camera looks · QuizA learner writes var dir := Vector3(input.x, 0.0, -input.y). What happens when they push forward?B. The player runs toward the cameraget_vector already gives forward as a negative y, and forward in Godot is negative z, so the sign is right as it is: z = input.y. Negating it turns forward into +z, toward the camera. It is a common slip from engines where forward is +z. When movement feels mirrored, print input and dir and check each sign.
  124. 16. A 3D character: movement, physics and the camera · Mouse look: capture the mouse, read how far it moved · Find the bugWith the mouse captured, the camera turns once and then never moves again. Which line is wrong?Line 3: var dx: float = event.relative.xposition is where the pointer is, and a captured pointer is held still, so it is always about the same number. relative is how far it moved this event, which is what turning needs. Also note the input goes in _unhandled_input, not _process, so a menu that handles the click first keeps it from turning the camera.
  125. 16. A 3D character: movement, physics and the camera · A SpringArm3D keeps the camera out of walls · Put in orderBuild the third-person camera rig, in order.1. Add a Node3D called CameraPivot as a child of the player, at head height 2. Add a SpringArm3D under the pivot, and set its spring_length 3. Add a Camera3D under the spring arm 4. Keep the arm from hitting the player, with its mask or add_excluded_object 5. Turn the pivot from the mouse in _unhandled_inputEach node has one job. The pivot sits where the camera orbits around and holds the yaw and pitch; the arm sets the distance and handles walls; the camera only looks. Turning the pivot swings the arm and the camera with it, because children follow their parent’s transform.
  126. 16. A 3D character: movement, physics and the camera · Turn the model to face where it runs · QuizThe model faces an angle of 3.1 radians and should turn to −3.1. Why use lerp_angle and not lerp?B. lerp would spin it almost a full turn the long way; lerp_angle turns the 0.08 radians acrossAngles wrap: 3.1 and −3.1 are both nearly half a turn, about 0.08 radians apart. Plain lerp sees two numbers 6.2 apart and goes through 0, a full spin the wrong way round. lerp_angle knows angles wrap at a full turn and takes the shorter path.
  127. 16. A 3D character: movement, physics and the camera · Slopes, steps and sticking to the floor · QuizA ramp is 50 degrees steep and floor_max_angle is left at the default. What happens when the player runs at it?B. It counts as a wall: they slide back down and is_on_floor() is false on it50 is more than 45, so the ramp is a wall to move_and_slide(). That is the point of the setting: it stops players climbing cliffs. Raise floor_max_angle (with deg_to_rad(55.0)) if the ramp is meant to be walked, or make it gentler. And since the player is “not on floor” there, they cannot jump off it either.
  128. 16. A 3D character: movement, physics and the camera · RigidBody3D: things physics moves for you · Find the bugCrates fly toward the player when touched, instead of being pushed away. Which line is wrong?Line 8: crate.apply_central_impulse(-hit.get_normal() * push_force)The collision normal points out of the crate’s surface, toward the player. To push the crate away, use the opposite direction, -hit.get_normal(). as RigidBody3D gives null for anything else, the floor, walls, so if crate: skips them. Call it right after move_and_slide(): that call is what fills the list of collisions.
  129. 16. A 3D character: movement, physics and the camera · Area3D and raycasts in 3D · Fill the blanksA RayCast3D under the player points down 1.5 metres. Finish the ground check.1: target_position, 2: is_colliding, 3: get_collision_pointtarget_position is relative to the ray node, so (0, -1.5, 0) is straight down from wherever the ray is. is_colliding() says whether it hit, and get_collision_point() where, in world space. The ray updates once per physics frame; after moving it in code, call force_raycast_update() to check right away.
  130. 16. A 3D character: movement, physics and the camera · Prototype levels with CSG shapes · QuizYou have a CSGBox3D wall. How do you cut a doorway through it?A. Add a child CSGBox3D the size of the door, with operation set to subtractionA child set to subtract removes its volume from the parent, leaving a hole the player can walk through, collision included. It is quick to try ideas with, but CSG is rebuilt whenever it changes and is slower than plain meshes, so when the layout is settled, model the real level or bake the CSG to a mesh.
  131. 17. Shaders and visual effects · A shader is a tiny program the graphics card runs for every pixel · QuizA sprite covers 100 by 100 pixels on screen. How many times does its fragment shader run each frame?C. About 10 000 timesThe fragment shader runs once for every pixel the sprite covers: 100 × 100 = 10 000. The vertex shader runs 4 times, once per corner of its quad. This is why fragment work is where shaders get expensive: a full-screen effect runs for every pixel on the screen, millions of times a frame.
  132. 17. Shaders and visual effects · Your first shader: a canvas_item that changes colours · Fill the blanksTurn the sprite grey: work out its brightness and use it for all three channels.1: canvas_item, 2: fragment, 3: vec3dot multiplies the channels by those weights and adds them up: green counts most because eyes are most sensitive to it. A plain average, (r + g + b) / 3.0, works but makes blues look too bright. Colour is set per pixel, so it belongs in fragment(), not vertex().
  133. 17. Shaders and visual effects · UV: where on the texture this pixel is · QuizWhat does this shader draw on a square sprite?A. Black at the top fading to white at the bottomEach pixel’s grey is its UV.y, which is 0 at the top and 1 at the bottom in a canvas_item. UV.x would fade left to right. The texture is never read, so the picture is gone; only its rectangle is used. Drawing UV as a colour like this is the classic way to debug a shader.
  134. 17. Shaders and visual effects · TIME makes things move · QuizEvery tuft of grass using this shader sways exactly in step, which looks fake. What fixes it?A. Add an offset that differs per tuft, such as its world position, inside the sinTIME is the same for everyone, so the same formula gives the same swing. Add something that differs per tuft, sin(TIME * speed + offset): a uniform set from code, or the world position (in a canvas_item, (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).x in vertex()). cos is the same wave shifted, still in step for everyone.
  135. 17. Shaders and visual effects · Uniforms: knobs your scripts can turn · Find the bugThe sprite turns white on a hit and stays white; Godot reports a missing property. Which line is wrong?Line 5: tw.tween_property(mat, "shader_parameter/flash", 0.0, 0.15)A ShaderMaterial has no property called flash: its uniforms live under shader_parameter/, so the tween’s property path is "shader_parameter/flash". mix(a, b, t) returns a at t = 0 and b at t = 1, so tweening flash from 1 to 0 fades the white out.
  136. 17. Shaders and visual effects · A material is shared until you make it unique · QuizYou hit one slime and all twelve slimes on screen flash white. What is the cause?B. They share one ShaderMaterial, so its flash uniform is the same for allOne material, one set of uniform values, twelve sprites drawing with it. Make the material local to the scene or duplicate it per instance. Sharing is the default for a reason: identical materials can be drawn together, more cheaply. So only make unique what really changes per instance.
  137. 17. Shaders and visual effects · Dissolve: noise decides which pixels vanish first · Fill the blanksFinish the core of the dissolve.1: texture, 2: progress, 3: discardtexture samples the noise at this pixel’s UV, and .r takes the red channel, which is the grey value. discard skips the pixel entirely: not transparent black, simply not drawn. In a 3D spatial shader, discard stops the GPU from skipping hidden pixels early, so it costs more there than in 2D.
  138. 17. Shaders and visual effects · Spatial shaders paint 3D surfaces · QuizYou want a ghost that ignores every light in the scene and is always drawn in its own flat colour. What do you add?A. render_mode unshaded; after the shader_type linerender_mode unshaded skips lighting: ALBEDO is drawn as it is. Roughness only changes how light reflects, so it still depends on lights. canvas_item is for 2D and cannot be used on a 3D mesh. Other render modes include cull_disabled (draw both sides) and blend_add (add onto what is behind, for glows).
  139. 17. Shaders and visual effects · Visual shaders: the same thing as a graph · QuizWhich statements about visual shaders are true?A. They are converted into Godot’s shading language; C. They can use TIME, UV and textures, like text shadersA visual shader is another way to write the same program: Godot generates shader code from the graph, and it runs on the GPU like any other. It has canvas_item, spatial, particles, sky and fog modes, just as text shaders have those shader_types.
  140. 17. Shaders and visual effects · Post-processing: a shader over the whole screen · Find the bugThis shader comes from a Godot 3 tutorial and does not compile in Godot 4. Which line is wrong?Line 4: vec3 c = texture(screen, SCREEN_UV).rgb;Godot 4 removed the built-in SCREEN_TEXTURE. Declare your own sampler, uniform sampler2D screen : hint_screen_texture;, and read that. (The same goes for DEPTH_TEXTURE, now hint_depth_texture.) Old tutorials are full of Godot 3 names; when something does not exist, check the version first.
  141. 17. Shaders and visual effects · What makes shaders slow · Put in orderOrder these from cheapest to most expensive, all at 1920×1080, the same shader on each.1. A 64×64 sprite 2. A 512×512 sprite 3. One full-screen effect 4. Three full-screen effects stacked on top of each otherSame work per pixel, so the cost follows the pixels: about 4 thousand, 262 thousand, 2 million and 6 million. Stacking full-screen passes multiplies the cost, which is why it pays to combine several screen effects into one shader that reads the screen once.
  142. 18. Architecture: autoloads, custom resources and patterns · A game grows, so plan for change · QuizWhy group files by feature (player/, enemies/slime/) rather than by type (scripts/, scenes/, sprites/)?B. A change to one feature touches one folder, and a feature can be moved or deleted as a wholeGodot does not care where files are: any layout works, and the export is the same size. Feature folders help people: the slime’s scene, script and art change together, so they sit together, and deleting the slime is deleting one folder. With type folders, one feature is spread across four places. When you move files, move them in the FileSystem dock, so Godot updates every path that points at them.
  143. 18. Architecture: autoloads, custom resources and patterns · Call down, signal up · Find the bugThis coin only works in one exact tree. Which line breaks the rule?Line 6: collected.emit(1)get_parent().get_parent() assumes the coin is exactly two levels below something with add_score. Put it inside a container node, or in a test scene, and it crashes. Emitting collected lets whoever placed the coin decide what a coin is worth to them. The tempting answer, line 7, is fine: a node freeing itself is its own business.
  144. 18. Architecture: autoloads, custom resources and patterns · An event bus for news the whole game hears · Fill the blanksEmit on the bus from the player, and listen to it from the music player.1: emit, 2: connectA signal is an object: .emit() fires it, .connect(callable) subscribes a function, passed by name without parentheses. The handler then does whatever the music should do, here play_sad_song(). Notice that the player never mentions music, and the music never mentions the player: both know only Events.
  145. 18. Architecture: autoloads, custom resources and patterns · Components: behaviour as child nodes · QuizWhich is the best sign that a piece of behaviour should become a component?B. Several unrelated kinds of object need it, and it works without knowing which one it is inA component earns its place when it is reused across objects that don’t share a base class, and when it can do its job knowing only itself (health counts down; what death means stays in the owner). Length alone isn’t a reason, and splitting something only the player uses into components just adds nodes to look through.
  146. 18. Architecture: autoloads, custom resources and patterns · Custom resources hold the data · QuizTen slimes each have @export var stats: EnemyStats set to the same slime_stats.tres. One slime runs stats.health -= 5. What happens?B. All ten slimes lose health, because they share one resource objectGodot loads a resource file once and every user gets the same object, which is what makes resources cheap. So changing it changes it for everyone. Keep resources for data that doesn’t change while playing (max health, speed), and keep changing values (current health) on the node. If an instance truly needs its own copy, call stats = stats.duplicate() in _ready, or tick Local to Scene on the resource. Nothing is written to disk unless you call ResourceSaver.save.
  147. 18. Architecture: autoloads, custom resources and patterns · The command pattern: actions as objects · Fill the blanksRun a move and remember it; undo the last one.1: new, 2: push_back, 3: is_empty, 4: pop_backMoveCommand.new(by) calls _init(by). push_back (or append) adds to the end, and pop_back removes and returns the last element, so the most recent move is undone first: a stack, last in, first out. Checking is_empty() first matters: pop_back on an empty array returns null, and calling undo on null is an error.
  148. 18. Architecture: autoloads, custom resources and patterns · The state pattern: one object per state · Put in orderPut the steps of a state change in order.1. The current state’s update returns a different state 2. The machine calls exit() on the old state 3. The machine makes the new state the current one 4. The machine calls enter() on the new state 5. Next frame, the new state’s update runsexit runs while the old state is still current, so it can tidy up (stop its animation, its timers). Only then does the new state become current and enter (start its animation). Swapping before exit would call exit on the wrong state, a classic bug that leaves animations or sounds running.
  149. 18. Architecture: autoloads, custom resources and patterns · Testing GDScript with GUT or gdUnit4 · Fill the blanksFinish a GUT test that checks health never goes below zero.1: GutTest, 2: test_, 3: assert_eqThe test script extends GutTest, which gives it the asserts. GUT finds tests by the test_ prefix: a function named health_stops_at_zero would never run, and a test that never runs passes forever. assert_eq(got, expected) fails the test when they differ. In gdUnit4 the same check reads assert_int(health.health).is_equal(0) in a script that extends GdUnitTestSuite.
  150. 19. Performance, debugging and shipping · The debugger stops the game so you can look inside · QuizThe game is paused on the line var path := find_path(start, goal). You only care whether path comes out empty. Which button do you press?B. Step OverStep Over runs the whole line, including all of find_path, and pauses on the next line, where you can read path in the variables list. Step Into would take you inside find_path, line by line, which is what you want only when you suspect find_path itself. Continue runs on to the next breakpoint, maybe far away.
  151. 19. Performance, debugging and shipping · The remote scene tree shows the running game · QuizYou spawn coins from code. During play, the Local tab shows no coins under the level. What does that tell you?B. Nothing: Local is the saved scene, and nodes made by code only appear in RemoteLocal shows what is saved on disk, and code-spawned coins were never part of that file. Switch to Remote to see the live tree. Mixing the two up is a common source of “it isn’t there” confusion.
  152. 19. Performance, debugging and shipping · Printing, warnings and asserts · QuizThis line works while you develop. What happens in the exported release build?B. The key is never removed, because the whole assert line is left out of release buildsRelease builds skip assert entirely, including the expression inside it, so remove_item is never called and the player keeps the key forever. Never put work inside an assert: do the work on its own line, keep the result in a variable, and assert on the variable.
  153. 19. Performance, debugging and shipping · A frame has a budget, measured in milliseconds · Put in orderPut a performance fix in the order that works.1. Find a place in the game that is reliably slow 2. Record it with the profiler 3. Find the function that takes the most time 4. Change that one thing 5. Record the same place again and compareMeasure, change one thing, measure again. Fixing code before measuring means you may speed up something that took 0.1 ms, and changing several things at once means you can’t tell which helped. Always profile the same scene in the same way, so the numbers compare.
  154. 19. Performance, debugging and shipping · Common fixes once you know what is slow · Find the bugThe profiler says this enemy script is slow with 200 enemies. Which line is the problem?Line 6find_child searches recursively by default, so it walks the whole scene tree, every physics frame, for each of 200 enemies: that is thousands of node visits, 60 times a second. Find the player once, in _ready or an @onready variable (for example with a "player" group), and keep it. The maths and move_and_slide on the other lines are cheap.
  155. 19. Performance, debugging and shipping · An object pool reuses nodes instead of freeing them · Fill the blanksA bullet from the pool keeps its old position and direction. Reset it when it is fired.1: take, 2: null, 3: global_positionA pooled object comes back exactly as it was left, so everything a fresh one would have set in _ready must be set again on taking: position, direction, health, timers. Forgetting that is the classic pool bug: a bullet appears where the last one died. Check for null first, since the pool can run dry.
  156. 19. Performance, debugging and shipping · Exporting: presets and export templates · QuizWhat is the difference between a debug export and a release export?B. Debug builds keep checks like assert and can connect to the editor’s debugger; release builds drop them and run fasterA debug export uses the debug template: asserts run, errors are more detailed, and it can talk to the editor for remote debugging. A release export uses the optimised release template and drops that. Test with debug builds; give players release builds, and play-test the release build too, since it is what they actually run.
  157. 19. Performance, debugging and shipping · Desktop, web and mobile each need something different · QuizYou double-click the exported index.html and the game shows an error instead of starting. What is the likely reason?B. A web build must be served over HTTP; the browser blocks it when it’s opened as a local fileBrowsers refuse to load the .wasm and .pck from a file:// page. Serve the folder from a small local web server, or mark the Web preset Runnable and use the Remote Deploy button in the editor’s top-right, which runs the web build in your browser from a server Godot starts for you.
  158. 19. Performance, debugging and shipping · Putting it on itch.io · Put in orderPut shipping a web version in order.1. Download the export templates for your exact Godot version 2. Add a Web preset in Project > Export 3. Export a release build into an empty folder 4. Test it in a browser through a local server 5. Upload the folder as a zip to itch.io as an HTML projectTemplates come first: without them, the Export dialog can’t build. Export into an empty folder so old files don’t ride along. Testing locally before uploading catches missing files and the threads problem while it is still quick to fix.
  159. 20. Expert Godot · Tool scripts run inside the editor · Find the bugThis sprite should move while the game plays, and stay still in the editor. It does the opposite. Which line is wrong?Line 7: if not Engine.is_editor_hint():Engine.is_editor_hint() is true when the code runs in the editor. Here it moves the sprite only in the editor, so it slides away while you work, and the moved position is saved into your scene. The guard must be not: gameplay code runs only in the game. Every @tool script should ask this question in any callback that changes things.
  160. 20. Expert Godot · Editor plugins add docks and tools to Godot · Put in orderPut making a plugin in order.1. In Project Settings > Plugins, press Create New Plugin 2. Give it a name and folder; Godot writes plugin.cfg and the script 3. Write _enter_tree to add what the plugin provides 4. Write _exit_tree to remove all of it 5. Tick the plugin’s Enable box and try itThe Create New Plugin dialog writes the folder, plugin.cfg and a script with empty _enter_tree and _exit_tree, so you start from a working shape. Write both halves before enabling: a plugin that adds without removing leaves pieces behind each time you turn it off and on while developing.
  161. 20. Expert Godot · Custom node types with class_name and an icon · QuizYou want a ShakeCamera node teammates can add from the Create Node dialog, with its own icon. What is the simplest way?B. Give its script class_name ShakeCamera and an @iconclass_name registers a global class the editor lists with the built-in nodes; @icon sets its icon. add_custom_type in a plugin is the older way and is still around, but needs a plugin enabled. GDExtension is for code that must be native. A saved scene works too, but it is added with “Instantiate Child Scene”, not as a node type.
  162. 20. Expert Godot · GDExtension: native code when you need it · QuizWhich is the best reason to move code from GDScript to GDExtension?B. The profiler shows one GDScript function doing heavy maths on thousands of items takes most of the frameGDScript is slow at tight loops of its own, so a measured hot spot of pure computation (generating terrain, a custom pathfinder over a huge grid) is what native code fixes. move_and_slide is already C++ inside the engine, so calling it from GDScript costs little extra. Moving everything to C++ “to be fast” trades a lot of iteration speed for nothing you measured.
  163. 20. Expert Godot · C# in Godot · QuizWhich of these are true of C# in Godot 4? Pick all that are.A. You need the .NET build of Godot and the .NET SDK installed; B. Script classes are declared partial; D. Signals are declared as delegates whose names end in EventHandlerThe standard Godot download has no C#; the .NET build does, and it compiles with the .NET SDK. Classes are partial so generated code can add the glue, and a signal is a delegate named …EventHandler (the signal itself is the name without it, Died). Web export is the catch: Godot 4 cannot yet export C# projects to the web, so a browser game is a reason to choose GDScript.
  164. 20. Expert Godot · High-level multiplayer: peers and a server · Fill the blanksStart a server on port 7000 for up to 4 clients.1: ENetMultiplayerPeer, 2: create_server, 3: multiplayer_peerMake the peer, start it as a server, and give it to the MultiplayerAPI. Nothing is networked until that last assignment. The joining side is the same with create_client(address, 7000).
  165. 20. Expert Godot · RPCs and authority: who may do what · Find the bugEach player node is named after its peer id. Every player moves everyone else’s character and not their own. Which line is wrong?Line 9: if not is_multiplayer_authority():Line 6 makes the peer whose id is the node’s name its authority, in _enter_tree so it is set before anything replicates. Then only that peer should read input for it. The guard is inverted: it returns on the one peer that owns the character, and reads input on all the others. Add not, and each peer drives only its own character.
  166. 20. Expert Godot · RPCs and authority: who may do what · QuizA function is marked with a plain @rpc, and nodes have their default authority. A client calls heal.rpc_id(1, 50). What happens on the server?B. The call is refused: by default only the authority, here the server, may call itA bare @rpc means "authority" mode, and every node’s default authority is the server, so the server may call it on clients but a client may not call it on the server. That is a safe default: to accept requests from clients, mark it "any_peer", and then check in the function that the request is allowed.
  167. 20. Expert Godot · MultiplayerSpawner and MultiplayerSynchronizer · QuizPositions sync from the server to clients, but you want each client to move its own player and send its position to the others. What decides who sends?B. The multiplayer authority of the synchronizer, which you set to that client’s idA synchronizer sends from its authority. set_multiplayer_authority(id) on the player sets it for the player and, by default, all its children, synchronizer included. Client-owned movement feels responsive, but a cheating client can send any position, so competitive games keep the server as authority and send only input.
  168. 20. Expert Godot · Under the nodes: the servers · QuizWhat is the catch with drawing through RenderingServer directly?B. Nothing frees what you create: you must free_rid every RID, and you lose the editor, signals and the scene tree for themNodes free their server objects when they are freed; RIDs you create are yours to free, or they leak until the game closes. And they are invisible to the scene tree: no Inspector, no Remote tab, no collisions unless you also ask the physics server. Reach for servers after the profiler shows that node count is the problem; before that, a MultiMeshInstance2D often solves the same thing more simply.
  169. 20. Expert Godot · Reading Godot’s source and contributing · QuizYou want Godot to have a built-in node for 2D ropes. Where do you start?B. Open a proposal in godot-proposals, describing the problem it solvesNew features begin as proposals, so maintainers and users can agree that the problem is real and the design fits before anyone writes code. A big pull request with no proposal is often closed, however good. Many such ideas end up better as an add-on in the Asset Library, which you can ship today.
  170. 20. Expert Godot · Mastery: putting it all together · QuizBarrels, crates and slimes can all be destroyed, each drops loot from its own list, and the HUD counts how many things you broke. Which design fits Godot best?B. A HealthComponent child on each, a LootTable resource per kind, and an Events.thing_destroyed signal the HUD listens toThey have different base classes (a slime moves, a crate doesn’t), so a shared base class can’t work: composition can. Loot differs only in data, so it belongs in resources. The HUD is far away in the tree, so game-wide news goes through the event bus rather than objects reaching for the HUD, which would break the “call down, signal up” rule.
  171. 20. Expert Godot · Mastery: putting it all together · QuizYour game runs at 60 FPS in the editor but stutters in the web build when many enemies are on screen. What do you do first?B. Profile a debug web build (or the same scene on desktop) to see whether scripts, physics or drawing take the timeMeasure before changing anything: the fix for slow scripts (cache, pool, do less per frame) is different from the fix for drawing (fewer draw calls, simpler materials). Web builds run on the Compatibility renderer and are generally slower, so a scene with little spare time on desktop may run out of it there. GDExtension is only a reason once a script hot spot is measured.
  172. 20. Expert Godot · Mastery: putting it all together · QuizA @tool script on each level reads its LevelData resource and, in the editor, sets data.enemy_count from the enemies in the scene. Two levels share one .tres. What goes wrong?B. Both levels write to the same shared resource, so whichever level you opened last decides the count for bothOne resource file is one shared object, in the editor as in the game, and a tool script really changes it. Give each level its own resource, or compute the count when it is needed instead of storing it. Two ideas from different modules, tool scripts and shared resources, combine into a bug neither shows alone.
  173. 20. Expert Godot · Mastery: putting it all together · Put in orderA client joins a game with a spawner. Put what happens in order.1. The client calls create_client and sets multiplayer.multiplayer_peer 2. The server’s peer_connected signal fires with the client’s id 3. The server instantiates a player named after that id and adds it under the spawner’s path 4. The spawner creates the same player on every peer 5. The player’s _enter_tree sets its multiplayer authority to that idConnecting comes first, and the server hears about it through peer_connected. Only the server adds players, under the spawner’s spawn_path, so they are replicated. On each peer, the new player enters the tree and sets its authority from its name, which is why naming it after the peer id matters: every peer can work out the same owner without another message.