Menu
CoddyTech

虚方法简介

本课属于 Coddy GDScript 学习旅程的“面向对象编程”单元。第 31 课,共 65 课。

Python 有一些特殊方法,例如 __init__ 和 __str__,语言会为你调用它们。Godot 中与之对应的是虚方法:方法名固定,以下划线开头,由你在类中定义,Godot 会在适当的时候调用它。你已经用过两个:

  • 当 new() 创建对象时,会运行 _init(),并传入提供给 new() 的参数。
  • 当节点进入场景树时,会运行 _ready()。main.gd 就是从这里开始运行的。

其他方法还包括 _to_string(),它会在打印对象时被调用;还有让 for 循环遍历对象的迭代器方法(这两种方法会在接下来的两节课中介绍),以及 _notification(),它会接收来自引擎的事件。其中一个事件是 NOTIFICATION_PREDELETE,它会在对象被删除前立即触发:

# 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") # Hall torch 点亮了
torch = null # Hall torch 烧坏了

一旦没有变量再引用 RefCounted 对象,它就会被删除,因此 torch = null 会立即发送通知。_init() 中的默认值使 new() 的参数变为可选参数:Torch.new() 会创建一个名为 Torch 的火炬。

Python 也使用魔术方法来实现运算符,例如用 __add__ 表示 +,用 __eq__ 表示 ==。GDScript 不支持运算符重载:potion_a + potion_b 会报错。你可以改为编写一个方法:

# 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)会返回一个新药水,并且不会改变两个原药水。两个对象之间的==会检查两边是否是同一个对象,而不是检查它们的值是否相同:Potion.new(5) == Potion.new(5)的结果是false。如果你需要比较值,可以编写equals(other)这样的方法。

challenge icon

挑战

简单

在 counter.gd 中编写 Counter 类:

  • _init(start = 0, step_size = 1) 将起始值存入 value,将步长存入 step。签名中已经设置了默认值,因此 Counter.new()、Counter.new(5) 和 Counter.new(5, 2) 都可以使用。
  • tick() 将 step 加到 value 上。
  • plus(other) 用来替代 + 运算符,GDScript 不允许你定义这个运算符:它会返回一个新的 Counter,其值为两个计数器的值之和,步长则为当前计数器的步长。两个计数器都不会改变。
  • equals(other) 在两个计数器的值相同时返回 true。

提供的代码会读取两行来创建计数器 a 和 b(空行、START 或 START STEP),然后读取一行,指定对 a 和 b 执行 tick 的次数。它会输出两个计数器、它们的和、它们的值是否相等,以及 a == b 是否成立。对于 5、2 3 和 3 1 这几行输入,它会输出:

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

请将代码写在 counter.gd 中。main.gd 包含提供的输入/输出代码,不能编辑。

亲手试试

extends Node

func _ready():
	# 提供的输入输出代码:保持原样
	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 icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: GDScript 在线编译器