Godot: 更好的自定义 EditorPlugin 节点

创建于 2016-08-07  ·  121评论  ·  资料来源: godotengine/godot

由 EditorPlugin 中的add_custom_type函数创建的节点在添加时具有分配给它的选定脚本。 这使得它们几乎毫无用处,因为您只能在其他节点中使用该脚本中定义的函数。

这与其他节点完全不同,并且使节点插件几乎没用/使用起来更烦人。

feature proposal plugin usability

最有用的评论

据我了解,期望有可能创建行为类似于内置节点的自定义节点,即它们应该具有以下特性:

  • 自定义图标,自定义标识符
  • 可通过添加节点小部件实例化(我猜是通过脚本,因此暴露于全局范围?)
  • 通过脚本编码自定义 API(例如RedNode2D将扩展Node2D ,并通过自定义脚本定义红色调制)
  • 然后这个自定义节点的行为应该像一个内置节点,即用户应该能够在根本没有脚本的情况下实例化它(自定义 API 不会直接暴露给用户,就像它被硬编码的内置脚本一样在 C++ 中),并附加一个脚本来扩展自定义节点(例如extends RedNode2D )。

这将是声明自定义节点时的“自然”期望,并且将是一个非常强大的功能; 我从上面了解到,到目前为止,它并没有以这种方式工作,部分原因是设计决策。 如果有一种方法可以实现我上面描述的功能,我很确定它会有很多应用程序。 资产库将充满自定义节点,这些节点开箱即用,可以像内置一样使用。

重新开放,直到就可以/应该做什么达成共识。

所有121条评论

我不明白你的意思

@reduz当您向场景添加由插件创建的类型的节点时,它已经附加了插件脚本。 所以不可能添加另一个具有自定义行为的脚本。

当然不是,这是核心设计,不会改变。

2016 年 8 月 7 日 18:11,“George Marques” [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。

我可能会误解一些东西,但如果您的自定义类型是脚本,
它怎么会不包含在创建的节点中? 这没有意义
与众不同

2016 年 8 月 7 日晚上 9:52,“George Marques” [email protected]写道:

这真是难过;这真是伤心。 但是您始终可以将脚本添加到父级或子级。

关闭为 wontfix。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/6067#issuecomment -238120392,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AF-Z27Zo4Ixvo4APSC4Fxf5ZqCJRsAxXks5qdn3igaJpZM4JejbZ
.

我想它需要每个节点有两个(或更多)脚本的能力,尽管这真的没有多大意义。

戈多本来支持这个,但麻烦多于好处

2016 年 8 月 7 日晚上 10:36,“George Marques” [email protected]写道:

我想它需要每个拥有两个(或更多)脚本的能力
节点,虽然这真的没有多大意义。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/6067#issuecomment -238123729,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AF-Z27jidVZl-hHWW3G_ESr8Cqj3eX7Eks5qdogRgaJpZM4JejbZ
.

问题是自定义节点几乎没用,因为它们并不是真正的“自定义节点”,它们只是具有预设脚本和不同图标的基本节点。
我对“自定义节点”的期望是我可以扩展节点以使用脚本中定义的任何内容。 示例场景:
我有一个名为 Test 的自定义节点,它是 Sprite 的子节点,并添加了一个返回truetest()函数。 然后,我想创建一个新的测试节点,为其分配一个脚本并使用test()函数。
这是不可能的。

我想回到现场继承+脚本来扩展组合。

好吧,作为具有预设脚本和不同图标的预设节点是 IMO
足够的自定义,但是如果您真的想将自己的自定义代码放入
在那里,您始终可以继承节点附带的那些。 我们可以
如果真的需要,可以从 UI 中简化一些。

2016 年 8 月 8 日星期一上午 10:40,Dominik Banaszak [email protected]
写道:

问题是自定义节点几乎没用,因为它们并不是真的
“自定义节点”,它们只是具有预设脚本和不同的基本节点
图标。 我想回到现场继承+脚本来扩展组合。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/6067#issuecomment -238240130,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AF-Z27eHoXwgr6buF6CCAHfxwx1dKkCiks5qdzHhgaJpZM4JejbZ
.

如果它可以通过 UI 自动化/变得更容易,我会很高兴。

据我了解,期望有可能创建行为类似于内置节点的自定义节点,即它们应该具有以下特性:

  • 自定义图标,自定义标识符
  • 可通过添加节点小部件实例化(我猜是通过脚本,因此暴露于全局范围?)
  • 通过脚本编码自定义 API(例如RedNode2D将扩展Node2D ,并通过自定义脚本定义红色调制)
  • 然后这个自定义节点的行为应该像一个内置节点,即用户应该能够在根本没有脚本的情况下实例化它(自定义 API 不会直接暴露给用户,就像它被硬编码的内置脚本一样在 C++ 中),并附加一个脚本来扩展自定义节点(例如extends RedNode2D )。

这将是声明自定义节点时的“自然”期望,并且将是一个非常强大的功能; 我从上面了解到,到目前为止,它并没有以这种方式工作,部分原因是设计决策。 如果有一种方法可以实现我上面描述的功能,我很确定它会有很多应用程序。 资产库将充满自定义节点,这些节点开箱即用,可以像内置一样使用。

重新开放,直到就可以/应该做什么达成共识。

+1
当我尝试将现有项目从“OtherEngine(tm)”移植到 Godot 时,这是我遇到的第一个主要障碍。 自定义类型,如上面解释的 @akien-mga,应该在注册后表现得与任何其他内置类型一样。

请说明您认为他们不相信的方式

2016 年 8 月 8 日上午 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 将内置类型注册为父节点时才会显示在这些树中。

例如,假设我想从一个基类型继承项目中的所有自定义类型。

基础.gd

extends Node
export (Color) var color

type_a.gd

extends base.gd

type_b.gd

extends base.gd

就像现在一样,我必须像这样注册这些类型。 在这种情况下,add_custom_type 的第二个参数必须是“节点”,否则它们不会出现在对话框中。

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

虽然能够像这样注册自定义类型很好,但对话框并没有像其他内置类型那样反映这些类型的继承性质。 对于任何内置类型,我都可以查看那棵树并一目了然地了解我正在处理的内容。 例如,我可以确定 SpriteNode2D,因此继承提供的所有功能。 对于自定义类型,情况并非如此。

现在,如果自定义类型可以完全注册到全局范围中,就像上面提到的@akien-mga,事情会更容易理解和使用。

首先,您可以从由类型名称而不是文件路径/名称引用的自定义类型继承。

基础.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

哦,我明白了.. 这两个问题肯定是可以修复的,连同
为脚本创建对话框添加继承支持

2016 年 8 月 8 日下午 1:54,“Ralf Hölzemer” [email protected]写道:

如前所述,目前_最大的缺点_是
自定义类型只不过是实例化基本类型的一种快速方法
脚本并将适当的脚本分配给该实例。 这
使得在编辑器中使用另一个脚本扩展该节点是不可能的 -
就像您可以使用内置类型一样。

但是也不可能用自定义节点来构建继承树
“创建新节点/资源”对话框,因为它们只显示在这些
当您使用内置类型将树注册为父级时
添加自定义类型。

例如,假设我想继承我的所有自定义类型
来自单一基本类型的项目。

_base.gd http://base.gd_

扩展节点
导出(颜色)var color

_type_a.gd http://type_a.gd_

扩展 base.gd

_type_b.gd http://type_b.gd_

扩展 base.gd

就像现在一样,我必须像这样注册这些类型。 在这个
在这种情况下,add_custom_type 的第二个参数必须是“节点”,否则
它们不会出现在对话框中。

函数 _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]
https://cloud.githubusercontent.com/assets/8785013/17486294/9bcc029c-5d90-11e6-81e6-6fce6b0e7cf0.png

