人工知能に関する断創録

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

マップ間移動

複数のマップを移動できるようにします。王様の部屋の階段

f:id:aidiary:20100410211846g:plain

を下りるとフィールドマップへ移動します。階段を下るとそこはフィールドマップだったって感じです。1ルームとはずいぶんしけた城ですね(笑)

rpg20.jar

移動イベント

まずはマップを移動するイベントを作ります。マップが複数になるのでマップ番号を導入します。たとえば、お城のマップは0番、フィールドマップは1番というふうに。移動イベントは座標(x,y)に移動するとマップdestMapNo番の座標(destX,destY)に移動するというイベントです。MoveEventを見てください。

public class MoveEvent extends Event {
    // 移動先のマップ番号
    public int destMapNo;
    // 移動先のX座標
    public int destX;
    // 移動先のY座標
    public int destY;
    
    public MoveEvent(int x, int y, int chipNo, int destMapNo,
                     int destX, int destY) {
        // 上に乗ると移動するようにしたいのでぶつからない(false)に設定
        super(x, y, chipNo, false);
        this.destMapNo = destMapNo;
        this.destX = destX;
        this.destY = destY;
    }
    
    public String toString() {
        return "MOVE:" + super.toString() + ":" + destMapNo + ":" + destX + ":" + destY;
    }
}

元座標(x,y)、移動先マップ番号destMapNo、移動先座標(destX,destY)を記録しています。chipNoはマップ上に表示されるイメージです。たとえば、今回は下り階段の上に乗るとフィールドへ移動するので下り階段のチップ番号を指定します。元マップのマップ番号はいらないことに注意してください。イベントはマップごとのイベントファイルに保存されるためです。たとえば、王様の部屋のイベントはking_room.evt、フィールドのイベントはfield.evtに記録されます。king_room.evtです。

   MOVE,11,11,15,1,8,7

王様の部屋の座標(11,11)に乗るとマップ番号1(フィールド)の座標(8,7)へ移動する。チップ番号は15(下り階段)を使うという意味です。Mapクラスではイベントファイルを読み込んでMoveEventを生成しています。イベントのロードは宝箱や扉と同じなので省略します。

マップ配列

マップを管理しているのはMainPanelです。今までマップはMainPanelクラスで

    // マップ
    private Map map;

    // マップを作成
    map = new Map("map/map.dat", "event/event.dat", this);

のように使ってきました。Mapクラスのオブジェクト1つが1つのマップを表しています。では複数のマップを使いたいときはどうすればいいでしょう?そうMapオブジェクトの配列を使えばいいですね。

    // マップ
    private Map[] maps;

    // マップを作成
    maps = new Map[2];
    // 王の間
    maps[0] = new Map("map/king_room.map", "event/king_room.evt", this);
    // フィールド
    maps[1] = new Map("map/field.map", "event/field.evt", this);

さっき話したマップ番号は配列の添え字です。王の間は0でフィールドは1ですね。マップが2つになってしまったので勇者が現在どのマップにいるのか保存しておく必要があります。

    // 現在のマップ番号
    private int mapNo;

    // 最初は王の間
    mapNo = 0;

マップがmapからmaps[]に変わったので今までmapと書いていたところはすべてmaps[mapNo]に変更する必要があるので注意してください。

マップ間移動

では最後にマップ間移動の実装を見てみます。移動処理はMainPanelのheroMove()です。

    /**
     * 勇者の移動処理
     */
    private void heroMove() {
        // 移動(スクロール)中なら移動する
        if (hero.isMoving()) {
            if (hero.move()) {  // 移動(スクロール)
                // 移動が完了した後の処理はここに書く

                // 移動イベント
                // イベントがあるかチェック
                Event event = maps[mapNo].eventCheck(hero.getX(),
                                                     hero.getY());
                if (event instanceof MoveEvent) {  // 移動イベントなら
                    MoveEvent m = (MoveEvent)event;
                    // 移動元マップの勇者を消去
                    maps[mapNo].removeChara(hero);
                    // 現在のマップ番号に移動先のマップ番号を設定
                    mapNo = m.destMapNo;
                    // 移動先マップでの座標を取得して勇者を作り直す
                    hero = new Chara(m.destX, m.destY, 0, DOWN,
                                     0, maps[mapNo]);
                    // 移動先マップに勇者を登録
                    maps[mapNo].addChara(hero);
                }
            }
        }
    }

heroMove()は勇者の移動処理を書いていたところです。前回まではかなり単純なメソッドでした。ここで勇者のスクロール移動が完了した(1 歩移動が完了した)ときの処理を追加します。マップ間移動イベントは1歩の移動が完了したあと発動させたいからです。勇者(hero)も一般ピープルとまったく同じ扱いでMapのcharasベクトルに登録されていたことを思い出してください。charasベクトルはこのマップにいるキャラクターが登録されているベクトルです。勇者が別のマップに移動するときは元マップのcharasベクトルから削除して移動先のマップのcharasベクトルに登録し直す必要があります。さあこれで広大な世界へ旅立てますね。えっ?ラダトーム城?何のことですか?

追記

この手法はメモリ効率が悪いと思われます。ゲームが始まる前にすべてのマップとマップ内のイベントをメモリにロードしているからです。PS2の多くのゲームではマップが切り替わるたびにNow Loading...って出てきますよね?マップを切り替えるときにマップ・イベントをロードするという方が普通かもしれません。でも切り替え遅いといらつくんですよね。これが。あらかじめ読み込んでおくか、切り替えるたびに読み込むかってのはメモリとスピードのトレードオフの問題です。