Wie objektorientiert sind Videospiele? [geschlossen]

18

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 trollund werewolfihre Attribute von enemyihnen geerbt und einige von ihnen überschrieben haben. Aber vielleicht ist der Unterricht zu schwer, um praktisch zu sein, ich weiß nicht ...

vemv
quelle
2
Wie quantifiziert (oder sogar objektiv qualifiziert) man die Objektorientierung? Möchten Sie eine Antwort in Meyers oder Dahl-Nygaards?
Offensichtlich sind sie so schwer, dass eine stark OO-basierte Sprache der Industriestandard ist.
Die kommunistische Ente
4
Wenn Sie C ++ meinen, wurde es gewählt, weil es eine gute Sprache ist, um typsichere generische Programmierung auf geschwindigkeitsbewusste Weise zu erforschen. Die wenigen OOP-Funktionen sind offensichtlich ein unglückliches Fehldesign, das nur aus Gründen der Abwärtskompatibilität aufbewahrt wird. ;)

Antworten:

20

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 Trollund Werewolfwird erweitern Enemy, was erweitert Entity. Entitykümmert sich um die grundlegenden Dinge wie Bewegung, Gesundheit usw., während Enemydie Unterklassen als Feinde bezeichnet werden und andere Dinge tun (Minecraft folgt diesem Ansatz).

Komponentenbasiert : Der zweite Ansatz (und mein Favorit) ist eine EntityKlasse mit Komponenten. Eine Komponente könnte Moveableoder sein Enemy. 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.

Marco
quelle
2
Klingt interessant. Ich werde jedoch nicht lügen, ich weiß nicht, was ein componentin diesem Zusammenhang ist. Ein Link wäre sehr dankbar.
VEMV
Eine Komponente ist eine Entität, die für eine einzelne Funktionalität verantwortlich ist. Die komponentenbasierte Entität wird eine Komponente besitzen (falls dies der Fall ist), die sie nicht von ihr erbt. Das Eigentum impliziert, dass eine Entität eine ihrer Komponenten deaktivieren kann, um ihr Verhalten zu ändern. Im klassischen Erweiterungsparadigma ist das Verhalten auf die "Zugehörigkeit" zu einer Superklasse zurückzuführen. Eine Entität kann eine Komponente sein und eine Komponente kann als Klasse oder als Struktur modelliert werden, wenn Sie dies vorziehen.
FxIII
Komponente (manchmal auch als Entity-Systeme bezeichnet) - Sie können in diesem Blog nach Ideen suchen. T-machine.org Es gibt Varianten, aber die Grundidee ist, dass eine Komponente ein spezielles Objekt ist und Ihr Akteur eine Reihe von Komponenten zusammenfügt, wie z. B. das Erstellen eines Modells von Legos.
Patrick Hughes
2
Ich habe einen Blog über Spieleentwicklung gestartet und meinen ersten Blogeintrag dazu geschrieben. Nichts Neues, aber mit ein bisschen Code: jagamedev.wordpress.com/2011/06/26/…
Marco
23

Ich halte es für cool, wenn sowohl Troll als auch Werwolf ihre Attribute vom Feind erbten und einige von ihnen überschrieben.

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.

Aber vielleicht ist der Unterricht zu schwer, um praktisch zu sein, ich weiß nicht ...

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.

Kylotan
quelle
3
Liebe diese Antwort :)
Czarek Tomczak
8

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.

Patrick Hughes
quelle
2

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.

DeadMG
quelle
Das Verteilen zwischen verschiedenen polymorphen Typen erfolgt auf eine Weise, die eine schlechte Speicherlokalität ergibt. Selbst in den leichtesten Implementierungen wie C ++ vtables oder Objective-C IMP - Caching, wenn Sie tatsächlich nutzen den Polymorphismus zu befassen sich mit Objekten unterschiedlicher Art, werden Sie Ihre Caches blasen. Wenn Sie den Polymorphismus nicht verwenden, verbleibt die vtable in Ihrem Cache, oder die zwischengespeicherte IMP-Nachricht führt zum richtigen Ort. Aber warum dann? Und in Java oder C # sind die Kosten höher, und in JavaScript oder Python sind die Kosten noch höher.
1
@ Joe Wreschnig: Wenn Sie diese langsameren Sprachen verwenden, sind die Kosten für OO im Vergleich zu den anderen Kosten wie Dolmetschen / JIT gering. Noch wichtiger ist, dass Sie theoretisch herausfinden können, was Sie von einer schlechten Speicherlokalität erwarten. Fakt ist jedoch, dass die Verzweigungsvorhersage, die Ausführung in der falschen Reihenfolge und ähnliche Funktionen auf der CPU die Kosten praktisch vollständig negieren. Ansonsten, warum wird so viel Hochleistungssoftware objektorientiert geschrieben?
DeadMG
1

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) ...

jheriko
quelle
0

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.

Chris
quelle
3
Hey Chris. Ihre Erfahrungen und Pläne zu teilen ist zwar großartig, aber die Frage wird nicht direkt beantwortet. Ich denke, die Frage dreht sich eher darum, wie diese Dinge normalerweise dort gemacht werden, wo Sie antworten, wie Sie es vorhaben . Das sind ähnliche Fragen, aber nicht wirklich die gleichen.
MichaelHouse