虽然很高兴能够注册这样的自定义类型,但
对话框不反映这些类型的继承性质,例如
其他内置类型。 对于任何内置类型,我可以查看那棵树并
一目了然我在处理什么。 例如,我可以确定
Sprite _是一个_Node2D,因此继承了_provided的所有功能
by_Node2D。 对于自定义类型,情况并非如此。

现在,如果自定义类型可以完全注册到全局范围内,比如
@akien-mga https://github.com/akien-mga上面提到的,事情会
更容易理解和使用。

首先,您可以从 _type name_ 引用的自定义类型继承
而不是文件路径/名称。

_base.gd http://base.gd_

扩展节点
导出(颜色)var color

_type_a.gd http://type_a.gd_

扩展基地

_type_b.gd http://type_b.gd_

扩展基地

...然后,自定义类型的注册可以简化为
这。 请注意 add_custom_type 缺少的第二个参数。

函数 _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]
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
.

永远不会发生的是脚本从实例节点中消失,或者
被隐藏,需要明确节点是脚本化的,但是
向其中添加脚本仍然允许将脚本替换为
继承自它

2016 年 8 月 8 日下午 2:02,“Juan Linietsky” [email protected]写道:

哦,我明白了.. 这两个问题肯定是可以修复的,连同
为脚本创建对话框添加继承支持

2016 年 8 月 8 日下午 1:54,“Ralf Hölzemer” [email protected]写道:

如前所述,目前_最大的缺点_是
自定义类型只不过是实例化基本类型的一种快速方法
脚本并将适当的脚本分配给该实例。 这
使得在编辑器中使用另一个脚本扩展该节点是不可能的 -
就像您可以使用内置类型一样。

但是也不可能用自定义节点来构建继承树
“创建新节点/资源”对话框,因为它们只显示在这些
当您使用内置类型将树注册为父级时
添加自定义类型。

例如,假设我想继承我的所有自定义类型
来自单一基本类型的项目。

_base.gd http://base.gd_

扩展节点
导出(颜色)var color

_type_a.gd http://type_a.gd_

扩展 base.gd

_type_b.gd http://type_b.gd_

扩展 base.gd

就像现在一样,我必须像这样注册这些类型。 在这个
在这种情况下,add_custom_type 的第二个参数必须是“节点”,否则
它们不会出现在对话框中。

函数 _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]
https://cloud.githubusercontent.com/assets/8785013/17486294/9bcc029c-5d90-11e6-81e6-6fce6b0e7cf0.png

虽然很高兴能够注册这样的自定义类型,但
对话框不反映这些类型的继承性质,例如
其他内置类型。 对于任何内置类型,我可以查看那棵树并
一目了然我在处理什么。 例如,我可以确定
Sprite _是一个_Node2D,因此继承了_provided的所有功能
by_Node2D。 对于自定义类型,情况并非如此。

现在,如果自定义类型可以完全注册到全局范围内,
就像上面提到的@akien-mga https://github.com/akien-mga ,事情
会更容易理解和使用。

首先,您可以从 _type name_ 引用的自定义类型继承
而不是文件路径/名称。

_base.gd http://base.gd_

扩展节点
导出(颜色)var color

_type_a.gd http://type_a.gd_

扩展基地

_type_b.gd http://type_b.gd_

扩展基地

...然后,自定义类型的注册可以简化为
这。 请注意 add_custom_type 缺少的第二个参数。

函数 _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]
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
.

@reduz
是否有一些技术原因可以明确节点是脚本化的,如果是这样,它是否必须是一个被占用的脚本槽?

在我看来,指示自定义类型的一种相当笨拙的方式,我认为没有人会想到通过替换节点上的当前脚本,你会得到一个扩展已经存在的脚本的脚本。 这不是通过脚本扩展基本类型的方式。

如果用户清除该脚本字段会发生什么? 自定义类型是恢复到以前的脚本还是完全恢复到基本类型? 同样,我认为这不是一个好主意。

相反,它可以在节点树或属性编辑器中通过不同的颜色、一些图标或其他东西指示为自定义类型,而不会牺牲空的脚本槽。

永远不会发生的是脚本从实例化节点中消失或被隐藏,需要明确节点是脚本化的,但是向其添加脚本仍然允许替换从它继承的脚本

这里的要点是定义自定义节点的脚本_应该_被隐藏,因为它不是_instanced_节点的属性,而是它的类型。 所以这个脚本应该赋予自定义节点属性,但它应该对实例节点的用户不可见,就像内置节点的 C++ 类一样。 它将提供一个 API,但不能修改,只能扩展。 就像实例化 Sprite 时一样,您不会将scenes/2d/sprite.cpp附加为实例化节点的脚本,也不应将my_custom_node.gd附加为实例自定义节点的可修改脚本。

现在,我不知道它现在在技术上是否可能_,但这将是自然用例 AFAIU。 如果您修改自定义类型的脚本,它会修改类型本身,因此会影响此类型的所有实例。 但是使用自定义类型的实例节点应该有自己的脚本extends CustomNode

我认为该功能需要 Object 具有指向“基本自定义脚本类型”的附加指针,因为当您想从中删除用户脚本时需要该信息(实际上应该用基本脚本替换它)。
一旦你有了它,剩下的就是将它包含在所有情况下,因为它可能会引入许多副作用。 最后,只会附加一个脚本,只是处理它的方式不同。
一般来说,我不是继承的忠实粉丝,但这就是我的做法。

实际上,甚至不需要那个指针。 标记脚本就足够了,例如,如果您使用脚本 add_custom_type(),引擎可以在类上设置一个标志,以便信息可用,如“嘿,这个脚本类是引擎类型扩展”。 删除用户脚本然后将其替换为标记为“自定义类型”的第一个继承的脚本类,如果没有,则将其删除。

抱歉,如果节点有脚本,我反对隐藏脚本。 是什么
模拟一些不是的东西的点?

它有一个脚本的事实并不意味着该插槽很忙或它需要一个
第二个脚本,因为您可以简单地创建一个继承
现有的。

如果您同意,我们可以做的是在场景树中隐藏脚本图标,如果
分配的脚本是自定义类型的脚本,并制作脚本
创建对话框自动提供您在脚本创建时继承。
这够了吗?

2016 年 8 月 10 日晚上 11:01,“Marc” [email protected]写道:

实际上,甚至不需要那个指针。 标记脚本会
就足够了,例如,如果您使用脚本 add_custom_type(),引擎
可以在类上设置一个标志,以便信息可用,如“嘿,这个
脚本类是引擎类型扩展”。删除用户脚本会
然后用标记为“自定义”的第一个继承的脚本类替换它
type”,如果没有则删除。


你收到这个是因为你被提到了。

直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/6067#issuecomment -239055986,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AF-Z2xLGOhgMk__ZoRW1neRu1aRb5Qr_ks5qeoJogaJpZM4JejbZ
.

@reduz我认为这就足够了:微笑:

