Storyworld – ein Java-Projekt für die 10. Klasse

By | 21.3.2017

Paket für ILTB 2017 Download, Zip, ~20 MB (enthält Präsentation, Anleitung, Code)

Storyworld habe ich als Projekt für 10. Klassen am Gymnasium in Bayern im Rahmen des Fachs Informatik durchgeführt. Es geht dabei darum, dass Schülerinnen und Schüler für eine gemeinsame Fantasy-Spielwelt Geschichten schreiben, die in Form von Zustandsautomaten umgesetzt werden und die miteinander in Zusammenhang stehen können.

Vorgeschichte und Hintergrund

Eine beispielhafte Spielwelt

So sieht zum Beispiel eine Welt aus, die aus 6 Location-Objekten und 5 Connections besteht. Es ist Absicht, dass La Isla Bonita nicht so einfach zu erreichen ist.

Landkarte der Spielwelt

Im Spiel kann man sich zwischen den Orten, die durch Kanten verbunden sind, bewegen. Der eigentliche Clou: An manchen Orten werden zusätzlich zu den Ortsbeschreibungen Geschichten oder Teile von Geschichten präsentiert.

Die Geschichte vom Froschkönig, klassisch und linear

Klassische Geschichten sind linear in dem Sinn, dass sie Absatz für Absatz gelesen werden und man auch nicht mehr zurückspringt. Das gilt auch dann, wenn nicht chronologisch erzählt wird, sondern mit Sprüngen, wie etwa in der Odyssee. Deutschlehrer verlangen manchmal von ihren Schülern, die Geschichte in einzelne Stationen aufzuteilen. Das sieht dann, etwas formalisiert, so aus:

Zustandsübergangsdiagramm Froschkönig klassisch

Die Geschichte vom Froschkönig, als nichtlineares Spiel umgestaltet

Spiele sind of nicht-linear in dem Sinn, dass man zu vorherigen Zuständen zurück kann und bestimmte Phasen mehrfach durchläuft. Auch gibt es möglicherweise unterschiedliche Wege zwischen den Stationen. In der Informatik kann man so etwas als Zustandsautomat modellieren. Das sieht dann so aus, mit auslösenden Aktionen bei den Übergängen, ausgelösten Aktionen nach dem Schrägstrich, und zusätzlichen Bedingungen für den Zustandsübergang in eckigen Klammern.

Zustandsübergangsdiagramm Froschkönig nichtlinear

Die zentralen Methoden der Geschichtenklassen

Bewegt man sich durch die Welt, erscheinen Reiter mit den zur Verfügung stehenden Geschichten. Wählt man eine davon, sieht man zum Beispiel dieses schlichte Beispiel – der Spieler befindet sich gerade im Hafen, könnte nach Innismund wechseln, und hat gerade die Geschichte „Das öde Haus“ ausgewählt:
Benutzeroberfläche Spiel

Zu diesem Zweck hat jede Geschichtenklasse zwei Methoden: createReport und receiveMessage. Die erste Methode wird aufgerufen, damit die Geschichte einen Bericht oder eine Beschreibung abgeben kann, möglichweise mit einer oder mehreren Optionen, die der Spieler danach wählen kann. Die zweite Methode gibt an, was geschieht, wenn der Spieler die jeweilige Option wählt, sie entspricht mehr oder weniger der klassischen Zustandsübergangsmethode:

class LeeresHaus extends SingleLocationStory {
 
 public Report createReport() { 
  Report r = new Report("Ein leeres Haus.");
  r.addOption("Du untersuchst es", "anschauen");
  return r;
 }
 
 public void receiveMessage(String s) {
  if (s.equals("anschauen")) {
   sendMessage("Niemand zu Hause");
   if(get("Neugier")<10)increase("Neugier", 1);
  }
 }
 
 public String getName() { return "Das öde Haus"; }
 public String startStoryAt() { return "Hafen"; }
 public String getImageName() { return "resources/haus.png"; }
 
}

