Zurück zum Blog

Godot 4.7 Neue Funktionen enthüllt: Die Performance-Updates von Dev3 und Dev4 im Detail

Veröffentlicht am 14. April 2026
Godot 4.7 Neue Funktionen enthüllt: Die Performance-Updates von Dev3 und Dev4 im Detail

Godot 4.7 Neue Funktionen enthüllt: Die Performance-Updates von Dev3 und Dev4 im Detail

Jeder Indie-Entwickler, der ein Multiplayer-Spiel betreibt, kennt den genauen Moment, in dem sein Netcode anfängt, Phantom-Desyncs und unvorhersehbare Physik-Ruckler zu produzieren. Man verbringt Wochen damit, einen straffen Gameplay-Loop zu entwickeln, nur um dann festzustellen, dass die Synchronisierung von Zuständen über eine wackelige Verbindung eine völlig andere technische Denkweise erfordert. Mit der kürzlichen Veröffentlichung der Dev3- und Dev4-Snapshots verschieben die Kernentwickler der Engine die Grenzen, und das Verständnis dieser neuen Godot 4.7 Features ist für Studios, die ihre Produktionszeitpläne planen, von entscheidender Bedeutung.

Godot 4 befindet sich seit seiner massiven Kernüberarbeitung auf einem unaufhaltsamen Vormarsch. Während stabile Versionen das Fundament der Produktion bilden, bieten die Entwicklungs-Snapshots – insbesondere der Sprung von Dev2 zu den frisch gebackenen Dev3 und Dev4 – einen transparenten Einblick in die architektonischen Prioritäten der Engine. Für technische Entwickler sind diese Updates nicht nur Patch Notes; sie sind frühzeitige Warnungen, um die eigenen Pipelines für Netzwerk, Rendering und Speicherverwaltung anzupassen.

In dieser detaillierten Analyse werden wir die technischen Realitäten einer Projektaktualisierung entpacken, zeigen, wie man GDScript für Server-autoritären Multiplayer nutzt, und erklären, warum die ständige Weiterentwicklung der Engine einen intelligenteren Ansatz für die Backend-Infrastruktur erfordert.

Den Godot-Release-Zyklus entschlüsseln: Was Dev-Builds wirklich bedeuten

Die Migration eines in Produktion befindlichen Spiels auf einen Development-Build ist ein kalkuliertes Risiko. Ein "Dev"-Snapshot in der Nomenklatur von Godot bedeutet, dass der Feature-Freeze noch nicht stattgefunden hat. Die API könnte sich verschieben, das Verhalten von Nodes könnte geändert werden, und undokumentierte Regressionen sind praktisch garantiert.

Diese Builds zu ignorieren bedeutet jedoch, die Entwicklungslinie der Engine zu ignorieren. Der Übergang zu Godot 4.7 konzentriert sich stark auf die Stabilisierung der massiven Neuerungen, die in den Versionen 4.3 bis 4.6 eingeführt wurden. Wir beobachten eine deutliche Verlagerung hin zu Performance-Profiling, deterministischem Physikverhalten und optimierter Multiplayer-Synchronisation.

Für einen Solo-Entwickler oder ein kleines Team ist der Hauptschmerzpunkt in der Regel nicht das Schreiben der Spiellogik – es ist herauszufinden, warum eine Szene, die auf einem lokalen Rechner mit 144 FPS läuft, plötzlich auf 45 FPS abfällt, wenn sie über ein Netzwerk instanziiert wird, oder warum Pausen bei der Garbage Collection während intensiver Kampfsequenzen Mikroruckler verursachen. Die Updates, die in diesen Dev-Builds auftauchen, zielen direkt auf die Engpässe bei der Durchquerung des Node-Trees und den internen Speicherallokatoren ab.

Die wahren Kosten von Engine-Upgrades

Ein Upgrade der Engine-Version mitten in der Entwicklung kostet ein Team in der Regel zwei bis drei Wochen dedizierte Refactoring-Zeit. Nodes werden als veraltet markiert (deprecated), Physik-Ebenen werden neu definiert und Workflows für die Shader-Kompilierung ändern sich.

Bei der Bewertung der neuen Godot 4.7 Features muss man die versprochenen Leistungssteigerungen gegen diese Refactoring-Schulden abwägen. Wenn Ihr aktuelles Projekt stark auf benutzerdefinierte C++-Module (GDExtension) angewiesen ist, müssen Sie sicherstellen, dass Ihre Build-Ketten auf die aktualisierten Header vorbereitet sind. Wenn Sie ausschließlich GDScript verwenden, sind die Risiken geringer, aber Sie müssen Ihre RPC-Bindungen (Remote Procedure Call) dennoch rigoros testen.

