Während Unity eine erstaunliche gamedev-Plattform ist, muss man sich erst daran gewöhnen, wenn man sich erst einmal daran gewöhnen muss komponentenbasiert die Architektur.
Während die klassische objektorientierte Programmierung (OOP) verwendet werden kann und verwendet wird, baut der Unity-Workflow stark auf der Struktur der Komponenten auf, was ein komponentenbasiertes Denken erfordert. Wenn Sie mit Komponenten vertraut sind, ist das großartig. Wenn nicht, ist das kein Problem. Hier gebe ich Ihnen einen Absturzkurs zu Komponenten in Unity.
Vorschaubild: Old Cogs von Emmanuel Huybrech.
Bevor wir mit dem Arbeiten und Nachdenken über Komponenten fortfahren, sollten wir sicherstellen, dass wir genau verstehen, was genau sie sind.
In der Welt des Programmierens gehen die Konzepte der Komponenten und der Entkopplung Hand in Hand. Eine Komponente kann als kleineres Stück einer größeren Maschine betrachtet werden. Jede Komponente hat ihre eigene spezifische Aufgabe und kann ihre Aufgabe oder ihren Zweck im Allgemeinen (und optimal) ohne die Hilfe externer Quellen erfüllen. Darüber hinaus gehören Komponenten selten zu einer einzelnen Maschine und können mit verschiedenen Systemen zur Erfüllung ihrer spezifischen Aufgabe verbunden werden. Sie erzielen jedoch unterschiedliche Ergebnisse, wenn es um das Gesamtbild geht. Dies liegt daran, dass Komponenten sich nicht nur um das größere Bild kümmern, sondern auch Ich weiß gar nicht, dass es existiert.
Ein klassisches Beispiel für Komponenten sind die Teile eines Autos - aber das ist langweilig, da ich mich nicht für Autos interessiere. Betrachten Sie stattdessen einen Xbox 360-Controller. Es verfügt über zwei analoge Sticks, verschiedene Tasten, Trigger und so weiter. Die gesamte Steuerung selbst ist nicht nur eine Komponente, sondern jeder einzelne Aspekt der Steuerung ist eine Komponente.
Foto von Futurilla.Die X-Taste kann: gedrückt werden; senden Sie die Information ab, dass sie gedrückt wurde; veröffentlicht werden; und senden Sie Informationen, dass es veröffentlicht wurde. Es hat keine Ahnung, dass sich daneben verschiedene andere Knöpfe befinden, und es interessiert es auch nicht.
Der Controller selbst ist eine Komponente, die aus anderen Komponenten (allen Knöpfen, Joysticks und Triggern) besteht, da er Daten an alles senden kann, woran er angeschlossen ist, aber es ist ihm egal, was das Objekt ist (Xbox, PC, einige) Arduino-Kreation oder was auch immer). Weder die X-Taste noch der Controller selbst müssen wissen, welches Spiel Sie spielen, da es seine Arbeit unabhängig von dem Empfänger seiner Informationen leistet.
Die Funktion des Controllers ist eine Einbahnstraße, und seine Aufgabe ändert sich nie, je nachdem, woran er angeschlossen ist. Dies macht es zu einer erfolgreichen Komponente, sowohl weil es seine Aufgabe als eigenständiges Gerät erfüllt, als auch, weil es seine Aufgabe erfüllen kann mehrere Geräte.
Unity wurde unter Berücksichtigung der Komponenten entwickelt und zeigt, dass dies der Fall ist. Einer der wertvollsten und charakteristischsten Aspekte von Unity ist, dass es sich um ein sehr visuelles Programm handelt. Ich arbeite seit Jahren in der Spieleentwicklung und abgesehen von meinen frühen Flash IDE-Tagen habe ich hauptsächlich mit PNG-Sprite-Sheets und einem Code-Editor wie FlashDevelop gearbeitet, der überhaupt nicht visuell ist.
Einheit ist das genaue Gegenteil. Mit Unity sehen Sie in Echtzeit alles, an dem Sie gerade arbeiten. Das bedeutet, Sie können Ihr Projekt testen, sehen, wie Ihr Projekt in einem separaten Fenster ausgeführt wird, Änderungen an Ihrem Code oder an Spielobjekten vornehmen und diese Bearbeitungen live spiegeln. Die Leistung, die dieses System einem Entwickler bietet, ist enorm und ist meiner Meinung nach ein wesentlicher Aspekt der modernen Spieleentwicklung. All dies wird durch die komponentenbasierte Architektur von Unity ermöglicht.
Falls Sie Unity nicht kennen, erkläre ich den Inspektor. Dies ist ein Bereich in Unity, in dem alle Eigenschaften eines Spielobjekts angezeigt werden. Wenn Sie auf Ihren Player in der Welt klicken (entweder zur Laufzeit oder zuvor), können Sie alles über dieses Objekt sehen.
Wenn sich in Ihrem Player-Avatar sechs Komponenten befinden, wird jede auf einer separaten Registerkarte aufgeführt. Jede öffentliche Variable steht Ihnen zur Verfügung und kann angepasst werden. Wenn Ihr Spieler-Avatar über ein Inventar verfügt, sehen Sie nicht nur, dass er über ein Inventar verfügt, sondern Sie können auch die Elemente in diesem Inventar sehen und welchen Index sich in der Liste oder im Array jedes einzelnen Elements befindet. Wenn Sie während des Tests ein neues Objekt im Spiel abholen, wird es zum Inventar-Live hinzugefügt. Sie können sogar Artikel zu diesem Inventar hinzufügen oder daraus entfernen, sodass Sie schnell neue Elemente testen können, die Sie möglicherweise sogar erstellen während das Spiel läuft.
Der Inspektor der Einheit.Live-Editing ist zwar wahnsinnig mächtig, aber es hängt nicht unbedingt von der Verwendung von Komponenten ab. Sie können ein Skript ändern und sehen, wie diese Änderungen live wiedergegeben werden. Dies ist jedoch einschränkend im Vergleich zu den Komponenten, die Sie ausführen dürfen.
Betrachten Sie einen vertikalen Weltraum-Shooter. Wenn Sie Ihr Projekt in den meisten anderen Umgebungen testen, sehen Sie, wie Ihr Spiel spielt, machen sich Notizen, gehen dann zurück zum Code und nehmen die Einstellungen vor, nur um das Projekt erneut zu kompilieren und diese Punkte zu testen. Wenn Sie ein Live-Setup haben, können Sie den Code im Handumdrehen anpassen und diese Änderungen während des Spiels sehen, was noch besser ist. Abgesehen davon, wenn Sie keine Komponenten verwenden, müssen Sie eine Menge Code ändern, um die wichtigsten Effekte zu sehen, was Zeit braucht, und der Zweck der Live-Bearbeitung wird dadurch eher zunichte gemacht.
Wenn Sie mit Komponenten arbeiten, können Sie innerhalb von zwei Sekunden neue Komponenten hinzufügen. Sie können die Waffen Ihres Schiffes gegen die Waffen austauschen, die ein Boss verwendet (vorausgesetzt, Sie haben Ihre Komponenten so programmiert, dass sie für sich selbst funktionieren, wie es bei den guten Komponenten der Fall ist). Sie können Ihr Gesundheitssystem mit fünf Treffern in einen kühlen, Halo-ähnlichen Ladeschild umwandeln für ein anderes Spiel programmiert. Sie können einem Ihrer Charaktere eine Reihe von Zaubern hinzufügen, und das alles in Sekundenschnelle.
Sie müssen keinen Code ändern, Sie müssen nicht neu kompilieren. Sie müssen einfach per Drag & Drop die gewünschte Komponente aus einer Dropdown-Liste auswählen und hinzufügen. Diese Art von Leistung ist für den Spielausgleich von unschätzbarem Wert und spart enorm viel Zeit.
Das Schwierigste an der Arbeit mit Komponenten besteht darin, zu lernen, wie Sie Ihre Projekte strukturieren, wenn Sie sie verwenden. Für die meisten Programmierer bedeutet dies wahrscheinlich, dass Sie viel mehr Skripts erstellen und jeweils kleinere, spezifischere Aufgaben ausführen.
Wie Sie zwischen Skripten kommunizieren, ist auch eine anständige Hürde, da Sie viel mehr Teile und weniger riesige Klassen haben, in denen jedes Objekt jedes andere Objekt kennt. Es gibt offensichtlich Möglichkeiten, wie statische Variablen für die Kernkomponenten Ihres Spiels (Spieler, Punktzahl usw.). Dies funktioniert jedoch selten für alles (und wird nicht empfohlen), und es gibt fortgeschrittene Methoden, um Ihre Komponenten richtig zu strukturieren und entkoppelt zu bleiben.
Da Unity unter Berücksichtigung von Komponenten entwickelt wurde, verfügt es zum Glück über eine Reihe von integrierten Funktionen, die uns dabei helfen, dies zu erreichen. Es gibt Funktionen, um Verweise auf eine bestimmte Komponente zu erhalten, um zu überprüfen, ob alle Objekte eine bestimmte Komponente enthalten usw. Mit diesen verschiedenen Funktionen können Sie leicht die Informationen abrufen, die zum Erstellen dieser magischen Einbahnstraße des Wissens in den Komponenten benötigt werden kommunizieren mit Objekten, die sie betreffen, aber die Komponente selbst hat keine Ahnung, was genau dieses Objekt ist. Kombinieren Sie dies mit der Verwendung von Schnittstellen, und Sie haben genug Programmierfähigkeit, um sich jeder einfachen oder komplexen Angelegenheit zu nähern.
In einem OOP-Vererbungssystem können Sie eine Basis haben Feind
Klasse, die alle Funktionen enthielt, die die meisten Ihrer Feinde verwenden würden, und Sie könnten diese dann erweitern, um bestimmte Funktionen hinzuzufügen.
Wenn Sie jemals ein solches System implementiert haben, wissen Sie das zwischen Ihrer Basis Feind
Klasse und vielleicht Ihre Basis GameObject
(oder eine gleichwertige) Klasse: Sie haben am Ende viele unnötige Störungen durch die Verwendung von Variablen und Funktionen, die manche Klassen benötigen, viele jedoch nicht. Schnittstellen kann helfen, aber sie sind nicht immer die Lösung.
Schauen wir uns jetzt die gleiche Konfiguration an, denken aber mit Komponenten. Ihr habt immer noch verschiedene Feinde, von denen alle eine Menge gemeinsamer Funktionalität aufweisen, von denen jeder jedoch einzigartige Eigenschaften hat.
Der erste Schritt besteht darin, alle Funktionen in Stücke zu zerlegen. Man könnte das denken Gesundheit haben und im Sterben sind Teil desselben Systems, aber selbst dieses Beispiel kann in eine Gesundheitssystemkomponente und eine Todessystemkomponente unterteilt werden. Dies liegt daran, dass Ihre Gesundheit Ihre Gesundheit ist und nicht mehr. Wenn es null erreicht, ist es nicht Sache des Gesundheitssystems, zu entscheiden, was als Nächstes passiert. Es ist nur Sache des Gesundheitssystems, zu wissen, dass es tatsächlich bei Null ist. Andere Systeme, beispielsweise ein Todesfallsystem, können diese Informationen lesen und dann entscheiden, wie sie möchten.
Vielleicht wird das Todessystem einen neuen Feind hervorbringen (denken Sie an einen großen Feind, der in Stücke zerbricht); Vielleicht fällt ein Power-Up aus. Vielleicht fügt es dem Bildschirm einen Explosionseffekt hinzu. Unabhängig davon, was passiert, ist das Gesundheitssystem kein Teil davon, und so stellen wir saubere und nützliche Komponenten her.
Wenn wir an Bewegung denken, denken wir vielleicht, dass alle Bewegungen in einem einzigen Skript sein müssen. Aber einige Gegner in manchen Spielen können nicht laufen; Sie können nur springen. Einige Feinde können laufen, können aber nicht springen. Wenn wir über diese Dinge nachdenken, erkennen wir, wo Komponenten existieren könnten und sollten. Wenn es um Mobilität geht, könnten wir den Sprung vom Gehen oder Laufen trennen, vom Fliegen trennen. Wenn Sie dies tun, erhalten Sie saubereren Code und mehr Flexibilität.
Komponenten machen unseren Code übersichtlicher (keine unnötigen Variablen und Funktionen), sie machen aber auch das Erstellen von Feinden flexibler und angenehmer. Mit jeder als Komponente eingerichteten Feindfunktion können wir Aspekte von Feinden ziehen und ablegen und sehen, wie sich diese verhalten - und sogar in Echtzeit, wenn wir Unity verwenden.
Angenommen, alle folgenden Merkmale sind bereits programmiert. Während unseres Feindentstehungsprozesses konnten wir drei leere Feinde erstellen, die alle nur leere Prefabs in Unity sind. Wir können dann ein Gesundheitssystem, ein Objektabwurfsystem und ein Todessystem ziehen, da wir wissen, dass alle unsere Feinde unabhängig von den Unterschieden Gesundheit haben, sterben und einen Gegenstand fallen lassen. Wir können tatsächlich alle drei Prefabs gleichzeitig auswählen, diese Komponenten dann per Drag & Drop in das Inspektorfenster ziehen und alle drei gleichzeitig aktualisieren.
Als Nächstes wissen wir, dass ein Feind fliegen kann, also wählen wir diesen Feind aus und ziehen einen fliegend Komponente darauf. Ein anderer kann ein Ziel im Spiel erkennen und darauf schießen, also werfen wir auf ein Ziel feuer auf ziel Komponentenskript. Der dritte kann eine Barriere aufwerfen, die alle Angriffe für eine kurze Dauer blockiert, also werfen wir auf Barriere Komponente.
Wir haben jetzt drei einzigartige Feinde, die alle bestimmte Komponenten teilen, aber auch Komponenten, die nur für sie zutreffen. Der Spaß dabei ist, dass das Erstellen dieser Feinde einfach ist und das Experimentieren mit neuen Variationen so einfach ist wie Ziehen und Ablegen. Wünschen Sie einen fliegenden Feind mit einer Barriere, der einen Feind angreifen und auf ihn schießen kann? Ziehen Sie alle oben genannten Komponenten auf einen einzelnen Gegner, und Sie haben genau das!
Mit Komponenten zu denken ist zwar nicht einfach, hat aber durchaus Vorteile. Sie werden in Ihrer zukünftigen Programmierung weiterhin sowohl Komponenten als auch Vererbung verwenden. Es ist jedoch äußerst nützlich, Ihre Fülle von Möglichkeiten zu erweitern, um das gleiche Problem anzugehen, indem Sie verschiedene Perspektiven betrachten.
Wenn es um Unity geht, können Sie die von Ihnen bevorzugte Methode verwenden, aber Komponenten werden auf jeden Fall bevorzugt, und es macht einfach keinen Sinn, gegen einen solch erstaunlichen Workflow zu kämpfen. Das Erlernen der Einheit und das Umstellen meiner Denkweise auf die Arbeit mit Komponenten war ein mäßig schwieriges Hindernis, das ich überwinden kann, aber jetzt, wo ich hier bin, sehe ich nicht, dass ich bald wiederkommen werde.