How to make some properties of sub node configurable for each instance in a clean way, updating the 2D editor view as well?

I have just started learning Godot (using Version 4.1.1 stable mono.official (bd6af8e0e) )

I think I have a basic question.

I am trying to make a 2d platformer game.
Currently I am working on a Scene (for the walls and floors) which is configured like this:

RegidBody2D
TextureRect
CollisionShape2D

The TextureRect has it's stretch mode set to Tile. (because I want to have larger walls and floors)

What I want is to be able to set this size per instance.

You could do this by setting Editable children, but then you get clutter, because you can change every little thing on every instance. Which I don't want.

So I tried it with @export, with a script for the scene (set on RigidBody2D)

It has 2 @export variables, which then resize the width of the textureRect inside of the _ready() function.

But then it did not update the preview inside of the 2D view.
To fix this, I added @tool at the top, but it's not up to date.

Is there a simple way to just make some properties configurable, or maybe do it via @tool script, but then keep the 2D view up to date, or maybe force the _ready() script to rerun for the 2D view ( tool )?

Thanks 🙂

So, I have created a method in order to set the size of the textureRect, and putting it inside of _process, and only for Engine.is_editor_hint(): makes it work (obviously), but it seems ugly that you have to reset a texture 60 times a second, just inside of the editor, in order to make it update at all, when _ready() does not update it, even though you update a variable. Now, it makes sense for some reason, but still, I wish it would just do what you wanted. Maybe there is another lifecycle hook in order to listen to a change of an @exported variable, just for inside of the editor, so you could then update it, but even that seems ugly, when really godot should just update the size of my texture rect (which is based upon a variable, which was the cleanest way to do it.) But even that seems like it's not the cleanest way, when really, godot should just allow you to specify which fields of which inherited objects, of whatever children to expose as editable for the scene component, so you could change it for each instance of that scene in your master scene, or what have you.

@tool # this shows the changes insid of the editor itself.
extends RigidBody2D

@export var width : int = 1
@export var height : int = 1

var speed: float = 100.0
var textureRect: TextureRect  # Reference to the TextureRect
var collisionShape2D: CollisionShape2D

var actualWidth: int
var actualHeight: int

func _ready():
	updateTexture()
	updateCollisionShape()

func updateTexture():
	actualWidth = width * 16
	actualHeight = height * 16
	textureRect = get_node("TextureRect")
	textureRect.set_size(Vector2(actualWidth, actualHeight))

# Update the CollisionShape2D size based on the TextureRect size
func updateCollisionShape():
	actualWidth = width * 16
	actualHeight = height * 16
	var rect = RectangleShape2D.new()
	rect.set_size(Vector2(actualWidth, actualHeight))
	collisionShape2D = get_node("CollisionShape2D")
	collisionShape2D.set_shape(rect)
	
	collisionShape2D.position.x = textureRect.position.x + (actualWidth / 2)
	collisionShape2D.position.y = textureRect.position.y + (actualHeight / 2)

func _process(delta):
	if Engine.is_editor_hint():
		updateTexture()
		updateCollisionShape()

You can try using getters/setters. Those will update in the editor. I don't think you even need @tool but I might be mistaken.

@export var width:int : set = set_width, get = get_width

func set_width(p_value) -> void:
	width = p_value
	if Engine.is_editor_hint(): #this line so it only runs in editor and not in game
		updateTexture()
		updateCollisionShape()
	
func get_width() -> int:
	return width

    award Yeah, well. So you need 7 lines of code just to do something for the editor? No, I'm not doing that. And eventually, I
    found a clean way of making it happen.

    Doing your function inside of the _process(): function is the cleanest, because you can just call it. But the big problem with this, is that it will call it every frame, so we need to somehow rate limit it. We can do that by setting a numerical variable, and adding to it, and by moduloing this counter by some value (say 60) and comparing the value to 0, and only then calling the function, we are limiting the frequency of when this function will get executed.

    This works nice, but we still have that variable. So we can use set_meta(key, value), get_meta(key) and has_meta(key) instead, so that we don't need the extra variable.

    Now we can pack this up inside of a static function inside of a class (using class_name) which are globally exposed and now we have exactly what we want.

    To show you the code:

    Inside our scene script:

    func _process(delta):
    	Helper.updateEditorView(self, [setTexture])

    helper.gd

    class_name Helper
    
    # ... other fns
    
    static func updateEditorView(self2, callableArr):
    	if Engine.is_editor_hint():
    		const key = "update_editor_count"
    		if self2.has_meta(key):
    			var val = self2.get_meta(key)
    			if floor(val) % 60 == 0:
    				for callable in callableArr:
    					callable.call()
    			self2.set_meta(key, val + 1)
    		else:
    			self2.set_meta(key, 0)