2023-10-15に更新

Godot でサブウィンドウを作ろうぜ(^~^)?

読了目安:24分

親記事から来た

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

はじめに

202310__godot__08-1349-Windows-o2o0.png

ramen-tabero-futsu2.png
「 👆 ウィンドウが 増えてきそうなので、
ツリー階層を 再編したいというところだぜ」

ohkina-hiyoko-futsu2.png
「 とりあえず 既存の分は やってしまえば?」

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

202310__godot__08-1429-WindowsRestructure-o2o0.png

ramen-tabero-futsu2.png
「 👆 ひとまず 既存の分を移動したぜ」

ohkina-hiyoko-futsu2.png
「 じゃあ どっちも ウィンドウなのだから スクリプトは共通化できるはずなのよ。
メッセージ と センター のスクリプトは何が違うの?」

📄 Director/Windows/メッセージ:

#   メッセージ・ウィンドウ(Message Window)
extends Node2D


#   状態遷移機械
var statemachine = load("scripts/MessageWindowStatemachine.gd").new()


#   メッセージ・ウィンドウを閉じる
func initialize():
    $"TextBlock".initialize()
    self.statemachine.all_page_flushed()


#   ウィンドウを空っぽにして、次の指示を待ちます
func clear_and_awaiting_order():
    print("[メッセージ・ウィンドウ] ウィンドウを空っぽにして、次の指示を待ちます")
    $"TextBlock".text = ""

    #   メッセージウィンドウは指示待ちだ
    $"../../AssistantDirector".is_message_window_waiting_for_order = true


#   先頭行と、それ以外に分けます
func split_head_line_or_tail(text):
    #   最初の改行を見つける
    var index = text.find("\n")
    var head = text.substr(0, index)
    var tail = text.substr(index+1, text.length() - (index+1))
    # print("[メッセージ・ウィンドウ] head: [" + head + "]")
    # print("[メッセージ・ウィンドウ] tail: [" + tail + "]")
    return [head, tail]


#   メッセージを追加
func push_message(text):
    # print("[メッセージ・ウィンドウ] 台詞追加")
    print("[メッセージ・ウィンドウ] 台詞: [" + text + "]")
    $"TextBlock".push_message(text)

    #   表示
    self.visible = true

    #   タイプライター風表示へ状態遷移
    self.statemachine.scenario_seted()


#   選択肢を追加
func push_choices(row_numbers, text):
    print("[メッセージ・ウィンドウ] 選択肢: [" + text + "]")
    $"TextBlock".push_choices(row_numbers, text)

    #   表示
    self.visible = true

    #   タイプライター風表示へ状態遷移
    self.statemachine.scenario_seted()



#   ページ送り
func on_page_forward():
    #   効果音
    $"../../Musician".playSe("ページめくり音")

    #   ブリンカーを消す
    $"TextBlock".clear_blinker()

    #   ウィンドウを空っぽにして、次の指示を待ちます
    self.clear_and_awaiting_order()


#   下位ノードで選択肢が選ばれたとき、その行番号が渡されてくる
func on_choice_selected():
    #   カーソル音
    $"../../Musician".playSe("選択肢確定音")

    var row_number = $"TextBlock/ChoiceCursor".selected_row_number  
    print("[メッセージ・ウィンドウ] 選んだ選択肢行番号:" + str(row_number))

    #   選択肢の行番号を、上位ノードへエスカレーションします
    $"../../AssistantDirector".on_choice_selected(row_number)


#   サブツリーが全てインスタンス化されたときに呼び出される
func _ready():
    # ステートマシーンを、子にも参照させる
    $"Background".statemachine = self.statemachine
    $"TextBlock".statemachine = self.statemachine
    $"TextBlock/BlinkerTriangle".statemachine = self.statemachine
    $"TextBlock/BlinkerUnderscore".statemachine = self.statemachine
    $"TextBlock/ChoiceCursor".statemachine = self.statemachine

    #   ウィンドウを空っぽにする
    $"TextBlock".text = ""


