Ansätze und Hilfen zur Lösung von komplexen Probleme

[nocontentad]

Ansätze und Hilfen zur Lösung von komplexen Probleme


zum Anfang


Einleitung

Der Trigger it out hat durchblicken lassen, dass siche viele Mapper gar nicht erst an komplexere Probleme heran trauen.

Ein komplexes (Trigger) Problem scheint oftmals auf den ersten Blick überwältigend, aber wenn man ein paar Dinge beachtet, kann man fast jede Problemstellung mit mehr oder weniger Aufwand meistern.
In diesem kleinen Tutorial sollen ein paar Hilfen zur Lösung solcher Probleme vorgestelllt werden.
Ich werde als begleitendes Beispiel den dritten Trigger it out und dessen Musterlösung zu Hilfe ziehen. Doch auch wenn sich dieses Tutorial im speziellen an dem TiO orientiert, kann es doch für jede Problemstellung benutzt werden, es soll dies nur zur besseren Veranschaulichung dienen. Ebenso kann die Vorgangsweise nicht nur auf Triggerprobleme umgelegt werden, sondern mehr oder weniger auf alles.

Um die Beispiele zu verstehen, sollte man sich die Aufgabenstellung einmal durchlesen und das Template anschaun.


zum Anfang


Komplexe Probleme

Komplexe Probleme sind manchmal so überwältigend, dass man am liebsten gar nicht erst anfangen möchte.
Aber schaut man sich die Problemstellung genauer an, so erkennt man bei eigentlich allen komplexen Problemen, dass sie aus mehreren kleinen, nicht komplexen Teilproblemen bestehen.
Konzentiert man sich auf diese und erarbeitet sich die Lösungen Stück für Stück, ist das gesamte Problem schneller bearbeitet, als man denkt.
Anhand eines Triggerproblems betrachtet kann dies z.B. ein Save/Load System sein, ein System zum Helden wiederbeleben, etwas das eben in seiner Gesamtheit sehr umfangreich ist. Allerdings lassen sich all diese Aufgaben in kleinere zerlegen, wie das im konkreten aussehen kann, wird anschließend anhand des Trigger it Out erklärt.
Auch erweist es sich oft auch als praktisch bei Trigger erstmal auf einer eigenen Testmap ein wenig herumzuexperimentieren. Dadurch ist es möglich, dass man auf neue Lösungen und Ideen kommt, und auch ist es bei einem größeren Projekt besser, wenn man ersteinmal einen „Prototypen“ auf einer eigenen Map entwickelt, da so die Übersicht höher ist.

Die Aufgabe des dritten Trigger it out bestand darin, ein Wettersystem zu basteln, das auch noch verschiedenen Ansprüchen (Abhängigkeiten) genügen sollte.

Als ich mich an die Musterlösung gesetzt habe, waren mir die Abhängigkeiten und das System, dass das Wetter verändern soll, erstmal absolut egal.
Angefangen habe ich mit der Erzeugung von Wetter auf den einzelnen Inseln.
Gebiete über allen Inseln wurden erstellt und in einem Array gespeichert.
Ebenso habe ich ein Array vom Typ Wettereffekt erstellt, in dem die aktuellen Wettereffekte der Inseln gespeichert werden soll.
Ein erster Test-Trigger hat dann das Wetter von Insel 7 nach 5 Sekunden auf Mondstrahlen gestellt und nach 10 Sekunden auf leichten Regen.
Das ist schon die Lösung des ersten Teilproblems. Man löscht das aktuelle Wetter, erstellt ein neues und speichert dieses ab.

Man kann eigentlich alle Probleme in kleinere Probleme aufspalten und die kleinen Teilprobleme wiederum in kleinere Teile bis man das gesamte System auf fast triviale Probleme reduziert hat. Ich finde es angenehm bei den aller kleinsten Problemen anzufangen. Probiert erstmal ein wenig aus, was geht und was nicht; dann wisst ihr, worauf ihr aufbauen könnt.


zum Anfang


Eine Idee haben

Nachdem man die Werkzeuge, die einem zur Verfügung stehen, erkundet hat, kann man sich darüber Gedanken machen, was man im Endeffekt brauchen könnte.

Und zwar geht es um Strukturen, die das gesamte System, aber auch einzelne Teilprobleme angeht.
Es ist niemals gesagt, dass man alle Dinge, die man sich hier überlegt, im Endeffekt wirklich braucht, aber man kann sich einen roten Faden basteln, an dem man die Teillösungen herleiten wird.
Sollte es sich um ein größeres Vorhaben handeln, an dem man gerade arbeitet ist es wichtig, das Problem mit den Mitteln zu lösen, die nicht unbedingt am einfachsten zu implementieren sind, sondern das höchste Maß an Erweiterbarkeit bietet. Denn oftmals ist es erforderlich, dass man nachträglich noch etwas hinzufügt und verbessert, weil man entweder neue Features entwickelt hat oder etwas vergessen hat. Und dann ist nichts ärgerlicher, als eine Lösung gewählt zu haben, welche dies nicht wirklich zulässt.

