Menu
CoddyTech

Введение в виртуальные методы

Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 31 из 65.

В Python есть специальные методы, такие как __init__ и __str__, которые язык вызывает за вас. В Godot им соответствуют виртуальные методы: методы с фиксированным именем, начинающимся с подчёркивания, которые вы определяете в своём классе, а Godot вызывает в нужный момент. Вы уже использовали два таких метода:

  • _init() выполняется, когда new() создаёт объект, с аргументами, переданными в 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") # Факел в зале зажжён
torch = null # Факел в зале погас

Объект 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 в файле counter.gd:

  • _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), а затем одну строку с количеством вызовов tick для a и b. Он выводит оба счётчика, их сумму, совпадают ли их значения и выполняется ли условие 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