Is it possible to change Sprite2D properties automatically if values in a (.tres) resource file changes?
My goal is to update existing Sprite2D node width, height and noise parameters from a resource, rather than from the inspector and see the end result in the editor without running the game. Thanks a lot in advance.
Yes this is totally possible.
Create a custom resource with @tool keyword
Add @export properties with custom setters
Emit the changed signal from the custom setters whenever a property is modified
Then add script on your sprite, with @tool keyword as well and an @export property for your custom resource, with a custom setter again.
On this setter function, hook the changed signal from the resource to a function in your script.
From this last function you can modify the properties of the sprite according to the resource every time it changes, even from the editor
I hope this does not sound too complex
Feel free to ask for help along the way in this thread
Thanks for your suggestion. Here’s my attempt:
WorldConfig.gd
@tool
class_name WorldConfig
extends Resource
# Map size
@export var height := 512
@export var width := 512
# Random generator parameters
@export var noise_seed := 0
@export var fractal_octaves := 2
@export var fractal_lacunarity := 0.8
@export var fractal_gain := 3
@warning_ignore("unused_signal")
signal world_config_changed
func _set(property, value):
if property == "height":
height = value
elif property == "width":
width = value
elif property == "noise_seed":
noise_seed = value
elif property == "fractal_octaves":
fractal_octaves = value
elif property == "fractal_lacunarity":
fractal_lacunarity = value
elif property == "fractal_gain":
fractal_gain = value
else:
print("Invalid property:", property)
emit_signal("changed")
I created a tres-file and exported it to my 2DSprite <world_image.gd>, which has a following script attached:
@tool
extends Sprite2D
@export var world_config : WorldConfig
func set_world_config(new_config):
world_config = new_config
if world_config:
world_config.connect("world_config_changed", _on_world_config_changed)
func _ready():
if world_config:
world_config.connect("world_config_changed", _on_world_config_changed)
func _on_world_config_changed():
# Modify sprite properties based on the world config
print("New width:", world_config.width)
print("New height:", world_config.height)
self.width = world_config.width + 1
self.height = world_config.height + 1
When I try to change the values from a resource, I can see Set height & Set width in console output, which indicates that the signal is being emitted, but no parameter change or printout from _on_world_config_changed()
Maybe I’m not using the connect function correctly? Maybe I should use some other function than _ready?
It looks like you’re emitting the changed signal but hooking to the world_config_changed signal
You dont need a custom signal since Resource have a built-in signal for this (the changed signal)
Thanks. Now I think I figured out how the signals work. The last problem is how to set the height, width and some noise parameters of a Sprite2D texture. I can get the height for instance with self.texture.get_height(), but none of my attempts on setting the value has proven to work.
I finally managed to solve the issue. Thanks for guiding me into right direction @francois.delataste. Couple of questions relating to the code quality and optimization:
The only way to tweak the properties of a texture in 2DScript I found was to create a new texture and replace the old one. Please let me know if there are more efficient ways of doing this.
My WorldConfig.gd code has a lot of repetition with the sets and emits. Is there a way to write this in more neatly manner?
@tool
extends Sprite2D
@export var world_config : WorldConfig:
set(new_world_config):
world_config = new_world_config
# Connect the changed signal as soon as a new resource is being added.
world_config.changed.connect(_on_world_config_changed)
func _on_world_config_changed():
# Create a new texture
var new_texture = NoiseTexture2D.new()
new_texture.height = world_config.height + 1
new_texture.width = world_config.width + 1
# Create new noise for the new texture
var new_noise := FastNoiseLite.new()
new_noise.noise_type = FastNoiseLite.TYPE_SIMPLEX
new_noise.seed = world_config.noise_seed
new_noise.fractal_octaves = world_config.fractal_octaves
new_noise.fractal_lacunarity = world_config.fractal_lacunarity
new_noise.fractal_gain = world_config.fractal_gain
new_texture.noise = new_noise
# Keep the existing color ramp
new_texture.color_ramp = get("texture").color_ramp
# Replace the old texture with a texture generated with new parameters
set("texture", new_texture)
Glad I could be of help.
Unfortunately not much improvement can be done in my opinion.
For the second point you can look into overriding the _set() function
But honestly if it works, and you understand how it works, and you know you’ll still understand it in 6 months, and the performance is acceptable for your project, I would keep it as is.