Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

11

12.10.2011, 22:49

Das ist auch wieder je nach Spiel. Angenommen du hast eine Art Spaceshooter. Die Gegner kommen von oben und dürfen den Unteren Bildschirmrand nicht passieren. Du selbst musst sie abschießen. Kollisionsfälle der Entitäten wäre:
Spieler - Gegner
Projektil - Gegner


Was da genau passiert ist ja erst mal egal.
Deine Gegner-Klasse könnte nun eine Kollisionsfunktion bekommen. Du checkst mit jedem Gegner einfach auf Kollision. Wenn du deine Manager mit einbauen möchtest, könntest du natürlich dem Manager die Funktion geben. Quasi EnemyManager.Collide(Spieler);
Dieser würde dann bei dem Enemy den Code ausführen, der ausgeführt werden soll. Zusätzlich könnte er beim Spieler eine Kollisionsroutine aufrufen.
Bei den Projektilen würde es ähnlich ablaufen. Hier müsste man jetzt natürlich irgendwie mit einem foreach oder so durch die Projektile iterieren. Aber wie dot schon sagt. Guck ob du diese Manager brauchst. Ich habe auch oft Klassen um Entitäten zu halten. Das ist dann aber eher eine Art Repository. Man kann seinen Code mit Design aber auch echt kaputt machen. Guck lieber dass du deine Probleme gelöst bekommst und einigermaßen zufrieden bist. Es gibt viel zu viele Projekte die einschlafen weil man sich unnötig mit Designkram rumschlägt.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

12

13.10.2011, 00:38

Ja, letztendlich merkt man die störken und schwächen eines bestimmten Designs am ehesten durch ausprobieren. Ist natürlich doof, wenn man einiges Umstellen muss, aber so bekommt man eben ein schönes Gefühl dafür, warum gutes Design sinnvoll ist und lernt nicht nur irgendwelche Regel wie "Globale Variablen sind böse" (weil dann kommt man schnell auf die Idee, daraus ein Singleton zu machen und denkt dann, man hätte alle guten Regeln befolgt und alles wäre toll, nur ist es leider einfach nur schlechter geworden).

Für dein Problem würde ich vorschlagen, die Objekte zu verallgemeinern. Normalerweise können ja auch Gegner untereinander kollidieren, d.h. nicht durcheinander durch laufen. Man könnte also z.B. eine Oberklasse für Player und Enemy schreiben und dann einen Manager schreiben, der alle Objekte managed und bei der Bewegung von irgendeinem Objekt, die Kollision mit den anderen prüft. Gegner würden dann halt durch eine KI bewegt, Spieler durch Benutzereingaben (z.B. durch eine virtuelle Move Funktion). So brauchst du für neue Objekttypen nicht direkt einen neuen Manager schreiben, sondern kannst einen für alle benutzen.

Und nochwas, weil es hier am Rande erwähnt wurde: Globale Objekte vermeiden, indem man Zeiger auf ein Objekt über 17 Ebenen an alle anderen Objekte hinunter reicht ist auch nicht unbedingt toller OOP Stil. Hat man zum Beispiel eine Game Klasse, kann man sich fragen ob es nicht sowieso immer nur eine gibt, und daher alle Klassen auf ein globales Objekt zurückgreifen könnten. So kann man potentiell eine Menge durchreiche Code sparen, und weniger Code ist fast immer besser. Ich will damit nicht sagen, dass globale Objekte toll sind, sondern eher, dass man seine Mittel mit ihren Vor- und Nachteilen kennen sollte und wissen sollte, wo man sie einsetzen kann. Und das man einen Überblick haben sollte, wie groß das Projekt einmal werden wird, denn manchmal bekommt man durch schlechtere Kapselung kompakteren und einfacher zu verstehenden Code (der solange besser ist, bis man ihn massiv erweitern möchte, was dann aber bei vorausschauender Planung vielleicht nie der Fall sein wird).
Lieber dumm fragen, als dumm bleiben!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

13

13.10.2011, 01:23

Und nochwas, weil es hier am Rande erwähnt wurde: Globale Objekte vermeiden, indem man Zeiger auf ein Objekt über 17 Ebenen an alle anderen Objekte hinunter reicht ist auch nicht unbedingt toller OOP Stil.

