导出类似结构的对象

发布于 2025-01-22 23:30:34 字数 1324 浏览 0 评论 0原文

我一直对此感到震惊,似乎无法弄清楚。在另一个引擎中,我可以制作一个结构,然后制作一个结构的数组,然后可以在检查员中进行编辑。似乎没有办法在戈多特找到。

我想拥有一个资源,该资源在骰子上拥有多个面的起始价值和类型。例如,一侧可能有“ 2损伤”,而另一侧则具有“治愈3”。 (这是受 slice& dice )的首次Godot实验。但是,我观看的每个教程看起来都使它看起来好像,如果我想这样做,我必须为每种价值和类型组合制作一个全新的资源(损坏1资源,损害2资源等),

class_name DiceResource extends Resource

class DiceFaceData:
    export var BaseValue = 0
    export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")
    func _init():
        Type = 2
        BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

export(Array) var Faces = [DiceFaceData.new()]

我无法获得Dicefacedata出现在检查员的数组中,或在数组的对象类型列表中。扩展对象行不通。扩展节点意味着我必须实例化,我不想为仅编辑资源做。

我很难想象戈多没有类似的东西。我可以将检查器中的任何东西作为数据加载而不必实例化吗?另一个选项是创建两个阵列,一个带有int和另一个资源,但这似乎不方便地填写。还是我应该放弃资源,并使所有内容都附加到附加到节点的节点上?谢谢!

Godot版本3.4.3

编辑:如果您是来自团结或虚幻的人,那么您要寻找的就是资源。与其他引擎的ScriptableObjects或数据集相比,这并不是完整的答案。您会认为,由于这些游戏引擎处理它的方式,您只能在文件系统/内容浏览器中作为资产创建自定义或DA,,但您也可以将资源用作Instanced类。您可以在Inspector中使用文件系统中的新资源

export(Resource) var n = preload("res://MyResourceScript.gd").new()

,而是可以从列表新的myResourcescript 中选择并创建它。您不会引用外部制作的参考文件,您将在此处创建一个自定义文件。还要查看以下答案,以了解以酷炫方式使用资源的好提示。

I have been bashing my head about this and can't seem to figure it out. In another engine, I could make a struct, then make an array of that struct that I could then edit in the inspector. There seems to be no way of doing this that I can find in Godot.

I want to have a Resource that holds the starting Value and Type of multiple faces on a dice. For example, one side could have "2 Damage" while another has "Heal 3." (this is a first-time godot experiment inspired by Slice&Dice). Every tutorial I watch however makes it seem like, if I want to do so, I'd have to make a completely new Resource for each combination of Value and Type (Damage 1 Resource, Damage 2 Resource, etc.)

class_name DiceResource extends Resource

class DiceFaceData:
    export var BaseValue = 0
    export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")
    func _init():
        Type = 2
        BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

export(Array) var Faces = [DiceFaceData.new()]

I cannot get DiceFaceData to show up in the Inspector's array, or be on the list of object types for an array. Extending Object doesn't work. Extending Node means I have to instantiate it, which I don't want to do for an editor-only Resource.

I find it hard to imagine Godot doesn't have anything like this available. Is there anything I can load in the inspector as just data and not have to instantiate it? Another option is create two arrays, one with int and another Resource, but that seems inconvenient to fill out. Or should I just give up with Resources and make everything a Node attached to a Node attached to a Node? Thanks!

Godot version 3.4.3

EDIT: If you're someone coming from Unity or Unreal, what you're looking for is Resource. While compared to ScriptableObjects or DataAssets from those other engines, that's not the complete answer. You would think, because of the way those game engines handle it, you can only create custom SO or DA as assets in the filesystem/content browser, but you can also use Resources as instanced classes. Instead of creating a new Resource in the filesystem, you can use

export(Resource) var n = preload("res://MyResourceScript.gd").new()

In the inspector, you can choose from the list New MyResourceScript and create it. You won't be referencing an externally made Reference file, you'll be creating a custom one right there. And look at the below answer as well on good tips for using Resources in cool ways.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

从此见与不见 2025-01-29 23:30:34

首先,我想说我同情。自定义资源和检查员的运作不佳。关于工作有一个解决方案……但这并不意味着我们唯一能做的就是继续等待戈多特。


在您的代码上观察

您的代码,我想指出Dicefacedata不是资源类型。您可以这样写它:

class DiceFaceData extends Resource:
    export var BaseValue = 0
    export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")
    func _init():
        Type = 2
        BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

而且……什么都没有解决。

而且,顺便说一句,我提醒您可以将其放在自己的文件中:

class_name DiceFaceData
extends Resource:

export var BaseValue = 0
export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")
func _init():
    Type = 2
    BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

……这也不是解决方案。


我想指出的是,GDScript有类型。参见 gdscript中的静态键入。使用它们。为了说明……

这是一个具有`nnt值的变体

var BaseValue = 0

,这是int,明确键入:

var BaseValue:int = 0

这是int,隐含地键入推理:

var BaseValue := 0

如果您使用类型,Godot会告诉您这是一个错误:

BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

因为baseValueint,并且您为其设置了资源。


资源问题的数组

首先,

export(Array) var Faces = []

变体,恰好具有array值,并且将其导出为Array:让我们将其键入array

export(Array) var Faces := []

可悲的是,我们无法指定Godot 3.x中数组元素的类型(我们需要Godot 4.0为此功能)。但是,我们可以指定如何导出它。

因此,这是一个array作为array of resource> resource> resource

export(Array, Resource) var Faces := []

参见

​向上。现在,您遇到了相反的问题:显示所有资源类型。 ,这包括您的自定义资源类型,如果它在自己的文件中。

您猜想我们需要指定我们想要的资源类型:

export(Array, DiceFaceData) var Faces = []

如果它是建筑资源类型,那是正确的。但这是一个自定义的。 我们希望将其修复在将来的版本中。


通过插件来缓解问题,

以减轻拥有所有可能的资源类型的痛苦,请考虑使用Makovwait的插件“改进的资源选择器”。您可以在 github


无论如何

,我们可以做得更好。但是您将需要使脚本成为工具 脚本(您可以通过将tool放在脚本顶部来执行此操作,这意味着脚本中的代码可以并且将在编辑器上运行) 。

我们将用 setget ,在那里,我们将确保元素的类型是正确的:

export(Array, Resource) var Faces = [] setget set_faces

func set_faces(new_value:Array) -> void:
    Faces = []
    for element in new_value:
        element = element as DiceFaceData
        if element == null:
            element = DiceFaceData.new()

        Faces.append(element)

现在,在增加数组大小时,在检查员面板中,Godot将插入一个新的空元素到达数组,这使我们定义了运行的设置器,这将发现NULL并将其转换为自定义资源类型的新实例,因此您根本不必在Inspector面板中选择资源类型。


如您所知,这是一种“ hacky”解决方案

,这是不起作用的:

export(Array, DiceFaceData) var Faces = []

但是,我们可以用

另请参见

​有点儿。像这样的代码:

var Faces := []

func _get_property_list() -> Array:
    return [
        {
            name = "Faces",
            type = TYPE_ARRAY,
            hint = 24,
            hint_string = "17/17:DiceFaceData"
        }
    ]

它将显示在检查器上,作为数组,其中元素只能是您的自定义资源类型。

问题是它会导致一些错误垃圾邮件。您可能会或可能不同意。这是您的项目,因此取决于您。

我知道它看起来像是伏都教魔术,部分原因是我们正在使用一些无证件的东西。如果您想解释该2417/17:请参阅如何如何添加带有提示和hint_string的数组?


关于子资源

我观看的每个教程都使它看起来像是,如果我想这样做,我必须为每种价值和类型组合制作一个全新的资源(损害1资源,损害2资源等)< /p>

我不是确保您要使用“全新资源”,但是是的。资源是资源类型的实例。这些组合中的每一个都是资源。

也许“损害”,“治愈”等也是资源。让我们看看……我猜那是类型的目的:

export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")

戈多将显示其所知道的所有资源类型,这是一种痛苦。我将提出与上述方法不同的方法:制作String枚举。

export(String, "Damage", "Heal") var Type:String

这将显示为检查员面板上的下拉列表,并带有您指定的选项。

为什么字符串而不是int?啊,因为如果您愿意的话,您可以这样做:

var type_resource := load("Resources/DiceFaceTypes/" + Type + ".tres")

我假设这些代码实际上会损坏或治愈或其他


。 ,您必须来这里进行更新……还是您?借助工具脚本的功能,我们将更新该列表以反映实际存在的文件!

首先,我们不会使用导出,因此仅限:

var Type:String

现在我们可以从_GET> _GET_PROPERTY_LIST导出它。在那里我们可以查询文件。但是在我们这样做之前,因此我们清楚要做什么,以下代码等同于我们之前的导出:

func _get_property_list() -> Array:
    return [
        {
            name = "Type",
            type = TYPE_STRING,
            hint = PROPERTY_HINT_ENUM,
            hint_string = "Damage,Heal"
        }
    ]

no无证件的内容。

我们的任务是构建该hint_string带有文件的名称。看起来像这样:

const path := "res://"

func _get_property_list() -> Array:
    var hint_string := ""
    var directory := Directory.new()
    if OK != directory.open(path) or OK != directory.list_dir_begin(true):
        push_error("Unable to read path: " + path)
        return []

    var file_name := directory.get_next()
    while file_name != "":
        if not directory.current_is_dir() and file_name.get_extension() == "tres":
            if hint_string != "":
                hint_string += ","

            hint_string += file_name

        file_name = directory.get_next()

    directory.list_dir_end()
    return [
        {
            name = "Type",
            type = TYPE_STRING,
            hint = PROPERTY_HINT_ENUM,
            hint_string = hint_string
        }
    ]

ah,


是 此示例:

export(Resource) var n = preload("res://MyResourceScript.gd").new()

在这里,我们将变量n作为资源出现,它将出现在Inspector面板中。该变量当前是一个变体,我们可以键入资源

export(Resource) var n:Resource = preload("res://MyResourceScript.gd").new()

然后我们不需要告诉Godot将其导出为资源,因为它是Resource

export var n:Resource = preload("res://MyResourceScript.gd").new()

我们可以做的其他事情是preload进入const要清楚,preload在分析时解决。喜欢:

const MyResourceScript := preload("res://MyResourceScript.gd")

export var n:Resource = MyResourceScript.new()

这样,如果您需要在多个位置使用相同的脚本,则无需重复路径。


但是,您可能根本不需要路径。如果在脚本res://myresourcescript.gd中,我们添加class_name(在脚本的顶部):

class_name MyResourceScript

那么我们不需要使用preload完全。该名称可以到处可用,您可以使用它:

export var n:Resource = MyResourceScript.new()

该资源存储在哪里?

无处可去。上面,我们告诉Godot在我们初始化对象时创建一个新的(例如,可以是node或其他resource - 因为,是的 s可以具有资源 s),而这些仅在RAM中存在。

但是,如果您从检查员面板中修改资源,则Godot需要将这些更改存储在某个地方。现在,如果您正在编辑节点,则默认情况下它们转到场景文件。如果您正在编辑另一个资源,则可以在存储资源的任何地方。 要清楚,场景也是资源(packedScene)。而且,是的,这意味着文件可以具有多个资源 s(主要的重新出现和子资源)。您也可以告诉Godot存储Resource在检查员面板中自己的文件中。将文件提供给资源的优点在于在多个位置重复使用它(例如,多个场景)。

因此,资源可以存储在文件中,也可以完全存储。资源文件可以单独具有资源,也可以具有子资源。


我将花点时间提醒您,场景可以在其中包含其他场景的实例。 ”之间没有界限。


因此,场景与所谓的

“预制 /代码>。例如,这可能是一种节省球员进度的方法。您还可以使用加载resourceloader(实际上,loadresourceloceloader.load)的速记)加载它们。 。

实际上,如果您可以在某些方面使用loadpreload,则是资源。等一下,我们在上面做到了:

const MyResourceScript := preload("res://MyResourceScript.gd")

是的。 脚本资源。是的,您也可以在运行时创建这种资源。创建一个gdscript对象(gdscript.new()),设置其source_code,和reload it。然后,您可以将其连接到对象(例如A node),并带有set_script您现在可以开始考虑元编程或改装支持。

First of all, I want to say that I sympathize. Custom resources and the inspector do not work well. There is a solution on the work… However that does not mean that the only thing we can do is keep Waiting For Godot.


Observations on your code

About your code, I want to point out that DiceFaceData is not a resource type. You could write it like this:

class DiceFaceData extends Resource:
    export var BaseValue = 0
    export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")
    func _init():
        Type = 2
        BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

And… That solves nothing.

And, also, by the way, I remind you can put it on its own file:

class_name DiceFaceData
extends Resource:

export var BaseValue = 0
export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")
func _init():
    Type = 2
    BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

And… That is not the solution either.


Something else I want to point out is that GDScript has types. See Static typing in GDScript. Use them. To illustrate…

This is a Variant with an ìnt value

var BaseValue = 0

This is an int, typed explicitly:

var BaseValue:int = 0

And this is an int, typed implicitly with type inference:

var BaseValue := 0

And if you were using types Godot would tell you that this is an error:

BaseValue = preload("Resources/DiceFaceTypes/Damage.tres")

Because BaseValue is an int, and you setting a resource to it.


The Array of Resources problem

First of all, this is a Variant that happens to have an Array value, and it is exported as an Array:

export(Array) var Faces = []

Let us type it as an Array:

export(Array) var Faces := []

And sadly we cannot specify the type of the elements of the arrays in Godot 3.x (we need Godot 4.0 for that feature). However we can specify how we export it.

So, this is an Array exported as an Array of Resource:

export(Array, Resource) var Faces := []

See Exporting arrays.

Before you could not get your custom resource type to show up. And now you have the opposite problem: all the resource types show up. And this includes your custom resource type, if it in its own file.

You would guess that we need to specify the resource type we want:

export(Array, DiceFaceData) var Faces = []

And that would be correct if it were a build-in resource type. But it is a custom one. We are expecting this to be fixed in a future version. Meanwhile we will have to leave it with export(Array, Resource).


Mitigating the problem with an addon

To alleviate the pain of having all the possible resource types, consider using the addon "Improved resource picker" by MakovWait. You can find it on itch, or on github.


A proper solution

Anyway, we can do better. But you are going to need to make your script a tool script (you do that by putting tool on the top of the script, and it means that the code from the script can and will run on the editor).

We are going to define a setter with setget, and in there we are going to make sure the elements are of the correct type:

export(Array, Resource) var Faces = [] setget set_faces

func set_faces(new_value:Array) -> void:
    Faces = []
    for element in new_value:
        element = element as DiceFaceData
        if element == null:
            element = DiceFaceData.new()

        Faces.append(element)

Now, in the inspector panel when you increase the size of the array, Godot will insert a new null element to the array, which makes the setter we defined run, which will find that null and convert it to a new instance of your custom resource type, so you don't have to pick the resource type in the inspector panel at all.


A "hacky" solution

As you know, this does not work:

export(Array, DiceFaceData) var Faces = []

However, we can replace an export with _get_property_list. What happens is that Godot asks the object what properties it has to show up in the inspector panel. Godot does this by calling get_property_list And it will statically report the ones it found while parsing (the ones with export). However, Godot also defines a function _get_property_list where we can add more at run time.

See also Advanced exports.

Which begs the question, could we possibly make it work with _get_property_list? Kind of. The The code like this:

var Faces := []

func _get_property_list() -> Array:
    return [
        {
            name = "Faces",
            type = TYPE_ARRAY,
            hint = 24,
            hint_string = "17/17:DiceFaceData"
        }
    ]

It will show up on the inspector as an array where the elements can only be of your custom resource type.

The issue is that it causes some error spam. Which you might or might not be OK with. It is your project, so it is up to you.

I know it looks like voodoo magic in part because we are using some undocumented stuff. If you want an explanation of that 24 and that 17/17: see How to add Array with hint and hint_string?.


About the sub-resources

Every tutorial I watch however makes it seem like, if I want to do so, I'd have to make a completely new Resource for each combination of Value and Type (Damage 1 Resource, Damage 2 Resource, etc.)

I'm not sure what you are getting to with "a completely new Resource", but yes. A resource is an instance of a resource type. And each of those combination would be a resource.

Perhaps "Damage", "Heal" and so on are resources too. Let us see… I'm guessing that is what the Type is for:

export(Resource) var Type = preload("Resources/DiceFaceTypes/Damage.tres")

Godot would be showing all the resource types it is aware of, which is a pain. I'm going to suggest a different approach than those above for this: Make an String enumeration.

export(String, "Damage", "Heal") var Type:String

That will show up as a drop down list on the inspector panel, with the options you specified.

Why String and not int? Ah, because you can then do this if you so desire:

var type_resource := load("Resources/DiceFaceTypes/" + Type + ".tres")

I'm assuming that those have the code that actually does damage or heal or whatever.


Alright, but when you add a new type of dice face, you would have to come here and update it… Or do you? With the power of tool scripts we are going to update that list to reflect the files that actually exist!

First of all, we are not going to use export, so it will be just:

var Type:String

And now we can export it from _get_property_list. There we can query the files. But before we do that, so we are clear what we have to do, the following code is equivalent to the export we had before:

func _get_property_list() -> Array:
    return [
        {
            name = "Type",
            type = TYPE_STRING,
            hint = PROPERTY_HINT_ENUM,
            hint_string = "Damage,Heal"
        }
    ]

No undocumented stuff here.

Our task is to build that hint_string with the names of the files. And that looks like this:

const path := "res://"

func _get_property_list() -> Array:
    var hint_string := ""
    var directory := Directory.new()
    if OK != directory.open(path) or OK != directory.list_dir_begin(true):
        push_error("Unable to read path: " + path)
        return []

    var file_name := directory.get_next()
    while file_name != "":
        if not directory.current_is_dir() and file_name.get_extension() == "tres":
            if hint_string != "":
                hint_string += ","

            hint_string += file_name

        file_name = directory.get_next()

    directory.list_dir_end()
    return [
        {
            name = "Type",
            type = TYPE_STRING,
            hint = PROPERTY_HINT_ENUM,
            hint_string = hint_string
        }
    ]

Ah, yes, set the path constant to the path of the folder where the resources types you have are.


Addendum post edit

I want to elaborate on this example:

export(Resource) var n = preload("res://MyResourceScript.gd").new()

Here we are exporting a variable n as a Resource, which will appear in the Inspector panel. The variable is currently a Variant, we could type it Resource:

export(Resource) var n:Resource = preload("res://MyResourceScript.gd").new()

And then we don't need to tell Godot to export it as a Resource, because it is a Resource:

export var n:Resource = preload("res://MyResourceScript.gd").new()

Something else we can do is preload into a const. To be clear, preloads are resolved at parse time. Like this:

const MyResourceScript := preload("res://MyResourceScript.gd")

export var n:Resource = MyResourceScript.new()

This way, if you need to use the same script in multiple places, you don't need to repeat the path.


However, you might not need the path at all. If in the script res://MyResourceScript.gd we add a class_name (at the top of the script):

class_name MyResourceScript

Then we don't need to use preload at all. That name will be available everywhere, and you can just use it:

export var n:Resource = MyResourceScript.new()

Where is that resource stored?

Potentially nowhere. Above we are telling Godot to create a new one when our it initializes our object (e.g. which could be a Node, or another Resource - because, yes, Resources can have Resources) and those would only exist in RAM.

However, if you modify the Resource from the Inspector panel, Godot needs to store those changes somewhere. Now, if you are editing a Node, by default they go to the scene file. If you are editing another Resource, then it goes to wherever that Resource is stored. To be clear, scenes are resources too (PackedScene). And, yes, that means a file can have multiple Resources (A main resurce and sub-resources). You could also tell Godot to store the Resource in its own file from the Inspector panel. The advantage of giving a file to a Resource is in reusing it in multiple places (multiple scenes, for example).

So, a Resource could be stored in a file, or not stored at all. And a resource file could have a Resource alone, or it could also have sub-resources as well.


I'll take a moment to remind you that scenes can have instances of other scenes inside. So, there is no line between scenes and the so called "prefabs" in Godot.


… Did you know?

You can save the resources you created in runtime, using ResourceSaver. Which could be a way to save player progress, for example. You can also load them using load or ResourceLoader (in fact, load is a shorthand for ResourceLoader.load).

In fact, if you can use load or preload on something, it is a Resource. Wait a minute, we did this above:

const MyResourceScript := preload("res://MyResourceScript.gd")

Yep. The Script is a Resource. And yes, you can create that kind of resources in runtime too. Create a GDScript object (GDScript.new()), set its source_code, and reload it. Then you can attach it to an Object (e.g. a Node) with set_script. You can now start thinking of meta-programming, or modding support.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文