Input Kontrolle

Hey Leute…

Angenommen wir haben eine Klasse Game. Nun haben wir eine andere Klasse wo alle
Input Events ankommen (irgendein library interner Adapter oder so).

Wie würde man die Input Events nun möglichst “richtig” an das Spiel weiterleiten?
zB hat man im Adapter die Methoden press() und release() oder so. Wenn ich jetzt
diesem Adapter im Konstruktor einfach die Instanz der Klasse Game mitgebe, dann hat
ja meine Input Klasse quasi die volle Kontrolle über das Spiel was ja nicht so sein sollte?
Und wenn ich nur 2 public methoden press() und release() im Game selbst mache, dann
kann ich die Klasse an sich ja direkt zum Inputlistener machen… versteht ihr was ich meine?

Ist da nicht Observer Pattern das richtige Stichwort? Nur irgendwie Blick ich das nicht so ganz…

danke ^^

keine Angst vor ‘dann hat ja meine Input Klasse quasi die volle Kontrolle über das Spiel’ im kleinen Maßstab,
es mag richtig sein dass so am Ende nicht ideal, aber es lohnt nicht unbedingt, solche sekundären Ziele am Anfang zu verfolgen,

gut möglich dass sowieso alles noch paar Mal umstrukturiert wird,
reicht wenn überhaupt später anzugehen statt jeweils zwischendurch Sorge und Arbeit allein dafür und dann doch wieder zu verwerfen

und die Gefahr eines falschen Aufrufs ist sowieso reichlich abstrakt, ist ja kein Fremdcode,
nahezu jeder Programmteil der etwas bestimmtes kann, wie einen Gegenstand in die Spielwelt zu setzen/ entfernen,
könnte auch Amok laufen und alles mögliche ändern, da braucht es nicht strenge Kontrolle/ Rechteverwaltung…


ob Game Inputlistener im klassischen Sinne sein soll oder nicht, darüber musst du dir selber klar sein,
wenn es langweiligen Code auszuklammern gibt, dann kann das sinnvoll sein,

als Auftrennung der Aufgaben sicher separater Listener auch so ein sekundäres Ziel,
was ich wiederum genauso am Anfang nicht so eng sähe


ein kleiner Trick noch:
ähnlich wie Game als möglicher Inputlistener in der API nur als Inputlistener mit bestimmten Methoden bekannt ist, obwohl mehr vorhanden sind,
könntest du innerhalb deines Programmes auch ein Interface GameInput nur einem bestimmten Methodensatz definieren,
Game implementiert das Interface,
-> ein separater Listener/ Adapter bekommt Game nur in Form von GameInput zu sehen, kann nur diese Methoden aufrufen,
nicht die durchaus 20 anderen public Methoden von Game

[quote=SlaterB]ein kleiner Trick noch:
ähnlich wie Game als möglicher Inputlistener in der API nur als Inputlistener mit bestimmten Methoden bekannt ist, obwohl mehr vorhanden sind,
könntest du innerhalb deines Programmes auch ein Interface GameInput nur einem bestimmten Methodensatz definieren,
Game implementiert das Interface,
-> ein separater Listener/ Adapter bekommt Game nur in Form von GameInput zu sehen, kann nur diese Methoden aufrufen,
nicht die durchaus 20 anderen public Methoden von Game[/quote]

den Ansatz könnte man noch etwas weiterspinnen, da in diesem Fall ja ein einfacher Cast reichen würde und schon hätte man wieder das Game. Meine Idee (die ich mal woanders umgesetzt habe aber nicht auf “Sicherheit” überprüft habe) war, eine anonyme Instanz des Interfaces zu erzeugen, dass letztendlich auch einem per Parameter übergebenen Instanz arbeitet, diese aber nicht explizit Speichert. Auf “normalen” Wegen dürfte man da meiner Ansicht nach nicht mehr ran kommen

Ach Spieldesign und richtiges OOP funktionieren sowieso nicht in allen Fällen.
Viele Spiele implementieren allein deshalb eine externe Skriptsprache die dann das gesamte OOP Konzept über den Haufen wirft.

