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

1

22.07.2013, 20:54

[Java] Subklassen in Array ablegen?

Heyho,

in meinem aktuellen Projekt hab ich nun folgendes Problem.

Ich hab eine abstrakte Klasse "Stories" mit den abstrakten Methoden StartStory() und EndStory(), sowie dem Attribut StoryID. Dann hab ich mehrere Unterklassen (Story1, Story2, Story3, etc.), die diese Klassen erben und die abstrakten Methoden mit der jeweiligen Geschichte füllen. Jede Subklasse (Story1, ....) hat eine eigene ID (Story1 hat die StoryID 1, usw.)

Nun zum "Problem". Ich will nun zufällig eine dieser Geschichten erstellen und ausführen (Also die StartStory(); aufrufen). Problem dabei ist, dass das nicht nur 3 Geschichten sind, sondern ~50+. Da wird mir eine Switch/Case zu groß. Nun dachte ich mir, warum probierste es nicht mit einem Array (a lá: public static Story StoryList[] = new Story[57];). Geht ja leider nicht, da die Klassen alle unterschiedlich heißen (Story1, usw...).
-> Ich will diese StartStory() im Endeffekt so, bzw. so ähnlich aufrufen können: StoryList[StoryID].StartStory();

Wie kann ich das nun konkret realisieren?

Auf Switch/Case im Sinne von

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        switch(StoryID)
    {
        case 0:
        {
            Story1 story1 = new Story1();
            story1.StartStory();
            break;
        }
        case 1:
        {
            Story2 story2 = new Story2();
            story2.StartStory();
            break;
        }
    }


hab ich bei 50+ Stories echt keine Lust :I

Sylence

Community-Fossil

Beiträge: 1 663

Beruf: Softwareentwickler

  • Private Nachricht senden

2

22.07.2013, 21:01

Wenn Story1 von Story erbt, dann geht das natürlich

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
class Story
{
...
}

class Story1 extends Story
{
...
}

Story[] Stories = new Story[xyz];
Stories[0] = new Story1();


Wenn du aber 50 Klassen, die alle größtenteils das selbe machen, würde ich mich da noch einmal dran setzen und mir ein besseres Design ausdenken. Zum Beispiel die entscheidenen Daten aus einer Datei laden. Dann kommst du mit einer Story-Klasse aus.

3

22.07.2013, 21:06

Wenn Story1 von Story erbt, dann geht das natürlich

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
class Story
{
...
}

class Story1 extends Story
{
...
}

Story[] Stories = new Story[xyz];
Stories[0] = new Story1();


Wenn du aber 50 Klassen, die alle größtenteils das selbe machen, würde ich mich da noch einmal dran setzen und mir ein besseres Design ausdenken. Zum Beispiel die entscheidenen Daten aus einer Datei laden. Dann kommst du mit einer Story-Klasse aus.
Also muss ich dann ein Array für die abstrakte Klasse anlegen, von der die Unterklassen dann erben?

Ich zeig dir mal den Code, warum ich dann jeweils eine neue Klasse erstellen muss ;)

Die Stories-Klasse:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.util.Scanner;

public abstract class Stories
{   
    public static int StoryList[] = new int[100];
    
    Scanner scanner;
    Player Player;
    
    public String author;
    public int XPReward;
    public int ItemReward;
    public int ZombieCount;
    public int StoryLevel;
    public int StoryID;
    protected int Choose;
    protected int var1;
    protected int var2;
    protected int var3;
    
    public Stories()
    {
        scanner = new Scanner(System.in);
    }

    public abstract void StartStory();
    
    public abstract void EndStory();
    
    public int returnXPReward()
    {
        return XPReward;
    }
    
    public int ItemReward()
    {
        return ItemReward;
    }
    
    public int returnZombieCount()
    {
        return ZombieCount;
    }
    
    public int returnStoryLevel()
    {
        return StoryLevel;
    }
    
    public String returnAuthor()
    {
        return author;
    }
}


