Menu
CoddyTech

Giới thiệu về phương thức ảo

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 31 trên 65.

Python có các phương thức đặc biệt như __init__ và __str__, mà ngôn ngữ sẽ gọi thay bạn. Phiên bản tương ứng trong Godot là phương thức ảo: một phương thức có tên cố định, bắt đầu bằng dấu gạch dưới, do bạn định nghĩa trong lớp và Godot gọi vào đúng thời điểm. Bạn đã dùng hai phương thức:

  • _init() chạy khi new() tạo đối tượng, với các đối số được truyền vào new().
  • _ready() chạy khi một nút đi vào cây cảnh, nơi main.gd bắt đầu.

Các phương thức khác là _to_string(), được gọi khi in ra một đối tượng, các phương thức iterator cho phép vòng lặp for duyệt qua một đối tượng (cả hai nội dung này sẽ có trong các bài học tiếp theo), và _notification(), nhận sự kiện từ engine. Một trong số đó, NOTIFICATION_PREDELETE, được gửi ngay trước khi đối tượng bị xóa:

# torch.gd
class_name Torch
extends RefCounted

var label: String

func _init(new_label: String = "Torch") -> void:
	label = new_label
	print(label, " lit")

func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		print(label, " burned out")
# main.gd
var torch := Torch.new("Hall torch") # Đuốc Hall torch đã sáng
torch = null # Đuốc Hall torch đã tắt

Đối tượng RefCounted sẽ bị xóa ngay khi không còn biến nào tham chiếu đến nó, vì vậy torch = null gửi thông báo ngay lập tức. Giá trị mặc định trong _init() khiến đối số của new() trở thành tùy chọn: Torch.new() tạo ra một ngọn đuốc có tên là Torch.

Python cũng dùng các phương thức đặc biệt cho toán tử, chẳng hạn như __add__ cho + và __eq__ cho ==. GDScript không hỗ trợ nạp chồng toán tử: potion_a + potion_b sẽ gây lỗi. Thay vào đó, hãy viết một phương thức:

# potion.gd
class_name Potion
extends RefCounted

var power: int

func _init(start_power: int) -> void:
	power = start_power

func combined(other: Potion) -> Potion:
	return Potion.new(power + other.power)

potion_a.combined(potion_b) trả về một lọ thuốc mới và giữ nguyên cả hai lọ thuốc ban đầu. Toán tử == giữa hai đối tượng kiểm tra xem hai bên có phải là cùng một đối tượng hay không, chứ không kiểm tra xem giá trị của chúng có khớp nhau hay không: Potion.new(5) == Potion.new(5) là false. Hãy viết một phương thức như equals(other) khi bạn cần so sánh các giá trị.

challenge icon

Thử thách

Dễ

Viết lớp Counter trong counter.gd:

  • _init(start = 0, step_size = 1) lưu giá trị bắt đầu vào value và bước tăng vào step. Các giá trị mặc định đã có trong chữ ký hàm, nên Counter.new(), Counter.new(5) và Counter.new(5, 2) đều hoạt động.
  • tick() cộng step vào value.
  • plus(other) thay cho toán tử +, vốn không thể được định nghĩa trong GDScript: hàm trả về một mới Counter có giá trị bằng tổng của hai giá trị và có bước tăng bằng bước tăng của bộ đếm này. Cả hai bộ đếm đều không thay đổi.
  • equals(other) trả về true khi hai bộ đếm có cùng giá trị.

Đoạn code được cung cấp đọc hai dòng để tạo bộ đếm a và b (một dòng trống, START hoặc START STEP), sau đó đọc một dòng cho biết số lần tăng a và b. Đoạn code in ra cả hai bộ đếm, tổng của chúng, việc giá trị của chúng có bằng nhau hay không và việc a == b có đúng hay không. Với các dòng 5, 2 3 và 3 1, đoạn code in ra:

a: 8 (step 1)
b: 5 (step 3)
a plus b: 13 (step 1)
same value: false
same object: false

Đặt code của bạn trong counter.gd. main.gd chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

extends Node

func _ready():
	# Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
	var a := make_counter(OS.read_string_from_stdin())
	var b := make_counter(OS.read_string_from_stdin())
	var ticks := OS.read_string_from_stdin().split(" ")
	for i in int(ticks[0]):
		a.tick()
	for i in int(ticks[1]):
		b.tick()
	print("a: %d (step %d)" % [a.value, a.step])
	print("b: %d (step %d)" % [b.value, b.step])
	var total := a.plus(b)
	print("a plus b: %d (step %d)" % [total.value, total.step])
	print("same value: %s" % a.equals(b))
	print("same object: %s" % (a == b))

func make_counter(line: String) -> Counter:
	var parts := line.split(" ", false)
	if parts.is_empty():
		return Counter.new()
	if parts.size() == 1:
		return Counter.new(int(parts[0]))
	return Counter.new(int(parts[0]), int(parts[1]))
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