Custom resources in Godot Mono

Godot Version

v4.4.1.stable.mono.official [49a5bc7b6]

Question

I’m trying to create custom config file where I can declare difficulty options, and then use them later, so I don’t have to hardcode them. Unluckily for me, this topic is not well documented for Godot Mono.

This is what I have right now:

using Godot;
using System;
using System.Collections.Generic;

[Tool]
[GlobalClass]
public partial class DifficultyOption : Resource
{
    [Export] public string Name;
   /// more properties will be added later
}

[Tool]
public partial class DifficultyConfig : Resource
{
    [Export] public Godot.Collections.Array<DifficultyOption> Difficulties = new();
}

I have attached the script to .tres file and I can add empty elements to the list using Godot GUI. The issue is I cannot fill the slot with DifficultyOptions - when I click on empty slot, I’m getting:

ERROR: Cannot get class 'DifficultyOption'.

in the output menu. And this is as far as I was able to go. LLMs are very confidently incorrect about the topic, and their solutions don’t improve my situation at all.

P.S

I have posted this in UI section since to me it seems related to the UI section of the resources, not to code itself.

Edit

I have decided to make different approach - remove [GlobalClass] from DifficultyOption and move it to separate script, and treat it like separate resource. This way I can add objects into DifficultyConfig, but if I edit one element of the list, every other element is automatically edited too without my input. Which means this is not the way to achieve what I want.

Don’t use LLMs for Godot. They will lie to you. They will tell you functions exist that don’t. They will lead you astray.

I would recommend you use the ConfigFile class. To see the documentation, you have to click the C# tabs in the code examples. It’s pretty well-documented in my opinion, but if you have questions ask the here instead of to an LLM.

1 Like

I have learned programming before LLMs were a thing, and I am most of the time capable of figuring out what’s going on. But LLMs tend to be useful, just remember you have mind on your own, and you should be fine. Godot is new for me though.

I will take a look at ConfigFile class tomorrow, but may I ask what is incorrect in my approach? I have also watched some tutorials but they show very simple examples without showing how to handle collections.

Edit

Ok, I’ve looked at this class now, and I’m not sure if it’s what I want. Because basically this is built-in class for creating and saving/loading config files. And end user can tamper with config file. Unless I encrypt it, but it’s kind of an overkill. I want to customize the parameters from Godot GUI. I could do that with a scene, but if I understand it right, resources are made for this kind of thing.

I’ve also tried changing DifficultyOption to normal class (no custom tags, no inheritance), but then it just doesn’t compile at all (hard to tell what’s going on, it just shows hundreds of errors unrelated to the issue).

Same. A few decades before in fact.

LLMs are amazing. I actually learned how to write my own using PyTorch before ChatGPT came out. When it came out, it took the developers at my job about a week before we started playing with it and making plans for how we could use it. We were early adapters of GitHub Copilot (the programming one, not the one they put into Google searches). So I have some experience with LLMs.

I also have experience using LLMs to specifically about Godot programming. Since 2022 when ChatGPT came out. I’d been using Godot about 6 months when it came out and as a test I thought I’d get it to help me write some simple code. I most recently tried using a programming-tuned LLM that I spun up on my own machine a couple months ago using Ollama. I got similar results. I also see similar results every time I google something and I get the AI answer at the top telling me how to program.

I got curious three years ago about why the models were so bad about certain things, so I did a bunch of research. LLMs are pattern matchers. You scan a bunch of information into them, and they match patterns. But they do not reason. They cannot do math. They can pattern match math problems - and have gotten much better at it - but they cannot solve them. They are also instructed to answer questions and not to say “I don’t know”. Instead they try to give the best answer possible. That has led to what is known as AI hallucinations. What I referred to as “lying” in my above post.

AI hallucinates because it doesn’t answer questions the way we as humans would. Instead, it searches for the best pattern it can find and presents that. Here’s the kicker: We don’t know how it does that. Let’s say we give it pictures of 100,000 pictures each of dogs and cats to train an LLM. We give it categories and we tell it which is which. But what we really do is we have a person go through 100 of each saying “this is a dog”, “this is a cat”. Then we feed the rest in, and the LLM makes comparisons and stores the data of which is which. But it does that by pulling out the markers that it thinks make a dog or a cat. Then it can not only identify dogs and cats, but draw them. Except one day someone feeds it a chihuahua pictures, and it confidently identifies it as a cat. As it gets fed more chihuahua pictures, they all become “cats” in its relational database. It starts drawing them when people ask for dogs. A user points out that it’s a dog, and the AI responds “Oh you’re right, let me fix that”. It modifies its data to say that that particular picture it generated is a dog. But it does not make any logical leap to know that all the chihuahuas were categorized incorrectly. It also usually only makes the modification for that conversation and doesn’t actually make any long term changes to itself.