Dann hat man generell ein Designproblem, das durch eine globale Variable auf keinen Fall besser wird. Denn das einzige was man damit erreicht ist, dass das Symptom des Problems (durchreichen durch 17 Ebenen) versteckt wird. Das Problem besteht weiterhin, da das Design sich nicht verändert hat, nur sieht man es nun nichtmehr auf den ersten Blick.

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

14

13.10.2011, 07:58

Hatte letzten noch einen Artikel gelesen in dem stand "man schreibt keine Klassen die auf -er enden!". Finde aber den Link nicht mehr.
Grundsätzlich ging es um Manager/Helper usw. Klassen.
Bleiben wir mal bei dem Beispiel mit dem einfachen Spaceshooter. Man hat also eine Klasse für das Spaceship und dann eine Liste an Enemies.
Einen Manager für beides zu schreiben fühlt sich für mich sehr schräg an. Das Spaceship sollte alle Methoden beinhalten, die sich auf das Spaceship
beziehen. Kollisionsabfragen etc. gehören natürlich NICHT dort hinein. Das Spaceship braucht ja nicht zu wissen, dass es getrofffen wurde, denn das
ist Teil des Games. Also ist die Kollisionsabfrage außerhalb zu realisieren. Ich habe dafür einen Klasse CollisionDetector. Die verwaltet eine Liste
von Objekten und bei jeder Aktualisierung werden die einzelnen Kollisionen gesammelt und in einen Buffer geschrieben. Bloß nicht bei jeder Kollision
direkt per Event/Listener Pattern Nachrichten verschicken. Erst wenn ALLE Objekte durch sind, dann wird eine Nachricht an registrierte Listener verschickt
und als Nachricht geht mein CollisionBuffer auf Reisen. Hier kann dann jeder seine für sich relevanten Kollosionen abfragen.
In unserem Beispiel sind das aber NICHT das Spaceship und nicht die Enemies. Das geschieht in einer andere Klasse (zum Beispiel GameCore). Die behandelt
dann zum Beispiel was bei einem Treffer Spaceship-Enemy überhaupt passieren soll (Energie abziehen, Explosion starten, Sound abspielen etc.).
Mein CollisionDetector besteht aus einer Klasse UND diversen Methoden, die nicht in einer Klasse sind. Das sind vornehmlich Methoden um im CollisionBuffer nach
einer bestimmten ID oder einem Handle etc zu suchen oder ob zwei bestimmte Objekte in einer Kollision verwickelt sind.
Als Klassen hätte ich dann:
  • Spaceship
  • Enemies
  • CollisionDetector
  • GameCore
Kein Manager. Kein Helper. Spaceship zum Beispiel wird dadurch auch zu einer kleinen, einfachen Klasse.
Weiteres Beispiel wären Bullets. Unser Spaceship soll sich ja wehren können. Das Spaceship braucht das aber gar nicht zu wissen. Es besteht gar kein Zusammenhang zwischen Spaceship und Bullets. Beide werden nur durch die GameCore Klasse verwaltet.

15

13.10.2011, 09:12

