Vor ein paar Wochen haben wir uns Soft- vs Hard-References angesehen und wie eine einzige Hard-Reference still dein halbes Projekt in den Speicher ziehen kann. Casting ist der häufigste Weg, auf dem Anfänger genau solche Referenzen erzeugen, ohne es zu merken. Wirf ein Cast To BP_Door in deinen Player-Blueprint und du hast der Engine gerade gesagt: immer wenn der Player lädt, lade auch BP_Door — und alles, was BP_Door anfasst. Mach das ein Dutzend Mal und deine "kleine" Player-Klasse zieht einen Baum aus Meshes, Sounds und Partikelsystemen rein, nach denen du nie gefragt hast.

Interfaces sind die Lösung. Das hier ist die praktische Version: was Casting wirklich tut, wann es okay ist, und wie du die schlechten Fälle ersetzt.

Was ein Cast wirklich tut

Ein Cast To-Node nimmt eine generische Referenz — etwa Other Actor aus einem Overlap — und fragt: "Ist das konkret ein BP_Door?" Wenn ja, bekommst du eine typisierte Referenz und kannst BP_Doors Funktionen aufrufen und seine Variablen lesen. Wenn nein, schlägt der Cast fehl und der Failed-Exec-Pin feuert.

Das ist wirklich nützlich. Der versteckte Preis ist die Abhängigkeit, die dabei entsteht.

Warum Casting auf Blueprints Speicher kostet

Wenn dein Blueprint ein Cast To BP_Door enthält, vermerkt der Editor eine Hard-Reference von deinem Blueprint auf BP_Door. Unreal lädt Abhängigkeiten vor dem Asset, das sie braucht — in dem Moment, in dem dein Blueprint lädt, lädt also auch BP_Door, plus BP_Doors Referenzen, und deren Referenzen, bis ganz nach unten.

Auf einen Blueprint zu casten ist keine Katastrophe. Das Problem ist die Kette: Player castet auf BP_Door, BP_Door castet auf BP_KeyItem, BP_KeyItem referenziert drei Meshes und einen Sound. Jetzt zieht das Laden des Players alles davon rein. Rechtsklick auf ein Asset und Reference Viewer öffnen, oder Size Map nutzen, um zu sehen, was deine Klasse wirklich mitschleppt.

Wann Casting völlig in Ordnung ist

Casting ist nicht böse. Auf native (C++) Klassen zu casten — APawn, ACharacter, APlayerController oder deine eigenen C++-Klassen — erzeugt keine Asset-Hard-Reference, weil das keine uassets sind. Casting ist auch okay, wenn das Ziel ohnehin schon geladen ist: den überlappenden Actor zu casten, der bereits in deinem Level steht, kostet keinen zusätzlichen Speicher; an der Stelle ist es nur ein Typ-Check.

Faustregel: auf native Klassen frei casten. Vorsicht beim Casten auf Blueprint-Assets, besonders aus Klassen, die früh laden — Player, GameMode, GameInstance, HUD.

Was ein Interface ist

Ein Blueprint Interface ist ein Vertrag: eine Liste von Funktionsnamen ohne Implementierung. Du erstellst es (Rechtsklick → Blueprint → Blueprint Interface), legst Funktionen wie Interact oder Take Damage an, und dann kann jeder Blueprint es über Class Settings → Interfaces implementieren.

Die entscheidende Eigenschaft: eine Interface-Funktion aufzurufen erfordert nicht, die konkrete Klasse zu kennen. Dein Player kann Interact auf allem aufrufen, was er gerade ansieht, ohne jemals BP_Door zu referenzieren. Kein Cast, keine Hard-Reference.

Wie du ein Interface aufrufst

Du rufst über den Message-Node auf — z.B. Interact (Message). Zieh von einer beliebigen Actor-Referenz weg, such den Funktionsnamen, nimm den Eintrag mit (Message). Implementiert das Ziel das Interface, läuft es. Wenn nicht, passiert nichts — kein Crash, kein Failed-Pin, den du behandeln musst. Diese Stille ist das Feature: ein Button, eine Truhe und ein NPC können alle auf denselben Interact-Aufruf antworten, und eine Wand ignoriert ihn einfach.

Wenn du tatsächlich auf "unterstützt das Ding das überhaupt" verzweigen musst, nimm Does Implement Interface vor dem Aufruf.

Ein schnelles Refactor

Die Cast-Version von "E drücken zum Interagieren":

CB0

Drei Casts, drei Hard-References, und es bricht in dem Moment, in dem du ein viertes Interactable hinzufügst. Die Interface-Version:

CB1

Ein Node. Jedes Interactable implementiert Interact auf seine eigene Art. Einen fünften Typ hinzuzufügen braucht null Änderungen am Player.

Häufige Fallstricke

  • Interface-Funktionen mit Rückgabewerten können keine Events sein. Hat deine Interface-Funktion einen Output, muss der implementierende Blueprint sie als Function anlegen (sie taucht in My Blueprint zum Überschreiben auf). Nur Funktionen ohne Output lassen sich als Events implementieren. Darüber stolpert jeder einmal.
  • Die Nicht-Message-Variante aufrufen. Der einfache Aufruf nimmt an, dass das Objekt das Interface implementiert, und kann fehlschlagen, wenn nicht. Nimm die (Message)-Variante, außer du hast es schon geprüft.
  • Alles per Interface machen. Interfaces fügen Indirektion hinzu. Für einen einmaligen Aufruf auf eine Klasse, die du aus anderen Gründen sowieso hart referenzierst, ist ein einfacher Cast simpler. Reiß nicht dogmatisch jeden Cast raus.
  • Vergessen das Interface wirklich hinzuzufügen. Implementieren heißt Class Settings → Interfaces → Add. Nur den Funktionsnamen zu treffen reicht nicht — der Message-Aufruf tut dann still gar nichts.

30-Sekunden-Recap

  • Auf ein Blueprint-Asset zu casten erzeugt eine Hard-Reference — dieses Asset und sein Baum laden, sobald deine Klasse lädt.
  • Auf native C++-Klassen zu casten ist kostenlos; einen schon geladenen Actor zu casten ist nur ein Typ-Check.
  • Interfaces lassen dich Funktionen aufrufen, ohne die konkrete Klasse zu referenzieren — keine Hard-Reference.
  • Ruf über den (Message)-Node auf; er tut nichts, wenn das Ziel das Interface nicht implementiert.
  • Funktionen mit Rückgabewerten müssen als Functions implementiert werden, nicht als Events.
  • Nutz Reference Viewer und Size Map, um zu finden, was deine Casts mitschleppen.

Als Nächstes: Event Dispatchers — die andere Hälfte sauberer Blueprint-Kommunikation, für den Fall, dass der Aufrufer gar nicht wissen soll, wer zuhört.

— Marco