TIO:
Ich habe als erstes bemerkt, dass man wohl viel speichern muss. Wettereffekte, Wetter in Zahlen, Wahrscheinlichkeiten,…
Als gute Speicherstruktur habe ich das 2D-Array empfunden, für welches ich als erstes Funktionen zum Umrechnen des Index‘ bastelte.

Solche Hilfsfunktionen sind ebenso Teillösungen und können einen die Arbeit während der gesamten weiteren Map erleichtern; sie können sich aber später auch als unnütz erweisen, da man sie nur ein einziges mal braucht.
Nach einer Weile bekommt man ein Gefühl dafür, was einem helfen wird und was nicht.

Um das Wetter möglichst einfach wechseln zu können, habe ich eine Funktion gebaut, die auf Insel x,y das Wetter auf i setzt (i = 1 bis 5).
Diese Funktion benutze schon die Teillösungen aus der vorhergehenden Arbeit und war somit leicht zu implementieren.
Und darauf wiederum kann später ein System aufsetzen, das das Wetter komplett kontrollieren wird.

Ich habe derweil noch weitere Variablen eingeführt. So z.B. ein Integer Array, welches die Zahl des aktuellen Wetters speichern soll.
Es ist aber absolut nicht ratsam auf gut Glück Variablen zu erstellen! Dabei kann eigentlich nur Chaos rauskommen.
Ich erstelle Variablen, wenn ich sie gerade brauche. Überlegt auch davor noch, ob ihr sie wirklich braucht und ob ihr eine spezielle Struktur benutzen solltet (Array, GameCache?).


zum Anfang


Vorausschauend arbeiten

Während man sein Teillösungen zusammenbastelt, sollte man schon im Auge behalten, was man später erreichen will.

So kann man z.B. beim Ändern eines Wetters bemerken, dass das Wetter danach mindestens 4 Sekunden erhalten bleiben soll.
Da das Wetter nur von der einen Funktion verändert werden soll, kann man dort also die Sperre einbauen, in dem man z.B. ein Array mit Flags definiert, die die Information enthalten, ob die Insel zur Zeit verändert werden darf.
Damit muss dann aber feststehen, dass keine ander Funktion, das Wetter einer Insel ändern kann.
Ebenso sollte man dabei sehen, dass das Wait über 4 Sekunden die Funktion nicht stoppen darf. Das Wetter kann auf mehreren verschiedenen Inseln beliebig oft gesetzt werden. Man braucht also Paralellität und damit einen extra Trigger.

Vorausschaundes Triggern ist nicht immer leicht und schon gar nicht immer möglich, da man manchmal noch gar nicht alle Probleme kennt, auf die man treffen wird.
Auch kann es passieren, dass man zu viel vorausschaut und dadurch ein Teilproblem unnötig kompliziert macht, da man zu viel beachten will.
Auch hier gilt, dass die Erfahrung einem da mit der Zeit besser beurteilen lässt, worauf man achten sollte.


zum Anfang


Spezielle Probleme behandeln

Während man das komplexe Problem Stück für Stück löst, werden einem immer wieder einzelne Teilprobleme auffallen, die besondere Aufmerksamkeit erfodern.

Als Beispiel, will ich Wetter Nummer 5 – das Gewitter – angeben. Es reicht hier nicht, einen Wettereffekt zu erstellen, sondern es müssen immer wieder Blitz und Donner erstellt werden.
Das Wetter fällt damit aus dem Rahmen und doch kann man versuchen, es auf dem bisherigen System aufbauen zu lassen.
Ich habe einen weiteren Trigger, der periodisch läuft, erstellt, der alle Inseln sucht, die gerade Gewitter haben und dort Blitze und Donner erstellt.
Ein neuer Trigger-Start für jeden Blitz ist dabei wichtig, da das Wettersystem und das Gewitter auf anderen Inseln ja paralell dazu laufen soll.


zum Anfang


Tests zwischendurch

