Code translation lab

C# to GDScript Code Lab

Compare familiar Unity C# patterns against concise Godot 4 GDScript equivalents. Pick a topic, scan the differences, then copy the Godot version into your own scene script.

Live comparison

Variables

Unity C# to GDScript
Coached translation

Translation Workshop

You are turning a tuned Unity or Unreal component into a Godot script that designers can still adjust in the Inspector.

0/3 proofs
Habit shift

Godot exports variables from the script that owns the node. The important shift is deciding what belongs as editable data and what should stay internal state.

Unity C# habit Serialized fields live on a component or reflected C++ property.
Godot habit @export exposes node-owned data; := keeps local state readable without ceremony.
Unity C#
123456
public float speed = 5.0f;
private int health = 100;
[SerializeField]
private GameObject target;
[SerializeField]
private Sprite[] sprites;
GDScript
1234
var speed := 5.0
var health := 100
@export var target: Node
@export var sprites: Array[Texture2D]
@export = shows in Inspector (like [SerializeField])
Type inference with := (optional but recommended)

Quick Reference

_ready() = Start() / BeginPlay()
_process(delta) = Update() / Tick()
extends Node = : MonoBehaviour
@export var = [SerializeField]
$NodePath = GetComponent / FindChild
queue_free() = Destroy()
What changes between C# and GDScript

All 15 lab topics at a glance

Most of the translation is mechanical once three habits change. First, there is no component lookup: a script is the node it sits on, and child nodes are reached with $Path or get_node(). Second, lifecycle and event plumbing move to _ready(), _process(delta), and signals, so there are no delegates or UnityEvents to wire in the Inspector. Third, types are optional but worth writing: var speed := 5.0 and func hit(damage: int) -> void give you autocompletion and catch the same mistakes the C# compiler would. Each topic below lists the differences the lab highlights; open one to see the full side-by-side code.

Variables

  • @export = shows in Inspector (like [SerializeField])
  • Type inference with := (optional but recommended)

Functions

  • No semicolons! Indentation matters (like Python)
  • _ready() = Start(), _process() = Update()

Classes

  • extends = inheritance, class_name = global access
  • No public/private keywords - use _ prefix for "private"

Signals

  • Signals are built-in! No need for third-party events
  • Connect via editor or code: signal.connect(callable)

Input

  • Define actions in Project > Input Map
  • is_action_pressed() for held, is_action_just_pressed() for down

Instantiate

  • PackedScene = Prefab. Use .instantiate() to create instance
  • Must add_child() to scene tree for it to be active

Physics

  • CharacterBody2D has built-in move_and_slide()
  • Use _physics_process() for physics, not _process()

UI

  • @onready = GetComponent in Start/BeginPlay
  • $ is shorthand for get_node("path")

Scene Tree

  • $ = get_node() shorthand. $Enemy = get_node("Enemy")
  • Unique names: %Enemy = get_node("%Enemy") finds anywhere in tree

Resources

  • preload() = compile-time, load() = runtime. Use preload when possible
  • Custom Resources (.tres) = ScriptableObjects/DataAssets

Async/Timers

  • await signal - pause until signal emits. Clean async code!
  • get_tree().create_timer() = quick one-shot delay

Animation

  • AnimationPlayer = simple playback. AnimationTree = blending & state machines
  • Use .queue() to play animations sequentially

Audio

  • AudioStreamPlayer = 2D UI sound, AudioStreamPlayer2D = positional
  • Use Audio Bus for mixing, effects, and volume control

Tweening

  • Tweens auto-cleanup when done. create_tween() binds to node lifetime
  • Chain methods: set_ease(), set_trans(), set_delay()

Debugging

  • 4.6+ adds Step Out (Shift+F11) and ObjectDB snapshots for memory leak detection
  • Tracy profiler integration lets you trace engine + game code externally