@reduz我同意,我没有说我们需要第二个脚本。 我只是想知道如果您添加一个继承第一个脚本(由插件定义的自定义)的脚本会发生什么,然后决定将其删除。 然后它会在没有任何脚本的情况下将节点恢复为基本引擎类型?

我想我们可以通过某种方式使它在这方面更加用户友好

2016 年 8 月 11 日 06:10,“Marc” [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 基于另一个配置文件夹上的精确通知程序开发一个触发器插件。
add_custom_type("Trigger", "PreciseNotifier", preload("trigger.gd"), preload("icon.png"))
在 trigger.gd 脚本上,他也应该使用类型名称对其进行扩展
extends PreciseAddon
当然,用户应该添加两个插件以使用触发器之一

似乎一个组想要在没有脚本的情况下定义一个自定义节点类型,因为它仍然从它所基于的自定义节点类型派生,而另一组想要将相同的场景定义为一个已经恢复到其基础的节点 -发动机类型。 虽然我属于前一组,但我也可以看到后一组关于自定义类型仍处于“脚本化”状态并希望在 UI 中明确这一点的观点。

折衷方案可能是在场景树停靠中的节点行上在其脚本图标旁边添加一个额外的 UI 按钮,并带有某种 script++ 图标。 单击它将通过典型的“创建脚本”弹出窗口,其中包含已经从自定义类型继承的脚本,例如extends Baseextends "Base" 。 然后将创建定义的脚本并立即替换任何预设脚本。 因此,您仍然会清楚地表明节点上已经存在一个脚本,但您也会有一个熟悉的界面来轻松替换该预设脚本。

这个提议可能不太直观,因为它仍然对待自定义节点与引擎内对应的节点有所不同。 想法?

在本次讨论的后面给出我的 2 美分方式,我认为问题在于默认情况下继承插件的多个节点共享相同的原始脚本。 我不介意代码可见性,正如 Juan 在该线程一开始所说的那样,是一种设计选择,也是使节点行为对使用插件的开发人员透明的重要因素。 但通常您希望更改代码中不同节点的行为,而现在唯一的方法是删除原始脚本引用,创建新脚本并复制粘贴基本脚本代码。 您甚至不能将新插件节点的脚本save as转换为新的 .gd 文件,因为这将使用原始脚本更改对所有其他节点的引用,所以有这个三步复制粘贴程序怪癖。

再说一遍,并不是那么复杂,只是在这种特殊情况下,GDScript 编辑器上的save as选项的行为不像我预期的那样,我认为拥有一个 ' 会更加 UI 友好GDScript 编辑器上的“复制和保存”按钮,以允许快速自定义插件(在架构方面,使该按钮可见是有意义的,因为这是在 Godot 上创建游戏的好方法,无需使用继承的脚本)。

@henriquelalves我认为在代码中自定义自定义节点基本上是继承? 比如, extends "addons/thing/thing.gd" ? 假设您知道要覆盖什么,继承的脚本仍将执行与插件版本相同的操作。 无需复制/粘贴。

@Zylann你是对的,但通常我不喜欢这种特殊的方法,因为代码可见性和自动完成的怪癖(至少在 2.1 上,我现在还没有测试过)。 而且大多数时候我不想覆盖方法,只更改原始插件脚本上不是扩展变量的特定内容。 这就是当前save as行为困扰我的地方,如果不更改对此类脚本的每个节点引用,我就无法快速创建脚本的副本; 并且以 UI 友好的方式解决这个问题解决了最初的问题,即需要自定义多个节点,加上代码可见性等(至少在我的工作流程中,我可能是唯一一个这样想的人,尽管哈哈)。

@henriquelalves好吧,我无法忍受复制/粘贴 tbh xD 而且您还可以使用版本控制来分叉插件并首先使用它。

@Zylann分叉一个插件正在为应该很简单的东西添加更多步骤,哈哈哈。 如果这不是优先事项,我想我会坚持手动复制粘贴,即使我仍然认为save as行为很奇怪。

@henriquelalves复制/粘贴是分叉^^,但如果没有版本控制,如果插件得到更新,它会在将来咬你。

将脚本附加到自定义节点的当前行为对于将自定义节点作为插件与简单地将脚本附加到节点相比几乎没有任何好处。 唯一的好处是它显示在节点对话框中,这根本不是一个好处。

我在说自定义节点的行为应该像一流的内置类型一样。 否则,为什么还要为它制作/使用插件呢? 只是为了让您可以在对话框中看到它而不必单击添加脚本按钮?

@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 MyClassextends custom("MyClass")制作脚本。

    C。 如果用户确实将自定义类型派生脚本加载到节点上,那么只有这样“此节点具有脚本”图标才会出现在场景停靠栏中。

  5. 脚本使用的任何编辑器图标都应添加到类别块中,而不是当前用于脚本的类似白框的图标(在property_editor.cpp中)。 这应该是自定义类型节点对象的__meta__ Dictionary 属性的一部分。

  6. 当您在自定义类型上单击“添加脚本”时,它应该使用 4b 中使用的任何方法预填充“继承”字段。
  7. 如果您删除脚本,自定义类型脚本应该仍然存在于引擎盖下并且不会被删除。 在加载的脚本设置为空的情况下,自定义类型节点将有效地使用自定义类型脚本作为备份脚本。 因此,您仍然应该在编辑器的场景停靠点和检查器停靠点中看到基本类型的编辑器图标和脚本属性。 我已经在合并一个功能以用实际脚本的名称替换“脚本变量”,但如果添加了所有这些更改,它可能需要更新。
  8. 对于具有自定义类型脚本且未加载脚本的节点, Object::get_script()应返回null
  9. Object::get_property_list ,方法和信号的类似函数应该包括自定义类型脚本的内容,即使没有加载脚本。
  10. 可能需要第二个 C++ 对象函数,例如Object::get_custom_script()或其他东西,以便引擎可以查看是否有脚本,即使脚本编写方不知道第二个脚本。
  11. 将非自定义类型派生脚本加载到对象上的尝试应该完全失败并报告错误(可能是关联函数中的&is_valid引用布尔值)以确认是否允许对象执行此操作。 必须提供有关此信息的反馈的相关 Godot 编辑器场景也需要更新以解决此问题。

这只是表面上的问题,但我认为用户正在寻找的那种行为是关于这种广泛的(所以它非常强烈)。 我们希望在必要时使自定义类型的存在可访问(因此在节点行的场景停靠栏中有自定义类型图标以查看其脚本),但我们也希望尽可能隐藏它们的存在以便我们可以感知它们作为引擎内类型。 真正做到正确需要做很多工作,因为它可能会在很多地方破坏东西。

这些听起来不错吗? 我确定还有更多我缺少的项目,因为我只是在思考一下。 如果这听起来很疯狂,请告诉我。 ;-)

编辑:啊,但是如果脚本与自定义类型脚本匹配,reduz 建议将脚本图标隐藏在场景码头中也可能很有价值。 只是, get_script()方法应该仍然不返回任何内容。 也许一种方法可以做到这一点,而不必在 Object 本身上创建单独的脚本属性? Idk,因为代码库中已经有很多假设,每个对象有 1 个脚本,我认为我们想保留这些假设,但使用custom_type脚本概念很难维护。

好建议,深思熟虑。 我认为如果所有这些都实现了,它会给出我们正在寻找的行为,但是我认为打破每个节点 1 个脚本规则可能是一个坏消息,它可能会在代码的许多意想不到的部分产生共鸣。 对此持保留态度,因为我不太了解代码库,而且我的 C++ 是平庸的。 Reduz 上面表示,他们最初尝试为每个节点设置多个脚本,并且“这样做更麻烦”,这在我看来是合理的。

第 1 点到第 4 点很棒,我认为可以在不违反 1-script 规则的情况下实现。 抽象自定义类型显然不是问题,因为无论如何您都无法实例化它们,因此用户无法在编辑器中向其添加脚本。 但是,可以想象他们可能会尝试通过代码来这样做,因此必须进行必要的检查和错误。

第 6 点也很好,这就是我认为我们可以摆脱这一点的地方。 根据第 6 点创建新脚本会将当前附加到自定义节点的脚本更改为新的(派生的)脚本。 旧的(基本)脚本将从节点中删除。 由于新附加的脚本是从原始脚本派生的,因此所有功能都保留了。 我经常这样做,我有一个基类(抽象或非抽象)并将派生脚本附加到节点。 这里的不同之处在于,新脚本可能必须说extends "res://addons/path/to/base-script.gd"而不仅仅是自定义节点的名称,因为自定义类型不再附加该脚本....虽然再三考虑, remove_custom_type()没有被调用,并且附加的脚本仍然来自旧的,所以也许这不是必需的? 请在这一点上为我澄清。

第 7、8 和 9 点很好,在保持 1-script 规则的同时可能不会太难。 如果我们保持 1-script 规则,则不需要 10。 11 很好,因为如果您尝试将不扩展节点类型的脚本附加到内置节点,这就是内置节点的行为方式。

无论如何,这听起来确实需要做很多工作,而且我们已经处于测试阶段,所以我想这将适用于 3.1 甚至 3.2(最近没有查看路线图)。

@RodeoMcCabe是的,这绝对不适用于 3.0。

打破每个节点 1 个脚本的规则可能是个坏消息,可能会在代码的许多意想不到的部分引起共鸣

我的想法完全正确。 我正在考虑一个公共接口,其中对象知道其自定义备份脚本,但其他对象不知道它,并且只是将对象视为具有单个脚本。 诀窍在于编辑脚本属性的 setter 和 getter。 getter 应该检查 null。 如果它为空,它应该返回备份脚本。 setter 同样应该仔细检查任何新脚本是否扩展了备份脚本,否则它应该以某种方式报告失败。

抽象自定义类型显然不是问题,因为无论如何您都无法实例化它们,因此用户无法在编辑器中向其添加脚本。

脚本方面没有抽象类型(afaik)。 您是说您知道如何制作脚本摘要吗? can_instance方法向脚本端公开,但它所做的只是检查脚本本身是否有效,如果是工具,则 ScriptServer 目前是否启用了脚本。 我不认为它与脚本类型的抽象性无关。

您需要能够检查该类是否是抽象的,以便CreateDialog::_update_search方法知道何时使文本变灰/不可选择等。

仅供参考,我创建了一个关于问题的抽象部分的问题(#13401)。

我认为总的来说,如果我们只在Object类型中添加一个backup_script私有成员,然后使用它来执行script属性检查,那将是可行的。

我在自定义节点上所做的是继承一个基本脚本,默认情况下使自定义节点脚本为空......


自动继承会影响插件本身,并且不复制插件就不可能制作多个,对吧?

今年第二次添加了 Multiscript,并且很容易再次将其杀死(引发多个问题)。


允许场景作为自定义节点而不是脚本的基础可以让根没有脚本。

我在自定义节点上所做的是继承一个基本脚本,默认情况下使自定义节点脚本为空......

抱歉,您是说这会以某种方式影响抽象性或我之前提出的其他建议吗? 我没有看到这在哪里连接......

自动继承会影响插件本身,并且不复制插件就不可能制作多个,对吧?

您是否想说尝试将插件包含到/addons/文件夹中两次并在项目设置的插件部分中启用它们会以某种方式导致问题(我的意思是,如果您的插件正在添加自定义,这听起来很正常类型。不能定义多个具有相同名称的自定义类型)。

不太清楚您所说的“如果不复制插件就无法制作多个”是什么意思。 您可以很好地创建自定义类型节点的多个实例(?),因为它只会创建节点并自动附加定义的自定义类型脚本。 我的建议将涉及更改 CreateDialog 的脚本创建过程以执行...

  1. 让节点的内置脚本决定节点是否可以实例化(抽象)。
  2. 创建内置节点类型。
  3. 将自定义脚本设置为节点的backup_script属性(不暴露给脚本 API)。
  4. 让节点自己的Object代码处理欺骗其他人将backup_script视为对象上的官方script属性的任务。
  5. 更新编辑器图标的元数据
  6. 更新简要描述的元数据(?)

...代替...

  1. 创建内置节点。
  2. 将自定义类型脚本附加为script属性。
  3. 更新自定义编辑器图标的元数据。

今年第二次添加了 Multiscript,并且很容易再次将其杀死(引发多个问题)。

我同意。 我认为在这一点上多脚本对象将是一个坏主意(而且甚至根本不需要)。 这不是我的建议。 在公众看来, Object应该仍然只有 1 个脚本,但我建议有一个可用的备份脚本来接管脚本的角色(因此将自己分配给script属性)每当主script属性设置为 null / unloaded 等时。这将使我们能够更有效地支持“自定义类型”,而无需更改代码库与 Object 类的接口。 然后,我们可以为此备份脚本属性提供一个专用的 setter/getter,它允许代码(例如在CreateDialog中分配自定义类型脚本的代码)添加或删除它的存在。 这样,它是对script属性如何工作的选择性修改,并将导致对引擎的必要更改相对较少。

我认为节点上的上下文菜单的新选项可以解决这个问题“替换继承的脚本”这甚至可以有一个子菜单,其中包含所有检测到的脚本,从当前继承,最后是“新脚本”,单击它时新脚本对话框应在“扩展”字段上显示脚本的路径。

仅仅添加一个“替换和继承脚本”编辑器工具不是更容易和更透明吗? 我的意思是,这只是一个问题,因为当我们创建一个插件节点时,我们希望它是无脚本的 - 但插件节点实际上只是您要添加到创建节点树的自定义节点,用户应该知道这一点。 从用户体验的角度来看(虽然我不是专家),我认为我们不应该为了方便而牺牲透明度。

@MarianoGnu @henriquelalves这些都不一样。 “自定义类型”的含义是您正在模拟引擎内节点类型。 这意味着脚本本身不能被删除。 它赋予节点的功能是内置的,就像您无法删除 Node2D 的 Node2D 特性以使其像纯节点一样工作。

上面 Akien 广受欢迎的帖子涵盖了类似的细节:

这里的要点是定义自定义节点的脚本应该被隐藏,因为它不是实例节点的属性,而是它的类型。 所以这个脚本应该赋予自定义节点属性,但它应该对实例节点的用户不可见,就像内置节点的 C++ 类一样。 它将提供一个 API,但不能修改,只能扩展。

如果我们确实创建了任何编辑器工具来促进“替换继承的脚本”的东西,那会很酷,但是任何功能都不应该能够看到自定义类型脚本处或之下的脚本层次结构,因为它应该模拟“内置”行为。

事实上,为了使内容可以从gdscript_parser和其他脚本内容访问,最好确保自定义类型信息包含在 ClassDB 单例中,而不仅仅是EditorNode::get_editor_data().get_custom_types() ,因为模块将无法访问该信息,但确实应该可以访问它。

我对暴露脚本没有任何个人问题,这实际上是一个术语和哲学问题,根据你提到的,可以做的是向 ClassDB 添加一个新类并向该类添加一个脚本属性而不是节点和类应该检查该方法脚本是否存在并在调用它的父类之前调用​​它们。 我相信这是可能的,但如果在 RC1 之前不这样做,会破坏与以前版本的向后兼容性

@willnationsdev我明白了,我只是不完全同意将自定义类型视为引擎内节点。 对我来说,Editor Addons 的“强大”在于创建自定义节点和编辑器工具的“包”并通过 github 共享它是多么容易; 实际上,所有这些工具和节点只是您可以复制粘贴的场景和脚本,但 Godot 提供了插件 API 来促进这一点。 当我第一次使用它时,我也希望插件是无脚本的,但这只是因为引擎本身看起来像是将它们视为引擎内节点,这是一个用户体验问题,而不是架构问题。 我认为可以纠正的是:

  1. 创建节点树中的自定义类型以不同的颜色显示(清楚地表明它们是自定义类型)。
  2. 当您将它们添加到场景中时,脚本图标就在那里,但略微透明(表明它们有一个默认脚本,但可以替换)。 单击脚本图标将在编辑器上显示自定义类型的脚本。
  3. 自定义类型上的“添加脚本”按钮将使用自定义类型脚本自动填充“继承”选项。

再说一遍,我绝对不是专家,所以我最初对 Custom-Types != In-Engine Nodes 的假设可能是错误的; 另外,您的解决方案是我在该线程上看到的最清晰的解决方案,因此我完全理解 Godot 开发是否采用这种方式。

编辑:我又读了一遍,我的“解决方案”基本上是你的前 6 个解决方案步骤,哈哈哈。

@henriquelalves @MarianoGnu我认为这些组合肯定是必要的。 将自定义类型类添加到 ClassDB 是必须的。 真的很喜欢你对恩里克的所有三个建议。 尤其是想法 2(比我单独的自定义类型图标建议要好得多)。 我同意你的观点,我们需要确保“自定义类型是什么”在某种程度上保持透明。 我觉得需要保持平衡:人们应该能够理解某个东西是否是自定义类型以及这意味着什么,但我们也应该尽可能地让自定义类型感觉像引擎类型。

@willnationsdev我从assetlib 中获取了这个,将自定义节点的代码移动到“基础”脚本中:
碰撞路径_2d-1-modified.zip

我看到的直接问题是插件加载脚本,并且该脚本是唯一的,如果我添加另一个自定义节点,它将具有_the same_ 脚本。

那么,在这种情况下,脚本替换对插件意味着什么? (也许什么都没有,我不确定)。

如果替换脚本没有问题(在某些情况下工具模式可能不满意),自定义节点的菜单可以添加“扩展自定义脚本”条目来执行替换过程。

@henriquelalves @eon-s 我认为我们在想同样的事情。 我同意这是一个用户体验问题,而不是任何事情。 到目前为止,我偏爱这种方法,因为我认为它使事情尽可能简单,这总是好的 IMO。

如果替换脚本没有问题(在某些情况下工具模式可能不满意),自定义节点的菜单可以添加“扩展自定义脚本”条目来执行替换过程。

我认为由于扩展脚本必须继承自插件脚本,因此替换它不会有问题。 但是,同样,我对代码库不是很熟悉。 无论如何,我已经给了我的 2 美分,我会把它留给真正的开发者;)

@willnationsdev对不起,我对抽象的东西搞混了。 我的“抽象”基类只是在 _init() 中有一个打印语句,告诉我是否意外实例化了它......

顺便说一句,自定义节点类型的行为绝对应该像引擎类型,包括设置自定义脚本、在 GDScript 中按名称引用和继承的能力,以及无法删除类型的基本脚本。 这模仿了引擎实现的节点的行为,提高了内部一致性和易用性。

关于显示它是“自定义”节点的问题,我建议该节点在“显示在帮助”上方的 Inspector 下拉列表中有一个条目,该条目显示对象的基本脚本文件。 从用户和开发人员的角度来看,您应该知道它是一个自定义节点,只需使用它(还有谁将它添加到项目中?),“自定义”节点(IMO)的目标是允许在 GDScript 中定义无法区分的类型从内置引擎类型。

@Web-eWorks 我实际上正在研究这个(此时此刻)并且我已经完成了相当大的一部分(尽管还有一些路要走)。 我在 Object 类中设置了一个备份脚本,并在 ClassDB 中为自定义类型创建了存储,同时更新了 EditorPlugin 方法以使用新的 API。 仍然需要更新相关的 ClassDB 函数(如can_instanceget_parent_class等)并更新所有 Editor / CreateDialog 的东西。

@willnationsdev应该注意的是,在 ClassDB 中注册也意味着制作插件的人都应该在他们的类前面加上前缀,以防止冲突^^"

@Zylann是的,不幸的是,这将是必要的。

在这一点上,我想我已经整理出了大部分绑定和核心更改。 现在只需更新 Editor、CreateDialog 和 EditorHelp / DocData 类(以及 GDScript 编译器)。

正如我之前提到的......我仍然认为建议的解决方案不是很好。
在尝试最有可能发生的事情之前,请与我确认
拒绝。

2018 年 2 月 7 日 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好吧,我一直在尝试考虑您之前提出的问题。

我没有实现任何类型的多脚本系统。 我也没有将脚本伪装成 ClassDB 中的 ClassInfo 对象。 到目前为止,我所做的只是将一个脚本(通过 RefPtr)附加到 ClassDB 中的继承层次结构中。 每当常规脚本分配试图侵犯备份脚本的继承层次结构时,Object 类才会使用“backup_script”。

哦,我明白了.. 那么这个用例是什么?

2018 年 2 月 7 日 20:32,“Will Nations” [email protected]写道:

@reduz https://github.com/reduz好吧,我一直在尝试接受
考虑您之前提出的问题。

我没有实现任何类型的多脚本系统。 我也不
使脚本伪装成 ClassDB 中的 ClassInfo 对象。 我所拥有的
到目前为止所做的是将脚本(通过 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() ,那么它将返回 null。 如果我尝试set_script(null) ,则脚本将设置为备份脚本。 如果我尝试set_script(a_script) ,那么只有在新脚本位于备份脚本的继承层次结构中时,它才会成功分配脚本。

对于 UX,如果只有备份脚本存在,我计划让脚本图标在编辑器中显示为透明图标。 在这种情况下,添加脚本的按钮仍将是“添加脚本”按钮而不是“删除脚本”按钮,单击它将使用类型名称预填充文本(尽管我可能需要使如果用户将脚本类型从 GDScript / VisualScript 等更改为其他类型,则它会预先填充脚本路径)。 一旦分配了不同的脚本,透明图标将再次变得不透明。 在任何一种情况下单击脚本图标都会将您带到活动脚本源代码(如果不存在其他脚本,则为备份脚本源代码)。

我不得不承认我并没有真正看到这样做的好处,所以又是什么
您正在考虑的具体用例?

2018 年 2 月 7 日 20:46,“Will Nations” [email protected]写道:

用例是当有人希望引入一种新类型的节点时
引擎使用 EditorPlugin,但他们不希望脚本类型
该节点是可编辑的(他们希望与它的交互模仿
发动机内型)。 因此,例如,如果我尝试在节点上 get_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 作为子节点获得即时功能(类似于 Area2D 和 CollisionShape2D 节点)。

当前的工作流程要求用户...

  1. 添加自定义类型节点
    2a。 删除附加到它的脚本,然后单击添加脚本按钮或
    2b。 在节点上设置脚本属性
  2. 在 EditorPlugin 保存您希望派生的自定义类型脚本的对话窗口中找到。

该系统将允许您将所有这些替换为:

  1. 添加自定义节点类型。
  2. 添加为脚本(自动开始派生自定义类型脚本)

用户将能够动态添加他们自己的自定义类型,这些类型派生引擎内类型(为了更简单的可用性)。 它还将通过强制在相关节点上存在脚本功能来增强插件功能的安全性。 目前,用户可以轻松地删除脚本并破坏内容(不可取)。 此外,如果有人想“清除”节点上的脚本,那么该节点不应该失去它的自定义类型功能(这是一个很大的功能)。 它应该恢复为具有自定义类型脚本。

好的,所以更新。 我目前有以下。

  • ClassDB现在可以直接添加和删除自定义类型。
  • EditorPlugin具有类似的自定义类型方法,不仅可以添加和删除自定义类型,还可以添加和删除其他与编辑器相关的数据,例如图标。 这些方法连接到一个新信号,该信号在EditorPlugin切换为活动/非活动时发出。
  • GDScripts 现在可以正确地直接从自定义类型的名称继承以从脚本继承。
  • 当当前脚本与节点的自定义类型脚本匹配时,场景停靠会创建一个透明的脚本图标。 单击它将在编辑器中打开自定义类型脚本。 在这种状态下,显示的是“添加脚本”按钮而不是“删除脚本”。 如果选中,此按钮将使用自定义类型脚本的名称(如果非空)预填充“继承”字段。
  • 任何删除脚本的尝试都会导致自定义类型脚本被重新分配为脚本值。 因此,为了删除脚本,您必须首先使用set_custom_script(RefPtr());清除自定义类型脚本值。
  • 创建节点对话框正确显示自定义类型的继承关系和抽象节点。
  • project.godot文件填充了所有自定义类型,以便执行的游戏可以在游戏的其余部分开始之前在main.cpp中设置它们。

剩下的任务要完成...

  • 允许用户为自定义类型生成类 API 文档。
  • 完全修复了一个单独的错误,该错误阻止用户编辑script_create_dialog.cpp中的继承字段。
  • 找到一种从 GDScript 全局映射/数组中动态添加和删除自定义类型的方法 *
  • 在 VisualScript 中实现自定义类型支持

*我实际上对此有几个问题。

首先,我只在gdscript_compiler.cpp文件中看到add_global_constant / _add_global ,而没有任何方法从 GDScriptLanguage 类中删除全局变量。 每当添加或删除单例时,它是否会完全重新初始化? 全局标识符如何动态编辑? 这样用户就可以执行MyNode.static_func()而无需先将脚本加载到变量中。

当我这样做的时候,我觉得 Godot 的 GDScript 可以从引入命名空间中受益匪浅,尤其是作为这种变化的一部分。 我们必须找到正确的分隔符来标记它们,但是您可以在制作自己的脚本时消除对引擎和插件类型的名称冲突的担忧。 例如,假设\是选择的分隔符...

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

无论如何,所有这些 ^ 只是目前被抛出的想法。 如果引入了命名空间,您甚至可以使用EditorNode中的resource_saved信号根据文件名自动将脚本与名称相关联。 它们不一定是自定义类型(从某种意义上说,您不能从对象中删除脚本),但它们可以仅通过名称访问,这将非常有用和直观。 脚本甚至可以通过在顶部提供命名空间的标题来覆盖默认的自动命名( 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

嗯...这对常量和/或变量有问题吗? 如果不是,那么我们可以假设括号意味着它是一个超级函数,否则它是一个可能的命名空间标识符。

老实说,我现在暂不考虑命名空间——它可能会在以后引入,我宁愿尽快使用当前系统。 我们还需要一个 _really_ 深思熟虑的自动名称注册系统,可能扩展到整个插件组织结构的改造,这是相当多的工作,超出了本问题的范围。

就个人而言,我宁愿“自动注册”脚本的名称由文件中的声明控​​制,即

register <NAME> extends <PARENT>

或基于 GDScript 已经存在的当前extend <NAME> prolog 的类似语法。

编辑:我还想问如何将源自父级(例如 Spatial)的脚本分配给自定义子级(例如 Sprite3D->CustomType)将被处理? 这已经适用于引擎类型,并且限制从自定义类型本身派生的分配脚本不会很好地工作。

@Web-eWorks 够公平的。 我将努力完善当前的 API,然后在提交 PR 后为命名空间打开一个新问题。

如果我很好理解@Web-eWorks 的含义,我自己仍然有这个问题:您将如何解决Object只能有一个脚本的事实? 他们将如何......“可编写脚本”? 因为除此之外,注册到 ClassDB 就程序员和设计人员的使用而言,只不过是一个全局标识符。

@Zylann我在 fork 中所做的是为Object创建一个custom_script属性。 只有给它分配了一个值,它才会变得重要,但是如果给分配了一个脚本,那么它就可以作为一个备份脚本/对主要script属性的约束。 清除script会将其设置为custom_script ,如果您将新的Script分配给script ,那么只有在Script实例派生自custom_script 。 而这一切都是由Object在幕后管理的,所以就引擎而言, Object仍然只有 1 个实际脚本。

ClassDB 内容只有一个自定义类型的辅助映射,这些映射已注册,将脚本本身和一些其他信息映射到您为其分配的名称。 然后,它使用相关的继承方法( get_class_listis_parent_classget_parent等)提供该名称及其继承层次结构。 这是作为某些方法的选择加入标志来完成的。 get_class_list现在将采用 bool 标志,如果您将true作为第二个参数传递,它只包含自定义类型。 这就是它如何防止破坏与引擎其余部分的兼容性。

@willnationsdev那么,有了一个使脚本表现得像内置类的系统,是什么阻止了用户希望他们的所有脚本和插件都像内置类一样被视为一等公民,并放弃“老派”的方式?

啊。 脚本当前的工作方式是您可以将extends Node的脚本放在VBoxContainer上,它仍然可以正常工作。 如果当前的实现破坏了自定义类型的行为,将会出现一些问题 - 有没有办法保持自定义脚本的行为,同时拥有直接从引擎类型派生的“正常”脚本?

@Zylann这实际上正是我想引入命名空间的原因,以便脚本和插件的脚本可以通过名称安全地集成,而不会与引擎内的类型发生冲突。 没有什么可以阻止人们只使用新方法而忽略旧方法。 我实际上认为总体上会更好。 每次都必须使用显式路径很费力。

@Web-eWorks 不确定我是否在这里关注,但是......

自定义类型专门用于继承的引擎内类型。 因此,如果您从自定义类型扩展,就好像您是从引擎内层次结构的一个完全不同的分支进行扩展一样。 前任。 如果我创建扩展Node的自定义类型MyNode $ ,我将无法将该脚本放在VBoxContainer上,就像我不能放置Node2D “上” VBoxContainer 。 这实际上是预期的行为。

现在,使用命名空间概念,理想情况下,您将命名非自定义类型的脚本。 因此,在这种情况下,我可以创建一个扩展Node的脚本并将其放在NodeVBoxContainerMyNode上。 那有意义吗?

所以我要说的是,如果你创建一个自定义类型MyNode继承说MeshInstance ,在编辑器中实例化它的一个节点,并尝试添加一个继承Spatial的脚本到MyNode类型的节点,它会正常工作吗? 它适用于“引擎”节点,这就是我们需要模拟的行为。

它应该是独立于命名空间的,因为这更多的是确保自定义类型的脚本被调用,而不管“用户级”脚本的继承层次结构中的点如何。

@Web-eWorks 是的,那行得通。 ClassDBcustom_types映射中的每个“第一级”条目都必须链接回classes映射中的类型,否则会发生错误并且自定义类型为没有创建。 这确保了在从自定义类型移回引擎类型一直到Object时保留继承链。

@willnationsdev只是为了确定-如果您在MyNode中添加一个_ready方法,并在您的用户脚本中从 Spatial 派生一个就绪节点,它将被称为Spatial._ready(), MeshInstance._ready(), MyNode._ready(), script._ready() ? (或发送任何订单_ready 。)

我仍然坚持我的观点,即这是不必要的和令人困惑的。 这个问题需要从编辑器UI解决,而不是核心引擎。

@reduz 虽然这不是仅通过编辑器 UI 就能解决的问题。 如果您想控制用户对对象上的脚本的操作,则不是。 在我看来,这是许多人想要设置的功能。

您是否建议 gdscript_compiler 应该引用 EditorData 中的内容以便为脚本类型设置新的全局标识符,而不是在 ClassDB 中跟踪它们并允许 ClassDB 管理引擎中的所有继承信息?

当然有一部分需要从编辑器 UI 的角度来解决,但也有一部分需要在核心解决。 将自定义类型添加到引擎中,从 ClassDB 的角度来看 _actual_ 类型,是一个非常强大且最终必要的功能。 至于令人困惑,我仍然认为它并不比当前系统更令人困惑,而且很可能更少。

@Web-eWorks 我同意。 在我看来,在编辑器中跟踪继承数据实际上会更加混乱。 这会导致奇怪的解决方法,因为一旦您在没有编辑器的情况下运行游戏,该类型信息将不再存在(这就是为什么我实际上必须开始在project.godot中加载信息才能将其加载到ClassDB on startup. No EditorPlugins添加自定义类型导致 GDScripts 在运行场景时编译失败)。

@Web-eWorks 并回答您之前的问题,是的,它可以正常工作。

在我的测试场景中,我有Node.gd ,它正在扩展GDSkillsEffect > GDSkillsAPI > Node ,并且每个脚本中实现的 _ready 方法都会被正确触发顺序:API、效果、Node.gd。

我目前遇到某种无限循环错误,尽管每当两个脚本都尝试按名称从自定义类型脚本继承时。 前任。 如果 Node.gd 有extends GDSkillsEffect并且gdskills_effect.gdextends GDSkillsAPI ,那么尝试修改和保存gdskills_effect.gdgdskills_api.gd将导致编辑器无限“加载”然后崩溃。 这可能是我目前最大的错误。

@willnationsdev啊,不,我说的是Node.gd扩展Node ,_not_ GDSkillsEffect 。 如果 _that_ 有效,那么一切都很好。

例如,这是场景树的表示:

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

@Web-eWorks 啊,我明白你的意思了。 好吧,它应该可以工作,但我刚刚对其进行了测试,似乎我在Object::set_script()逻辑由于某种原因阻止了它,所以这是我需要修复的错误。 XD 它失败了,因为它检测到Node没有派生自定义类型脚本(这是真的,是的),但在这种情况下它不应该失败,哈哈。

@Web-eWorks 正如我所提到的,这不是需要在 ClassDB 中更改的内容。

我真的不认为这需要在核心中解决,因为每种脚本语言处理它的方式不同。 您可以轻松地在 Mono 中创建一个新类,并且它会起作用。 对于 GDScript 来说,将脚本文件公开为预加载的全局文件可能是一个简单的情况,仅此而已。 PR 中提出的一刀切的解决方案绝对不是可行的方法。

@willnationsdev我很欣赏你的努力,但这已经讨论到了无止境,共识是:

  • 对于每种脚本语言,添加自定义类型都是不同的,因此在 ClassDB 中破解某些东西,这只是为了注册 C++ 的东西,不是要走的路。
  • 在谈到这些长度之前,我们可以在您想要扩展自定义节点的脚本的情况下改进 UI 可用性,因此它只是创建简单的继承。
  • 一般来说,向核心引擎添加 hack 以处理极端情况绝不是要走的路。 核心需要保持无黑客攻击。

@willnationsdev作为参考,引擎行为是任何在继承层次结构中扩展 _something_ 等于或先前的脚本都可以添加到节点中。 因此,可以将extends Spatial的脚本添加到MeshInstance但不能添加到ControlNode ,因为后者既不是也不是继承自Spatial 。 这是必须复制的行为。 (无压力...)

@reduz但是在 Mono 中创建一个新类是否会将其添加到引擎的继承层次结构中? 还是编辑器的创建节点对话框? 也许需要打开一个新问题,简明扼要地说明这样做的目的是什么,因为我认为我们在这里讨论的不是同一件事。

ClassDB 修改的重点是允许注册新类型的节点(或资源)而无需重新编译引擎。 不要在 GDScript 中添加新的自定义全局变量,尽管这是@willnationsdev在同一分支中处理的一个切题问题。

这样做的目的不是添加 hack,而是创建一个设计良好的 _system_ 来扩展引擎的类型。

@Web-eWorks 引擎继承层次结构适用于 C++,而不是脚本。 试图混合它们在概念上是错误的。

也许误解是创建节点对话框使它看起来像添加了一个新类,因此将此类自定义类型标记为明确包含脚本可能是个好主意。

@Web-eWorks 如果您使用的是 Mono,它看起来就像添加了一个新类一样透明,因为它是一个新类。 对于 GDScript,因为默认情况下类是文件,所以不能这样做,所以这样做的方法可能是添加一个具有专用 UI​​ 的新全局来设置它(就像我们为单例自动加载所做的那样)......但这仍然是一个脚本类,而不是一个引擎类。

@willnationsdev我的全部观点是,我认为让它看起来好像你正在向引擎添加实际的新类是错误的,因为它们不是。 它很容易混淆。 它只是一个带有脚本的节点,所以与其隐藏脚本,我认为正确的做法是让它在创建对话框中更加可见。

@reduz好吧,就是这样。 节点+脚本是一个场景。 add_custom_type 机制应该像 Mono 一样,注册一个“新类”,即使它在技术上不是任何标准的“类”。 如果有一种方法可以做到这一点而无需接触 ClassDB,我会全力以赴。

另外,@akien-mga 建议在场景树视图中,我们可以隐藏自定义节点的脚本,以避免用户混淆他们创建了该脚本并错误地编辑它(但仍然在底部的检查器中显示它) .

在这样的节点中,添加脚本将以继承模式打开创建对话框,因此它对用户工作流程仍然非常透明,但很明显它是自定义节点而不是引擎的一部分。

@willnationsdev对我提出一个新的、更简洁的问题有什么反对意见吗?

@Web-eWorks Node + Script 不是场景,树形布局中的节点是场景。 您还可以添加自定义资源类型,而不仅仅是节点。

不,它不应该像注册一个新课程一样。 需要非常清楚的是,它是一个预先添加了脚本的 C++ 类。

@Web-eWorks 是的,这对我来说没问题。 请提及我,如果您这样做,我会得到一个 ping。 不确定我们是否需要单独的问题?

从另一个角度来看它。 您的论点是让用户看起来好像您正在使用新引擎类型扩展引擎类型。

我的观点是您正在使用脚本扩展引擎类型,而不是使用新的引擎类型。 而且用户并不愚蠢,需要知道这一点。

@reduz我对 Node+Script 的意思是,该机制已经通过实例化场景涵盖。

也许我误解了 Mono 的实现,但 Mono 类不还是脚本吗?

@reduz我基本上同意你的看法,当你说向自定义节点添加脚本只会建议继承它时。 但是您希望这如何与 GDNative 自定义类型一起使用?

@reduz我们的目标之一是允许自定义类型脚本,即这些放置在 C++ 类上的预先分配的脚本,不可移除。 这是在编辑器上下文中无法完成的事情,因为您始终可以在运行时删除脚本(除非您想保留该功能并且只在编辑器中的设计时保护自定义类型脚本)

我当然愿意接受另一种方法。 无论哪种方式,最简单的实现是通过检查一些第三方注册类(无论是 ClassDB、ProjectSettings、EditorData 还是谁知道其他),让 Object 有条件地为自己分配脚本(正如我在实现中所做的那样) . 然后,编辑器本身就可以很容易地与第三方进行检查并以不同的方式显示内容(就像我在实现中所做的那样)。 不过,我认为将注册移到 ClassDB 之外的另一个上下文不会有太多的工作。

^ 这种妥协可以接受吗? 否则,您必须对每个可能的位置进行微观管理,在这些位置中,Object 获得了分配给第三方检查的脚本。 那会复杂得多。

到目前为止,我所做的实现非常清楚,哪些“类型”是脚本,哪些不是。 毕竟,在我分享的视频中,您仍然可以清楚地看到和访问与自定义类型相关的脚本。

@willnationsdev我了解您要做什么,但我认为这永远无法做到干净。 它隐藏了一个没有理由隐藏的复杂性,其方式永远不会完全按预期工作。

@Zylann我认为它是相同的,但是a)脚本是gdnative内部的b)它使用gdnative内部的方法。 老实说,我对它还不够熟悉。

