2023-10-01に更新

Godot でテキストを表示しようぜ?

読了目安:12分

親記事から来た

📖 Godot って何だぜ(^~^)?

はじめに

ramen-tabero-futsu2.png
「 Godot でテキストを表示しようぜ?」

202309__godot__30-1620--Label-o2o0.png

kifuwarabe-futsu.png
「 👆 Label と RichTextLabel は見つかるが」

202309__godot__30-1623--LabelSettings-o2o0.png

ramen-tabero-futsu2.png
「 👆 フォント・サイズを いくつにするのが適切なのか 現代のディスプレイ事情では 分からんなあ」

ohkina-hiyoko-futsu2.png
「 ゲームの画面サイズを 先に確定した方が よくない?」

画面サイズ

202309__godot__30-1629--WindowSize-o2o0.png

ramen-tabero-futsu2.png
「 👆 じゃあ 画面サイズを 1280 x 720 ピクセルに縛ろうぜ?」

グリッド

202309__godot__30-1642--Text.png

ramen-tabero-futsu2.png
「 👆 フォントを適当に設定して……、グリッドが無いと サイズ感をつかめないな」

ohkina-hiyoko-futsu2.png
「 👇 シェーダーで勝手に作れという 界隈みたいよ」

📖 2D Grid

ramen-tabero-futsu2.png
「 👇 また勉強か」

📖 Your first 2D shader

ramen-tabero-futsu2.png
「 ダメだ シェーダー難しい 画像でやる」

202309__godot__30-1657--Tileset-o2o0.png

ramen-tabero-futsu2.png
「 👆 Windows Paint で線を引き、 GIMP で背景色を抜いたぜ」

kifuwarabe-futsu.png
「 技術の無さを 労働で補うの わらう」

202309__godot__30-1716--Grid.png

kifuwarabe-futsu.png
「 👆 ぴったりなの わらう」

ohkina-hiyoko-futsu2.png
「 これ、工作用紙の罫線のデザインよね」

📖 参考画像

ramen-tabero-futsu2.png
「 近所で入手しやすいアイテムだぜ。8の倍数にアレンジしたぜ」

フォントをインストールしようぜ?

ramen-tabero-futsu2.png
「 Godot のデフォルトのフォントが クソ で嫌いだぜ。
他の日本人は 誰一人 疑問にも思わず 放置しているのか?」

ohkina-hiyoko-futsu2.png
「 海外が本場なんじゃないの?」

ramen-tabero-futsu2.png
「 他のフォントに変えられないのかだぜ?」

kifuwarabe-futsu.png
「 👇 調べろだぜ」

📖 【Godot】日本語フォントの設定方法 (Godot3.4〜3.5)

ramen-tabero-futsu2.png
「 フォント作成者のページを見にいったら フォント・ファイルをソースに同梱して配布するのをやめてくれといった趣旨が 書いてあった」

ohkina-hiyoko-futsu2.png
「 当然よね」

ramen-tabero-futsu2.png
「 👇 この M PLUS 1 Code フォントなら わたしの感覚に合うんだが、オープンソースのゲームに同梱していいんだろうか?」

📖 M PLUS 1 Code

ramen-tabero-futsu2.png
「 👇 ダウンロードできるページは ここだろうか?」

📖 Git Hub/MPLUS FONTS

202309__godot__30-1803--font-o2o0.png

ramen-tabero-futsu2.png
「 👆 再配布可能なライセンスの フォント・ファイルをダウンロードしてきたぜ」

202309__godot__30-1808--NewResource-o2o0.png

ramen-tabero-futsu2.png
「 👆 フォント・ファイルを置いても 認識しないようなので、
リソースを新規作成するぜ」

202309__godot__30-1810--FontFile-o2o0.png

ramen-tabero-futsu2.png
「 👆 説明と画面が違うんで あとは勘で進むぜ。 FontFile でどうか?」

202309__godot__30-1813--NewFontFile-o2o0.png

ramen-tabero-futsu2.png
「 👆 new_font_file.tres というファイルを作った」

