← Back to Blog

I've been working with Godot since version 3.0, and when 4.0 was announced, I was excited but cautious. A complete rendering rewrite, new scripting features, and breaking changes meant migration wouldn't be trivial. After porting three projects from 3.x to 4.x, here's what I learned.

The Big Picture: What Changed

Godot 4 isn't just an incremental update—it's a fundamental rewrite:

Reality Check: Budget at least 2-4 weeks for a medium-sized project migration. Small projects might take a few days, but complex ones with custom shaders can take months.

The Automated Migration Tool

Godot 4 includes an automatic conversion tool when you open a 3.x project. It handles:

But it's not magic. Expect to fix:

Shader Migration: The Big One

This was my biggest pain point. Godot 4's shader language changed significantly:

// Godot 3.x
shader_type canvas_item;

void fragment() {
    COLOR = texture(TEXTURE, UV);
    COLOR.a *= MODULATE.a;
}

// Godot 4.x
shader_type canvas_item;

void fragment() {
    COLOR = texture(TEXTURE, UV);
    COLOR *= COLOR.a;  // Modulate is gone, use COLOR directly
}

Key shader changes:

Script Migration Patterns

Here are the most common script changes you'll need:

Export Variables

# Godot 3.x
export var speed = 10.0
export(String, "walk", "run", "sprint") var move_mode
export(NodePath) var target_path

# Godot 4.x
@export var speed: float = 10.0
@export_enum("walk", "run", "sprint") var move_mode: String
@export var target_path: NodePath

Signal Connections

# Godot 3.x
connect("body_entered", self, "_on_body_entered")

# Godot 4.x
body_entered.connect(_on_body_entered)

Node Instantiation

# Godot 3.x
var scene = preload("res://scene.tscn")
var instance = scene.instance()
add_child(instance)

# Godot 4.x
var scene = preload("res://scene.tscn")
var instance = scene.instantiate()
add_child(instance)

Physics Changes

3D physics got a complete rewrite. If you used custom physics queries or advanced features:

# Godot 3.x
var space_state = get_world().direct_space_state
var result = space_state.intersect_ray(from, to)

# Godot 4.x
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(from, to)
var result = space_state.intersect_ray(query)

This pattern applies to all physics queries. The new system is more flexible but requires more setup.

Performance Improvements

Despite the migration pain, Godot 4 brought real performance gains:

In Tumblefire, I saw frame rates improve by 30% just from the migration, with no code changes.

New Features Worth Using

Once you're migrated, take advantage of new features:

WorkerThreadPool

# Easy background processing
func generate_terrain():
    WorkerThreadPool.add_task(_generate_terrain_async)

func _generate_terrain_async():
    # Runs on background thread
    var terrain = heavy_generation_function()
    # Call back to main thread
    call_deferred("_on_terrain_ready", terrain)

Improved Signals

# Signals with types
signal health_changed(new_health: int, max_health: int)

# Lambda connections
button.pressed.connect(func(): print("Pressed!"))

Better Animation Tools

AnimationTree got major improvements. The state machine is clearer, and blend trees are more intuitive.

Common Gotchas

Watch out for these issues that bite everyone:

  1. TileMap changes: Tilemaps were completely redesigned. You'll need to recreate them.
  2. Light3D vs OmniLight3D: Different light types now, not just properties
  3. Input changes: Some input event properties renamed
  4. ResourceLoader: Now requires full paths more strictly
  5. FileAccess: Replaces File class with different API

Migration Strategy

Here's how I approached migration:

  1. Make a branch: Never migrate your main branch directly
  2. Run the auto-converter: Let Godot do the easy work
  3. Fix compilation errors: Start with scripts that won't compile
  4. Update shaders: This takes the longest, do it incrementally
  5. Test thoroughly: Bugs can hide in subtle behavior changes
  6. Check plugins: Make sure all addons have 4.x versions
Pro Tip: Keep Godot 3.x and 4.x installed side-by-side. You'll need to reference the old project often.

Plugin Compatibility

This was painful. Many popular plugins weren't ready when 4.0 launched:

Should You Migrate?

Depends on your project status:

Migrate if:

Stay on 3.x if:

Godot 4.3+ Improvements

If you're migrating now, good news: later 4.x versions fixed many early issues:

Final Thoughts

Migration was painful but worth it. The new renderer is fantastic, the performance improvements are real, and the engine feels more mature. If you're on the fence, I'd recommend migrating—but budget time for it.

My projects run faster, look better, and the codebase is cleaner thanks to typed GDScript. The pain was temporary, the benefits are permanent.

Resources: The official Godot docs have a comprehensive migration guide. Also check the community forums—someone has probably solved your specific issue.
← Back to Blog