Den Albtraum der Multiplayer-Desynchronisation bewältigen

Die Entwicklung von Multiplayer-Spielen ist im Grunde eine Übung im Verbergen von Latenz. Wenn ein Spieler eine Taste drückt, um zu springen, muss der lokale Client diesen Sprung sofort vorhersagen und gleichzeitig den Server um Erlaubnis bitten. Wenn der Server nicht zustimmt – vielleicht, weil der Spieler eigentlich den Bruchteil einer Sekunde zuvor von einem Gegner betäubt wurde – muss der Client die Position des Spielers zwangsweise korrigieren (Reconciliation), was zu einem störenden visuellen "Gummiband"-Effekt (Rubber-Banding) führt.

Godot 4 führte die Nodes MultiplayerSynchronizer und MultiplayerSpawner ein, die einen Großteil des für die Zustandsreplikation erforderlichen Boilerplate-Codes abstrahierten. Eine Out-of-the-box-Synchronisation reicht jedoch für rasante, kompetitive Spiele selten aus. Man benötigt eine granulare Kontrolle darüber, welche Daten gesendet werden, wie oft sie gesendet werden und ob sie zuverlässige (reliable) oder unzuverlässige (unreliable) Transportkanäle benötigen.

Implementierung von Server-autoritärer Bewegung

Ein klassischer Fehler, den Indie-Entwickler machen, ist es, dem Client zu vertrauen. Wenn Ihr Client dem Server seine eigene Position diktiert, werden böswillige Spieler einfach ihren Client modifizieren, um sich über die Karte zu teleportieren. Der Server muss die ultimative Autorität sein.

Hier ist ein praktischer, produktionsreifer Ansatz zur Implementierung von Server-autoritärer Bewegung mit clientseitiger Vorhersage (Client-Side Prediction) in GDScript. Dieses Muster stellt sicher, dass sich die Bewegung reaktionsschnell anfühlt, während grundlegende Speed-Hacks verhindert werden.

extends CharacterBody3D

# Multiplayer-Setup
@export var player_id := 1

# Bewegungskonstanten
const SPEED := 5.0
const JUMP_VELOCITY := 4.5

# Zustandsverfolgung für Reconciliation (Abgleich)
var unacknowledged_inputs := []
var latest_server_state := {}

func _ready() -> void:
    # Setzt die Multiplayer-Autorität auf die ID des Spielers
    set_multiplayer_authority(player_id)
    
    # Wenn wir der Server sind, verarbeiten wir die Physik normal
    # Wenn wir der Client sind, sagen wir nur voraus und warten auf Server-Korrekturen
    if not is_multiplayer_authority() and not multiplayer.is_server():
        set_physics_process(false)

func _physics_process(delta: float) -> void:
    if is_multiplayer_authority():
        # Eingabe erfassen
        var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
        var input_state := {
            "tick": Engine.get_physics_frames(),
            "dir": input_dir,
            "jump": Input.is_action_just_pressed("ui_accept")
        }
        
        # Lokal anwenden für sofortiges Feedback (Prediction)
        _apply_movement(input_state, delta)
        
        # Eingabe für möglichen späteren Abgleich speichern
        unacknowledged_inputs.append(input_state)
        
        # Zur Validierung an den Server senden
        rpc_id(1, "_receive_client_input", input_state)

# Unreliable RPC ist hier entscheidend, um Netzwerküberlastung zu vermeiden.
# Verworfene Eingaben werden durch die autoritären Zustandsaktualisierungen des Servers korrigiert.
@rpc("any_peer", "call_remote", "unreliable")
func _receive_client_input(input_state: Dictionary) -> void:
    # NUR SERVER-SEITIG
    if not multiplayer.is_server():
        return
        
    var sender_id = multiplayer.get_remote_sender_id()
    if sender_id != player_id:
        # Unautorisiertes Eingabe-Spoofing ablehnen
        push_warning("Player %s attempted to spoof input for player %s" % [sender_id, player_id])
        return
        
    # Eingabe auf dem Server anwenden
    _apply_movement(input_state, get_physics_process_delta_time())
    
    # Den validierten Zustand an alle Clients senden
    var new_state = {
        "tick": input_state.tick,
        "pos": global_position,
        "vel": velocity
    }
    rpc("_receive_server_state", new_state)