202309__godot__30-1815--SelectAFile-o2o0.png

ramen-tabero-futsu2.png
「 👆 フォント・ファイルと紐づけよ」

202309__godot__30-1818--QuickLoad-o2o0.png

ramen-tabero-futsu2.png
「 👆 これで フォントを選べるようになったぜ」

202309__godot__30-1821--Mplus1CodeFont.png

ramen-tabero-futsu2.png
「 👆 よし、まともな フォントになったぜ」

202309__godot__30-1840--Pixel.png

ramen-tabero-futsu2.png
「 👆 おお、このフォント、 分かってる フォントだぜ」

kifuwarabe-futsu.png
「 多分、 M Plus 1 Code フォントの作者が 古臭い日本人なのでは?」

古臭いウィンドウ

ohkina-hiyoko-futsu2.png
「 じゃあ 次は 昔ながらの 古臭いウィンドウを 作ってみましょう」

ramen-tabero-futsu2.png
「 なんで わたしたちのやることは 昔とか、古臭いという 形容詞が付くんだぜ?」

kifuwarabe-futsu.png
「 事実だろ」

ramen-tabero-futsu2.png
「 あの メッセージ・ウィンドウ、現代では 何て呼ばれてるんだぜ?」

kifuwarabe-futsu.png
「 👇 StyleBoxFlat じゃないか?」

📖 Theme Variation with StyleBoxTexture returns wrong type #66850

ramen-tabero-futsu2.png
「 適当に触ってみるか」

ramen-tabero-futsu2.png
「 存在しなかった」

202309__godot__30-1901--MessageWindow-o2o2o0.png

ramen-tabero-futsu2.png
「 👆 そんなときは Windows Paint で枠を描いて GIMP で透明度を抜けばいいんだぜ」

kifuwarabe-futsu.png
「 技術の無さを 労働で補うの わらう」

202309__godot__30-1944--FixedMessageWindow.png

ramen-tabero-futsu2.png
「 👆 固定長で十分だろ」

202309__godot__30-1947--FixedMessageWindow-Run.png

ohkina-hiyoko-futsu2.png
「 👆 1、2、3、 …… 全角で1行20文字、3段で
計 60文字でいいの?」

ramen-tabero-futsu2.png
「 シナリオライターに 足らん と言われても ごめんなさい しようぜ」

背景を付けましょう

ohkina-hiyoko-futsu2.png
「 バックが方眼紙だと 落ち着かないから 背景を付けましょう」

ramen-tabero-futsu2.png
「 オープンソースで ばらまくのに ライセンスが適していないフリー素材が あるからな。
素材を揃えるのが 難しいぜ」

202309__godot__30-2016--Background.png

ramen-tabero-futsu2.png
「 👆 コンピューター将棋選手権の会場だぜ」

kifuwarabe-futsu.png
「 著作権的に問題があるのではないか?」

ramen-tabero-futsu2.png
「 著作権的に 問題のある素材しか 持ってないぜ」

ohkina-hiyoko-futsu2.png
「 困ったわねえ。 素材不足は深刻ねえ」

ブリンカー

ramen-tabero-futsu2.png
「 メッセージ・ウィンドウの最後で点滅してる アレの名前 何だぜ?
アレを作ろうぜ?」

kifuwarabe-futsu.png
「 アレは 文字だろ。 文字と同じように 輪郭を付けて、ドロップシャドウもしなければ
浮いてしまうだろ」

ohkina-hiyoko-futsu2.png
「 外字で作れないの?」

ramen-tabero-futsu2.png
「 .otf ファイルを使っているのに 外字が出てくるのか分からん」

202309__godot__30-2237--TextEndBlinker-o2o0.png

ramen-tabero-futsu2.png
「 👆 とりあえず Windows Paint と GIMP で それっぽいのを作って、画面に置いてみようぜ?」

202309__godot__30-2300--TextEndBlinkerTest.png

ramen-tabero-futsu2.png
「 👆 どうだぜ? 溶け込んでいるかだぜ?」

ohkina-hiyoko-futsu2.png
「 浮いているわねぇ」

