Open22

Godot4 Tipsいろいろ適宜更新

ふぉるたふぉるた

スクリプトエディタにCtrl+ドラッグドロップすると変数がさくっとできる

さらに!
スクリプトからシーンツリー内のノードを参照するのは%アクセスが便利!

  • あとからツリー内の位置を変えてもコードは%NodeNameのままなのでそのままでよい!
  • $はもう使わなくてよいかも、それぐらい%は便利


    ノード表示の%がつく。
ふぉるたふぉるた

get_tree().create_timer()はなにがどこにできるのか理解して使う必要ありっ

get_tree().create_timer()で生成されるのはSceneTreeTimerなので
timeoutに消えるかもしれないノードにconnectするとエラーが発生する

  1. get_tree().create_timer()のtimeoutに、ノードの関数を設定する
  2. SceneTreeTimerが作成され、タイマーカウントがはじまる。SceneTreeTimerはシーンツリーにひもづく。
  3. 設定した関数のノードが消える。SceneTreeTimerはシーンツリーにひもづいているので消えない。
  4. タイマーが終了し、timeoutに設定した関数を呼ぶぞ!ないぞ!エラー

解決策
消えるノードの、タイマーNodeを子ノードにしてそれを使う

  1. タイマーNodeのtimeoutに、ノードの関数を設定する
  2. タイマーカウントをはじめる。
  3. 設定した関数のノードが消える。タイマーNodeも子ノードなので一緒に消える
  4. タイマーはもうないのでタイマーが終了しない
ふぉるたふぉるた

プロジェクト設定のAdvancedSettingは、まったくAdvancedではない基本設定が埋まりまくってるので常時表示がよいです。

ふぉるたふぉるた

Godotプロジェクトが大きくなっていってエディタ動作やゲーム起動後のデバッガ接続が遅くなっていったときは.godotのeditorとimportedディレクトリを消すといい感じになります
もちろん再起動するとアドオンとかクラス再読み込みの関係でエラー出ますが3回ぐらい再起動すると再読み込みされて快適に!

ふぉるたふぉるた

UIとStateChartsの相性めちゃくちゃいいのでおすすめ
コードから状態が消えるのでシンプルになって良い
ついでにデバッガもついてて遷移も分かりやすい

アクションゲームの敵キャラのAIもStateChartsが各状態のprocessで処理を分けれるので相性悪くない。
さらにアクションだとBehaviorTreeを組み合わせるとうまくまとめられそう。
ターン制はBehaviorTreeを使った方が良い感じ。BeeTreeとか。

ふぉるたふぉるた

Godot4のオートタイル作成スクリプトを作りました。
RPGツクールMVMZ,VXのタイルセット画像をGodot4のタイルセットと、展開した画像に変換します。
ディレクトリを指定して、実行するだけなので楽です。

Godotのタイルはいろいろできるんだけど複雑かつ手数が多いので
ツクール移行者がてこずってるイメージ。

Godot4のオートタイルは3よりはやりやすくなったんだけど大量にやるのは相変わらずたいへん。

https://github.com/folt-a/godot4-rpgmaker-tile-converter

ふぉるたふぉるた

GLTFDocumentとGLTFStateを使えば3Dモデルのインポートエクスポートがかんたんに!

エクスポートしたゲームでも3Dモデルをロードできるんですねえ。MODもいける?

とりあえずアドオンでは大活躍です。

# Save a new glTF scene.
var gltf_document_save := GLTFDocument.new()
var gltf_state_save := GLTFState.new()
gltf_document_save.append_from_scene(gltf_scene_root_node, gltf_state_save)
# The file extension in the output `path` (`.gltf` or `.glb`) determines
# whether the output uses text or binary format.
# `GLTFDocument.generate_buffer()` is also available for saving to memory.
gltf_document_save.write_to_filesystem(gltf_state_save, path)

https://docs.godotengine.org/en/stable/tutorials/io/runtime_file_loading_and_saving.html#d-scenes

ふぉるたふぉるた

1スクリプトの処理をすぐ実行できるEditorScriptはとても便利。
アドオンにしてGUIをつくるまでもないバッチ処理はEditorScriptで決まり!

注意点

EditorScriptはRefCountedのみ継承で参照がないと即開放されてしまうのでawaitで待つなどができない
なのでEditorScriptの_run内でawaitを使いたいときはreference()とunreference()を使おう

Imageで作ってsave_pngなどで保存したファイルは保存時点ではインポートされていないので
後の処理でそのImageをリソースとして使うときはEditorInterface.get_resource_filesystem().scan()でスキャンさせてawait EditorInterface.get_resource_filesystem().filesystem_changedで待とう

ふぉるたふぉるた

Godot4だとブレークポイントに来た時にゲームのウィンドウがドラッグで位置変えれない問題があって、
デバッグ時に覆いかぶさって邪魔すぎます。

「エディタ設定」のウィンドウ位置を変えておくと、ひとまず邪魔にはならない!

ゲームのウィンドウ位置設定が反映されず真ん中に出る問題は
エディタだとプロジェクト設定よりエディタ設定のほうが優先されるので注意!

プロジェクト設定のウィンドウ変えてるのに反映されなーい!ってなっていいた…わかりにくくない?

ふぉるたふぉるた

Blender→Godotのアニメインポートで
SpineのEventみたいにアニメーションの任意タイミングでパラメータ拾いたいな~って思ってたけど
外部のJSONファイルに書いて、Godotのカスタムインポーターでインポート時に読み込んでAnimationに関数呼び出し追加するのが良さそうな気がする、やってみよ

ふぉるたふぉるた

Input.get_vectorを使ってたのですが
コントローラーをつないだ状態でキーボードのキー移動をすると
設定したデッドゾーンを無視してコントローラーの微妙な傾きが加えられてしまうので
Input.get_action_strengthに変更しました。ヨシ

ふぉるたふぉるた

Input.get_vectorを使ってたのですが
コントローラーをつないだ状態でキーボードのキー移動をすると
設定したデッドゾーンを無視してコントローラーの微妙な傾きが加えられてしまうので
Input.get_action_strengthに変更しました。ヨシ

ふぉるたふぉるた

Godot4.2からスクリプトエディタでregion機能が追加されていますね。
regionはアンチパターンにもなりうる機能ですが
欲しくなるときもある……かも?

とか言っていましたが
regionはコードに載せたものの機能ぜんぜん使ってませんね・・・

ふぉるたふぉるた

Godotのカスタムインポーターはいろいろ便利

インポーターの処理内でGodotのシーンを好きに組み立てて3Dシーンとして読み込む。

名前のプレフィックスやサフィックス、モデルファイル保存先のディレクトリパス、ファイル名などから判定して
シェーダーやナビゲーション、割り当てるメッシュを変えたり、
アニメーションのトリムをしたり、アニメーションにフレームを追加したりと
痒い所に手が届かなければ自分で痒い所をポリポリかくことができるぞ

ふぉるたふぉるた

3DモデルのアニメーションはGLTFを分けて作成したほうが楽で良さそう。
BlenderのGLTFエクスポートで毎回アニメーションをベイク後出力するのは時間がかかるので……

また、汎用キャラクターは同じアニメーションを使うので、最初からアニメとメッシュのGLTFを分けるようにしておく。

ふぉるたふぉるた

ループ付きのアニメーションの終了や、AnimationTreeのtravelの途中アニメーションなど終了・開始タイミングが簡単にとれないものがあります。
@toolでAnimationのデータを加工して、任意のタイミングでSignalをemitする関数トラックを作ることで解決しました。

具体的なやり方

3Dならモデルのインポーター内でやると簡単にAnimationLibraryの加工ができます。
2DだとEditorScriptを使って仮のAnimationPlayerを作って外部保存したAnimationLibraryリソースを加工するバッチ処理を作ると良さそうです。

他にも同様のやり方で好きなタイミングデータを拾うことができます。
3Dアニメの途中で音やエフェクトを再生したいなど
別途アニメーションとタイミング、引数の情報を格納したJSONを用意して、同じように関数Trackを差し込めば直接関数に処理を書くなり引数付きSignalを発行して処理するなり、いろいろな応用ができそうな感じです。

GodotはインポーターやEditorScriptのような@toolをうまく使うと効率よく制作を進められますね。

ふぉるたふぉるた

MeshInstance2Dのスクリプトからの作り方

MeshInstance2Dはmeshtextureの2つプロパティがあり、

MeshInstance3DのようにArrayMeshを登録してそれに対応するUVを登録すれば期待通りの動きをする。

MeshInstance2Dの使い方について、公式ドキュメントでは

「Sprite2Dを配置して、MeshInstance2Dに変換する」しか記載されていない。

https://docs.godotengine.org/en/stable/tutorials/2d/2d_meshes.html

確かに、このやり方をするとmeshとtextureに正しく設定されるのだが、

単純に表示させるのみで頂点の細かい制御やUVの割り当てはできない。

とにかく正しく設定されるmeshのデータは作成できるため、これを参考にスクリプトから作ってみた。
三角ポリゴン2枚の正方形で作成するとこんな感じのデータができた。
(.tscnに保存してテキストで確認)
頂点は4隅の4、index数は三角ポリゴン2枚の6なのでindexが作られていることがわかる。
"2d": true,のデータがあり、vertex_dataの数もVector3ではなくVector2になっているような気がする。
materialは存在しない。

[sub_resource type="ArrayMesh" id="ArrayMesh_g4yoq"]
_surfaces = [{
"2d": true,
"aabb": AABB(-16, -16, 0, 32, 32, 0),
"attribute_data": PackedByteArray(0, 0, 128, 63, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0),
"format": 34393296913,
"index_count": 6,
"index_data": PackedByteArray(3, 0, 0, 0, 1, 0, 1, 0, 2, 0, 3, 0),
"primitive": 3,
"uv_scale": Vector4(0, 0, 0, 0),
"vertex_count": 4,
"vertex_data": PackedByteArray(0, 0, 128, 65, 0, 0, 128, 65, 0, 0, 128, 193, 0, 0, 128, 65, 0, 0, 128, 193, 0, 0, 128, 193, 0, 0, 128, 65, 0, 0, 128, 193)
}]

失敗

これを確認して、
3Dメッシュでのやり方と同様に、
SurfaceToolMeshDataToolでZを0にしたArrayMeshを生成して割り当ててみたが、表示されない。

MeshInstance2Dの公式ドキュメントに
「 You can experiment creating them yourself using SurfaceTool from code and displaying them in a MeshInstance2D node.」
と記載はあるが、SurfaceToolを使ってもうまく生成できなかった。

SurfaceTool、Vector3にしか対応していないのでは……?

ArrayMeshのformatを2Dに設定しないといけないようだが、SurfaceToolだけではおそらく不可能。

MeshDataToolでもformatは設定できなさそう。


成功

ArrayMeshの関数add_surface_from_arraysを使ってArrayMeshを作る。

頂点はVector2の配列を使用して作成する。大きさはピクセル

var arr = []
    arr.resize(Mesh.ARRAY_MAX)
    
    var verts = PackedVector2Array([
        Vector2(0,0),
        Vector2(32, 0),
        Vector2(0, 32),
        Vector2(32, 32),
        Vector2(0, 32)
    ])
    var indices = PackedInt32Array([0, 1, 2, 4, 1, 3])
    var uvs = PackedVector2Array([
        Vector2(0.0,0.0),
        Vector2(1.0, 0.0),
        Vector2(0.0,1.0),
        Vector2(1.0, 1.0),
        Vector2(0.0, 1.0)
    ])
    
    arr[Mesh.ARRAY_VERTEX] = verts
    arr[Mesh.ARRAY_TEX_UV] = uvs
    arr[Mesh.ARRAY_COLOR] = colors
    
    var ary_mesh = ArrayMesh.new()
    ary_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arr,[],{}, Mesh.ARRAY_FLAG_USE_2D_VERTICES)

materialが存在せず、

テクスチャはMeshInstance2Dに1枚だけ指定する。

そのため、3Dメッシュの複数Surfaceのように複数のテクスチャを割り当てることはできなさそう。

テクスチャは1枚にまとめる必要がある。


これで大きなイベントCGをメッシュにして、透明部分の描画をしないようにしたり、
差分の一部分だけのメッシュを切り替えることでいい感じにできそう。
できてよかった~。

ふぉるたふぉるた

Godot4でのゲーム内状態のデバッグ監視はWindowノードで作って別ウィンドウがいいかも

デバッガーやprint、画面文字表示よりも表示対象を好きに選べて分離しやすいのが◎

別窓でも同ノードツリーなので全情報にアクセスできるし外すときも楽。

デバッグ用Windowのスクリプトの_readyで、

get_tree()してメインツリーのnode_addedシグナルとかに関数をconnectしておく。

関数では追加されたノードが監視したいものかどうかチェックして、

監視対象ならプロパティにセットして、それをprocessで毎フレームチェックする。