#   テキストボックスなどにフォーカスが無いときの入力を拾う
func _unhandled_key_input(event):

    #   完全表示中
    if self.statemachine.is_completed():

        #   選択肢モードなら
        if $"TextBlock".is_choice_mode:

            #   何かキーを押したとき
            if event.is_pressed():

                #   確定ボタン以外は無効
                if event.keycode != KEY_ENTER:
                    # print("[メッセージ・ウィンドウ] 選択肢モードでは、エンターキー以外ではメッセージ送りしません")
                    return

                else:
                    #   選択肢を確定した
                    self.on_choice_selected()
                    return

        #   それ以外なら
        else:

            #   何かキーを押したとき
            if event.is_pressed():

                if event.keycode == KEY_R:
                    # print("[メッセージ・ウィンドウ] Rキーは、メッセージの早送りに使うので、メッセージ送りしません")
                    return

                #   ページ送り
                self.on_page_forward()

📄 Director/Windows/センター:

#   センター・ウィンドウ(Center Window;中央窓)
extends Node2D


#   現在表示中のセンターウィンドウ画像のノード名
var current_name = null


#   ウィンドウを表示する
func show_window(name):
    print("[センター・ウィンドウ] 表示:[" + name + "]")

    #   既に表示中の画像を非表示にする(上に乗っかっていて、表示したい絵が見えないケースがある)
    if  self.current_name != null:
        self.get_node(self.current_name).hide()

    self.current_name = name
    self.show()
    self.get_node(self.current_name).show()
    $"System".show()
    $"System/Frame".show()


#   ウィンドウを非表示にする
func hide_window():
    if self.current_name == null:
        return

    print("[センター・ウィンドウ] 非表示:[" + str(self.current_name) + "]")
    self.get_node(self.current_name).hide() 
    $"System/Frame".hide()
    self.current_name = null

ramen-tabero-futsu2.png
「 👆 そら、メッセージ・ウィンドウには
メッセージ表示をコントロールするスクリプトが書いてるよな」

kifuwarabe-futsu.png
「 そういえば メッセージ・ウィンドウの背景は 半透明の黒で、
センター・ウィンドウの背景は 画像 という違いもあるぜ」

ohkina-hiyoko-futsu2.png
「 半透明の黒も 窓から切り離して 背景画像という扱いにしたらいいんじゃない?」

ramen-tabero-futsu2.png
「 メッセージ・ウィンドウ用の画像と、センター・ウィンドウ用の画像は 縦横のサイズが違う」

ohkina-hiyoko-futsu2.png
「 じゃあ 画像ファイルは、サイズ別のフォルダーに入れることにしましょう」

ramen-tabero-futsu2.png
「 直交性を考えると ウィンドウ名別のフォルダーにした方が 記述が簡潔になるぜ」

kifuwarabe-futsu.png
「 Godot のツリー構造と、ファイルシステムのツリー構造に 依存性があると不利だぜ」

ramen-tabero-futsu2.png
「 あっ そうか~」

202310__godot__08-1450-ImageFolderRestructure-o2o0.png

ramen-tabero-futsu2.png
「 👆 ひとまず サイズ別のフォルダーを採用だぜ」

静的なウィンドウ

ramen-tabero-futsu2.png
「 ゲームプログラムの観点から言うと 自由自在なウィンドウが欲しいような
設計の固まってない状態が いつまでも続いて 進捗が進んでたら 良くないぜ」

kifuwarabe-futsu.png
「 それはそうだが……」

ohkina-hiyoko-futsu2.png
「 試しに ゲームを終了するための システム・メニューを設計してみればいいんじゃない?」

202310__godot__08-1532-Schetch-o2o0.png

ramen-tabero-futsu2.png
「 👆 グリッドに合わさないと サイズ感は 分からないものだ」

kifuwarabe-futsu.png
「 そんな小さなウィンドウでは 英語が入らないだろう。
ローカライズして大丈夫か?」

ramen-tabero-futsu2.png
「 間に合わないんで ローカライズで」

ramen-tabero-futsu2.png
「 半透明の黒い所は RGBAが 32, 32, 32, 192 か。 192 は、百分率の 75% でもいいことにしよう」