Die Methode getName gibt den Namen der Geschichte zurück, die für diese Unterklasse von Geschichten notwendige Methode startStoryAt gibt den Ort zurück, an dem die Geschichte spielt, die optionale Methode getImageName gibt den Ort und Namen eines passenden Bildes an.

Zu den typischen ausgelösten Aktionen bei der Zustandsübergangsmethode gehören set, increase, decrease von beliebigen Spielwelt- oder Spielerattributen. (Wenn ein Attribut dieses Namens noch nicht existiert, wird es angelegt.) Den Wert dieses geschichtenübergreifenden Attributs kann man auch mit get abfragen, um Anweisungen von bestimmten Bedingungen abhängig zu machen.
Außerdem kann man mit sendMessage Text ausgeben. Daneben gibt es viele Möglichkeiten, den Zustand der Spielwelt zu ändern – man kann diese Geschichte an einen anderen ort platzieren, den Spieler auch, neue Orte oder Verbinungen zwischen Orten erschaffen. Häufig wird man auch ein Zustandsattribut in die eigene Geschichtenklasse einbauen und dessen Wert ändern. Von dem Wert dieses eigenen Zustandsattributs hängt dann auch der zurückgegebene Report ab, und die damit verbundenen Optionen – und schon hat man einen einfachen Zustandsautomaten.

— Wer die Trennung oder Doppelung der zwei Methoden createReport und receiveMessage unübersichtlich findet, für den gibt es Alternativen: In einem Anhang unten habe ich die verschiedene anderen Geschichtenklassen gegenübergestellt. Sie sind alle Unterklassen von AbstractStory, und logisch alle äquivalent, bieten eben nur unterschiedliche Implementierungen.

ActionStory

Implementiert eine Geschichtenklasse das Interface ActionStory, so muss es eine Methode void act() geben. Diese Methode wird jedesmal aufgerufen, wenn der Spieler sich entscheidet, zu einem anderen Ort zu gehen. So kann man zum Beispiel einen Wanderer erschaffen, der sich bei jeder Bewegung des Spielers auch bewegt, zum Beispiel zu einem zufälligen Nachbarort.

Ausgelöste Aktionen

Typisch sind:

  • set(String, int), increase(String, int), decrease(String, int)
  • movePlayerTo(String)
  • sendMessage(String)
  • moveStoryTo(String) – das geht nur bei Objekten der Klasse SingleLocationStory und deren Unterklassen, da nur diese Geschichten an genau einem Ort spielen
  • addLocation(Location), removeLocation(Location)
  • addConnection(Location, Location, int), removeConnection(Location, Location)
  • enter() und exit() – optional: nachdem man mit enter() eine Geschichte betreten hat, kann man nicht mehr zu einer anderen Geschichte oder einem anderen Ort wechseln, bis man zu einem Ende gekommen ist, das einen mit exit() wieder aus der Geschichte entlässt (es gibt auch einen automaticEntryMode)
  • introduceStory(AbstractStory)
  • deleteStory(AbstractStory)

Bedingungen/Informationen über die Welt

  • int get(String)
  • boolean playerIsIn(String)
  • String getPlayerLocationName()
  • String getRandomNeighbourName(String)
  • Location [] getAllNeighbours(String)
  • Location getRandomLocation()
  • int getTimeSinceLastReport()