Ich sag mal so wie es bei einer meiner Projekte gemacht wurde, dabei wurde der Input wie ein Netzwerkinput behandelt. D.h. der lokale Input wird in ein Byte-Array verarbeitet und kodiert und an das eigentliche Spiel wie ein UDP Päckchen gesendet/eingeschleust und das Spiel wertet die Daten dann wieder aus.
Das hat den Vorteil dass man den lokalen Input auch über ein Netzwerk senden kann, Client und Spielserver müssen also nicht zwingend Beide lokal sein sein oder man kann auch den Input eines anderen Spielers/Benutzers übertragen.

Ein weiterer Vorteil ist dass die Inputklassen vollkommen unabhängig vom Spiel sind.
Jedes Gerät kann einen anderen Input haben, ein Computer meist eine Maus, ein Smartphone einen Touchscreen aber manche spielen auch lieber mit den Pfeiltasten oder einem Controller und entsprechend gibt es andere Listener-Klassen/Schnittstellen.
Für die Spiellogik ist das aber vollkommen uninteressant, sie interessiert es nur ob der Spieler einen Klick gemacht hat oder ob du deinen Bildschirmausschnitt nach oben/unten/rechts/links verschieben willst nicht woher der Befehl kommt. Das kann auf einem Computer ein Links oder Rechtsklick, auf einem Smartphone ein Doubletap sein, auf einem Controller einer der Sticks oder Tasten oder gar ein Befehl über das Netzwerk wie bei Teamviewer z.B…

Die Daten könnten z.B. so abstrahiert werden:
new int[] { User.GESTURE, User.MOVEWORLD, deltaX, deltaY };
oder
new int[] { User.GESTURE, User.ZOOM, deltaZ };
oder
new int[] { User.INPUT, User.CLOSEPROGRAM };
oder
new int[] { User.INPUT, User.CLICK, x, y };

Das wäre jetzt eine Möglichkeit wie wir das z.B. gemacht haben, d.h. nicht das es DIE EINE richtige Möglichkeit ist.
Aber der größte Teil und Programmieraufwand ist es natürlich den Input erstmal bis dahin zu verarbeiten, auf einem Touchscreen war das ziemlich schwer weil dieser kann z.B. nicht zwischen unterschiedlichen Fingern unterscheiden (natürlich!) dafür muss man selber Heuristik schreiben. Auf dem Computer mit Maus/Tastatur und Controller relativ leicht.

das Datenformat ist keine Voraussetzung für austauschbare Eingabe, genauso könnte es verschiedene Adapter geben,
von GUI aus und Verarbeitung von Netzwerk-Nachrichten, die jeweils die richtigen OOP-Methoden aufrufen,

das Datenformat kann aber trotzdem vorteilhaft sein zum einheitlichen Loggen, Speicherung, Undo/ Redo usw.,

ich merke es auch in meinen Programmen bei allen möglichen Daten wie Person mit Name, Alter usw.,
oft reizt mich, sie temporär oder gleich komplett als Liste bzw. Map von Feldern darzustellen mit Datentyp, Wert, besondere Eigenschaften,

dann könnte man generische Eingabeformulare in der GUI erstellen, Vergleiche, Ausgaben, alles Richtung DB sowieso ganz anders anzugehen,
SQL und DB-Tabellen haben eben ihre Berechtigung, für DATEN,
wobei DATEN weit gehen kann, in einer Rechteverwaltung können auch Komponenten der Anwendung und Aktionen wie EDIT usw. zu herumreichbaren Enum-Werten werden…

[quote=SlaterB]das Datenformat ist keine Voraussetzung für austauschbare Eingabe, genauso könnte es verschiedene Adapter geben,
von GUI aus und Verarbeitung von Netzwerk-Nachrichten, die jeweils die richtigen OOP-Methoden aufrufen,