ohkina-hiyoko-futsu2.png
「 スポイトで色を吸ってみたけど、白は (248, 248, 248) で同じなんだけど、
影の色が 違うのよね。影は 半透明にしなきゃ。
あと、フォントには アンチエイリアシングが かかってんのよ」

202309__godot__30-2237--TextEndBlinker-o2o2o0.png

ramen-tabero-futsu2.png
「 👆 ドロップシャドウも アンチエイリアシングも 見よう見まねで Windows Paint と GIMP で やってみようぜ?」

202309__godot__30-2317--TextEndBlinkerTest.png

ramen-tabero-futsu2.png
「 👆 今度は どうだぜ? 溶け込んでいるかだぜ?」

ohkina-hiyoko-futsu2.png
「 さっきよりは マシな気がするけど、 三角形が でかくて 主張が強い気がするのよねえ」

202309__godot__30-2321--UnderscoreTriangle.png

ramen-tabero-futsu2.png
「 👆 参考に アンダースコアと 逆三角形の文字を横に並べたが、そんなに違うかだぜ?」

ohkina-hiyoko-futsu2.png
「 逆三角形と アンダースコアを重ねて置けばいいと思う」

202309__godot__30-2340--TextendBlinker.png

ramen-tabero-futsu2.png
「 👆 これでどうだぜ?」

ohkina-hiyoko-futsu2.png
「 目の錯覚か 暗く見えるけど もう こんなもんで いいんじゃない?」

ブリンカーを点滅させようぜ?

ramen-tabero-futsu2.png
「 👇 タイマーを使わなくても、 _process() を使えば点滅できるんじゃないか?」

extends Label

var count = 0

# サブツリーが全てインスタンス化されたときに呼び出される
# Called when the node enters the scene tree for the first time.
func _ready():
    pass


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
    count += delta

    if 0.75 <= count: 
        visible = not visible
        count -= 0.75

📺 動画

ohkina-hiyoko-futsu2.png
「 👆 この動画に映ってるブリンカーで 充分よ」

文字が1個ずつ出てくるやつをやろうぜ

ramen-tabero-futsu2.png
「 カタカタカタカタ、ってやつ」

kifuwarabe-futsu.png
「 Label コントロールには テキストを全文入れてるじゃないか。
あれを 空っぽにして、1文字ずつ 入れていくということかだぜ?」

ramen-tabero-futsu2.png
「 そうなるな」

202310__godot__01-0022--SetText-o2o0.png

ramen-tabero-futsu2.png
「 👆 例えば、こう書いても 同じことだぜ」

ohkina-hiyoko-futsu2.png
「 そのテキストを、別のところに保存しておいて、
時間経過とともに ラベルに追加していけばいいのよ」

文字列の長さを計るには?

ramen-tabero-futsu2.png
「 GDScript で 文字列の長さを計るには?」

kifuwarabe-futsu.png
「 👇 この中から探せだぜ」

📖 Godot Engine / String

ramen-tabero-futsu2.png
「 スライス 無いのか。じゃあ C言語風に書けばいいんだ」

extends Label

var count = 0

var text_storage = """お父ん、なんで唐揚げを食べているんだぜ?
ダイエットはどうした?
野菜を TABERO だぜ!
"""

# Called when the node enters the scene tree for the first time.
func _ready():
    self.text = ""


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
    count += delta

    if 0.05 <= count:
        if 0 < self.text_storage.length():
            self.text += text_storage.substr(0, 1)
            text_storage = text_storage.substr(1, self.text_storage.length()-1)
        count -= 0.05

📺 動画

ohkina-hiyoko-futsu2.png
「 👆 この動画に映ってるタイプライター風の文字列出力で 充分よ」

文字が出終わってから ブリンカーが出るように 合わせてくれだぜ

kifuwarabe-futsu.png
「 文字が出終わってから ブリンカーが出るように 合わせてくれだぜ」

ramen-tabero-futsu2.png
「 👇 別のノードの変数を どうやって参照するのか……。調べるか」

📖 [Godot]$(ドルマーク)とget_node関数の違いについて

