2023/3/10 あたりから Steam版の Godot Engine も v4.0 にアップデートしていて「ついに移行せねば……」と思って移行したのですが、少し引っかかったところがあったので、備忘録を残しておきます。
目次
3.5から4.0への移行めも
プロジェクトの自動コンバート
プロジェクトマネージャーから古いプロジェクトファイルを開くと、自動で最新のプロジェクトへコンバートするかどうかのダイアログが表示されます(直接 project.godot を開くとコンバートダイアログは表示されません)。
ただ「Convert Full Project」を選ぶと最新のバージョンへの置き換えが行われるのですが、一部正常に置き換えられないことがあったので、それの解決方法を書いていきます。
関数呼び出しの引数が Callable().bind() に置き換えられてしまった
Callableとは関数をオブジェクトとして渡す場合に使う定義なのですが、どうも置き換えるべきところの判定に失敗してしまっているようです。
# コンバート後.
p.start(Callable(pos,vec).bind(sc),t,is_gravity)
というように、なぜか Callable().bind() という呼び出しに置き換えられてしまいました。
これは置き換えミスなので、もとの記述に戻してしまえばOKのようです。
# コンバート前の記述に戻す.
p.start(pos, vec, sc, t, is_gravity)
KinematicBody2Dが “CharacterBody2D” に置き換えられた
4.0 では、KinematicBody2D ノードは CharacterBody2Dノード に置き換えられました。CharacterBody2Dでは velocity 変数がすでに定義されているので、もし velocity 変数が定義されている場合は velocity変数を削除する必要があります。
自動で動作するようにコードを書き換えてくれるので修正は不要なのですが、謎の変数や関数が定義されていて冗長な記述に変換されていたので、個人的な理解のためにメモを残しておきます。
v3.xでの move_and_slide() の使い方は以下の通り。
var velocity:Vector2 # 速度
func _physics_process(delta:float) -> void:
# 重力値を加算
velocity.y += GRAVITY
# 移動+スライド
velocity = move_and_slide(velocity, Vector2.UP)
if is_on_floor():
# 床に着地している
v4.0 での move_and_slide() は引数がなくなり、velocity 変数を定義する必要がなくなりました
# この定義は不要
#var velocity:Vector2 # 速度
func _ready() -> void:
# 床として扱う方向の変更が必要なら up_velocity を書き換える
# デフォルトは Vector2.UP なので通常は設定不要.
#up_velocity = Vector2.UP
func _physics_process(delta:float) -> void:
# 重力値を加算
velocity.y += GRAVITY
# 移動+スライド
move_and_slide()
if is_on_floor():
# 床に着地している
コリジョンの位置がずれる。サイズが小さくなる
足元にくっつけて配置していたコリジョンの位置がスプライト中央に移動していました。(さらにサイズが小さくなっている)
この問題が起きたら手動で位置とサイズを直してあげる必要があります。
他のオブジェクトも見てみたのですが、全体的に小さくなってしまっているような印象ですね……。
ただ、”CircleShape2D” の場合は問題なかったので、“RectangleShape2D” だけ問題が発生しているのかもしれない。
CheckButton.pressed はシグナルとなった
チェックボタンのCheckButtonがONになっているかどうかは CheckButton.button_pressed で判定するようになりました。
Color定数が小文字から大文字になった
3.5までは、Color定数は小文字でした。
sprite.modulate = Color.red # スプライトを赤色にする
4.0 以降では、これを大文字に置き換える必要があります。
sprite.modulate = Color.RED # スプライトを赤色にする
update() は queue_redraw() に置き換えられた
_draw() を実行するための update() は queue_redraw() に置き換えられました。
setgetキーワードは削除された
Godot 3.5 では以下の書き方で setget を指定しました。
var now_room:int = 0 setget _set_now_room, _get_now_room
# 現在のルーム番号のsetter
func _set_now_room(v:int) -> void:
now_room = v
# 現在のルーム番号のgetter
func _get_now_room() -> int:
return now_room
Godot 4 では以下の書き方に変える必要があります。(setgetキーワードはなくなった)
# 現在のルーム番号
var now_room:int = 0:
# 現在のルーム番号のsetter
set(v):
now_room = v
# 現在のルーム番号のgetter
get:
return now_room
FileクラスはFileAccessクラスに名前が変わった。JSON.print()はJSON.stringify()になった
FileクラスはFileAccessクラスになり、open() が static関数になったようです。また JSON.print() は JSON.stringify() になり、インスタンス関数となりました。
Godot 3.5 のときは以下の記述でしたが……
# セーブ処理
func save_data() -> void:
var f = File.new()
f.open(SAVE_FILE, File.WRITE)
var data = _get_save()
var s = JSON.print(data)
f.store_string(s)
f.close()
# ロード処理
func load_data() -> void:
var f:File = File.new()
if f.file_exists(SAVE_FILE):
# セーブデータが存在する
var _err = f.open(SAVE_FILE, File.READ)
var s = f.get_as_text()
var j = JSON.parse(s)
if j.error == OK:
_copy_load(j.result)
else:
# セーブデータが破損していたら初期化する
init()
else:
# 存在しない場合はいったん保存する
init()
save_data()
Godot 4 では以下の記述に変更する必要があります。
# セーブ処理
func save_data() -> void:
# FileAccess.open()はstatic関数
var f = FileAccess.open(SAVE_FILE, FileAccess.WRITE)
var data = _get_save()
# JSON.print() は JSON.stringify()になりインスタンス関数になった.
var json = JSON.new()
var s = json.stringify(data)
f.store_string(s)
f.close()
# ロード処理
func load_data() -> void:
if FileAccess.file_exists(SAVE_FILE):
# セーブデータが存在する
var f = FileAccess.open(SAVE_FILE, FileAccess.READ)
var s = f.get_as_text()
var json = JSON.new()
var j = json.parse(s)
if j.error == OK:
_copy_load(j.result)
else:
# セーブデータが破損していたら初期化する
init()
else:
# 存在しない場合はいったん保存する
init()
save_data()
公式の情報ではありませんが jsonフォーマットをセーブデータとして使用することは非推奨という主張があります。
I see it very often (and some readers may be asking it already): “What if I want to use JSON to save my data?” This is my response:
Don’t use JSON for your save files!
While Godot has JSON support, saving game data is not what JSON is for. JSON is a data interchange format – its purpose is to allow systems using different data formats and/or languages to exchange data between each other.
This means JSON has limitations that are negatives for you when it comes to saving your game data. JSON doesn’t support many data types (no int vs. float, for example) so you have to do a lot of converting and validating to try and save/load your data. It’s cumbersome and time consuming.
Don’t waste your time. Using Godot’s built-in serialization, you can store native Godot objects – Nodes, Resources, even Scenes – without any effort, which means less code and fewer errors.
There’s a reason that Godot itself doesn’t use JSON for saving scenes and resources.
私はそれを非常に頻繁に見ます(そして、一部の読者はすでにそれを尋ねているかもしれません):「JSONを使用してデータを保存したい場合はどうなりますか?」これが私の返事です。
セーブファイルにJSONを使用しないでください!
Godotが持っている間JSONサポート、ゲームデータを保存することは、JSONのためのものではありません。JSONはデータ交換形式です – その目的は、異なるデータ形式および/または言語を使用するシステムが互いにデータを交換できるようにすることです。
これは、JSONにはゲームデータの保存に関してあなたにとってネガティブな制限があることを意味します。JSONは多くのデータ型(例えば、int と float は区別しない)をサポートしていないため、データを保存/ロードするために多くの変換と検証を行う必要があります。面倒で時間がかかります。
時間を無駄にしないでください。Godotの組み込みのシリアル化を使用すると、ネイティブのGodotオブジェクト(ノード、リソース、さらにはシーン)を手間をかけずに保存できます。つまり、コードが少なく、エラーが少なくなります。
Godot自体がシーンやリソースの保存にJSONを使用しない理由があります。
Godot 4 Recipes > SAVING/LOADING DATA
「Godotにはシリアライズの仕組みがあるし、jsonだとデータ型の制約もあるから面倒だよ!」とのことです。
また、2023.3.16時点、私の環境では、json.stringify() で作成した文字列を json.parse() しても正常に復元できない問題がありました。という事情も踏まえて、Godot標準のシリアライズ関数である var_to_str() / str_to_var() を使ったほうが良さそうな印象です。
JSONクラスの使い方が変わった
重複になりますが、JSONはnew()して使うようになりました。
Godot 3.5 では以下の記述でしたが……
# JSONにパースする
var json_parse = JSON.parse(text)
# パースした結果を取得.
_cdb = json_parse.result
Godot 4 だと、JSON.new() でインスタンスを生成し、”result” ではなく”data” で結果を受け取るようになりました。
# JSONにパースする
var json = JSON.new()
var err = json.parse(text)
if err != OK:
# パースエラー.
push_error("パースエラー")
# パースした結果を取得.
_cdb = json.data
Windowというクラス名が使えなくなった
Viewport周りにWindowというモジュールが定義されたので、“Window” という名前を定義することができなくなったようです。
class_name Window # ←エラー
ビューポートのサイズが置き換えられてしまう
Godot3.5 ではデフォルトのサイズが 1024×600 だったのが、1152×648 に置き換えられてしまいます。(Godot4系でのデフォルトサイズ?)
動作に問題がある場合、1024×600 に戻しておきます。
私の環境(macOS)のみかもしれませんが、ウィンドウが半分のサイズで表示される不具合が発生しています。
その場合は、以下の関数でウィンドウサイズを2倍にするととりあえず正しいサイズとなります。
# 2倍のサイズにする
DisplayServer.window_set_size(Vector2i(1024*2, 600*2))
ColorRect.rect_size プロパティがなくなって、ColorRect.get_rect().size で取得するようになった
Godot 3.5 の場合
var _bg:ColorRect
func get_height() -> float:
return _bg.rect_size.y + MARGIN_Y
Godot 4 の場合
var _bg:ColorRect
func get_height() -> float:
return _bg.get_rect().size.y + MARGIN_Y
Array.empty() は Array.is_empty() に変更された
Array.empy() は Array.is_empty() に関数名が変更されました
deg2rad() / rad2deg() は deg_to_rad() / rad_to_deg() に変更された
度をラジアンに変換するには deg_to_rad()、ラジアンを度に変換する関数はrad_to_deg()となりました。
乱数関連の関数が randf_*() に変更された
例えば rand_range() は randf_range() に変更されました
Vector2.linear_interpolate() は Vector2.lerp() に変わった
It got shortened into
lerp
.linear_interpolate in Godot 4 ??velocity_ = velocity.lerp(direction * speed, accel * delta)
TileMap.get_cell()
Godot 4ではレイヤーの仕組みとタイルを複数のソースIDに分割できるようになったため、Godot 3.5 と比べるとやや煩雑な関数となりました。
- int get_cell_source_id(layer:int, coords:Vector2i, use_proxies:bool=false)
- TileData get_cell_tile_data(layer:int, coords:Vector2i, use_proxies:bool=false)
ソースIDごとに分割しない場合は、TileData を取得して Custom Data などから判定するのが良さそうです。
Custom Dataの設定は以下のとおりです。
- 1. tilesetを開いて、Custom Data Layers から「要素を追加」
- 2 選択モードでタイルを選択
- 3. Custom Data に値を入力
Custom Dataを取得するためのサンプルコードは以下の通り (カスタムデータ名を “Terrain” とした場合)
var tile_map:TileMap # タイルマップ
...
# クリックしたセルの座標系に変換.
var clicked_cell = tile_map.local_to_map(tile_map.get_local_mouse_position())
# クリックした位置のタイルデータを取得する.
var data:TileData = tile_map.get_cell_tile_data(0, clicked_cell)
if data:
# カスタムデータ "Terrain" を取得する
var custom_data = data.get_custom_data("Terrain")
ColorRectのサイズが0になってしまう
ColorRectノードのサイズがなぜか 0 になってしまうようです。ColorRectのサイズの値は、Control > Transform > Size にあります。
get_tree().change_scene() は get_tree().change_scene_to_file() となった
Godot 3.5
get_tree().change_scene("res://path/to/scene.tscn")
Godot 4.0
get_tree().change_scene_to_file("res://path/to/scene.tscn")
Control.get_font() が Control.get_theme_font() になった
Godot 3.5
var _label:Label
func _calc_width(text:String) -> float:
# 文字幅を計算する
var font = _label.get_font("font")
return max(MIN_WIDTH, font.get_string_size(text).x)
Godot 4.0
var _label:Label
func _calc_width(text:String) -> float:
# 文字幅を計算する
var font = _label.get_theme_font("font")
return max(MIN_WIDTH, font.get_string_size(text).x)
yield は await になった
Godot 3.5
yield(get_tree().create_timer(3.0), "timeout")
Godot 4
await get_tree().create_timer(3.0).timeout
Node.find_node() は Node.find_child() になった
子ノードを探す関数の find_node() は find_child() に置き換えられました。
フォントのテーマが初期フォントに戻されている
テーマファイルに設定したフォントが初期フォントに戻されてしまっているようです。なのでフォントを使用しているテーマファイルを開いて「Default Font」からクイックロードなどで、使いたいファイルを再設定します。
ちなみにフォントサイズは “Default Font Size” から設定します。
しかしフォントのアウトラインの設定がどこかへ行ってしまった…。設定方法がわかったら追記します。
アウトラインの設定ですが、Control > Theme Override に移動したようです。
- Font Outline Color: アウトラインの色
- Outline Size: アウトラインの幅
instantiate() で null が返却される
原因は不明ですが、シーンのキャッシュが古いと発生するような印象です。
対象のシーンを編集したり、アタッチしているスクリプトを書き換えたりすることでキャッシュが更新されると、正常に生成されるようです。
シェーダー関連
var mat:ShaderMaterial = _spr.get_material()
if _is_entered:
mat.set_shader_parameter("outline_color", Color.RED)
Shader でのuniform の色指定は「hint_color」から「source_color」に変更されました。
// アウトラインの色
uniform vec4 outline_color : source_color;
.godot フォルダが追加された
特に実行の不具合というわけではないですが、4系から godot 関連のキャッシュデータとして「.godot」というフォルダが追加されたようです。おそらくバージョン管理不要なファイルのようなので、無視リストに追加しておきます。
# Godot-specific ignores
.import/
export.cfg
export_presets.cfg
.godot # これを追加
*.import
参考
その他、Godot 4.0 の変更点は以下のページが詳しいです。