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

人工知能に関する断創録

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



流れるメッセージ

メッセージウィンドウをもう少し改造してドラクエのようにメッセージが流れて表示されるようにします。TimerTaskを使って1文字ずつ順に表示するようにすれば意外と簡単にできます。

rpg17.jar

TimerTaskの起動

描画部でメッセージをcurPos文字まで表示するとします。このcurPosをTimerTask(定期的に処理を実行する)を使って1文字ずつ増やします。これを連続的に行うとメッセージが流れるような効果が出せます。今回は50ミリ秒ごとにcurPosをインクリメント(1ずつ増やす)しています。TimerTaskはメッセージをセットするときに起動します。schedule()で直後(0L)に起動してから50ミリ秒(50L)ごとに taskを実行するようにしています。taskはDrawingMessageTaskです。

    /**
     * メッセージをセットする
     * @param msg メッセージ
     */
    public void setMessage(String msg) {

        // メッセージをセット
        
        task = new DrawingMessageTask();
        timer.schedule(task, 0L, 50L);
    }

DrawingMessageTaskはcurPosをインクリメントするタスクです。

    // メッセージを1文字ずつ順に描画するタスク
    class DrawingMessageTask extends TimerTask {
        public void run() {
            if (!nextFlag) {
                curPos++;  // 1文字増やす
                // 1ページの文字数になったら▼を表示
                if (curPos % MAX_CHAR_IN_PAGE == 0) {
                    nextFlag = true;
                }
            }
        }
    }

もし1ページ分表示し終わったnextFlagをtrueにセットします。nextFlagがtrueになると▼が表示されます。

1文字ずつ表示

次に文字を描画するdraw()です。

    public void draw(Graphics g) {
    
        // 枠を描く
        
        // 現在のページ(curPage)のcurPosまでの内容を表示
        for (int i=0; i<curPos; i++) {
            char c = text[curPage * MAX_CHAR_IN_PAGE + i];
            int dx = textRect.x +
                     MessageEngine.FONT_WIDTH *
                     (i % MAX_CHAR_IN_LINE);
            int dy = textRect.y +
                     (LINE_HEIGHT + MessageEngine.FONT_HEIGHT) *
                     (i / MAX_CHAR_IN_LINE);
            messageEngine.drawCharacter(dx, dy, c, g);
        }

        // 最後のページでない場合は矢印を表示する
        if (curPage < maxPage && nextFlag) {
            int dx = textRect.x +
                     (MAX_CHAR_IN_LINE / 2) *
                     MessageEngine.FONT_WIDTH - 8;
            int dy = textRect.y +
                     (LINE_HEIGHT + MessageEngine.FONT_HEIGHT) * 3;
            g.drawImage(cursorImage, dx, dy, null);
        }
    }

curPosまでしか表示しないのがミソです。このcurPosは50ミリ秒ごとにTimerTaskによって1ずつ増やされます。&& nextFlagは文章が流れている間は矢印が表示されないようにするために必要です。ためしにとって見るとなぜ必要かわかります。

TimerTaskの停止

最後にTimerTaskの停止です。TimerTaskはほっとくとずっと動き続けるので注意が必要です。

    /**
     * メッセージを先に進める
     * @return メッセージが終了したらtrueを返す
     */
    public boolean nextMessage() {
        // 現在ページが最後のページだったらメッセージを終了する
        if (curPage == maxPage) {
            task.cancel();
            task = null;
            return true;
        }
        // ▼が表示されてなければ次ページへいけない
        if (nextFlag) {
            curPage++;
            curPos = 0;
            nextFlag = false;
        }
        return false;
    }

メッセージをすべて表示し終わったらTimerTaskをcancel()してnullをセットしておけばタスクは止まります。ドラクエのメッセージを見てみるとこの実装でもまだ少し違います。ドラクエはページという概念がなくて1行ずつ上に流れてるんですね。いつかやってみます。