das Datenformat kann aber trotzdem vorteilhaft sein zum einheitlichen Loggen, Speicherung, Undo/ Redo usw.,[/quote]
Deine Anwendung wird nicht auf dem UI Thread laufen, je nach OS oder Eingabegerät wirst du die Eingaben aber über den UI oder anderweitige Threads in dein Programm einschleusen müssen.
Einfach irgendwelche Methoden aufzurufen vom UI in dein Programmthread wird Fehler und Exceptions verursachen und für jede Methode synchronized und volatile und noch was überall einzuführen würde den Code zuballern, verlangsamen und Fehleranfällig machen.
Schleust man die Daten aber synchronisiert in den Spielverlauf über das Netzwerkmodul hat man eine zentrale Verwaltungsstelle und der lokale Input ist auch nichts anderes wie ein Netzwerkinput.

Zudem wird die Verarbeitungslogik des Human-Machine-Inputs komplett vom Spiel abgekoppelt.
Du sagst man könnte einfach entsprechenden Methoden aufrufen, was passiert denn wenn man die Taste “W” drückt? Wird dann die Methode “pressedW()” aufgerufen? Oder “pressedKey( ‘W’ )”? Oder abstrahierter die Methode “moveForward()”?
Damit ist die Verarbeitungslogik nicht komplett abgekoppelt, du musst für alles eine eigene Methode schreiben und alle Spiel-Objekte und GUI-Elemente müssen diese Methoden implementieren und es ist schwerer erweiter oder veränderbar als eine einfache switch-Abfrage bei der man einmal “return true” schreibt und nicht (übertrieben) für jede Taste auf der Tastatur eine eigene Methode in jedem Spielobjekt oder GUI Element kreiert.
Im OOP Kontext magst du recht haben, aber im Praxis sieht das meiner Meinung anders aus und wie gesagt wir haben das so erfolgreich umgesetzt, was natürlich nicht heißt das es nicht besser geht und gerade diese Diskussion mit dir begrüße ich sehr!!
Input-Handling ist in GUI Systemen kein einfaches Thema, beim MVC Modell fehlt es komplett.

Zum letzteren: Eben, es ist vielseitig einsetzbar und einheitlich.

[quote=mymaksimus]Nun haben wir eine andere Klasse wo alle
Input Events ankommen (irgendein library interner Adapter oder so).[/quote]Also wenn Du Dir nicht gerade Dein eigenes Betriebssystem schreibst hat Du für jede relevante Programmiersprache bereits eine Abstraktionsebene zur Eingabe-Hardware.
Du hast also nicht die Eingabeklasse, sondern ein Framework mit Hooks, an die Du Dich anhängen kannst. Bei Swing sind das beispielsweise die diversen add*Listener() Methoden an GUI-Komponentenklassen.

bye
TT

zunehmend Offtopic, aber einmal noch

[QUOTE=TMII]Deine Anwendung wird nicht auf dem UI Thread laufen, je nach OS oder Eingabegerät wirst du die Eingaben aber über den UI oder anderweitige Threads in dein Programm einschleusen müssen.
[/quote]
verstehe ich nicht ganz, der Kern jeder Anwendung, welche über verschiede Arten zu steuern ist,
sollte unabhängig etwa vom AWT-Thread einer GUI laufen,

was die Anwendung selber an UI-Threads und sonstiger zentraler Steuerung hat wird natürlich genutzt,
von jedem Input gleich,
wenn ein bestimmtes Event-Format die Anwendung tief durchdringt, dann ist sie nicht zu umgehen, stimme zu,

aber so ein System führt man eher auch schon für nur eine Eingabe ein, nicht in Hinblick auf mehrere :wink:
hat Swing ja auch, ok Tastatur + Maus, evtl. noch interne Events, aber nur mit Tastatur würde sicher nicht anderer Aufbau gewählt

Du sagst man könnte einfach entsprechenden Methoden aufrufen, was passiert denn wenn man die Taste „W“ drückt? Wird dann die Methode „pressedW()“ aufgerufen? Oder „pressedKey( ‚W‘ )“? Oder abstrahierter die Methode „moveForward()“?