Hatte letzten noch einen Artikel gelesen in dem stand "man schreibt keine Klassen die auf -er enden!". Finde aber den Link nicht mehr.
Grundsätzlich ging es um Manager/Helper usw. Klassen.
Bleiben wir mal bei dem Beispiel mit dem einfachen Spaceshooter. Man hat also eine Klasse für das Spaceship und dann eine Liste an Enemies.
Einen Manager für beides zu schreiben fühlt sich für mich sehr schräg an. Das Spaceship sollte alle Methoden beinhalten, die sich auf das Spaceship
beziehen. Kollisionsabfragen etc. gehören natürlich NICHT dort hinein. Das Spaceship braucht ja nicht zu wissen, dass es getrofffen wurde, denn das
ist Teil des Games. Also ist die Kollisionsabfrage außerhalb zu realisieren. Ich habe dafür einen Klasse CollisionDetector. Die verwaltet eine Liste
von Objekten und bei jeder Aktualisierung werden die einzelnen Kollisionen gesammelt und in einen Buffer geschrieben. Bloß nicht bei jeder Kollision
direkt per Event/Listener Pattern Nachrichten verschicken. Erst wenn ALLE Objekte durch sind, dann wird eine Nachricht an registrierte Listener verschickt
und als Nachricht geht mein CollisionBuffer auf Reisen. Hier kann dann jeder seine für sich relevanten Kollosionen abfragen.
In unserem Beispiel sind das aber NICHT das Spaceship und nicht die Enemies. Das geschieht in einer andere Klasse (zum Beispiel GameCore). Die behandelt
dann zum Beispiel was bei einem Treffer Spaceship-Enemy überhaupt passieren soll (Energie abziehen, Explosion starten, Sound abspielen etc.).
Mein CollisionDetector besteht aus einer Klasse UND diversen Methoden, die nicht in einer Klasse sind. Das sind vornehmlich Methoden um im CollisionBuffer nach
einer bestimmten ID oder einem Handle etc zu suchen oder ob zwei bestimmte Objekte in einer Kollision verwickelt sind.
Als Klassen hätte ich dann:
  • Spaceship
  • Enemies
  • CollisionDetector
  • GameCore
Kein Manager. Kein Helper. Spaceship zum Beispiel wird dadurch auch zu einer kleinen, einfachen Klasse.
Weiteres Beispiel wären Bullets. Unser Spaceship soll sich ja wehren können. Das Spaceship braucht das aber gar nicht zu wissen. Es besteht gar kein Zusammenhang zwischen Spaceship und Bullets. Beide werden nur durch die GameCore Klasse verwaltet.
Und Listener endet nicht auf "er"? Davon abgesehen ist diese Aussage ziemlich verallgemeinernt. Demnach dürfte man nie Listener-, Observer- oder Adapterklassen verwenden. Es geht darum, dass Klassen einen möglichst aussagekräftigen Namen haben sollen. Aber manchmal muss man halt in ermangelung eines akkuraten Terms einen "Manager" machen. Es bringt nichts einen schönen Namen für ne Klasse zu haben, der aber im Grunde nichts aussagt. Und wenn man keinen guten Namen hat, müssen halt Kommentare herhalten (das gilt nicht nur für Klassen) bis man einen gefunden hat.

Zum eigentlichen Thema: Es ist schwer Spiele streng objektorientiert zu programmieren. Das liegt auch zum Teil daran, dass striktes OOD sehr performancelastig ist, was bei größeren Spielen eher nachteilig ist. Das schönste Design bringt dir nix, wenn dein Spiel dadurch unspielbar wird. Solange aber der Prozessor die nötige Leistung hergibt spricht nichts gegen OOD. Ich mach das gern so, dass ich am Anfang nur meine Game-Klasse habe, anfange da rein zu proggen und später refactore. Wenn ich zB sehe, dass hier und da ne Klasse ausgelagert werden kann, oder irgendwo redundanter Code ist. So musst du dir nicht erst ewig ein gutes Design überlegen, sondern nimmst halt alles wie es kommt. Und wenn dus richtig anstellst ist dein Design am Ende sogar besser, weil es ziemlich schwer ist im Vorfeld alles zu planen, oder währenddessen dir noch Ideen kommen. Es ist ja nicht so als würdest du ein Legoschloss nach Anleitung bauen ôo

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

16

13.10.2011, 10:03

Wie scharfsinnig das mit dem Listener zu erwähnen. Bei dieser Geschichte geht es NICHT um vernünftige Namen oder so etwas. Einen Manager nur anders zu benennen ist
damit nicht gemeint. Sondern vielmehr, dass es keinen Grund gibt für solche Klassen.
Seltsamerweise denken viele Leute bei OOP geht es NUR um Klassen und deren Methoden. Kaum einer denkt mal daran, dass Methoden auch außerhalb von Klassen existieren können und vor allem auch sollten.
In solch einem Manager sind oft Methoden drin, die eigentlich gar nicht in eine Klasse gehören. Darum die Aussage, keine Klassen mit "er". Natürlich gibt es Ausnahmen, da es ja englische Worte gibt, die auf er enden (siehe Listener, Player usw.).
In C gibt es den struct um Daten zu einer logischen Einheit zusammenzufassen. Das OOP Konzept stellt die logische Erweiterung dieser struct dar und fügt noch Methoden zu structs hinzu (darum kann man auch class und struct gleich verwenden). Das war seinerzeit die Idee hinter den Klassen und das grundlegende von C++.
Dennoch existieren weiterhin Methoden, die NICHT zu einer Klasse gehören. Da viele aber das Konzept nicht verstehen, entstehen plötzlich Manager/Helper etc. Klassen.
Aber nur weil sie denken, dass eine Methode schließlich in irgendeine Klasse rein MUSS. Das ist aber falsch.
Dieses Mißverständnis eisenhart durchgezogen führt dann zu Performance Problemen.
Also was shiroto schreibt, ist natürlich vollkommen falsch. Ein falsch angewendetes OOP Design kann zu Performance Problemen führen.
Grundsätzlich der Ansatz, ich fang einfach an und dann refactore ich bei Bedarf ist auch etwas schräg. Man sollte schon vorher ein bisschen drüber nachdenken.
Dann kann man sich direkt viel Refactoring ersparen. Aber dazu bedarf es vor allem viel Erfahrung.

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

17

13.10.2011, 10:25

Was du so sagst kann man aber auch nicht 100%ig unterschreiben. Absolut von Manager- und Helperklassen abzuraten ist genauso quatsch. Dass Problem ist, dass diese Klassen oft falsch eingesetzt werden. Ein großer Punkt von OOP ist Kapselung. Daten kapseln und nach außen verschleiern. Da passen dann freie Methoden erst mal genauso wenig rein wie irgendwelche globalen Variablen. Wenn man sich "strikte" objektorientierte Sprachen anguckt fällt das einem auch schnell auf. Methoden werden hier einfach immer in Klassen zusammen gefasst. Das hat alles mit Performance erst mal wenig zu tun. Hinter OOP steckt ein Gedanke. Genauso steckt ein Gedanke hinter funktionalen Sprachen. Das hat alles oft erst mal wenig mit Performance zutun sondern mehr mit Logik. Dass da dann irgendwann die Performance auch mit rein spielt ist klar. Ich finde was shiroto von sich gibt überhaupt nicht doof. Klar ist das mit den listenern quatsch. Klar ging es hier nicht um aussagekräftige Namen, aber man darf nicht zu streng in Objekten denken. OOP kann einfach viel Performance fressen. Es gibt oft Lösungen die zwar nicht so schön ins OOP Konzept passen, aber trotzdem lohnenswert sind. Sei es, weil es einem eine Menge arbeit abnimmt, da man sich viel unnötigen Code spart, oder da man es anders schneller oder speicherschonender lösen könnte. Man muss einfach erkennen, wann man anders arbeiten sollte/kann. Vor allem sieht man dann auch oft wieder bei Sprachen wie Python etc, dass die Leute ihre sonst so strikte Objektorientiertheit vergessen. Und warum ist das so? Nun weil es manchmal einfach schneller geht. Und das muss halt nicht immer schlimm sein.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

18

13.10.2011, 10:57

Das ist der Grund warum zum Beispiel Java keine reine objekt orientierte Sprache ist. Denn in Java ist man gezwungen Methoden immer in Klassen zu kapseln.
Das widerspricht aber dem OOP.
Der Grund für Manager oder Helper Klassen ist meiner Meinung nach, dass Leute halt auf teufen komm raus Methoden in Klassen kapseln wollen.
Oder sogar denken, dass man das nach OOP machen muß. Sie denken immer nur in Klassen. Das entspricht aber nicht dem OOP.
Wenn man sich nämlich dann mal so Manager/Helper Klassen anschaut, findet man dann Konstrukte:

C-/C++-Quelltext

1
2
3
4
5
class EnemyManager {

  public:
    Enemy* createBoss(int energy);
}


Wozu ist solche eine Methode in einer Klasse? Es gibt keinen Grund.
Ein sehr guter Hinweis sind auch ebenfalls statische Methoden.

