読者です 読者をやめる 読者になる 読者になる

人工知能に関する断創録

人工知能、認知科学、心理学、ロボティクス、生物学などに興味を持っています。このブログでは人工知能のさまざまな分野について調査したことをまとめています。最近は、機械学習、Deep Learning、Kerasに関する記事が多いです。



オブジェクト化

今まで作ってたプログラムはMainPanelクラスにすべての機能をつぎ込んでいました。今回はこれらの機能を別々のクラスに分割してJavaの特徴であるオブジェクト指向をフル活用していきます。

今回はキャラクターを表すCharaクラスとマップを表すMapクラスを作ります。MainPanelにあったキャラクター関連の機能をChara クラスにマップ関連の機能をMapクラスに移動させました。

このようにクラスとして独立させておくとプログラムの構造も把握しやすくなるという利点があります。またプログラムの拡張性も飛躍的に高まります。たとえば今までキャラクターは勇者しかいませんでした。新しく王様、兵士を追加したい場合どうすればいいでしょう?Charaクラスを作っておくと王様、兵士のインスタンスを作ればいいだけなので非常に楽になります。

rpg07.jar

Charaクラス

まずキャラクターに関する機能をMainPanelから独立させます。キャラクターに関する情報としてイメージ、位置、向いている方向などが考えられます。またキャラクターに関する機能として移動、描画、アニメーションなどが考えられます。どの情報・機能をキャラクターに持たせるかは人によって異なりますのでこの方法が一番いいというわけではありません。

インスタンス変数やメソッドの中身は前回とほとんど変わらないので詳しい説明は省きます。詳しくはソースファイルを見てください。ここでは特徴的な部分だけ解説します。

まずクラスを作る場合に必要なのがコンストラクタです。コンストラクタは次のような形になってます。

    public Chara(int x, int y, String filename, Map map);

まずキャラクターの位置を(x,y)でイメージファイルのファイル名をfilenameで指定できるようにしました。このようにしておくと Charaクラスからさまざまなキャラクターを作り出せます。たとえば、勇者を作りたかったら

    Chara hero = new Chara(1, 1, "hero.gif", map, this);

で(1,1)の位置に勇者があらわれます(hero.gif は勇者のイメージファイル名です)。王様を作りたかったら

    Chara king = new Chara(5, 3, "king.gif", map, this);

で(5,3)の位置に王様があらわれます(king.gif は王様のイメージファイル名です)。このようにクラスを使うと楽にキャラクターを増やすことができます。実際に王様を登場させるのは今度やります。

コンストラクタの引数にmapやthisがあるのに気づいたでしょうか?mapはキャラクターが乗るMapオブジェクト、thisは MainPanelオブジェクトです。キャラクターがmapやMainPanelオブジェクトを持っているのは変だ!と思われるかもしれませんが、 Charaクラス内でmapやMainPanelオブジェクトを参照しなければならないため渡す必要があります。たとえば、mapはキャラクターが移動するときに障害物があるかどうか調べるとき使っています。

    public void move(int dir) {
        // dirの方向でぶつからなければ移動する
        switch (dir) {
            case LEFT:
                if (!map.isHit(x-1, y)) x--;
                direction = LEFT;
                break;
            case RIGHT:
                if (!map.isHit(x+1, y)) x++;
                direction = RIGHT;
                break;
            case UP:
                if (!map.isHit(x, y-1)) y--;
                direction = UP;
                break;
            case DOWN:
                if (!map.isHit(x, y+1)) y++;
                direction = DOWN;
                break;
        }
    }

isHit()はMapクラスに移した(後で解説します)のでmap.isHit()のように呼び出す必要があります。このときCharaクラスで mapが必要になるのです。またMainPanelオブジェクトはAnimationThreadでカウンタを切り替えた後再描画するときに使ってます。このテクニックはよく使うのでなれておくといいです。

Mapクラス

マップに関する情報として行数、列数、壁や床のイメージなどを移しました。またマップに関する機能として障害物のチェック、描画を移しました。

Mapを独立したクラスにしておくとラダトーム城のマップ、ラダトームの町のマップ、ロトの洞窟のマップというようにさまざまなマップを簡単に作り出せます。ただ今のままだとマップの中身をmap配列で固定してしまっているのでこういうことはできません。マップの中身を指定してオブジェクトを作れるようにする必要があります。

MainPanelクラス

キャラクターとマップ関連の機能をごっそり移したのでMainPanelはずいぶんシンプルになります。MainPanelに残ったのはキーボードを受け付ける処理、画面へキャラクターやマップを描画する処理のみになりました。そして一番大事な部分がMainPanelに付け加わります。キャラクターやマップオブジェクトを作る処理です。

    // マップ
    private Map map;
    // キャラクター(勇者)
    private Chara hero;
    
    ・・・
    
    // マップを作成
    map = new Map(this);
    // 勇者を作成
    hero = new Chara(1, 1, "image/hero.gif", map);

CharaやMapはクラス(雛形)であり、実際にキャラクターやマップが作られるのはMainPanel上になります。heroやmapは次のようにドット記法でメソッドを呼び出します。今まではdrawMap()、drawChara()、goLeft()のように呼び出していましたが、今度はオブジェクトを指定してメソッドを呼び出せるようになった点に注意してください。

    map.draw(offG);  // mapを描く
    hero.draw(g);    // heroを描く
    hero.goLeft();   // heroを左に移動する
    hero.goRight();  // heroを右に移動する