Mögliche Geschichten

  • Ein Wanderer, der zufällig herumzieht.
  • Ein Tribble, das gefangen werden kann und dann verschwindet, sich ansonsten vermehrt, indem es als ActionStory in jedem Spielerzug mit einer gewissen Wahrscheinlichkeit neue Objekte der eigenen Klasse ins Spiel bringt. (Besser mit Obergrenze, da exponentielles Wachstum.)
  • Eine klassische Kuriermission, bei der ein Gegenstand von Ort A nach Ort B gebracht werden muss und man dafür eine Belohnung erhält.
  • Romeo und Julia: Man trägt Briefe zwischen beiden hin und her, bis beider Geschichte ein Ende findet.
  • Eine Begleitmission, bei der man einen Passagier zu einem möglicherweise unbekannten Ort geleiten und abliefern muss. (Die letzten drei sind Beispiele für Geschichten, die an mehr als einem Ort spielen.)
  • Ein bürokratisches Labyrinth, aus dem man herausfinden muss.
  • Eine Fähre zwischen dem Hafen und La Isla Bonita – als Geschichte, die nur an einem Ort spielt, der aber mit moveStoryTo geändert wird, oder als Geschichte an zwei Orten. (An dem einen hat man die Fähre und die Möglichkeit, sie zu besteigen; am anderen wird man auf die abwesende Fähre hingewiesen. Die Fähre wechselt natürlich den Ort, entweder durch Spieleraktion oder selbstständig.)
  • „Das öde Haus“ von E.T.A. Hoffmann, umgeformt in eine Minigeschichte, mit viel Originalzitaten.

Langer Anhang: Varianten der zentralen Methoden in verschiedenen Unterklassen

Ich finde die Trennung in zwei Methoden createReport und receiveMessage übersichtlich und praktisch. Aber gerade wenn man die Geschichte mit einem Zustandsattribut umsetzt, gibt es unschöne Doppelungen im Code: Man braucht für beide Methoden ein ähnliches if-else- oder switch-Konstrukt, um den aktuellen Zustand abzufragen, von dem ja sowohl der Report als auch der Zustandsübergang abhängen. Wem das nicht gefällt, für den gibt es andere Möglichkeiten. Es folgt ein kurzer Überblick über die verschiedenen Geschichtenklassen. Eine Schüler-Klasse ist jeweils eine Unterklasse einer davon; die in diesem Fall zu überschreibenden Methoden sind jeweils farblich markiert.

Klassendiagramm Story-Klassen

AbstractStory

Das ist die allgemeinste Klasse. Unterklassen davon müssen einfach nur drei Methoden implementieren; wem das reicht, der kann die restlichen Klassen ignorieren:

  • String getName() – das gilt für alle Geschichten-Klassen. Jede Geschichte braucht einen (eindeutigen) Namen.
  • Report createReport(String locationName) – die Geschichte wird bei jeder Bewegung und jeder Entscheidung eines Spielers gebeten, einen Bericht abzuliefern. Dieser Bericht hängt vielleicht vom Zustand der Geschichte ab, aber ganz sicher vom Ort des Spielers, dessen Bezeichner als Argument übergeben wird. Wenn eine Geschichte in Innismund und in Das Schloss spielt, wird sie nur in diesen beiden Fällen einen Report abgeben, in allen anderen Fällen null zurückgeben. So ein Null-Report wird dann nicht angezeigt.
    Einem Report kann man Optionen hinzufügen, die dann als anklickbare Knöpfe erscheinen. Das geht unter anderem mit der Methode addOption(String, String). (Man kann auch Option-Objekte erzeugen, die dann mehr Möglichkeiten bieten.) Der erste String ist der Text, den der Spieler zu sehen kriegt; der zweite String ist die Nachricht, die an die Geschichtenklasse geschickt wird, damit auf die Wahl des Spielers reagiert werden kann – siehe folgende Methode.
  • void receiveMessage(String message) – diese Methode wird aufgerufen, wenn der Spieler auf den entsprechenden Options-Knopf geklickt hat, dabei wird der String, der der Option als zweites Argument übergeben worden ist, zum Argument der receiveMessage(String)-Methode gemacht. (Tatsächlich wird vorher die Methode receiveMessage(Option) aufgerufen, wobei aus dem Option-Argument eine Reihe von Consequence-Objekten extrahiert und automatisch ausgeführt werden, bevor der Message-String extrahiert und an receiveMessage(String) übergeben wird.)
import storyworld.*;
public class Fischer extends AbstractStory {
    int zustand = 0;
    public Fischer () { }
    public String getName() { return "Der Angler"; }    
 