@rpc("authority", "call_remote", "unreliable")
func _receive_server_state(server_state: Dictionary) -> void:
    # NUR CLIENT-SEITIG
    if is_multiplayer_authority() or multiplayer.is_server():
        return
        
    # Auf Serverposition springen (Reconciliation)
    # In einem echten Spiel würde man dies interpolieren, um den Sprung zu verbergen
    global_position = server_state.pos
    velocity = server_state.vel
    
    # Bestätigte Eingaben entfernen
    unacknowledged_inputs = unacknowledged_inputs.filter(func(input): return input.tick > server_state.tick)

func _apply_movement(state: Dictionary, delta: float) -> void:
    # Standard-Godot-Character-Controller-Logik, angewendet auf einen spezifischen Zustands-Payload
    if not is_on_floor():
        velocity.y -= 9.8 * delta

    if state.jump and is_on_floor():
        velocity.y = JUMP_VELOCITY

    var direction := (transform.basis * Vector3(state.dir.x, 0, state.dir.y)).normalized()
    if direction:
        velocity.x = direction.x * SPEED
        velocity.z = direction.z * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        velocity.z = move_toward(velocity.z, 0, SPEED)

    move_and_slide()

Dieses Skript adressiert den grundlegenden Schmerzpunkt der autoritären Bewegung. Durch die Verwendung von unreliable RPCs für konstante Datenströme wie Position und Eingabe verhindern wir, dass sich die zugrunde liegende Netzwerk-Warteschlange staut und katastrophale Verzögerungen verursacht. Die neuen Engine-Updates verfeinern weiterhin, wie diese internen RPC-Warteschlangen verwaltet werden, was Server mit hoher Tickrate deutlich praktikabler macht.

Performance-Profiling: Dem GDScript-Flaschenhals entkommen

GDScript ist eine unglaublich produktive Sprache, aber ihre dynamische Natur bringt eine Leistungsobergrenze mit sich. Wenn man Hunderte von Entitäten in einer _physics_process-Schleife verarbeitet, kann der Overhead von Variant-Typen und dynamischen Methodenaufrufen die Framerate halbieren.

Einer der heimtückischsten Performance-Killer in Godot ist die Speicherzuweisung zur Laufzeit (Runtime Memory Allocation). Das Instanziieren eines neuen Nodes oder das Erstellen eines neuen komplexen Dictionarys in jedem Frame triggert den Speicherallokator der Engine. Im Laufe der Zeit führt dies zu Fragmentierung und Spitzen bei der Garbage Collection – was sich als spürbares Ruckeln während des Gameplays äußert.

Object Pooling: Eine obligatorische Architektur

Um diese Allokatoren zu umgehen, muss man Object Pooling implementieren. Anstatt während des Spiels queue_free() und instantiate() aufzurufen, weist man während der Ladebildschirme ein riesiges Array von Objekten im Voraus zu und schaltet einfach deren Sichtbarkeits- und Verarbeitungszustände um.

Stellen Sie sich einen Bullet-Hell-Shooter vor. Wenn ein Boss 500 Projektile pro Sekunde abfeuert, wird die dynamische Instanziierung von 500 Area2D-Nodes Ihre CPU in die Knie zwingen.

So baut man einen robusten Object Pool in GDScript:

extends Node
class_name BulletPool

@export var bullet_scene: PackedScene
@export var pool_size: int = 1000

var _available_bullets: Array[Node] = []
var _active_bullets: Array[Node] = []

func _ready() -> void:
    # Alle Objekte vor Spielbeginn im Voraus zuweisen
    for i in range(pool_size):
        var bullet = bullet_scene.instantiate()
        
        # Das Projektil vollständig deaktivieren
        bullet.process_mode = Node.PROCESS_MODE_DISABLED
        bullet.visible = false
        
        # Zum Scene Tree hinzufügen, aber im Ruhezustand belassen
        add_child(bullet)
        _available_bullets.append(bullet)

func spawn_bullet(spawn_position: Vector2, direction: Vector2) -> Node:
    if _available_bullets.is_empty():
        push_error("Bullet pool exhausted! Increase pool size.")
        return null
        
    var bullet = _available_bullets.pop_back()
    
    # Den Zustand des Projektils neu initialisieren
    bullet.global_position = spawn_position
    if bullet.has_method("set_direction"):
        bullet.set_direction(direction)
        
    # Das Projektil aufwecken
    bullet.visible = true
    bullet.process_mode = Node.PROCESS_MODE_INHERIT
    
    _active_bullets.append(bullet)
    return bullet

