How to change Sprite2D properties in editor with a script

Godot Version

4.3

Question

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.

BR, ₳

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 :sweat_smile:
Feel free to ask for help along the way in this thread :+1:

1 Like

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)

1 Like

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?

WorldConfig.gd:

@tool
class_name WorldConfig
extends Resource

# Map size
@export var height = 500:
	set(new_height):
		height = new_height
		changed.emit()
@export var width := 500:
	set(new_width):
		width = new_width
		changed.emit()
	
# Random generator parameters
@export var noise_seed := 0:
	set(new_noise_seed):
		noise_seed = new_noise_seed
		changed.emit()
@export var fractal_octaves := 2:
	set(new_fractal_octaves):
		fractal_octaves = new_fractal_octaves
		changed.emit()
@export var fractal_lacunarity := 0.8:
	set(new_fractal_lacunarity):
		fractal_lacunarity = new_fractal_lacunarity
		changed.emit()
@export var fractal_gain := 3:
	set(new_fractal_gain):
		fractal_gain = new_fractal_gain
		changed.emit()

world_map.gd:

@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)
1 Like

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.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.