    public Report createReport(String locationName) {
        if (!locationName.equals("Hafen")) return null; // die Geschichte gibt nur für einen Ort einen Report zurueck
        if (zustand == 0) {
            Report r = new Report("Ein Fischer sitzt am Kai und angelt.");
            r.addOption("Du fragst ihn, wie es so läuft.", "ansprechen");
            return r;
        } else {
            Report r = new Report("'Toll!' Der Angler erzählt dir eine lange Geschichte, wie er einmal fast den größten Fisch gefangen hat, denn es je zu sehen gab, und gibt dir einen Fisch.");
            r.addOption("Du dankst für das Gespräch und den Fisch.", "beenden"); 
            return r;
        }
    }
 
    public void receiveMessage(String s) {
        if (zustand==0) {
            if (s.equals("ansprechen") ) // ausloesende Aktion
            { 
                if (get("Fische")<3) { // Bedingung des Zustandsuebergangs 
                    zustand = 1; 
                    increase("Fische", 1); // ausgeloeste Aktionen
                } else {
                    sendMessage("Du hast schon genug Fische."); 
                }
            }
        }
        else if (zustand==1) {
            if (s.equals("beenden")) // ausloesende Aktion
            {
                zustand = 0; 
            }
        }
    }    
}

SingleLocationStory

Diese Klasse ist für Einsteiger gedacht; bei ihr muss die Methode createReport() ohne Argument überschrieben werden. Diese Methode wird nämlich nur dann aufgerufen, wenn der Spieler sich an dem Ort befindet, an dem die Geschichte spielt, und der am Anfang durch startStoryAt() festgelegt wird. Dafür kann die Geschichte aber auch nur an einem Ort gleichzeitig spielen. Dieser Ort kann mit moveStoryTo(String) geändert werden.
Die Methoden getName() und receiveMessage(String) funktionieren wie oben.

import storyworld.*;
public class Fischer extends SingleLocationStory {
    int zustand = 0;
    public Fischer () { }
    public String startStoryAt() { return "Hafen"; }
    public String getName() { return "Der Angler"; }    
 
    public Report createReport() {        
        if (zustand == 0) {
            Report r = new Report("Ein Fischer sitzt am Kai und angelt.");
            r.addOption("Du fragst ihn, wie es so läuft.", "ansprechen");
            return r;
        } else {
            Report r = new Report("'Toll!' Der Angler erzählt dir eine lange Geschichte, wie er einmal fast den größten Fisch gefangen hat, denn es je zu sehen gab, und gibt dir einen Fisch.");
            r.addOption("Du dankst für das Gespräch und den Fisch.", "beenden"); 
            return r;
        }
    }
 
    public void receiveMessage(String s) {
        if (zustand==0) {
            if (s.equals("ansprechen") ) // ausloesende Aktion
            { 
                if (get("Fische")<3) { // Bedingung des Zustandsuebergangs 
                    zustand = 1; 
                    increase("Fische", 1); // ausgeloeste Aktionen
                } else {
                    sendMessage("Du hast schon genug Fische."); 
                }
            }
        }
        else if (zustand==1) {
            if (s.equals("beenden")) // ausloesende Aktion
            {
                zustand = 0; 
            }
        }
    }    
}

ShopStory

Eine Klasse, um Läden anzulegen, die Waren oder Gegenstände kaufen und verkaufen. Arbeitet viel mit Requirement– und Consequence-Objekten bei den Optionen. Überschrieben werden muss keine Methode, aber sinnvollerweise werden mit addItem im Konstruktor Waren hinzugefügt, die der Laden kauft und verkauft.

import storyworld.*;
 