202310__godot__08-1546--SystemMenu.png

ramen-tabero-futsu2.png
「 👆 位置も サイズも 色も 素材を作る手間がかからないように妥協したぜ」

ohkina-hiyoko-futsu2.png
「 画面上に置いてみましょう」

名前を変更

202310__godot__08-1633-CenterMessageWindow.png

ramen-tabero-futsu2.png
「 👆 分かった。名前が悪いんだ。名前を変えよう。
こいつは システム・ウィンドウ ではなくて、
中央メッセージ・ウィンドウ だぜ」

202310__godot__08-1635-BottomMessageWindow.png

ramen-tabero-futsu2.png
「 👆 こいつは 下メッセージ・ウィンドウ だぜ」

202310__godot__08-1646-CenterViewingWindow.png

ramen-tabero-futsu2.png
「 👆 こいつは 中央ビューイング・ウィンドウ だぜ」

kifuwarabe-futsu.png
「 別物と割り切ったわけだな 実践的だぜ」

202310__godot__08-1659-Msg-o2o0.png

ramen-tabero-futsu2.png
「 👆 どのメッセージ・ウィンドウを使うか指定できるようにしようぜ?」

ohkina-hiyoko-futsu2.png
「 状態を持っていいいの?」

ramen-tabero-futsu2.png
「 そこは ごめんなさい しようぜ?」

位置合わせ

202310__godot__08-1725-Center-o2o0.png

ramen-tabero-futsu2.png
「 👆 テキストの表示位置をどうするか。 Godot の思想だと コピー貼り付けして 座標変えて……」

ohkina-hiyoko-futsu2.png
「 コピー貼り付けするしか ないんじゃない? 動的にやったら レイアウトの機能の利便性を損なうんだし」

202310__godot__08-1955-Copy-o2o0.png

ramen-tabero-futsu2.png
「 👆 同じものが コピーされて DRYの法則が破れているように見えるが、
座標位置を覚えておくデータだから 残しておいた方が エディターが活きるのか~」

202310__godot__08-2207--CenterMessageWindow.png

ramen-tabero-futsu2.png
「 👆 文字数を調整しないと
CSSチョットワカル みたいになるんだな」

進行

ramen-tabero-futsu2.png
「 ゲームの進行を止めて、システム・メニューを出すんだっけ?」

kifuwarabe-futsu.png
「 そうだぜ」

  📂 Director
    ├── 📂 Main
    └── 📂 SystemMenu

ramen-tabero-futsu2.png
「 👆 じゃあ 大きく2つに分かれないかだぜ?」

  📂 Director
    ├── 📂 Main
    ├── 📂 SystemMenu
👉  └── 📂 Musician

ohkina-hiyoko-futsu2.png
「 👆 ミュージシャンは 別れなくてよくない?」

  📂 Director
👉 └── 📂 ScenarioBook
      ├── Main
      └── SystemMenu

ramen-tabero-futsu2.png
「 👆 じゃあ 分かれるのは シナリオブックだ」

kifuwarabe-futsu.png
「 大改造すると 時間が無くなってしまうから、今回は 分けずに行こうぜ?」

エスケープ・キー

ramen-tabero-futsu2.png
「 エスケープ・キーを押したら メニューが出るようにするにしても、
メッセージ・ウィンドウが出てないときにも メニューは出したいから、
キー・イベントを取得するのは メッセージ・ウィンドウより 上位のノードだよな」

ohkina-hiyoko-futsu2.png
「 ルートで キー判定すりゃ よくない?」

ramen-tabero-futsu2.png
「 メッセージ・ウィンドウが出てるときは キーが反応しないな。
func _unhandled_key_input(event): を2か所で使うとか よくないのか?」

kifuwarabe-futsu.png
「 ルートで全部取って、必要なら 子ノードに配るようにしたらどうだぜ?」

ramen-tabero-futsu2.png
「 それもまた 大改造だな……」

ramen-tabero-futsu2.png
「 子ノードが先に _unhandled_key_input() をキャッチするのか?
後ろ向き探索?」

