人工知能に関する断創録

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

メッセージウィンドウの表示

今回はメッセージウィンドウを表示させてみます。ドラクエのように黒い背景に白枠のウィンドウです。ウィンドウが表示されている間のキー処理についても解説します。

rpg15.jar

メッセージウィンドウの表示

メッセージウィンドウはMessageWindowクラスで実装されています。まずは、使い方から見ていきます。MainPanelを見てください。

    // ウィンドウ
    private MessageWindow messageWindow;
    // ウィンドウを表示する領域
    private static Rectangle WND_RECT =
        new Rectangle(62, 324, 356, 140);
    
    public MainPanel() {
        ・・・
        // ウィンドウを追加
        messageWindow = new MessageWindow(WND_RECT);
    }

メッセージウィンドウ(MessageWindow)と表示する領域WND_RECTを定義しています。MessageWindowを追加しても画面には表示されません。MessageWindowの中でisVisible = false、つまり、初期状態では表示されないようにしているからです。表示するのは、スペースキーを押したときですね。

    if (spaceKey.isPressed()) {  // スペース
        // 移動中は表示できない
        if (hero.isMoving()) return;
        if (!messageWindow.isVisible()) {  // メッセージウィンドウを表示
            messageWindow.show();
        }
    }

スペースキーを押すとshow()でウィンドウを表示します。ただし、勇者が移動中はウィンドウを表示できなくしてあります。あとメッセージウィンドウの表示中はキャラクターが移動しないようにしてあります。つまり、下のように表示してないときだけ移動します。

    if (!messageWindow.isVisible()) {
        // 勇者の移動処理
        heroMove();
        // キャラクターの移動処理
        charaMove();
    }

メッセージウィンドウの描画

メッセージウィンドウの構造を詳しく見ていきます。

    public MessageWindow(Rectangle rect) {
        this.rect = rect;

        innerRect = new Rectangle(
                rect.x + EDGE_WIDTH,
                rect.y + EDGE_WIDTH,
                rect.width - EDGE_WIDTH * 2,
                rect.height - EDGE_WIDTH * 2);
    }

    public void draw(Graphics g) {
        if (isVisible == false) return;
        
        // 枠を描く
        g.setColor(Color.WHITE);
        g.fillRect(rect.x, rect.y, rect.width, rect.height);

        // 内側の枠を描く
        g.setColor(Color.BLACK);
        g.fillRect(innerRect.x, innerRect.y,
                   innerRect.width, innerRect.height);
    }

MessageWindowの引数にはメッセージウィンドウの位置と大きさを設定したWND_RECTが渡されるのでrectに設定します。 innerRectはドラクエのように白い枠線ができるための仕掛けです。メッセージウィンドウ全体を白く塗っておいて少し(EDGE_WIDTH)内側の四角形(innerRect)を黒く塗りつぶせば白い枠線がある黒いウィンドウができます。draw()を見てください。下図のようなイメージです。

f:id:aidiary:20100404102612g:plain

キー入力の横取り

メッセージウィンドウが表示中は別のキー処理をする必要があります。フィールド画面でスペースキーを押すとメッセージウィンドウが表示されますがメッセージウィンドウが表示された状態では次のメッセージへいったり、ウィンドウを閉じたりする必要があります。どのウィンドウが表示されているかで異なるキー処理をする必要があります。これをどうやるかすごく悩んだのですが次のようにしました。

    public void run() {
        while (true) {
            // キー入力をチェックする
            if (messageWindow.isVisible()) {  // メッセージウィンドウ表示中
                messageWindowCheckInput();
            } else {  // メイン画面
                mainWindowCheckInput();
            }
            
            ・・・
        }
    }

run()はゲームループです。ゲームループ内でキー入力を調べますが、メッセージウィンドウが表示されている(isVisible()がtrue を返す)間はmessageWindowCheckInput()、表示されていない間はmainWindowCheckInput()を呼び出しています。mainWindowCheckInput()は前回までのcheckInput()と同じメソッドです。ウィンドウが表示されているかいないかで異なるメソッドを呼び出すのがミソです。メッセージウィンドウが表示されているときのキー処理を行うmessageWindowCheckInput()です。

   /**
     * メッセージウィンドウでのキー入力をチェックする
     */
    private void messageWindowCheckInput() {
        if (spaceKey.isPressed()) {
            messageWindow.hide();
        }
    }

スペースキーが押されたらメッセージウィンドウを閉じています。この方法はコマンドウィンドウなどたくさんウィンドウが出てくると管理がややこしくなりそうです。どのウィンドウが一番上にあるかを管理するスタック(後入れ後出しのデータ構造)を用意しておく方式がよいかもしれません。