TypeScriptでゲームプログラム9「クラス化でメッセージを管理する」

メッセージ管理クラスの追加

<div id="message">(以後これをメッセージボックスと呼ぶことにする)には、現在の得点やゲームオーバーのメッセージなど表示しています。

しかし、メッセージボックスの変更は複数の場所で行っています。現在は数が少ないので大丈夫ですが、このまま、メッセージボックスを変更する場所があちこちに増えてくると、不具合の温床になりかねません。

そこでメッセージボックスを管理するクラスを追加し、表示処理を統一します。

/**
 * <div id="message">を管理するクラス
 */
class MessageMng {
    /**
     * id="message"に対するDIV要素
     */
    private _element: HTMLDivElement | undefined;

    /**
     * コンストラクタ
     */
    constructor() {
        document.addEventListener("DOMContentLoaded", () => {
            let element = document.getElementById("message");
            if (element instanceof HTMLDivElement) {
                this._element = element;
            }
        });
    }

    /**
     * テキストを変更する。
     * @param text 
     */
    public ChangeText(text: string) {
        if (this._element) {
            this._element.innerText = text;
        }
    }
}

let Message = new MessageMng();

これまでメッセージボックスの変更は、ChangeMessage()で行っていましたが、Message.ChangeText()でも同様に変更できます。(単なる置き換えなので、コードは省略。)

しかし、このまま置き換えるだけでは表示処理の統一にはなりませんので、もう少しコードを整理します。

タイミングを合わせる

  • 球を表示する:プレイヤーに球の場所を教えるため。
  • ラケットを表示する:プレイヤーにラケットの場所を教えるため。
  • メッセージボックスを表示する:プレイヤーにゲームの状況を教えるため。

このように考えると、メッセージボックスは球やラケットと同じように処理できるはずです。そうすることで、プログラムの流れが統一されます。

つまり、次のように球やラケットとメッセージボックスの処理のタイミングを合わせたコードにしたいと考えました。

function GameLoop(timestamp: number) {
  // 変更のない部分は省略
    if (frame > FrameCounter) {
        // ゲームの状況を更新する。
        UpdateGameSituation();
        // メッセージボックスを更新する。
        Message.Update();

        // フレーム数が更新されたので、描画を行う。
        DrawGameCanvas();
        // メッセージボックスを表示する。
        Message.Show();
    }
}

メッセージボックスに表示する内容の整理

メッセージボックスには、スコアとゲームの状態を表示しています。そして、ゲーム状態(GameStatus)によって内容を切り替えています。

整理すると次のようになっていました。

GameStatus表示内容
startボタンを押すとゲーム開始
gameスコア
stopポーズ中であること
ボタンを押すと再開
gameoverゲームオーバーしたこと
最終スコア
ボタンを押すと次のゲームを開始
GameStatusとメッセージ

したがって、Message.Update()はGameStatusによって表示内容を更新すればよいです。

MessageMngの実装例

最終的なMessageMngクラスのコードを以下に示します。

/**
 * メッセージボックス(<div id="message">)を管理するクラス
 */
class MessageMng {
    /**
     * id="message"に対するDIV要素
     */
    private _element: HTMLDivElement | undefined;

    /**
     * innerTextに表示するテキスト
     */
    private _text = "";

    /**
     * コンストラクタ
     */
    constructor() {
        document.addEventListener("DOMContentLoaded", () => {
            let element = document.getElementById("message");
            if (element instanceof HTMLDivElement) {
                this._element = element;
            }
            else{
                console.log("id=\"message\"のdiv要素がありません。");
            }
        });
    }

    /**
     * メッセージボックスを更新する。
     */
    public Update() {
        switch (GameStatus) {
            case "start":
                this._text = "ボタンを押すとゲームを開始します。";
                break;
            case "game":
                this._text = "スコア:" + Score;
                break;
            case "stop":
                this._text = "ゲームポーズ中\nボタンを押すと再開します。";
                break;
            case "gameover":
                this._text = "ゲームオーバー\n" +
                    "あなたのスコアは" + Score + "でした。\n" +
                    "ボタンを押すと次のゲームを開始します。";
                break;
        }
    }

    /**
     * メッセージボックスを表示する。
     */
    public Show() {
        if (this._element) {
            this._element.innerText = this._text;
        }
    }
}

Message.ChangeText()は不要になったので、削除しました。

ChangeMessage()も削除し、それを使用していたコードも下に示すように、すっきりすることができました。

//変更前
document.addEventListener("DOMContentLoaded", () => {
    let button1 = document.getElementById("button1");
    if (button1) {
        button1.addEventListener("click", () => {
            switch (Game.Status) {
                case "game":
                    Game.Status = "stop";
                    ChangeButton1Text("スタート");
                    ChangeMessage("ゲームポーズ中\nボタンを押すと再開します。");
                    break;
                case "stop":
                    Game.Status = "game";
                    ChangeButton1Text("ストップ");
                    ChangeMessage("");
                    break;
                case "start":
                case "gameover":
                    Game.Status = "game";
                    InitGameSituation();
                    ChangeButton1Text("ストップ");
                    ChangeMessage("");
                    break;
            }
        })
    }
});

// 変更後
document.addEventListener("DOMContentLoaded", () => {
    let button1 = document.getElementById("button1");
    if (button1) {
        button1.addEventListener("click", () => {
            switch (GameStatus) {
                case "game":
                    GameStatus = "stop";
                    ChangeButton1Text("スタート");
                    break;
                case "stop":
                    GameStatus = "game";
                    ChangeButton1Text("ストップ");
                    break;
                case "start":
                case "gameover":
                    GameStatus = "game";
                    InitGameSituation();
                    ChangeButton1Text("ストップ");
                    break;
            }
        })
    }
});

ボタン1管理クラスの追加

ボタン1のテキストを変更する処理も目的はメッセージボックスと同じです。それならば同様にしたほうがよいでしょう。そこで、ボタン1の管理クラスを追加します。

/**
 * ボタン1(<button id="button1">)を管理するクラス
 */
class Button1Mng {
    /**
     * id="message"に対するDIV要素
     */
    private _element: HTMLButtonElement | undefined;

    /**
     * innerTextに表示するテキスト
     */
    private _text = "";

    /**
     * コンストラクタ
     */
    constructor() {
        document.addEventListener("DOMContentLoaded", () => {
            let element = document.getElementById("button1");
            if (element instanceof HTMLButtonElement) {
                this._element = element;
            }
            else {
                console.log("id=\"button1\"のbutton要素がありません。");
            }
        });
    }

    /**
     * メッセージボックスを更新する。
     */
    public Update() {
        switch (GameStatus) {
            case "start":
                this._text = "スタート";
                break;
            case "game":
                this._text = "ストップ";
                break;
            case "stop":
                this._text = "スタート";
                break;
            case "gameover":
                this._text = "NEXTゲーム";
                break;
        }
    }

    /**
     * メッセージボックスを表示する。
     */
    public Show() {
        if (this._element) {
            this._element.innerText = this._text;
        }
    }
}

let Button1 = new Button1Mng();

そして、Button1.Update()とButton1.Show()をMessageのときと同じように実行います。また、既存コードのChangeButton1Text()を削除します。(変更は簡単なため、ソースの記載は省略する。)

コメント