Godot: Улучшенные пользовательские узлы EditorPlugin

Созданный на 7 авг. 2016  ·  121Комментарии  ·  Источник: godotengine/godot

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

Это полностью отличается от других узлов и делает надстройки узлов практически бесполезными/намного более раздражающими в использовании.

feature proposal plugin usability

Самый полезный комментарий

Насколько я понимаю, ожидается возможность создания пользовательских узлов, которые будут вести себя как встроенные узлы, т.е. они должны иметь следующие функции:

  • Пользовательский значок, пользовательский идентификатор
  • Мгновенное создание с помощью виджета «Добавить узел» (и, я думаю, с помощью скрипта, так что доступ к Global Scope?)
  • Иметь собственный API, закодированный с помощью скрипта (например, RedNode2D будет расширять Node2D и иметь красную модуляцию, определенную с помощью пользовательского скрипта)
  • Затем этот пользовательский узел должен вести себя как встроенный узел, т. е. пользователь должен иметь возможность создать его экземпляр вообще без сценария (пользовательский API не будет напрямую доступен пользователю, как и для встроенного сценария, где он жестко запрограммирован). в C++) и прикрепите к нему скрипт, который будет расширять пользовательский узел (например, extends RedNode2D ).

Это было бы «естественным» ожиданием при объявлении пользовательского узла и было бы очень мощной функцией; Из вышесказанного я понял, что пока так не получается, отчасти из-за дизайнерских решений. Если бы существовал способ иметь функциональность, подобную той, что я описал выше, я почти уверен, что она нашла бы множество применений. Активная библиотека будет полна настраиваемых узлов, которые делают много работы из коробки и могут использоваться так, как если бы они были встроенными.

Открытие до тех пор, пока не будет достигнут консенсус о том, что можно/нужно делать, а что нет.

Все 121 Комментарий

я не понимаю, что ты имеешь в виду

@reduz, когда вы добавляете в сцену узел, тип которого создан плагином, к нему уже прикреплен скрипт плагина. Поэтому невозможно добавить еще один скрипт с нестандартным поведением.

Конечно нет, это по основной конструкции и не изменится.

7 августа 2016 г., 18:11, «Джордж Маркес» [email protected] написал:

@reduz https://github.com/reduz при добавлении в сцену узла, который
созданный плагином, к нему уже прикреплен скрипт плагина. Так
невозможно добавить еще один скрипт с настраиваемым поведением.


Вы получаете это, потому что вас упомянули.

Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238108767,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z29F5q8PaoBv4OrzAUayzrfNjfHyZks5qdkoUgaJpZM4JejbZ
.

Это грустно. Но вы всегда можете добавить скрипт к родительскому или дочернему.

Закрытие как wontfix.

Я могу что-то не так понять, но если ваш пользовательский тип — это скрипт,
как его не включить в созданный узел? Это не имеет смысла для
быть другим

7 августа 2016 г., 21:52, «Джордж Маркес» [email protected] написал:

Это грустно. Но вы всегда можете добавить скрипт к родительскому или дочернему.

Закрытие как wontfix.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238120392,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z27Zo4Ixvo4APSC4Fxf5ZqCJRsAxXks5qdn3igaJpZM4JejbZ
.

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

Годо изначально поддерживал это, но это было больше неприятностей, чем пользы.

7 августа 2016 г., 22:36, «Джордж Маркес» [email protected] написал:

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


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238123729,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z27jidVZl-hHWW3G_ESr8Cqj3eX7Eks5qdogRgaJpZM4JejbZ
.

Проблема в том, что пользовательские узлы практически бесполезны, поскольку на самом деле они не являются «настраиваемыми узлами», это просто базовые узлы с предустановленным скриптом и другим значком.
Чего я ожидал от «настраиваемых узлов», так это того, что я мог бы расширить узел, чтобы использовать все, что определено в сценарии. Пример сценария:
У меня есть пользовательский узел под названием Test, который является дочерним элементом Sprite и добавляет функцию test() , которая возвращает true . Затем я хотел бы создать новый тестовый узел, назначить ему скрипт и использовать функцию test() .
Это невозможно.

Я думаю, вернемся к сцене, чтобы наследовать + скрипт, чтобы расширить комбо.

Ну, быть предустановленным узлом с предустановленным скриптом и другим значком — это ИМО.
достаточно пользовательского, но если вы действительно хотите поместить свой собственный код в
там вы всегда можете унаследовать те, которые идут с узлом. Мы могли бы
сделать это немного проще из пользовательского интерфейса, если это действительно необходимо.

Пн, 8 августа 2016 г., 10:40, Доминик Банашак, [email protected]
написал:

Проблема в том, что пользовательские узлы практически бесполезны, поскольку на самом деле они не
"пользовательские узлы", это просто базовые узлы с предустановленным скриптом и разными
значок. Я думаю, вернемся к сцене, чтобы наследовать + скрипт, чтобы расширить комбо.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238240130,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z27eHoXwgr6buF6CCAHfxwx1dKkCiks5qdzHhgaJpZM4JejbZ
.

Я был бы рад, если бы это можно было автоматизировать/упростить через пользовательский интерфейс.

Насколько я понимаю, ожидается возможность создания пользовательских узлов, которые будут вести себя как встроенные узлы, т.е. они должны иметь следующие функции:

  • Пользовательский значок, пользовательский идентификатор
  • Мгновенное создание с помощью виджета «Добавить узел» (и, я думаю, с помощью скрипта, так что доступ к Global Scope?)
  • Иметь собственный API, закодированный с помощью скрипта (например, RedNode2D будет расширять Node2D и иметь красную модуляцию, определенную с помощью пользовательского скрипта)
  • Затем этот пользовательский узел должен вести себя как встроенный узел, т. е. пользователь должен иметь возможность создать его экземпляр вообще без сценария (пользовательский API не будет напрямую доступен пользователю, как и для встроенного сценария, где он жестко запрограммирован). в C++) и прикрепите к нему скрипт, который будет расширять пользовательский узел (например, extends RedNode2D ).

Это было бы «естественным» ожиданием при объявлении пользовательского узла и было бы очень мощной функцией; Из вышесказанного я понял, что пока так не получается, отчасти из-за дизайнерских решений. Если бы существовал способ иметь функциональность, подобную той, что я описал выше, я почти уверен, что она нашла бы множество применений. Активная библиотека будет полна настраиваемых узлов, которые делают много работы из коробки и могут использоваться так, как если бы они были встроенными.

Открытие до тех пор, пока не будет достигнут консенсус о том, что можно/нужно делать, а что нет.

+1
Это было одним из первых серьезных препятствий для меня, когда я пытался перенести существующий проект с «OtherEngine(tm)» на Godot. Пользовательские типы, такие как описанный выше @akien-mga, после регистрации должны вести себя как любой другой встроенный тип.

Пожалуйста, объясните, каким образом, по вашему мнению, они не

8 августа 2016 г., 11:50, «Ralf Hölzemer» [email protected] написал:

+1
Это было одним из первых серьезных препятствий для меня, когда я пытался портировать
существующий проект с "OtherEngine(tm)" на Godot. Пользовательские типы, такие как
@akien-mga https://github.com/akien-mga описано выше, следует просто
вести себя как любой другой встроенный тип после регистрации.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238261603,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z25PJzv9w7iXO350tRF3FcEuYQYUKks5qd0IrgaJpZM4JejbZ
.

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

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

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

base.gd

extends Node
export (Color) var color

type_a.gd

extends base.gd

type_b.gd

extends base.gd

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

func _enter_tree():
    add_custom_type("Base", "Node", preload("base.gd"), preload("base.png"))
    add_custom_type("TypeA", "Node", preload("type_a.gd"), preload("type_a.png"))
    add_custom_type("TypeB", "Node", preload("type_b.gd"), preload("type_b.png"))

... и вот результат.

add_node_flat

Хотя возможность регистрировать пользовательские типы, подобные этому, удобна, диалоговые окна не отражают природу наследования этих типов, как другие встроенные типы. Для любого встроенного типа я могу посмотреть на это дерево и сразу увидеть, с чем имею дело. Я могу, например, быть уверенным, что Sprite является Node2D и поэтому наследует все функции , предоставляемые Node2D. То же самое не верно для пользовательских типов.

Теперь, если бы пользовательские типы можно было полностью зарегистрировать в глобальной области видимости, как упомянутый выше @akien-mga, все было бы намного проще понять и использовать.

Во-первых, вы можете наследовать от пользовательского типа, на который ссылается имя типа , а не путь/имя файла.

base.gd

extends Node
export (Color) var color

type_a.gd

extends Base

type_b.gd

extends Base

... тогда регистрация пользовательских типов может быть упрощена следующим образом. Обратите внимание на отсутствующий второй параметр add_custom_type.

func _enter_tree():
    add_custom_type("Base", preload("base.gd"), preload("base.png"))
    add_custom_type("TypeA", preload("type_a.gd"), preload("type_a.png"))
    add_custom_type("TypeB", preload("type_b.gd"), preload("type_b.png"))

... и вы получите гораздо более приятный обзор в диалоговых окнах «Создать новый узел/ресурс»:

add_node_tree

... и, конечно же, возможность расширить эти типы в редакторе с помощью пользовательского скрипта:

custom_type_no_script

... на который также будет ссылаться имя типа вместо имени/пути скрипта

extends Base

Вот пример плагина выше, чтобы поиграть.
аддоны.zip

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

8 августа 2016 г., 13:54, «Ralf Hölzemer» [email protected] написал:

Как уже было сказано ранее, _самый большой недостаток_ на данный момент заключается в том, что
пользовательские типы - это не более чем быстрый способ создания экземпляра базового типа.
этого скрипта и назначьте соответствующий скрипт этому экземпляру. Этот
делает невозможным расширение этого узла в редакторе другим скриптом -
как вы можете сделать со встроенными типами.

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

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

_base.gd http://base.gd_

расширяет узел
экспорт (Цвет) var цвет

_type_a.gd http://type_a.gd_

расширяет base.gd

_type_b.gd http://type_b.gd_

расширяет base.gd

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

функция _enter_tree():
add_custom_type («База», «Узел», предварительная загрузка («base.gd»), предварительная загрузка («base.png»))
add_custom_type("TypeA", "Node", preload("type_a.gd"), preload("type_a.png"))
add_custom_type("TypeB", "Node", preload("type_b.gd"), preload("type_b.png"))

... и вот результат.

[изображение: add_node_flat]
https://cloud.githubusercontent.com/assets/8785013/17486294/9bcc029c-5d90-11e6-81e6-6fce6b0e7cf0.png

