Java Swing テキストRPGを作る ~Textの作成を行うゲーム進行の作成~

イントロダクション

前回は、Java Swingを使用して画面作成を行いました。下のような画面です。

この状態は、表示するための領域があるだけですので、これに表示するもの(内容)を出力する部品が必要になります。
もちろん、出力する内容はテキストRPGの「RPG」の部分です。そのためにまずはタイトルを表示することを考えます。

ソースはGithubにアップしています。

RpgProgressRpgStoryクラスの作成

ストーリーを表示するための画面を作成したので、今度はストーリーを出力(文字列を読み込んだり)するクラスを作成します。
まとめると、ゲーム進行を開始、実行するクラスです。

ちなみに、現状のクラスは下のようになっています。

<ゲーム進行の実装後>

RpgStoryクラスの設計

クラスを作成するために、まずは設計を行います。何から考えるか?

1. 目的(役割)を考える

作成するクラスの名前は「RpgStory」という名前に決めました。なのでこのクラスの役割(目的)を考えます。
やりたいことは次の通りです。フローチャートにしてみました。

とりあえず、下のような形で処理を進める形で作りたいと考えています。

  1. ゲーム開始
  2. タイトル表示(これもシーンとして扱う)
  3. 各シーンを表示
      1. Storyの表示
      1. 入力を受ける
      1. 結果の表委
      1. 次のシーンへ移動
  4. Escが押下されたとき、終了シーンに移動したときゲームを終了

こんな感じの処理フローを考えています。これを実現するために頭をひねります。
そして、上記の処理にある。「各シーンのStoryを表示」を実現する役割のクラスが「RpgStoryクラス」になりますので、下のようにクラスを作成します。

ストーリーを表示するまでの処理

まずは、クラスを作成します。Storyや結果などを表示する必要があるのでこのクラスのフィールドにテキストエリアをフィールド変数にセットします。
※RpgStoryというクラスを作成していましたが、クラス名を以下のように変更しました。

package jp.zenryoku.rpg;

import javax.swing.JComponent;
import jp.zenryoku.rpg.utils.FileReaderUtil;
import jp.zenryoku.rpg.utils.StoryBuffer;

import jp.zenryoku.rpg.views.InputSelector;
/**
 * クラス RpgProgress の注釈をここに書きます.
 * テキストRPGのゲーム進行を行う。以下の処理を行う。
 * 
 * 1. ロード済みのストーリー(シーン)を呼び出す。
 * 2. シーンのストーリー表示。
 * 3. 入力受付。
 * 4. 結果の表示。
 * 5. 次のシーンへ移動。
 * 
 * @author (Takunoji)
 * @version (1.0)
 */
public class RpgProgress
{
    /** テキストエリア */
    private RpgTextArea fView;
    /** ストーリーのテキストを保持するバッファ */
    private StoryBuffer fBuf;

    /** コンストラクタ */
    public RpgProgress(RpgTextArea view) {
        // ここに実行するための準備を実装する
        this.fView = view;
        // StoryBufferをセット
        fBuf = StoryBuffer.getInstance();
    }

    /**
     * テキストRPGのシーンを呼び出し実行する。
     * プログラムのメイン処理フローを実装。
     */
    public void run(JComponent comp, int x, int y) {
         // - 1. Storyの表示
         StringBuilder build = FileReaderUtil.readFile("title.txt", true);
         fView.setText(build.toString());
         // - 2. 入力を受ける
         openSelects(comp, x, y);
         // - 3. 結果の表委
         // - 4. 次のシーンへ移動
    }

    /**
     * ユーザーの入力を促すためのポップアップを表示する。
     */
    private void openPopup(JComponent comp, int x, int y) {
        String[] yesNo = new String[] {"start", "continue"};
        InputSelector pop = new InputSelector("First", yesNo);
        pop.show(comp, x, y);
    }
}

そして、ここから考えている処理を実現するための仕組みを考えていきます。そのためにクラスとクラスの関係を作成します。
このクラス同士の関係を作るために必要になる知識が以下のものになります。

クラス関係を作るための知識

「基本」という言葉で丸め込まれることが多いですが、以下のようなものです。

  1. 通常のクラスの書き方
  2. クラスの継承の仕方
  3. インターフェース・クラスの作り方
  4. 抽象クラスの作り方