Wann immer ihr einen Teil des Systems fertig habt, solltet ihr Tests durchführen und feststellen, ob alles so funktioniert, wie es soll.
Dabei ist es nicht immer direkt möglich, einen Test zu machen, weil das System z.B. so noch nicht laufen kann oder weil man, wenn alles funktioniert, davon nichts mitbekommen würde.
Testfunktionen und Testausgaben sollten das Problem beheben. Schaut nur nach, ob das System im jetzigen Zustant korrekt arbeitet. Ignoriert Fehler, die noch auftauchen müssen, da sie noch nicht behandelt wurden (das soll dann ein anderes Teil des Systems erledigen). Überlegt euch, was für Werte/Zustände euer derzeitiges System durchlaufen muss und vergleicht das mit euren Testausgaben.
Ich finde solche Tests sehr langweilig, aber sie helfen wirklich ungemein, denn so kann man Fehler wirklich früh erkennen. Je später man einen Fehler erkennt, desto schwerer ist es, dessen Ursache zu finden und ihn zu beheben.

So hat mein Zufallssystem erstmal nur darauf geachtet, dass das neue Wetter nur um 1 weiter sprang. Die Nachbarn wurden da nochgar nicht beachtet.

Erst als der Teil des Zufallssystems fertig war, der die Nachbarn geprüft hat, habe ich auch diese Korrektheit überprüft.


zum Anfang


Das Gesamtproblem

Während ihr die einzelnen Lösungsstücke an einander fügt, dürft ihr nicht aus den Augen verlieren, welches Teil was macht.
Fragt euch, ob wirklich alle Teilprobleme behandelt wurden oder wird ein Teil vielleicht doppelt bearbeitet?
Ich hatte ausversehen das Wetter anfangs je um zwei Stufen erhöht, da zwei verschiedene Teile meines Systems das übernommen haben.
Solche Fehler sind nur schwer zu finden. Man erwischt sie am besten mit sehr sehr vielen Ausgaben. In meinem Fall habe ich mir das Wetter in jeder Zeile meiner Funktionen ausgeben lassen und so gesehen, wann das Wetter ein zweites mal verändert wurde.

Prüft jedes Teil unabhängig voneinander; danach könnt ihr das Teil benutzen mit dem Wissen, dass es bugfrei ist. Ihr müsst also die Korrektheit der Teillösungen nicht mehr prüfen, wenn ihr das gesamte System baut.

Aber ihr müsst wissen, was genau das Teilsystem macht denn nur so könnt ihr es richtig benutzen.

Auch wenn ihr mehrere Teillösungen zu einem größeren System zusammengefasst habt – und dieses System auf Bugfreiheit geprüft habt, denn die Verbindung zwischen den Teillösungen müssen noch gecheckt werden – so könnt ihr dieses System dann in der Gesamtlösung in beliebiger Form benutzen.
Hier möchte ich ein letztes mal auf die Musterlösung des Trigger it out zu sprechen kommen.
Ich habe eine Funktion gebaut, die das Wetter der Nachbarn prüft und es gegebenfalls korregiert (und dann die Nachbarn des Nachbarn ggf noch überprüft). Dies ist aber nicht immer möglich, da eine Nachbarinsel in den letzten 4 Sekunden bereits geändert worden sein kann.
Ich musste also ziemlich spät feststellen, dass man nicht immer jede Insel in jede Richtung ändern kann.
Ich habe also versucht, den gesamten Vorgang abzubrechen, falls eine solche Situation eintritt.
Aber statt eine neue Struktur zu bauen, die alle Änderungen speichert, um sie rückgängig machen zu können, habe ich auf mein vorhandenes System zurück gegriffen.
Ich habe das Array, das das Wetter beinhaltet einfach kopiert und in der Kopie mit dem normalen System, von dem ich wusste, dass es bugfrei ist, gearbeitet.

Hinterher wurde dann das Wetter von der Kopie ins Orginal übernommen und erst dann wurde das Wetter wirklich geändert (also Effekte zerstört, wieder erzeugt, Flags gesetzt usw.). Bei einem Abbruch wurde die Kopie einfach verworfen.


zum Anfang


Schlusswort

Dieses Tutorial erklärte anhand des Trigger it Out die Lösung komplexer Systeme. Es ist klar, dass es schwer ist so etwas
allgemein gültig zu formulieren, und das Beispiel werdet ihr vermutlich praktisch nie verwenden bzw. benötigen können in euren Projekten, doch das ist auch nicht so wichtig.

Wichtig ist es zu verstehen wie man an die Sache am besten herangeht und dazu ist am besten ein Beispiel geeignet, da dies das Verständnis für die Vorgangsweise steigert.
Wir hoffen, dass nun wieder ein bisschen weniger Projekte scheitern bzw. mehr angepackt werden, da ihr nicht mehr so oft an komplexen Problemen scheitert ;).

zum Anfang

  • 28.07.2008 um 18:54
Cinematic Sequenz Video-Tutorial Map Design Tutorial