Ich habe mich immer gefragt, inwieweit die Objektorientierung von Videospielen verwendet wird, insbesondere bei großen 3D-Projekten. Ich halte es für cool, dass beide troll
und werewolf
ihre Attribute von enemy
ihnen geerbt und einige von ihnen überschrieben haben. Aber vielleicht ist der Unterricht zu schwer, um praktisch zu sein, ich weiß nicht ...
18
Antworten:
Um zu Ihrem Beispiel zu kommen, werde ich meine Gedanken über Entites in Videospielen erläutern. Ich werde nicht auf die allgemeinen Vor- und Nachteile von Objekten und Klassen eingehen, auch nicht auf ihre Rolle in Videospielen.
Entitäten in kleinen Videospielen werden meist durch separate Klassen definiert, in großen Spielen werden sie häufig in Dateien definiert. Es gibt zwei allgemeine Ansätze:
Erweiterung : In Ihrem Beispiel
Troll
undWerewolf
wird erweiternEnemy
, was erweitertEntity
.Entity
kümmert sich um die grundlegenden Dinge wie Bewegung, Gesundheit usw., währendEnemy
die Unterklassen als Feinde bezeichnet werden und andere Dinge tun (Minecraft folgt diesem Ansatz).Komponentenbasiert : Der zweite Ansatz (und mein Favorit) ist eine
Entity
Klasse mit Komponenten. Eine Komponente könnteMoveable
oder seinEnemy
. Diese Komponenten kümmern sich um eine Sache wie Bewegung oder Physik. Diese Methode unterbricht lange Erweiterungsketten und minimiert das Risiko, in einen Konflikt zu geraten. Zum Beispiel: Wie kann man unbewegliche Feinde machen, wenn diese von einem beweglichen Charakter erben? .In großen Videospielen wie World of Warcraft oder Starcraft 2 sind Entities keine Klassen, sondern Datensätze, die das gesamte Entity spezifizieren. Skripting ist auch wichtig, da Sie definieren, was die Entität nicht in einer Klasse, sondern in einem Skript tut (wenn es eindeutig ist, keine Dinge wie Verschieben).
Ich programmiere gerade ein RTS und benutze den komponentenbasierten Ansatz mit Deskriptor- und Skriptdateien für Entities, Skills, Items und alles andere, was im Spiel dynamisch ist.
quelle
component
in diesem Zusammenhang ist. Ein Link wäre sehr dankbar.Leider hat sich diese in den 90er Jahren beliebte Herangehensweise an Polymorphismus in der Praxis als schlechte Idee erwiesen. Stellen Sie sich vor, Sie fügen der Feindesliste 'Wolf' hinzu - nun, es teilt einige Attribute mit Werwolf, so dass Sie diese in einer gemeinsam genutzten Basisklasse zusammenfassen möchten, z. "Wolfsähnlich". Jetzt fügst du der feindlichen Liste "Mensch" hinzu, aber Werwölfe sind manchmal Menschen, sodass sie auch Attribute gemeinsam haben, wie z. B. auf zwei Beinen gehen, sprechen können usw. Schaffst du eine "humanoide" gemeinsame Basis für Menschen und Werwölfe? , und müssen Sie dann aufhören, Werwölfe von WolfLike abzuleiten? Oder erben Sie mehrfach von beiden - in welchem Fall hat welches Attribut Vorrang, wenn etwas in Humanoid mit etwas in WolfLike kollidiert?
Das Problem ist, dass das Versuchen und Modellieren der realen Welt als Klassenbaum wirkungslos ist. Nehmen Sie 3 beliebige Dinge und Sie können wahrscheinlich 4 verschiedene Möglichkeiten finden, um ihr Verhalten in gemeinsam genutzte und nicht gemeinsam genutzte zu zerlegen. Aus diesem Grund haben viele ein modulares oder komponentenbasiertes Modell gewählt, bei dem ein Objekt aus mehreren kleineren Objekten besteht, aus denen das Ganze besteht. Die kleineren Objekte können ausgetauscht oder geändert werden, um das gewünschte Verhalten zu erzielen. Hier haben Sie vielleicht nur 1 Feindklasse, und sie enthält Unterobjekte oder Komponenten für das Gehen (z. B. Bipedal vs. Quadrupedal), seine Angriffsmethoden (z. B. Biss, Klaue, Waffe, Faust), seine Haut (grün) für Trolle, pelzig für Werwölfe und Wölfe usw.
Dies ist immer noch vollständig objektorientiert, aber die Vorstellung, was ein nützliches Objekt ist, unterscheidet sich von dem, was früher in den Lehrbüchern gelehrt wurde. Anstatt einen großen Vererbungsbaum mit verschiedenen abstrakten Klassen und mehreren konkreten Klassen an den Baumspitzen zu haben, haben Sie normalerweise nur eine konkrete Klasse, die ein abstraktes Konzept darstellt (z. B. "Feind"), die jedoch konkretere Klassen enthält, die abstraktere Konzepte darstellen (zB Anfälle, Hauttyp). Manchmal können diese zweiten Klassen am besten über eine Vererbungsebene implementiert werden (z. B. eine Basis-Angriffsklasse und mehrere abgeleitete Klassen für bestimmte Angriffe), aber der Deep-Inheritance-Baum ist weg.
In den meisten modernen Sprachen sind Klassen nicht "schwer". Sie sind nur eine andere Art, Code zu schreiben, und sie verpacken normalerweise nur den prozeduralen Ansatz, den Sie ohnehin geschrieben hätten, außer mit einer einfacheren Syntax. Schreiben Sie aber auf keinen Fall eine Klasse, in der eine Funktion funktioniert. Klassen sind an sich nicht besser, nur dort, wo sie die Verwaltung oder das Verständnis des Codes erleichtern.
quelle
Ich bin mir nicht sicher, was Sie mit "zu schwer" meinen, aber C ++ / OOP ist die Verkehrssprache der Spieleentwicklung aus den gleichen guten Gründen, die es an anderer Stelle verwendet.
Die Rede davon, Caches zu sprengen, ist ein Designproblem, kein Sprachfeature. C ++ kann nicht schlecht sein, da es in den letzten 15 bis 20 Jahren in Spielen verwendet wurde. Java kann nicht schlecht sein, da es in sehr engen Laufzeitumgebungen von Smartphones verwendet wird.
Nun zur eigentlichen Frage: Viele Jahre lang wurden die Klassenhierarchien tief und litten unter den damit verbundenen Problemen. In jüngerer Zeit haben sich Klassenhierarchien abgeflacht, und Komposition und andere datengesteuerte Entwürfe sind eher als logisch gesteuerte Vererbung.
Es ist alles OOP und wird immer noch als Grundlage beim Entwerfen neuer Software verwendet, nur mit einem anderen Fokus auf das, was die Klassen verkörpern.
quelle
Klassen beinhalten keinen Laufzeit-Overhead. Der Laufzeit-Polymorphismus ruft nur einen Funktionszeiger auf. Dieser Overhead ist im Vergleich zu anderen Kosten wie Draw-Aufrufen, Synchronisation der Parallelität, Festplatten-E / A, Kernel-Modus-Switches, unzureichender Speicherlokalität, übermäßiger Nutzung der dynamischen Speicherzuweisung, unzureichender Auswahl von Algorithmen, Verwendung von Skriptsprachen und der Liste äußerst gering geht weiter. Es gibt einen Grund, warum die Objektorientierung im Wesentlichen das ersetzt hat, was zuvor vorlag, und es ist, weil es viel besser ist.
quelle
Zu beachten ist, dass die Konzepte des objektorientierten Codes oft wertvoller sind als ihre Implementierung in Sprachen. Insbesondere wenn Sie Tausende von virtuellen Aktualisierungsaufrufen für Entitäten in einer Hauptschleife ausführen müssen, kann dies auf Plattformen wie 360 oder PS3 zu einem Durcheinander im Cache von C ++ führen, sodass bei der Implementierung möglicherweise "virtuelle" oder sogar "Klassenschlüsselwörter" vermieden werden der Geschwindigkeit.
Oft folgt es jedoch immer noch den OO-Konzepten von Vererbung und Kapselung - implementiert sie nur anders als die Sprache selbst (z. B. kurioserweise rekursive Vorlagen, Komposition statt Vererbung (die von Marco beschriebene komponentenbasierte Methode)). Wenn die Vererbung immer zur Kompilierungszeit behoben werden kann, verschwinden die Leistungsprobleme, aber der Code kann bei Vorlagen, benutzerdefinierten RTTI-Implementierungen (die integrierten sind normalerweise unpraktisch langsam) oder der Logik zur Kompilierungszeit ein wenig unübersichtlich werden Makros usw.
In Objective-C für iOS / Mac gibt es ähnliche, aber schlimmere Probleme mit dem Messaging-Schema (sogar mit dem ausgefallenen Caching-Zeug) ...
quelle
Die Methode, die ich verwenden werde, mischt Klassenvererbung und Komponenten. (Erstens mache ich Enemy nicht zu einer Klasse - ich mache das zu einer Komponente.) Mir gefällt die Idee nicht, eine allgemeine Entitätsklasse für alles zu verwenden und dann die erforderlichen Komponenten hinzuzufügen, um die Entität zu vervollständigen. Stattdessen möchte ich, dass eine Klasse dem Konstruktor automatisch Komponenten (und Standardwerte) hinzufügt. Dann fügen Unterklassen ihre zusätzlichen Komponenten ihrem Konstruktor hinzu, sodass die Unterklasse ihre Komponenten und ihre übergeordneten Klassenkomponenten haben würde. Zum Beispiel würde eine Waffenklasse grundlegende Komponenten hinzufügen, die allen Waffen gemeinsam sind, und dann würde die Schwert-Unterklasse zusätzliche Komponenten hinzufügen, die für Schwerter spezifisch sind. Ich überlege immer noch, ob ich Text- / XML-Assets verwenden soll, um Werte für Entitäten (oder bestimmte Schwerter zum Beispiel) zu definieren. oder das alles im Code zu tun, oder was die richtige Mischung ist. Dies ermöglicht es dem Spiel weiterhin, die Typinformationen und den Polymorphismus der Codesprache zu verwenden, und vereinfacht ObjectFactories erheblich.
quelle