Custom resource in C#

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By realManix

I created a custom resource with a C# script containing a class that extends Resource. I am able to create a resource file (.tres) using the file loading function in the engine (to load C# resource script), but the class name for the C# resource does not show up in “Create new Resource” dialog.
This is problematic because while I can create resources based on the script and save them as files, I can’t create one as a SubResource to a scene. As an example, I create a script to a Node that has an export variable to my custom C# resource, and after I compile and click on the field corresponding to the variable in the inspector, a long list of Resources show up that contains options like:

New Animated Texture
New Animation
New World


where my C# resource doesn’t show up. I have to “Load” my resource file to set the field but then it overrides the properties of the resource file that I loaded.
As a test, I created a GDScript resource and it showed up on the list as “New <class_name I specified for the resource>” and I was able to create a sub-resource for it in the scene.
For the time being I’m manually editing my scene file to replicate it like:

[ext_resource path="res://path/to/MyResource.cs" type="Script" id=<x>]
[sub_resource type="Resource" id=<y>]
script = ExtResource( <x> )
<Field1> = <Value1>
[node name=<MyNode> type="Node" parent="."]
script = ExtResource( <z> )
<ResourceField> = SubResource( <y> )
:bust_in_silhouette: Reply From: Paterno

Afaik, it’s still not implemented well for c#. It was one of the main reasons why I opted to go with GDScript on my first two games last year, when starting out.

Anyway, here’s the github link to the issue, which is still open:

:bust_in_silhouette: Reply From: aftamat4ik

Well. In Godot 3.5 it’s still not implemented, but there is … solutions.
First let’s overview the problem. If we make in c# class and make that class extend Resource then we can’t see it in export selection variants.
My resource class looks like this:

using Godot;
using System;
[Tool] // don't forget about this line of code!
public class LocomotionProperties : Resource {
    	public bool follow_surface_normal { get; set; } = false;

It’s just part of it from actual code.
And now if we want to use this resource as universal setting for nodes we need to do so:

public LocomotionProperties LocomotionData;

This will create resource field that will accept other resources as it’s input.
resource field
(images hosted on Imgur and one day they will just go brr - disappear, sorry)
But if we want to load our custom LocomotionProperties resource in there - we can’t find it in accepted list:
No such resource as we can see
So how do we solve this problem?
Just set this resource as Default!
Yes it’s possible, but needs to be preloaded.
We can do it in two steps.

  1. First need to create resource for preloading. Go to Godot’s File System Tab and by right click in there create new Resource, name it, in my case it will be - LocomotionProperties.tres and attach your c# resource script to the resource script field.
    Here how it looks like in FileSystem Tab:
    Godot's File System Tab
    and field to attach c# script to resource:
    resource with attached c# script
  2. Now we can use this resource file to preload it into our resource variable! Use ResourceLoader.Load to do so.
    In my case it looks like this:
public LocomotionProperties LocomotionData = ResourceLoader.Load<LocomotionProperties>("res://Characters/States/Core/LocomotionProperties.tres");

If we build solution now - in our Export resource field we will see this resource as preloaded:
Here how it looks like in my case:
Resource preloaded in field by defalut

As i underestand this is the only way of handling resources in godot c# on version 3.5
It’s still better than nothing. And also we can make another Export field of type PropertyHint.GlobalFile to get path to resource and use this path to preload it manually if needed.