Godot 4.0で変わったところ
概説
フリーのゲーム開発エンジンGodot Engineのバージョン4.0 beta1を実際に使ってみて、3.5.xからの変化で個人的に戸惑った点を書き連ねていきます。同じく3.x系からの移行する方の手助けになれば幸いです。
4.0に戸惑ったら、随時追記していく予定です。
この記事の更新履歴
2022-09-18 : export, Stringのキャスト, enum, Tween, アンチエイリアスについて
2022-09-20 : シグナルを加筆
2022-09-21 : フォント, 変更になった関数名・変数名など, setgetの書き方を加筆
2022-09-22 : 型キャストまわり / → string型を加筆
2022-09-29 : シグナルで引数を渡す方法を加筆
2022-10-01 : ディレクトリやファイルの操作はDirAccess、FileAccessでを加筆
2022-10-07 : Popupのexclusiveの動作条件変更を加筆
2022-10-08 : pause_modeがprocess_modeに変更に
2023-05-07 : OpenSimplexNoise → FastNoiseLite
バグや所感、無くなった機能など
これらについては、Godotのバージョンアップで解消されていくと思われるので、スクラップにしてまとめています。
GDScript
export → @export
4.0から変数のexportが@exportに変わりました。また、@export_node_pathなどexport自体の種類も増えました。
以下にint型の場合の書き方を示します。4.0の方がシンプルですね。
export (int) var foo:int = 1 # Godot 3.x
@export var bar:int = 1 # Godot 4.0+
enumが型として使えるようになった
4.0から、enum(列挙型)が型として使えるようになりました。簡単なコードを以下に例示します。
enum SampleType {TYPE_A, TYPE_B}
# Godot 3.xでenum型の変数を宣言する場合 -----------------------------------
var sample_type:int = SampleType.TYPE_A # 宣言するときはint型
export (SampleType) var sample_type:int # exportの書き方が少し複雑
# Godot 4.0+でenum型の変数を宣言する場合 ----------------------------------
var sample_type:SampleType = SampleType.TYPE_A # SampleType型として宣言できる
@export var sample_type:SampleType # exportがシンプルになった
Tweenの書き方が刷新された
こちらの記事を参照してください。
型キャストまわり
 → String型
String(42)みたいな感じで、intやfloatを雑にString型にキャストするString()関数は4.0で廃止されました。
結論から書くと、単純なString型へのキャストはvar_to_str()を使うのが良さそうです。
詳細は以下の通りです:
調べた限りでは、var_to_str() str() String.num_int64()などの関数でString型に変換できます。なお、String.num_int64()はint型からのキャストにのみ対応しており、var_to_str()とstr()は、さまざまな型からのキャストが可能です。
いずれの関数もちゃんとString型に変換できるか、typeof()関数を使って実際に動作確認してみます。
# int型の変数を宣言
var i:int = 42
print(typeof(i)) # → 出力結果:2(int)
# ここから下のコードでString型にキャストします
# Godot 3.x での int → String キャスト -------------------------------
var the_answer = String(i)
print(typeof(the_answer)) # → 出力結果:4(=String)
# Godot 4.0+ での int → String キャスト ------------------------------
var casted_answer_one = String.num_int64(i)
var casted_answer_two = str(i)
var casted_answer_three = var_to_str(i)
print(typeof(casted_answer_one)) # → 出力結果:4(=String)
print(typeof(casted_answer_two)) # → 出力結果:4(=String)
print(typeof(casted_answer_three)) # → 出力結果:4(=String)
ちなみに、var_to_str()とstr()はほとんど挙動が同じでしたが、速度には少し差がありました。
単純なint -> Stringの型キャストを100万回行うスクリプトを10回ほど実行したところ、処理に要した時間の平均はvar_to_str()がおおよそ270msecで、str()が310msec程度でした(String.num_int64()は275msecくらい)。
Array -> Stringを100万回型キャストした場合は、var_to_str()が1380msec、str()が1800msec前後でした。Dictionary -> Stringの場合は、それぞれ5000msec, 7000msec前後です。
普通は100万回も型変換することはないと思いますが、var_to_str()関数を使うとほんの少しだけ早そうです。
シグナル
シグナル自体がクラスになり、connect()の書き方が変わりました。
以下のサンプルコードでは、子ノードである$Buttonのbutton_downとbutton_upシグナルを使って、ログを出力します。
Godot 4.0+ のconnect文の箇所、button_downとbutton_upで微妙に書き方が違いますが、どちらもやっていることは同じで、呼び出したいメソッドをconnect()の引数に指定しています。後者の方がシンプルですし、typoが起きづらくて良さそう。引数を使う場合の書き方はこちらです。
func _ready():
    # Godot 3.x のconnect文 --------------------------------
    $Button.connect("button_down", self, "_on_button_down")
    # Godot 4.0+ のconnect文 -------------------------------
    $Button.button_down.connect(Callable(self, "_on_button_down"))
    $Button.button_up.connect(_on_button_up)
# シグナルで呼び出されるメソッドの書き方は、3.x, 4.0で共通
func _on_button_down():
    print("pressed")
func _on_button_up():
    print("released")
ちなみに、Godot 4.0からはconnect()の第1引数に無名関数(ラムダ式)を書けるので、ちょっとした処理ならわざわざ別途関数を用意する必要がなくなりました。便利ですね。
    $Button.button_down.connect(func(): print("pressed"))
シグナルについては、以前submaxさんの記事でも紹介されています。
シグナルで引数を渡す方法
emit_signal()を使って引数を渡す方法はGodot 3.xと同じですが、connect()の時点で引数を設定する場合は、3.xとはやり方が変わっているので注意が必要です。
4.0では、引数付きのCallableオブジェクトを返す関数であるbind()を使う必要があります。
コードを見ていただいた方が早いかと思います。
func _ready():
    # bindで任意の個数の引数を設定できる
    $Button.button_down.connect(_on_button_down.bind("foo", "bar"))
func _on_button_down(arg1, arg2):
    print(arg1, arg2) # $Buttonを押下すると「foobar」と出力される
なお、connect()時点で引数を設定した場合は、その値がdisconnect()するまで保持されます。変数を代入した場合には、参照ではなく値を渡すので注意してください。
変更になった関数名・変数名・クラス名など
変更になった変数名などで、ちょっと調べる必要があったものやアナウンスがあったものを列挙します。なお、DirAccess、FileAccessについては後述します。
クラス名
- 
Directory→DirAccess - 
File→FileAccess - 
OpenSimplexNoise→FastNoiseLite- 
FastNoiseLiteではSimplexを含む、さまざまなタイプのノイズの生成ができるそうです 
 - 
 
関数名
- 
CanvasItem.update()→CanvasItem.queue_redraw() - 
PackedScene.instance()→PackedScene.instantiate() 
 ディレクトリやファイルの操作はDirAccess、FileAccessで
Godot 4.0 beta2から、DirectoryクラスがDirAccessに、FileクラスがFileAccessになりました。また、クラス名が変わっただけでなく、いくつかの関数がstaticになった他、new()を使った初期化やclose()処理が不要になりました。
staticでない関数も残っており、例えばDirAccessで相対パスを扱う関数は初期化が必要です。ただ、初期化する場合も明示的にnew()を呼び出す必要はありません。open()関数で、任意のディレクトリを開いた状態で初期化されます。
以下にサンプルコードを載せます。
# open()関数で初期化し、同時に指定したパスを開く
var directory = DirAccess().open("res://Sprites/")
# res://Sprites/ ディレクトリにある file_a.txt を file_b.txt に変更
directory.rename("file_a.txt", "file_b.txt")
なお、上記の例で言えば絶対パスを使ってファイル名やディレクトリ名を変更する場合は、rename()ではなく、rename_absolute()を使うことができます。こちらはstaticな関数なのでopen()を使った初期化は不要で、DirAccess.rename_absolute(from, to)のように書きます。ただし、絶対パスでしか動きません。
setgetの書き方
- 
3.x:変数の宣言後にsetgetと書いて、その後にsetter, getterの関数をそれぞれ指定する書き方でした。 - 
4.0:変数の宣言と併せて処理を書き込むことができます。 
以下に、全く同じsetget処理をするスクリプトを引用します。
# Godot 3.x のsetget --------------------------
var foo = 100 setget _foo_set, _foo_get
func _foo_set(value):
    print(value) # 値を代入したら、代入した値が出力される
    foo = value
func _foo_get():
    print(foo) # 値をgetしたら、 fooの値が出力される
    return foo
# Godot 4.0+ のsetget -------------------------
var foo = 100:
    set(mod_value):
        print(mod_value) # 値を代入したら、代入した値が出力される
        foo = mod_value
    get:
        print(foo) # 値をgetしたら、 fooの値が出力される
        return foo
 pause_modeがprocess_modeに変更に
ノードのpause_modeプロパティが、process_modeに変更になりました。それに伴い、SceneTreeのプロセスをポーズした際のノードの挙動が細かく設定できるようになりました。設定できる値は以下の通りです。
- 
PROCESS_MODE_INHERIT
親ノードのプロセス状態を引き継ぐ(ルートノードに限り、PROCESS_MODE_PAUSABLEと同じ挙動) - 
PROCESS_MODE_PAUSABLE
SceneTreeがポーズされた場合にプロセスが止まり、ポーズが解除されると動きだす - 
PROCESS_MODE_WHEN_PAUSED
SceneTreeがポーズされた場合にだけ動く - 
PROCESS_MODE_ALWAYS
SceneTreeがポーズされたかどうかに関わらず常に動作する - 
PROCESS_MODE_DISABLED
SceneTreeがポーズされたかどうかに関わらずプロセスが止まる 
3.xまでと比較して、プロセスのポーズが簡単に細かく設定できるようになりました。SceneTreeがポーズされたときにだけ動くノードを実装できたり、非常に利便性が高まった印象です。
シーンの編集関連
画像 / スプライト / テクスチャのアンチエイリアス設定
3.5.xまでは、インポートの際に画像ごとにフィルタの設定をしましたが、4.0からはプロジェクト設定か、CanvasItem.texture_filterプロパティから、CanvasItem系のフィルタの挙動を設定するようになったようです。
基本的には、プロジェクト設定でCanvasItem系のノードの初期値を設定し、ノードごとに挙動を変更したい場合はCanvasItem.texture_filterを設定する運用が良さそうです。
最初は戸惑いましたが、プロジェクトごとにまるっと管理できるので、3.x系より便利な印象。
CanvasItemのアンチエイリアスの初期値を設定する
プロジェクト設定から設定できます。以下の項目を探して変更するようにしてください。なお、プロジェクト設定画面の右上にあるAdvanced Settingsを有効にするのを忘れないでください。
プロジェクト設定 > レンダリング > Textures > Default Texture Filter

テクスチャのアンチエイリアスを個別に設定する
個別にアンチエイリアスの設定を変更したい場合には、CanvasItemのtexture_filterプロパティから変更するのが良さそうです。

フォント
4.0ではフォントの機能が大幅に拡張されています。大きなところだと例えばOpenType機能が使えるようになったり、右→左で書き方向が設定できたり、フォントだけで記事が3, 4本書けそうな充実感です。ここでは、3.xからの移行でつまずいたポイントに絞って触れます。
 DynamicFont BitmapFontは廃止
フォント関係はDynamicFont BitmapFontクラスが無くなり、SystemFont FontFile FontVariationなどのクラスが実装されました。3.x系とはガラリと顔ぶれが変わっている印象です。まだそれぞれのクラスの役割を把握しきれていないので、改めて追記するかもしれません。
システムフォントが使えるSystemFontクラスは、特定の環境では非常に便利そうです。
フォントの見た目に関する設定
3.5.xまでは、素のフォントデータとは別にDynamicFontリソースを用意し、そこでフォントの描画に関するプロパティを設定していました。4.0からはテクスチャと同じ感覚で、フォントデータ自体にさまざまなプロパティを付与できるように仕様変更されています。例えばアンチエイリアスの設定も、フォントデータ自体に付与します。
そのほかの文字の見た目に関する細かいプロパティは、別途FontVariationのリソースを用意して設定します。
 FontVariationクラス
FontVariationでは、フォントの描画に関するプロパティの設定ができます。具体的には任意の比率で太字にしたり、長体にしたり、斜体にしたりできますし、OpenType機能を使うこともできます。extra spacingの設定もここです。
なお、フォントデータをダブルクリックして開いたウィンドウのPre-render configurationsタブにあるconfigurationからも、一部FontVariationと同じ項目が設定できます。

フォントの設定画面。左の赤枠部分が、フォントデータをダブルクリックして開いたもので、右の緑枠がFontVariationの設定画面。私がインポートしたフォントは、FontVariationでのみOpenType機能が使えます。(ちなみにRendering OptionsタブにあるOpenType Featuresの項目ですが、ここから設定できるOpenType機能が無いフォントもあるそうなので、ご注意ください。今後のバージョンで設定項目がない場合は、ボタン自体が非表示になるそうです🙏)
 Popupのexclusiveの動作条件変更
Popup系のクラスが、ControlではなくWindowを継承するクラスに変更になりました。それに伴って、exclusiveプロパティの動作条件が、親ウィンドウのtransientプロパティがtrueであることになったようです。
Popup系のクラスのexclusiveをtrueにするだけでは、想定した挙動にならないので、ポップアップを使う場合には注意が必要です。
Discussion