Items und Inventory

Wie bei vielem gibt es nicht DIE Art und Weise eine Item Verwaltung mit Inventar zu realisieren. In diesem Dokument versuchen wir verschiedene Implementierungsarten zu Zeigen. Bei der Implementierung sollte man immer den Verwendungszweck im Auge haben. Gerade in unseren Projekte konzentrieren wir uns auf das Notwendigste.

Eine Alternative die viele Vorteile bietet ist das SPUD-Projekt von Steve Streeting. Hier werden gleich mehrere Probleme angegangen. Auf Kosten der Komplexität und der Notwendigkeit das man schon einige Erfahrungen in UE haben muss. Ebenso gibt es im MarketStore einige Inventarsysteme die Kostenlos sind. Bei solchen System sollte man immer darauf achten wie diese Implementiert sind um eine Wiederverwendbarkeit zu gewährleisten die nicht auf Kosten der Flexibilität geht.

Sich an solche System anzuschauen und dann das beste daraus zu ziehen ist oft die beste Methode.

Interaction – Version 1

Das Beispiel ist als solches zu Verstehen, es soll die Methodik erläutern nicht zwingend vollständig funktionierend.

Erster Schritt für ein Item und Inventarsystem ist die Entscheidung ob man auf Objektorientierung setzen möchte oder auf Tabellen. Die Realisierung über eine Klasse die, Datengetrieben, über Informationen aus einer „Datenbank“ Arbeitet ist möglich, setzt aber einiges an Planung, Infrastruktur und Zeit voraus.

Wir verfolgen hier erst einmal einen eher objektorientierten Ansatz.

Gut geplant würden sich die Interfaces, zur Kommunikation von Character und Item, nicht erst im lauf der Erstellung ergeben aus Notwendigkeiten. Anhand eines Beispiels das durchgespielt wird, auf dem Papier, ist hier ein sehr guter Ansatz.

Interfaces sind essentiell will man nicht viel verlieren: Performance, Zeit, Wiederverwendbarkeit und Übersicht.

Unreal Engine – SPE – SHK Tutorials | Tutorials – MedienInformatik (w-hs.de) hier finden Sie ein Tutorial zu Interfaces.

Damit ein Character ein Inventar haben kann muss man als erstes ein System schaffen das einem den Umgang mit Items gestattet. Diese Items können dann im Inventar gehalten und verwaltet werden. Ein Inventar kann als Kiste, Sack oder am Körper befestigte Gegenstände betrachtet werden. Zumindest in der klassischen Sichtweise, die aus Adventure-Spielen oder Rollenspielen her geläufig ist.

Mit Items aka Gegenstände muss man also Umgehen können. Was dieserlei Dinge sind oder sein können muss man durchspielen. Z.B. Wir wollen Becher, Rüstung, Waffen und Heiltränke tragen oder in einem Sack haben bzw. nutzen. Items müssen also Aufgehoben werden können, Weggeworfen werden können und Genutzt werden können. Man muss sie trage können.

Einige dieser Fähigkeiten definiert man am besten über Interfaces.

Mit einer guten Kategorisierung kann man viel redundante Arbeit vermeiden.

BPI_ (Blueprint Interface) Interaction

In diesem Fall ist im Interface noch Register und Unregister als Funktionalität vorhanden. Diese werden benötigt für das Erkennen von Items vom Character. Siehe weiter unten.

Eine gute Datei/Verzeichnis-Struktur hilft Ordnung und Übersicht zu halten. Viele Lösungen nutzen ein Basis Blueprint welches Dupliziert wird und dann einsortiert um neue Objekte zu erstellen.

Alle Grundlegenden Daten die jedes Item besitzt sollten in der _Base definiert werden.

Vorausschauend ist es die Dinge die ein Objekt definieren, gemeinhin als Properties bezeichnet, in einer Datenstruktur zu halten. Das Sichern und Übermitteln kann damit Optimiert werden.

Hier sind 3 Structs definiert. Jedes für eine Spezielle Oberkategorie von Objekten. Oft werden Allgemeine Items und Waffen stark getrennt realisiert und ggf. redundant.

Im Struct S_ItemData sind die Properties definiert die ein Objekt ausmachen. Idealerweise ist hier auch hinterlegt wie mit dem Objekt umgegangen wird. Diese Structs werden in einer Datatable gespeichert. Bei der Initialisierung eines Items wird aus dieser der Entsprechende Eintragen gelesen und gespeichert. Die Verwendung von Datatables ermöglicht eine einfache Verarbeitung und Erstellung der Inhalte. Ein Im-/Export ist möglich um die Daten ggf. extern zu Bearbeiten.

Am Beispiel S_WeaponData kann man siehen das hier auch die Animationen hinterlegt sind die bei bestimmten Aktionen abgespielt werden sollen. Ebenso wo das Objekt Verankert werden kann und welche AnimationBlueprint bzw. Mode verwendet werden soll. Soll die Bewegungsart abweichen vom normalen Modus kann dies auch hier hinterlegt werden.

Kontext bezogen werden also Information zum Gegenstand gespeichert. Das Item weiß alles über sich, der Character weiß über das Item nichts. Der Character bekommt immer nur einer Referenz auf das gerade Aktive Item.

Ein Item ist im einfachen Fall ein Blueprint vom Typ Actor.