diese Frage stellt sich beim Netzwerk-Input mit UDP-Päckchen genauso,
sobald ein Programm die Komplexität von ganzen GUIs erreicht mit Focus auf Komponenten, Berücksichtung Feststelltaste, Edit-Modus usw.,
da ist es natürlich ganz was anderes als einfache erste Anwendungen mit globalen Bedeutungen jeder Aktion, höchstens noch Position der Maus dazuzunehmen,
gegen OOP sehe so eine mächtige Verarbeitung auch nicht gestellt,

ich bezog mich vorher vor allem auf den Punkt der unterschiedlichen Eingabemedien und die genannte Umwandlung auf UDP,
die ich als Netzwerk-Anschluss verstand, über welchen Umweg es auch für andere Eingaben in das Programm mit interner Verarbeitung X rein ging,
da hätte jeder andere Input auch direkt an die interne Verarbeitung X anknüpfen können,

wenn UDP oder was auch immer im Inneren der Anwendung vorliegt so wie Event im AWT-Thread, dann natürlich zwingend

Hey also ich finde eure Diskussion ja sehr interessant und lese mir das gern alles durch, aber noch mal gaanz kurz zum thema… ^^

ja klar, ich benutze ja libgdx, natürlich ist das ganze sehr hart abstrahiert.

Ich habe nun eine Klasse GameInputAdapter und GameGestureAdapter, beide werden so:

multiplexer.addProcessor(new GameInputAdapter(this));
multiplexer.addProcessor(new GestureDetector(new GameGestureAdapter(this)));
Gdx.input.setInputProcessor(multiplexer);

wie… meinst du das?

naja jedenfalls sieht der InputAdapter beispielsweise so aus:

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        lastx = screenX;
        lasty = screenY;
        Vector3 v = game.getCamera().unproject(new Vector3(screenX, screenY, 0));
        game.getGameMap().debugScreenTapped(v.x, v.y);
        return super.touchDown(screenX, screenY, pointer, button);
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer){
        Vector2 v = new Vector2(screenX - lastx, screenY - lasty);
        v.scl(0.2f * Gdx.graphics.getDensity());
        game.getCamera().translate(-v.x, v.y);
        lastx = screenX;
        lasty = screenY;
        game.getCamera().update();
        return super.touchDragged(screenX, screenY, pointer);
    }

der holt sich also zugriff auf die kamera und auf die karte über die game instanz die im konstruktor übergeben wird…
kann man das fürs erste so stehen lassen? Oder wäre „wenigstens funktionalität auslagern“ ein schritt den man auch
nicht „erstmal so hinnehmen kann“ ?

die Adapter in Swing im Gegensatz zu Listener gibt es ja, damit man nicht alle Methoden teils leer implementieren muss, was schon etwas langweiligen Code darstellt,
dass die Hauptklasse von einem Adapter erbt ist ja ziemlich ausgeschlossen, da bliebe nur Listener mit Methoden oder Auslagern


bei den beiden zuletzt genannten Methoden sind die zugehörigen Instanzattribute lastx + lasty auch von Bedeutung,
das bildet eine Einheit, nicht unbedingt ‚langweilig‘ :wink: ,
technisch wäre es machbar das auch in Game als direkter Adapter abzubilden, aber in Game sicher noch viel mehr vorhanden,
also gut denkbarer Weg einer eigenen Klasse dafür, solches Aufräumen per Auftrennen ist schnell gemacht und hilft sofort für mehr Übersichtlichkeit

wenn lastx/lasty nicht in Game, dann muss der Adapter auch
Vector2 v = new Vector2(screenX - lastx, screenY - lasty);
ausrechnen, kann niemand sonst,

sonst müsste lastx/y woanders bekannt sein, da muss man sich entscheiden,
je mehr wichtige Berechnungen davon abhängen sollte es vielleicht doch wer wissen…

        game.getCamera().translate(-v.x, v.y);
        game.getCamera().update();

