Menu
CoddyTech

RefCounted và Node

Thuộc phần Lập trình hướng đối tượng trong hành trình GDScript của Coddy. Bài học 43 trên 65.

Mỗi đối tượng chiếm bộ nhớ cho đến khi được giải phóng. RefCounted và Node được giải phóng theo những cách khác nhau.

Đối tượng RefCounted đếm số biến, mảng và đối tượng tham chiếu đến nó. Khi số đếm giảm về 0, Godot tự giải phóng đối tượng đó. NOTIFICATION_PREDELETE, trong chương Phương thức đặc biệt, cho thấy thời điểm đó:

# 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 written, sau đó Note freed
	print("Back in _ready")

note là tham chiếu duy nhất, nên ghi chú sẽ được giải phóng ngay khi write() kết thúc.

Node không được tính. Một nút trong cây cảnh sẽ được giải phóng cùng với nút cha, và toàn bộ cây sẽ được giải phóng khi trò chơi kết thúc. Bạn phải tự giải phóng nút mà bạn đưa ra khỏi cây hoặc không bao giờ thêm vào cây:

crate.free()         # Giải phóng ngay cùng với tất cả nút con
crate.queue_free()   # Giải phóng vào cuối khung hình hiện tại

Sau khi gọi free(), nút không còn tồn tại: is_instance_valid(crate) trả về false, và gọi phương thức trên nút đó sẽ gây ra lỗi. queue_free() là lựa chọn an toàn khi code khác vẫn có thể dùng nút đó trong khung hình này, nhưng nút vẫn nằm trong cây và vẫn được tính trong get_child_count() cho đến khi khung hình kết thúc.

Một nút không bao giờ được giải phóng sẽ gây ra rò rỉ bộ nhớ: Godot báo cáo "ObjectDB instances were leaked at exit" khi chương trình kết thúc. Các đối tượng RefCounted cũng bị rò rỉ khi chúng tham chiếu lẫn nhau, vì bộ đếm của cả hai không thể giảm về 0. Chỉ lưu liên kết theo một chiều, hoặc lưu tên thay vì đối tượng.

Dùng RefCounted cho dữ liệu: vật phẩm, điểm số, bản ghi. Dùng Node cho những thứ nằm trong cây cảnh.

challenge icon

Thử thách

Dễ

Một nút Wave lưu các kẻ địch của nó dưới dạng các nút con. enemy.gd đã hoàn chỉnh: Enemy.new("Goblin", 5) tạo một nút kẻ địch có tên Goblin với 5 HP. Hãy viết ba phương thức của Wave:

  • spawn(enemy_name, hp) thêm một kẻ địch mới làm nút con. Nếu đã có nút con mang tên đó, nó in ra Orc is already here và không tạo gì cả.
  • hit(enemy_name, damage) giảm hp của kẻ địch đó và in ra Orc has 8 HP. Khi HP còn 0 hoặc thấp hơn, nó in ra Orc defeated và xóa hẳn kẻ địch ngay lập tức: lần gọi alive() tiếp theo không được tính kẻ địch đó. Nếu không có tên đó, nó in ra No Orc here.
  • alive() trả về số kẻ địch còn lại.

Phải giải phóng mọi nút: mọi rò rỉ bộ nhớ khi thoát đều khiến bài kiểm tra thất bại. Code được cung cấp thêm wave vào cây cảnh, đọc số lệnh tiếp theo (spawn Goblin 5, hit Goblin 6, count) rồi in ra số kẻ địch còn lại. Với spawn Goblin 5, spawn Orc 12, hit Goblin 6, count, hit Goblin 1, spawn Orc 3, hit Orc 4, chương trình in ra:

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

Code của bạn nằm trong wave.gd và enemy.gd. main.gd chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

extends Node

func _ready():
	# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
	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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch GDScript online