func return_bullet(bullet: Node) -> void:
    if not bullet in _active_bullets:
        return
        
    # Das Projektil wieder in den Ruhezustand versetzen
    bullet.process_mode = Node.PROCESS_MODE_DISABLED
    bullet.visible = false
    
    _active_bullets.erase(bullet)
    _available_bullets.append(bullet)

Indem man die Rechenlast vom volatilen Gameplay-Loop in die statische Ladephase verlagert, garantiert man ein flaches, vorhersehbares Speicherprofil. Wenn Sie Ihr Spiel im Godot-Editor profilen, sollten Sie sehen, dass sich Ihre Speichernutzung auf einem Plateau einpendelt, anstatt ständig zu steigen und zu fallen. Allein diese Technik kann die Varianz der Frame-Zeiten in projektillastigen Spielen von ~15ms auf grundsolide ~2ms reduzieren.

Rendering-Workflows und Szenenoptimierung

Während Backend- und Logik-Performance kritisch sind, bleibt das Rendering der visuell offensichtlichste Engpass. Der Vulkan-Renderer von Godot 4 ist leistungsstark, erfordert jedoch eine bewusste Optimierung. Ein häufiger Fehler ist es, sich darauf zu verlassen, dass die Engine unsichtbare Geometrie auf magische Weise aussortiert (Culling). Obwohl Godot über ein hervorragendes Frustum Culling verfügt, erfordert das Senden von rohen Vertex-Daten an die GPU immer noch eine CPU-seitige Vorbereitung (Draw Calls).

Um dies abzumildern, müssen Entwickler MultiMeshInstance3D aggressiv für wiederholte Geometrie wie Gras, Bäume oder Menschenmengen-Systeme nutzen. Ein standardmäßiges MeshInstance3D erfordert einen eigenen Draw Call für jedes Objekt. Wenn Sie einen Wald mit 5.000 Bäumen haben, sind das 5.000 Draw Calls – genug, um eine Mittelklasse-GPU lahmzulegen.

Die Umwandlung dieser 5.000 separaten Nodes in eine einzige MultiMeshInstance3D reduziert die Draw Calls von 5.000 auf genau 1. Die GPU ist unglaublich effizient darin, dasselbe Mesh tausende Male zu zeichnen; es ist die Anweisung der CPU, dies zu tun, die den Engpass verursacht. Während sich Godot durch seinen 4.x-Lebenszyklus weiterentwickelt, wird die Pipeline zur Verwaltung dieser Batches zunehmend optimiert, aber die architektonische Verantwortung bleibt beim Entwickler.

Das Dilemma der Backend-Infrastruktur

Lassen Sie uns den Elefanten im Raum ansprechen. Sie haben Ihre Object Pools optimiert, Sie haben sauberes, Server-autoritäres GDScript geschrieben, und Ihr Multiplayer-Spiel läuft beim Testen auf localhost einwandfrei.

Jetzt wollen Sie launchen.

Plötzlich sind Sie kein Spieleentwickler mehr; Sie sind ein DevOps-Ingenieur. Sie müssen Linux-Server bereitstellen. Sie müssen einen Matchmaker schreiben, der Spieler nach Ping und Fähigkeiten gruppiert. Sie benötigen ein automatisiertes System, das dedizierte Serverinstanzen dynamisch basierend auf der Spielernachfrage hochfährt und sie wieder herunterfährt, um Geld zu sparen, wenn die Spielerzahl sinkt. Sie brauchen sichere Datenbanken für Spieler-Inventare und Bestenlisten, die alle hinter SSL-Zertifikaten und DDOS-Schutzschichten gesichert sind.

Dies selbst aufzubauen erfordert die Einrichtung von Kubernetes-Clustern, Load Balancern, Database Sharding und Echtzeit-Socket-Managern – leicht 4 bis 6 Monate zermürbende Infrastrukturarbeit, die absolut nichts damit zu tun hat, Ihr Spiel unterhaltsam zu machen.

