人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

マップの読み込み

ファイルからマップを読み込めるようにしてみます。

rpg11.jar

マップファイルの構造

マップファイル(map.dat)の中身は次のようになってます。

16
16
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111000000001111
1111022222201111
1111020220201111
1111000000001111
1111000000001111
1111000000001111
1111111011111111
1111000000001111
1111111111111111
1111111111111111
1111111111111111
1111111111111111

1行目はマップの行数、2行目はマップの列数です。前回までは行数と列数が

    // 行数(単位:マス)
    public static final int ROW = 20;
    // 列数(単位:マス)
    public static final int COL = 30;

のように固定されていましたが定数から変数にしてマップファイル読み込み時に変えられるようにしました。

    // マップの行数・列数(単位:マス)
    private int row;
    private int col;

3行目からがマップになっています。前回までは床0と壁1だけでしたが、玉座

f:id:aidiary:20100327200915g:plain

として追加しました。実行したときのマップと見比べてみてください。これでマップをファイルから読み込めるようになりましたが、大きいマップになるとmap.datを自分で書くのは非常に面倒です。あとでマップエディタを作ってmap.datファイルを簡単に作れるようにしてみます。

マップの読み込み

マップを読み込んでいる部分がMapクラスのload()です。filenameでマップファイル(ここではmap/map.dat)を指定するとマップデータを読み込んでmap配列に展開します。

    /**
     * ファイルからマップを読み込む
     * @param filename 読み込むマップデータのファイル名
     */
    private void load(String filename) {
        try {
            BufferedReader br = new BufferedReader(
                new InputStreamReader(
                    getClass().getResourceAsStream(filename)));
            // rowを読み込む
            String line = br.readLine();
            row = Integer.parseInt(line);
            // colを読む
            line = br.readLine();
            col = Integer.parseInt(line);
            // マップサイズを設定
            width = col * CS;
            height = row * CS;
            // マップを作成
            map = new int[row][col];
            for (int i=0; i<row; i++) {
                line = br.readLine();
                for (int j=0; j<col; j++) {
                    map[i][j] = Integer.parseInt(line.charAt(j) + "");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

最初の行数と列数は1行まるごと読み込みます。これでは文字型Stringなので、Integer.parseInt()で整数型に変換します。行数と列数が読み込めたらマップの全体サイズ(単位:ピクセル)widthとheightを計算します。あとは1マスごとにデータを読み込みます。1行分のデータを読み込み、charaAt()で1文字ずつ取り出し、これを整数型に変換してmapに代入します。 JARからテキストファイルを読み込むテキストファイルから文字列を読み込むBufferedReaderの使い方は少し注意してください。普通は、

    BufferedReader br = new BufferedReader(new FileReader(filename));

のようにFileReader()を使います。ただこれではJARにまとめたときうまく動作しません。JAR内にまとめられたテキストファイルを読み込むには、

    BufferedReader br = 
        new BufferedReader(
            new InputStreamReader(
                getClass().getResourceAsStream(filename)));

のようにInputStreamReader()とgetResourceAsStream()を使う必要があります。

変更部分

その他の変更部分を簡単に解説します。行数と列数が定数から変数になったので他のクラスから取得できるようにgetRow()とgetCol()を追加しました。またマップ全体の大きさを返すgetWidth()とgetHeight()も追加しました。

    public int getRow() {
        return row;
    }

    public int getCol() {
        return col;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

あとマップチップとして玉座を追加したため玉座のイメージを読み込む部分と描画する部分が追加されています。また、玉座はぶつかるようにしたいため isHit()の条件を追加しています。

    /**
     * (x,y)にぶつかるものがあるか調べる。
     * @param x マップのx座標
     * @param y マップのy座標
     * @return (x,y)にぶつかるものがあったらtrueを返す。
     */
    public boolean isHit(int x, int y) {
        // (x,y)に壁か玉座があったらぶつかる
        if (map[y][x] == 1 || map[y][x] == 2) {
            return true;
        }
        
        // なければぶつからない
        return false;
    }

これで自由にマップを作れるようになりました。ただスクロール処理の実装上16×16より小さいマップだと動作がおかしくなってしまいます。

追記

ここで取り上げたマップの作り方はいい方法ではありません。マップをテキストファイルとして保存すると各マスは0〜9までの値しか取れないからです。つまり10種類のマップチップしか使えないってことです。これを解決するにはマップをバイナリ形式で保存すればいいです。バイナリ形式では各マスを1バイトとして保存します。1バイトは0〜255までの 256種類の値を取れるので256種類のマップチップが使えることになります。ただし、バイナリ形式のマップはメモ帳で読み書きすることはできませんし、直感的に修正するのも難しくなります。そんなときはマップエディタを作りましょう。マップエディタ編を参考にしてください。