ramen-tabero-futsu2.png
「 いや、会話イベント中に エスケープ・キー を押して システム・メニューを出そうなんてのが
間違いなんだぜ」

会話じゃないシーン

kifuwarabe-futsu.png
「 しかし 現在の設計では 会話シーンしかない。
マップの上を移動するような シーンや、 アドベンチャーのようなメニューの並んだシーンがない」

ramen-tabero-futsu2.png
「 会話が途切れることなく連続しちゃ ダメ なんだ」

ohkina-hiyoko-futsu2.png
「 じゃあ マップ画面を挟んだらいいんじゃない?」

ramen-tabero-futsu2.png
「 1~2時間かけて、1枚 絵を追加するか~」

📖 ツイート

ramen-tabero-futsu2.png
「 👆 こういうマップだろ」

Z-index のルール分からん

202310__godot__09-0001--ZIndex.png

ramen-tabero-futsu2.png
「 👆 Z-index で前景、後景の調整ができない…… 分けわからん……」

導線を中断するのが 難しい

ramen-tabero-futsu2.png
「 サブ・ウィンドウが どうのこうのより
メインで ゲームが進行していて それを 停止させるのが難しい」

ohkina-hiyoko-futsu2.png
「 _process() で動かしてるんだから 嚙み合わせを外したらいいじゃない」

ramen-tabero-futsu2.png
「 それを1個1個 仕込んでいくのが大変だ。 いったん休憩するぜ」

親記事へ戻る

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

📅 (2023-10-10 tue) 再開

ramen-tabero-futsu2.png
「 エスケープ・キーを押したら 一時停止する機能は実装してきたぜ」

kifuwarabe-futsu.png
「 じゃあ ついでに 現在表示しているメッセージ・ウィンドウも非表示にしてくれだぜ」

#   サブツリーの visible を設定
func set_visible_subtree(is_visible):
    print("[チョイス・カーソル] 可視性:" + str(is_visible))

    #   見せろ(true) という指示のとき、見えてれば(true) 、何もしない(pass)。
    #   隠せ (false)という指示のとき、見えてれば(true) 、隠す   (false)。
    #   見せろ(true) という指示のとき、隠れてれば(false)、見せる  (true)。
    #   隠せ (false)という指示のとき、隠れてれば(false)、何もしない(pass)
    if is_visible != self.visible:
        self.visible = is_visible

        #   子ノード
        for child in self.get_children():
            if child.has_method("set_visible_subtree"):
                child.set_visible_subtree(is_visible)

ramen-tabero-futsu2.png
「 👆 こんな感じのメソッドを ノードに持たせていくかだぜ」

202310__godot__10-2243--ElaseWindow.png

ramen-tabero-futsu2.png
「 👆 背景を残して、それ以外を非表示にしたぜ」

中央メッセージ・ウィンドウを表示してくれだぜ

kifuwarabe-futsu.png
「 続けて 中央メッセージ・ウィンドウを表示してくれだぜ」

ramen-tabero-futsu2.png
「 前に cwnd って命令を作ってたよな。あれを v-wnd に名前を変えようかな」

202310__godot__10-2320--ViewingWindow.png

ramen-tabero-futsu2.png
「 👆 あっ、これは ビューイング・ウィンドウだ。間違えた」

ramen-tabero-futsu2.png
「 前に msg って命令を作ってたよな。あれを m-wnd に名前を変えようぜ?」

202310__godot__10-2338--CenterMessageWindow.png

ramen-tabero-futsu2.png
「 👆 こうだぜ」

kifuwarabe-futsu.png
「 じゃあ そこに 選択肢を表示してくれだぜ。 再開と 続行でいいかな」

202310__godot__10-2355--Choice.png

ramen-tabero-futsu2.png
「 枠のサイズが なんか余ったな」

復帰処理

kifuwarabe-futsu.png
「 じゃあ そこで エスケープ・キーを押したら 元の状態に戻してくれだぜ」

ramen-tabero-futsu2.png
「 元の状態を保存してないから 難しいなあ」

ohkina-hiyoko-futsu2.png
「 何を消したか 覚えておくしかなくない?」