すでに、クラスの作り方は学んでいるので大丈夫だと思います。が、作り方がわかっても使うのに慣れていないと、「理解した」というところに、たどり着かないので。。。

クラスを使ってみよう(ちょっと脱線)

Javaプログラミングを行うときにはJDKというものを使用してプログラムを使っています。
このJDKにはJavaプログラムを作成した人々が用意しているAPIというものを使うことができます。まずはこのAPIを使い
クラスの扱い方を理解しましょう。今回は、java.io.BufferedReaderクラスを使ってみます。

次は、タイトルを表示するために「Story.txt」ファイルを読み込み、それを表示するプログラムを作成します。

ファイルの読み込み

テキストRPGを起動して、初めにタイトルを表示します。このタイトルはRPGを作成するユーザーが作るものなので「固定」ではなく「動的」に作ります。

「動的」とは

上記の通り「固定」ではなく「動的」という言い方がわかりやすいと思うのですが、

固定的に実装

固定的に実装するというのを、初めのプログラムハローワールドを例にして考えてみます。
ハローワールドのプログラムを思い出してみてください。下のように実装しました。

public class Kotei {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

これは固定的で、出力する文字列が必ず「Hello World」です。

動的に実装

上記のプログラムを動的に変更します。動的になると必ずしも「Hello World」ではない文字列が出力できます。
でわ、動的なプログラムに修正しましょう。

public class Doteki {
    public static void main(String[] args) {
        if (args.length != 0) {
            System.out.println(args[0]);
        } else {
            System.out.println("Hello World");
        }
    }
}

上記のような形で実装すると動的に文字列を表示できます。

このような形で、実装すると表示する文字列を動的に出力することができます。

ファイルを読む形だと?

ファイルを読み込む形にすると、プログラムは全く同じでも、出力する文字列は、ファイルの中身に依存しますので、動的に表示するタイトルを表示することができるというわけです。別な言い方をすると「プログラムの修正をしなくても出力する文字列を変更できる」ということです。

ファイルの読み込み処理

ここで注意が一つあります。本来であれば、ファイルコントロール用のクラスを作るのが、通常のやり方ですが、今回はクラスを作成する一歩前の学習を目的としているので、メソッドを追加する形で実装します。

追加するのは下のようなメソッドです。

    private void readFile(String fileName) throws FileNotFoundException, IOException {
        File f = new File(fileName);
        BufferedReader buf = new BufferedReader(new FileReader(f));
        String line = null;
        while((line = buf.readLine()) != null) {
            System.out.println(line);
        }
        buf.close();
    }

ポイント

throws FileNotFoundException, IOExceptionの部分は「例外を呼び出し元に投げますよ」という意味です。
具体的には、例外があったときに、上記のメソッドを呼び出しているメインメソッドに例外が投げられます。

Scanner scan = new Scanner(System.in);
System.out.print("入力してください: ");
String in = scan.next();

try {
    if (in.equals("test")) {
        System.out.println("*** Testing now! ***");
    } else if ("file".equals(in)) {
        main.readFile("resources/FirstStage.txt");
    }
} catch (FileNotFoundException e) {
    System.out.println("*** No File! ***");
} catch (IOException e) {
    System.out.println("*** What's up!? ***");
}

このコードでは、以下のような処理を行っています。