202310__godot__01-0105--Blinker-o2o0.png

ramen-tabero-futsu2.png
「 👆 ツリー構造を変えて」

extends Label

# 点滅用
var is_blink_started = false
var count_of_blink = 0

# タイプライターの文字出力間隔
var count_of_typewriter = 0

# サブツリーが全てインスタンス化されたときに呼び出される
# Called when the node enters the scene tree for the first time.
func _ready():
    # 最初は非表示
    visible = false


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
    # テキストを出し終えたか?
    count_of_typewriter += delta
    if not is_blink_started and 0.5 <= count_of_typewriter:
        var message_window_text = $".."
        if message_window_text.get("text_storage").length() < 1:
            is_blink_started = true
            visible = true

        count_of_typewriter -= 0.5

    # 点滅
    if is_blink_started:
        count_of_blink += delta
        if 0.75 <= count_of_blink: 
            visible = not visible
            count_of_blink -= 0.75

ramen-tabero-futsu2.png
「 👆 ブリンカーのスクリプトも変更」

📺 動画

ohkina-hiyoko-futsu2.png
「 👆 この動画に映ってるメッセージ・ウィンドウで 充分よ」

メッセージ送り、してくれだぜ

kifuwarabe-futsu.png
「 メッセージ送り、してくれだぜ」

Push any key

ramen-tabero-futsu2.png
「 何か どれでも キーを押したかどうかの判定って、どうやんの?」

kifuwarabe-futsu.png
「 👇 シグナルを使うことになるんじゃないか?」

📖 How to detect if any key is pressed

ramen-tabero-futsu2.png
「 Godot では どんなコレクション・クラスを使えるんだぜ?」

kifuwarabe-futsu.png
「 👇 配列しかないんじゃないか?」

📖 Godot Engine / Array

ramen-tabero-futsu2.png
(カタカタカタカタ)

📄 textend_blinker.gd:

extends Label

# 点滅用
var is_blink_started = false
var count_of_blink = 0

# タイプライターの文字出力間隔
var count_of_typewriter = 0

# サブツリーが全てインスタンス化されたときに呼び出される
# Called when the node enters the scene tree for the first time.
func _ready():
    # 最初は非表示
    visible = false


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
    # テキストを出し終えたか?
    count_of_typewriter += delta
    if not is_blink_started and 0.5 <= count_of_typewriter:
        var message_window_text = $".."
        if message_window_text.get("text_storage").length() < 1:
            is_blink_started = true
            visible = true

        count_of_typewriter -= 0.5

    # 点滅
    if is_blink_started:
        count_of_blink += delta
        if 0.75 <= count_of_blink: 
            visible = not visible
            count_of_blink -= 0.75

func reset():
    self.visible = false
    self.is_blink_started = false
    self.count_of_blink = 0
    self.count_of_typewriter = 0

📄 MessageWindowText.gd:

extends Label

var count_of_typewriter = 0

var scenario_array = [
    # 2345678901234567890
    """\
    お父ん、知ってたら教えてくれだぜ。
    エスフェン(SFEN)の 7g7f って何だぜ?
    """,
    """\
    あー。7筋の7段目の駒を
    6段目に突くことだぜ。
    分かったら もう寝ろ
    """,
    """\
    3c3d って何だぜ?
    """,
    """\
    角換わりだろ。
    もう寝ろ
    """,
    """\
    お父ん、なんで唐揚げを食べているんだぜ?
    ダイエットはどうした?
    野菜を TABERO だぜ!
    """,
    # 2345678901234567890
    """\
    元気になりたくて唐揚げを食べるんだぜ。
    カロリー計算をしようと思ったときもあった
    限界まで食べてしまうので止めた
    """,
]

var text_storage = ""

# Called when the node enters the scene tree for the first time.
func _ready():
    # 最初のテキスト
    self.text = ""

    if self.text_storage == "" and 0 < self.scenario_array.size():
        self.text_storage = self.scenario_array.pop_front()


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):

    # タイプライター風出力
    count_of_typewriter += delta

    if 0.05 <= count_of_typewriter:
        if 0 < self.text_storage.length():
            self.text += text_storage.substr(0, 1)
            text_storage = text_storage.substr(1, self.text_storage.length()-1)
        count_of_typewriter -= 0.05