So now we get to Godot and LLMs. There is very little knowledge about Godot on the internet as compared to almost any other programming language. There are two main places that LLMs can scrape for answers about programming. The first is the Godot docs, which have changed consistently and significantly every few months for years as improvements and changes are made. The second is these forums, in which we try to give answers, but we are just people trying to help other people. Most tutorials are in YouTube videos, and so a lot of that knowledge isn’t available to LLMs, (though I believe that Google Copilot has been scraping them over the last few months). Still, there aren’t decades of post to pull from on Godot.

So LLMs do what they always do. They pattern-match. GDScript looks a lot like python. Godot supports C# and looks a lot like C#. LLMs cannot tell the difference. Their patterns will fill in answers from decades of python and C# answers when it can’t find a pure Godot one. It will make up reasonable-sounding names for functions that it thinks should exist. This last one comes from the fact that LLMs are programmed to regurgitate the information they store be writing completely new but slightly different version of what they’ve seen. They hallucinate answers. They also suck at math, and there’s a lot of math in game programming.

Finally, more and more people are coming here and saying some variation on what you’re saying, “LLMs gave me this code and it doesn’t work. Help.” Instead of reading the docs.

You said:

This is patently false. There’s specifically documentation on configuration files.

Then you said in your reply:

You didn’t say that in your initial post. Setting aside that you can in fact edit configuration files directly in the Godot editor - you didn’t supply the actual problem you’re trying to solve. You provided code and asked us to fix it without context. With the context I had, I attempted to give you a better, well-documented solution with the information I had.

Here’s the documentation on resources. Also keep in mind that when you chose C# you chose the path less traveled. There are a LOT less tutorials about Godot with C#. You will have a much easier time using GDScript.

Based on your code, it’s hard to know what you’re trying to do. I don’t know why you’re trying to make a Resource a Tool script. I don’t know why you didn’t add [GlobalClass] to DifficultyConfig. I don’t know why you are adding two classes to one file.

using System;
using Godot;

[GlobalClass]
public partial class DifficultyOption : Resource
{
    [Export] public string Name;
   /// more properties will be added later
}
using Godot;
using System;
using System.Collections.Generic;

[GlobalClass]
public partial class DifficultyConfig : Resource
{
    [Export] public Array<DifficultyOption> difficulties = new(); // Not a class name shouldn't be capitalized. Also not sure why you were qualifying the array.
}

Try those. See if that works.


As a final note, you can learn a lot from LLMs, but you have no idea if any of it is correct. LLMs are most useful as tools to automate work you already know how to do, so when it messes up you can immediately spot and correct the mistakes.

1 Like

I don’t know if it’s optimal solution, but I have created something like this for controlling individual difficulty (easy, medium, hard, etc) resource:

using Godot;
using System;

[Tool]
public partial class DifficultyOption : Resource
{
    [Export] public int Id { get; set; } = 0;
    [Export] public string Name { get; set; } = "Default";
//other stuff
}

And separate script + resource for having it all in one place:

using Godot;
using System;

[Tool]
public partial class DifficultyList : Resource
{
    [Export]
    public Godot.Collections.Array<DifficultyOption> Difficulties { get; set; } = new Godot.Collections.Array<DifficultyOption>();
}

And now I can add each difficulty (resource) into the resource collection, and edit it from Godot GUI with no hardcoding involved.

And yes, I have to admit my original post ended up not being informative enough. I appreciate that you don’t want to murder me here like people on stackoverflow would.

This is kind of unrelated, but while doing it (and some other stuff) I’ve got hit with the dreaded ERROR: Script class can only be set together with base class name twice. Godot keeps source of the problem a secret, and many people claim they have gave up on searching for the source of the issue, since apparently this error is harmless and does not affect how the application actually runs. But if someone has any clues, I will appreciate it.

Edit

I have no idea why but removing = new Godot.Collections.Array<DifficultyOption>(); from the script above fixes both issues at once. My guess is new happens on it’s own, and Godot really hates when you force it again.

A bit late perhaps, but Godot didn’t see DifficultyOption there because you had it in a DifficultyConfig script. This is mentioned in the docs, amongst a million other things, the script file name is expected to match the class. That is, every Godot-facing C# class must be placed in its own correctly named file. I didn’t need to make any other changes you described (though I had to reload the project) for it to start working.

1 Like