Story1 (heißt eig. TestStory, habs nur hier zum leichteren Verständnis "Story1" genannt):

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class TestStory extends Stories
{           
    public TestStory()
    {       
        author = "Zweistein2";
        XPReward = 100;
        ItemReward = 0;
        ZombieCount = 2;
        StoryLevel = 1;
        StoryID = 1;
        
        StartStory();
    }
    
    public void StartStory()
    {
        try
        {
            Thread.sleep(3000);
        } catch ( java.lang.InterruptedException ie) {
            System.out.println(ie);
        }
        System.out.print('\f');
        System.out.println("Diese Geschichte ist von " + author);
        System.out.println("");
        System.out.println("");
        System.out.println("");
        System.out.println("Du läufst schon seit Tagen durch den Wald und dein Wasservorrat neigt sich dem Ende zu. Du hast Hunger und Durst und weißt nicht, wo du hinsollst. Plötzlich lichtet sich der Wald und du siehst eine einzelne Forsthütte... Sie sieht verlassen aus");
        System.out.println("Willst du die Forsthütte betreten?");
        System.out.println("");
        Choose = scanner.nextInt();
        System.out.println("");
        if(Choose == 1)
        {
            System.out.println("Du gehst in die Forsthütte. Plötzlich hörst du ein lautes Grunzen und ein Zombie steht vor dir. Wie du siehst ist es ein Runner!");
            System.out.println("");
            Runner runner = new Runner();   
            System.out.println("Du besiegst den Runner und durchsuchst die Hütte");
            System.out.println("");
            var1 = 1;
            EndStory();
        }else
        if(Choose == 2)
        {
            System.out.println("Du entscheidest dich nicht in die Hütte zu gehen und irrst weiter durch den Wald!");
            System.out.println("");
            var1 = 2;
            EndStory();
        }else
        {
            StartStory();
        }
    }
    
    public void EndStory()
    {
        if(var1 == 1)
        {
            TestWaffe Waffe = new TestWaffe();
            TestWaffe2 Waffe2 = new TestWaffe2();
            TestArmor Armor = new TestArmor();
            System.out.println("Du findest eine " + Waffe.returnWeaponName() + " ,eine " + Waffe2.returnWeaponName() + " und eine " + Armor.returnArmorName());
            Player.Location = "Forsthütte";
            Player.LocationType = 2;
            Game.Menu();
        }else
        if(var1 == 2)
        {
            System.out.println("Du bist immernoch im Wald.");
            Player.Location = "Waldrand";
            Player.LocationType = 3;
            Game.Menu();
        }
    }
}

Sylence

Community-Fossil

Beiträge: 1 663

Beruf: Softwareentwickler

  • Private Nachricht senden

4

22.07.2013, 21:09

Jap. Das würde sich alles mit einer Klasse lösen, die sich ihre Daten aus dann entsprechend 50 Dateien holt.
Dann müsstest du dir nur etwas für die Verzweigungen ausdenken. Aber das ließe sich sicherlich durch eine Baumartige-Struktur lösen.

Das wird nämlich garantiert nicht das einzige Mal bleiben, wo du vor dem Problem stehst ein switch mit 50 cases schreiben zu wollen. Und was machst du, wenn du dir eine 51. Story überlegst? Alle switch noch einmal anfassen?

5

22.07.2013, 21:20

Habs nun mit

Quellcode

1
2
        TestStory TestStory = new TestStory();
        Stories.StoryList[TestStory.StoryID] = TestStory;


und dem Aufruf

Quellcode

1
        Stories.StoryList[1].StartStory();


gelöst. Vielen Dank!

(Befindet sich alles in der Game.java, daher auch immer das Stories. davor ;) )

6

22.07.2013, 21:38

Ein Tipp am Rande dazu, nimm eine XML, schreib da de Texte rein und mach eine ContainerKlasse für den Inhalt.

Hab ich mir auch schon überlegt, allerdings müsste ich das, was in der XML steht ja dann wieder in "Java-Code übersetzen" :I

7

22.07.2013, 23:30

Ja du müstest einen Parser schreiben. Aber der Vorteil ist das du auch schnell etwas modifizieren kanst. und die anzahl der Geschichten ist völlig egal. Vor allem so ein Fall würde ich auf jeden Fall mit einem File oder einem XML lösen.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

8

23.07.2013, 00:07

"In Java Code übersetzen" stimmt so nicht ganz.

Gehen wir das Problem mal etwas systematischer an:
Der Spieler soll mehrere Ausgaben erhalten. Zwischendurch soll er die Möglichkeit haben, aus einer von mehreren Optionen auszuwählen. Die Story, die der Spieler erhalten soll, soll zufällig ausgewählt werden (?).
Es reicht also, wenn du eine einzige Klasse Story schreibst, bei der jede Instanz dieser Klasse eine andere Story darstellt. Dabei muss diese dann irgendwie diese Einzelteile, also normale Ausgaben und Abfragen, speichern können. Auch wäre es ganz gut, wenn die Stories dann aus einer oder mehreren Dateien ausgelesen werden könnten.