public class KleinerLaden extends ShopStory {
    public String startStoryAt() {  return "Innismund"; }
    public String getName() { return "Der kleine Laden";}
    public KleinerLaden() {
        setDescription("Hier gibt es allerlei Dinge.");        
        addItem("Wasser",50, "Geld", 2, "Geld", 1); // 50 Wasser vorhanden, fuer 2 Geld zu kaufen, fuer 1 Geld an den Laden verkaufen 
        shopSells("Kräuterbaguette",5, "Geld",1000); // 1 Kraeuterbaguette für 1000 Geld kaufen (5 vorhanden)             
        shopBuys ("Seele",1, "Geld", 1000); // 1 Seele fuer 1000 Geld verkaufen
        shopBuys ("Ansehen",10, "Geld", 1); // 10 Ansehen fuer 1 Geld verkaufen
        shopBuys ("Tribble",1, "Geld", 1); // 1 Tribble fuer 1 Geld verkaufen
    }
}

TestableStory

Eine Unterklasse, die speziell für das Projekt eingerichtet wurde; sie hat eine test-Methode, die eine Welt erzeugt und die Geschichte, die die Methode aufgerufen hat, in die Testwelt einbaut. Diese Methode muss von der Unterklasse am Ende des Konstruktors aufgerufen werden.

SimpleStory

Ist identisch zu SingleLocationStory, nur mit der zusätzlichen Möglichkeit des Testens, indem am Ende des Konstruktors test() aufgerufen wird.

MultiplaceStory

Entspricht letztlich der Klasse AbstractStory – das heißt, die createReport-Methode wird jetzt wieder bei jeder Location aufgerufen, dafür erhält sie jetzt wieder als String-Argument den Namen der aktuellen Location. Am Ende des Konstruktors wird test() aufgerufen.

ModifiedSimpleStory

Vielleicht stört den einen oder anderen die Doppelung der if-Anweisung in den beiden Methoden. Deshalb bietet diese Klasse eine Methode Report receiveChoice(String message), die überschrieben werden muss. Sie ersetzt die beiden Methoden receiveMessage und createReport der Oberklasse, die demnach nicht überschrieben werden müssen.
Auch diese Methode wird nur dann aufgerufen, wenn der Spieler an dem einen Ort ist, an dem die Geschichte spielt. Man muss also nie einen Null-Report zurückgeben.
In dieser Klasse muss man zwar nur eine zentrale Methode schreiben, aber dese Methode enthält wieder zwei gro0ße if/switch-Blöcke: Zuerst einen für das Auswerten der Nachricht, dann einen für das Senden des aktuellen Zustands-Reports.

import storyworld.*;
public class Fischer extends ModifiedSimpleStory {
    int zustand = 0;
    public Fischer () { }
    public String startStoryAt() { return "Hafen"; }
    public String getName() { return "Der Angler"; }    
 
    public Report createReport(String s) {
        //Teil 1   
        if (zustand==0) {
            if (s.equals("ansprechen") ) // ausloesende Aktion
            { 
                if (get("Fische")<3) { // Bedingung des Zustandsuebergangs 
                    zustand = 1; 
                    increase("Fische", 1); // ausgeloeste Aktionen
                } else {
                    sendMessage("Du hast schon genug Fische."); 
                }
            }
        }
        else if (zustand==1) {
            if (s.equals("beenden")) // ausloesende Aktion
            {
                zustand = 0; 
            }
        }
        //Teil 2
        if (zustand == 0) {
            Report r = new Report("Ein Fischer sitzt am Kai und angelt.");
            r.addOption("Du fragst ihn, wie es so läuft.", "ansprechen");
            return r;
        } else {
            Report r = new Report("'Toll!' Der Angler erzählt dir eine lange Geschichte, wie er einmal fast den größten Fisch gefangen hat, denn es je zu sehen gab, und gibt dir einen Fisch.");
            r.addOption("Du dankst für das Gespräch und den Fisch.", "beenden"); 
            return r;
        }
    }
}

ModifiedMultiplaceStory

Analog zu MultiplaceStory wird hier Report receiveChoice(String message) an jedem Ort des Spiels aufgerufen, man muss in dieser Methode also für die meisten Orte einen Null-Report zurückgeben. (Die Methoden receiveMessage und createReport entfallen wieder.)

Letztlich wie die Oberklasse, nur mit einem zusätzlichen if (!s.equals("Hafen")) return null;

YetAnotherModifiedSimpleStory

Diese Klasse ist für Leute, die die zwei if/switch-Blöcke stören: Hier gibt es nur einen davon. Da der verlangte Report ja erst am Schluss der Methode zurückgegeben werden kann, hat die zu implementierende Methode void createReport(String message) hier keinen Rückgabetyp; dafür wird mit Aufrufen von setReport(Report) der später zurückzugebende Report festgelegt.

import storyworld.*;
public class Fischer extends YetAnotherModifiedSimpleStory{
    int zustand = 0;
    public Fischer () { }
    public String startStoryAt() { return "Hafen"; }
    public String getName() { return "Der Angler"; }    
 
    public void receiveChoice(String s) {        
        if (zustand == 0) {
            Report r = new Report("Ein Fischer sitzt am Kai und angelt.");
            r.addOption("Du fragst ihn, wie es so läuft.", "ansprechen");
            setReport(r);
            if (s.equals("ansprechen")) {
               if (get("Fische")<3) { // Bedingung des Zustandsuebergangs 
                   zustand = 1; 
                   increase("Fische", 1); // ausgeloeste Aktionen
               } else {
                   sendMessage("Du hast schon genug Fische."); 
               }
           }
        } else {
            Report r = new Report("'Toll!' Der Angler erzählt dir eine lange Geschichte, wie er einmal fast den größten Fisch gefangen hat, denn es je zu sehen gab, und gibt dir einen Fisch.");
            r.addOption("Du dankst für das Gespräch und den Fisch.", "beenden"); 
            setReport(r);
            if (s.equals("beenden")) {
                zustand = 0;
            }
        }
    }
}

SimpleStoryChrisGraessl

Eine weitere, sehr elegante Methode. Hier muss man nur Report createReport() überschreiben, die Methode receiveMessage(String) entfällt beziehungsweise ist in der Oberklasse festgelegt. Das zweite Argument von:

addOption(String, String)

wird dabei als Methodenbezeichner verwendet. Setzt man diese Option:

addOption("Du isst den Kuchen.", "essen");

so wird, wenn der Spieler sich für sie entscheidet, die Methode void essen() aufgerufen, die natürlich existieren muss.

import storyworld.*;
public class Fischer extends SimpleStoryChrisGraessl {
    int zustand = 0;
    public Fischer () { }
    public String startStoryAt() { return "Hafen"; }
    public String getName() { return "Der Angler"; }    
 
    public Report createReport() {        
        if (zustand == 0) {
            Report r = new Report("Ein Fischer sitzt am Kai und angelt.");
            r.addOption("Du fragst ihn, wie es so läuft.", "ansprechen");
            return r;
        } else {
            Report r = new Report("'Toll!' Der Angler erzählt dir eine lange Geschichte, wie er einmal fast den größten Fisch gefangen hat, denn es je zu sehen gab, und gibt dir einen Fisch.");
            r.addOption("Du dankst für das Gespräch und den Fisch.", "beenden"); 
            return r;
        }
    }
 
    public void ansprechen() {
        if (get("Fische")<3) { // Bedingung des Zustandsuebergangs 
            zustand = 1; 
            increase("Fische", 1); // ausgeloeste Aktionen
        } else {
            sendMessage("Du hast schon genug Fische."); 
        }
    }
 
    public void beenden() {
        zustand = 0; 
    }    
}

