Godot 4 触ってみた(主にGDScript2.0)
Godot4を待ちわびているんですが、なかなかリリースされません。
新機能としてVulkan APIを採用したグラフィック強化が注目されており、弱点とされていた3Dが強化されUnityやUnreal Engineの競合として台頭するのではないかと噂されております。
目玉としてはVulkanがよく取り上げられてますが、個人的にはGDScript2.0と題される新しいスクリプト機能に注目してます。
lambda式(無名関数)の導入、Callableオブジェクトなど、関数をファーストクラスの値として導入する試みが採用されており、Godot特有のsignalシステムなどにも統合されています。
ということで待ちきれずGodot4のnightly buildを落としてきて触ってみることにしました。
Godot4を入手する
Godot4はまだ正式リリースされてないので、入手する方法は以下の2通り
- Godotのgithubリポジトリからcloneしてビルドする
- 非公式ビルドを配布しているサイトからダウンロードする
自前でビルドは結構難しそうなので、今回は非公式ビルドをDLします。patreonで募っているそうなので、この記事を見てる人で役に立ったという人は寄付してあげましょう。
配布サイトのメンテナンス費用をインストール
macOSなので、dmgファイルをDLしてインストールする。
プロジェクト作成
おなじみの起動画面
プロジェクトを作成しようとすると、Rendererは Vulkan Clustered
と Vulkan Mobile
が選べます。
前者がデスクトップ用の高品質、後者がモバイル用の高効率というところですかね?
今回はClustered
を選びます。
3Dシーンを作成してみる
若干のAPIの更新が見られます。
例えば、Godot3.x系では、3DシーンのノードはRigidBody
, KinematicBody
のような名前でしたが、Godot4では 3D
というsufixがついていたり、KinematicBody
がCharacterBody
という名称に変わったりしています。
2Dシーンのノードは3.xの頃からRigidBody2D
のように2D
sufixがついていたので、統一されていてよりわかりやすいと思います。
多分3.x系とだいたい同じ感じで使えるでしょう。
GDScript 2.0
Godot の公式サイトのNewsとかを見て、色々と魅力的な新機能が追加されているのは知っていたんですが、百聞は一見にしかず。触っていきます。
Callable
extends Node
func hello():
print("hello, godot4!")
func exec_callable(f: Callable):
f.call()
func _ready():
exec_callable(hello)
宣言したメソッドは、Callable
オブジェクトの値として使えます。
これにより、高階関数(関数を引数として受け取る関数)などのテクニックを利用できます。
代表的な高階関数(メソッド)である map
や filter
と、次に紹介するラムダ式を組み合わせるとスッキリしたコードを書くことが出来ます。
ラムダ式(無名関数)
GDScript2.0では、名前無しのfunc
宣言を書くことで、それをCallableオブジェクトとして使うことが出来ます。
# Arrayから3より小さい値を取り出すコード
func _ready():
# ラムダ式 + 高階関数
var result1 = [1, 2, 3, 4, 5].filter(func (x: int): return x < 3)
# Godot3.x系での同じ結果になるコード
var result2 = []
for item in [1, 2, 3, 4 ,5]:
if item < 3:
result2.push_back(item)
print(result1) # [1, 2]
print(result2) # [1, 2]
例えばArrayの要素をフィルタリングするコードは、ラムダ式とfilterメソッドを使うことでコードの合成は関数を用いたものになり、filterに渡す関数を変えることでfilterの挙動をかんたんに変更することが出来ます。
コードブロックのネストも浅いです。
3.x系以前はfor loopを使った手続き的な処理になり、コードブロックのネストが深く複雑な見た目になります。
Signal Object
GDScript2.0では、Signal
はclass定義されたオブジェクトとなり、signal宣言をするとsignalがそのノードのメンバ変数として使えるようになります。
signal.connect()
とsignal.emit()
というメソッドが使える様になり、以前よりも格段に使いやすくなっています。
extends Node
signal my_singal(String)
func _ready():
# Godot4
self.my_singal.connect(func(msg: String): print(msg))
self.my_singal.emit("hello, signal!")
# Godot3.x以前(Godot4ではエラーになる)
self.connect("my_signal", "on_my_signal_func")
self.emit_signal("my_signal", "hello, signal!")
func on_my_signal_func(msg):
print(msg)
connect
メソッドの引数の型はCallableになっています。
当然、lambda式を渡すこともでき、ちょっとしたデバッグや関数を作るほどでもない処理をsignalに登録するのはすごく簡単です。
Godot3.x以前のsignalの仕様には前から気に入らないと思ってた所がありまして、それは何かというとconnect
, emit_signal
メソッドの引数にシグナルや関数名をStringで渡さなければならないというところです。
コンパイルエラーは出ず、プログラマーが名前を間違えないように気をつける以外に防止する方法はなかったのですが、CallableやSignalがオブジェクトとして引数に渡せるようになったことでより型に守られてプログラミングできるようになったと言えるでしょう
まとめ
- Vulkanばかり注目されてるけど、GDSCript2.0もかなりいい新機能盛りだくさんだよ
- lambda式, Callable によって関数がファーストクラスの値に
- signalの取り扱いがより簡単に
- はよGodot4リリースして
眠いのでここまで
Discussion