Genau aus diesem Grund existiert Backend-as-a-Service (BaaS). Mit horizOn kommen diese komplexen Backend-Dienste speziell für Spieleentwickler vorkonfiguriert. Anstatt benutzerdefinierte Matchmaking-Logik zu schreiben und AWS EC2-Instanzen bereitzustellen, integrieren Sie ein SDK und überlassen der Plattform die Server-Orchestrierung, Spielerauthentifizierung und Datenpersistenz. So können Sie Ihr eigentliches Spiel ausliefern anstatt Ihres Infrastruktur-Stacks.

Indem Sie die Serververwaltung an eine für Spiele entwickelte Plattform auslagern, gewinnen Sie die Hunderte von Stunden zurück, die Sie benötigen, um Ihren Gameplay-Loop zu verfeinern und Bugs zu beheben.

5 Best Practices für die Migration auf Godot 4.7 Dev-Builds

Ein Upgrade auf einen Entwicklungs-Snapshot ist von Natur aus gefährlich. Wenn Sie fest entschlossen sind, die neuen Godot 4.7 Features in Ihrem aktuellen Projekt zu testen, müssen Sie strenge Deployment-Hygiene einhalten, um eine Beschädigung Ihrer Projektdateien zu vermeiden.

  1. Obligatorisches Branching: Öffnen Sie niemals Ihren primären Projektordner in einem Dev-Build. Verwenden Sie Git, um einen dedizierten Branch speziell für das Testen des Upgrades zu erstellen. Wenn das Projekt kaputt geht, können Sie den Branch einfach löschen und zur Sicherheit zurückkehren.
  2. Profiling-Baselines festlegen: Bevor Sie ein Upgrade durchführen, führen Sie Ihr Spiel in Godot 4.3/4.6 aus und notieren Sie die durchschnittlichen FPS, Draw Calls und die Speichernutzung in Ihrer anspruchsvollsten Szene. Vergleichen Sie genau diese Metriken im neuen Build. Wenn die Leistung abfällt, haben Sie eine Regression gefunden, die Sie den Engine-Maintainern melden können.
  3. RPC-Konfigurationen überprüfen: Netzwerkcode ist oft das Erste, was bei Engine-Updates kaputt geht. Überprüfen Sie jede @rpc-Annotation. Stellen Sie sicher, dass sich Ihre Reliable- und Unreliable-Flags unter simulierter Netzwerklatenz weiterhin wie erwartet verhalten.
  4. Benutzerdefinierte Export-Templates kompilieren: Wenn Sie einen dedizierten Server erstellen, verlassen Sie sich nicht auf Standard-Export-Templates. Kompilieren Sie benutzerdefinierte Headless-Templates aus dem Godot-Quellcode, um Audio- und Rendering-Module zu entfernen, was den RAM-Bedarf Ihres Servers drastisch reduziert.
  5. Automatisierte Tests implementieren: Verwenden Sie ein Framework wie GUT (Godot Unit Test), um automatisierte Tests für Ihre Mathematik- und Zustandslogik zu schreiben. Wenn Sie die Engine aktualisieren, wird die Ausführung dieser Tests sofort signalisieren, ob sich eine interne Engine-Berechnung geändert hat.

Ausblick: Der Weg zur Stable-Version

Die Godot-Engine ist vollständig Community-gesteuert, was bedeutet, dass ihre Entwicklungsgeschwindigkeit direkt an die Entwickler gebunden ist, die diese frühen Snapshots testen und Probleme melden. Während Dev3 und Dev4 Sprungbretter sind, repräsentieren sie die absolute Spitze (Bleeding Edge) der Open-Source-Spieleentwicklung. Sie geben Technical Directors und Solo-Entwicklern die nötige Weitsicht, um ihre Architektur Monate vor der Veröffentlichung der Stable-Version zu planen.

Indem Sie die Server-autoritäre Architektur meistern, Ihre Objekte aggressiv poolen und die Rendering-Pipeline verstehen, garantieren Sie, dass Ihr Spiel unabhängig von der Engine-Version skaliert. Und wenn Sie bereit sind, dieses stark optimierte Multiplayer-Spiel einem globalen Publikum zu präsentieren, stellen Sie sicher, dass Ihr Backend genauso robust ist wie Ihr Client-Code.

Bereit, Ihr Multiplayer-Spiel zu skalieren, ohne im Server-Management zu ertrinken? Testen Sie horizOn kostenlos und konzentrieren Sie sich auf das, was Sie am besten können: unglaubliche Spiele zu entwickeln.


Quelle: Godot 4.7 Dev3 und Dev4 veröffentlicht