ist im Adapter durchaus noch eine Stufe fraglicher,
kann so akzeptabel sein, falls etwa niemand sonst die Kamera nutzt,

vielleicht aber auch ein Aufruf game.doSomethingWith(v) oder camera.doSomethingWith(v) gut,
vielles ist möglich, hängt vom Gesamtbild, den Aufgaben der anderen Komponenten,
der Frage ob noch andere Akteure gleiche oder ähnliche Aufrufe absetzen usw. ab…

[quote=SlaterB]vielles ist möglich, hängt vom Gesamtbild, den Aufgaben der anderen Komponenten,
der Frage ob noch andere Akteure gleiche oder ähnliche Aufrufe absetzen usw. ab[/quote]

ja und genau ist wohl die aussage „erstmal stehen lassen, und schauen wie es sich entwickelt“ nicht ganz fehl am platz? :wink:

Danke auf jedenfall für die ganzen Antworten ^^

ja, das war ja zumindest auch meine Meinung am Anfang :wink:
in der Tat erstmal nicht schlimm Game zu übergeben und dass der Adapter auf die Kamera + getGameMap() zugreift,

ob das in zwei Wochen noch so dasteht falls täglich drangearbeitet wäre fraglich,
und noch viel später wenn viele Features eingebaut sind kann man dann überlegen was zusammengehört und neu sortieren

nicht gerade empfehlenswertes Vorgehen für ‚professionelle Softwareentwicklung‘ mit SVN, Test und Doku und 100 Euro die Stunde,
aber nachdem was du fragst und vom Programm erkennbar ist, gewiss erstmal ok

haha naja wie wäre es denn mit svn test und 100 euro die stunde? :wink:


ach ja und was ist jetzt mit der kommunikation >zwischen den einzelnen Input Adaptern< ?
Gerade habe ich ja nur die Trennung Gesture und Input einfach weil es library intern so ist.

Nun muss mein input listener teilweise aber wissen ob gerade gezoomt wird… das weiss der gesture listener.
ist soetwas wie public static boolean ZOOM_ACTIVE = false; im GestureAdapter in ordnung?

Bzw wie würde man es mit 100€/h machen? :wink:

entsprechende Spezialisten hier im Forum sind dir vielleicht bekannt wenn auch andere Threads gelesen, sonst auch nicht weiter auszubreiten,

auch über das static mag ich mal hinwegsehen, ohne hoffentlich manche Leser mit anderen Vorstellungen zu sehr zu erzürnen,

ansonsten könnten evtl. mehrere Listener ihre Informationen an eine zentrale Komponente (nicht Game) übergeben,
diese bekommt setZoom(boolean) auf true von Adapter A mit, merkt sich das sauber intern,
dazu dann touchDown(int screenX, int screenY, int pointer, int button) wahrscheinlich unverarbeitet von Adapter B durchgereicht
und kann das verarbeiten mit Zoom-Info lokal vorhanden

ein Adapter der beite Input-Interface implementiert geht nicht direkt?

Die Frage ist nicht vollstaendig. In Ordnung wofuer oder fuer wen?

Man koennte sagen, es funktioniert, also ist es „in Ordnung“.
Man koennte auch sagen, es ist eine globale Variable, also nicht in Ordnung. Gruende sind vielfaeltig, vor allem aber verteilst du Code, der eigentlich zusammen gehoert ueber mehrere Klassen.

Zum Thema: Ich wuerd versuchen, die einzelnen Aktionen die du machen kannst, als Klassen abzubilden. Die koennen dann die Input/Gesture/Network-Listener implementieren oder irgendwie benutzen. Auch eine Referenz um Game werden die wohl brauchen. Da rein gehoert die Steuerlogik der Aktion (also z.B. die zoomActive Variable, lastX und lastY, etc).