ramen-tabero-futsu2.png
「 それか、何も消さず 背景の後ろに隠しておくかだな」

kifuwarabe-futsu.png
「 Godot での z-index の処理は 分けが分からないのではなかったか?」

📅 (2023-10-11 wed) 再開

ramen-tabero-futsu2.png
「 Visual Novel パートと System Menu パートを分けるべきか、
パートという呼称は 適切か?」

ohkina-hiyoko-futsu2.png
「 コンポーネントなんじゃないの?」

kifuwarabe-futsu.png
「 班なら グループ(Group)だが」

ramen-tabero-futsu2.png
「 ゲームは どれだけの画面で構成される?」

kifuwarabe-futsu.png
「 画面なら スクリーン(Screen)とか シーン(Scene)かな」

ramen-tabero-futsu2.png
「 Scene は Godot に用語を取られたから使いたくない」

ohkina-hiyoko-futsu2.png
「 セッションでどう?」

ramen-tabero-futsu2.png
「 ビジュアル・ノベル・セッション、 バトル・セッション、 メニュー・セッション……、
良い案だぜ 今んとこ候補」

ramen-tabero-futsu2.png
「 セパレーション(Separation;離別)は どうだぜ?」

kifuwarabe-futsu.png
「 意味は適切だが」

ohkina-hiyoko-futsu2.png
「 用語として 他の単語と絡みにくいのよね」

ramen-tabero-futsu2.png
「 ビジュアル・ノベル・セパレーション、 バトル・セパレーション、 メニュー・セパレーション……、
おかしいか。どうすれば?」

ohkina-hiyoko-futsu2.png
「 デパートメント(Department;部)で いいんじゃない?」

ramen-tabero-futsu2.png
「 それだぜ!
ビジュアル・ノベル・デパートメント、 バトル・デパートメント、 システム・メニュー・デパートメント、
これで行こう」

ビジュアル・ノベル・デパートメント

202310__godot__11-2022--VisualNovelDepartment-o2o0.png

ramen-tabero-futsu2.png
「 👆 シナリオを書くというのは、
VisualNovelDepartment のスナップショットを変更することだと、
そういう概念にしてしまおう」

202310__godot__11-2102--VisualNovelDepartment-o2o0.png

ramen-tabero-futsu2.png
「 👆 スナップショットではない ビジュアル・ノベル部 も作っておこう」

kifuwarabe-futsu.png
「 ファイル名を間違えそうだ」

ramen-tabero-futsu2.png
「 ロケーションの名前も デパートメント毎に覚えておく必要があるか」

タイプライターで表示途中の文

kifuwarabe-futsu.png
「 タイプライターで表示途中の文も 覚えておけだぜ」

ramen-tabero-futsu2.png
「 なんで こんなに難しいのだろう?」

ramen-tabero-futsu2.png
「 スナップショットは グローバル変数のように 変数を持っておきたくて、
オブジェクト指向のカプセル化とは 反するんだが、
データを1か所で管理したいときは オブジェクト指向じゃない方がいいんだ」

ramen-tabero-futsu2.png
「 テキストブロックが タイプライター表示をしていて
文字列を切り分けたりしているが、この機能は デパートメント の方に持たせたい」

ohkina-hiyoko-futsu2.png
「 また 次の日ねえ」

📅 (2023-10-12 thu 20:12) 再開

ramen-tabero-futsu2.png
「 テキストブロックを見るか」

# メッセージを追加
func push_message(new_text):
    # print("[テキストブロック] 台詞追加")
    print("[テキストブロック] 台詞: [" + new_text + "]")
    self.get_snapshot("VisualNovelDepartment").is_choice_mode = false
    self.get_snapshot("VisualNovelDepartment").choice_row_numbers = []
    self.get_snapshot("VisualNovelDepartment").text_block_buffer = new_text

    # 空欄化
    self.emptize()