Хотя приятно иметь возможность регистрировать пользовательские типы, подобные этому,
диалоги не отражают характер наследования таких типов, как
другие встроенные типы. Для любого встроенного типа я могу посмотреть на это дерево и
видеть с первого взгляда, с чем я имею дело. Я могу, например, быть уверенным, что
Sprite _является_ Node2D и поэтому наследует все функции _обеспечиваемые
by_ Node2D. То же самое не верно для пользовательских типов.

Теперь, если бы пользовательские типы могли быть полностью зарегистрированы в глобальной области видимости, например
@akien-mga https://github.com/akien-mga упоминалось выше, все бы
быть намного проще для понимания и использования.

Во-первых, вы можете наследовать от пользовательского типа, на который ссылается _имя_типа_.
вместо пути/имени файла.

_base.gd http://base.gd_

расширяет узел
экспорт (Цвет) var цвет

_type_a.gd http://type_a.gd_

расширяет базу

_type_b.gd http://type_b.gd_

расширяет базу

... тогда регистрация пользовательских типов может быть упрощена, например
это. Обратите внимание на отсутствующий второй параметр add_custom_type.

функция _enter_tree():
add_custom_type («База», предварительная загрузка («base.gd»), предварительная загрузка («base.png»))
add_custom_type("TypeA", preload("type_a.gd"), preload("type_a.png"))
add_custom_type("TypeB", preload("type_b.gd"), preload("type_b.png"))

... и вы получите гораздо более приятный обзор в "Создать новый
Диалоговые окна узла/ресурса:

[изображение: add_node_tree]
https://cloud.githubusercontent.com/assets/8785013/17487264/619f4da0-5d94-11e6-880f-a00791e30125.png

... и, конечно же, возможность расширять эти типы в редакторе с помощью
пользовательский скрипт:

[изображение: custom_type_no_script]
https://cloud.githubusercontent.com/assets/8785013/17487807/b54aced2-5d96-11e6-90e5-ee838b6a1335.png

... на который также будет ссылаться _type name_ вместо скрипта
имя/путь

расширяет базу

Вот пример плагина выше, чтобы поиграть.
addons.zip https://github.com/godotengine/godot/files/407291/addons.zip


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238299152,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z20FQioFVkVcvhhbvDQ7jQKv5De7Hks5qd19QgaJpZM4JejbZ
.

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

8 августа 2016 г., 14:02, «Хуан Линиецки» [email protected] написал:

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

8 августа 2016 г., 13:54, «Ralf Hölzemer» [email protected] написал:

Как уже было сказано ранее, _самый большой недостаток_ на данный момент заключается в том, что
пользовательские типы - это не более чем быстрый способ создания экземпляра базового типа.
этого скрипта и назначьте соответствующий скрипт этому экземпляру. Этот
делает невозможным расширение этого узла в редакторе другим скриптом -
как вы можете сделать со встроенными типами.

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

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

_base.gd http://base.gd_

расширяет узел
экспорт (Цвет) var цвет

_type_a.gd http://type_a.gd_

расширяет base.gd

_type_b.gd http://type_b.gd_

расширяет base.gd

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

функция _enter_tree():
add_custom_type («База», «Узел», предварительная загрузка («base.gd»), предварительная загрузка («base.png»))
add_custom_type("TypeA", "Node", preload("type_a.gd"), preload("type_a.png"))
add_custom_type("TypeB", "Node", preload("type_b.gd"), preload("type_b.png"))

... и вот результат.

[изображение: add_node_flat]
https://cloud.githubusercontent.com/assets/8785013/17486294/9bcc029c-5d90-11e6-81e6-6fce6b0e7cf0.png

Хотя приятно иметь возможность регистрировать пользовательские типы, подобные этому,
диалоги не отражают характер наследования таких типов, как
другие встроенные типы. Для любого встроенного типа я могу посмотреть на это дерево и
видеть с первого взгляда, с чем я имею дело. Я могу, например, быть уверенным, что
Sprite _является_ Node2D и поэтому наследует все функции _обеспечиваемые
by_ Node2D. То же самое не верно для пользовательских типов.

Теперь, если бы пользовательские типы можно было полностью зарегистрировать в глобальной области видимости,
как @akien-mga https://github.com/akien-mga , упомянутый выше, вещи
было бы намного проще понять и использовать.

Во-первых, вы можете наследовать от пользовательского типа, на который ссылается _имя_типа_.
вместо пути/имени файла.

_base.gd http://base.gd_

расширяет узел
экспорт (Цвет) var цвет

_type_a.gd http://type_a.gd_

расширяет базу

_type_b.gd http://type_b.gd_

расширяет базу

... тогда регистрация пользовательских типов может быть упрощена, например
это. Обратите внимание на отсутствующий второй параметр add_custom_type.

функция _enter_tree():
add_custom_type («База», предварительная загрузка («base.gd»), предварительная загрузка («base.png»))
add_custom_type("TypeA", preload("type_a.gd"), preload("type_a.png"))
add_custom_type("TypeB", preload("type_b.gd"), preload("type_b.png"))

... и вы получите гораздо более приятный обзор в "Создать новый
Диалоговые окна узла/ресурса:

[изображение: add_node_tree]
https://cloud.githubusercontent.com/assets/8785013/17487264/619f4da0-5d94-11e6-880f-a00791e30125.png

... и, конечно же, возможность расширять эти типы в редакторе с помощью
пользовательский скрипт:

[изображение: custom_type_no_script]
https://cloud.githubusercontent.com/assets/8785013/17487807/b54aced2-5d96-11e6-90e5-ee838b6a1335.png

... на который также будет ссылаться _type name_ вместо скрипта
имя/путь

расширяет базу

Вот пример плагина выше, чтобы поиграть.
addons.zip https://github.com/godotengine/godot/files/407291/addons.zip


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -238299152,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z20FQioFVkVcvhhbvDQ7jQKv5De7Hks5qd19QgaJpZM4JejbZ
.

@редуз
Есть ли какая-то техническая причина указывать, что узел заскриптован, и если да, то должен ли он быть занятым слотом скрипта?

Мне кажется довольно корявый способ указания пользовательского типа и думаю никому не придет в голову, что заменив текущий скрипт на узле, вы получите скрипт, расширяющий уже существующий скрипт. Просто расширение через скрипт не работает для базовых типов.

И что произойдет, если пользователь очистит это поле скрипта? Пользовательский тип возвращается к предыдущему сценарию или полностью возвращается к базовому типу? Опять же, я не думаю, что это было бы хорошей идеей.

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

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

Дело в том, что сценарий, определяющий пользовательский узел, _должен_ быть скрыт, потому что это свойство не _instanced_ узла, а его типа. Таким образом, этот сценарий должен присваивать свойства пользовательскому узлу, но он должен быть таким же невидимым для пользователя экземпляра узла, как и классы встроенных узлов C++. Он будет предоставлять API, но его нельзя будет модифицировать, а только расширять. Точно так же, как когда вы создаете экземпляр Sprite, вы не получаете scenes/2d/sprite.cpp в качестве скрипта экземпляра узла, вы не должны прикреплять my_custom_node.gd в качестве модифицируемого скрипта пользовательского узла экземпляра.

Теперь я не знаю, возможно ли это технически _возможно_ прямо сейчас, но это был бы естественный вариант использования AFAIU. Если вы измените сценарий пользовательского типа, он изменит сам тип и, таким образом, повлияет на все экземпляры этого типа. Но инстансированные узлы, использующие пользовательский тип, должны иметь свой собственный скрипт, который extends CustomNode .

Я думаю, что для этой функции потребуется, чтобы Object имел дополнительный указатель на «базовый пользовательский тип сценария», потому что вам нужна эта информация, когда вы хотите удалить из него пользовательский сценарий (который фактически должен заменить его базовым сценарием).
Как только вы это сделаете, все остальное — это включить его во все случаи, так как это может привести к многочисленным побочным эффектам. В конце концов, будет прикреплен только один скрипт, просто другой способ работы с ним.
Я не большой поклонник наследования в целом, но я бы сделал именно так.

На самом деле, этот указатель мог даже не понадобиться. Пометки скрипта будет достаточно, например, если вы добавляете add_custom_type() со скриптом, движок может установить флаг для класса, чтобы информация была доступна, как «Эй, этот класс скрипта является расширением типа движка». Удаление пользовательского сценария затем заменит его первым унаследованным классом сценария, помеченным как «пользовательский тип», или удалит его, если его нет.

Извините, я против того, чтобы скрывать скрипт, если на узле есть скрипт. Что такое
смысл имитировать что-то, что не является?

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

Что мы можем сделать, если вы согласны, так это скрыть значок скрипта в дереве сцен, если
назначенный сценарий является сценарием пользовательского типа, и сделайте сценарий
Диалоговое окно создания автоматически предлагает вам наследовать при создании скрипта.
Будет ли этого достаточно?

10 августа 2016 г., 23:01, «Марк» [email protected] написал:

На самом деле, этот указатель мог даже не понадобиться. Пометка сценария
достаточно, например, если вы add_custom_type() со скриптом, движок
можно установить флаг для класса, чтобы информация была доступна, как "Эй, это
script является расширением типа движка". Удаление пользовательского скрипта
затем замените его первым унаследованным классом сценария, помеченным как «пользовательский
type", или удалите его, если их нет.


Вы получаете это, потому что вас упомянули.

Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -239055986,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z2xLGOhgMk__ZoRW1neRu1aRb5Qr_ks5qeoJogaJpZM4JejbZ
.

@reduz Думаю, этого будет достаточно :smile:

@reduz Я согласен, и я не говорил, что нам нужен второй сценарий. Мне просто интересно, что произойдет, если вы добавите скрипт, наследующий первый (пользовательский, определенный плагином), но затем решите его удалить. Затем он вернет узел к базовому типу двигателя без какого-либо сценария?

Я полагаю, мы могли бы как-то сделать его более удобным для пользователя в этом отношении.

11 августа 2016 г., 06:10, «Марк» [email protected] написал:

@reduz https://github.com/reduz Я согласен, и я не говорил, что нам нужен
второй скрипт. Мне просто интересно, что произойдет, если вы добавите скрипт
наследует первый (пользовательский, определенный плагином), но затем решает
убери это. Затем он вернет узел к базовому типу двигателя без каких-либо дополнительных действий.
тогда сценарий?


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment -239109334,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z26JUJ0gjaCFwlsIDsINWp3_nqliwks5qeubygaJpZM4JejbZ
.

