Trennung von Grafik und Logik
-
Hallo zusammen,
Ich habe schon oft gehört, dass man bei der Spieleprogrammierung grundsätzlich Grafik und Logik voneinander unabhängig implementieren soll. Ich habe dazu auch viele Threads gelesen, aber ich verstehe immer noch einige grundlegende Dinge nicht.Erstens: Ich bin der Ansicht, Logik sind all die Berechnungen der Spielfiguren, Kollisionsabfragen, Eingaben etc. und Grafik ist nur die Ausgabe. Besteht hierbei ein Missverständnis? Versteht man unter Logik etwa nur Spielereingaben?
Zweitens: Inwiefern kann man Grafik und Logik überhaupt strikt trennen? Nach meiner Definition oben ist ja Grafik immer direkt von der Logik abhängig. Für die Grafik ändert sich nur etwas, wenn auch die Logik neu berechnet wurde. Welchen Sinn macht es also, Grafikroutinen häufiger aufzurufen als Logik?
Drittens: Braucht man hierfür Multithreading oder reichen einfache Schleifen? Ich wäre froh, wenn es einfach ginge...
Bitte antwortet spezifisch auf die einzelnen Fragen.
-
Nexus schrieb:
Hallo zusammen,
Ich habe schon oft gehört, dass man bei der Spieleprogrammierung grundsätzlich Grafik und Logik voneinander unabhängig implementieren soll. Ich habe dazu auch viele Threads gelesen, aber ich verstehe immer noch einige grundlegende Dinge nicht.Erstens: Ich bin der Ansicht, Logik sind all die Berechnungen der Spielfiguren, Kollisionsabfragen, Eingaben etc. und Grafik ist nur die Ausgabe. Besteht hierbei ein Missverständnis? Versteht man unter Logik etwa nur Spielereingaben?
Zweitens: Inwiefern kann man Grafik und Logik überhaupt strikt trennen? Nach meiner Definition oben ist ja Grafik immer direkt von der Logik abhängig. Für die Grafik ändert sich nur etwas, wenn auch die Logik neu berechnet wurde. Welchen Sinn macht es also, Grafikroutinen häufiger aufzurufen als Logik?
Drittens: Braucht man hierfür Multithreading oder reichen einfache Schleifen? Ich wäre froh, wenn es einfach ginge...
Bitte antwortet spezifisch auf die einzelnen Fragen.
class mygame { public: void render() { berechne_zeug(); berechne_schatten(); berechne_mehr_grafik_zeug(); male_zeug(); } void logic() { berechne_ki(); if(spieler_drückt(EINE_TASTE)) { irgendwas->bewege(DAHIN,DORTHIN); } update_alles(); //goldstand, holzvorrat, vergiftung schadet spieler usw } void run() { logic(); render(); } };Natürlich ist die Grafik von der Logik abhängig. Es geht dabei ledichglich darum sowas zu trennen:
if(player_drückt(EINE_TASTE) { player->move(100,100); player->draw(); }
-
TravisG schrieb:
Natürlich ist die Grafik von der Logik abhängig. Es geht dabei ledichglich darum sowas zu trennen:
if(player_drückt(EINE_TASTE) { player->move(100,100); player->draw(); }Okay, aber so was mache ich auch nicht, sondern schön alles nacheinander (allerdings je einmal pro Frame). Ist jedoch so etwas sehr schlecht?
int main() { while (Laufend) { // warten, bis eine bestimmte Zeit seit letztem Frame vergangen ist Spieler.Eingabe(); Spieler.RechneAllesAus(); Spieler.Ausgabe(); } }Und bei deiner Klasse, würden die drei
berechne-Funktionen nicht eher inlogic()gehören? Oder nach welchem Kriterium teilst du diese genau ein?
Anders ausgedrückt: Wieso gehörtupdate_alles();in die Logikfunktion und dieberechne-Funktionen nicht? Oder ist das hier einfach mehr oder weniger willkürlich?
-
Nexus schrieb:
TravisG schrieb:
Natürlich ist die Grafik von der Logik abhängig. Es geht dabei ledichglich darum sowas zu trennen:
if(player_drückt(EINE_TASTE) { player->move(100,100); player->draw(); }Okay, aber so was mache ich auch nicht, sondern schön alles nacheinander (allerdings je einmal pro Frame). Ist jedoch so etwas sehr schlecht?
int main() { while (Laufend) { // warten, bis eine bestimmte Zeit seit letztem Frame vergangen ist Spieler.Eingabe(); Spieler.RechneAllesAus(); Spieler.Ausgabe(); } }Und bei deiner Klasse, würden die drei
berechne-Funktionen nicht eher inlogic()gehören? Oder nach welchem Kriterium teilst du diese genau ein?
Anders ausgedrückt: Wieso gehörtupdate_alles();in die Logikfunktion und dieberechne-Funktionen nicht? Oder ist das hier einfach mehr oder weniger willkürlich?Nein, aber das ist nirgends wirklich festgelegt und wohl Ansichtssache. Ich sage jedoch, solange die Berechnungen von Grafiksachen wirklich keine Auswirkungen auf die Logik haben (also, nicht dass aus irgendeinem grund die berechnung eines schattens den wechsel der spielerposition zur folge hätte oder so), gehören sie auch zum Grafikteil dazu. Wäre auch irgendwie unlogisch, z.B. die Berechnungen von irgendwelchen postprocessing Effekten in die Logik reinzupacken, dessen Verarbeitung normal vor der grafischen Ausgabe erfolgt.
-
Ist in diesem Falle mein kleines Codebeispiel in Ordnung?
Und ist es legitim, mitwhile(AbgelaufeneZeit() < 0.04);zu warten?Sleep()ist da nicht so optimal, oder?
-
die logik deines spieles sollte auf einem rechner ohne dein graphikmodul laufen, dann ist das gut getrennt.
die graphik andererseits sollte noch zu einem gewissen grad laufen, auch wenn die logik nicht laeuft, weil sie z.b. in pause ist.
diese trennung erlaubt dir dann auch weitere dinge, z.b. kannst du mit einem eigenen debugging tool die objekte in deinem spiel z.b. mit einem in-game-browser, durchgehen (waehrend sie im pausemodus sind) und dir alle states anschauen, weil die graphik und input dann noch weiter laufen koennten. das ist z.b. fuers debuggen vielleicht nuetzlich.
wenn man es gut macht, kann jedes modul fuer sich ueberleben und 'kommuniziert' nur die dinge weiter die nicht offensichtlich sind. z.b. kann music die ganze zeit weiter spielen, ein schuss muesste aber an das soundsystem weitergereicht werden.
-
Da haben sich schon andere Gedanken drum gemacht und vor 30 Jahren das Model-View-Controller Konzept erfunden.
-
Nexus schrieb:
Ist jedoch so etwas sehr schlecht?
int main() { while (Laufend) { // warten, bis eine bestimmte Zeit seit letztem Frame vergangen ist Spieler.Eingabe(); Spieler.RechneAllesAus(); Spieler.Ausgabe(); } }Naja "schlecht" ist es nicht unbedingt, es funktioniert und auch verständlich. Aber folgendes:
int main() { Player player; // ... while(running) { while(get_input()) { if(...LEFT...) { player.move_left(); } } world.calculate_enemy_positions(); renderer.draw(player); // Renderer::draw() für class Player überladen } }ist meiner Meinung nach eine striktere Trennung.
Denn so ist
1. die Bewegung (Logik ist in dem Fall die Bewegung des Spielers und die Bewegung der Gegner) des Spielers unabhängig der verwendeten Technologie, d.h. sie kann in einem SDL_Event empfangen werden, in einer WndProc mit WinAPI, mit DirectInput, es könnte eine Taste auf der Tastatur sein, ein Joystick, oder eine Mausbewegung, oder oder oder ...
2. der Player unabhängig von der Grafik, d.h. nicht als Methode von class Player, sondern als Methode von Renderer implementiert. Sie könnte mit ncurses auf einem Text-Terminal ausgegeben werden (Rogue-like), sie könnte mit OpenGL, DirectX, SDL, GDI+, XLib, Gdk, ... ausgegeben werden. Je nach verwendetem Renderer.Das macht z.B. eine Portierung erheblich einfacher.
"Schlecht" kann man aber dein Beispiel nicht unbedingt nenne. Es gibt Designs, die sich anbieten, und Designs, die Overkill für bestimmte Dinge sind. Desins, die das Verständnis des Codes erschweren - und somit die Wartbarkeit.
Joa..das wäre eigentlich nen MVC.
...MVC's a paradigm for factoring your code
into functional pieces so your brain does not explode
to achieve reusability you gotta keep these boundaries clean
Model on the one side, View on the other,
the Controller in-between...
heißt es in einem Lied...google mal nach MVC und mp3
Grüße, Xantus
-
naja, 'player' ist eigentlich eine fuers rendering irrelevante klasse. was noetig ist ist 'camera' oder 'view'.