Ich versuche nur zu sagen, dass es vollkommen legitim ist, wenn man auch eigenständige Methoden außerhalb von Klasse definiert. Das ist OOP.
Vielleicht denke manche, man würde C und C++ vermischen und das wäre falsch. Aber es ist genau anders rum.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

19

13.10.2011, 11:48

Das ist der Grund warum zum Beispiel Java keine reine objekt orientierte Sprache ist. Denn in Java ist man gezwungen Methoden immer in Klassen zu kapseln.
Das widerspricht aber dem OOP.

Java ist aufgrund der primitiven Datentypen nicht _rein_ objektorientiert - Integer sind in Java keine Objekte (auch C++ ist nicht _rein_ objektorientiert)
zudem gehören Funktionen außerhalb von Klassen (und statische Funktionen in Java/C#) meines Wissens nicht zur objektorientierten Programmierung

Ich versuche nur zu sagen, dass es vollkommen legitim ist, wenn man auch eigenständige Methoden außerhalb von Klasse definiert. Das ist OOP.

da wage ich ebenfalls, zu widersprechen

wenn man sich über objektorientierung unterhält, sollte man auch immer ein Auge auf Smalltalk werfen


es ist richtig, dass (einige) Beispiele von dir so nicht umgesetzt werden sollten
das allerdings nur, weil es einem schlechten Design entspricht, nicht weil die Funktionen zwingend ausgelagert werden müssen

am Beispiel des Player- und Enemymanagers:
die Methoden, die diese beiden Klassen enthalten, hätten in eine andere Klasse gelegt werden müssen (wie "GameCore")
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

20

13.10.2011, 12:18

Seltsamerweise denken viele Leute bei OOP geht es NUR um Klassen und deren Methoden. Kaum einer denkt mal daran, dass Methoden auch außerhalb von Klassen existieren können und vor allem auch sollten.

Was hat OOP mit irgendwelchen sprachspezifischen Artefakten zu tun? Ich kann auch in Assembler objektorientiert Programmieren. Dort gibt es weder Methoden noch Funktionen. Was nun?

Dennoch existieren weiterhin Methoden, die NICHT zu einer Klasse gehören. Da viele aber das Konzept nicht verstehen, entstehen plötzlich Manager/Helper etc. Klassen.
Aber nur weil sie denken, dass eine Methode schließlich in irgendeine Klasse rein MUSS. Das ist aber falsch.
Dieses Mißverständnis eisenhart durchgezogen führt dann zu Performance Problemen.

Ich seh da keinen Zusammenhang. Methoden die nicht zu einer Klasse gehören? Wenn du mich fragst widerspricht das der Definition einer Methode. Und was hat das mit Manager Klassen zu tun? Und wieso sollte genau das zu Performance Problemen führen?

Ein großer Punkt von OOP ist Kapselung. Daten kapseln und nach außen verschleiern. Da passen dann freie Methoden erst mal genauso wenig rein wie irgendwelche globalen Variablen.

Ich sehe nicht inwiefern freie Funktionen oder globale Variablen den Prinzipien der OOP oder der Kapselung widersprechen würden.

Das ist der Grund warum zum Beispiel Java keine reine objekt orientierte Sprache ist. Denn in Java ist man gezwungen Methoden immer in Klassen zu kapseln.
Das widerspricht aber dem OOP.

Sry, aber das ist Schwachsinn.

Der Grund für Manager oder Helper Klassen ist meiner Meinung nach, dass Leute halt auf teufen komm raus Methoden in Klassen kapseln wollen.
Oder sogar denken, dass man das nach OOP machen muß. Sie denken immer nur in Klassen. Das entspricht aber nicht dem OOP.

Auch hier: Wo genau soll da der Zusammenhang sein.

Ich versuche nur zu sagen, dass es vollkommen legitim ist, wenn man auch eigenständige Methoden außerhalb von Klasse definiert. Das ist OOP.

Und ohne freie Funktionen ist es kein OOP? Warum? Was hat OOP mit irgendwelchen Features einer bestimmten Sprache zu tun?

Vielleicht denke manche, man würde C und C++ vermischen und das wäre falsch. Aber es ist genau anders rum.

Das ist mir wohl zu hoch.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (13.10.2011, 12:36)


Werbeanzeige