Menu
CoddyTech

RefCountedとNode

CoddyのGDScriptジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 43/65。

すべてのオブジェクトは解放されるまでメモリを使用します。RefCountedとNodeは異なる方法で解放されます。

RefCountedオブジェクトは、それを参照する変数、配列、オブジェクトの数を数えます。その数がゼロになると、Godotは自動的にオブジェクトを解放します。「特殊メソッド」の章にあるNOTIFICATION_PREDELETEは、その瞬間を示しています。

# note.gd
class_name Note
extends RefCounted

func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		print("Note freed")

# main.gd
func write() -> void:
	var note := Note.new()
	print("Note written")

func _ready():
	write()          # Note が書き込まれ、その後 Note が解放される
	print("Back in _ready")

noteだけが参照だったため、write()が終了するとすぐにnoteは解放されます。

Nodeはカウントされません。シーンツリー内のノードは親とともに解放され、ゲームが終了するとツリー全体が解放されます。ツリーから取り出したノード、またはツリーに追加しなかったノードは、自分で解放する必要があります。

crate.free()         # すべての子とともに今すぐ解放される
crate.queue_free()   # 現在のフレームの終了時に解放される

free()の後、ノードは消えます。is_instance_valid(crate)はfalseを返し、そのノードのメソッドを呼び出すとエラーになります。このフレーム中にほかのコードがまだノードを使う可能性がある場合は、queue_free()を使うのが安全ですが、フレームが終了するまではノードはツリー内に残り、get_child_count()のカウントにも含まれます。

解放されないノードはメモリリークです。プログラムの終了時に、Godot は「ObjectDB instances were leaked at exit」と報告します。RefCounted オブジェクトも、互いに参照し合うとリークします。どちらの参照カウントもゼロにならないためです。リンクは一方向だけに保存するか、オブジェクトの代わりに名前を保存してください。

データ(アイテム、スコア、レコード)にはRefCountedを使います。シーンツリー内に存在するものにはNodeを使います。

challenge icon

チャレンジ

簡単

Waveノードは敵を子ノードとして保持します。enemy.gdは完成しています。Enemy.new("Goblin", 5)は、5 HPのGoblinという名前の敵ノードを作成します。Waveの3つのメソッドを記述してください。

  • spawn(enemy_name, hp)は新しい敵を子として追加します。その名前の子がすでに存在する場合は、Orc is already hereを出力し、何も作成しません。
  • hit(enemy_name, damage)はその敵のhpを減らし、Orc has 8 HPを出力します。HPが0以下になった場合は、Orc defeatedを出力し、敵をすぐに完全に削除します。次に呼び出されるalive()では、その敵を数えてはいけません。存在しない名前の場合は、No Orc hereを出力します。
  • alive()は残っている敵の数を返します。

すべてのノードを解放しなければなりません。終了時にメモリリークがあるとテストに失敗します。提供されているコードはウェーブをシーンツリーに追加し、後に続くコマンドの数を読み取って(spawn Goblin 5、hit Goblin 6、count)、残っている敵を出力します。spawn Goblin 5、spawn Orc 12、hit Goblin 6、count、hit Goblin 1、spawn Orc 3、hit Orc 4を実行すると、次のように出力されます。

Goblin defeated
Enemies: 1
No Goblin here
Orc is already here
Orc has 8 HP
Enemies left: 1

コードはwave.gdとenemy.gdに記述してください。main.gdには提供された入出力コードが含まれており、編集できません。

自分で試してみよう

extends Node

func _ready():
	# 提供された入出力コード:そのままにしておいてください
	var wave := Wave.new()
	add_child(wave)
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "spawn":
			wave.spawn(parts[1], int(parts[2]))
		elif parts[0] == "hit":
			wave.hit(parts[1], int(parts[2]))
		else:
			print("Enemies: ", wave.alive())
	print("Enemies left: ", wave.alive())
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: GDScriptオンラインコンパイラ