Bei der Konzipierung sollte man einen Plan haben wo und wofür man diese Items verwenden will und was man mit ihnen anstellen möchte. Entsprechend sind bestimmte Componenten von Nöten. Im Allgemeinen ist es so das Waffen oft Skeletal Meshes sind. Ein einfacher Becher oder ein Heiltrank aber meist nur Static Mesh. Das ist oft ein Grund für Redundante Implementierungen. Im Beispiel sind beide Typen verwendet und es wird zu Initialisierung oder zu Nutzzeitpunkt entschieden welches Gültig ist. Wichtig ist die Definition eines Collider-Objektes. Dieses ist für die eigentliche Interaktion zuständig. Die Visuellen Repräsentationen haben damit nicht zu tun.

Das Collider Objekt sollte möglichst einfach sein. Am wenigsten Aufwand erzeugt die Kugel, daher wird diese oft verwendet. Die Größe sollte/muss Empirisch ermittelt werden.

Im Eventgraph kann man sehen das hier die Register und Unregister Events fehlen. Diese bilden die Verbindung zum Character. Die Entscheidung ob und wie ein Item verarbeitet wird liegt im Character.

Im unten stehenden Beispiel wird für die Items das S_ItemData nicht verwendet. Das zeigt wie unübersichtlich das ganze ggf. werden kann.

Item Overlap mit Character

Im Bild oben kann man sehen ein Widget erscheint mit der Meldung das man mit diesem Item Interagieren kann. Dieses Widget erscheint wenn folgendes Event Sphere::BeginOverlap durchlaufen wurde.

Als erstes der Test ob das Widget schon existiert. Wird löschen Widgets im allgemeinen um Ressourcen zu sparen und weil wird schnell genug sind um neue zur Laufzeit zu erzeugen. Dann wird geprüft ob das Objekt (Actor) mit dem wir Kollidiert sind das Interface BPI_Player implementiert. Dies ist eine sichere und sehr schnelle Methode nur auf Kollisionen mit dem Spieler zu reagieren.

Es wird dann der Actor zu einem Spieler gecastet und geospeichert. Für das folgende absetzen einer Message (Aufruf eines Events) an einen Actor wäre das nicht notwendig, aber da wir gerade den Spieler an der Hand haben und wir später eine direkte Referenz benötigen merken wir uns an dieser Stelle eine Referenz zum Spieler. (Optimierungen sind hier möglich, aber erst mal soll es funktionieren).

Im folgenden wird alle getan damit das Widget mit der Nachricht zu Verwendung erscheint. Wichtiger für die Funktion ist die Nachricht an den Player: „Register Interactable“

Diese wird jetzt im Character aka Spieler verarbeitet.

Character/Spieler

Das Spieler Blueprint kann am Ende recht vielteilig aussehen. Man könnte auch Ordnung mit Sub-Eventgraphs erstellen, das führt aber nicht unbedingt zu mehr Durchblick.

Der Spieler implementiert das Interface BPI_Interactables. Dadurch kann er Nachrichten (Messages) für dieses Interface Empfangen.

Die Funktionen, besser Events, Register und Unregister, werden jetzt genutzt um eine Referenz auf das Item zu bekommen.

Im Prinzip wird die Variable „Interactable Item“ auf das Item gesetzt mit dem der Spieler Kollidiert ist. Die Technik mit der die Kollision ausgelöst wird kann von Dumm bis sehr Intelligent umgesetzt werden. In unserem Fall sehr Dumm. Man kann auch einen Extra Collider im Character definieren der für das Auslösen zuständig ist, oder man realisiert es abhängig davon wo die Kamera hinblickt. Die Abfrage im Item muss dann entsprechend realisiert werden.

Das Ergebnis der Aktion ist das man ein Objekt/Item kennt mit dem man Interagieren kann. Jetzt muss der Spiele nur die Entsprechende Input Aktion auslösen und das Event Interact wird als Message an ein Item geschickt. Was das für ein Item genau ist wissen wir noch nicht. Es könnte eine Ableitung von der _Base sein oder von einem seine Kinder.

Ein Heiltrank wird jetzt vielleicht Konsumiert werden, ein Becher oder Messer ins Inventar gelegt. Das hängt jetzt davon ab wie das Event Interact (oder Use Action) handelt.

in unserem kleinem Beispiel ist Interact ein Event das auf dem Server ausgeführt wird und dann das eigentliche Event Use Action aufruft. Multiplayer erfordert solche Vorgehensweisen.

BP_Item_Base ist so Implementiert das es nur eine Nachricht ausgibt:

BP_Item_Health tut tatsächlich auch etwas:

Inventar

Sollte das Verhalten anders sein, z.B. soll das Item in ein Inventar gehen muss man sich als erstes Gedanken machen wie das Inventar aussehen soll und was es können soll.

Ganz einfacher Fall: Eine Anzahl an Objekten vom Typ BP_Item_Base aufnehmen.

in diesem Fall muss die Logik des Verhaltens im Spieler Character implementiert werden. Für das SPE Projekt würde das ausreichen. Für ein reales Spiel natürlich nicht. Hier würde man die Funktionalität in eine Actor Klasse oder eine Actor Componente auslagern. Zu beachten ist das in einer ActorComponenten Klasse nicht alles machbar ist.

Was braucht man? …

In der Datenstruktur für die Waffen kann man erkennen das es feinkörniger gehandhabt werden muss.

Wo die Objekte ihren Platz finden wird im Skelet des Characters definiert, in dem Sockets oder Virtual Bones definiert werden.