func _unhandled_key_input(event):

    print("_unhandled_key_input")

    # 何かキーを押したとき
    if event.is_pressed():
        print("_unhandled_key_input is_pressed")
        # TODO ブリンカーを消す
        $"BlinkerTriangle".reset()
        $"BlinkerUnderscore".reset()

        # メッセージ送り
        if self.text_storage == "":
            self.text = ""

            if 0 < self.scenario_array.size():
                self.text_storage = self.scenario_array.pop_front()

📺 動画

ohkina-hiyoko-futsu2.png
「 👆 この動画に映ってるメッセージ・ウィンドウで だいたい 充分よ」

メッセージ・ウィンドウを閉じてくれだぜ

kifuwarabe-futsu.png
「 出すメッセージが無いんだったら、メッセージ・ウィンドウを閉じてくれだぜ」

202310__godot__01-0210--CloseMessageWindow-o2o0.png

            else:
                # 出すメッセージが無ければ、メッセージ・ウィンドウを閉じる
                $"..".visible = false

ramen-tabero-futsu2.png
「 👆 これで オッケー」

誰が しゃべってるか 分からない

ohkina-hiyoko-futsu2.png
「 誰がしゃべってるか 分かんなくない?」

ramen-tabero-futsu2.png
「 顔グラ(Face Graphic)を出すと 素材不足で 苦しむしな」

ramen-tabero-futsu2.png
「 工夫で乗り切るか。
昔のゲームは ちからわざ が使えないときは ごめんなさい で通していたからな」

202310__godot__01-0230--Name.png

ramen-tabero-futsu2.png
「 👆 メッセージ・ウィンドウの中に 名前を書くとかな」

ohkina-hiyoko-futsu2.png
「 それで十分ねえ」

kifuwarabe-futsu.png
「 カギかっこ付けたから 1行に 19文字しか入らないぜ?」

ramen-tabero-futsu2.png
「 ごめんなさい するんだぜ」

親記事へ戻る

📖 Godot って何だぜ(^~^)?

以下、追加機能

ohkina-hiyoko-futsu2.png
「 メッセージの早送り機能が欲しくない?」

ramen-tabero-futsu2.png
「 どのボタンを押したら 早送りするんだぜ?
まだ キーボードで遊ぶのか、ゲームパッドで遊ぶのか、何も決まってないぜ?」

kifuwarabe-futsu.png
「 とりあえず キーボードに絞ってもいいのでは?」

ramen-tabero-futsu2.png
「 じゃあ スーパーファミコンの R ボタンとか よく使ったから、キーボードの R キーでいいか」

        # 1文字 50ms でも、結構ゆっくり
        var wait_time = 0.05

        # メッセージの早送り
        if Input.is_key_pressed(KEY_R):
            print("[テキストブロック] メッセージの早送り")
            wait_time = 0.01

ramen-tabero-futsu2.png
「 👆 こんな感じで ウェイトを変えればいいぜ」

            if event.keycode == KEY_R:
                print("[テキストブロック] Rキーは、メッセージの早送りに使うので、メッセージ送りしません")
                return
            # 選択肢モードの場合は、確定ボタン以外は無効
            elif self.is_choice_mode and event is InputEventKey and event.pressed:
                if event.keycode != KEY_ENTER:
                    print("[テキストブロック] 選択肢モードでは、エンターキー以外ではメッセージ送りしません")
                    return
                else:
                    print("[テキストブロック] 選んだ選択肢行番号:" + str($"ChoiceCursor".selected_row_number))
                    pass

ramen-tabero-futsu2.png
「 👆 逆に、メッセージ送りには R キーは使えないぜ」

kifuwarabe-futsu.png
「 送り とか 早送り とか 国語が難しいな」

.

ツイッターでシェア
みんなに共有、忘れないようにメモ

むずでょ

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント