Solving a problem like a dynamic system with many individual units, where the state of any unit can depend on the previous states of any of the other units (including itself) is a pretty complex and large topic. The way to build such a simulation can be very different depending on what the character of the things you are simulating is, and what kind of performance you are targeting, as well as which systems you are able to understand. I’ll try to give some general advice thoug.
Using Resources might not be the best path for what you are trying to do. Resources are a type of Object that specifically is able to be saved as a file, this is their primary purpose. So if a resource A contains another resource B within it, then saving A to a file first requires turning B into data that can be saved into a file, and then including that data along with the rest of what A needs in a file.
Since both A and B could be saved as files, B cannot possibly contain A, if it did then saving B would require collecting all the data required to save A, which includes all the data required to save B, which includes all the data required to save A… to infinity…
This problem has many different solutions, but you want something that will help you be able to edit the behavior of your simulated units in the inspector rather than changing scripts all the time, there aren’t any existing inspector that fit this very closely without having to do a little work, but lets try something like this:
I’ll assume that each unit has up to 4 internal values that it can keep track of for simplicity and it can “announce” any number of external values that other units will be able to see, but only on the following timestep, I’ll call the collection of all the announced values the “environment”. I’ll also assume that the new value of the unit’s internal values can be expressed as an “expression” which can be handled by the Expression
class in godot. The same will apply to announcements which will be evaluated after the internal values are updated so that they can use those new values. Finally I’ll assume all values can be represented as float
s so numbers including a fractional component like 3.529
The units now only need to store their internal values, the expressions to update them, and the expressions to announce new values. They will not directly store other units, so they can be easily represented as a Resource
. The script will look something like this, but this is psuedocode so it’s just a general idea:
class_name SimUnit
extents Resource
@export var a: = 0
@export var b: = 0
@export var c: = 0
@export var d: = 0
const vals_index = ["a", "b", "c", "d"]
@export var update_a: String = ""
... b, c, d
@export var announcements: Array[String] = []
var update_expressions: Array[Expression] = []
var announce_expressions: Dictionary = {}
func _ready() -> void:
# parse expressions here making Expression obj for each of them taking ["a", "b", "c", "d"] as variables
update_expressions = [...parse(update_a)]
announce_expressions = {announcements[i].split('<>')[0]: parse(announcemts[i].split('<>')[1]..., ...}
func do_timestep(old_environment: Dictionary) -> Dictionary:
var my_values: = [a, b, c, d]
for index in range(4):
var abcd = vals_index[index]
var result = update_expressions[index].execute(my_values, old_environment)
set(abcd, result)
var new_announcements: = {}
for an_name in announce_expressions:
var result = announcement_expressions[an_name].execute(my_values, old_environment)
new_announcements[an_name] = result
return new_announcements
There would be a simulator script that runs all the units do_timestep passing them the dictionary of announcements, the expressions could read the announcement values like get('sugar_level')
and use the internal values just with a
or b
etc.
In the inspector you’d write announcements like “name<>get(‘other’) + (a * 2)”
where “<>” or some other unique separator is used between the name and the expression, each internal value would have it’s own expression string
You could then save all the units into an array or resources in a node or other resource to keep track of them
If you wanted to have a heirarchical structure you could also add something like
@export var child_units: Array[SimUnit] = []
And then you could nest other units inside this one, it would purely be for organizational purposes though, they would still interact with announcements in the same way, the simulator would need to loop through all the child units recursively as well
Whenever any other part of the game or program wants to interact with the system it should also be able to tell the simulator to add an announcement or read out what the announcements were for the last frame.
This is just one example of how make something like this making use of godot’s built in editor to help make iterating on a design easier. In reality, the indidual project will almost always be much easier to work with if the tools are created with that project in mind, being able to make your own UI and editing tools, both using @tool
scripts and by running an editor scene and saving Resource
files is the main strength of godot here, since you can very quickly make an editor or tool that suites your exact needs once you figure out what they are, don’t shy away from making your own tools, but explore until you know something about what you need first. Good luck!