Я копаю часть расширений по типу, определенному узлом, вместо пути, описанного в этом комментарии https://github.com/godotengine/godot/issues/6067#issuecomment -238299152.

Еще несколько предложений для добавления:

  • node.get_type() для типа аддона должен возвращать имя типа.

Пример:

add_custom_type("MyCustomNode", "Node2D", preload("my_custom_node.gd"), preload("icon.png"))

node.get_type() should return "MyCustomNode" instead of "Node2D"
  • Плагин может расширять другой плагин по своему типу

Пример:
Пользователь A создает плагин для более точного уведомления о видимости на основе Node2D.
add_custom_type("PreciseNotifier", "Node2D", preload("precise_notifier.gd"), preload("icon.png"))

Затем пользователь B разрабатывает надстройку Trigger на основе точного уведомителя в ДРУГОЙ папке надстроек с другой конфигурацией.
add_custom_type("Trigger", "PreciseNotifier", preload("trigger.gd"), preload("icon.png"))
и в сценарии trigger.gd он также должен расширить его с помощью имени типа
extends PreciseAddon
Конечно, пользователь должен добавить оба дополнения для использования триггера.

Похоже, что одна группа хочет определить пользовательский тип узла без сценария как производный от пользовательского типа узла, на котором он основан, в то время как другая группа хочет определить тот же самый сценарий как узел, который вернулся к своей базе в прошлом. Тип двигателя. Хотя я принадлежу к первой группе, я также вижу точку зрения последней группы в отношении того, что пользовательский тип все еще «заскриптован» и хочет четко указать это в пользовательском интерфейсе.

Тогда компромиссом может быть дополнительная кнопка пользовательского интерфейса в строке узла в доке дерева сцен рядом со значком его сценария со значком типа сценария ++. Щелкнув по нему, вы попадете в типичное всплывающее окно «Создать сценарий» со сценарием, который уже наследуется от пользовательского типа, например, extends Base или extends "Base" . Затем будет создан определенный сценарий, который немедленно заменит любой предустановленный сценарий. Таким образом, вы по-прежнему четко показываете, что на узле уже существует сценарий, но у вас также будет знакомый интерфейс для простой замены этого предустановленного сценария.

Это предложение, вероятно, будет менее интуитивным, поскольку оно по-прежнему обрабатывает пользовательские узлы несколько иначе, чем их аналоги в движке. Мысли?

Отдавая свои 2 цента позже в этом обсуждении, я думаю, что проблема заключается в том факте, что несколько узлов, наследующих аддон, по умолчанию используют один и тот же исходный скрипт; Я не возражаю против видимости кода, как утверждал Хуан в самом начале этой темы, это выбор дизайна, а также что-то важное, чтобы сделать поведение узла прозрачным для разработчика, использующего дополнение. Но обычно вы хотите изменить поведение различных узлов в коде, и сейчас единственный способ сделать это — удалить исходную ссылку на скрипт, создать новый скрипт и скопировать и вставить код базового скрипта. Вы даже не можете save as скрипт нового узла аддона в новый файл .gd, так как это изменит ссылку на все другие узлы, использующие исходный скрипт, так что это трехэтапное копирование-вставка процедурная фишка.

Опять же, это не НАСТОЛЬКО сложно, просто в этом конкретном случае опция save as в редакторе GDScript ведет себя не так, как я ожидал, и я думаю, что было бы удобнее иметь ' кнопку «копировать и сохранить» в редакторе GDScript, чтобы обеспечить быструю настройку надстроек (и с точки зрения архитектуры, сделать эту кнопку видимой имеет смысл, поскольку это хороший подход к созданию игр на Godot без необходимости использования унаследованных скриптов).

@henriquelalves Я думал, что настройка пользовательских узлов в коде - это в основном наследование? Например, extends "addons/thing/thing.gd" ? Унаследованный скрипт по-прежнему будет делать то же самое, что и версия дополнения, если вы знаете, что переопределяете. Нет необходимости копировать/вставлять.

@Zylann Вы правы, но обычно мне не нравится этот конкретный подход из-за видимости кода и особенностей автозавершения (по крайней мере, на 2.1, я еще не тестировал его). И большую часть времени я не хочу переопределять методы, а только изменять определенные вещи, которые не являются расширенными переменными в исходном скрипте дополнения. Вот что беспокоит меня в текущем поведении save as , я не могу быстро создать копию скрипта, не меняя каждую ссылку узла на такой скрипт; и решение этого с помощью удобного для пользовательского интерфейса способа решает первоначальную проблему наличия нескольких узлов для настройки, а также видимости кода и тому подобного (по крайней мере, в моем рабочем процессе я могу быть единственным, кто думает так, ха-ха).

@henriquelalves ну, я терпеть не могу копировать/вставлять tbh xD И вы также можете разветвить плагин с контролем версий и использовать его вместо этого.

@Zylann Разветвление плагина добавляет еще больше шагов к чему-то, что должно быть чертовски простым, хахаха. Думаю, я буду придерживаться ручного копирования и вставки, если это не является приоритетом, хотя я все еще считаю поведение save as странным.

@henriquelalves Копирование/вставка разветвления IS ^^, но если это сделать без контроля версий, в будущем это укусит вас в спину, если плагин будет обновлен.

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

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

@RodeoMcCabe Проблема в том, что под капотом пользовательские типы по-прежнему представляют собой сценарии, наложенные поверх узла внутри движка, и это нельзя изменить, поскольку узлы пользовательских типов не компилируются напрямую в исходный код движка. Что нам нужно сделать, так это сформулировать конкретный набор шагов/функций, которые позволили бы этим узлам со сценариями имитировать в глазах пользователя поведение узла в движке. Например...

  1. Добавление узла в окно "создать узел" (сделано)
  2. Разрешение пользователю предоставить текстовое «краткое описание» узла для окна «создать узел» (похоже, это еще не сделано? - по крайней мере, это не является частью add_custom_type ).
  3. Разрешение пользователю отображать иерархии узлов и определять абстрактные пользовательские типы в окне «создать узел». Это, вероятно, потребует добавления логического значения в функцию add_custom_type , независимо от того, является ли она абстрактным типом или нет. Мастер «Создать узел» необходимо обновить, чтобы соответствующим образом заблокировать создание абстрактных пользовательских типов.
  4. Пусть узел ВЫГЛЯДИТ так, как будто у него НЕТ сценария.

    а. У узла не должно быть значка «скрипт находится на этом узле» в доке «Сцена». Чтобы сделать вещи более прозрачными, возможно, вместо этого должен быть значок «это узел пользовательского типа». Нажав на нее, пользователь может перейти непосредственно к сценарию с добавлением пользовательского типа. Это нарушает «погружение», но это была бы необходимая жертва по соображениям удобства использования (очевидно, вы захотите посмотреть и увидеть, как работает узел пользовательского типа, если хотите).

    б. Свойство сценария, отображаемое в Инспекторе, по умолчанию должно быть пустым, если только пользователь не загрузит в него сценарий, и в этом случае сценарий должен быть производным от типа сценария, используемого в качестве пользовательского типа. Однако пользователь не должен знать местонахождение файла сценария пользовательского типа (концепция того, что это сценарий, должна быть скрыта от них). Это означает, что либо строковое имя пользовательского класса должно распознаваться синтаксическим анализатором GDScript, как и другие встроенные в движок классы (не знаю, насколько это будет просто/сложно), либо должна быть какая-то глобальная функция для извлечения записи через имя класса, например, для сценария в add_custom_type("MyClass", "Node", load(res://addons/git-repo/api/my_class.gd), load(res://addons/git-repo/icons/icon_myclass.svg) пользователь может создать сценарий с помощью extends MyClass или extends custom("MyClass") .

    в. Если пользователь действительно загружает на узел сценарий, производный от пользовательского типа, только тогда значок «у ​​этого узла есть сценарий» должен отображаться в доке «Сцена».

  5. Любой значок редактора, используемый для сценария, должен быть добавлен в блок категорий вместо значка в виде белого ящика, который в настоящее время используется для сценариев (в property_editor.cpp ). Это должно быть частью свойства __meta__ Dictionary для объекта узла пользовательского типа.

  6. Когда вы нажимаете «добавить скрипт» для пользовательского типа, он должен предварительно заполнить поле «Наследует» любым методом, используемым в 4b.
  7. Если вы удаляете сценарий, сценарий пользовательского типа все еще должен существовать под капотом, а НЕ удаляться. Узлы пользовательского типа могут эффективно использовать сценарий пользовательского типа в качестве сценария резервного копирования в тех случаях, когда для загруженного сценария задано значение null. Таким образом, вы по-прежнему должны видеть значок редактора базовых типов и свойства сценария в доках сцены редактора и доках инспектора. Я уже нахожусь в процессе слияния функции, чтобы заменить «Переменные сценария» фактическими именами сценариев, хотя, вероятно, потребуется обновить ее, если будут добавлены все эти изменения.
  8. Object::get_script() должен возвращать null для узлов со скриптом пользовательского типа и без загруженного скрипта.
  9. Object::get_property_list , а аналогичные функции для методов и сигналов должны включать содержимое скрипта пользовательского типа, даже если скрипт не загружен.
  10. Вероятно, потребуется вторая функция объекта C++, например Object::get_custom_script() или что-то в этом роде, чтобы движок мог видеть, есть ли сценарий, даже если сторона сценария не знает об этом втором сценарии.
  11. Попытки загрузить сценарий, не производный от пользовательского типа, в объект должны завершиться корректно и сообщить об ошибке (вероятно, логической ссылке &is_valid в связанной функции), чтобы подтвердить, разрешено ли объекту это делать. Связанные сценарии редактора Godot, которые должны предоставлять обратную связь по этой информации, также должны быть обновлены с учетом этого.

Это только царапает поверхность, но я думаю, что поведение, которое ищут пользователи, примерно такое экстенсивное (поэтому оно довольно интенсивное). Мы хотим сделать существование пользовательских типов доступными, если это необходимо (поэтому поместите значок пользовательского типа в доке Сцена в строке узла, чтобы просмотреть его сценарий), но мы также хотим максимально скрыть их существование, чтобы мы могли воспринимать их. их как внутридвигательные типы. Потребуется много работы, чтобы действительно сделать это правильно, так как это, вероятно, сломает вещи во МНОЖЕСТВЕ мест.

Что-нибудь из этого звучит хорошо? Я уверен, что есть еще вещи, которые мне не хватает, так как я просто немного обдумываю это. Если это звучит безумно, просто дайте мне знать. ;-)

Редактировать: ах, но предложение Редуза просто скрыть значок сценария в доке сцены, если сценарий соответствует сценарию пользовательского типа, также может быть ценным. Только метод get_script() все равно ничего не должен возвращать. Может быть, есть способ сделать это без необходимости создавать отдельное свойство сценария для самого объекта? Не знаю, потому что в кодовой базе уже есть много предположений для 1 скрипта на объект, которые, я думаю, мы хотим сохранить, но которые трудно поддерживать с концепцией скрипта custom_type .

Хорошие предложения, продуманные. Я думаю, что если бы все это было реализовано, это дало бы поведение, которое мы ищем, однако я думаю, что нарушение правила «1 скрипт на узел» может быть плохой новостью, которая может найти отклик во многих неожиданных частях кода. Отнеситесь к этому с долей скептицизма, так как я не очень хорошо знаю кодовую базу, а мой C++ посредственен. Reduz заявил выше, что они изначально пытались иметь более одного скрипта на узел, и «это было больше проблем, чем оно того стоило», что звучит разумно для меня.

Пункты с 1 по 4 великолепны, и я думаю, что их можно реализовать, не нарушая правило 1-скрипта. Абстрактные пользовательские типы, очевидно, не являются проблемой, поскольку вы все равно не можете создать их экземпляры, и поэтому пользователь не может добавить к ним скрипт в редакторе. Однако вполне возможно, что они могут попытаться сделать это с помощью кода, поэтому необходимо будет провести необходимые проверки и ошибки.

Пункт 6 тоже хорош, и здесь, я думаю, нам это сойдет с рук. Создание нового скрипта в соответствии с пунктом 6 изменит скрипт, в настоящее время прикрепленный к пользовательскому узлу, на новый (производный) скрипт. Старый (базовый) скрипт удаляется с ноды. Поскольку новый прикрепленный скрипт является производным от исходного, все функции сохраняются. Я часто так делаю, когда у меня есть базовый класс (абстрактный или нет) и прикрепляю производные скрипты к узлам. Разница здесь в том, что в новом скрипте может потребоваться указать extends "res://addons/path/to/base-script.gd" , а не просто имя пользовательского узла, потому что к пользовательскому типу больше не прикреплен этот скрипт... Хотя, если подумать, remove_custom_type() не вызывался, а прилагаемый скрипт все еще происходит от старого, так что, может быть, в этом нет необходимости? Прошу разъяснить мне этот момент.

Пункты 7, 8 и 9 хороши и, вероятно, не слишком сложны при соблюдении правила 1-скрипта. 10 не обязательно, если мы придерживаемся правила 1-скрипта. 11 хорош, так как именно так ведут себя встроенные узлы, если вы пытаетесь прикрепить к ним скрипт, не расширяющий тип узла.

В любом случае, это звучит как много работы, и мы уже находимся в бета-версии, поэтому я предполагаю, что это будет для 3.1 или даже 3.2 (недавно не смотрел дорожную карту).

@RodeoMcCabe Да, это определенно не для 3.0.

нарушение правила «1 скрипт на узел» может быть плохой новостью, которая может найти отклик во многих неожиданных частях кода.

Мои мысли точно. Я имею в виду общедоступный интерфейс, в котором Объект знает о своем пользовательском сценарии резервного копирования, но другие объекты не знают об этом и просто воспринимают Объект как имеющий один сценарий. Хитрость заключается в редактировании сеттера и геттера для свойства скрипта. Геттер должен проверять значение null. Если он равен нулю, он должен вместо этого вернуть сценарий резервного копирования. Установщик также должен перепроверить, что любой новый сценарий расширяет сценарий резервного копирования, иначе он каким-то образом должен сообщить об ошибке.

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

На стороне сценариев нет абстрактных типов (афаик). Вы хотите сказать, что знаете, как сделать сценарий абстрактным? can_instance метод подвергается воздействию со стороны сценариев, но все это делает чек ли сам скрипт является действительным и, если это инструмент, имеет ли ScriptServer включить поддержку сценариев в данный момент. Я не думаю, что это связано с абстрактностью типа сценария.

Вы должны иметь возможность проверить, является ли класс абстрактным, чтобы метод CreateDialog::_update_search знал, когда сделать текст серым или недоступным для выбора и т. д.

К вашему сведению, я создал вопрос об абстрактной части проблемы (# 13401).

Я думаю, что в целом это было бы выполнимо, если бы мы просто добавили частный член $# backup_script 0$#$ к типу Object , а затем использовали его для проверки свойств script .

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


Автоматическое наследование повлияет на сам плагин, и нельзя будет сделать больше одного без дублирования плагина, верно?

Мультискрипт был добавлен во второй раз в этом году, и его было легко снова убить (вызывает множество проблем).


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

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

Извините, вы говорите, что это как-то влияет на абстрактность или какое-то другое предложение, которое я сделал ранее? Я не вижу, где это связано...

Автоматическое наследование повлияет на сам плагин, и нельзя будет сделать больше одного без дублирования плагина, верно?

Вы пытаетесь сказать, что попытка дважды включить плагин в папку /addons/ и включить их оба в разделе «Плагины» в настройках проекта вызовет проблемы (я имею в виду, что это звучит довольно нормально, если ваш плагин добавляет пользовательский типы. Не может быть определено несколько настраиваемых типов с одним и тем же именем).

Не совсем уверен, что вы подразумеваете под «нельзя сделать больше одного без дублирования плагина». Вы можете создать несколько экземпляров узла пользовательского типа (?), так как он просто создаст узел и автоматически прикрепит определенный сценарий пользовательского типа. Мое предложение будет включать изменение процесса создания сценария CreateDialog, чтобы сделать...

  1. Пусть встроенный сценарий узла решает, может ли узел быть экземпляром или нет (абстрактный).
  2. Создайте встроенный тип узла.
  3. Установите пользовательский сценарий в качестве свойства узла backup_script (не подвергается воздействию API сценариев).
  4. Пусть собственный код узла Object справится с задачей обмана всех остальных, заставив их рассматривать backup_script как официальное свойство script объекта.
  5. Обновите метаданные для значка редактора
  6. Обновить метаданные для краткого описания(?)

...вместо...

  1. Создайте встроенный узел.
  2. Прикрепите сценарий пользовательского типа в качестве свойства script .
  3. обновить метаданные для пользовательского значка редактора.

Мультискрипт был добавлен во второй раз в этом году, и его было легко снова убить (вызывает множество проблем).

Я согласен. Я думаю, что мультискриптовые объекты были бы плохой идеей на данный момент (и в любом случае это даже не нужно). Это не то, что я предлагаю. В открытом доступе у Object по-прежнему должен быть только 1 скрипт, но я рекомендую иметь доступный резервный скрипт, который берет на себя роль скрипта (поэтому присваивает себе свойство script ) всякий раз, когда для основного свойства script установлено значение null / unloaded и т. д. Это позволит нам более эффективно поддерживать «пользовательские типы», не изменяя интерфейс кодовой базы по отношению к классу Object. Затем мы можем просто иметь специальный установщик/получатель для этого свойства сценария резервного копирования, который позволяет коду (например, коду, назначающему сценарии пользовательского типа в CreateDialog ) добавлять или удалять его существование. Таким образом, это добровольная модификация того, как работает свойство script , что приведет к сравнительно меньшему количеству необходимых изменений в движке.

Я думаю, что новая опция контекстного меню на узлах решит эту проблему. «Заменить скрипт на унаследованный», это может даже иметь подменю со всеми обнаруженными скриптами, которые наследуются от текущего, и «Новый скрипт» в конце, при нажатии на него новый скрипт диалог должен отображать путь к скрипту в поле «расширяет».

Разве не проще и прозрачнее просто добавить инструмент редактирования «Заменить и наследовать скрипт»? Я имею в виду, что это только проблема, потому что, когда мы создаем узел Addon, мы ожидаем, что он будет без скриптов, но узлы addon на самом деле просто настроенные узлы, которые вы добавляете в дерево Create Node, и пользователь должен это знать. С точки зрения UX (хотя я не эксперт), я не думаю, что мы должны жертвовать прозрачностью ради удобства.

@MarianoGnu @henriquelalves Ни то, ни другое на самом деле не одно и то же. Смысл «настраиваемого типа» заключается в том, что вы имитируете тип узла внутри движка. Это будет означать, что сам сценарий не может быть удален. Функциональность, которую он наполняет узел, встроена в него точно так же, как вы не сможете удалить Node2D-ness из Node2D, чтобы заставить его работать как чистый Node.

В популярном посте Akien, проголосованном выше, описаны аналогичные детали:

Дело в том, что скрипт, определяющий пользовательский узел, должен быть скрыт, потому что это свойство не экземпляра узла, а его типа. Таким образом, этот сценарий должен присваивать свойства пользовательскому узлу, но он должен быть таким же невидимым для пользователя экземпляра узла, как и классы встроенных узлов C++. Он будет предоставлять API, но его нельзя будет модифицировать, а только расширять.

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

На самом деле, для того, чтобы содержимое было доступно из gdscript_parser и другого содержимого сценариев, может быть лучше убедиться, что информация о пользовательском типе включена в синглтон ClassDB, а не просто EditorNode::get_editor_data().get_custom_types() , поскольку модули не будут иметь доступа к этой информации, но должны иметь к ней доступ.

у меня нет никаких личных проблем с раскрытием сценария, это действительно вопрос терминологии и философии, в соответствии с тем, что вы упомянули, что можно сделать, так это добавить новый класс в ClassDB и добавить свойство сценария в этот класс вместо узел и класс должны проверить, существует ли сценарий методов, и вызвать их перед вызовом родительского класса. Я верю, что это возможно, но обратная совместимость с предыдущими версиями будет нарушена, если это не будет сделано до RC1.

@willnationsdev Понятно , я просто не совсем согласен с тем, что пользовательские типы рассматриваются как узлы в движке. Для меня «мощность» Editor Addons заключается в том, насколько легко создать «пакет» пользовательских узлов и инструментов редактора и поделиться им через github; все эти инструменты и узлы на самом деле являются просто сценами и сценариями, которые вы можете копировать и вставлять, но Godot предоставляет API-интерфейс Addon для облегчения этого. Когда я впервые использовал его, я тоже ожидал, что аддоны будут без скриптов, но только потому, что сам движок выглядел так, как будто он рассматривает их как узлы внутри движка, и это проблема UX, а не архитектуры. Что, я думаю, исправит это:

  1. Пользовательские типы в дереве создания узлов отображаются другим цветом (четко указывая на то, что они являются пользовательскими типами).
  2. Когда вы добавляете их в свою сцену, значок сценария присутствует, но слегка прозрачен (указывает на то, что у них есть сценарий по умолчанию, но его можно заменить). Если щелкнуть значок сценария, в редакторе отобразится сценарий пользовательского типа.
  3. Кнопка «Добавление скрипта» в пользовательских типах автоматически заполнит параметр «Наследует» скриптом пользовательского типа.

Опять же, я определенно не эксперт, поэтому мое первоначальное предположение о Custom-Types != In-Engine Nodes может быть неверным; Кроме того, ваше решение является наиболее четким из тех, что я видел в этой ветке, поэтому я полностью понимаю, идет ли разработка Godot таким образом.

РЕДАКТИРОВАТЬ: я прочитал это снова, и мое «решение» - это в основном ваши первые 6 шагов решения, хахаха.

@henriquelalves @MarianoGnu Я думаю, что их сочетание, безусловно, необходимо. Наличие класса пользовательского типа, добавленного в ClassDB, является обязательным. Мне очень понравились все три предложения, которые у вас были, Энрике. Особенно идея 2 (намного лучше, чем мое отдельное предложение по иконке пользовательского типа). И я согласен с вами в том, что нам нужно убедиться, что «ЧТО такое пользовательские типы» остается в некоторой степени прозрачным. Я чувствую, что необходимо поддерживать баланс: люди должны быть в состоянии понять, является ли что-то пользовательским типом И что это значит, но мы также должны сделать все возможное, чтобы пользовательские типы ощущались как типы движка.

@willnationsdev Я взял это из assetslib, переместил код пользовательского узла в «базовый» скрипт:
Collision_path_2d-1-modified.zip

Непосредственная проблема, которую я вижу, заключается в том, что плагин загружает скрипт, и этот скрипт уникален, если я добавлю еще один пользовательский узел, он будет иметь _тот же_ скрипт.

Тогда что означает замена скрипта для плагина в этом случае? (может и ничего, я не уверен).

Если нет проблем с заменой скрипта (инструментальный режим может не подойти в некоторых случаях), в меню для пользовательских узлов можно добавить запись «расширить пользовательский скрипт», чтобы выполнить этот процесс замены.

@henriquelalves @eon-s Думаю, мы думаем об одном и том же. Я согласен с тем, что это проблема UX больше, чем что-либо еще. Пока что я неравнодушен к этому подходу, потому что я думаю, что он делает все максимально простым, что всегда хорошо, IMO.

Если нет проблем с заменой скрипта (инструментальный режим может не подойти в некоторых случаях), в меню для пользовательских узлов можно добавить запись «расширить пользовательский скрипт», чтобы выполнить этот процесс замены.

Я думаю, поскольку расширяющий скрипт должен наследоваться от скрипта плагина, его замена не будет проблемой. Но, опять же, я не очень хорошо знаком с кодовой базой. В любом случае, я дал свои 2 цента, я оставлю это настоящим разработчикам отсюда;)

@willnationsdev Извините, я запутался в абстрактных вещах. Мои «абстрактные» базовые классы просто имеют оператор печати в _init(), чтобы сообщить мне, создал ли я его случайно....

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

Что касается проблемы с отображением того, что это «пользовательский» узел, я предлагаю, чтобы узел имел запись в раскрывающемся списке Инспектора над «Показать в справке», которая показывает базовый файл сценария объекта. С точки зрения пользователя и разработчика, вы должны знать, что это пользовательский узел, просто используя его (кто еще добавил его в проект?), и цель «настраиваемых» узлов (IMO) состоит в том, чтобы позволить определять типы в GDScript, которые неразличимы. от встроенных типов двигателей.

@ Web-eWorks Я на самом деле работаю над этим (в данный момент), и я сделал приличную часть этого (хотя еще есть немного пути). Я настроил сценарий резервного копирования в классе Object и создал хранилище для пользовательских типов в ClassDB, а также обновил методы EditorPlugin для использования нового API. По-прежнему необходимо обновлять соответствующие функции ClassDB (такие вещи, как can_instance и get_parent_class и т. д.) и обновлять все материалы Editor/CreateDialog.

@willnationsdev , следует отметить, что регистрация в ClassDB также означает, что люди, создающие плагины, должны ставить префиксы своих классов, чтобы предотвратить коллизии ^^"

@Zylann Да, к сожалению, это было бы необходимо.

На данный момент я думаю, что разобрался с большинством привязок и основных изменений. Это всего лишь вопрос обновления классов Editor, CreateDialog и EditorHelp/DocData (и компилятора GDScript).

Как я уже упоминал ранее... Я все еще не думаю, что предложенное решение является хорошим.
Пожалуйста, проконсультируйтесь со мной, прежде чем пытаться сделать что-то, что, скорее всего, будет
отклоненный.

7 февраля 2018 г., 20:05, «Will Nations» [email protected] написал:

@Zylann https://github.com/zylann Да, это было бы необходимо,
к несчастью.

На данный момент я думаю, что разобрался с большинством привязок и ядра.
изменения. Это всего лишь вопрос обновления Editor, CreateDialog и
Классы EditorHelp / DocData теперь.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment-363893711 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z2xIYRcs0BFDqTyiFwqlhQWTqjEZtks5tSgIVgaJpZM4JejbZ
.

@reduz Ну, я пытался принять во внимание опасения, которые вы ранее изложили.

Я не внедряю какую-либо многоскриптовую систему. Я также не заставляю сценарии маскироваться под объекты ClassInfo в ClassDB. Все, что я сделал до сих пор, это прикрепил скрипт (через RefPtr) к иерархии наследования в ClassDB. Класс Object будет использовать «backup_script» только тогда, когда обычное назначение сценария пытается нарушить иерархию наследования сценария резервного копирования.

О, я понимаю .. тогда какой вариант использования для этого?

7 февраля 2018 г., 20:32, «Will Nations» [email protected] написал:

@reduz https://github.com/reduz Ну, я пытался понять
учитывать проблемы, которые вы ранее изложили.

Я не внедряю какую-либо многоскриптовую систему. я тоже не
заставляя сценарии маскироваться под объекты ClassInfo в ClassDB. Все, что у меня есть
сделано до сих пор, это прикрепить скрипт (через RefPtr) к наследству
иерархии в ClassDB. Класс Object будет использовать только
«backup_script» всякий раз, когда обычное задание сценария пытается нарушить
в иерархии наследования сценария резервного копирования.


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment-363900920 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z2yn68Iy6AgAKJHZ4qImH4UBAm5skks5tSghSgaJpZM4JejbZ
.

Вариант использования, когда кто-то хочет ввести новый тип узла в движок с помощью EditorPlugin, но они не хотят, чтобы скриптовый тип этого узла был редактируемым (они хотят, чтобы взаимодействие с ним имитировало тип в движке). ). Так, например, если я попытаюсь get_script() на узле только с его скриптом резервного копирования, он вернет ноль. Если я попытаюсь set_script(null) , сценарий будет настроен на сценарий резервного копирования. Если я попытаюсь set_script(a_script) , сценарий будет успешно назначен только в том случае, если новый сценарий находится в иерархии наследования сценария резервного копирования.

Для UX я планирую сделать так, чтобы значок сценария отображался в редакторе как прозрачный значок, если присутствует только сценарий резервного копирования. В этом случае кнопка для добавления скрипта по-прежнему будет кнопкой «добавить скрипт», а не кнопкой «удалить скрипт», и при нажатии на нее текст будет предварительно заполнен именем типа (хотя мне может потребоваться сделать он предварительно заполняет путь к сценарию, если пользователь меняет тип сценария с GDScript / VisualScript и т. д.). Как только будет назначен другой сценарий, прозрачный значок снова станет непрозрачным. Щелчок по значку скрипта в любом случае приведет вас к исходному коду активного скрипта (исходный код резервной копии скрипта, если другого скрипта нет).

Должен признаться, я не вижу в этом пользы, так что опять же, в чем
конкретные варианты использования, о которых вы думаете?

7 февраля 2018 г., 20:46, «Will Nations» [email protected] написал:

Вариант использования, когда кто-то хочет представить новый тип узла для
движок, использующий EditorPlugin, но они не хотят, чтобы скриптовый тип
этот узел можно редактировать (они хотят, чтобы взаимодействие с ним имитировало
тип двигателя). Так, например, если я попытаюсь получить_script() на узле
только со своим сценарием резервного копирования, он вернет null. Если я попытаюсь
set_script(null), тогда сценарий становится сценарием резервного копирования. Если я
попытаться установить set_script(a_script), тогда он только успешно назначит
сценарий, если новый сценарий находится в иерархии наследования резервной копии
сценарий.

Для UX я планирую сделать так, чтобы иконка скрипта отображалась в редакторе как
прозрачный значок, если присутствует только сценарий резервного копирования. В этом случае
кнопка для добавления скрипта по-прежнему будет кнопкой "добавить скрипт", а не
кнопка «удалить скрипт», и нажатие на нее заполнит текст именем
типа (хотя мне может потребоваться предварительно заполнить путь к сценарию
если пользователь меняет тип скрипта с GDScript/VisualScript,
и т.д.). Как только будет назначен другой сценарий, прозрачный значок
снова станет непрозрачным. Щелчок по значку скрипта в любом случае
приведет вас к исходному коду активного сценария (исходный код резервной копии, если
другого сценария нет).


Вы получаете это, потому что вас упомянули.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/godotengine/godot/issues/6067#issuecomment-363904934 ,
или заглушить тему
https://github.com/notifications/unsubscribe-auth/AF-Z26oeOMT4HpjzDsX8eygMSeUhbTgVks5tSgurgaJpZM4JejbZ
.

Вариант использования высокого уровня — позволить плагинам (EditorPlugins, системе PluginScript/SubModule) определять «защищенные от дурака» пользовательские типы узлов. В настоящее время система пользовательских типов представляет собой тонкую фабрику для «скрипта на узле», которая имеет множество проблем с рабочим процессом и удобством использования, если вы пытаетесь создавать новые типы узлов, наименьшая из которых — полная неспособность легко, интуитивно и эффективно расширить пользовательский узел кодом игрового процесса (в отличие от встроенных узлов). Более насущной проблемой является невозможность создания пользовательских узлов, которые наследуются от других пользовательских узлов.

Некоторые конкретные варианты использования заключаются в создании низкоуровневых узлов: возможно, реализация воксельной сетки GridMap на основе марширующих кубов для разрушаемой местности, аля Minecraft или любой другой системы местности, использующей марширующие кубы/двойное контурирование; настраиваемые типы элементов управления (контейнер кругового меню, реализованный во многих современных играх, любой тип элемента управления низкого уровня, который достаточно высок, чтобы выйти за рамки спецификации движка); и в любом месте, где вы хотите создать плагин, который регистрирует новые узлы. В каждом из этих случаев вы обычно хотите добавить собственный скрипт для обработки кода игрового процесса и отделить его от кода «реализации» (написанного на GDScript/GDNative).

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

Это улучшение не обязательно вызвано конкретной функцией (хотя их много), а скорее обоснованием того, что «текущий способ является отсталым, ненадежным и утомительным». В случае полной реализации эта система позволила бы чрезвычайно мощный метод расширения встроенных типов движка из GDScript/GDNative без необходимости перекомпилировать движок. С точки зрения дизайна это буквально прямое улучшение по сравнению с текущей системой.

Если после всего этого вам нужен конкретный вариант использования: вся экосистема плагинов/AssetLibrary .
Это то, что выиграет от этого изменения.

Итак, вот явный пример:

У меня есть плагин, над которым я работаю, godot-skills , который вводит множество новых типов узлов:

Эффект: модификация функтора
Targeter: аналогично, но просто находит и собирает другие узлы
Навык: объединяет иерархии целей и эффектов, чтобы найти узлы и применить эффекты ко всем им.
SkillUser: «владеет» несколькими навыками

Я бы хотел, чтобы каждый из этих узлов был вещами, которые пользователи могут создавать и добавлять непосредственно в сцену. Однако каждый из этих узлов предназначен для использования в качестве базового типа. У них не так много функций из коробки, и они должны иметь производный скрипт, размещенный на любом узле, с которым они в противном случае были бы связаны. Для таких случаев использования может быть полезно создать узел, из которого вы не хотите, чтобы пользователь мог удалить определенный набор функциональных возможностей сценария. Например, я хочу запретить пользователям размещать сценарий, не производный от Targeter, на любой узел Targeter, который они добавляют в свою сцену. Это связано с тем, что есть мгновенная функциональность, которую навыки получают от целевых объектов в качестве дочерних элементов (аналогично узлам Area2D и CollisionShape2D).

Текущий рабочий процесс требует, чтобы пользователи...

  1. Добавить узел пользовательского типа
    2а. Удалите прикрепленный к нему скрипт и нажмите кнопку добавления скрипта ИЛИ
    2б. Установите свойство скрипта на узле
  2. Найдите в диалоговом окне, где EditorPlugin хранит сценарий пользовательского типа, который вы хотите получить.

Эта система позволит вам заменить все это только:

  1. Добавьте пользовательский тип узла.
  2. Добавить как скрипт (который автоматически запускает производный скрипт пользовательского типа)

Пользователи смогут динамически добавлять свои собственные пользовательские типы, производные от типов в движке (для более простого использования). Это также повысит безопасность функциональности плагина, обеспечив наличие скриптовой функциональности на рассматриваемом узле. В настоящее время пользователи могут легко просто удалить скрипт и сломать что-то (что нежелательно). Кроме того, если кто-то хочет «очистить» скрипт на узле, то этот узел не должен терять свою функциональность пользовательского типа (это большая проблема). Он должен вернуться к сценарию пользовательского типа.

Итак, обновление. В настоящее время у меня есть следующее.

  • Теперь ClassDB может напрямую добавлять и удалять пользовательские типы.
  • В EditorPlugin есть аналогичные методы пользовательского типа, которые добавляют и удаляют не только пользовательский тип, но и другие данные, связанные с редактором, такие как значок. Эти методы связаны с новым сигналом, который выдается всякий раз, когда EditorPlugin переключается в активное/неактивное состояние.
  • GDScripts теперь могут правильно наследовать имена пользовательских типов напрямую от скрипта.
  • В доке сцены будет создан прозрачный значок сценария, когда текущий сценарий соответствует сценарию пользовательского типа узла. Щелкнув по нему, вы откроете скрипт пользовательского типа в редакторе. В этом состоянии отображается кнопка «добавить скрипт», а не «удалить скрипт». Если выбрано, эта кнопка предварительно заполнит поле «Наследует» именем скрипта пользовательского типа (если оно не пустое).
  • Любые попытки удалить сценарий приведут к повторному назначению сценария пользовательского типа в качестве значения сценария. Таким образом, чтобы удалить сценарий, вы должны сначала очистить значение сценария пользовательского типа с помощью set_custom_script(RefPtr()); .
  • Диалоговое окно создания узла правильно показывает отношения наследования и абстрактные узлы для пользовательских типов.
  • Файл project.godot заполняется всеми пользовательскими типами, чтобы исполняемые игры могли настроить их за main.cpp до запуска остальной части игры.

Осталось выполнить задания...

  • Разрешить пользователям создавать документы API классов для пользовательских типов.
  • Полностью исправьте отдельную ошибку, которая не позволяет пользователям редактировать поле Inherits в script_create_dialog.cpp .
  • Найдите способ динамически добавлять и удалять пользовательские типы из глобальной карты/массива GDScript *
  • Реализовать поддержку пользовательских типов в VisualScript

* На самом деле у меня есть несколько вопросов по этому поводу.

Во-первых, я вижу только add_global_constant / _add_global в файле gdscript_compiler.cpp без каких-либо средств удаления глобальных переменных из класса GDScriptLanguage. Он просто полностью повторно инициализируется всякий раз, когда добавляются или удаляются синглтоны? Как динамически редактируются глобальные идентификаторы? Это будет сделано для того, чтобы пользователи могли выполнять MyNode.static_func() без предварительной загрузки скрипта в переменную.

Пока я занимаюсь этим, я чувствую, что GDScript Godot мог бы значительно выиграть от введения пространства имен, особенно как часть этого изменения. Нам нужно было бы найти правильный разделитель, чтобы пометить их, но вы могли бы избавиться от проблем с конфликтами имен из типов движка и плагинов при создании собственных скриптов. Например, предполагая, что \ является предпочтительным разделителем...

extends Node # an in-engine type.
extends \ # triggers an error if left alone, but would prompt a dropdown of the plugin API names for auto-completion as well as the names of any scripts in the current project that are not part of a plugin.
extends \MyPlugin\ # triggers an error if left alone, but would prompt a dropdown of the scripted types that exist for the plugin
extends \MyNode # extends my_node.gd somewhere in res://
extends \MyPlugin\MyNode # extends my_node.gd somewhere in res://addons/[my_plugin_something?]/

func _ready():
    print(\MyNode.CONSTANT) # would print CONSTANT in the current project's my_node.gd script

В любом случае все это ^ просто идеи, которые в данный момент разбрасываются. Если бы было введено пространство имен, вы могли бы даже автоматически связывать сценарии с именем на основе их имени файла, используя сигнал resource_saved в EditorNode . Они НЕ обязательно должны быть пользовательскими типами (в том смысле, что вы не можете удалить скрипт из объекта), но они БУДЕТ доступны только по имени, что было бы чрезвычайно полезно и интуитивно понятно. Скрипты могут даже переопределить автоматическое именование по умолчанию ( filename.capitalize().replace(" ", "") ?), предоставив заголовок в пространстве имен вверху, например, \MyPlugin\mynode . Теперь внезапно "\MyPlugin\mynode" является глобальным идентификатором этого скрипта.

Вот фрагмент моего прогресса до сих пор.

@willnationsdev , пожалуйста, не выбирайте \ , он уже используется для экранирования и многострочного ввода. . , : или :: выглядят лучше.

@Zylann Я планировал использовать разделитель в качестве префикса для любых узлов, которые также добавляются в проект. Так что, например, сценарий в res://container.gd не захочет конфликтовать с реальным классом Container . Если бы мы использовали точку (.), то это могло бы выглядеть так.

var engine_type = Container.new()
var script_type = .Container.new()
var plugin_script_type = .MyPlugin.Container.new()

Кто-нибудь возражает против такого формата?

Редактировать:

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

func _virtual_method():
    ._virtual_method() # parent stuff
    # my stuff

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

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

Лично я бы предпочел, чтобы имя для «автозарегистрированного» сценария контролировалось объявлением в файле, т.е.

register <NAME> extends <PARENT>

или аналогичный синтаксис, основанный на текущем прологе extend <NAME> , который уже присутствует в GDScript.

РЕДАКТИРОВАТЬ: я также хотел спросить, как будет обрабатываться назначение сценариев, происходящих от родителя (скажем, Spatial), для пользовательского дочернего элемента (скажем, Sprite3D-> CustomType)? Это уже работает для типов движков, и ограничение назначенных скриптов производными от самого пользовательского типа не будет работать очень хорошо.

@Web-eWorks Достаточно честно. Я буду работать над совершенствованием текущего API, а затем открою новую проблему для пространства имен после отправки PR.

Если я хорошо понимаю, что означает @Web-eWorks, у меня все еще есть этот вопрос: как вы обойдете тот факт, что Object может иметь только один скрипт? Как они будут... "скриптовыми"? Потому что помимо этого регистрация в ClassDB — это не что иное, как глобальный идентификатор с точки зрения использования для программистов и дизайнеров.

@Zylann Что я сделал в своей вилке, так это создал свойство custom_script для Object . Это только когда - либо становится существенным , если значение присваивается, но если скрипт назначен к нему, то он служит в качестве резервного сценария / ограничения на то , что основной script свойство может быть. Очистка script установит его равным custom_script , и если вы назначите новый Script на script , он будет успешно назначен только в том случае, если Script Экземпляр custom_script . И всем этим за кулисами управляет Object , так что, что касается движка, у Object все еще есть только 1 реальный сценарий.

Содержимое ClassDB просто имеет вторичную карту настраиваемых типов, которые регистрируются, сопоставляя сам скрипт и некоторую другую информацию с именем, которое вы ему присвоили. Затем он предоставляет это имя и его иерархию наследования с соответствующими методами наследования ( get_class_list , is_parent_class , get_parent и т. д.). И это делается как флаг согласия для некоторых методов. get_class_list теперь будет принимать логический флаг, который включает только пользовательские типы, если вы передадите true в качестве второго параметра. Таким образом предотвращается нарушение совместимости с остальной частью движка.

@willnationsdev Итак, с системой, которая заставляет сценарии вести себя как встроенные классы, что мешает пользователям захотеть, чтобы все их сценарии и надстройки считались первоклассными гражданами, такими как встроенные классы, и отказались от «старой школы»?

Ах. В настоящее время сценарии работают так: вы можете поместить сценарий extends Node на VBoxContainer , и он все равно будет работать нормально. Если текущая реализация нарушит это поведение для пользовательских типов, возникнут некоторые проблемы — есть ли способ сохранить поведение пользовательского сценария при наличии «нормального» сценария, полученного непосредственно из типа движка?

@Zylann Именно поэтому я хочу ввести пространство имен, чтобы сценарии и сценарии плагинов можно было безопасно интегрировать с помощью имен, не сталкиваясь с типами в движке. Ничто не мешало бы людям использовать только новый путь и игнорировать старый. И я действительно думаю, что это было бы лучше в целом. Трудоемко каждый раз использовать явный путь.

@Web-eWorks Не уверен, что я слежу прямо здесь, но...

Пользовательские типы специально работают как типы в движке для наследования. Таким образом, если вы расширяетесь из пользовательского типа, это будет так, как если бы вы расширялись из совершенно другой ветви иерархии в движке. Бывший. если я создам собственный тип MyNode , который расширяет Node , я не смогу поместить этот скрипт в VBoxContainer так же, как я не могу поместить Node2D "на" VBoxContainer . На самом деле это было бы предполагаемым поведением.

Теперь, с концепцией пространства имен, в идеале вы должны иметь именованные сценарии, которые НЕ являются пользовательскими типами. Итак, в этом случае я мог бы создать скрипт, который расширяет Node и помещал его в Node , VBoxContainer И MyNode . Имеет ли это смысл?

Итак, я имею в виду, что если вы создаете собственный тип MyNode , наследующий, скажем, MeshInstance , экземпляр его узла в редакторе и пытаетесь добавить скрипт, наследующий Spatial к типизированному узлу MyNode , будет ли это работать правильно? Он правильно работает с узлами «двигателя», и это поведение нам нужно эмулировать.

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

@Web-eWorks Да, это сработает. Каждая запись «первого уровня» в карте ClassDB custom_types должна быть связана с типом в карте classes , в противном случае произойдет ошибка, и пользовательский тип не будет не создан. Это гарантирует, что цепочка наследования сохраняется при переходе от пользовательских типов обратно к типам движка вплоть до Object .

@willnationsdev Просто чтобы быть уверенным - если вы добавите метод _ready в MyNode и готовый узел в свой пользовательский скрипт, производный от Spatial, он будет вызываться как Spatial._ready(), MeshInstance._ready(), MyNode._ready(), script._ready() ? (Или любой другой порядок, в котором отправляется _ready .)

Я по-прежнему остаюсь при своем мнении, что это ненужно и сбивает с толку. Эту проблему нужно решать с помощью пользовательского интерфейса редактора, а не основного движка.

@reduz Это не то, что можно решить только с помощью пользовательского интерфейса редактора. Нет, если вы хотите контролировать действия пользователей над сценарием объекта. Мне кажется, что это функция, которую многие люди хотят установить.

Вы предлагаете, чтобы gdscript_compiler ссылался на содержимое в EditorData, чтобы установить новые глобальные идентификаторы для скриптовых типов, вместо того, чтобы отслеживать их все в ClassDB и позволять ClassDB управлять всей информацией о наследовании в движке?

Конечно, есть часть, которую нужно решить с точки зрения пользовательского интерфейса редактора, но есть и часть, которую нужно решить в ядре. Добавление пользовательских типов в движок, _фактических_ типов с точки зрения ClassDB, является очень мощной и крайне необходимой функцией. Что касается запутанности, я по-прежнему утверждаю, что она запутывает не больше, чем нынешняя система, а, возможно, и меньше.

@Web-eWorks Я согласен. На мой взгляд, отслеживание данных наследования в редакторе было бы гораздо более запутанным. И это привело бы к странным обходным путям, так как информация о типе больше не существовала бы, как только вы запустили игру без редактора (именно поэтому мне фактически пришлось начать загрузку информации в project.godot , чтобы она загружалась в ClassDB при запуске. Отсутствие EditorPlugins для добавления пользовательских типов приводило к сбою компиляции GDScripts при запуске сцены).

@Web-eWorks И, отвечая на ваш предыдущий вопрос, да, это работает правильно.

В моем тестовом сценарии у меня есть Node.gd , который расширяет GDSkillsEffect > GDSkillsAPI > Node , и методы _ready, реализованные в каждом скрипте, запускаются в правильном порядке. порядок: API, Эффект, Node.gd.

В настоящее время я испытываю какую-то ошибку бесконечного цикла, хотя всякий раз, когда два сценария пытаются наследовать от сценариев пользовательского типа по имени. Бывший. если Node.gd имеет extends GDSkillsEffect и gdskills_effect.gd имеет extends GDSkillsAPI , то попытка изменить и сохранить gdskills_effect.gd или gdskills_api.gd приведет к тому, что редактор бесконечно "загружается" и потом вылетает. Это, наверное, моя самая большая ошибка на данный момент.

@willnationsdev Ааа , нет, я говорю о Node.gd , расширяющем Node , _not_ GDSkillsEffect . Если _это_ работает, то все хорошо.

Например, вот представление дерева сцены:

-> User Script:
  extends Node
  func _ready(): pass

@Web-eWorks А, я понимаю, что вы имеете в виду. Что ж, это ДОЛЖНО работать, но я только что проверил это, и кажется, что моя логика в Object::set_script() по какой-то причине блокирует его, так что это ошибка, которую мне нужно исправить. XD Ошибка, потому что было обнаружено, что Node не создает сценарий пользовательского типа (что правда, да), но в этом случае ошибка не должна возникать, лол.

@Web-eWorks Как я уже упоминал, это НЕ то, что нужно менять в ClassDB.

Я действительно не думаю, что это нужно решать в ядре, поскольку каждый язык сценариев обрабатывает это по-разному. Вы можете легко создать новый класс в Mono, и он будет работать. Для GDScript это может быть простой случай представления файла сценария как предварительно загруженного глобального файла, и все. Универсальное решение, предложенное в PR, определенно не подходит.

@willnationsdev Я ценю ваши усилия, но это уже обсуждалось без конца, и все согласны с тем, что:

  • Добавление пользовательского типа отличается для каждого языка сценариев, поэтому взламывать что-то в ClassDB, которое на самом деле предназначено только для регистрации материалов C++, - это НЕ путь.
  • Прежде чем идти на такие длины, мы можем улучшить удобство использования пользовательского интерфейса для случаев, когда вы хотите расширить сценарий пользовательского узла, чтобы он просто создавал простое наследование.
  • В общем, добавление хаков к основному движку для обработки угловых случаев никогда не бывает правильным. Core должен оставаться свободным от взлома.

@willnationsdev Для справки, поведение движка заключается в том, что любой скрипт, расширяющий _что-то_ равное или предшествующее в иерархии наследования, может быть добавлен к узлу. Таким образом, скрипт, который extends Spatial , может быть добавлен к MeshInstance , но не к Control или Node , поскольку ни один из последних не является или не наследуется от Spatial . Это поведение, которое необходимо воспроизвести. (Никакого давления...)

@reduz Но добавляет ли создание нового класса в Mono его в иерархию наследования движка? Или диалоговое окно редактора Create Node? Возможно, необходимо открыть новый выпуск, в котором кратко изложено, какова цель этого, потому что я думаю, что мы говорим здесь о разных вещах.

Смысл модификаций ClassDB в том, чтобы позволить регистрировать новые типы узлов (или ресурсов) без необходимости перекомпилировать движок. Не добавлять новые настраиваемые глобальные переменные в GDScript, хотя это второстепенная проблема, над которой @willnationsdev работает в той же ветке.

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

@Web-eWorks Иерархия наследования движка предназначена для C++, а не для скриптов. Попытка смешать их обоих концептуально неверна с самого начала.

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

@Web-eWorks Если вы используете Mono, это будет выглядеть прозрачно, как если бы был добавлен новый класс, потому что это ЯВЛЯЕТСЯ новым классом. Для GDScript, поскольку классы являются файлами по умолчанию, это невозможно сделать таким образом, поэтому способ сделать это может состоять в том, чтобы добавить новый глобальный объект с выделенным пользовательским интерфейсом для его настройки (как у нас для одноэлементной автозагрузки)... но это все еще класс скрипта, а не класс движка.

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

@reduz Ну вот в чем дело. Node+Script — это сцена. Механизм add_custom_type должен вести себя как Mono, регистрируя «новый класс», даже если технически он не является «классом» по какому-либо стандарту. Если есть способ сделать это без необходимости прикасаться к ClassDB, я весь внимание.

Кроме того, @akien-mga предложил, чтобы в представлении дерева сцен мы могли скрыть сценарии пользовательских узлов, чтобы не сбивать пользователей с толку тем, что они создали этот сценарий и отредактировали его по ошибке (но все равно отображать его в инспекторе внизу) .

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

@willnationsdev Есть возражения против того, чтобы я открыл новую, более сжатую проблему, в которой подробно описывался объем реализации?

@Web-eWorks Node + Script — это не сцена, узлы в древовидной структуре — это сцена. Вы также можете добавлять пользовательские типы ресурсов, а не только узлы.

И нет, это не должно вести себя как регистрация нового класса. Должно быть совершенно ясно, что это класс C++ с предварительно добавленным сценарием.

@Web-eWorks Да, для меня это не проблема. Пожалуйста, упомяните меня, чтобы я получил пинг, если вы это сделаете. Не уверен, что нам нужна отдельная тема?

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

Моя точка зрения заключается в том, что вы расширяете типы движков скриптами, а не новыми типами движков. И что пользователь не глупый и должен это знать.

@reduz Что я имел в виду в отношении Node + Script, так это то, что этот механизм уже был охвачен созданием экземпляров сцен.

Может быть, я неправильно понимаю реализацию Mono, но разве классы Mono не являются скриптами?

@reduz Я в основном согласен с вами, даже когда вы сказали, что добавление скрипта в пользовательский узел просто предложит его наследовать. Но как вы ожидаете, что это будет работать с пользовательскими типами GDNative?

@reduz Одна из наших целей, однако, состоит в том, чтобы разрешить

Я, конечно, был бы открыт для альтернативного метода. В любом случае, самая простая реализация состоит в том, чтобы объект условно назначал сценарии самому себе (как я сделал в своей реализации), проверяя какой-либо сторонний регистрационный класс (будь то ClassDB, ProjectSettings, EditorData или кто знает что еще) . Затем самому редактору становится очень легко сверяться с этой третьей стороной и отображать вещи по-разному (также, как я сделал в своей реализации). Я не думаю, что это будет слишком много работы, чтобы переместить регистрацию в другой контекст, кроме ClassDB.