@reduz您是说您不想允许对对象创建任何形式的基于脚本的约束吗? 因为我可以将检查逻辑移到编辑器本身中,这样您就可以轻松地在设计时检查约束(如您所说的 UI 更改),但它们在运行时不起作用,因此人们仍然可能会一旦游戏运行,就能够改变事物。 这对你会更好吗? 我仍然希望能够在运行时检查东西......

@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_scriptObject实例的custom_script ScriptDB给关联的ScriptLanguage
  4. 鉴于custom_scriptObject的隐含约束,从ScriptDB获得关于p_script是否可以分配给Object的响应Object实例。

这将允许我们保持ClassDB没有任何修改并保持object.h干净(除了custom_script的 setter/getter)。 它还可以防止不需要依赖 Godot 来定义标识符和命名空间的脚本语言不必要的复杂化(就像 GDScript 和 VisualScript 需要的,不像 C#、C++、Python 等)。

然后您需要做的就是定义ScriptServer方法,这些方法可以在所有适用的脚本语言中执行标识符/命名空间检查,以替换确认自定义类型/自定义类型继承的实验性ClassDB功能层次结构。 很容易。

这听起来像是一个可以接受的替代@reduz吗?

只是在这里添加我的声音。 我刚开始使用 Godot,当我读到自定义类型插件时,我确实认为我会创建一个新的可重用基础类型。 就像当你创建一个KinematicBody时你可以给它附加一个脚本,但它仍然会做所有KinematicBody的事情。

这通过两种方式得到加强:

  1. 它们被称为类型。 虽然语言在类型是否可扩展方面有所不同(javascript:是,golang:否,python:是),但如果第一类语言类型是可扩展的,那么您定义的类型通常是可扩展的。 在 Godot 中,第一类类型是可扩展的,但自定义类型不是(至少不是以相同的方式)。 将它们称为其他东西,例如预制场景或模板场景,将有助于将它们标记为不同的。
  1. 它们显示在与“创建新节点”窗口中的第一个类节点相同的树中。 这意味着您以相同的方式使用它们。 有一个单独的部分用于“预制件”或“模板”或任何你想称呼它们的东西都会再次将它们称为不同的。

@JPTeasdale不幸的是,此时更大的问题是实施,而不是“你如何定义这个”问题。 Godot 自定义类型是可扩展的(它们只是脚本)。 编辑器只是还没有代码支持在节点创建对话框中显示自定义类型之间的继承关系,但是数据是可用的。 我也没有真正看到将它们从“自定义类型”重命名为其他名称的理由。 “预制”和“模板”并不是真正合适的术语,它们也不是场景(它们只是脚本,简单明了)。

我认为它比预期的解决方案要简单得多:

  1. 将一个隐藏在检查器中的属性添加到名为“custom_type_script”的 Object 类中,并指定自定义脚本以选择此属性而不是 Script
  2. 当您尝试扩展节点时,检查此属性是否为空,如果不是,请扩展此脚本而不是节点类(编写扩展“res:/path/to/script.gd”)
  3. 如果节点脚本为空,则更改 get_script_instance 以指向自定义脚本的实例

@MarianoGnu好吧,就创建编辑器更改而言,只需自动填充script属性并首先防止它变为空(因为编辑器只查看script属性,并且我们不必每次都查找它引用script属性并修改该代码,如果我们所做的只是更改script的值) .

话虽如此,重做此过程的部分目标是允许脚本语言通常选择使用标识符而不是文件路径来定位脚本类。 这就是最初提出ScriptDB概念或ClassDB修改的唯一原因。 但现在我明白为什么 Juan 一直反对ClassDB的变化如此之大,因此建议将信息单独存储在脚本 API 中。 它自包含单个脚本语言中的所有信息。

可以进行组合,而不是将不可见的脚本存储在对象中,将其存储在 ScriptDB 中,向脚本阅读器添加预传递以将“扩展 CustomClassName”替换为“扩展”res:/path/stored/inScriptDB.gd " 在GdScript的预编译器中,因为让脚本脱离脚本继承层次很麻烦(层次必须是从下到根的直线,有分支会导致混乱)

因为让脚本脱离脚本继承层次很麻烦(层次必须是从下到根的直线,有分支会导致混乱)

@MarianoGnu也许这里有些误解。 你认为在什么时候没有直线继承? 就我实现的东西而言,脚本之间总是有一个非常清晰的继承层次结构。 我没有改变继承层次结构本身的工作方式。 我刚刚使将给定脚本映射到 StringName 并在某种数据库中查找它成为可能。

如果我们已经完成了这项工作,就没有理由将脚本文本替换回 GDScript 编译器中的路径。 编译器所做的只是将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 修复。 虽然在https://github.com/godotengine/godot/issues/6067#issuecomment -238250383 中详细阐述的 OP 中的原始提案尚未实施,但由于@reduzhttps:// 中的担忧,这不会发生

此页面是否有帮助?
0 / 5 - 0 等级