# 選択肢を追加
func push_choices(row_numbers, new_text):
    print("[テキストブロック] 選択肢: [" + new_text + "]")
    self.get_snapshot("VisualNovelDepartment").choice_row_numbers = row_numbers
    self.get_snapshot("VisualNovelDepartment").text_block_buffer = new_text
    self.get_snapshot("VisualNovelDepartment").is_choice_mode = true

    # 空欄化
    self.emptize()

    # さらに、ブリンカーは無いことにする
    $"BlinkerTriangle".initialize()
    $"BlinkerUnderscore".initialize()

ramen-tabero-futsu2.png
「 👆 通常のメッセージと、選択肢でメソッドが分かれているの、
改造の邪魔なんで 1本化したいぜ」

ohkina-hiyoko-futsu2.png
「 前処理として .setup_normal_mode() と、 .setup_choices_mode() を作ったら?」

# メッセージを追加
func push_message(new_text, choice_row_numbers = null):

    #   テキスト設定
    self.get_snapshot("VisualNovelDepartment").text_block_buffer = new_text

    #   空欄化
    self.emptize()

    #   選択肢なら
    if choice_row_numbers != null:
        print("[テキストブロック] 選択肢: [" + new_text + "]")
        self.get_snapshot("VisualNovelDepartment").is_choice_mode = true
        self.get_snapshot("VisualNovelDepartment").choice_row_numbers = choice_row_numbers

        # メッセージエンド・ブリンカーは無いことにする
        $"BlinkerTriangle".initialize()
        $"BlinkerUnderscore".initialize()

    #   それ以外なら
    else:
        print("[テキストブロック] 台詞: [" + new_text + "]")
        self.get_snapshot("VisualNovelDepartment").is_choice_mode = false
        self.get_snapshot("VisualNovelDepartment").choice_row_numbers = []

ramen-tabero-futsu2.png
「 👆 関数を増やしたくないんで 1本化するぜ」

元の状態に復元するのが難しい

202310__godot__12-2241--RestoreToFailed.png

ramen-tabero-futsu2.png
「 👆 難しい!」

kifuwarabe-futsu.png
「 しかし 不具合の状況はマシになってきたぜ」

ramen-tabero-futsu2.png
「 部門切り替え時に 下メッセージ・ウィンドウの初期化をやってしまっていて、そのとき透明になってるようだぜ」

ohkina-hiyoko-futsu2.png
「 初期化するのは 正しいんじゃない? そのあと表示しないのが悪いだけで」

202310__godot__12-2351--Opaque.png

ramen-tabero-futsu2.png
「 👆 可視性ではなく、不透明性で 見えなくなっていたのだった」

kifuwarabe-futsu.png
「 不具合は 少しずつ マシになってきているぜ」

アクティブ

ramen-tabero-futsu2.png
「 Godot のオブジェクトは、存在しない、という設定にできないのかだぜ? .set_active() みたいな」

ohkina-hiyoko-futsu2.png
「 不可視だったら 存在しない という取り決めにするしかなくない?」

📅 (2023-10-13 fri 21:21) 再開

kifuwarabe-futsu.png
「 状態機械をガチガチに作ろうぜ?」

スクリプトを統合しよう

202310__godot__13-2125--Tree-o2o0.png

ramen-tabero-futsu2.png
「 その前に スクリプトの機能分担が 煩雑になってきたので シンプルにしていこうぜ?」

202310__godot__13-2315--MessageWindow-o2o0.png

ramen-tabero-futsu2.png
「 👆 統合して 1ファイル減らしたぜ」

セリフと選択肢は別状態か?

ramen-tabero-futsu2.png
「 台詞は 最後に ▼ が出て、
選択肢は 最後に移動できる ▶ が出るのが 違いなんだよな」

kifuwarabe-futsu.png
「 メッセージエンド・ブリンカー(Message-end Blinker※造語)が違うだけか」

202310__godot__14-0002--MessageEndBlinker-o2o0.png

ramen-tabero-futsu2.png
「 ここらへんのスクリプトを 内部的に1種類に統合したいぜ」

メッセージエンド・ブリンカーの状態機械

#   メッセージエンド・ブリンカー(Message-end Blinker)
extends Node