  1. 標準入力を受けとる
  2. 入力した値が「test」の場合は、「* Testing now! *」を表示する
  3. 入力した値が「file」の場合は、「resources/FirstStage.txt」のファイルを読み込み表示

ここで使用しているメソッドは引数に「resources/ファイル名」を渡しています。つまり、プロジェクト直下にある「resources」フォルダ内にある「FirstStage.txt」ファイルを読み込んで表示します。

このような、「引数にファイル名(パスも含める)」を渡してやれば、ファイルを読み込むようなメソッド、そのような役割を持っているメソッドを作ってやればこのメソッドは、いろんな場面で使用することができます。

このような役割を持たせてメソッドを作ることを練習してできるようになったら、次はクラスにその役割を持たせるように実装します。
メソッドと違いクラスの場合は、もっと大きな役割を持たせることができます。詳細は後々に説明します。まずは、メソッドを作れるようになりましょう。

サンプルの説明

上記に書いた処理を実行していますが、プログラムが動いている部分を見る方が理解が早いと思います。

ファイルの中身を表示する

話をもとに戻します。RpgStoryの処理で「タイトル表示」を行うために「動的に表示したい」のでファイルの中身を表示するように
プログラムを作成したいともいます。

作成した画面に読み込んだファイルの中身を表示します。
先ほどのBufferedReaderクラスを使用して次のようなクラスを作成、付随するクラスを同様に、作成しました。

プログラムの実行結果は次のような感じです。

タイトル表示後は?

タイトルを表示した後は、「A.はじめてやる」のと「B.続きをやる」の2択にします。なので、AとBのどちらかを選択し次の処理を行うようにしたいと思います。

選択をしてもらうのに「ポップアップ」を使用したいと持っています。なので。次のクラスを使用してポップアップを作成します。

そして、このクラスは今回のテキストRPGで使いたいのでテキストRPGようにカスタムしますのでこのクラスを拡張します。

JPopupMenuクラスを拡張する

クラスの継承方法は、「tends XXXXように書きます。作成するクラスは「putSelector」クラスにします。

public class InputSelector extends JPopupMenu { ... }

これで、拡張する準備ができました。
ここから、RpgStoryクラスに、ストーリーを進めていったときの入力を受け取るクラスとして役割を果たします。

JMenuItemクラスも拡張

上記のポップアップメニューに表示する選択肢を表示するためのクラスがJMenuItemクラスです。そして、このクラスがクリックされたときに何かしらのアクションを起こすクラスが、Actionインターフェース・クラスなのですが、扱いが簡単な「AbstractAction」を使用して実装します。

やりたいことは、つぎの操作が行いたいです。

  1. 選択肢を表示する
  2. クリックしたときに次の処理をする

なので、下のように実装します。

public class TitleMenu extends AbstractAction { ... }

これで、AbstractActionに定義され散る「オーバーライドしてね」とされているメソッド「actionPerformed()」をオーバーライドしてやればOKです。
実際には、コンストラクタも作成しました。

public class TitleMenu extends AbstractAction
{
    public TitleMenu() {
        super("titleMenu");
    }

    public TitleMenu(String str) {
        super(str);
    }

   public void actionPerformed(ActionEvent event) {
        System.out.println("Message: ");
   }
}

現状では、クリックされたときに「Message:」を標準出力に出力するだけです。
<クラス図>

処理の内容について
  1. 画面の表示を行う
  2. title.txtを読み込み画面に表示
  3. 初めの選択肢(ポップアップメニュー)を表示
  4. クリックしたらそれぞれの処理を行う(TitleMenuクラス)

現状では、上記のような実装ができています。

この後は、以下の処理を実装したいと思います。

  • 「continue」をクリックしたときには、「未実装です」を表示した後に元のポップアップを表示する。
  • 「start」をクリックしたときには、Story.txtに定義されている文章を表示する。

さてどのような仕組みにしたらよいでしょうか?イメージとしては、初めに記述したように「Scene」を使用してストーリーを展開していきます。
なので、これを忠実に実行するための仕組みを考えなくてはいけません。

次は、これを実現すための仕組みを考えます。
さてさて。。。どうしたものか?

仕様を確認する

今回の開発は、以下の目的があります。

  • 以前作成したものをリメイクする。
  • アプリケーションを動かし、java学習者が学習に使う。
  • ゲームブックのようにゲームを展開する。

なので、なるべく制限を少なく、自由にゲームを作成する事ができるように、。各設定ファイルの拡張性を高めたうえで実装するのが理想的。

実装のポイント

実装する上でのポイントになる部分、ゲームの設定を行うためのファイルと実行するシーンの種類を一覧しました。

設定ファイル一覧(ゲーム作成者用)

ゲームの作成者は以下のファイルの内容を記述する。サンプルゲームにある、これらのファイルには、簡単な設定が書かれているのでそれをカスタムして利用してほしい。※Githubを更新していきます。

ファイル名 記述内容
title.txt ゲームを起動したときの初期表示画面(テキストのみで描画する)
conf.txt ゲームの設定、言葉(単語)の意味を定義、HPやMPは固定だが他の値を追加、使用方法を指定することができる
story_XXX.xml ゲームのストーリーを記述、各ストーリーのシーンを描画するXMLファイル。「XXX」にはシーン番号が入る。 例: story_1.xml
Story.txt ゲームのストーリーを記述、ゲームブックの本体のイメージ
Job.xml 各職業の技、魔法などの習得レベルを記述
Commands.xml 各キャラクターが実行するコマンド
STM.xml 技(Skill, Tech), 魔法(Magic)の効果などを定義
MonseterType.xml モンスターの技、魔法などの習得レベルを記述
Monsters.xml モンスターの名前、HPやMPなどを記述する
Shops.xml ショップの定義、販売しているものと値段のリスト
Items.xml アイテム(武具も含む)の定義、販売しているものと値段のリスト、非売品は値段を「-」にする

Sceneの種類

No シーンタイプ シーンの役割
1 ストーリーシーン XMLファイルに定義されているstoryタグの内容を表示する
2 バトルシーン XMLファイルに定義されているmonsterタグの内容を表示する
3 プレーヤー生成シーン プレーヤーを作成するシーン
4 次のシーンを選択するシーン 次のシーン番号を指定してそのシーンを呼び出す
5 ショップシーン 買い物を行うシーン
6 エフェクトシーン 「お金を拾う」「HPが回復した」などのプレーヤーに対する効果を起こすシーン

それぞれのシーンを実装するのに、前回のシーンでは、テキストファイルからタグをつけてシーンタイプを指定していたため
ユーザーが作成するストーリーに対し大きな制限があった。
これを、XMLファイルに変更して次のように実装する

XMLでのシーン定義

タグでシーンの動きを定義するようにする。

1.storyタグ

「ストーリーシーン」を実行する。
単純にタグ内の文字列を表示する。

2.batlleタグ

「バトルシーン」を実行する。
戦闘開始前の文言、例えば「XXXが現れた!」、を定義。モンスターIDを指定し、Monster.xmlから対象のモンスターを取得する。

3.create

「プレーヤー生成シーン」を実行する。もしくは、プレーヤーを初めから用意しておく。

  1. 名前から性別などを入力して、生成する(設定ファイルにて必要な入力項目を定義するようにする)
  2. プレーヤーの名前から性別、ステータスを定義、職業の類もセットできるようにする。
4.nextタグ

次のシーンを選択するシーンを実行する。
選択肢と次のシーン番号をセットにして表示、選択する。

5.effectタグ

「エフェクトシーン」を実行する。
「conf.txt」で定義しているパラメータをプレーヤーに対して上げたり下げたりする。
例:「プレーヤーは10のダメージを受けた!」、「100円拾った」、「たろう(犬)がなついてきた(効果なし)」など

6.shopタグ

「ショップシーン」を実行する。
ショップIDで指定した店を表示、買い物など利用することができる。「宿屋」などはeffectタグをつけることで何かしらの効果を起こすことができる

XMLの読み込み

DocumentBuilderFactoryを使用してXMLファイルを読みこみ、ゲーム展開に必要なデータ(情報)を取得するようにプログラムを作っていくことにしました。

このクラスは、XMLファイルを読み込んで、Documentというインターフェース(の中身が実装されたもの)を取得できる。というJavaAPIです。

<サンプルコード>

