Введение в виртуальные методы
Часть раздела Объектно-ориентированное программирование путешествия по 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).
Задание
ЛегкоНапишите класс 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]))
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово selfМетодыСвойстваКонструктор (_init)Повторение: простой калькулятор4Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур7Сигналы
Что такое сигнал?Объявление и отправкаПодключение к сигналамСигналы с аргументамиПовторение — система здоровья10Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»2Свойства классов
Переменные экземпляра и классаСтатические функцииСеттеры свойствГеттеры свойствПриватность по соглашениюПовторение — менеджер банковского счёта5Инкапсуляция
Открытые и закрытые членыПроверка данных с помощью сеттеровСокрытие информацииСвойства только для чтенияПовторение — система учета студентов8Продвинутые концепции ООП
Композиция и наследованиеВнутренние классыУзлы как компонентыRefCounted и NodeПеречисления в классах11Проект: Управление библиотекой
Обзор проектаКлассы книг и пользователей3Наследование
Основы наследованияКлючевое слово superПереопределение методовЦепочки наследованияПроверка типов с помощью isПовторение — иерархия сотрудников6Специальные методы
Введение в виртуальные методыМетод _to_stringПользовательские итераторыПовторение: пользовательский список9Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор GDScript