Im Idealfall kannst du dann irgendwo zentral sagen: inputDispatcher.activeDispatch(zoomAction). Damit kannst du also die aktuelle „Aktion“ (okay, Aktion is doofer Name, vielleicht Controller… nee, auch bloed. CursorControl? Tool?) aktivieren. Evlt. brauchst du auch mehrere gleichzeitig aktive Tools?

Aber wie auch Slater schon sagte, mach erstmal das es geht und ueberleg danach, was alles bloed ist und verbessere es (oder fang dann nochmal von vorne an :-P).

Darauf wird es wahrscheinlich am ehesten hinaus laufen. Aber das kennen wir doch alle irgendwie, oder?
Würde auch sagen probier einfach mal rum, neu machen kannst du später mit der gewonnenen Erfahrung.

Z.B. sehe ich jetzt nicht dass du dir schon ein Hierarchie Modell überlegt hast. In Spielen sind Spielobjekte meist Subelemente eines größeren Objektes die dann irgendwann ein Subelement der Welt darstellen oder des Besitzers/Nutzers des Objektes, der Input wird dann durch die Hierarchie nach unten weiter gegeben bis eines dieser Objekte den Input verarbeitet hat.
Da du LibGDX verwendest kommt das dann auch noch irgendwann.

Solche und weitere Dinge werden irgendwann wichtig und deshalb ist der Input eines der ersten Dinge die man zu Anfang eines Projektes richtig implementieren muss und sich dann durch das gerammte Programm ziehen.

Ein im Nachhinein nachbessern funktioniert deshalb meist einfach nicht.
Das gleiche gilt für Netzwerkprogrammierung, ein Singleplayerspiel im Nachhinein zu einem Multiplayerspiel zu machen ist fast ein Ding der Unmöglichkeit und lokaler Userinput und Netzwerkinput greifen thematisch wie auch logisch ineinander.

Von daher gehe ich davon aus dass du das immer wieder neu implementieren wirst, sei es für das gleiche Projekt oder für folgende größere Projekte.

Für 100€/h würde man wahrscheinlich nicht wirklich OOP (oder Java) verwenden. Bei Spielen wird OOP meist der Performance geopfert. Das dann noch alles richtig funktioniert und gewartet wird in dem Spaghetti-Code, dafür bekommst du dann deine 100€/h :wink:

Vielleicht übertreibe ich auch gerade ein bisschen und das schießt dir über den Kopf für ein Spiel das vielleicht den Umfang von Pac-Man oder Ping Pong hat, bei mir spricht einfach die Erfahrung in größeren Projekten.

naja ich habe irgendwie Lust mal wirklich ein Projekt durchzuziehen und es am Ende in den Play Store zu stellen.
Deshalb überlege ich von vornherein wo ich alles von Anfang an „sauber“ programmieren sollte, damit ich später
wenigstens noch etwas überblick und kontrolle habe. (klar wird das jetzt keine app mit 10000000 downloads und
100€ werbeeinnahmen täglich, aber ich will halt mal etwas machen wo ich am ende sagen kann hey, es ist „fertig“
geworden, und es haben vllt 100 oder 200 (vielleicht mal 1000) leute heruntergeladen und mir ihre meinung dazu gesagt).

das war ein gedanke den ich auch von vornherein hatte. Warum ich mich dagegen entschieden habe (wahrscheinlich ein ziemlich
schwachsinniges Argument) ist folgendes:

die ganzen input listener implementieren in libgdx InputProcessor. Dieser Multiplexer (der verschiede listener kombiniert) will
input processors haben (logisch). GestureListener ist aber erstaunlicherweise eben kein InputProcessor, sondern ein Interface
in der Klasse GestureDetector - sodass der Multiplexer ein new GestureDetector(irgendeinGestureListener) bekommt.
Ich habe mich seltsam dabei gefühlt, dem Konstruktor von GestureDetector die Instanz einer Klasse zu geben, die noch mehr Inhalt hat
als GestureListener…

Moment gerade beim schreiben nochmal drüber nachgedacht - das ist doch kompletter Unfug was ich da denke oder? Genau dafür sind doch
Interfaces gedacht, es ist doch vollkommen egal was die Klasse noch alles ist… oder nicht? Ich bin verwirrt.