8 thoughts on “Storyworld – ein Java-Projekt für die 10. Klasse

  1. Aginor

    Ist gut geworden!
    Ein recht gut lesbarer Code wie ich finde, und auch den Aufbau der Klassen finde ich ansprechend.
    Musste lachen beim Tribble. Würde den sofort nehmen. :)
    …und hmm…. wenn der sich so vermehrt, kann man das dann nicht im Laden durch Verkauf exploiten? Tribbles farmen sozusagen. :D
    Und in dem Zusammenhang interessant: Für komplexere Spiele fehlt beim Laden IMHO eine Mechanik die das Geld des Ladens begrenzt. Die Spiele der „The Elder Scrolls“-Reihe haben das z.B. recht gut umgesetzt, mit einem sich langsam bis zu einem Maximum auffüllenden Geldwert, und wenn der Händler kein Geld mehr hat gerade, dann kann man ihm auch nichts mehr verkaufen (außer durch Tauschgeschäfte, aber das ginge hier zu weit).

    Mit welchem Tool ist denn das Klassendiagramm erstellt? Und ist das automatisch aus dem Code erstellt oder manuell?

    Gruß
    Aginor

  2. Herr Rau Post author

    Nun wollte ich ja nicht unbedingt den Elder Scrolls Konkurrenz machen… :-) Aber ist notiert und lässt sich ergänzen. Der Tribbles-Exploit: Klar. Was der Spieler nicht weiß: Sobald es zu viele werden, brechen sie aus und es kommt zu Katastrophe und Bürgerkrieg, alle Stories werden auf inaktiv geschaltet, bis ordentlich aufgeräumt wurde; Geld verliert seinen Wert und so weiter. Oder man lässt den Exploit drin. Hm, wenn man stattdessen eine Krankheit schreibt, die sich ausbreitet, und wenn der Spieler am selber Ort ist, infiziert er sich (ein Schalter der Krankheitsgeschichte wird auf true gesetzt), woraufhin die Geschichte ihm bei jedem Zug Punkte abzieht, bis er irgendwo Heilung findet…

    Das Diagramm ist manuell erstellt, mit dem Uralt-Allround Dia. Richtig zufrieden bin ich damit nicht, aber ich habe mich noch nicht nach einer Alternative umgesehen. Vermutlich gibt es da am meisten für Eclipse, so dass ich mein BlueJ-Projekt in Eclipse importieren müsste. Irgendwann mal.

  3. Aginor

    Oh, das sind ein paar nette/gemeine Ideen! :D

    Ich finde zum manuell Diagramme zeichnen das Programm „yED“ recht ordentlich.

    Für Java gab es mal wenn ich mich recht entsinne ein Tool das brauchbar automatisch funktioniert. Erinnere mich aber nicht mehr an den Namen. Könnte ein Eclipse-Plugin gewesen sein, Omondo oder ObjectAid, aber auch ein Standalone-Tool…

    Gruß
    Aginor

  4. Stephan

    Zum manuellen Diagramm-Erstellen (auch wegen verschiedener Integrationen, z.B. in GoogleDrive) nett: draw.io

  5. Herr Rau Post author

    draw.io ausprobiert: Sehr viel komfortabler als Dia. Jetzt muss ich mir nur noch daran gewöhnen, dass die SOftware dazu nicht bei mir liegt. Aber das sollte kein echtes Problem sein.

  6. Theresa

    Auch BlueJ kann man mit Extensions erweitern: https://www.bluej.org/extensions/extensions.html
    Relativ weit unten findet sich dann auch „UML Extension“, vielleicht passt das ja?

    Ansonsten kann ich wie Aginor auch yED empfehlen. Vielleicht nicht unbedingt für Klassendiagramme, aber für viele viele andere Diagramme, wie z.B. Zustandsautomaten sehr gut geeignet.

    Ach und übrigens: Der gute alte Pacman-Verschnitt „Krümel und Monster“, den ich ja damals als Programmier-Projekt hatte, hat es bei mir inzwischen sogar auf den Arduino geschafft: http://tetopia.de/programmieren/arduino.php

  7. Herr Rau Post author

    Krümel & Monster würde ich vielleicht wieder machen, aber das läuft leider nur noch mit alten BlueJ- oder Java-Versionen. Arduino und vergleichbare Kleingeräte sind gerade der Renner in der Informatikdidaktik, aber wir machen noch wenig damit. Ich bin ja auch selber nicht so der Bastler, aber habe natürlich schon damit herumgespielt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.