A Godot scene is a tree of typed Nodes; there is no empty container you bolt components onto.
var go = new GameObject("Player");
go.AddComponent<SpriteRenderer>();var player := Node2D.new()
add_child(player)Type a Unity or Unreal concept and see its Godot 4.7 equivalent with side-by-side code.
Want to check what you remember? Practice matching engine terms.
61 results
A Godot scene is a tree of typed Nodes; there is no empty container you bolt components onto.
var go = new GameObject("Player");
go.AddComponent<SpriteRenderer>();var player := Node2D.new()
add_child(player)A saved scene is Godot's prefab: instance it at runtime or drop it into another scene.
Instantiate(prefab, pos, Quaternion.identity);@export var enemy_scene: PackedScene
var e := enemy_scene.instantiate()
add_child(e)Composition happens by adding typed child nodes to the tree, not by attaching components to a container object.
public class Health : MonoBehaviour { public int hp = 100; }
// attached in the Inspector# health.gd attached as a child Node
class_name Health
extends Node
@export var hp: int = 100Node2D and Node3D each carry position, rotation, and scale directly — there is no separate Transform component to fetch first.
transform.position = new Vector3(0, 5, 0);
transform.Rotate(0, 90, 0);position = Vector3(0, 5, 0)
rotate_y(deg_to_rad(90))Use a bare Node for a non-visual container, or Marker2D/Marker3D when you also want the editor gizmo for a reference point.
var anchor = new GameObject("SpawnPoint");# SpawnPoint as a Marker3D child in the scene tree
@onready var spawn_point: Marker3D = $SpawnPointPer-frame logic; use _physics_process for physics-step logic.
void Update() { transform.position += v * Time.deltaTime; }func _process(delta: float) -> void:
position += v * delta_init() runs when the object is constructed, before it enters the scene tree — child nodes and @onready vars aren't ready yet.
void Awake() { instanceId = System.Guid.NewGuid(); }func _init() -> void:
instance_id = Time.get_ticks_usec()_ready() fires once a node and all its children have entered the scene tree — the right place for @onready-dependent setup.
void Start() { targetHp = maxHp; }func _ready() -> void:
target_hp = max_hpRuns on the fixed physics tick — put movement, collision, and RigidBody-adjacent code here instead of _process.
void FixedUpdate() { rb.AddForce(Vector3.up * jumpForce); }func _physics_process(delta: float) -> void:
velocity.y += jump_force * deltaFires when the node is removed from the scene tree — the place to disconnect signals or release external resources.
void OnDestroy() { EventBus.OnDied -= HandleDied; }func _exit_tree() -> void:
EventBus.died.disconnect(_on_died)There's no direct enable/disable callback pair; hook tree_entered/tree_exiting for lifecycle, or visibility_changed for show/hide-driven logic.
void OnEnable() { spawner.Register(this); }
void OnDisable() { spawner.Unregister(this); }func _ready() -> void:
tree_entered.connect(_on_tree_entered)
tree_exiting.connect(_on_tree_exiting)Reference named child nodes directly instead of searching by type at runtime.
var body = GetComponent<Rigidbody2D>();@onready var body: CharacterBody2D = $CharacterBody2Dawait a signal or a scene-tree timer instead of an IEnumerator coroutine.
yield return new WaitForSeconds(1f);await get_tree().create_timer(1.0).timeoutDeclare a signal, emit it, and connect listeners — no manual unsubscribe needed.
public UnityEvent onDied;
onDied.Invoke();signal died
died.emit()There's no universal base behavior class — any GDScript file that extends a Node subclass becomes that node's behavior.
public class Player : MonoBehaviour { }class_name Player
extends CharacterBody2DLoad or @export a PackedScene, call instantiate(), then add_child() it into the tree.
var bullet = Instantiate(bulletPrefab, muzzle.position, Quaternion.identity);var bullet := bullet_scene.instantiate()
bullet.global_position = muzzle.global_position
get_tree().current_scene.add_child(bullet)queue_free() frees the node at the end of the current frame; use free() only when you need it gone immediately and know it's safe.
Destroy(gameObject);queue_free()SendMessage's stringly-typed broadcast has no direct match — prefer a declared signal, or call a method directly if you hold a reference.
gameObject.SendMessage("TakeDamage", 10);if target.has_method("take_damage"):
target.take_damage(10)One-shot delayed calls use get_tree().create_timer(...).timeout; recurring calls use a Timer node with autostart and wait_time.
Invoke(nameof(Explode), 2f);await get_tree().create_timer(2.0).timeout
explode()@export exposes a typed variable to the Inspector and to scene/resource serialization.
[SerializeField] private float moveSpeed = 5f;@export var move_speed: float = 5.0Register a script as an Autoload (Project Settings) and it becomes a globally-accessible node singleton — no static-field workaround needed.
public static GameManager Instance;
void Awake() { Instance = this; }# res://autoload/game_manager.gd registered as "GameManager" Autoload
GameManager.score += 10Full physics-simulated bodies; apply forces/impulses instead of setting position directly.
rb.AddForce(Vector3.up * force, ForceMode.Impulse);apply_central_impulse(Vector3.UP * force)Set the velocity property, then call move_and_slide() — it reads and writes the velocity property itself.
controller.Move(velocity * Time.deltaTime);velocity.y -= gravity * delta
move_and_slide()Connect the body_entered signal on the physics body instead of overriding a collision callback.
void OnCollisionEnter(Collision c) { TakeDamage(c.impulse.magnitude); }func _on_body_entered(body: Node) -> void:
take_damage(10)Areas are Godot's trigger volumes — connect body_entered or area_entered instead of flagging a collider IsTrigger.
void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) Collect(); }func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
collect()collision_layer declares what a body IS; collision_mask declares what it SCANS for — both are 32-bit flag sets.
if (Physics.Raycast(origin, dir, out hit, 100f, enemyLayerMask)) { }$Area3D.collision_layer = 1 << 3 # this body is on layer 4
$Area3D.collision_mask = 1 << 2 # it scans layer 3 onlyUse a RayCast node for continuous checks, or build a PhysicsRayQueryParameters and call intersect_ray() for one-off queries.
if (Physics.Raycast(origin, dir, out RaycastHit hit, 100f)) { }var query := PhysicsRayQueryParameters3D.create(origin, origin + dir * 100.0)
var hit := get_world_3d().direct_space_state.intersect_ray(query)ShapeCast nodes sweep a shape continuously; intersect_shape() does a one-off query with a PhysicsShapeQueryParameters.
Collider[] hits = Physics.OverlapSphere(pos, radius, mask);var params := PhysicsShapeQueryParameters3D.new()
params.shape = SphereShape3D.new()
var hits := get_world_3d().direct_space_state.intersect_shape(params)get_vector() combines two axes into a single deadzone-normalized Vector2 in one call.
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");var dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")True for exactly one frame — the frame the mapped action was pressed.
if (Input.GetButtonDown("Jump")) Jump();if Input.is_action_just_pressed("jump"):
jump()Define named actions in Project Settings -> Input Map, then read them with the Input singleton — no separate asset/context system needed.
playerInput.actions["Jump"].performed += OnJump;# Input Map: action "jump" mapped to Space / gamepad A
if Input.is_action_just_pressed("jump"):
jump()An AnimationTree with a state-machine root replaces the Animator Controller's states and transitions.
animator.SetBool("isRunning", true);
animator.SetTrigger("jump");$AnimationTree["parameters/conditions/is_running"] = true
$AnimationTree["parameters/playback"].travel("jump")BlendSpace1D blends along one parameter (e.g. speed); BlendSpace2D blends across two (e.g. move direction).
// Blend Tree node mixing Idle/Walk/Run by a "speed" float$AnimationTree["parameters/Blend/blend_position"] = speed / max_speedAn AnimationPlayer holds named Animation resources with keyed tracks — play them by name.
animator.Play("Attack");$AnimationPlayer.play("attack")An Anim Blueprint's event graph plus state machine maps to an AnimationTree paired with a regular GDScript for logic.
// Anim Blueprint: AnimGraph blends poses driven by a state machine# AnimationTree node with a State Machine root, driven from a script
$AnimationTree.active = trueSubclass Resource with @export vars for data-driven, savable assets.
[CreateAssetMenu]
public class WeaponData : ScriptableObject { public int damage; }class_name WeaponData extends Resource
@export var damage: int = 0Background-load a resource by path, then poll status and fetch it once ready — no addressable asset groups to configure.
var handle = Addressables.LoadAssetAsync<GameObject>("enemy");
await handle.Task;ResourceLoader.load_threaded_request("res://enemies/goblin.tscn")
# poll later:
if ResourceLoader.load_threaded_get_status("res://enemies/goblin.tscn") == ResourceLoader.THREAD_LOAD_LOADED:
var scene: PackedScene = ResourceLoader.load_threaded_get("res://enemies/goblin.tscn")preload() resolves at parse time (path must be a literal); load() resolves at runtime, like a dynamic Resources.Load call.
var tex = Resources.Load<Texture2D>("icons/sword");var icon := preload("res://icons/sword.png")
# or dynamically:
var tex: Texture2D = load(path)StandardMaterial3D exposes the built-in PBR shader's parameters without writing shader code.
renderer.material.SetColor("_Color", Color.red);var mat := StandardMaterial3D.new()
mat.albedo_color = Color.RED
$MeshInstance3D.material_override = matVisualShader gives a node-graph editor like Unreal's Material Editor; ShaderMaterial is what you assign once the graph (or raw shader code) is authored.
// Material Editor node graph compiled into a Material assetvar mat := ShaderMaterial.new()
mat.shader = preload("res://shaders/dissolve.gdshader")
$MeshInstance3D.material_override = matSwaps the entire current scene tree for a new one loaded from a .tscn path.
SceneManager.LoadScene("Level2");get_tree().change_scene_to_file("res://levels/level_2.tscn")There's no separate additive-load API — instancing a scene and parenting it into the running tree is inherently additive.
SceneManager.LoadScene("UI_HUD", LoadSceneMode.Additive);var hud := preload("res://ui/hud.tscn").instantiate()
add_child(hud)Autoload nodes live outside the current scene and survive scene changes automatically — nothing to mark persistent.
void Awake() { DontDestroyOnLoad(gameObject); }# res://autoload/music_player.gd registered as an Autoload — survives change_scene_to_file()A Godot 'scene' can be an entire level, a prefab-like reusable piece, or a single node — Levels map to top-level scenes loaded via change_scene_to_file().
// A Level asset (.umap) containing placed Actors# levels/forest.tscn -- a scene tree rooted at Node3D holding the level contentPick the flavor by dimensionality: plain AudioStreamPlayer for UI/music, 2D or 3D for positional sound.
audioSource.PlayOneShot(explosionClip);$AudioStreamPlayer3D.stream = explosion_sound
$AudioStreamPlayer3D.play()If no AudioListener2D/3D is made current, the active Camera acts as the listener; add one explicitly to decouple hearing position from the camera.
// AudioListener component on the Main Camera$AudioListener3D.make_current()Annotate a method with @rpc(...) and call it directly — the annotation configures authority, reliability, and call mode.
[ServerRpc] void RequestFireServerRpc() { }@rpc("any_peer", "reliable")
func request_fire() -> void:
passList properties in a MultiplayerSynchronizer's replication config instead of wrapping each field in a NetworkVariable type.
NetworkVariable<int> health = new NetworkVariable<int>();# MultiplayerSynchronizer node with "health" added to its replication property listA MultiplayerSpawner watches a parent node and auto-replicates scenes instanced under it to peers.
networkObject.Spawn();# MultiplayerSpawner configured with spawn_path + spawnable_scenes
$MultiplayerSpawner.spawn(enemy_data)Combine a MultiplayerSynchronizer for property replication with @rpc-annotated methods for replicated function calls.
UPROPERTY(Replicated) int32 Health;
void AMyActor::GetLifetimeReplicatedProps(...) { DOREPLIFETIME(AMyActor, Health); }# MultiplayerSynchronizer replicates `health`; RPCs handle replicated actions
@rpc("authority", "call_local")
func apply_damage(amount: int) -> void:
health -= amountBuild UI from Control nodes; a reusable widget is just a saved Control scene, styled with a Theme.
// Canvas + RectTransform anchors in the Inspector# Control scene: VBoxContainer > Label + Button, styled by a Theme resourcePausing stops _process/_physics_process on pausable nodes; set a node's process_mode to PROCESS_MODE_ALWAYS to keep it running while paused.
Time.timeScale = 0f;get_tree().paused = true
$PauseMenu.process_mode = Node.PROCESS_MODE_ALWAYS_process and _physics_process each receive delta as an argument instead of a global property.
transform.position += velocity * Time.deltaTime;func _process(delta: float) -> void:
position += velocity * deltaThere's no global 'main camera' singleton — ask the viewport for its current camera, and it can return null.
var screenPoint = Camera.main.WorldToScreenPoint(target.position);var cam := get_viewport().get_camera_3d()
if cam:
var screen_point := cam.unproject_position(target.global_position)print() writes to the Output panel/stdout; print_debug() additionally appends the calling function and line.
Debug.Log($"Score: {score}");print("Score: ", score)ConfigFile reads/writes simple section/key/value data to disk — a lightweight PlayerPrefs-style store for small settings and save data.
PlayerPrefs.SetInt("highScore", 1200);
PlayerPrefs.Save();var cfg := ConfigFile.new()
cfg.set_value("scores", "high_score", 1200)
cfg.save("user://save.cfg")Groups are Godot's tag system — a node can belong to many groups at once, unlike Unity's single-tag-per-object default.
if (other.CompareTag("Enemy")) { }if body.is_in_group("enemy"):
passThere's no built-in GameMode concept; an Autoload script holding rules and state (turn order, win conditions) fills the same role.
AGameModeBase* GM = GetWorld()->GetAuthGameMode();# res://autoload/match_rules.gd registered as "MatchRules" Autoload
MatchRules.start_round()Godot's Input Map plays the same role as an Enhanced Input Action plus Mapping Context pair, without a separate context-priority stack.
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &APlayer::Jump);if Input.is_action_just_pressed("jump"):
jump()`as` performs a safe downcast that returns null on failure; `is` just tests the type without casting.
AEnemy* Enemy = Cast<AEnemy>(OtherActor);
if (Enemy) { }var enemy := other as Enemy
if enemy:
pass
# or: if other is Enemy:Model row-based data as an array of custom Resource instances (or a Dictionary keyed by id) instead of a DataTable asset plus row struct.
UDataTable* ItemTable;
FItemRow* Row = ItemTable->FindRow<FItemRow>(RowName, TEXT(""));@export var items: Array[ItemData] # ItemData extends Resource
func find_item(id: String) -> ItemData:
return items.filter(func(i): return i.id == id).front()