Ich denke, dass nachfolgende Punkte der Reihe nach abgearbeitet werden können:
  1. Um das Ganze etwas einfacher anzugehen, wäre die einfachste Möglichkeit erstmal, dafür zu sorgen, dass eine Story einen Text beinhalten kann (noch ohne irgendeine Verzweigung).
  2. Da du, so wie es mir scheint, noch nicht viel mit Dateien gearbeitet hast, wäre es eine gute erste Übung, diese Story in eine einfache Textdatei auszulagern, aus welcher diese ausgelesen wird, damit sie dann "Abgespielt" werden kann.
  3. Nun könnte man sich überlegen, dass der Nutzer nicht unbedingt den ganzen Text mit einem einzigen Mal vorgesetzt bekommen möchte, sondern erst, wenn er soweit mit lesen fertig ist. In normalen Fließtexten hat man neben Kapiteln und Seiten auch eine Unterteilung mit Hilfe von Absätzen, also wäre es doch ganz gut, wenn auch die bisher vermutlich nur als ein einziger String vorliegende Story in mehrere Abschnitte aufgeteilt wird. Diese könnten dann in Form eines Arrays oder einer Liste vorliegen, bei der nach der Ausgabe eines jeden Abschnitts auf eine Benutzereingabe gewartet wird. Um dies aus der Datei erkennen zu können, könnte entweder jeder Zeilenumbruch oder jede Leerzeile (2 Zeilenumbrüche) zur Abgrenzung der Abschnitte verwendet werden.
  4. Wie ich oben schon erwähnt habe, gibt es ja auch Überschriften (die im Falle der Konsole wohl mit mehreren Bindestrichen oder mehreren Gleichheitszeichen unterstrichen sein könnten). Eine noch relativ einfache Art, diese umzusetzen, könnte sein, dass diese immer mit einem Gleichheitszeichen anfangen und dann bei der Ausgabe entsprechend anders formatiert werden. aber bevor du dich um die Umsetzung davon kümmerst: so langsam dürfte auffallen, dass eine Story aus ziemlich vielen Dingen besteht: Absätze, Überschriften, später auch die ersehnten Auswahlmöglichkeiten und ganz später vielleicht auch noch weitere Dinge, wie Bilder, Aufzählungen usw. All das sind "Bestandteile" der Story, also könnte man doch überlegen, ob man dafür nicht auch eine entsprechende Klassenstruktur anlegt: Es könnte die abstrakte Basisklasse bzw. das Interface StoryElement vorhanden sein, wovon StoryParagraph, StoryHeadline und später dann auch StoryChoice abgeleitet sein könnten. Vorerst dürfte dies für Abschnitte und die Überschriften genügen.
  5. Nun also zu dem, was du eigentlich schon die ganze Zeit drin haben wolltest: die Auswahlmöglichkeiten. Diese sind nicht ganz so einfach, die die Absätze bzw. die Überschriften. Vorweg erstmal: eine Auswahlmöglichkeit wird eigentlich immer durch eine Frage oder eine Aufforderung eingeleitet. Dann gibt es ein paar Optionen, unter den man auswählen kann und je nach Option passiert wahrscheinlich auch ein bisschen was anderes. Vorerst ist es wohl erstmal genug, wenn die Speicherung in der Datei so angepasst wird, dass auch die Auswahlmöglichkeiten gespeichert werden sollen. Man könnte nun einfach sagen, dass eine Auswahlmöglichkeit immer mit einem Fragezeichen und die Optionen mit einem Bindestrich oder einer Raute o. ä. eingeleitet werden, aber was passiert dann mit den Absätzen, die bei einer bestimmten Option ausgeführt werden sollen? Sie sollten wohl am ehesten auch nach der Option stehen, zu der sie gehören, nur könnte es da Probleme geben, wenn zwischendurch eine weitere Entscheidung ansteht, da dann die noch folgenden Optionen zu dieser Entscheidung gehören. Es wäre auch möglich, dass die Elemente unterhalb einer Option eingerückt werden, aber da muss man dann beim Bearbeiten aufpassen, dass die Einrückung auch immer die richtige ist. Statt sich ein so eigenes Format weiterhin auszudenken, könnte man auch den Rat beherzigen, der hier bereits gegeben wurde: man könnte doch eine XML-Datei anlegen, die die Story speichert. exemplarisch könnte das ja dann so aussehen:

    Quellcode

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    <story>
      <p>Es ist ein wunderhübscher Tag</p>
      <choice title="Finden Sie nicht auch?">
        <option title="Ja, und ob!">
          <p>Es ist immer wieder schön, gleichgesinnte zu treffen</p>
        </option>
        <option title="Nein, wie kommen Sie denn auf diese Idee!?">
          <p>Für diese Frechheit wirst du in der Hölle schmoren!!!</p>
          <p person="well-that-escalated-quickly-guy">Well, that escalated quickly!</p>
        </option>
      </choice>
      <p>Auf wiedersehen</p>
    </story>
    Es gibt verschiedene Möglichkeiten, eine XML-Datei in Java zu parsen, allerdings dürften sich diese im Wesentlichen in 2 Gruppen einteilen lassen: solche, bei denen der Dateiinhalt durchgegangen wird und man jedes Mal Bescheid bekommt, wenn ein öffnender Tag auftauchte, ein schließender Tag auftauchte usw. und solche, bei denen Java-Objekte anhand der XML-Struktur aufgebaut werden. Letzteres würde ich als einfacher ansehen. Eine Bibliothek dafür wäre JDOM. Damit das Ganze aber, unabhängig davon, ob XML oder eigenes Format, ein wenig einfacher wird, sollte man den Punkt, dass eine Option wiederum Storyelemente beinhaltet außen vor lassen. Es würde reichen, mit entsprechenden Klassen für die Auswahl und die Optionen erstmal nur dafür zu sorgen, dass die Frage dem Nutzer gestellt wird, er die Optionen eingeblendet bekommt und sich für eine entscheiden kann. (Nach Möglichkeit wird die Auswahl auch ausgegeben.)
  6. Danach kannst du dich dann auch daran machen, dafür zu sorgen, dass abhängig von der ausgewählten Option etwas passiert. Um genauer zu sein: das, was sich innerhalb der Option befindet, soll dann ausgeführt werden, wenn die Option ausgewählt wird. Idealerweise ist es dann schon möglich, eine Auswahl in eine Auswahl zu legen.
Eigentlich dürfte damit bereits das erledigt sein, was du eigentlich haben wolltest, aber das heißt noch lange nicht, dass nichts mehr gemacht werden kann: Es könnte beispielsweise dafür gesorgt werden, dass an einer bestimmten Stelle der Inhalt einer anderen Datei verwendet wird, dass es einen gewissen Zustand gibt, der sich im Laufe der Geschichte verändert (beispielsweise könnte irgendwie gespeichert werden, wie man sich an einer bestimmten Stelle entschieden hat oder wie "gut" bzw. "schlecht" die Antworten waren. Abgesehen davon gibt es bestimmt noch viele weitere Dinge, aber sobald die Liste erstmal umgesetzt ist, fliegen die dir bestimmt wie von selbst zu. ;)

Bitte versteh mich nicht falsch: ich will nicht behaupten, dass das der einzig wahre Weg ist, wie du vorgehen kannst, zumal man sich wegen der Benennungen streiten kann, allerdings würde ich mir denken, dass es so funktionieren könnte.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

9

23.07.2013, 20:40

Du kannst auch einfach alle Klassen in einen Array packen:

Quellcode

1
2
3
4
5
6
7
Class<? extends Story>[] stories=new Class[]{ TestStory.class, Story1.class, Story2.class };
// oder Namen der Klassen aus Datei lesen und mit Class.forName holen, es empfiehlt sich auch eher List bzw. ArrayList zu nehmen (statt array)

Random random=new Random();
Story story=stories[random.nextInt(stories.length)].newInstance();
story.startStory();
// ...

Ich würde an Deiner Stelle eher eine vernünftige Java Api für Deine Stories entwickeln, anstatt da ne gruselige XML-DSL einzubauen. Wenn Dir Java zu schwergewichtig ist, dann nimm dafür am Besten Groovy oder den eingebauten javascript-Dialekt (über http://docs.oracle.com/javase/6/docs/api…ge-summary.html einbinden). PS: Wenn Du dennoch XML einsetzen willst, dann nimm am Besten JAXB, StAX oder SAX das ist Teil der Standardbibliothek.

Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von »Chromanoid« (23.07.2013, 20:58)


Werbeanzeige