# Vorgehensweise: Gegenstände in Text-basiertem Spiel



## beginner99 (12. Jan 2010)

Hallo zusammen,

zu Übungszwecken erweitere und verbessere ich ein ganz simples vorgegebens text-basiertes Spiel. Im spiel gibt es Räume, Gegenstände und Spielfiguren (=Gute und Böse).

Die Bösen kann man a) erschiessen oder b) verhaften. Auf jedenfall erhält man dann alle Gegenstände dieser Spielfigur. Es geht darum einen Fall/Rätsel zu lösen mit Hilfe der Gegenstände. Zb. kann ein Gegenstand ein Notebook sein. "Durchsucht" man es, kommt man einen Schritt weiter, zb. neue Räume werden freigeschaltet oder Abkürzungen geöffnet. 

Wie gehe ich da vor? Momentan gibts nur die Klasse "Item". Ein Item hat ein Name, Beschreibung, Gewicht (der Spieler kann nur ein bestimmtes Gewicht tragen, ein gegenstand könnte zb dieses Limit erhöhen). Ich könnte jetzt für jeden Gegenstand eine eigene Klasse machen und jede Klasse dieser Klassen hat eine Methode, die dann das gewünschte Ereignis auslöst. Aber dann gibt es schnell eine unüberschabaure Anzahl an klassen plus wird es von jeder maximal 1 Instanz geben. 

Besser wäre es jeder "Item" Instanz noch eine Eigenschaft zu geben, die das Ereignis ausführt nur wie?


----------



## Marco13 (12. Jan 2010)

Schau mal im Web nach "Strategy Pattern" oder "Command Pattern". Ganz grob (!!!) könnte das sowas sein wie

```
interface GameAction
{
    public void execute();
}

class Item
{
    private GameAction gameAction;

    public Item(GameAction gameAction)
    {
        this.gameAction = gameAction;
    }


    public void executeGameAction()
    {
        gameAction.execute();
    }
}
```

Anwenden könnte man das z.B. so

```
Item key = new Item(new GameAction()
{
    public void execute()
    {
        openSomeDoor();
    }
});

key.executeGameAction(); // Macht die Tür auf


Item poison = new Item(new GameAction()
{
    public void execute()
    {
        killPlayer();
    }
});

poison.executeGameAction(); // Killt den Spieler ;-)
```

(Wirklich nur ganz grob angedeutet...!)


----------



## beginner99 (12. Jan 2010)

Danke! Mache mal ne Suche nach den beiden Begriffen. Melde mich dan ev. nochmals bei Problemen.


----------



## beginner99 (13. Jan 2010)

Ich habe jetzt folgendes Problem. Bleiben wir beim Notebook beispiel. Die gewünschte action wäre jetzt zb. das ein neuer Raum zugänglich wird. Die Action soll aber erst dann ausgeführt werden, wenn der Spieler sich in einem bestimmten Raum befindet, nennen wir den zb Hauptquartier (da hat es IT spezialisten, die das password knacken können, als grund wieso man dahingehen muss zb).

Dazu bräuchte ich ja eine Art Listener? 

Momentan wird schon bei jedem Raumwechsel gewisse Dinge überprüft, vorallem ob im neuen Raum eine böse Figur ist. Da könnte ich über alle Gegenstände Iterieren und auf eine noch hinzuzufügende Eigenschaft prüfen (if gameActionRoom == currentRoom  -> execute()).

Wäre das sinnvoll?

Das hat natürlich dann auch gewisse Limitierungen. Zb könnte ich mir auch eine GameAction vorstellen, die 1 minute nach dem Fund des Items auftritt. 

Dann noch eine weitere Frage bezüglich Design und Strategy Pattern. Nehmen wir an ich hab jetzt 20 Items mit einer GameAction. Ergo brauche ich jetzt 20 methoden, die in execute() aufgerufen werden. Die Frage ist jetzt, wo man den ganzen Code hinschreibt. Momentan habe ich eine Klasse Main, wo ich in der main methode alles aufsetze:


```
public static void main(String[] args)
    {          
        
        map = new GameMap(createRooms());
        
        addItemsToRooms();
        
        addCharactersToRooms();
        
        Player player = new Player("Me", 80, 50); 
        
        player.setCurrentOwner(map.getRoomByName("ctu"));
        
        Parser parser = new Parser();
        
        Game game = new Game(player, map, parser);
        game.play();        
    }
```

Hier müsste ich jetzt also noch die 20 oder später ev. 100 Methoden für die Items hinzufügen?
(ok, bei 20 geht es noch, aber mehr wird dann langsam unübersichtlich)


----------



## Marco13 (13. Jan 2010)

Solche Fragen tendieren dazu, schnell zu allgemeinen Designfragen zu werden, die nur schwer zu beantworten sind, wenn man das endgültige Ziel und den vorgesehenen Rahmen für das Spiel nicht kennt.

_Die Action soll aber erst dann ausgeführt werden, wenn der Spieler sich in einem bestimmten Raum befindet,..._

Wenn man das Notebook in einem (falschen) Raum benutzt, und dann ins Hauptquertier geht, soll die Aktion dann ausgeführt werden? Oder nur, wenn man IM Hauptquartier ist, WÄHREND man es benutzt? Man könnte jetzt sagen: Dann übergibt man der executeGameAction()-Methode (und ggf. der GameAction#execute-Methode) halt den aktuellen Raum...

```
Item laptop = new Item(new GameAction()
{
    public void execute(Room currentRoom)
    {
        if (currentRoom.equals(headquarter)) doSomething();
        else printErrorMessage();
    }
});
```
aber das wäre so natürlich unüberlegt. Du solltest dir überlegen, wie man "sowas" allgemein beschreiben kann. Nicht jede Aktion wird einen "Room" benötigen. Andere Aktionen brauchen, um ausgeführt werden zu können, vielleicht einen "Opponent" oder ein anderes "Item". DA könnte man sagen: Joa, dann übergibt man halt irgendein allgemeines "Object argument", und die execute-Methode schaut dann, ob sie mit dem agrument was anfangen kann - das wäre aber auch evtl. zu unflexibel. Sehr allgemein wäre, wenn es sowas gäbe wie ein

```
interface Condition
{
    boolean evaluate();
}
```
das man dann auch den Items übergeben könnte, und das ausgewertet werden kann - im Falle des Laptops dann halt mit sowas wie

```
laptop.setCondition(new Condition()
{
    public boolean evaluate()
    {
        return ownerOfItem.getCurrentRoom().equals(headquarter));
    }
});

// und dann
laptop.setAction(new GameAction()
{
    public void execute(Room currentRoom)
    {
        if (condition.evaluate()) doSomething();
    }
});
```
Man könnte das auch mit der GameAction kombinieren, im Sinne von

```
interface GameAction
{
    void execute();
    boolean canBeExecuted();
}
```
oder, oder, oder... Trotzdem könnte es sein, dass manche Actions Parameter brauchen oder Ergebnisse zurückgeben müssen ... das musst du dir alles überlegen.

(Hat eigentlich irgendwann mal irgendjemand behauptet, dass Programmieren einfach wäre? )



_. Zb könnte ich mir auch eine GameAction vorstellen, die 1 minute nach dem Fund des Items auftritt._

Auch DAfür müßtes du dir ein allgemeines Konzept überlegen. (Abgesehen davon, um welche Zeit es hier geht...). Es könnte ja auch sein, dass eine GameAction 1 Minute nach dem "execute" eine Wirkung zeigt (Handgranate...), oder erst 1 Minute nach dem Fund ausgeführt werden kann, oder nur mindestens 2 Minuten und Höchstens 3 Minuten nach dem Fund ausgeführt werden kann wenn VORHER eine bestimmte andere Action ausgeführt wurde, und dann mindestens 21 und höchstens 42 Sekunden nach der Auführung eine Wirkung zeigt, wenn in diesem Zeitraum 3 andere Actions in einer bestimmten Reihenfolge ausgeführt werden.... 

Das führt schon indirekt zum nächsten Punkt...

_Die Frage ist jetzt, wo man den ganzen Code hinschreibt. Momentan habe ich eine Klasse Main, wo ich in der main methode alles aufsetze:_

In der main sollte normalerweise nicht viel stehen. Im Falle eines Spiels sollte in der main im Idealfall (abzüglich eventuell zu parsender Startparameter) nur noch eine Zeile stehen, nämlich 
[c]new Game().start();[/c]
oder so...

Die Frage, wo man dieses (möglicherweise sehr komplexe) "Gesamtverhalten" des Spiels konfigurieren kann, ist berechtigt. Um die beantworten zu können, müßte man aber wissen, wie viele und welche Actions es geben kann, wie die Abhängigkeiten dieser Actions zu anderen Klassen ist usw. Man könnte ja z.B. in Erwägung ziehen, das ganze irgendwann durch eine einfache Beschreibungsdatei zusammenzufassen:

```
ITEM 
Name = Granade
Weight = 450
InitialLocation = Headquarter
Action = Explode
Delay = 10
ActivationCondition = ...
ExecutionCondition = ...
...
```
aber sowas kann natürlich beliebig(!) aufwändig sein...


----------



## beginner99 (13. Jan 2010)

danke für die ausführliche Antwort. Mein Problem ist halt immer, dass ich etwas irgendwie hinkriege, aber ob es sinnvoll ist oder "ein guter Weg" kann ich teilweise nicht sagen oder weiss, dass der Weg schlecht ist.

Zur Main Methode. Ich setze da halt alle Räume und Gegenstände auf. Das ist so natürlich nicht ideal. besser wäre es wohl, dass aus einem file (text, xml etc) zu lesen. Oder anders gesagt, die paar Räume, die es bis jetzt gibt, sind hardcoded inkl figuren und gegenstände.

Das ganze gibt mir auf jedenfall ein gutes Bild wie schnell scheinbar einfache Sachen (text-basiertes Spiel) sehr komplex werden können.

Muss jetzt mal überlegen, wo ich beginnen soll. Lauter Baustellen.


----------



## Marco13 (14. Jan 2010)

Ja, für erste Tests codet man sowas natürlich hart - während man noch überlegt, welche Struktur da am günstigsten ist. (Ein File-Format 1000 mal ändern zu müssen macht nämlich GAR keinen Spaß). 

Und... wenn man nachher feststellt, OB das, was man gemacht hat, "ein guter Weg" war, ist das doch schon was. (Man muss nicht feststellen, DASS es ein guter Weg war - sondern nur OB - wenn es nämlich KEIN guter Weg war, und man merkt das, weiß man, was man in Zukunft besser machen kann). 

_Muss jetzt mal überlegen, wo ich beginnen soll. _

In diesem Sinne vielleicht: Die Grenzen abstecken. Soll es Items und Actions geben, die so ein komplexes Verhalten haben? Wenn man Zeit und "Conditions" einfach wegläßt, wird es schon deutlich einfacher. Was willst du haben, wenn du "fertig" bist?


----------



## beginner99 (14. Jan 2010)

Marco13 hat gesagt.:


> Was willst du haben, wenn du "fertig" bist?



Bessere Java Kenntnisse ist mal das wichtigste. Ein funktionierendes Spiel wäre abe rauch ganz ok.


----------



## Landei (14. Jan 2010)

Ich kann nur dazu raten, von Anfang an auf eine klare Struktur zu achten und zu vermeiden, "zu clever" sein zu wollen. Jedes Objekt soll möglichst "selbstständig" sein. Es sollte etwa ein Tür, eine Kiste oder ein Bett selbst wissen, wie es zu reagieren hat. Und jedes Objekt und jede Methode soll nur eine "Aufgabe" haben (Seperation of Concerns). Ansonsten die üblichen OO-Tips: Interfaces verwenden, Vererbung nicht überstrapazieren, Komposition bevorzugen...


----------

