godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
88.84k stars 20.15k forks source link

c# code When using Duplicate to copy a new class, the property value is null instead of the value before copying #95031

Closed ice-ko closed 2 weeks ago

ice-ko commented 1 month ago

Tested versions

Godot_v4.3-beta3_mono

System information

Godot v4.3.beta3.mono - Windows 10.0.22631 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3060 Laptop GPU (NVIDIA; 32.0.15.5599) - 12th Gen Intel(R) Core(TM) i7-12700H (20 Threads)

Issue description

复制前 复制后

Steps to reproduce

Use the Duplicate API to copy the new entity class

Minimal reproduction project (MRP)

No

ice-ko commented 1 month ago

I tested it in Godot_v4.3-rc1_mono_win64 and got the same result, just copying the new instance. Why does this happen?

raulsntos commented 1 month ago

Please upload a minimal reproduction project to make this easier to troubleshoot.

Although I imagine what's happening here is that those members aren't annotated with the [Export] attribute. Resource.Duplicate only copies exported properties.

ice-ko commented 1 month ago

@raulsntos Yes, I tested it and found that [Export] attribute is required to copy to the new instance, but is this not friendly enough? Because in many cases we assign internal values ​​without exporting the variable, so using Duplicate to copy becomes very unfriendly!

raulsntos commented 1 month ago

Godot only knows about the members you export, so it can't copy internal data. It's usually not a good idea to copy everything anyway.

If your only concern is you want to hide the exported members, that should be possible by removing the PROPERTY_USAGE_EDITOR flag. To do this you have multiple options:

Option 1: Use _ValidateProperty

This method allows you to customize members exported with the [Export] attribute by modifying their PropertyInfo (including their usage flags).

public override void _ValidateProperty(Godot.Collections.Dictionary property)
{
    if (property["name"].AsStringName() == PropertyName.MyInternalProperty)
    {
        var usage = property["usage"].As<PropertyUsageFlags>();

        // Remove PROPERTY_USAGE_EDITOR flag so it doesn't show in the inspector.
        usage &= ~PropertyUsageFlags.Editor;

        property["usage"] = (int)usage;
    }
}

Option 2: Use _GetPropertyList

This method allows you to export members without using the [Export] attribute. It's a more advanced way of exporting members and allows you to specify the usage flags.

public override Godot.Collections.Array<Godot.Collections.Dictionary> _GetPropertyList()
{
    var properties = new Godot.Collections.Array<Godot.Collections.Dictionary>();

    properties.Add(new Godot.Collections.Dictionary()
    {
        { "name", "MyInternalProperty" },
        { "type", (int)Variant.Type.Int },
        // Set the PROPERTY_USAGE_STORAGE flag, required for 'Duplicate' to copy the property.
        // We don't set the PROPERTY_USAGE_EDITOR flag, so it doesn't show in the inspector.
        { "usage", (int)PropertyUsageFlags.Storage },
    });

    return properties;
}

Don't forget to also override _Get and _Set. See their documentation for more information.

Option 3: Don't use Duplicate

Alternatively, you can always create your own method to duplicate your class instead of using the built-in Duplicate method if it doesn't fit your needs.

public MyResource CustomDuplicate()
{
    // Here we create a new instance of MyResource and set all the properties as needed.
    var copy = new MyResource()
    {
        MyPublicProperty = this.MyPublicProperty,
        MyInternalProperty = this.MyInternalProperty,
    };
    return copy;
}
ice-ko commented 1 month ago

@raulsntos Thank you. It solved my doubts and I know how to use it.

raulsntos commented 2 weeks ago

Closing as resolved.