Unityに慣れていると、Godotを触った瞬間にまずシーンとノードで首をかしげがちです。私も最初は「GameObjectはどこ?」「Prefabは?」となりました。
この記事は、その最初のつまずきを乗り越えるための内容です。Godot4系を前提に、Unity用語との対応関係と考え方のコツをまとめ直しました。
この記事でわかること
- Godotのシーン/ノードがUnityのGameObject/Componentとどう違うかが分かります。
- Prefab ≒ PackedScene、Additiveシーン ≒ シーンのインスタンス化など、手元の作業に落とし込めます。
- ライフサイクルやSignal、Autoloadなど、移行でハマりにくくなる実務の要所を押さえます。
1) 「シーン/ノード」は「Prefab+階層」に近い
まずは対応関係のイメージから。ざっくりこんな感じです。
Unity | Godot |
---|---|
GameObject | Node(例:Node2D、Sprite2D、Area2D など) |
ヒエラルキー | ノードツリー(親子関係) |
Prefab | PackedScene(.tscn) |
Additiveシーン | シーンのインスタンス化(PackedScene.instantiate) |
DontDestroyOnLoad | Autoload(Project Settings → AutoLoad) |
Componentスクリプト | ノードに付けるスクリプト(GDScript/C#) |
UnityEvent/Delegate | Signal |
UnityだとGameObjectに機能を付ける設計ですが、Godotは機能つきのノードを直接並べていくイメージです。UIでも敵キャラでも弾1発でも、それ自体が1つのシーンにできます。最初は大胆になんでもシーン化してOKです。
2) ノードは「機能つきの実体」
2Dアクションのプレイヤーなら、ベースにCharacterBody2Dを置いて、子にSprite2D、CollisionShape2D、Camera2Dなどをぶら下げます。スクリプトは必要なノードに素直に1本ずつ。雰囲気としては「GameObject+主要コンポーネントが1枚岩になっている」感じです。
# Player.gd(CharacterBody2D にアタッチ)
extends CharacterBody2D
const SPEED := 200.0
func _physics_process(delta: float) -> void:
var dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = dir * SPEED
move_and_slide()
3) PackedSceneとインスタンス化:Prefab運用とどう違う?
UnityのInstantiate(prefab)に相当するのが、GodotのPackedScene.instantiate()です。.tscnをロードして、欲しいときに実体化→ツリーに差し込むだけ。
# 弾をスポーン(親ノードにアタッチ)
@onready var bullet_scene: PackedScene = preload("res://scenes/Bullet.tscn")
func spawn_bullet(global_pos: Vector2) -> void:
var b = bullet_scene.instantiate()
add_child(b)
b.global_position = global_pos
シーンは入れ子にできます。敵1体もUIの1ウィジェットもエフェクトも、ぜんぶシーン。小さく作って組み合わせていくのがコツです。
4) 参照はGetComponentではなく「ノードパス」
GetComponent<T>()はありません。代わりに、ツリー上の位置(ノードパス)で取得します。型注釈を付けると補完も効いて安心。
横のノードを直参照しすぎると絡まりやすいので、連携はSignalに逃がすとラクです。
5) ライフサイクル対応表
Unity | Godot | 使いどころ |
Awake | enter_tree() | ツリーに入った直後。早すぎる初期化はここだとこけることも |
Start | ready()_ |
子ノードがそろった後。初期化の定番 |
Update | process(delta) | 毎フレーム(描画に同期) |
FixedUpdate | physics_process(delta) | 物理フレーム固定(既定60Hz) |
OnEnable/OnDisable | enter_tree()/ _exit_tree() | 入退場時のフック |
実務的には「参照の確定は_ready() 以降」「物理はphysics_process()」の2つを覚えておけばOKです。
6) イベントはSignalでゆるくつなぐ
Signalは「発信側がイベント名を宣言→受信側が接続してコールバックを受ける」仕組み。インスペクタでもコードでもつなげます。
signal hit(damage: int)
func _on_area_entered(_other):
emit_signal("hit", 1)
func _ready():
$Enemy.connect("hit", Callable(self, "_on_enemy_hit"))
func _on_enemy_hit(dmg: int) -> void:
print("got hit:", dmg)
お互いを直接参照しないので、差し替え・テストがぐっと楽。大きめのプロジェクトほど恩恵があります。
7) シーン切り替えと永続オブジェクト:Autoload
DontDestroyOnLoadの代わりに、GodotはAutoloadでスクリプトやシーンを常駐させます。BGMやセーブ、共通データにどうぞ。
どこからでもGlobal.scoreのようにアクセスできます。やりすぎるとなんでも屋になりがちなので、役割はほどほどに。
8) 入力・物理・座標でよくある小さなつまずき
- InputMap:プロジェクト>プロジェクト設定>インプットマップでアクションを登録 → Input.is_action_pressed(“jump”) の形で使います。
- 2D/3Dのノードは別物:2DはNode2D系、3DはNode3D系。2DはY軸が下向き正です。
- 物理_physics_process()とmove_and_slide()をセットで。フレーム不一致で挙動がぶれたらまずここを確認。
- 座標:global_positionとpositionの取り違えは定番の事故。弾の発射位置などは global_positionが安心。
9) よくある質問(Unity視点)
Q. Prefab Variantはありますか?
A. Godotはシーン継承や、インスタンス化後の差分編集で近い運用ができます。ベース.tscnを継承して局所的に差し替えるのが実用的。
Q. GetComponent相当は?
A. 構造を明示して、必要な子は@onready varで取得。横連携はSignalに寄せると絡みにくいです。
Q. ScriptableObjectは?
A. Resource(.tres/.res) が近い立ち位置。パラメータやデータ定義を外だしできます。
Q. Timeline/Animatorは?
A. AnimationPlayer/AnimationTree が担当。2DスプライトはSpriteFrames、合成や遷移は AnimationTree。
小さく宣伝
記事の内容を「避けゲー」を作りながら一気に身につけたい人向けに、Udemyで講座を用意しています。GDScriptの基礎 → 当たり判定 → 演出 → Windows向け出力まで、完成プロジェクト付きで伴走します。