#  状態遷移図
#  ーーーーー
#
#             Entry
#             +
#             |
#             |
# +ーーーーーーーーーー>+
# |           |
# |           | resolved ※解決済み
# |           |
# |           V
# |        +ーーーーーー+
# |        |  None  | ※メッセージエンド・ブリンカーが存在しない唯一の状態
# |        +ーー+ーーー+
# |           |
# |           | worry ※悩む
# |           |
# |   +ーーーーーー>+
# |   |       |
# |   |       V
# |   |    +ーーーーーーーーー+
# |   |    |  BlinkHere   | ※その場で点滅中
# |   |    +ーー+ーーーーーー+
# |   |       |
# |   |       |
# |   |       ◇ ーーーーーーーーーーーーーーーーーーーーー+
# |   |       |                     |
# |   |       |                     |
# |   |       | move ※カーソルを動かす        |
# |   |       |                     |
# |   |       V                     |
# |   |    +ーーーーーーーーーー+             |
# |   |    |  BlinkMoving   | ※点滅しながら     |
# |   |    +ーー+ーーーーーーー+  カーソル移動中    |
# |   |       |                     |
# |   |       |                     |
# |   |       |                     |
# |   +ーーーーーーー+ moved  ※移動完了           |
# |                                 |
# |                                 |
# |                                 |
# +ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー+
#
enum States {None, BlinkHere, BlinkMoving}

# 状態
var state = States.None

# 関数の変数
var on_resolved = null
var on_worry = null
var on_move = null
var on_moved = null


func is_none():
    return self.state == States.None


func is_blink_here():
    return self.state == States.BlinkHere


func is_blink_moving():
    return self.state == States.BlinkMoving


func resolved():
    if on_resolved != null:
        on_resolved.call()

    print("[メッセージエンド・ブリンカー] 解決済み")
    self.state = States.None


func worry():
    if on_worry != null:
        on_worry.call()

    print("[メッセージエンド・ブリンカー] 悩む")
    self.state = States.BlinkHere


func move():
    if on_move != null:
        on_move.call()

    print("[メッセージエンド・ブリンカー] カーソルを動かす")
    self.state = States.BlinkMoving


func moved():
    if on_moved != null:
        on_moved.call()

    print("[メッセージエンド・ブリンカー] カーソルは移動した")
    self.state = States.BlinkHere

ramen-tabero-futsu2.png
「 👆 こんな感じか」

📅 2023-10-14 sat ⏰ 12:10 再開

202310__godot__14-1206--TextBlock-o2o0.png

ramen-tabero-futsu2.png
「 👆 スクリプトが分かれてると つらいから 統合するぜ」

kifuwarabe-futsu.png
「 使ってみると 最適が分かってくるな」

ブリンカーの状態機械

#   ブリンカー(Blinker;点滅するもの)
extends Node

#  状態遷移図
#  ーーーーー
#
#             Entry
#             +
#             |
#             |
# +ーーーーーーーーーー>+
# |           |
# |           | switch_off ※スイッチ・オフ
# |           |
# |           V
# |        +ーーーーーー+
# |        |  None  | ※ブリンカーが存在しない唯一の状態
# |        +ーー+ーーー+
# |           |
# |           |
# |           | switch_on ※スイッチ・オン
# |           |
# |           V
# |        +ーーーーーーーーーーー+
# |        |  BrightAtFirst   | ※初回はすぐ表示
# |        +ーー+ーーーーーーーー+
# |           |
# |           |
# |   +ーーーーーー>+
# |   |       |
# |   |       |
# |   |       ◇ ーーーーーーーーーーーーーーーーーーーーーーーーー+
# |   |       |                         |
# |   |       |                         |
# |   |       | turn_off ※時間経過による消灯         |
# |   |       |                         |
# |   |       V                         |
# |   |    +ーーーーーー+                     |
# |   |    |  Off   | ※消える                |
# |   |    +ーー+ーーー+                     |
# |   |       |                         |
# |   |       |                         |
# |   |       ◇ ーーーーーーーーーーーーーーーーーーーー>+<ーー+
# |   |       |                     |
# |   |       |                     |
# |   |       | turn_on ※時間経過による点灯      |
# |   |       |                     |
# |   |       V                     |
# |   |    +ーーーーーーー+                |
# |   |    |  Bright  | ※灯る            |
# |   |    +ーー+ーーーー+                |
# |   |       |                     |
# |   |       |                     |
# |   |       |                     |
# |   +ーーーーーーー+                     |
# |                                 |
# |                                 |
# |                                 |
# +ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー+
#
enum States {None, BrightAtFirst, Off, Bright}

# 状態
var state = States.None

# 関数の変数
var on_switched_on = null
var on_switched_off = null
var on_turned_on = null
var on_turned_off = null


func is_none():
    return self.state == States.None


func is_bright_at_first():
    return self.state == States.BrightAtFirst


func is_off():
    return self.state == States.Off


func is_bright():
    return self.state == States.Bright


func switch_on():
    if on_switched_on != null:
        on_switched_on.call()

    print("[ブリンカー] スイッチ・オン")
    self.state = States.BrightAtFirst


func switch_off():
    if on_switched_off != null:
        on_switched_off.call()

    print("[ブリンカー] スイッチ・オフ")
    self.state = States.None


func turn_on():
    if on_turned_on != null:
        on_turned_on.call()

    print("[ブリンカー] 点灯")
    self.state = States.Bright


func turn_off():
    if on_turned_off != null:
        on_turned_off.call()

    print("[ブリンカー] 消灯")
    self.state = States.Off

ramen-tabero-futsu2.png
「 👆 点滅の状態も機械に」

202310__godot__14-1528--Cursor-o2o0.png

ramen-tabero-futsu2.png
「 👆 非表示のウィンドウのカーソルが 見えてる」

ramen-tabero-futsu2.png
「 Godot の .show().hide() メソッドは アホが考えたメソッドなんだ。
わたしが考えた .set_visible_subtree() メソッドを使うことで解決!」

ウィンドウの表示/非表示まででけた

202310__godot__14-1621--SubWindow.png

ramen-tabero-futsu2.png
「 👆 ウィンドウの表示/非表示まででけた。
中の文章の復元は まだ」

ohkina-hiyoko-futsu2.png
「 細かくやってくしかないわねえ」

📅 2023-10-15 sun

202310__godot__15-0138--Restore.png

ramen-tabero-futsu2.png
「 👆 芋づる式に 次から次へと できてないところが 出てくるぜ」

kifuwarabe-futsu.png
「 順調だけど 遅いなあ」

Department も一般化しないと きつい

ramen-tabero-futsu2.png
「 ビジュアルノベル部門とか、システムメニュー部門も 一般化しないと きつくなってきた」

ohkina-hiyoko-futsu2.png
「 再開するのに?」

ramen-tabero-futsu2.png
「 そう。個別にハードコーディングではきつい」

func get_current_snapshot():
    if self.statemachine_of_director.is_playing_visual_novel():
        return self.get_snapshot("VisualNovelDepartment")

    elif self.statemachine_of_director.is_playing_system_menu():
        return self.get_snapshot("SystemMenuDepartment")

    else:
        return null

ramen-tabero-futsu2.png
「 👆 状態遷移にしていたが、 Department は、ただの変数にしたい」

kifuwarabe-futsu.png
「 Department は状態ではないという建付けにするわけだな」

再開の機能付けた

202310__godot__15-0512--Department.png

ramen-tabero-futsu2.png
「 👆 再開の機能付けたんだが なぜだか知らないが このメニューは1回使うと 2回目以降から出てこないぜ」

kifuwarabe-futsu.png
「 作った本人が分かってないの わらう」

ポップしてるから、ランタイム中に元データが空になってる

ramen-tabero-futsu2.png
「 ポップしてるから シナリオが空になってるようだぜ」

ohkina-hiyoko-futsu2.png
「 カーソルをインクリメントする方が いいのかしらねえ?」

⏰ 12:57 再開

ramen-tabero-futsu2.png
「 じゃあ プログラム・カウンターみたいに実装し直すか」

(カタ カタ カタ カタ)

ramen-tabero-futsu2.png
「 直した。うまくいったぜ」

親記事から来た

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

.

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

むずでょ

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

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

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

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

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

コメント