    /**
     * XMLドキュメント(ファイル)を読み込む。
     * @param directory ファイルの配置しているディレクトリ
     * @param fileName ファイル名
     * @return Documentオブジェクト
     * @throws RpgException ファイルの読み込みエラー
     */
    private static Document loadDocumentBuilder(String directory, String fileName) throws RpgException {
        //creating a constructor of file class and parsing an XML files
        Path path = Paths.get(directory, fileName);
        File file = path.toFile();// ("/Monster.xml");
        //an instance of factory that gives a document builder
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        //an instance of builder to parse the specified xml file
        DocumentBuilder db = null;
        Document doc = null;
        try {
            db = dbf.newDocumentBuilder();
            doc = db.parse(file);
            doc.getDocumentElement().normalize();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
            throw new RpgException(MessageConst.ERR_XML_PERSE.toString() + ": " + e.getMessage());
        } catch (SAXException e) {
            e.printStackTrace();
            throw new RpgException(MessageConst.ERR_XML_PERSE.toString() + ": " + e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            throw new RpgException(MessageConst.ERR_IOEXCEPTION.toString() + ": " + e.getMessage());
        }
        return doc;
    }

前回作成したコードですが、これでDocumentを取得して、各タグを取得してい行きます。
下のようなコードでタグを取得します。上のメソッドを呼び出しています。これはMonster.xmlを読み込んだ時のサンプルコードです。

Document doc = loadDocumentBuilder("src/main/resources", "Monsters.xml");

NodeList list = doc.getElementsByTagName("monster");
for (int i = 0; i < list.getLength(); i++) {
    Node node = list.item(i);
    if (node.getNodeType() == Node.ELEMENT_NODE) {
        monsList.add(createMonster(node));
    }
}

XMLから設定情報クラスを生成

XMLファイルの解説は別の記事にて行っております。

上記のようなXMLファイル、つまり設定ファイルをユーザー(ゲームブック作成者)が作成することで、ユーザーは自由に自分の作品(RPGゲームブック)を作成することができるというわけです。ただし、XMLとかフローチャートとか、使い方を覚える必要がありますが。。。

しかし、ここで重要なのは、「動的なアプリケーションを作成する事」ですのでユーザーの定義する情報からアプリケーションの動作をコントロールできるように作ることです。
ちなみに、サンプルで「世界観」を作成してみました。XMLファイルは「Worlds.xml」、読み込みはXMLUtilsクラスです。

Worlds.xml

<?xml version="1.0"?>
<!-- 世界観 -->
<class>
    <world>
        <id>upperWorld</id>
        <!-- name: 世界の名前 -->
        <name>ちきゅう</name>
        <!-- 世界地図(画像ファイル) -->
        <img>https://img.game8.jp/3808388/f49a39a900970874043b2d4c899bce8e.jpeg/show</img>
        <!-- nature: 自然 -->
        <nature>
            <climate>
                <name>寒帯気候(かんたいきこう)</name>
                <creatures>
                    <creature id="tonakai"/>
                    <creature id="arcticfox"/>
                    <cresture id="grizzlybear"/>
                </creatures>
                <!-- 定義は自由に追加してよい -->
            </climate>
            <climate>
                <name>亜寒帯気候(あかんたい きこう)</name>
                <creatures>
                    <creature id="amurleopard"/>
                </creatures>
            </climate>
            <climate>
                <name>温帯気候(おんたいきこう)</name>
                <creatures>
                    <creature id="lynx"/>
                    <creature id="eagle"/>
                </creatures>
            </climate>

            <climate>
                <name>乾燥帯気候(かんそうたい きこう)</name>
                <creatures>
                    <creature>
                        <id>camel</id>
                        <name>ラクダ</name>
                        <discription>ラクダは、楽だ</discription>
                    </creature>
                </creatures>
            </climate>

            <climate>
                <name>熱帯雨林気候(ねったいうりん きこう)</name>
                <discription>./climate/tropical_rainforest_climate.txt</discription>
            </climate>

        </nature>
        <!-- モンスターを含む動植物には食物連鎖 -->
        <!-- 「植物→草食動物→肉食動物」のような書き方でもよい -->
        <food_chain>./FoodChain.md</food_chain>
        <!-- 生息地、生物分布 -->
        <creatures>./Creatures.xml</creatures>
        <!-- 「地域」としてまとめる(地形、天候、四季(雨季と乾季など)) -->
        <regions>
            <region>
                <id>1</id>
                <name>FirstStage</name>
                <discription>アリアハンのある大陸</discription>
            </region>
            <region>
                <id>2</id>
                <name>SecondStage</name>
                <discription>ポルトガのある大陸</discription>
            </region>
            <region>
                <id>1</id>
                <name>ThirdStage</name>
                <discription>ミラミッドのある大陸</discription>
            </region>
        </regions>
        <!-- 魔法(の類)の発動ロジック、効果、特性 -->
        <logic>呪文を唱えて、まほうを発動する。魔法力(MP)を消費する。</logic>
        <!-- civilzation: 世界の文明 -->
        <civilzations>
            <civilzation>
                <id>DOQ</id>
                <discription>ドラ○エ3の文明</discription>
                <charactor></charactor>
                <structures></structures>
                <technology></technology>
                <arts></arts>
            </civilzation>
        </civilzations>
        <!-- 文化 -->
        <cultures>
            <culture>
                <id>ARIAHAN</id>
                <discription>アリアハン周辺の文化</discription>
                <!-- 生活様式全般 -->
                <life_style>中世の生活様式</life_style>
                <!-- 生活習慣 -->
                <habit>士農工商に分かれて生活、進行するのは神様、協会に祈りを捧げる。</habit>
                <!-- 価値観 -->
                <values></values>
                <!-- 世界観 -->
                <view_of_world></view_of_world>
                <!-- 規範 -->
                <norm></norm>
                <!-- 思考様式 -->
                <way_of_thinking></way_of_thinking>
                <!-- 社会制度 -->
                <social_system>封建社会</social_system>
                <!-- 社会構造 -->
                <social_structure>王族→貴族・聖職者→士族→平民(農民、工業人、商人)</social_structure>
                <!-- 組織 -->
                <organization></organization>
            </culture>
            <culture>
                <id>PORUTOGA</id>
                <discription>ポルトガ周辺の文化</discription>
            </culture>
            <culture>
                <id>ROMARIA</id>
                <discription>ロマリア周辺の文化</discription>
            </culture>
            <culture>
                <id>NORNEAL</id>
                <discription>ノアニール周辺の文化</discription>
            </culture>
        </cultures>
    </world>
    <world>
        <id>arefguld</id>
        <!-- 上記の世界の情報を継承する -->
        <inherit>upperWorld</inherit>
        <!-- name: 世界の名前 -->
        <name>アレフガルド</name>
        <!-- 世界地図(画像ファイル)を上書きする(オーバーライド) -->
        <img>https://img.game8.jp/3808505/5ce596169ba7e3a6386f26ca395aed35.jpeg/show</img>
        <!-- 生息地、生物分布 -->
        <creatures>./Arefgulds.xml</creatures>
        <regions>./Regions.xml</regions>
    </world>
</class>

XMLUtilsクラス

まずは、コードを見てください。XMLファイルを読み込んで、各タグを表示するものです。

public static void loadWorlds(String directory, String fileName) {
    // 空オブジェクトのクラスを取得する
    Class instance = null;
    try {
        Document doc = loadDocumentBuilder(directory, fileName);
        doc.normalizeDocument();
        if (isDebug) System.out.println("Root element: " + doc.getDocumentElement().getNodeName());

            NodeList list = doc.getChildNodes();
            int len = list.getLength();
            int level = 0;
            for (int i = 0; i < len; i++) {
                Node node = list.item(i);
                String s = node.getNodeName();
                if (s.startsWith("#") == false) {
                    if (isDebug) {
                        System.out.println("Lv: " + level);
                        System.out.println("名前: " + s);
                    }
                    if (!"class".equals(s) == false) {
                        System.out.println("XMLの初めは<class>タグで開始してください。");
                        System.exit(-1);
                    }
                    instance = new Object().getClass();
                }
                if (node.hasChildNodes()) {
                    level = level + 1;
                    childNodes(node.getChildNodes(), level);
                }
            }

    } catch (RpgException e) {
        e.printStackTrace();
    }
}
private static void childNodes(NodeList list, int level) {
    int len = list.getLength();
    for (int i = 0; i < len; i++) {
        Node node = list.item(i);
        String s = node.getNodeName();
        if (s.startsWith("#") == false) {
            if (isDebug) {
                System.out.print("*** Lv: " + level);
                System.out.print(" TAG名前: " + s);
            }
            printVlue(level, node);
        }
        if (node.hasChildNodes()) {
            level = level + 1;
            if(isDebug) System.out.print(level + " : " + node.getNodeName());
            childNodes(node.getChildNodes(), level);
            level = level - 1;
        }
        System.out.println();
    }
}
private static void printVlue(int level, Node node) {

    //System.out.print("Level: " + level);
    if (node.hasAttributes()) {
        NamedNodeMap map = node.getAttributes();
        int len = map.getLength();
        for (int i = 0; i < len; i++) {
            Node n = map.item(i);
            if (isDebug) System.out.println(" 属性: " + n.getTextContent());
        }
    }
    if (node.hasChildNodes() && level >= 2)  {
        if (!"".equals(node.getFirstChild().getTextContent().trim())) {
            if (isDebug) System.out.println("Node : " + node.getFirstChild().getTextContent());
        }
    }

}

この表示している項目を取得して、各設定クラス(オブジェクト)にセットしアプリケーションをコントロールできるようにしたいと思います。

XMLファイルのロード(読込)

XMLファイルを読み込む処理ができました。テストをしないと、ちゃんと動いているかわからないので、テストしました。
テストの実装方法は、こちらの記事に記載しています。JUnitです。

テストコード

    @Test
    public void testCreateConfig() {
        String[] filesA = new String[] {"Worlds.xml", "Regions.xml", "Creatures.xml"};
        for (String fileName : filesA) {
            try {
                StoryConfig c = (StoryConfig) XmlUtils.createConfig(fileName);
                assertTrue(c instanceof ConfigIF);
            } catch (Exception e) {
               e.printStackTrace();
               fail("file: " + fileName);
            }
        }

        String[] filesB = new String[] {"MonsterType.xml", "Job.xml", "Commands.xml", "STM.xml", "Effects.xml"};
        for (String fileName : filesB) {
            try {
                ConfigIF c = (ConfigIF) XmlUtils.createConfig(fileName);
                assertTrue(c instanceof ConfigIF);
            } catch (Exception e) {
                e.printStackTrace();
                fail("file: " + fileName);
            }
        }

    }

テスト対象メソッド

クラスはXMLUtilsです。

   /**
     * 各種設定ファイルを読み込むときに、ファイル名に対応した
     * クラスを生成して返却する。
     *
     * @param fileName
     * @return ConfigRpptを継承しているクラス
     */
    public static ConfigIF createConfig(String fileName) {
        ConfigIF conf = null;
        switch (fileName) {
            case StoryConfig.WORLD_XML:
                conf = new World();
                break;
            case StoryConfig.NATURE_XML:
                conf = new Nature();
                break;
            case StoryConfig.CREATURE_XML:
                conf = new Creature();
                break;
            case StoryConfig.REGIONS_XML:
                conf = new Region();
                break;
            case StoryConfig.EFFECTS_XML:
                conf = new Effects();
                break;
            case StoryConfig.STM_XML:
                conf = new STM();
                break;
            case StoryConfig.CPMMADS_XML:
                conf = new Command();
                break;
            case StoryConfig.JOB_XML:
                conf = new Job();
                break;
            case StoryConfig.MONSTER_TYPE_XML:
                conf = new MonsterType();
            case StoryConfig.MONSTERS_XML:
                /** TODO-[MonsterクラスはPlayerクラスを継承する] */
                //conf = new Monster();
                break;
            default:
                System.out.println("想定外の設定ファイル名です。 : " + fileName);
                System.exit(-1);
        }
        return conf;
    }

とりあえずは、XMLファイルのロード時に、対象のクラスをインスタンス化する処理を実装しました。

まとめ

ここまで作ったら、文字列が表示できることを確認する必要があります。
つまりは、RpgProgressの処理が動いた後に出力する文字列が画面に表示できないことには始まりませんので。。。
結局、ユーザー入力は、ポップアップメニューを使用することにしました。
以下のような処理順序です。

  1. ゲームを起動する
  2. タイトルの表示
  3. ゲーム開始:この時にRpgProgressを実行して、常に出力する文字列を表示しているテキストエリアに出力する形をとるようにします。

投稿者:

takunoji

音響、イベント会場設営業界からIT業界へ転身。現在はJava屋としてサラリーマンをやっている。自称ガテン系プログラマー(笑) Javaプログラミングを布教したい、ラスパイとJavaの相性が良いことに気が付く。 Spring framework, Struts, Seaser, Hibernate, Playframework, JavaEE6, JavaEE7などの現場経験あり。 SQL, VBA, PL/SQL, コマンドプロント, Shellなどもやります。

コメントを残す