Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Когда использовать сцены по сравнению со скриптами

Мы уже рассмотрели, чем отличаются сцены и скрипты. Скрипты определяют расширение класса движка с императивным кодом, сцены - с декларативным кодом.

В результате возможности каждой системы оказываются различными. Сцены могут определять, как инициализируется расширенный класс, но не его поведение. Сцены часто используются совместно со скриптами, при этом сцена объявляет состав узлов, а скрипт добавляет поведение с помощью императивного кода.

Анонимные типы

Возможно полностью определить содержимое сцены, используя только скрипт. Это, по сути, то, что делает редактор Godot, только в конструкторе C++ своих объектов.

Но выбор того, какой из них использовать, может оказаться дилеммой. Создание экземпляров скрипта идентично созданию классов в движке, тогда как обработка сцен требует изменения в API:

const MyNode = preload("my_node.gd")
const MyScene = preload("my_scene.tscn")
var node = Node.new()
var my_node = MyNode.new() # Same method call.
var my_scene = MyScene.instantiate() # Different method call.
var my_inherited_scene = MyScene.instantiate(PackedScene.GEN_EDIT_STATE_MAIN) # Create scene inheriting from MyScene.

Кроме того, скрипты будут работать немного медленнее, чем сцены, из-за разницы в скорости между движком и кодом скрипта. Чем больше и сложнее узел, тем больше причин строить его как сцену.

Именованные типы

Скрипты могут быть зарегистрированы как новый тип в самом редакторе. При этом он отображается как новый тип в диалоге создания узла или ресурса с дополнительной пиктограммой. Таким образом, возможности пользователя по использованию скрипта становятся гораздо более удобными. Вместо того чтобы...

  1. Знать базовый тип скрипта, который они хотели бы использовать.

  2. Создайте экземпляр этого базового типа.

  3. Добавьте скрипт в узел.

При наличии зарегистрированного скрипта заскриптованный тип становится опцией создания, как и другие узлы и ресурсы в системе. В диалоге создания даже имеется строка поиска, позволяющая найти тип по имени.

Существует две системы регистрации типов:

  • Пользовательские Типы

    • Editor-only (Только редактор). Имена типов недоступны во время выполнения.

    • Не поддерживает унаследованные пользовательские типы.

    • Инструмент инициализации. Создает узел со скриптом. Ничего более.

    • Редактор не имеет никакого представления о типе скрипта или его связи с другими типами движков или скриптов.

    • Позволяет пользователям определять значок.

    • Работает для всех скриптовых языков, потому что работает со скриптовыми ресурсами абстрактно.

    • Настройка с помощью EditorPlugin.add_custom_type.

  • Классы сценариев <doc_gdscript_basics_class_name>`

    • Доступны редактор и среда выполнения.

    • Полностью отображает отношения наследования.

    • Создаёт узел со скриптом, но также может изменять типы или расширять тип из редактора.

    • Редактор знает об отношениях наследования между скриптами, классами скриптов и классами C++ движка.

    • Позволяет пользователям определять значок.

    • Разработчики движка должны добавлять поддержку языков вручную (как для отображения имени, так и для доступности во время выполнения).

    • Только Godot версии 3.1 и выше.

    • Редактор сканирует папки проекта и регистрирует любые открытые имена для всех языков скриптов. Каждый язык скриптов должен реализовывать свою собственную поддержку для раскрытия этой информации.

Обе методологии добавляют имена в диалог создания, но классы скриптов, в частности, также позволяют пользователям получить доступ к названию типа без загрузки ресурса скрипта. Создание экземпляров и доступ к константам или статическим методам является жизнеспособным из любого места.

С такими функциями можно пожелать, чтобы их тип был скриптом без сцены из-за простоты использования, которую он предоставляет пользователям. Тем, кто разрабатывает плагины или создает собственные инструменты для дизайнеров, таким образом будет легче.

С другой стороны, это также означает необходимость использования в значительной степени императивного программирования.

Производительность сценариев и PackedScene

Последний аспект, который следует учитывать при выборе сцен и скриптов, - это скорость выполнения.

По мере увеличения размера объектов необходимый размер скриптов для их создания становится намного больше. Создание иерархии узлов демонстрирует это. Логика каждого отдельного узла может состоять из нескольких сотен строк кода.

Приведенный ниже код создаёт новый узел Node, меняет его имя, назначает ему сценарий, устанавливает его будущего родителя в качестве его владельца, чтобы он сохранялся на диске вместе с ним, и, наконец, добавляет его в качестве дочернего элемента главного узла Main:

# main.gd
extends Node

func _init():
    var child = Node.new()
    child.name = "Child"
    child.script = preload("child.gd")
    add_child(child)
    child.owner = self

Такой код сценария намного медленнее, чем код C++ на стороне движка. Каждое изменение вызывает отдельный вызов API сценариев, что приводит к множеству "поисков" в серверной части, чтобы найти логику для выполнения.

Сцены помогают избежать этой проблемы с производительностью. PackedScene, базовый тип, от которого наследуются сцены, - это ресурсы, использующие сериализованные данные для создания объектов. Движок может обрабатывать сцены в пакетном режиме на сервере и обеспечивать гораздо лучшую производительность, чем скрипты.

Заключение

В конце концов, лучший подход заключается в том, чтобы рассмотреть следующее:

  • Если кто-то хочет создать базовый инструмент, который будет повторно использоваться в нескольких различных проектах, и который, скорее всего, будут использовать люди всех уровней квалификации (включая тех, кто не называет себя "программистами"), то, скорее всего, это должен быть скрипт, скорее всего, с пользовательским именем/значком.

  • Если кто-то хочет создать концепцию, характерную для его игры, то это всегда должна быть сцена. Сцены легче отслеживать/редактировать и обеспечивают большую безопасность, чем скрипты.

  • Если хочется дать сцене имя, то это можно сделать, объявив класс скрипта и присвоив ему сцену в качестве константы. Скрипт становится, по сути, пространством имён:

    # game.gd
    class_name Game # extends RefCounted, so it won't show up in the node creation dialog.
    extends RefCounted
    
    const MyScene = preload("my_scene.tscn")
    
    # main.gd
    extends Node
    func _ready():
        add_child(Game.MyScene.instantiate())