thumbnail
thumbnail

【Phaser3入門】メッセージウィンドウ

updated 2022-4-24

環境

Phaser 3.55.2

メッセージウィンドウとは

主人公や語り部、登場人物が文字を使用して物語を進めるために必要なテキストエリアのこと
誰が話しているか、何を話しているかが伝わることが大事

種類

二種類あって

語り部の上に現れるタイプ
固定で(主に下半分に)表示されるタイプ

頭上タイプ

  • 誰が話しているか分かりやすい
  • 動きがあって面白い
  • 移動するため内容が頭に入りにくい

固定タイプ

  • 見る位置が変わらないので頭に入りやすい
  • 絵や名前がないと誰が話しているか分かりにくい
  • 単調なので飽きやすい

スタイル

文字

Phser3で決められるのはTextStyleで設定できる項目のみ
設定したい項目は設定されていたので嬉しい
公式サンプル

TextStyle

公式Doc

PropertyTypeSampleAbout
fontFamilystring"yusei, sans-serif"this.load.scripts("yusei", ["assets/scripts/webfont.js"]); Webフォントなどロードした名前で呼ぶ, フォントの種類
fontSizestring"32"CSSと同じだけど文字列, 文字の大きさ
fontStylestring"strong"CSSと同じ文字列, 文字の太さ
backgroundColorstring"#95A69C"CSSと同じ, 文字の背景色
colorstring"#B7BF8E", "rgb(218, 193, 137)"CSSと同じ, 文字の色
strokestring"#222A00", "rgb(59, 44, 0)"CSSと同じ, 文字の縁の色
strokeThicknessnumber3CSSと同じ, 文字の縁の太さ
shadowTextShadow{offsetX: 2, offsetY: 1, color: "#000", blur: 2, stroke: true, fill: true}公式 TextShadow
paddingTextPadding{ x: 10, y: 5 }公式 TextPadding
alignstringleft,right,center,justify文字をどちらに寄せるか
maxLinesnumber3何行目まで表示させるか
rtlbooleanfalsefalseだと右から、trueだと左から
baseLineXnumber1.2ベースライン, CSSと同じ
baseLineYnumber1.4ベースライン, CSSと同じ
wordWrapTextWordWrapTextWordWrap

枠として使うのであれば上記のものから選ぶのが良いかと考えた

実装

一番難易度の低いものから実装するため、下記を選択した

固定表示
Rectangleの枠

windowクラスを用意して、話すオブジェクト(キャラクター)のクラス内に持たせる
文字と四角を含むため、Containerの中に入れる

import Phaser from "phaser";

export class Window extends Phaser.GameObjects.Container {

    private _width: integer;
    private _height: integer;
    private _margin: integer;
    private _padding: integer;

    private _msg: string;
    
    private box: Phaser.GameObjects.Graphics;
    private text: Phaser.GameObjects.Text;

    constructor(scene: Phaser.Scene, x = 0, y = 0, width = 0, height = 0) {
        super(scene, x, y);

        if (width) {
            this._width = width;
        } else {
            this._width = typeof scene.game.config.width == "string" ? parseInt(scene.game.config.width): scene.game.config.width;
        }

        if (height) {
            this._height = height;
        } else {
            this._height = typeof scene.game.config.height == "string" ? parseInt(scene.game.config.height): scene.game.config.height;
        }
        this._margin = 10;
        this._padding = 10;

        this.box = new Phaser.GameObjects.Graphics(scene);
        this.box.fillStyle(0x0000ff, 1);
        this.box.fillRoundedRect(this._margin, this._margin, this._width - this._margin * 2, this._height - this._margin * 2, this._padding / 2);
        this.box.lineStyle(2, 0xffffff, 1);
        this.box.strokeRoundedRect(this._margin, this._margin, this._width - this._margin * 2, this._height - this._margin * 2, this._padding / 2);
        this.add(this.box);

        this._msg = "";
        this.text = new Phaser.GameObjects.Text(scene, this._padding + this._margin, this._padding + this._margin, this._msg, { fontSize: "20px", color: "#fff" });
        this.add(this.text);

        scene.add.existing(this);
    }

    setMessage(text: string) {
        this._msg = text;
        this.text.text = this._msg;
    }

    getMessage() {
        return this._msg;
    }
}

問題

Phaser3にはwordWrapというものが用意されていて、簡単に実装するとWidthで改行
なのだけれど、日本語にはスペースが無いせいでうまく改行されないので, wordWrapのcallback内で改行する必要がある
公式 wordwrap callback

new Phaser,GameObject.Text(scene, 0, 0, "これはサンプルのテキストでなんの意味もありませんがとても長いため改行が必要です。", {
    wordWrap: { callback: callbackWordWrap, scope: this });

function callbackWordWrap(text) {
    let windowWidth = 400;
    let sizeOfString = 24;
    let numOfString = windowWidth / sizeOfString;

    let words = [];
    for (let i = 0, max = Math.ceil(text.length / numOfString); i < max; i++) {
        words[i] = text.substr(i * numOfString, numOfString);
    }
    if (text.length - numOfString * words.length > 0) {
        words[words.length] = text.substr(numOfString * words.length, text.length - numOfString * words.length);
    }
    return words;
}
});

おまけ

テキスト送りを実装したのですが、UIでキー操作するのを目指して作っていたらかなりめんどくさかった(時間かかった)ので またまとめて行こうかなと思っています
そのときはnoteとかになるかも