無料・軽快な 2D / 3D 用のゲームエンジン Godot Engine 4 で、Array[Dictionary] 型など辞書と配列とオブジェクトのデータをタブや改行を加えて見やすく整形した Pretty-Printing の文字列に変換する GD スクリプトで作った自作関数と、例としてオブジェクトの持つシグナルの一覧情報のデータのテキストを整形した結果を紹介します。

※ GodotEngine 4.3 を使用しています。.NET 版ではありません。
※スクリプトは自己責任でご使用ください。
※ 2025 年 3 月 23 日に公開した記事です。
PrettyPrint とは
プリティプリント (Pretty-Printing) は、さまざまなスタイルの書式設定規則を適用して、テキストコンテンツを読みやすく・理解しやすくします。

Pretty-printing (or prettyprinting) is the application of any of various stylistic formatting conventions to text files, such as source code, markup, and similar kinds of content. These formatting conventions may entail adhering to an indentation style, using different color and typeface to highlight syntactic elements of source code, or adjusting size, to make the content easier for people to read, and understand. Pretty-printers for source code are sometimes called code formatters or beautifiers.
プリティプリント (またはプリティプリント) は、ソース コード、マークアップ、および同様の種類のコンテンツなどのテキスト ファイルに、さまざまなスタイルの書式設定規則を適用することです。これらの書式設定規則には、インデント スタイルの遵守、ソース コードの構文要素を強調するために異なる色や書体を使用すること、コンテンツを読みやすく理解するためにサイズを調整することが必要な場合があります。ソース コードのプリティ プリンタは、コード フォーマッタまたはビューティファイアと呼ばれることもあります。Pretty-printing – Wikipedia と Google 翻訳
PrettyPrint を行う GD スクリプトの関数例
以下のスクリプトは、辞書と配列のデータを見やすくするために PrettyPrint の整形を行うために必要な関数群です。
※詳細はスクリプト内のコメントを参照してください。
使用する際は、2行目に書いてある1番目の to_pretty_print_string 関数の引数、配列や辞書などのデータを渡します。
戻り値で得た文字列を print 文などで出力することで、PrettyPrinting の整形を行ったデータのテキストを確認できます。
修正履歴:
- 2025/03/24 配列の要素が配列または辞書でない場合、要素が出力されないバグを修正
- 2025/03/24 オブジェクトを オブジェクト名 (クラス名) {プロパティリストの配列} 形式で表示する機能を追加
## [param object] の持つ Array や Dictionary をタブで整形して、項目ごとに改行した文字列を返します。
static func to_pretty_print_string(object) -> String:
var string: String = to_pretty_print_string_recursive(object)
return string
## [param data] の持つ Array や Dictionary をタブで整形して、項目ごとに改行した文字列を返します。
## 再帰的に呼びだして、Array や Dictionary の複数階層に対応します。
## 階層の一部を整形したい場合などに、初期のタブ数を指定して利用できます。
##
## 値は、文字列の場合は "" で括ります。 StringName は & つきの "" で括ります。他は括りません。
## json 形式で処理したい場合は、 &" を " に置き換えるなどしてください。
##
## [param numof_tab] は最初につけるタブ数です。
## [param string] は、それまでに作成してきた文字列が格納されています。
## [param no_tab_first] は最初の要素にタブをつけないで、すぐに要素の文字列を追加することを指定します。
## これは、呼び出し元がすでに辞書の \t"keys": まで入力してある状況などの際に用います。
## [param no_comma_first] は最初の要素の最後(配列や辞書の場合は最後の ] や } の後)に , をつけない場合は true を指定します。
## return は、[param string] に追記した [param data] の整形した文字列を返します。
##
## 用例は以下です。シグナルのリスト(Array[Dictionary])を、整形して出力します。
## [Object.get_signal_list] 関数はそのオブジェクトが持つシグナルのリストを返します。
## 参照: https://docs.godotengine.org/ja/4.x/classes/class_object.html#class-object-method-get-signal-list
## [codeblock]
## var signal_list: Array[Dictionary] = self.get_signal_list()
## var string = ScUtil.to_pretty_print_string(signal_list)
## print(string)
## [/codeblock]
## TODO: print_rich 出力用にカラーを指定できるバージョンも作りたい。
static func to_pretty_print_string_recursive(data, numof_tab: int = 0, string: String = "", no_tab_first: bool = false, no_comma_first: bool = true) -> String:
if data is Array:
var array := data as Array
var size = array.size()
if size <= 0:
string += make_tabs_and_string("[]\n", numof_tab, no_tab_first)
else:
string += make_tabs_and_string("[\n", numof_tab, no_tab_first)
var count: int = 0
for element in array:
# 最後の要素の末尾には , をつけません。
if count >= (size - 1):
string = to_pretty_print_string_recursive(element, numof_tab + 1, string, false, true)
else:
string = to_pretty_print_string_recursive(element, numof_tab + 1, string, false, false)
count += 1
if no_comma_first == true:
string += make_tabs_and_string("]\n", numof_tab, false)
else:
string += make_tabs_and_string("],\n", numof_tab, false)
elif data is Dictionary:
var dictionary := data as Dictionary
var size: int = dictionary.size()
var count: int = 0
if size <= 0:
string += make_tabs_and_string("{}\n", numof_tab, no_tab_first)
else:
string += make_tabs_and_string("{\n", numof_tab, no_tab_first)
for key in dictionary.keys():
var value = dictionary[key]
# タブとキーの部分だけ先に追加します。
string += make_tabs_and_string("\"%s\": " % [key], numof_tab + 1, false)
# 最後の要素の末尾には , をつけません。
# すでにタブとキーを文字列に追加しているので、no_tab_first を true で処理します。
if count >= (size - 1):
string = to_pretty_print_string_recursive(value, numof_tab + 1, string, true, true)
else:
string = to_pretty_print_string_recursive(value, numof_tab + 1, string, true, false)
count += 1
if no_comma_first == true:
string += make_tabs_and_string("}\n", numof_tab, false)
else:
string += make_tabs_and_string("},\n", numof_tab, false)
elif data is Object: # Object 派生クラスの場合
var object := data as Object
# JSON で用いられる ObjectName (ClassName) {\n...\n} の形式で出力します。
var name: String = ""
if "name" in object:
# name プロパティを持っている場合のみ、名前を表示します。
name = object.get("name")
var classname: String = object.get_class()
string += make_tabs_and_string("%s (%s) {\n" % [name, classname], numof_tab, no_tab_first)
# ObjectName (ClassName) {\n...\n} の ... の部分にプロパティリストを出力します。
var property_list: Array[Dictionary] = object.get_property_list()
string = to_pretty_print_string_recursive(property_list, numof_tab + 1, string, no_tab_first, no_comma_first)
if no_comma_first == true:
string += make_tabs_and_string("}\n", numof_tab, false)
else:
string += make_tabs_and_string("},\n", numof_tab, false)
else:
# 文字列の場合は "" で括ります。 StringName は & つきの "" で括ります。他は括りません。
var string_data: String = ""
if data is String:
string_data = "\"%s\"" % [str(data)]
elif data is StringName:
string_data = "&\"%s\"" % [str(data)]
else:
string_data = str(data)
# Array でも Dictionary でもない場合は、要素の文字列を追加します。
# 呼び出し元の no_comma_first に従って、 , の有無で分岐します。
if no_comma_first == true:
string += make_tabs_and_string(string_data + "\n", numof_tab + 1, no_tab_first)
else:
string += make_tabs_and_string(string_data + ",\n", numof_tab + 1, no_tab_first)
return string
## [param numof_tab] で指定された数のタブの後に、[param string] で指定された文字列を出力します。
## [param no_tab] が true の場合はタブを追加しません。
static func make_tabs_and_string(string: String, numof_tab: int, no_tab: bool = false) -> String:
var string_tabs: String = ""
# タブをつける場合は、指定された回数のタブを追加します。
if no_tab == false:
for i in range(numof_tab):
string_tabs += "\t"
return string_tabs + string
整形するサンプル
メニュー「シーン」→「新規シーン」を選択してから、シーンドックで「インターフェース」を選択して Control ルートノードを作成したあと、それに以下のスクリプトを割り当てて F6 キーで現在のシーンを実行してください。
1番目に整形していない print 出力、2番目に自作関数で整形した print 出力が行われます。
extends Control
# Called when the node enters the scene tree for the first time.
func _ready():
# このノードオブジェクトが持つシグナルの情報を取得します。
var signal_list: Array[Dictionary] = self.get_signal_list()
# シグナルの情報を print で出力します。
print("print(signal_list)")
print(signal_list)
print()
# シグナルの情報を、整形した文字列を print で出力します。
print("print(ScUtil.to_pretty_print_string(signal_list))")
print(ScUtil.to_pretty_print_string(signal_list))
return
以下は、 get_signal_list メンバ関数で得た Control ルートノード自身のシグナルの情報の配列と辞書が入れ子になっているデータを直接 print 出力した結果です。
まだ、改行やタブによる整形が行われていないため、見やすくはありません。
print(signal_list)
[{ "name": "resized", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "gui_input", "args": [{ "name": "event", "class_name": &"InputEvent", "type": 24, "hint": 17, "hint_string": "InputEvent", "usage": 6 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "mouse_entered", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "mouse_exited", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "focus_entered", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "focus_exited", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "size_flags_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "minimum_size_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "theme_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "draw", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "visibility_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "hidden", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "item_rect_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "ready", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "renamed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "tree_entered", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "tree_exiting", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "tree_exited", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "child_entered_tree", "args": [{ "name": "node", "class_name": &"Node", "type": 24, "hint": 0, "hint_string": "", "usage": 6 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "child_exiting_tree", "args": [{ "name": "node", "class_name": &"Node", "type": 24, "hint": 0, "hint_string": "", "usage": 6 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "child_order_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "replacing_by", "args": [{ "name": "node", "class_name": &"Node", "type": 24, "hint": 0, "hint_string": "", "usage": 6 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "editor_description_changed", "args": [{ "name": "node", "class_name": &"Node", "type": 24, "hint": 0, "hint_string": "", "usage": 6 }], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "script_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }, { "name": "property_list_changed", "args": [], "default_args": [], "flags": 1, "id": 0, "return": { "name": "", "class_name": &"", "type": 0, "hint": 0, "hint_string": "", "usage": 6 } }]
変換結果
以下は、前述の to_pretty_print_string 自作関数で PrettyPrinting の整形を行った結果です。
改行やタブにより、データが見やすくなりました。
print(ScUtil.to_pretty_print_string(signal_list))
[
{
"name": "resized",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "gui_input",
"args": [
{
"name": "event",
"class_name": &"InputEvent",
"type": 24,
"hint": 17,
"hint_string": "InputEvent",
"usage": 6
}
],
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "mouse_entered",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "mouse_exited",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "focus_entered",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "focus_exited",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "size_flags_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "minimum_size_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "theme_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "draw",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "visibility_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "hidden",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "item_rect_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "ready",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "renamed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "tree_entered",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "tree_exiting",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "tree_exited",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "child_entered_tree",
"args": [
{
"name": "node",
"class_name": &"Node",
"type": 24,
"hint": 0,
"hint_string": "",
"usage": 6
}
],
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "child_exiting_tree",
"args": [
{
"name": "node",
"class_name": &"Node",
"type": 24,
"hint": 0,
"hint_string": "",
"usage": 6
}
],
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "child_order_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "replacing_by",
"args": [
{
"name": "node",
"class_name": &"Node",
"type": 24,
"hint": 0,
"hint_string": "",
"usage": 6
}
],
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "editor_description_changed",
"args": [
{
"name": "node",
"class_name": &"Node",
"type": 24,
"hint": 0,
"hint_string": "",
"usage": 6
}
],
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "script_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
},
{
"name": "property_list_changed",
"args": []
"default_args": []
"flags": 1,
"id": 0,
"return": {
"name": "",
"class_name": &"",
"type": 0,
"hint": 0,
"hint_string": "",
"usage": 6
}
}
]
まとめ
今回は、無料・軽快な 2D / 3D 用のゲームエンジン Godot Engine 4 で、Array[Dictionary] 型などの辞書と配列とオブジェクトのデータをタブや改行を加えて見やすく整形した Pretty-Printing の文字列に変換する GD スクリプトで作った自作関数と、例としてオブジェクトの持つシグナルの一覧情報のデータのテキストを整形した結果を紹介しました。
参照サイト Thank You!
- Godot Engine – Free and open source 2D and 3D game engine
- Pretty-printing – Wikipedia
- Object — Godot Engine (4.x)の日本語のドキュメント #get_single_list
記事一覧 → Compota-Soft-Press
コメント