^ Будет ли этот компромисс приемлемым? В противном случае вам придется микроуправлять всеми возможными местами, в которых Object получает сценарий, назначенный для проверки с третьей стороной. Это было бы намного сложнее.

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

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

@Zylann Я думаю, что это то же самое, но либо а) скрипт является внутренним gdnative, б) он использует внутренний метод gdnative. Я недостаточно знаком с этим, чтобы честно сказать.

@reduz Вы хотите сказать, что не хотите разрешать какую-либо форму создания ограничений на основе сценариев для объектов? Потому что я мог бы переместить логику проверки в сам редактор, и это дало бы вам такую ​​же простую проверку ограничений во время разработки (изменения пользовательского интерфейса, как вы говорите), но они не будут работать во время выполнения, поэтому люди все равно потенциально будут в состоянии изменить вещи, как только игра запущена. Будет ли это работать лучше для вас? Я бы все же предпочел иметь возможность проверять вещи во время выполнения, хотя...

@willnationsdev , какие ограничения на основе сценариев вы хотите сделать?

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

Я бы рекомендовал скрыть значок сценария для расширенного сценария в иерархии сцен, и в случае, если кто-то нажмет кнопку «Добавить сценарий» вместо наследования базового класса узла, он наследует атакованный сценарий. Это прозрачно и вообще не нужно возиться с ClassDB.

Итак, просто просматривая некоторую информацию в script_language.h , я чувствую, что могу просто переместить новый контент, который я добавил в ClassDB , в класс ScriptLanguage и создать своего рода синглтон ScriptDB , который создается только в том случае, если язык сценариев намеревается его использовать. И поскольку object.cpp уже включает script_language.h , он сможет сделать следующее в set_script(const RefPtr &p_script)

  1. проверить, действительно ли p_script является скриптом
  2. проверить, к какому языку принадлежит p_script
  3. передать экземпляр p_script и Object экземпляра custom_script ScriptDB для связанного ScriptLanguage ,
  4. получить ответ от ScriptDB относительно того, можно ли назначить p_script на Object с учетом ограничений, налагаемых custom_script на Object экземпляр.

Это позволит нам сохранить как ClassDB в чистоте от любых модификаций, так и object.h чистоте (за исключением сеттера/геттера для custom_script ). Это также предотвращает ненужное усложнение языков сценариев, которым не нужно полагаться на Godot для определения идентификаторов и пространств имен (как это было бы необходимо GDScript и VisualScript, в отличие от C#, C++, Python и т. д.).

Затем все, что вам нужно сделать, это определить методы ScriptServer , которые могут выполнять проверки идентификатора/пространства имен на всех применимых языках сценариев, чтобы заменить экспериментальную функциональность ClassDB подтверждения пользовательских типов/наследования пользовательских типов. иерархии. Достаточно легко.

Звучит ли что-нибудь из этого как приемлемая альтернатива @reduz?

Просто добавлю сюда свой голос. Я только начал работать с Godot, и когда я прочитал об аддонах пользовательских типов, я действительно подумал, что буду создавать новый базовый тип многократного использования. Например, когда вы создаете KinematicBody , вы можете прикрепить к нему скрипт, но он все равно будет выполнять все свои KinematicBody вещи.

Это усиливается двумя способами:

  1. Их называют типами. Хотя языки различаются тем, является ли тип расширяемым (javascript: да, golang: нет, python: да), тип, который вы определяете, обычно является расширяемым, если расширяемы типы языка первого класса. В Godot типы первого класса расширяемы, а пользовательские типы — нет (по крайней мере, таким же образом). Называя их как-то иначе, например, сборные сцены или сцены-шаблоны, можно было бы пометить их как разные.
  1. Они отображаются в том же дереве, что и узлы первого класса в окне «Создать новый узел». Это означает, что вы используете их одинаково. Наличие отдельного раздела для «Префабы» или «Шаблоны» или как бы вы их ни называли, снова вызовет их как разные.

@JPTeasdale К сожалению, на данный момент большую озабоченность вызывает реализация, а не столько вопрос «как вы это определяете». А пользовательские типы Godot ЯВЛЯЮТСЯ расширяемыми (это просто скрипты). Просто в редакторе еще нет поддержки кода для отображения отношений наследования между пользовательскими типами в диалоговом окне создания узла, но данные доступны. Я также не вижу причин переименовывать их из «нестандартного типа» во что-то другое. «Сборный» и «шаблон» не совсем подходящие термины, и в первую очередь они не являются сценами (это просто сценарии, простые и понятные).

я думаю, что это намного проще, чем предполагаемое решение:

  1. Добавьте свойство, скрытое в инспекторе, в класс объекта с именем «custom_type_script» и назначьте пользовательский сценарий для выбора этого свойства вместо сценария.
  2. Когда вы пытаетесь расширить узел, проверьте, является ли это свойство нулевым, если нет, расширьте этот скрипт вместо класса узла (написав расширение «res:/path/to/script.gd»)
  3. Измените get_script_instance так, чтобы он указывал на экземпляр пользовательского скрипта, если скрипт узла имеет значение null.

@MarianoGnu Что ж, с точки зрения создания изменений редактора, это проще сделать, просто автоматически заполнив свойство script и предотвратив его нулевое значение в первую очередь (поскольку редактор всегда смотрит только на script , и нам не нужно находить каждый раз, когда оно ссылается на свойство script , и изменять этот код, если все, что мы делаем, это меняем значение script ) .

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

Можно было бы сделать комбинацию, вместо того, чтобы хранить невидимый скрипт в объекте, сохранить его в ScriptDB, добавить предварительный проход в программу чтения скриптов, чтобы заменить «extend CustomClassName» на «extend» res:/path/stored/inScriptDB.gd " в прекомпиляторе GdScript, потому что наличие скрипта вне наследования иерархии скрипта очень хлопотно (иерархия должна быть прямой линией снизу до корня, наличие ответвления приводит к путанице)

потому что наличие скрипта вне наследования иерархии скриптов очень проблематично (иерархия должна быть прямой линией снизу до корня, наличие ответвления приводит к путанице)

@MarianoGnu Может быть, здесь какое-то недоразумение. Как вы думаете, в какой момент не существует прямой линии наследования? Насколько я реализовал вещи, всегда существует очень четкая иерархия наследования между сценариями. Я ничего не изменил в том, как работают сами иерархии наследования. Я только что сделал возможным сопоставление данного скрипта с StringName и поиск его в какой-либо БД.

И если мы уже проделываем эту работу, нет причин заменять текст скрипта на путь в компиляторе GDScript. Все, что делает компилятор, — это присваивает экземпляр Script script переменной script. Загрузил ли я скрипт с помощью метода ResourceLoader::load(path) или извлек его с помощью Class/ScriptDB::get_script(namespace_and/or_identifier) , на самом деле не имеет значения.

Просто хотел упомянуть, что как новый пользователь Godot я создал плагин под впечатлением, что мой «Пользовательский узел» из плагина будет действовать как встроенный узел. То есть, когда я выбрал его из списка узлов, я ожидал, что получу узел без скрипта, но с уже встроенными функциями, которые я заскриптовал в плагине (как и для встроенных узлов). Просто подумал, что я должен высказать еще одно мнение от кого-то, кто только что пришел (хотя похоже, что обсуждение здесь уже было обширным).

Поскольку «пользовательский узел» — это просто сценарий, в чем разница между простым назначением сценария встроенному узлу? Что должна делать функция пользовательского узла?

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

В настоящее время пользовательский узел — это просто узел, созданный с уже прикрепленным скриптом, а значок и имя уже изменены.

@willnationsdev Как далеко вы продвинулись, внедрив эту функцию.
Я бы очень хотел, чтобы эта функция была здесь до 3.1, это будет действительно потрясающее дополнение.
Для плагина Godot и системы дополнений.

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

Я полностью реализовал новый бэкэнд для пользовательской системы типов. Новый позволяет пользователям выполнять все исходные функции, а также делает так, чтобы кнопка «Добавить скрипт» добавляла новый скрипт, который по умолчанию расширяет пользовательский скрипт (а не открывает пользовательский скрипт) и делает так, чтобы вы не можете удалить пользовательский скрипт (вам придется использовать команду редактора «Изменить тип», как и для любого другого типа движка).

@swarnimarun

Кроме того, это позволит пользователям регистрировать типы в глобальном масштабе, который обеспечивает доступ к сопоставлениям имени типа и пути к файлу. GDScript сможет воспринимать эти сопоставления как простые глобальные имена типов. Кроме того, типы не обязательно должны быть пользовательскими для регистрации, и типы могут быть либо сценариями, либо сценами.

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

@willnationsdev Не беспокойтесь, если это попадет в 3.1, я буду доволен.
И я понял, что это может быть довольно много работы.
Продолжайте хорошую работу.

В настоящее время в редакторе есть возможность легко расширить сценарий любого узла:

extend

Сам узел на данный момент выглядит так:

2

И главная забота @reduz заключалась в том, чтобы скрыть тот факт, что сценарий уже прикреплен. С одной стороны, имеет смысл не скрывать это от пользователя. Но, с другой стороны, к пользовательским узлам почти всегда будут прикреплены скрипты. Без скриптов пользовательские узлы — это просто существующие обычные узлы с пользовательскими значками и без добавления дополнительных функций.

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

Итак, вот мое простое решение: когда сценарий пользовательского узла совпадает со сценарием в определении пользовательского узла, значок сценария должен исчезать, например:

3

Значение:

4

Ребята, пример из жизни: если у меня много пользовательских заметок одного типа, каждый скрипт настроен для своего экземпляра, что произойдет, если я захочу изменить базовый пользовательский скрипт узла? Я говорю вам: САМОЕ ХУДШЕЕ, потому что мне нужно изменить его, И все остальные экземпляры соответственно, обеспечивая высокую вероятность человеческих ошибок и опечаток.

Вы не хотите скрывать скрипт? Хорошо, тогда покажите скрипт custom_node И скрипт ноды проекта.

@aaronfranke На мой взгляд, это лучшее решение. Даже инстансные сцены могут выиграть от этого.

@zatherz и др., теперь, когда реализован #30697, что еще можно улучшить? Есть ли что-нибудь еще в https://github.com/godotengine/godot/issues/6067#issuecomment -238250383, что важно иметь?

Закрытие исправлено #30697. Хотя исходное предложение в OP, разработанное в https://github.com/godotengine/godot/issues/6067#issuecomment -238250383, не было реализовано, этого не произойдет из-за опасений @reduz в https:// github.com/godotengine/godot/issues/6067#issuecomment -239060186.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги