6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Godot】シーンとノードについて

Last updated at Posted at 2021-02-16

概要

この記事では、シーンとノードについて個人的に調べたことを書いていきます。

シーンの構成

シーンは、1つの「ルート」ノードと、それにぶら下がる「子ノード」から構成されます。
Godot_Engine_-TestTree-_Main_tscn.png

上記画像は以下のような構成となっています。

Main
 +-- bg_back
 +-- player
 +-- TakoLayer
      +-- tako
      +-- tako1
      +-- tako2

同一階層には同名のオブジェクトは配置できない

また同一階層のオブジェクト名は「違う名前」でなければなりません
例えば

// × この構成にはできない
Main
 +-- tako
 +-- tako
 +-- tako

という構成にはできません。(シーンエディタ上でこのように名前変更しようとしても、自動で連番にリネームされます)

ただし、異なる階層であれば同名のオブジェクトにすることが可能です。

// ○ 異なる階層であれば同名のオブジェクトは存在できる
Main
 +-- tako ← "tako" 同一シーンに2つ存在する
 +-- TakoLayer
      +-- tako ← "tako" 同一シーンに2つ存在する
      +-- tako1

オブジェクトの実行順

オブジェクトの実行順は、ルート以外の各オブジェクトが おおよそ 上から順に実行されます(例外があるみたい?)

Godot_Engine_-TestTree-_Main_tscn.png
この構成であれば実行順は以下のとおりです。

  1. bg_back
  2. player
  3. TakoLayer
    4. tako
    5. tako2
    6. tako3
  4. 最後に Main が実行される

Mainが最初に実行されるわけではないことに注意が必要そうです。
ただ、個人的にはノードの実行順に依存するような実装にはできるだけしないほうが、安全な作りになる気がしています。(よくわからない問題が起きにくくなるため)

シーンの描画順

シーンの描画順は基本的にノードの並び順で描画されるようです。
Mainが最後に描画、ということはなさそうです。

また、z_indexCanvasLayer で描画順を細かく制御可能です。
そのあたりについては以下の記事に書きましたので、ここでは省略します。

実行中のノードを確認する

実行中のノードの状況を確認する方法についてです。
テスト用のインスタンスとして "TestNode2D" というシーンを作成し、それをMainノードから生成するようにしてみます。

Main.gd
extends Node2D

onready var TestNode2D = preload("res://TestNode2D.tscn")

func _process(delta):
	if Input.is_action_just_pressed("ui_accept"):
		# Spaceキーで "TestNode2D" を生成
		var test = TestNode2D.instance()
		add_child(test)

実行して、シーンツリーにある「リモート」をクリックすると現在のシーンに存在するノードを確認できます。

shot.gif

ノードへのアクセス方法

$ / get_node() でアクセスする

最もお手軽な各ノードへのアクセス方法は $ノード名 です。

Main.gd
extends Node2D

func _ready():
	# Mainが現在のノード
	
	# Main/bg_back
	print($bg_back.name)
	
	# Main/player
	print($player.name)
	
	# Main/TakoLayer/tako2
	print($TakoLayer/tako2.name)

ノードの階層をたどって直接アクセスできます。
実行結果は以下のとおりです

bg_back
player
tako2

$によるアクセスは、get_node() と同じなので、先程のコードは以下のように置き換えることができます。

Main.gd
extends Node2D

func _ready():
	# Mainが現在のノード
	
	# Main/bg_back
	print(get_node("bg_back").name)
	
	# Main/player
	print(get_node("player").name)
	
	# Main/TakoLayer/tako2
	print(get_node("TakoLayer/tako2").name)

文字列指定なので、動的にノードツリーのパスを作りたい場合は、get_node() を使うことになると思います。

get_children() で子ノードををすべて取得

get_children() を使うと、対象のノードの子ノードをすべて取得することが可能です。

Main.gd
extends Node2D

func _ready():
	# Mainが現在のノード
	
	# TakoLayerのすべての子ノードを取得	
	for tako in $TakoLayer.get_children():
		print(tako.name)
実行結果
tako
tako2
tako3

親の階層をたどる "../ get_parent()

親の階層をたどる方法としてお手軽なのが、"../" です。
例えば、takoノードからplayerノードを取得するとします。
Godot_Engine_-TestTree-_Main_tscn.png

この場合、

  1. takoから TakoLayerに移動 (親の階層に移動)
  2. TakoLayerからMainに移動 (親の階層に移動)
  3. Mainからplayerを取得 (子 "player" を取得)

という流れになります。

takoノードスクリプトをアタッチした場合は以下のコードとなります。

tako.gd
extends Sprite

func _ready():
	# ○ この書き方のみ可能
	print($"../../player".name)

	# × この書き方はNG
	print($../../player.name)

"../"で1つ上の階層に移動できますが、"."(ピリオド) がプログラム上特別な文字として扱われるため、文字列(ダブルクオートで囲む)でないとコンパイルエラーとなります。

また get_parent() を使用すると親のノードを取得できるので、以下のような書き方も可能です。

tako.gd
extends Sprite

func _ready():
	# 親の親の子 "player" を取得
	print(get_parent().get_parent().get_node("player").name)

所有者の取得 get_owner()

get_owner() そのノードの所有者を取得します。
所有者とは、そのシーンのルートノードとなるようです。
(例えば現在の構成であれば常に "Main"ノード)
ただし、Node.instance() で動的に生成した場合は所有者はいない状態となります。

Main.gd
extends Node2D

onready var TestNode2D = preload("res://TestNode2D.tscn")

func _ready():
	# Mainが現在のノード
	# 以下はすべて "Main" が出力される
	print($player.get_owner().name)
	print($bg_back.get_owner().name)
	print($TakoLayer/tako.get_owner().name)
	
	# 自分自身が所有者なので null
	print(get_owner())

	# 動的に生成した場合も null
	var test = TestNode2D.instance()
	print(test.get_owner())
実行結果
Main
Main
Main
[Object:null]
[Object:null]

一応は Node.set_owner() で所有者を設定できるようですが、Godotの設計思想としては動的なインスタンスに対して所有者を設定することは良くないみたいです。

I believe you can use set_owner to make it work, but I feel it would go against the spirit of owners.
Instead, I'd suggest using node references, like get_parent or creating/setting a variable on the relevant objects.

set_ownerを使用して機能させることができると思いますが、所有者の精神に反すると思います。 代わりに、get_parentのようなノード参照を使用するか、関連するオブジェクトに変数を作成/設定することをお勧めします。

動的なインスタンスから動的にインスタンスを生成してノードにぶら下げるような場合(例えば、ボスが敵を生成して、敵が敵弾を発射するような場合)、敵のインスタンスは get_owner() を使うのではなく、get_parent() を使用するか、配置済みの CanvasLayer にぶら下げる……などの対応をしたほうが良さそうです。

find_node() / find_parent() でノードを検索する

find_node() を使用すると、子ノードから該当のノードを検索できます。

また検索文字列にはワイルドカードとして、*(アスタリスク) と ?(クエスチョンマーク)が使用できます。

例えば以下のコードでは "TestNode2D" が含まれるノードを検索します。

var node = find_node("*TestNode2D*")

文字数が決まっていれば "?" で検索することもできます。

# "TestNode2D" または "TestNode3D" を検索
var node = find_node("TestNode?D")

find_parent() は子ではなくて親を検索する関数となります。

参考

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?