Zum Projekt an sich, wen es interessiert:

Es wird eine Bananenfarm. ^^
Eine Karte aus Sechsecken, auf denen man später Bananenpflanzen pflanzen können wird.
Ich habe ganz viele Ideen wie man das ganz nett und vielleicht unterhaltsam gestalten könnte -
Momentan bin ich aber nicht weiter als die Karte die nur aus Erde besteht ( :smiley: ) und eben der Input -
auswählen von Feldern, bewegen der karte und zoomen.

Und ich versuche eben „von Anfang an“ etwas struktur reinzubekommen.

wie genau könnte ich das bei oben genanntem angehen? Ein paar vorschläge?

Danke euch ^^

im Nachhinein nachbessern funktioniert in der Regel, besonders bei so einfachen Programmen,
jeden Bestandteil kann man beliebig austauschen/ erweitern

man kann es am Anfang unter Aufwand und Sorge und Druck/ schlechten Gefühl etwas falsch zu machen, versuchen richtig zu machen und wird dennoch wahrscheinlich nicht alles treffen und muss später nachbessern,
oder ganz natürlich vorgehen und dann später korrigeren, aber auf ganz natürliche Weise wie es sich im Programm selber vorgibt, entspannt lernend zum korrekten Ergebnis, beim nächsten Programm entsprechende Punkte die besonders wichtig waren gleich von Anfang an berücksichtigt

eine Adapter mit beiden Interfacen ruhig ausprobieren, ja,
das ist dann so ein Tipp von außen den sonst vielleicht mit der Zeit alleine gelernt,
auch nicht schlimm wenn erstmal nicht vorhanden

Nachbessern kann auch ziemlich schnell darauf hinauslaufen, dann man die halbe Codebase umbaut. So ist das nunmal und ueberhaupt nicht schlimm (vor allem bei Projekten, wo keine Deadline im Vordergrund steht). Mir faellt es dann oftmals leichter, nochmal mit einem leeren Projekt zu beginnen und nur Teile des bestehenden Codes rueber zu ziehen. Das mein ich mit von vorn beginnen. Leider im beruflichen Umfeld schwierig. Vor allem wenn es gilt, einen Chef davon zu ueberzeugen :wink:

Was mir beim Thema Spieleprogrammierung (und auch darueber hinaus) einen echten Aha-Effekt gebracht hat, sind die verschiedenen Entity-Ansaetze. Denn bei Spielen haste oft das Problem, dass ein Spielobjekt irgendwie mehreres ist. Also bei dir die Felder z.B. Die kannst du rendern, also brauchen sie irgendwie die Informationen ueber Sprite, Farbe, Groesse, etc. Deine Felder haben aber sicherlich auch irgendwelche spielrelevanten Werte, wie Groesse der Bananenpflanze oder sowas.

Kannst natuerlich all das in eine Klasse hauen - ist aber nich sehr schoen und vor allem nicht flexibel. Vererbungshierarchie geht am Anfang scheinbar - stoesst aber sehr schnell an Probleme.

Also brauchst du eine Art Delegatemechanismus. D.h. du hast Spielobjekte (Entities), die haben nur Referenzen auf Components. Components sind simple Datenklassen (ganz wenige Methode und ueberhaupt keine Logik). Z.B. RenderableComponent{ float x,y; Sprite sprite; } oder MapComponent{ int bananas; }.
Dann hast du Systeme, die ueber einer Menge von Entities arbeiten, die bestimmte Component-Referenzen haben. Z.B. RenderSystem welches eine Liste von Entities bekommt, die alle eine RenderableComponent haben. Die Render Methode selbst geht dann einfach durch diese Liste und malt die Sprites an die richtige Stelle.

Bei LibGDX kannste dafuer Ashley hernehmen, dass macht genau das. Wie das am Ende aussieht, kannst dir auch in meinem Github ansehen :wink: