見出し画像

Godot Game Engine 4.0 勉強メモ

2023/09/14追記
これはGodotほとんど触ってない頃に座学だけで書いたメモ書きです。改めて読むとほんとわかりにくい~! 筆者の他の記事も読んでね!

この記事は、著者がGodot Game Engineを公式ドキュメントを読みながら勉強するにあたって、理解したことを備忘録的に書き連ねたものです。なので、他の人に読ませることをあまり意識していません。同じくらい初心者の人がGodotを始めるときに、ちょっとだけプラスになればと思って公開する次第です。
まだ本格的に使っていないので、間違いがたくさんあります。また、ゲーム系やIT系の職場にいたことがないので、用語や常識が間違ってたりすると思います。そこは勘弁してちょ。


■Godot大枠

Godot

読み方: ゴドー
ライセンス:
 MITライセンス。著作権条文表示が必要。コピーレフトではない。
 改造して自分のプログラムに利用してもコード公開の必要は無い。
 改造したものを公開するときにMITライセンスにする必要は無い。
初版: 2014年
対応プラットフォーム: Windows、Mac、iOS、Android、等
 コンソールへの移植の実績はあるっぽい。
スクリプト:
 独自スクリプト。(動的型付けだが静的にも書ける)
 C#にも対応。
 ビジュアルスクリプティングは存在したが、次世代版4.0で廃止。
 ネイティブコード呼び出し可能。
描画API:
 OpenGLとVulkan。次世代版4.xではVulkanがデフォルトに。
 4.0でOpenGLは一度捨てて、4.1までに再構築する予定。
 DirectXに対応しない。OpenGL系に1本化することでメンテ性重視。
描画パイプライン:
 デファードレンダリングではない。
 Clusterフォワードレンダリング(後述)を採用。
 HDRでレンダリングされて、最終的にトーンマッピングされる。
 シェーダ内はリニア色空間。
 2DもUnityレベルで扱える。
座標系: 右手Y-Up。-Z-Front。
 同じ座標系のソフトは、Maya、Softimage、OpenGL、Vulkan。

[メジャーバージョンのリリース日]
Godot 1.0: 2014年12月
Godot 2.0: 2016年2月
Godot 3.0: 2018年1月
Godot 4.0: 2022年Q4予定(22年1月からα、9月からβテスト中)
入門するには良いタイミングなように思う。

[Vulkan]
・グラフィックスのローレベルAPI。
・OpenGLの後継。OpenGLはGPUシーンとの乖離が大きくなったので、
 リーブトしたのがVulkan。高レベルライブラリとしての側面も
 あるOpenGLに対して、これはもっと薄い。
・2016年にVer.1.0。
・DirectX12同様、Mantleリスペクト。
 つまり小さなコマンドキューを発行する、ハード寄りの今風API。
 ちなみにMantleはいつのまにか更新が止まってしまった。
 結果的にVulkanはMantleの精神的続編。
・シェーダー言語としてOpenGL同様、GLSLを使う。
・C++、.NETで使用可能。
・Windows、Linux、AndroidのGPUドライバがVulkan対応済み。
・Appleは2022年現在も対応せず。
 Mac、iOSはMetal(Apple版Mantle的存在)経由で動く。

DirectXが世界の中心だと思って生きてきたので、OpenGL系に不安を感じていたが、思ったよりも存在感があり、大丈夫そう。

[ライセンス]
Godot自体のMITライセンス表記が必要。それとは別に、内部で使われている次の3つのライブラリに関しても、ライセンス表記が必要。これらのライセンス表記用テキストは、エンジンのグローバル関数から文字列として取得可能。
・FreeType(フォントラスタライザ)
・ENet(通信)
・mbed TLS(通信)
FreeTypeは公式サイトによると、GPLv3とGPLv2の2択から使用者が好きに選べるらしい。GPL・・・、大丈夫なのだろうか?

[Godot4]
現行のGodot3.xの次世代版として、Godot4がβテスト中。
・対応プラットフォームとしてのHTML5の廃止
・ビジュアルスクリプティングの廃止
・OpenGLを一度捨て、4.0でVulkanに一本化
・GLES3(OpenGLの組み込み機版)の一時廃止、4.1までに復活
・GLES2の廃止
・物理を内製に戻した(内製→Bulletエンジン→新内製)
・アニメーションを刷新、互換性向上
・スクリプト(GDScript)の高速化、機能追加
・.NET6対応(21年に出た.NET)
・ネイティブ呼び出しの刷新(GDNative→GDExtension)

[エディタ]
エディタはC#(Mono)対応版と非対応版の2種類がある。
エディタのファイル構成は、C#非対応版の場合、112MBのexeと専用コンソールの小さなexeのみ。実質1ファイル。エンジンのエディタがexeファイル1個だけで出来ている。C#対応版の場合さらに24MB程度のdllやnupkg(zip)が付いてくる。どちらにせよ、インストール不要でROMからでも起動可能。
環境設定は<C:\Users\ユーザ名\AppData\Roaming\Godot>以下に保存される。

[ビルド]
C#非対応版の場合、ビルド時に一切コンパイルの類を行わない。どういうことかというと、実行ファイルそのものが既に存在する。コンパイル済みのコア部分、60MB程度のexeファイル1個のみ、がネットにアップロードされている。エディタ上からダウンロード可能で、これにリソース(スクリプトとアセット)を添付したものが自分のゲームになる。
リソース部はPCK(非圧縮のZIPのようなもの)またはZIPファイルとして出力される。複数のPCK/ZIPファイルを含めることができ、スクリプトからロード可能。これによってパッチやDLCやModを実現する。
※2023/11/01追記:
「コンパイルをしない」は言い過ぎで、実際はスクリプトはある程度事前処理されてるだろうし、シェーダはVulkan用の中間言語にコンパイルされる模様。

[Cluster Forwardレンダリング]
視錐台を縦横奥行きで分割して、各セルが接触する点光源のリストを作る。このリストをシェーダ内で参照して、点光源データを取得して、フォワードレンダリングする。バイオハザード7で使われているらしく、マニアックなものではなさそう。
点光源の数だけシェーダーが走るDefferedの弱点を克服しているので、点光源が異常に多くても軽いらしい。点光源リストを作るのはコンピュートシェーダーでやるので、きっとGPUとのやり取りも少ないのだろう。

[機能の方針]
仕様や、ロードマップ、公式ドキュメントのFAQから、シンプルさを維持しようとしている堅い意志がうかがえる。
・メンテ性維持のためにPython対応をやめた。
・ビジュアルスクリプトさえも4.0で切った。
・DirectX非対応。Vulkan1本で全プラットフォーム対応。
・レンダリングパイプラインも2つのみ。どちらもフォワード。

■プロジェクト

フォルダを指定してプロジェクトを作成する。作成時にレンダラーを選択する。レンダラーはForward+とMobileの2種類がある。後から変更は可能だが、変更は非推奨とのこと。また、ついでにGitの設定ファイル(gitignoreとgitattributes)を作成可能。

プロジェクトを作成すると、プロジェクトフォルダには、次のファイルやフォルダが生成される。
project.godot: プロジェクトファイル
icon.svg: アイコン。初期状態で唯一のリソース
icon.svg.import: アイコンのインポート設定
.gitignore: gitignoreファイル
.gitattributes: Git設定ファイル。改行設定とか
.godotフォルダ: 中間生成物を入れる空のフォルダ

project.godotファイルが在るフォルダが、プロジェクトフォルダとして認識される。
このフォルダは同時に、リソース(アセット)のルートフォルダにもなっている。つまりリソースはフォルダを介さない限り、プロジェクトフォルダ直下にべた置きされる。リソースフォルダのパスは、<res://>。ちなみにパスは"/"で区切ることが推奨されている。"\"はエスケープ文字に使うから、とのこと。

中間生成物は.godotフォルダに保存される。ちなみに、プロジェクト作成時にgitignoreにて指定されているignore(除外)対象は1つだけであり、それがこの.godotフォルダである。つまりこのフォルダ以下は消してもいいし、それ以外は全部必須。わかりやすい。

<res://>下に置かれたファイルは、エクスプローラ経由のコピペであっても自動的にインポートされる。そこはUnityと同じ。とはいえ、インポートから除外したいファイルをあえて置きたいときもあるだろう。その場合は、<.gdignore>という名前の空ファイルを作成すればいい。これでこのファイルが置かれたフォルダ以下は、インポートから除外される。名前が紛らわしいが、gitignoreではない。

■リソース

Godotでユーザが用意するものはすべて「リソース」。スクリプトやシーン(UnityのPrefabのようなもの)もリソースである。概念だけでなく、内部実装的にも、ScriptクラスとPackedSceneクラスはResourceクラスのサブクラスである。

リソースのインポート

Unityはインポートした元ファイル、いわばソースファイルはプロジェクトに含まれる。そしてそこから生成されたアセット(pngならばSprite、fbxならMesh)は、インポートしたファイルに強力に紐づいていて、引きはがすことができない。
一方UEは、ファイルをインポートしてアセットを生成すると、ソースファイルとの縁が切れる。再インポートの簡易化のためにソースパスこそ記録されてはいるが、アセットになった時点で独立した別ファイルとなる。ソースファイルはプロジェクトに含まれない。
Godotのリソースの概念は、ソースファイルがプロジェクトに含まれるという点では、Unityに近い。しかし、ソースファイルとアセットオブジェクトが明確に区別されているUnityと違い、Godotではソースファイルとリソース(のファイル)の関係が地続きである。

Godotのリソースとして存在するファイルは、エディタ外から持ち込んだもの、エディタ上で作成したもの、エディタスクリプトで保存したもの、由来に関わらず、ファイル形式に応じて次の3つに分類される。
 A: リソースとして直接ロード可能
 B: Aの型式に変換可能
 C: それ以外
Aの場合はそのままROMに格納される。ddsファイルやjsonのようなオープンな形式の他、シーン(.tscn)やスクリプト(.gd)のようなエンジン独自の型式もこのタイプである。.resファイル・.tresファイルは、Resource派生クラスのデータをシリアライズ化してバイナリ・テキスト形式で保存したもので、様々なリソースデータで使われている。
Bの場合はAに変換される。これがGodotにおける『インポート』である。元ファイルを残したまま、別のファイルが生成される。変換されたファイルは<res://.godot/imported>フォルダ以下に保存される。例えばpngファイルをTexture2Dとしてインポートすると、.ctexファイルとして保存される。
Cの場合はインポート(変換)されないが、一応ROM(ビルド)には残る。スクリプトで自前で読み込むなどする。

※上記は、gltfとglb形式に関しては少し例外的である。リソースとして直接ロード可能な形式ではあるのだが、インポート対象(この場合B)でもある。

このような思想なので、Godotでは基本的に1ソースファイル1リソースである。ではblendやfbxのような、モデルデータの扱いはどうなるのか?1つのソースファイルの中に複数のメッシュデータやアニメーションデータが収められているはずだ。
モデルファイルは、シーン(UnityのPrefabのような存在)として取り込まれる。そしてメッシュやアニメーションは、シーンに埋め込まれた(公式の用語では「内蔵/Built-In」)リソースになる。これらのリソースは、シーン(Prefab)をロードさえすれば、get系関数を繰り返して取り出すことが可能である。一応、内蔵リソースにはパス(<元ファイルパス+::+クラス名+_+謎の文字列>)が存在するのだが、直接ロードすることはできない。
実は、メッシュとアニメーションは、シーンに内蔵するのではなく、個別のリソースファイルとして保存することも可能である。この場合、シーンリソースは、そうやって保存した他ファイルのリソースを参照することになる。詳細は後述。

md5ファイル

importedフォルダ下には、インポートで生成されたリソースファイルとは別に、同名の.md5ファイルが作成される。例えばMyTex.pngをインポートするとMyTex.md5ファイルができる。テキストエディタで開くと分かるが、これは、元ファイルのハッシュ値。Unityはタイプスタンプでアセットファイルの変更を検知するが、Godotの場合はこのハッシュ値で検知する。このほうがバージョン管理ツールと相性がいいのだろう。

load

リソースはGDScriptの組み込み関数であるload関数でロードされる。引数にはパスを指定する。ResourceクラスはRefCountedクラスのサブクラスで、名前の通り参照カウンタで管理されており、参照するオブジェクトがいなくなれば自動的にアンロードされる。load関数の代わりに、ResourceLoaderクラスのloadメンバ関数を使ってもよい。

load関数で指定するパスは、リソースとして直接ロード可能なファイルであろうとなかろうと、プロジェクトに持ち込んだファイルのパスを指定する。直接リソースになるGodotネイティブな形式の場合(前述のA)、ファイルの内容がそのままリソースに変換される。一方、リソースとして直接ロードしない、インポート時に変換されるファイルの場合(前述のB)は、元ファイルのパスを指定する。すると、リダイレクトされ、importedフォルダ下の変換後のファイルが読み込まれる。例えば、パスとして<res://mytex.png>を指定すると、<res://.godot /imported /mytex.png_(UUID) .ctex>が、Texture2Dとして読み込まれる。

実は、importedフォルダ下のファイルも、パスを指定することで直接load可能である。例えば、load("res:// .godot /imported /mytex.png_ -218a8f2b3041327d8a5756f3a245f83b .ctex)"とすれば、load("res:// mytex.png" )とするのと変わらず、Texture2Dをロードできる。ただ、これは非推奨なやり方だろう。

preload

スクリプト中にpreload関数が使われていると、引数で指定されているリソースが、シーンのロード時に同時に読み込まれる。UEのように芋づる式にロードするわけだ。動作としてはおそらく、シーンのロード時に、ノード(後述)にアタッチされているスクリプトがすべて走査されて、preload関数が使われているかどうかチェックされるのだろう。

アセットではないResource

Resource派生クラスが、すべて一般的な意味でのアセットというわけではない。
例えばInputEventというResource派生クラスがあるが、これは操作入力内容が格納されたオブジェクトで、エンジンで毎フレーム生成され、通常は1フレームで使い捨てられる。
また、ViewportTextureは、レンダーターゲットをテクスチャとして使うためのリソースだが、これはファイルとしてROM(ビルドのリソース)に仕込んでおく類のものではない。ゲーム中にスクリプトで生成するか、無名のリソースとしてシーンリソース(.tscn)にパックしておくのが通常だろう。
いずれもエディタのGUI上で作成してしまえるし、リソースファイルとして個別に保存もできる。が、それをしてもあまり意味がない。おそらく一貫性を持たせるための設計。
※2023/11/01追記:
厳密には、ViewportTextureはゲーム実行中であっても単体でnewして作成するものではない。Viewportを作って、その内部で自動的に作成されたViewportTextureをgetするしかない。

■シーンツリー

ノードとシーン

Godotではノードの木構造で2D/3D空間のオブジェクトを構成する。ノードは、Unityで言うとGameObjectとコンポーネントに相当する。UEではアクタやシーンコンポーネントに相当する。
木構造の一部をファイルに保存したものがシーンになる。つまりUnityのPrefabやUEのBPのような存在。シーンはインスタンス化することで、別のシーンのノードになる。入れ子構造にできる。
ノードにスクリプトをアタッチしたり、パラメータを設定していくことで、ユーザはシーンを作り上げていく。シーンとシーンを組み合わせることで、最終的にゲームが出来上がる。

[レベル]
Unityの「Scene」やUEの「レベル」に相当する、レベルエディット専用のリソースは、Godotには無い。Godotではレベルデザインもシーンで行う。シーンをツリーの最上位に展開することで、ステージや場面の遷移をする。
言い換えるならば、Unity風に言うと、GodotではPrefabとSceneの区別が無い。UE風に言うと、LevelとBPの区別が無い。

[シーンツリー]
ノードはシーンツリーというオブジェクト下に配置されて、はじめて実行状態になる。シーンツリーはエンジンが起動後に自動で生成し、常駐し続ける。ちなみにSceneTreeクラスの継承関係は、Object<-MainLoop<-SceneTree。MainLoopというクラスを継承していうので、シーンツリーがどういった性格のものか、ちょっとうかがい知れると思う。
厳密には、シーンツリーの下にはルートビューポートノード(後述)があり、これはrootノードとも呼ばれ、この下にユーザのノードが配置される。rootノードは自動的に作成され、破棄できない。ユーザの配置するノードはすべて、rootノードが文字通りルートになる。後述のAutoLoad機能で自動追加されるノードも、rootノード直下に配置される。

このような所有関係になっている。エディタ上では、編集中のシーン以下しか表示されないし、エディタ実行中もrootノード以下しか表示されない。
シーンツリー
 →rootノード(ルートビューポート)
  →ユーザのシーン
  →AutoLoadシーン1
  →AutoLoadシーン2
  →AutoLoadシーン3…

[ノードの種類]
2D/3Dという切り口では、ノードは次の3つに大別可能。
・CanvasItem派生クラス→2D
・Node3D派生クラス→3D
・その他
CanvasItemはさらに、ゲームコンテンツ(スプライト)用途のNode2Dと、UI用途のControlに派生する。
※Godot3では、Node3DはSpatial(空間的)という名前だった。
 古いドキュメントを読むときのために留意するといい。

3Dのトランスフォーム

[3D座標系]
座標系は右手+YUpの-ZFront。Maya、Softimage、Houdini、OpenGL、Vulkanと同じ。多分glTFとも同じ。
オイラー角はZ,X,Yの順番で回転する。これがロール→ピッチ→ヨーの順番なのは、Unity(ZXY)やUE(XYZ)と同じ。
オイラー角は軸方向から原点を見て、反時計回りが正方向。

[Transform3D]
Node3DノードはTransform3Dクラスを包含しており、3D空間上のトランスフォームを保持する。Transform3DクラスはBasisプロパティとOriginプロパティを持っている。Basisは、回転とスケールの変換行列(3x3)になっている。
x' = xx * x + yx * y + zx * z
y' = xy * x + yy * y + zy * z
z' = xz * x + yz * x + zz * z
Originは要するにTranlation。Vector3クラス。

Godotはオイラー角やクォータニオンではなく、BasisとOriginの使用を推奨している。ただし、Basisは変換行列の宿命で、回転するたびにスケールが徐々に少しずつ変化してしまう。歪む。なので、Basisは都度ノーマライズすること、その上で、回転するノードとスケールするノードを分けて構築することが、ドキュメントでは推奨されている。

2Dのトランスフォーム

CanvasItemノードは、ゲーム内容用途のNode2Dノードと、UI用途のControlノードに派生する。それぞれトランスフォームの保持方法が違う。
2Dの座標系は、左上原点で、右がX、下がY。Zは無い。

[Transform2D]
Node2D派生ノードはTransform2Dクラスで、2D空間上のトランスフォームを保持する。これは、Transform3Dと同様、内部的に行列(Vector2[3])で実装されている。2x3の行列になっていて、プロパティとして、xとy(それぞれどちらも2Dのベクトルで、xとyの回転、スケールを表す)、Origin(これも2Dのベクトル。オフセット成分)を持つ。

[Layout]
一方、Control派生ノードはLayoutという仕組みで、トランスフォームを保持する。親Controlに対して相対的に、自Controlの矩形がどのように配置されるかを記録する。これは役割としてはUnityのRectTransformのようなものだが、少し思想が違う。(後述)

ビューポートとカメラ

カメラオブジェクトに機能が集約されているUnityと違い、Godotでは描画の起点や主体となるオブジェクトと、描画の視点を提供するオブジェクトが分かれている。これはUEを触ったことがあるとピンと来ると思う。UEのプレイヤーコントローラーとカメラの関係だ。

[ビューポート]
ビューポートはノード。実際には、Window(およびその派生クラス)とSubViewportの2つのノードに派生している。プロパティとして、レンダーターゲットや解像度、クリアモード、AA設定等を保持している。ビューポートはノードツリーの中で仕切りのように働き、次のルールでビューポートの領域(公式の用語ではない)を形成する。
・ビューポートの子孫のノード
・ただし、子孫にビューポートがあれば、それ以下は対象外
直観的には「直轄」のノードがそのビューポートの領域。領域内におけるカメラが、ビューポートへの描画時の視点となる。描画対象(メッシュなど)は領域外にあってもよい。

実はビューポートは、描画だけでなく操作入力にも関係してくる。領域は、inputコールバック(後述)の呼び出しにも使われる。

ちなみに、Windowを作成すると、本当にWindowsのウィンドウ的なものがゲーム内に表示されるようになる。タイトルバーがあって、右上に×ボタンまで付いている。普通のゲーム用途にはSubViewportを作成する。

[カメラ]
カメラもノード。プロパティとして、カリングレイヤーやFOVなどを保持している。最も近い先祖ビューポートに、描画対象の選別と座標変換を提供する。Camera2DとCamera3Dの2種類が存在する。

1ビューポート1カメラ。領域内に複数カメラが存在するときは、最もビューポートに近いカメラが使用される。ただし、カメラにはcurrentプロパティ(bool)があり、current == trueのカメラが優先される。

[ルートビューポート]
前述の通り、シーンツリーの直下は特別なビューポート、ルートビューポートとなっている。これは変えられない。ルートビューポートのレンダーターゲットが、一般的な意味でのフレームバッファという認識でいいだろう。

ルートビューポートは自動的に作られ、ユーザは直接手を出すことができない。AA(アンチエイリアシング)のモードなどもViewportのプロパティであるが、ルートビューポートの場合はそれらをプロジェクト設定から事前に設定しておくことができる。

Godot3では、ルートビューポートもその他のビューポートも、同じViewportクラスだったが、Godot4では、WindowとSubViewportの2つのサブクラスに派生して、それぞれ使い分けられるようになった。ルートビューポートがWindowである。ベースクラスのViewport自体は、エディタ上では作成不可となっている。

ビューポートと描画対象

前述の通り、ビューポートの領域は、描かれるノードを選別しない。オブジェクトが領域外にあっても、カメラが領域内にあるならば描画される。
Godot3 日本語
 docs . godotengine .org / ja/stable/tutorials /rendering/ viewports . html
Godot4 英語
 docs . godotengine .org / en/latest/tutorials /rendering/ viewports . html
上記のGodotのドキュメントを読むと、Viewportの機能について、このような解釈ができる。
A. 領域内のカメラによって描かれる。
B. さらに、領域内にあるノードだけが描かれる。
しかし実際はBのような機能は無い。この解釈は誤り。そもそも、もし領域内にあるノードだけ描かれるのであれば、複数のカメラで同一のノードを描画できなくなる。

※2023/11/01追記:
2Dのみに限って言えばB.も間違いではない。

■メッシュ

ArrayMesh

3Dモデルをロードすると、メッシュとしてArrayMeshリソースが生成される。この場合の"Array"はおそらく頂点のArrayという意味。親クラスにMeshリソースというものも存在するが、これはインターフェースまたは抽象クラス的な存在で、メッシュといえばArrayMeshで問題無い。MeshからArrayMeshとPrimitiveMeshに派生していると聞けば、なんとなくその立ち位置がうかがえる。

Godotにおいては、メッシュのマテリアル違いのサブグループをサーフェスと呼ぶ。

メッシュはサーフェスごとに、デフォルトのマテリアルを持つ。インポート時には必ずなんらかのマテリアルを割り当てなければならない。しがらみのないメッシュを持ちたければ、事前に「無」のマテリアルを作っておくなどのテクニックが必要だろう。

スクリプトからメッシュ生成

ロード画面をはさむなどして、もしくはプチフリを許容して、静的にメッシュを生成したければ、ArrayMeshを使う。newしてArrayMesh .add_surface _from_arrays関数などを呼び出せばよい。SurfaceToolというクラスを使って、簡単に作ることもできる。ArrayMeshの解析や編集に使える、MeshDataToolというクラスもある。

動的にメッシュを生成したい場合は、ImmediateMeshノードを使う。リソースではなく、ノードである。これは毎フレーム更新しても十分なパフォーマンスが得られる。VFX、特に軌跡エフェクトなどを作るには、これを使うといいだろう。

■マテリアルとシェーダ

シェーダとマテリアルの定義は、エンジンやDCCツールによっては全然予想と違ったりするが、Godotの場合は一般的な意味で解釈していい。シェーダはGPUに処理させるプログラム、マテリアルはシェーダとそれに与えるパラメータの組み合わせのセット。

マテリアル

マテリアルはリソース。エディタ上で作成して保存する。またはスクリプトから動的に生成する。
マテリアルのタイプ(サブクラス)
・StandardMaterial3D(PBR)
・ORMMaterial3D
 (遮蔽(O)、ラフ(R)、メタル(M)を1枚のテクスチャにまとめたPBR)
・ShaderMaterial(カスタムシェーダ)
・CanvasItemMaterial
・ParticleProcessMaterial
等々計10種類。
ちなみに、Godot3では、マテリアルの種類がSpatialMaterial、ShaderMaterial、CanvasItemMaterial、ParticleMaterialの4つだけだった。SpatialMaterialが廃止されて、StandardMaterial3DとORMMaterial3D(ともにBaseMaterial3Dのサブクラス)が導入されたようだ。パーティクル系も名前が変わっている。

[StandardMaterial3D]
いわゆるPBRマテリアル。Albedo、Metallic、Roughness、Emissionを含む、よくある構成。
興味深い機能として、Diffuse ModeとSpecular Modeというプロパティで、陰影や光沢の出方を非物理的に設定できる。Diffuse Modeは陰影の計算方法を変更する。モードとしては、Disney PrincipledなBurley、旧来の計算式であるLambert、Roghness値に応じて拡散光を裏面まで回り込ませるLambert Wrap(陰が出にくく、柔らかくなる)、陰影をゼロイチはっきりさせるToonの4種類。Specular Modeは光沢部の計算方法を変更でき、これもToonにできる。

オプションパラメータがたくさん付いていて、パフォーマンスはさておき、大抵のことはこのマテリアルクラスだけで出来てしまいそうではある。
Unshaded: ライティングオフ。
Add/Subtract: 加算/減算。
Detail: 第2のUVを使ったデカール。
Billboard: 文字通りビルボードにできる。
Grow: 法線方向に頂点をずらす(原始的な輪郭表現に使える)
UV1 Scale/Offset: UV。オフセットとスケール。
UVを使ったパラパラ漫画的な炎も作れてしまう。

[ShaderMaterial]
自作シェーダ用のマテリアル。パラメータをエディタ上で設定できる。自作シェーダしか設定できない。というよりそもそもおそらく、組み込みのシェーダは、リソースとして認識されない。

シェーダ

リソースとしてのシェーダは次の2種類。
・Shader
・VisualShader
ShaderはGodotエディタ上でGLSLで記述する。VisualShaderはビジュアルスクリプティング的にノードグラフを作って、シェーダを定義する。ノードグラフは中間言語的にGLSLになって、プレビューできる。

Shaderでは、GLSLのコードで次の3つの関数を定義する。
vertex():
 いわゆる頂点シェーダ。頂点の数だけ走る。
fragment():
 ピクセルシェーダの色決定部分。ピクセルの数だけ走る。
light():
 ピクセルシェーダのライティング部分。
 ピクセルの数×ライトの数だけ走る。
シェーダのタイプが後述のparticle、sky、fogの場合には、satart()、process()、sky()、fog()を定義する。

シェーダのタイプには次のものがある。GLSLのコードの冒頭で宣言する。
・spatial
・canvas_item
・その他(particle、sky、fog)
おそらくこの宣言で、GPUに処理させる(本来の)シェーダプログラムのひな形が決まって、それと組み合わせてコンパイルされる。ひな形内では前述のvertex、fragment、light関数が呼ばれたり呼ばれなかったりする。

■テクスチャ

ImageとTexureの違い

Imageリソースはメインメモリ上のピクセルデータ配列。一方でTextureリソースは、これ自体はピクセルデータを持たず、実態はRenderingServer上の(おそらくVRAM上の)バッファにある。例えば、CompressedTexutre2Dは、ファイルロード時に一度内部でImageとしてデータを作成し、そのImageを持ってRenderingServerに対して初期化依頼をする。

テクスチャの種類

[CompressedTexture2D]
通常のテクスチャ。Godot3ではStreamTextureという名前だった。Texture2Dとしてインポートした画像ファイルはこれになる。importフォルダ下のファイルは.ctexファイル。

[ImageTexture]
Imageから生成したテクスチャ。ImageTexture . create_from_image( Image )関数で生成できる。用途としては、CPU(スクリプト)で動的に生成した画像をテクスチャとして使いたいときなど。ちなみに、ddsファイルはプロジェクトに取り込んでも「インポート」されず、ImageTextureとしてloadされる。

[ViewportTexture]
ビューポートの内容をテクスチャとして用いるためのリソース。プロパティとして、対象となるビューポートを参照するノードパスを持っている。他のTextureリソース同様、これ自体はバッファを持たないし、VRAM上のレンダーターゲットを利用するためのインターフェースやトークンに近い。独立したリソースファイル(.tres)としてROM上に存在できるが、あまり意味がない。シーンにパックされるか、スクリプトでゲーム中に作られるのが通常だろう。
ちなみに、CameraTextureリソースというのもあるが、これはARなどに使う物理カメラのテクスチャであって、レンダリングに使うカメラとは関係ない。

[CanvasTexture]
Canvasと冠しているが、Viewportとは関係ない。
CanvasItem(2Dオブジェクト)でノーマルマップやスペキュラーを使うためのテクスチャ。マニアックな存在。

[Texture3D]
いわゆる3Dテクスチャのベースクラス。

[TextureLayered]
配列タイプのテクスチャのベースクラス。要するにCubeMapに派生する。

テクスチャのインポート

PNGファイルをCompressedTexture2D(.ctex)としてインポート可能。または、ImageTexture(.image)やCompressedCubemap(.ccube)にもできる。

PNG以外だと、jpgやbmp、tgaにも対応している。

興味深い対応形式としてはsvgがある。svgはベクター式の画像ファイルとしてはメジャーで、イラレはもちろん、Visioも対応している。ちなみに、SVGはBlenderでも取り込み可能で、画像ではなく、カーブ(メッシュ?)になるらしい。覚えておくといいことあるかも。

■ライティング

ライトの種類

[指向性ライト]
並行光源。影を生成できる
[オムニライト]
点光源。減衰カーブを指定できる。影も生成できる。
[スポットライト]
スポットライト。影も生成できる。

シャドウアトラス

オムニライトとスポットライトのシャドウマップ(デプスバッファ)は、シャドウアトラスという仕組みでバッファが割り当てられる。巨大なシャドウマップが4等分され、さらにその中を固定サイズのシャドウマップスロットとして管理する。プロジェクト設定から、全体の解像度と、4つの領域への最大割り当て数を設定しておく。距離に応じて最適な解像度のスロットが使用される。

ReflectionProbe

ReflectionProbeはノード。環境光・反射プローブ。実装はおそらくキューブマップを使用している。エディタ上でベイクする性格のものではなく、ランタイムでの更新を前提にしている。モードが2つあって、UPDATE_ONCEの場合はデータDirty時に6フレームに跨って更新される。UPDATE_ALWAYSの場合は毎フレーム更新される。

VoxelGI

VoxelGI(Godot3ではGIProbe)はノード。UEやトゥモローチルドレンで使われているような、ボクセルコーンレイトレーシングによる環境光表現ができる。リアルタイム。

Environmentとライティング

環境光や反射には、EnvironmentというリソースのAmbient LightとReflected Lightというパラメータが反映される。Environmentはライティングのためだけのリソースでなく、設定内容には、背景(空、塗りつぶし)やトーンマップ、フォグなどがある。
Environmentはビューポートなどのノードにセットされる。ルートビューポートには必ずセットされており、SubViewportにEnvironmentがセットされていない場合は、最終的なフォールバック先としてルートビューポートのEnvironmentが適用される。描画されるオブジェクトはなんらかのViewport下にあるので、Environmentの存在は無視できない。カメラやWorldEnvironmentノードにEnvironmentをセットし、ViewportのEnvironmentをオーバーライドすることもできる。(後述)

■EnvironmentとCameraAttributes

Environment

Environmentはリソース。描画に関わる様々な設定がEnvironmentに集約されている。逆を言うと、この『環境(Environment)』は描画以外の環境を意味しない。例えば物理とかは関係ない。

[背景]
描画前のレンダーターゲットのクリア方法。Godotでは、カメラやViewportではなく、Environmentの設定になっている。選択肢は、単色の塗りつぶし、Skyリソース(空)、CanvasLayer(UIを背景に表示)、物理カメラ(AR的な用途)、クリア無し、等。
[環境光]
背景を使用、特定カラー、無効、特定Skyリソース(空)の4種類。混乱しそうになるが、背景に単色やSkyを使用していて、それをそのまま環境光にしたければ、「背景を使用」でいい。敢えて背景とは別の色や別のSkyを環境光にしたければ、ここでオーバーライドするといいだろう。
[Reflected Light]
背景を使用、無効、特定Skyリソース(空)の3種類。
[トーンマップ]
トーンマップの設定もここにある。選択肢は、リニア(1.0でサチる)、Reinhardt(c=c/1+c)、Filmic、等。補正係数(whiteとexposure)もある。
※トーンマップとは別に、カメラの露出設定が別リソースに存在する。
[SSR]
スクリーンスペースリフレクションの設定。
[SSAO]
SSAOの設定。
[Glow]
いわゆるブルーム。
[Fog/Volumetric Fog]
フォグ。
[Adjustment]
最終的な色補正。ルックアップテーブルとなるテクスチャを設定できる。

ルートビューポートには、プロジェクト設定を反映したEnvironmentが設定されており、これがオブジェクト描画時のデフォルトのEnvironmentになる。Environmentは、特定ノードのプロパティによって次のようにオーバーライドされる。
 優先順
 1. Camera3DのEnvironment
 2. WorldEnvironmentのEnvironment
 3. サブViewportのWorld3Dリソース内のEnvironment
 4. ルートのWorld3Dリソース内のEnvironment(デフォルト)
動的に空の色やフォグや色補正を変えたいということは、よくあるはず。運用方法としては、そのゲームの基準となるレンダリング設定をルートビューポートのEnvironmentにしておき、それをコピー&編集したものを、WorldEnvironmentやCamera3Dにセットすることになるのではないか。その場・フィールドの環境状態は文字通りWorldに、Buff/Debuffなどで一時的に見た目に補正をかけるならばCamera3Dにセットするなど。

CameraAttributes

CameraAttributesはリソース。カメラの露出設定や、フォーカス設定がプロパティとして含まれる。実際には、サブクラスのCameraAttributesPhysicalとCameraAttributesPracticalが使われる。
これはEnvironment同様に、Viewport、WorldEnvironment、Camera3Dの各ノードに含まれ、Environment同様の優先度でオーバーライドされる。
 優先順
 1. Camera3DのCameraAttributes
 2. WorldEnvironmentのEnvironmentCameraAttributes
 3. サブViewportのWorld3Dリソース内のCameraAttributes
 4. ルートのWorld3Dリソース内のCameraAttributes(デフォルト)

■サウンド

サウンドを処理するバックエンドはAudioServerというオブジェクト。

AudioStream

音アセットのリソースはAudioStream。
使用可能なファイル形式
・WAV
・Ogg
・MP3

AudioStreamPlayer

音を再生するにはAudioStreamPlayerノードを使う。空間的な表現用に、AudioStreamPlayer2DとAudioStreamPlayer3Dもある。AudioStreamPlayerにAudioStreamをセットして、play関数を呼び出すことで音を再生できる。

Godot的にはAudioStreamPlayerは音の発生源である。まず、AudioStreamPlayerがノードとして消えた時点で音も中断される。さらに、デフォルトでは1度に1つしか音を再生できない。連続で再生しようとすると、今なっている音は中断される。max_polyphonyプロパティを2以上に設定すれば、同時に複数の音を鳴らすことができる。しかしどのみち、AudioStreamを変えた時点で、音は中断される。これはMeshInstanceノードと同じように、あくまでアセットの一部として事前にセットアップされることを想定した仕組みである。
例えば鐘アセットとしての鐘シーンには、鐘の見た目がセットされたMeshInstanceノードと、鐘の音がセットされたAudioStreamPlayerノードが含まれている。鐘が連続で鳴らされる可能性があるなら、max_polyphonyを2以上に設定しておく。
しかし、これでは汎用的には使いづらい。特に、ノードが消えると音も消えるのは辛いものがある。例えば爆発が起こった時に、VFXとしての炎は消えても、爆発音は続いてほしい。または爆発の最中に場面切り替えがあっても、爆発音が続いてほしいこともある。

解決法は2つある。
1つは考え方を変える方法。爆発を表すシーンは、爆発のVFXが終わっても、SFXが終わるまでは残り続ければいい。もしくは、爆発音は炎が鳴っているのではなく、その場の空気を振動して鳴らしているので、時間で消える爆発音ノードを、フィールドにエミットすればいい。
もう1つの方法は、身もふたもないが、独自のマネージャクラスを作る方法。シングルトンシーンが効果音用のAudioStreamPlayerを32個くらい持って、チャンネルとして管理すればいい。縦割り設計を推奨するGodotの流儀に反するような気もするが、この方法は公式ドキュメントでAutoLoad機能の活用例として取り上げられている。

上記の、時間で消える音ノードのアイデアは悪くないように思う。爆発シーンを作るとなると、つかの間の時間経過に応じて複数種類の時間できるVFXパーツを次々とエミットさせることになるが、その一環として時間で消えるAudioStreamPlayerをエミットすればよい。
独自のマネージャクラスですべての音を一元管理するのも夢のある話だが、それはだいたいAudioServerと後述のAudioBusLayoutの機能で事足りてしまう。

AudioBusLayout

AudioServerは内部にAudioBusLayoutというリソースを常に1個保持していて、これが、AudioStreamPlayerの再生する音に対してエフェクトや増幅をかける。AudioBusLayoutは、ツリー状に接続されたAudioBus(非公開の構造体)の集合である。オーディオ機器には詳しくないが、イメージとしては、Busは現実のアンプやエフェクターに相当する。各Busにはボリュームと複数のエフェクト(ディレイやリバーブ)が設定できる。AudioStreamPlayerは、接続先となるBusを名前で指定する。音は各Busで経由し、ボリュームの補正やエフェクトがかけられ、最終的には"Master"という名前のBusに到達し、出力される。
デフォルトのAudioBusLayoutは、プロジェクト設定で指定する。(リソースが存在しない場合、音関係のシーンを編集していると、いつのまにか自動的に作られている。)AudioServer上のAudioBusLayoutは、add_bus関数やadd_bus_effect関数などで、動的にバスを編集可能。例えば、オプション設定の、効果音・環境音・音楽などの各ボリュームの設定値を反映させるなどできるだろう。音楽のクロスフェードなども、ここに実装してもいいかもしれない。また、AudioBusLayoutリソースそのものを差し替えることもできるが、これはあまり活用法が無いように思う。
ちなみに、エディタ上でAudioBusLayoutを編集するには、エディタ画面下の「オーディオ」タブを選択する。

■スクリプト

スクリプトはリソース。ノードにアタッチするスクリプトと、しないスクリプトがある。
アタッチするスクリプトはノードの機能の拡張であり、ノードの継承クラスが定義されていなければならない。1つのノードにアタッチできるスクリプトは1つのみ。
アタッチしないスクリプトは、単に新規クラス。またはノード以外の既存クラスの継承。
※2023/11/01追記:
改めて読み返してみると何言ってるかさっぱりわからなかった。
Node派生クラスのスクリプトとそうでないスクリプトがあって、Node派生の場合はノードインスタンスにアタッチできるよ、ということを言いたかったのだろう。

スクリプトの種類
・GDScript
・VisualScript(Godot4で廃止)
・C#
・GDNative(Godot4で廃止。GDExtensionが後継)
・GDExtension(Godot 4で追加)

ノードにアタッチするスクリプトは、アタッチ先ノードの継承クラスが定義されていなければならない。(なのでこの手のGDScriptは必ず"extends 親ノードクラス"という宣言が必須である。)祖先以外のノードであっても、エディタ上でアタッチはできてしまうが、実行時にエラーになる。
ノードにアタッチしないスクリプトはこの限りではない。Node派生クラスである必要もない。ちなみに、extendsを宣言しないと、自動的にRefCounted継承クラスになる。
※2023/11/01追記:
ある程度Godotを触った今となっては、スクリプトがNode派生クラスでないのは特段珍しいことでもないことがわかる。初心者向けのドキュメントがNode派生クラスありきで書かれてあるので、それに捕らわれていたのだろう。

定義したクラスは、他のスクリプトから利用するだけなら、プロジェクトにリソースとして存在するだけでよい。特に宣言も無しに、引数で受け取ったり関数を呼んだりできる。ただし、newしたければパスを指定してロードする必要がある。これはそもそも、スクリプトが「オブジェクトのクラスが何か」を関知しないからである。「オブジェクトに何という名前の関数が存在するか」で動いている。オブジェクトに、名前引きの関数ポインタテーブル的なものがくっ付いているのだろう。『ダックタイピング』という思想らしい。
※2023/11/01追記:
スクリプトにクラス名を付けて、型ヒントを駆使していれば、この限りではない。ロードは必要無いし、存在しない関数を呼ぶコードを書くとエディタがエラーを出してくれる。

Resourceクラスを直接継承したスクリプトは、独自リソースの定義になる。これは後述。

GDScript

Pythonに似ている。インデントが意味を持つ。

重要な特徴
・ソースファイルとクラスが完全に一体化している。1ファイル1クラス。
・Pythonと違い、新しい変数はvarを付けて明示的に宣言する。
・変数の型指定可能。var a: int = 5
・スクリプトに書かれる内容はクラスの定義。
・一応クラス内クラスは定義できる。
・クラス名を定義しない限り、匿名クラス。
・匿名クラスの場合は、スクリプトのファイルパスでアクセス可能。
・関数は常にクラスに属する。
・自作クラスは必ずObject派生クラス。
・メンバ変数は常にprivate。明示的にgetter/setterを定義する必要がある。
・staticメンバ変数は無い。
・多重継承は無い。
・他のファイルのクラスをnewするには、loadが必要。
 load関数には、スクリプトのファイルパスを指定する。
・C++のabstractやC#のinterfaceのような機能は無い。
 そもそも、関数を呼ぶ側は他のクラスに関与しない。
 「このオブジェクトが何々クラスだから、何々関数を呼ぶ」
 ではない。
 「このオブジェクトに何々という名の関数があるかどうか」
 これで動いている。
・Objectから直接派生される、RefCountクラスは、ほとんどの
 組み込みクラスの継承元であり、参照カウンタで解放される。
 一方、それ以外のクラスは手動で解放が必要。
※2023/11/01追記:
明らかに間違いがある。
メンバ変数は常にpublicである。getter/setterは必須ではない。
(Godot3ではそうだったのか・・・?)
また、staticメンバ変数はGodot 4.1で追加された。
また、「関数を呼ぶ側は他のクラスに関与しない」のは型ヒントを使わなかった場合の話。

C#

C#版エディタを使う際には、.NET SDKのインストールが別途必要。

IDEは、Visual Studio 2019、VS Code、Mono Developなどが使用可能。エディタ設定から使用するIDEを指定する。

C#にすると処理はGDScriptに対して最大4倍程度速いらしい。むしろGDScript結構速いな。

■GDScript

静的型付け

次のように記述することで型の指定が可能。
var my_val: int = 3
型を指定しておけば、スクリプト内で違反するような記述があった時にエディタがエラーで教えてくれる。また、インテリセンス的な補間機能も使えるようになる。さらに、型安全でない行番号をグレーアウトして教えてくれる。

RefCounted

GDScriptはC#的なガベコレで動いているわけではない。newしたオブジェクトは責任をもって破棄しなければならない。しかし、ユーザがnewするようなクラスはほとんどがRefCountedクラス派生である。これは名前の通り、参照カウンタで管理されるオブジェクト。
ガベコレで動いていないので、アクションゲーム等でプチフリを気にする必要は無い。ただし、参照カウンタの宿命で、循環参照でリークする可能性があるので注意が必要。

NodeはRefCounted派生ではない。作ったはいいが、シーンツリーに含めていないノードは、管理し忘れでリークする可能性がある。例えば、シーンの中身のリソースや一部のノードのコピーだけ欲しいときがある。そういうときは、loadしたシーンをinstantiateしても、ツリーには含めない。このとき、instantiateしたノードは忘れずに破棄する必要がある。
実は、print_stray_nodes関数で、ツリーに含まれていないノードをチェックすることができる。デバッグ用の機能。

※2023/11/01追記:
これはUnity的な観点からはピンと来ないと思う。
UnityではPrafabをInstanciateすると、ひとまずシーンの直下に置かれていた。そのあと、任意のGameObjectの子として配置しなおす必要があった。Godotの場合はインスタンス化とシーンへの配置が明確に分かれている。

getter/setter

GDScriptには、public変数が無い。となると、getter/setter関数を書くことになるわけだが、C#のプロパティのような機能が用意されている。
※2023/11/01追記:
明らかな間違い。逆。private変数が無い。
座学だけでこの記事書いたので・・・。

# setgetも型指定すると構文エラーになる
export var my_val: int = 1:
    set(new_val):
        my_val = new_val
    get:
        return my_val

setとgetは外部からアクセスした時のみ、関数が呼ばれる。明示的に内部から呼ぶには、selfを付けて、self.変数名で呼び出す。

Godot3ではsetgetというキーワードで取り扱っていたが、書式が変わった。変数だけをまとめて並べられるので、これはこれで悪くない気がする。

# Godot3
export var my_val: int = 1 setget set_my_val, get_my_val
func set_my_val(new_val):
    my_val = new_val
func get_my_val():
    return my_val

■ネイティブコード呼び出し

GDExtensionという機能でネイティブコードを呼び出すことができる。が、敷居が高い。Unityのように「いつものDLL」を作ればいいわけではなさそう。手順はGodotエンジンをビルドするのとあまり変わらない。

[GDNative]
GDNativeはGodot 3の機能だが、GDExtensionの十分な情報が見つからないので、ひとまずGDNativeについて記す。
結局のところ、dllを作ることになる。やり方はドキュメントに詳しく書いてあるが、エンジンから呼べる形にするためのソースコード(C版のgodot-headersとC++版のgodot-cpp)がGitHubにあるので、これをincludeして自分のソースコードをコンパイルする。
ビルドにはSConsというツールを使う。これは現代版makeのようなもの。SConsはGitHubからダウンロードできる。
コンパイラはVS付属のもの(cl.exe?)か、MinGW(詳細不明)が使われる。

[C++Modules]
Godotエンジンは複数のモジュールに分かれていて、Godotビルド時に取捨選択できる。これで軽量版などの独自のGodotエンジンを作ることができる。自作のモジュールを作れば、独自機能を持ったGodotエンジンになる。これがGDExtension/GDNative以外の、ネイティブコード実行の選択肢である。「モジュール」については要勉強。

■操作入力

入力の取得

操作入力の取得方法は2通りある。

1.
Inputシングルトン経由で取得する。

例
 If Input.is_joy_button_pressed(0, JOY_BUTTON_DPAD_UP) == true:
 If Input.is_key_pressed(KEY_SHIFT) == true:
 var y: float = Input.get_joy_axis(0, JOY_AXIS_LEFT_Y)
 If Input.is_action_pressed("my_action") == true:

一目瞭然。ポーリング式で分かりやすい。最後のactionというのは、InputMapという機能によるもので、プロジェクト設定から事前にアクション(文字列)と、対応する操作(キーボードのどのキーが押されたか、など)を設定しておく。

2.
コールバックで受け取る。Nodeクラスの特定の4つの関数、およびControlクラスの_gui_input関数をオーバーライドすることで、操作内容を受け取ることができる。

例:
# Node._inputのオーバーライド
func _input(event: InputEvent):
  if event is InputEventKey:
    if event.pressed == true and event.scancode == KEY_ESCAPE:
     処理

InputEventはリソースで、これに操作内容が記録されている。
オーバーライドしたNodeすべてに通知されるわけではない。通知には条件と優先順がある。そして、通知を受理したノードは、これ以上後続のノードに通知をさせないように、イベントをhandled状態にすることができる。
継承する関数は次の5つ
・_input
・_shortcut_input
・_unhandled_input
・_unhandled_key_input
・_gui_input(Control継承クラスのみ)
ここで言う"unhandled"は、通常の_input関数で受理されなかったイベントを処理する、という意味。だが、_inputであってもイベントを受理するとそれ以降の_inputは呼ばれないのは同じなので、実際は単に優先順のための名前付け(後述)である。

InputEvent

InputEventは、操作内容が記録されたオブジェクト。リソースなので、エディタ上でも作成可能だが、実際はスクリプトやエンジン内で動的に生成されるのが前提に思う。InputEvent自体は抽象クラス的存在であり、次のような派生クラスで運用される。
一例
InputEventAction: アクション名(文字列)、On/Off、強さ
InputEventJoypadButton: 物理ボタンの種類、On/Off、等
InputEventKey: キーコード、On/Off、等
InputEventMouseMotion: マウスの移動量や速さ、等

プレイヤーの操作によりデバイスからの入力があった時、InputEventが作成され、これを引数として、Input::parse_input_event()という関数が呼ばれる。これが入力処理の起点となる。
例えば、JoypadWindows::process_joypads()は、パッドへの入力があると、InputEventJoypadMotionを作成し、パッドの状態をそれに込め、Input::parse_input_event()を呼ぶ。

※2023/11/02追記
急にC++の話をし始めてるけど、これはGodotエンジン内部の処理のこと。process_joypads()は毎フレーム呼ばれる関数で、その中でパッドへの入力を検知している。こういった入力系ポーリング関数が共通して、parse_input_event()を呼んでますよ、と言いたかったはず。

inputコールバックの呼び出し順

次の優先順位でコールバックが呼ばれる。受理したことにしたければ、関数内で、SceneTree.set_input_as_handled()を呼べば、それ以降のオーバーライド関数は呼ばれない。
[1] 各ビューポート領域。よりルートに近い領域から。
[2] 同じビューポート領域ならば、以下の関数順。
  _input
 _gui_input
 _shortcut_input
 _unhandled_input
 _unhandled_key_input
[3] 同じ関数がオーバーライドされているならば、
 ノードのツリーの下から順。エディタに表示したときに、
 下になるほうが先。
 例:
 ┣Node 6番目
 ┃┣Node 5番目
 ┃┗Node 4番目
 ┃ ┗Node 3番目
 ┗Node 2番目
  ┗Node 1番目
運用としては、モーダルなUIの入力は_gui_inputで受け取って、キャラクターの操作などは_unhandledを使う、という流れになるはず。

Input::parse_input_event

前述の通り、Input::parse_input_event()関数が入力処理の起点となる。この関数が呼ばれると、次の2つの処理がなされる。
まず、InputEventの内容に応じて、Input内部で記録しているキーやボタンの状態が更新される。これは、Input::is_key_pressed()関数などの、ポーリング系関数の結果に反映される。
次に、InputEventをノードに通知するコールバックが順次実施される。

Input::parse_input_event()はスクリプトから呼ぶこともできる。この場合、
・リプレイ等での操作の模倣/再現
・ダブルクリックや長押しなどを判定してイベントとして発行
こういった用途での使い方になるはず。

備忘録的に、ソースコードにおける、parse_input_eventからコールバック関数までの、関数呼び出しの流れを以下に示す。これでも簡略化してある。ちょっと追いにくい。

Input::parse_input_event
 →DisplayServerWindows::_dispatch_input_events
   →Window::_window_input
     →Viewport::push_input
       →SceneTree::_call_input_pause(CALL_INPUT_TYPE_INPUT)
         →Node::_input
       →Viewport::push_input
         →Control::_gui_input(コードが読解しきれないがここのはず)
     →Viewport::push_unhandled_input
       →SceneTree::_call_input_pause(CALL_INPUT_TYPE_SHORTCUT_INPUT)
         →Node::_shortcut_input
       →SceneTree::_call_input_pause(CALL_INPUT_TYPE_UNHANDLED_INPUT)
         →Node::_unhandled_input
       →SceneTree::_call_input_pause(CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT)
         →Node::_unhandled_key_input

※2023/11/02追記
わかりにくいけど、parse_input_eventから呼ばれてる関数を、上から順にリストしている。
各Viewportに対して、INPUT、_gui_input、SHORTCUT_INPUT、UNHANDLED_INPUT、UNHANDLED_KEY_INPUTが順に呼ばれている。

どちらの入力処理を使うべきか?

Inputシングルトンのget系関数を使ったポーリング式、_input系関数のオーバーライドを用いたコールバック式、どちらの方法で入力を取得すべきか。エンジンの思想的にはおそらくどちらでもいいだろう。シーンは独立して動けるように作れ、というのがGodot流だ。どちらの方法でも達成できる。

しかしどちらの方法も、入力をラッピングできない。操作方法のマッピング変更や、履歴を問い合わせるようなことができない。オプション設定でボタンの割り当てを変えたり、スクリプトで長押しやダブルクリックの判定をしたい。なので結局、AutoLoad機能(後述)を使って、自前のInputManagerシングルトンみたいな、そんな感じのを作ることになる。

このInputManager(仮名)はどうふるまうか。Input::parse_input_eventを使って、自前のアクションをコールバックするか、各オブジェクトがInputManagerをポーリングするか。これもやはり、どちらでもいいのでは。

■アニメーション

アニメーションのリソースとノード

[Animation]
Animationはリソース。これがblendファイルのアクションに相当する。内部はTrack構造体(非公開)のリスト。
Trackの内部
・ノードパス(相対パス)
・時間(フレームではなく秒)とキー値のペアのリスト
・補間タイプ など
キー値には、3Dの位置・回転・スケール・音・メソッドなどの種類がある。

補間タイプには当然LINEARがある。FBXを介さず、blendを直接インポートする限り、Blender上で作成したリニアなキーしかないアニメデータを、キーフレームを保持したまま取り込める!

[AnimationLibrary]
AnimationLibraryもリソース。文字列引きのAnimation辞書。Godot3では存在しなかった。
blendファイルをインポートする際に、シーンではなく、AnimationLibraryとして取り込むことができる。また、エディタ上で編集することもできる。ただし、エディタ上での編集には少しひと手間が必要で、AnimationPlayer(後述)にセットした状態でしか、操作できない。もちろん、スクリプトを使って、ゲーム中もしくはエディタ上の実行で作成することもできる。スクリプトで作るのがシンプルで見通しが良く、繰り返しに強いと思う。

[AnimationPlayer]
AnimationPlayerはノード。これがアニメーションを再生する。クロスフェードに対応している。

Godot3と4で仕様が変わっている。
Godot3: Animationと名前を複数addする。Animationの名前を指定してアニメーションを再生する。
Godot4: AnimationLibraryを複数addする。AnimationLibrary内のAnimationの名前を指定して、アニメーションを再生する。

※2023/11/02追記
何言ってるか分からないと思う。
Godot3では、AnimationPlayer.add_animation()関数でAnimationリソースをセットしていた。godot4では、AnimationPlayer.add_animation_library()関数で、AnimationLibraryをセットするようになった。
それに伴い、AnimationPlayer.play()関数の引数も変わった、という話。
Godot3ではAnimationPlayer.play(”Run”)だったのが、Godot4ではAnimationPlayer.play("DogAnimLib/Run")になった。

AnimationPlayerノードはキャラクターのツリー構造のどこに配置してもよい。その代わり、プロパティとしてRoot Nodeを設定する。シーンツリーのrootのことではない。前述の通り、Animationはノードパスを記録しているので、Root Nodeからの相対パスでアニメーションが再生される。

[AnimationTree]
AnimationTreeはノード。UnityのAnimationControllerやUEのAnimBPのような機能。
AnimationNodeリソースを使っている。

[Skeleton3D]
アーマチュアとなるノード。ボーンはノードで表現されるのではなく、Skeleton3Dノードが内部にボーンの状態を持つ。これはUnityよりはUEに近い。
3Dモデルをインポートして、Skeleton3Dを単独のリソースにする単純な方法が無い。(そもそもリソースではない。)メッシュと紐づいた1つのシーンになってしまう。ここはUnityに似ている。
単独でSkeleton3Dを取り出したければ、シーンをロードして該当ノードを探して取得する。これはランタイムでやってもいいし、エディタ上のスクリプト実行で取り出して、1つのノードから成るシーンリソースとして保存してもいい。

Godot4でのアニメーションの刷新

Godot4では、アニメーションに関していくつか大きな変更点がある。
・Transformトラックが、Position・Rotation・Scaleトラックに分解された。
・Restポーズからの相対変化量ではなく、ポーズの絶対値が
 保持されるようになった。
 これはBlender式から、Maya式になったということ。
・オイラー角の回転順が選択可能になった。

■3Dモデルのインポート

対応型式

・.blend
・.fbx ※非推奨
・.gltf
・.gltf+.bin
・.glb
・DAE(COLLADA)
・OBJ(Wavefront) ※メッシュのみ
・ESCN

Godot4から対応するようになったblendが本命なように思う。Unity同様、エディタがBlenderのexeを直接実行して取り込むので、間に余計なものを挟まず確実。アニメーションのキーフレームが、何も足さず、除かず、維持されて取り込まれる。(超重要)

ESCNは、Blenderに専用プラグインで出力させる専用型式。おそらくExported Sceneの略。インポートファイルを分割したいときや、必要なデータだけをプロジェクトに込めたいときには、blendを直接インポートせず、あえてESCNやglTFを間に挟むのも良さそう。

インポートされた3Dモデルは、.gltf+.binファイルに変換され、importedフォルダ下に保存される。glTFはGodotネイティブな形式なのだ。なのでglTFもGodotと相性いいように思う。glTFも捨てがたい。ちなみに、glTF系(.gltf、.gltf+.bin、.glb)ファイルをプロジェクトに含めると、シーンとして「インポート」されるが、importedフォルダ下に.gltf+.binファイルとして再構築されるのは免除される。(動画投稿サイトの再エンコード回避みたいな話だ。)

COLLADAはPS3/PSPのために策定されたもので、ゲームと相性良さそうだが、glTFが実質後継なので、あまり立場がなさそう。

3Dモデルとシーン

3Dモデルのファイルはシーン(.tscn)として取り込まれる。厳密にはPackedSceneクラスになる。tscnファイル自体にはメッシュやアニメーションは含まれておらず、gltf+binファイル型式で別ファイルとして保存される。なので、blendファイルをインポートすると、importedフォルダ以下にtscn、gltf、binそれぞれのファイルが作成される。
gltf+bin形式は3Dモデル系のリソースの入れ物として、importedフォルダ下に作成され得る型式ではあるのだが、それはそれとして、gltfとglbはシーン(tscn)として『インポート』される対象の型式でもある。なので、gltfを<res://>フォルダ下に入れると、インポートされ、importedフォルダ下にtscnが作成される。gltf+binはわざわざ再作成されない。メッシュデータなどはソースファイルに収められている。
これは、ddsファイルなどの「リソースファイルとしてROMからロードされる型式」のファイルを直接プロジェクトに含めた時の動作とはまた異なる。ddsの場合はソースファイルがそのままImageTextureリソースのロード元であり、importedフォルダには何も作られない。そもそも『インポート』されていないのだ。

個別のシーンとして保存

公式ドキュメントには、「インポートシーンの各ルートノードを個別のシーンとして保存可能」、と書かれてあるのだが、方法が不明。

メッシュとアニメの外部保存

メッシュとアニメーションに関しては、インポート設定で個別のファイルとして保存可能である。つまりシーンとは別に、ArrayMesh用の.resファイルとAnimationの.resファイルが作成される。これは、importedフォルダ下に作られる中間ファイル的な存在とはまた別のものである。個別にloadすることを前提にしている。3Dモデルファイルをloadすると、importedフォルダ下のPackedScene(.tscnファイル)がロードされるが、分けて保存していれば、それとはまた別にArrayMeshやAnimationの.resファイルを直接load可能である。
分けて保存するには、インポート設定でメッシュやアニメーション個別に「Save To File」にチェックを入れて、パスを指定する。もしくはメニューの「Actions…」から、メッシュ用のフォルダ、アニメーション用のフォルダを指定してやれば、一括で保存設定が成される。

モデルはランタイムで組み立てたい。UnityのPrefabのような機能は苦手だ。だからこの外部保存機能は歓迎したいところなのだが・・・。ちょっと使い勝手が自分に合ってないように思う。
まず第1に、中途半端だ。後述するが、キャラクターモデルのSkeleton(アーマチュア)はこの機能で保存できない。キャラクターを動的にセットアップしたければ、Skeletonを別の方法(エディタスクリプト的な)でシーンとして保存するか、アーマチュアだけの3Dモデルファイルを別途用意する必要がある。
第2の理由として、管理がしにくい。メッシュやアニメーションのresファイルのパスを個別に指定する必要がある。繰り返しや変更に弱い。さらにデフォルトではUUID的な文字列が名前にくっついてくる。
なので結局、3Dモデルの各素材を分解して個別にファイル保存するのは、手間のわりに利が少ないように思う。全部ランタイムでやる。モデルをシーンとしてloadして、そこからメッシュやアニメーションやSkeletonを取得してストック用シングルトンで保持しておく。これがシンプルではないか。自分のゲームはどうせ全部のデータがオンメモリだ。

glTFのインポート

前述の通り、gltf(埋め込み)、gltf+bin+png、glb、いずれにおいても、これらはシーンとして取り込まれ、詳細なデータ自体はソースファイルに残る。単一ファイルにテクスチャが埋め込まれている場合、つまり埋め込み型のgltfやglbであっても、テクスチャはソースファイルから読み込まれる。
binやpngが分離している場合、これら複数のファイルを同時にプロジェクトにドラッグ&ドロップしてもいいが、binやpngを後から放り込んでも、問題無く動く。

objのインポート

.obj型式のファイルは例外的に、メッシュとしてインポート可能である。importedフォルダ下で.meshファイルに変換される。
objファイルをテキストエディタで開くと分かるが、obj型式は複数のメッシュを持つことができるし、Blenderはオブジェクト別にメッシュをエクスポートする。ただし、Godotでインポートすると単一のメッシュになる。

blendのインポート

エディター設定でBlender 3 Pathを設定すれば、blendファイルがインポートされる。
例<C:/Program Files/Blender Foundation/Blender 3.2>

[座標系]
シーン全体のスケールは、Blender上とGodot上で1:1で対応している。また座標系も、右手Z-Upから右手Z-Upに素直に変換されている。メッシュデータ自体の座標系が修正されており、Unityのようにノードのトランスフォームで差異を吸収しているわけではない。

[メッシュ]
オブジェクトがメッシュを共有していると、メッシュは共有された分だけコピーされる。メッシュにマテリアルが紐づく思想なので、こういう仕様なのだろう。
ミラーモディファイアーと辺分離モディファイアーはちゃんと適用される。

追記 2023/11/04:
これを書いた時点のGodot4ベータ版ではそうだったのかもしれないが、Godot 4.1.1においては、Blender上で共有されているメッシュは、インポート後も共有される。コピーされたりはしない。
では、メッシュを共有しているオブジェクトに、別々のマテリアルが割り当たっていた場合はどうなるかというと・・・。マテリアルの違うオブジェクトの分だけメッシュが作成される。

[マテリアル]
マテリアルは、デフォルト設定では自動で作成され、シーンにパックされる。このとき、Blender上のシェーダがプリンシプルBSDFならば、少なくともベースカラーのテクスチャは、既存のリソースから相対パスで探してきて参照するようになる。ベースカラー以外のテクスチャに関してもおそらく同様に参照されるだろう。一方、シェーダがディフューズBSDFだと、テクスチャはまったく検索されない。オブジェクトにマテリアルが割り当たっていなくても、一応マテリアルが作成される。
インポート設定で、「Use External」を有効にすることで、既存リソースのマテリアルを設定することができる。

スクリプトによるカスタムインポート

インポート設定でスクリプトを指定して、シーンを操作することができる。書式は下記の通りで、そんなに大げさじゃない感じだから、気軽に使って良さそう。

@tool
extends EditorScenePostImport

func _post_import(scene):
    # ここで処理
    return scene

3Dモデルのインポートヒント

DCCツールでオブジェクトに特定の接尾辞を付けるておくと、インポート設定のデフォルト値が変化する。
個人的に有効に使えそうなのは次の2つ。
-noimp: これを付けたオブジェクトはノードにならない。
-loop/-cycle: これを付けたアニメーションはループがオンになる。
ただ、アセットのマスターデータをエンジンに合わせるのはあまりやりたくない。ノードを除外するだけだったら、前述のカスタムインポートでもできる。

■3Dモデルの動的構築

前述の通り、DCCツールで作った3Dモデルのファイルは、シーンとしてインポートされる。このままでは、ランタイムにおいてスクリプトでメッシュやマテリアルを組み合わせて動的に構築することができない。

モデルの構築には、メッシュやマテリアル、さらにキャラクターの場合はアーマチュアやアニメーションが必要になる。それらのロードと参照の取得に関して、方法は2つある。
・メッシュとアニメーションに関しては、前述のインポート設定を用いて、
 個別のリソースァイルとして外部保存しておく。
 もしくは、エディタースクリプトを用いて、エディタ上でシーンから
 データを抽出し、別々のリソースファイルとして保存する。
 この場合は、アーマチュア(Skeleton3Dノード)も保存可能。
 ランタイムでそれらリソースファイルを読み込む。
・ランタイムでスクリプトを用いて、シーンからデータを抽出する。
前者は後者よりもパフォーマンスの面で有利である。後者は管理するリソースファイルが少なくて済むメリットがある。

エディタ上で任意のスクリプトを実行するには、後述のエディタースクリプトという機能を用いる。Unityのエディタ拡張のような機能だ。取得したリソースを保存する場合、ResourceSaver.save関数を用いる。アーマチュア(Skeleton3Dノード)はリソースではないので、シーン(PackedSceneリソース)として保存する。

シーンからデータを取得したい場合、loadしたあと、Instantiateする必要がある。このとき、副作用は気にしなくていい。GodotではシーンをInstantiateしただけでは、初期化関数_readyや更新関数_procは実行されない。シーンがアクティブになるには、Instantiateしたのちにシーンツリーに接続される必要がある。(Instantiateしただけのノードは、忘れずに解放しなければならない。)

■キャラクターモデル

UnityやUEと同様、Godotもアーマチュアとスキンメッシュとアニメーションの各データを密に結合しようとする。これらを同一のファイルから読み込んで、単一のシーンとして扱わない限り、単純な方法では面倒は見切れんよ、とそういう声が仕様から聴こえて来る。事情はわかるが・・・。

GodotはインポートしたblendファイルはPackedSceneリソースとして保存される。名前通り、パックされたシーンになってしまう。インポート設定を変えることによって、メッシュとアニメーションに関しては別リソースとして保存することはできる。しかしアーマチュアであるSkeleton3Dは単体で保存できない。そもそもSkeleton3Dはノードであり、リソースではない。
なので、通常は、スクリプトから動的にアーマチュアとメッシュを組み合わせることができない。それどころか、エディタ上でセットアップすることすらできない。
これではゲームや制作スタイルによっては困ることになる。キャラクターに着せ替えをさせたいし、髪型や顔パーツを差し替えたい。同骨格、別メッシュのモンスターを同一のblendファイルで扱いたいし、逆に骨格とアニメーションがそれぞれ別々のblendファイルに入ってる場合も許容したい。そういったことが普通にはできない。

前述の通り、回避方法が無いわけではない。下記の方法で、モデルのデータ(リソースとノード)を個別に保存し、エディタ上で手動で、またはスクリプトで動的に組み立ててることが可能。もしくは、データを個別に保存せず、ゲーム中にシーンからリソースを取得してもいい。

キャラクターモデルのリソース保存

次のようにモデルの各データをリソースとして保存することで、動的に、またはエディタ上で手動で、モデルを組み立てることができる。

ArrayMesh
メッシュはBlendファイルのインポート設定で、別リソースとして保存可能。メッシュごとの個別指定、またはフォルダを指定して一括で出力先を設定できる。

Animation
アニメーションは、メッシュと同様、インポート設定でリソースとして保存可能。

AnimationLibrary
アニメーションをAnimationPlayerで再生するには、AnimationLibraryを介す必要がある。AnimationLibraryの実体はStringをキーとするAnimationのマップである。AnimationLibraryはエディタ上もしくはスクリプトで新規作成するか、blendファイルをシーンではなくAnimationLibraryとしてインポートすることで作成可能。
分かりにくいのだが、AnimationLibraryのエディタ上での編集は直接は行えない。AnimationPlayerがシーンにある状態で、エディタ上の"Manage Animations…"ボタン(見つけにくい)から「Edit Animation Libraries」画面を呼び出して、AnimationPlayerにライブラリを割り当てて、ようやく編集可能である。

Skeleton3D
エディタ上のスクリプト実行において、ResourceSaverインスタンスを用いれば、リソースの保存は可能である。これによって、パックされてしまっているblendファイルから、Skeleton3Dを取り出して保存することが可能である。
ResourceLoaderでblendファイルをloadすると、PackedSceneリソースが得られる。これはPackedScene.insrantiateすると、Node3Dになる。ここからchildをたどって、ArrayMeshやSkeleton3Dにアクセスできる。
Skeleton3Dはノードなので、PackedSceneリソースを作成して、その中にノードを詰め、ResourceSaver.save()でシーンファイル(.tscn)として保存する。また、MeshInstance3DノードにはSkinリソースがセットされており、それを取得して同様にResourceSaver.save()で保存できる。

Skin
Skinは、(おそらく)ウェイトインデックスとボーン名の対応付け、ついでにバインド時のポーズを定義するリソース。MeshInstanceノードで使われる。Skeleton3D同様、これもBlendインポート時には個別保存されない。ResourceSaverインスタンスを使って保存することができる。

キャラクターモデルの構築

前記の通りリソースを作っておけば、モデルを動的に構築することができる。

次のような構成になる。
・Node3D
 ・Node3D(2つ目)
  ・Skeleton3D
   ・MeshInstance3D
 ・AnimationPlayer

上から順に説明する。
Node3D
最上位のNode3Dはなんでもいい。これがAnimationPlayerに設定するRoot Nodeになる。
Node3D(2つ目)
Skeleton3Dの親のNode3Dは名前に注意すること。Animationリソースは例えば次のような文字列でボーンを記憶している。"Armature/Skeleton3D:Hip"。なので、Root Nodeである最上位ノードからパスが通るような名前でなくてはならない。たいていは、"Armature"とかそんな名前になるだろう。
Skeleton3D
これはシーンとして保存したスケルトンをインスタンス化すればいい。
MeshInstance3D
これに対して、MeshArray、Skin、Skeletonを割り当てる。(Skinは無くてもなぜか動くっぽい?)
AnimationPlayer
AnimationLibraryを割り当てる。また、Root Nodeに最上位のノードをセットする。

アニメーションはこのようにして再生可能。
$AnimationPlayer.play("MyAnimLib/Attack")

■カリング

Godotではデフォルトで視錐台カリングが働く。カリングに用いる形状はメッシュからAABBが作成され、Meshリソースのプロパティとして保持される。これはMesh.get_aabb関数や、ArrayMesh.custom_aabbプロパティ、VisualInstance.get_transformed_aabb関数で取得できる。

これとは別に、ユーザが意識的にデザインするオクルージョンカリングのために、Occluder3DリソースとOccluderInstance3Dノードがある。Occluder3Dが形状で、OccluderInstance3Dがそれをシーンツリーに配置するノード。Occluder3Dは、3Dモデルのインポート時に、インポート設定でメッシュから作成することもできる。

Godot3では、RoomとPortalという機能も用意されていた。これは、レベルをエリア分けしてカリングをデザインする仕組みであると同時に、AI等の制御にも使うことができた。これはGodot4で廃止された。

■コリジョン

コリジョン判定には、Area3Dノードを使う。形状は、Shape3D派生リソースによって与えられるが、このノードに直接セットするわけではない。Area3Dノードの子としてCollisionShape3Dノードを複数ぶら下げ、この集合がコリジョン判定の形状となる。

Shape3D派生クラス
BoxShape3D: 箱。
CapsuleShape3D: カプセル。
HeightMapShape3D: doubleの2次元配列。
WorldBoundaryShape3D: 平面。
SeparationRayShape3D: +Z方向への線分。
ConvexPolygonShape3D: 凸だけのポリゴン。
ConcavePolygonShape3D: 凹を含むポリゴン。
など。

衝突の検出には、後述のシグナル機能を用いる。Area3Dノードは、コリジョン同士の衝突時に"area_entered"シグナルを発するので、これを受信するようにする。

■物理

物理演算機能を使うには、PhysicsBody3D派生ノードを用いる。PhysicsBody3D自体は抽象クラスで、次のような派生クラスがある。
主要なクラス
StaticBody3D: 自動で動かない。床や壁など。
AnimatableBody3D: 自動で動かないがコードで動かせる。ドアや動く床。
RigidBody3D: 物理演算で自動で動く。
CharacterBody3D: 自動で動かないが、コードで床や壁に沿って動かせる。

コリジョン判定に用いる形状は、Area3Dと同様の方法で与える。PhysicsBody3Dの子として、複数のCollsionShape3Dノードをぶら下げ、Shape派生リソースをセットする。

物理的な反応を定義するためには、PhysicsMaterialリソースを用いる。跳ね返りやすさや滑りやすさをプロパティとして持つ。マテリアルという名前だが、描画には関係ない。

■AutoLoad

ゲーム作りにはマネージャクラスが欠かせない。独立性が高い疎結合な関係が綺麗で柔軟な設計だとは言っても、各オブジェクトを支配する、もしくは各オブジェクトから使われる、なんらかのシングルトンが結局は必要なのだ。それはGodotの設計者も認めるところなのだろう。GodotにはAutoLoadという、シングルトンノードを作るための機能がある。

使い方は簡単で、プロジェクト設定からAuoLoadリストにNode継承スクリプトを登録する。複数登録可能。すると、ゲーム実行時に自動的に、これらのスクリプトをアタッチした各ノードが最初に作られる。あとは自由に使える。シーン中のスクリプトからシングルトンにアクセスしてもいいし、シングルトンがシーン中のノードを駆動してもいい。(ただし、シングルトンノードを開放すると、ゲームがクラッシュするらしい。)
Godotにはグローバル変数やstatic class的なものが存在しない。この機能は必須である。

実行中のノードツリーの構造は、例えば以下のようになる。
Rootノード(ビューポート)
 AutoLoadノード1
 AutoLoadノード2
 AutoLoadノード3
 シーンノード

2023/11/04追記:
その後、Godot 4.1でstatic変数が追加された。なのでシングルトンの実装手段としては必須ではなくなった。

■シグナル

Objectには、コールバック機能がある。シグナル名(文字列)をトリガーに、事前に登録された関数を呼ぶことができる。使い方は次の通り。

呼ぶ側 ObjA
 emmit_signal("signal0", 123, 456, 789) # 登録されたすべてのObjectに発信
呼ばれる側 ObjB
 a.signal0.connect(on_ObjA_signal0) # 推奨される書式
 a.connect("signal0", self, "on_ObjA_signal0") # レガシーな書式
 func on_ObjA_signal0(arg0, arg1, arg2) # コールバック関数
 ※慣習として関数名は"on_ノード名_シグナル名"

ちなみに、オブジェクトの削除時には、自動的に登録解除される模様。

実は、connectはエディタ上でGUI的な操作で設定することが可能。接続状態がシーンファイルに記録される。シーンツリー上にシーンを展開した時に、自動的にconnectされるのだろう。とはいえ、エディタであれこれセットアップするのは、見通しが悪いし作り直しに弱くなる。

■通知(Notification)

Objectクラスは_notification(int)関数が定義されている。Object.notification(int)(アンダースコアなし)が呼ばれると、すべての継承先で定義されている各_notification関数が呼ばれる。例えばNodeクラスの_notificationは、int値がNOTIFICATION_PROCESSだったときに、_process関数を呼び、NOTIFICATION_READYだったときには、_ready関数を呼ぶようになっている。
この機能はそもそも、エンジンの実装のために使われている。関数ポインタ的なものを使わずに、透過的にコールバックする仕組みだ。コールバックしたい側と実際にコールバックを実施する側が違っていても、実施側は関数を知らなくていい。
ただこれは、ユーザも利用することができる。自分のスクリプトで_notification関数を定義して、メッセージ(int値)に対する反応を追加できる。例えば、if what == NOTIFICATION_PARENTED:とすれば、親ノードがセットされた時の処理を定義できる。
また、既存のメッセージ以外にも独自のメッセージを定義して、通知・受信することもできる。ただしもちろん、既存のint値と重複しないように気を付ける必要がある。

■Layoutとそのラッピング

[Layout Mode]
LauoutはLayout Modeというプロパティを持つ。Layout ModeはPositionとAnchorsという2つのモードから成る。

[Positionモード]
「親Controlの左上を基準とした、自Controlの左上の位置」(Position)とサイズを設定する。スケールと回転も設定できる。ピボットも設定できて、スケールや回転に影響する。
なんでもかんでも左上なので、PositionモードではUIのレイアウトがしにくい。Anchorsモードのほうが本命のように思う。

[Anchorsモード(PresetとCustom)]
Anchorsの場合、アンカー設定が追加で考慮される。アンカーはCustomかPresetか選ぶ。Presetの場合、さらにTop LeftやCenter Right、Top Wide、V Center Wideなど、プリセットアンカーの種類を選ぶ。
Anchorsモードの場合であっても、Position値は、Positionモードと同様、「親Controlの左上を基準とした、自Controlの左上の位置」である。じゃあなんのためのモードなんだという気もするが、Anchorsモードは親のサイズが変わった時に自動でPosition値を変えてくれるのだ。このような思想なので、右詰などがしたくても、親のサイズを意識して、逆算してPosition値を設定しないといけない。これではコードからシンプルにレイアウトできない。Presetは使いにくい。
PresetではなくCustomを使えば親を意識せず値を管理可能になる。 
・Left/Top/Right/BottomのAnchor Points (0.0~1.0)
・Left/Top/Right/BottomのAnchor Offsets (ピクセル値)
Customではこの8つの値を設定する。UnityのRectTransformを使ったことがあるならピンと来るはず。どうせエディタでUIは作らない。使いやすいように自分のコードでラッピングしてやればいい。

例1: 親Controlの右中段を基準にしたい
 Anchor Points:
  Left/Right = 1.0
  Top/Bottom = 0.5

例2: 親Controlの左下段を基準にしたい
 Anchor Points:
  Left/Right = 0.0
  Top/Bottom = 1.0

例3: 自Control(64x24)の右中段を(-16,0)に配置したい
 Anchor Offsets:
  Right = -16px
  Left = -16 - 80 = -80px
  Top = 12px
  Bottom = -12px

例4: 自Control(48x48)の中央上段を(160,64)に配置したい
 Anchor Offsets:
  Right = 160 -24 = 136px
  Left = 160 + 24 = 184px
  Top = 64px
  Bottom = 64 + 48 = 112px

■オフスクリーンレンダリング

ビューポートでレンダリングされた内容を画面上に表示するには、次の3つの手段がある。
・SubViewportContainer: Control
・TextureRect: Control
・Sprite2D: Node2D
SubViewportContainerは、Control(UI)であり、子としてSubViewportが存在するならば、その内容を表示する。ViewportTextureを別途用意する必要は無い。
TextureRectとSprite2Dは、テクスチャの内容をそれぞれControl(UI)、またはNode2D(スプライト)として表示する。Viewportの内容をテクスチャとして表示するには、ViewportTextureを介す必要がある。
ViewportTextureを用意する方法
・エディタ上で新規作成してシーンに埋め込む
・ゲーム中にスクリプトで動的に作成する
・ViewPortのデフォルトテクスチャを取得(Viewport.get_texture())
・ROM上にファイルとして作成(あまり意味がない)
作成したViewportTextureには、Viewportのノードパスをセットする。

ViewportTexrure

ViewportノードはリソースであるViewportTextureと協調して動作する。
Viewportは内部にViewportTexture、デフォルトテクスチャを作成する。デフォルトテクスチャは、Viewportに割り当てられたレンダリングサーバー上のレンダーターゲットを参照している。一方、TextureRectなどのために作られた、Viewport外のViewportTextureは、ノードパスによってViewportを特定し、そのViewportに割当たっているレンダーターゲットを参照する。さらに、Viewportに対して、自分自身を記憶させる。これは、ビューポートのサイズが変更になった時に、テクスチャリソースとして何らかのふるまいが必要で、コールバックしてもらうためである。

勘違いしそうになるが、ROM上のViewportTextureリソースは、なんらかの画像データではない。サイズすら持っていない。.tresをテキストエディタで覗いてみると分かるが、ビューポートへのパス(どのシーンの、とかではなく、単に「どこかのシーンの相対パス」。)が書いてあるだけである。

■独自リソース

Recourceを直接継承したスクリプトを作成すると、それがオリジナルなリソースクラスになる。クラス名を定義してやれば、エディタで「新規リソース」を選んだ時に、リストに表示されるようになる。
このリソースは、ノードと同様にプロパティを持つことができる。大別して2種類。intやfloatのような、パラメータのプロパティ。もう1つはMeshのようなまた別のリソースへの参照プロパティ。いずれもexport宣言すればエディタのインスペクタ上で編集できるようになる。そして、リソースファイルとして保存可能。
この機能の用途は、エディタ上で設定できる、なんらかのパラメータ群・データ群だろう。公式ドキュメントは、エディタの拡張機能を組み合わせて使うことを示唆している。また、UnityのScriptableObjectやUEのDataTable的な使い方にも言及している。

個人的には、エディタ上で数値を設定できることには特に魅力を感じない。ただ、エクセルで設定したパラメータテーブルや、翻訳データを、一度リソースとして取り込むのは、パフォーマンスの面で有りかもしれない。大量のタブ区切りテキストデータをゲーム中にパースするとロードが長くなるので。

■自動化

プロジェクトの自動化は欠かせない。ゲーム作りは、作っては壊し、作っては壊しの繰り返しだ。エディタのGUIでポチポチと作業していては、気軽にそれができない。
エディタ上の「プロジェクトの実行」、「シーンの実行」で実行されるスクリプトは、普通にリソースに手出し可能である。ResourceSaver.save関数でリソースを保存できる。例えば前述の通り、Skeleton3Dを保存したければ、PackedSceneを作って、pack(skelton3d)でパックして、これをシーンとして保存すればいい。
ちなみに、ResourceSaver.save関数で指定するパスは、拡張子が無いとエラーになる。おそらく拡張子で形式を決定している。

@tool

スクリプトの先頭で@toolを宣言すると、編集中のシーンにアタッチしたスクリプトが、エディタでシーンを編集している最中にも処理されるようになる。
toolスクリプトになると、_ready関数と_process関数が呼ばれるようになる。関数内ではシーン編集中かゲーム実行中かで処理を分岐させるために、Engine. is_editor_hint()関数を使うことができる(実質必須)。
ドキュメントにある通り、_readyと_processが呼ばれるこの機能自体は、表示の補助やヒューマンエラー検知に使うといい。常時実行なので、リソースを作ったり改変したりには向いていない。

EditorScript

エディタースクリプト派生クラスを作ると、エディタから任意のタイミングでスクリプトを実行できる。Unityのエディタ拡張では、メニューバーに項目を追加してスクリプトを実行できるが、それに近い。
EditorScript派生クラスを作る。すると、Godotのスクリプトエディタから、メニュー「ファイル」>「実行」で、編集中のEditorScriptスクリプトの_run()関数が実行される。
これは、リソースを作ったり改変したりするのに向いている。

■動画出力

Godotは動画出力モードが付いている。いわゆるプリレンダムービーや、トレイラー作りに使えるかもしれない。プロジェクト設定の「Movie Writer」から設定可能。

■トラブル集

灰色一色でカメラに何も映らない。→ライトを置いていない。

■ソースコード

ソースコードそのものは280MB程度。.gitフォルダ内のリポジトリがギガサイズ。

クラス名からファイルを検索してもひっかからない時は、スネークケースを確かめてみると良い。canvasitem→canvas_item

ちなみにC++17で書かれているらしい。

重要または頻出のクラス
共通
<core/template/rid.h> RID (uint64のID。Rの意味は不明)
<core/math/transform_3d.h> Transform3D
<core/object/object.h> Objaect
<core/object/ref_counted.h> RefCounted : Objaect
<core/io/resource.h> Resource : RefCounted
メイン
<servers/display_server.h> DisplayServer : Object
<platform/windows/display_server_windows.h> DisplayServerWindows
<core/os/main_loop.h> MainLoop : Object
<scene/main/scene_tree.h> SceneTree : MainLoop
<platform/windows/godot_windows.cpp> エントリーポイント
ノード
<scene/main/node.h> Node : Object
<scene/3d/node_3d.h> Node3D : Node
<scene/main/canvas_item.h> CanvasItem : Node
<scene/2d/node_2d.h> Node2D : CanvasItem
<scene/gui/control.h> Control : CanvasItem
  ※"private: struct Data"がLayoutの実装
スクリプト
<core/object/script_language.h> Script : Resource
<modules/gdscript/gdscript.h> GDScript : Script
<modules/mono/csharp_script.h> CSharpScript : Script
<core/object/script_language_extension.h> ScriptExtension : Script
3D表示
<scene/3d/visual_instance_3d.h> VisualInstance3D : Node3D
<scene/3d/visual_instance_3d.h> GeometryInstance3D : VisualInstance3D
<scene/3d/mesh_instance_3d.h> MeshInstance3D : GeometryInstance3D
<scene/3d/light_3d.h> Light3D : VisualInstance3D
カメラ
<scene/main/viewport.h> Viewport : Node
<scene/main/window.h> Window: Viewport
<scene/3d/camera_3d.h> Camera3D : Naode3D
<scene/2d/camera_2d.h> Camera2D : Naode2D
アニメーション
<scene/animation/animation_player.h> AnimationPlayer : Node
オーディオ
<servers/audio/audio_stream.h> AudioStream : Resource
<scene/audio/audio_stream_player.h> AudioStreamPlayer : Node
<scene/3d/audio_listner.h> AudioListner : Node3D

この記事が気に入ったらサポートをしてみませんか?