(SOLVED) Property of Child Changed Using an @export_tool_button Being Reverted When Game is Loaded

Godot Version:

4.5

Problem:

I am making a script where I can simply press an @export_tool_button, and it will change the palette and the physics material on its staticbody2D child.

When I place one of these objects in a different scene and press the “Change Type” button, it will successfully change the palette and (according to the print statement) its physics material too. However, on loading the game, the object will have the correct “type” value, the palette will stay as the new palette, but the physics material on the child will revert to the original non-bouncy material.

I used another button to test when the material gets reverted, and it’s definitely on loading the game.

My code for this script is below:

@tool
extends Sprite2D


@export_range(0,1) var type : float
@export_tool_button("Change Type")
var change_func : Callable = func():
	change_type()


@export_group("Components")
@export var sprite2D : Sprite2D
@export var static_body2D : StaticBody2D

@export_group("Assets")
@export var normal_material : PhysicsMaterial
@export var bounce_material : PhysicsMaterial
@export var normal_palette : Texture
@export var bounce_palette : Texture



# ---CHANGE TYPE

func change_type():
	if (type == 0):
		##change material
		static_body2D.physics_material_override = bounce_material
		##change palette
		sprite2D.material.set_shader_parameter("palette", bounce_palette)
		type = 1
		print("bounce = ", static_body2D.physics_material_override.bounce)
	else:
		##change material
		static_body2D.physics_material_override = normal_material
		##change palette
		sprite2D.material.set_shader_parameter("palette", normal_palette)
		type = 0
		print("bounce = ", static_body2D.physics_material_override.bounce)






# ---TEST TO SEE WHEN THE REVERSION HAPPENS

@export_tool_button("Check if Reverted")
var check_func : Callable = func():
	check_for_bounce()


func check_for_bounce():
	print(type)
	print("bounce = ", static_body2D.physics_material_override.bounce)

func _ready() -> void:
	check_for_bounce()

Question:

Why does the physics material property on the child get reset when loading the game, and how can I prevent this?

Hints:

At this point, the object is simply structured: Sprite2D → StaticBody2D → CollisionShape2D.

Both resources are set to “Local to Scene.”

Extra :slight_smile:

*For now, I will simply use the type variable to reassign the material on load, but I’m not satisfied with that band-aid :frowning:

*Also also, I am using my laptop, and it has this problem where when certain apps are left without anything moving on the screen, my graphics card will crash the computer. So if you know what to do… please tell me. This particularly happens in Godot, and in Hades2 when it is in the pause menu. In Godot its very funny cause even just the flashing mouse when entering text will prevent it lol

When you load the game, the values in the control reset to the defaults.

When you manually change values in the editor Godot stores the values so they persist. Your button doesn’t store the values so they get overridden when you start a new instance.

As for how to fix it - I don’t have a solution.

What are you trying to do with your tool button? It isn’t something people who use the compiled program are going to have access to.

Save the scene after changing the value.

No. Without saving the scene, nothing will be changed when running. When saving the scene, the palette will load correctly in-game, but the physics material still doesn’t.

The button is simply for my convenience when designing the levels. It will be removed from the compiled version of the game.

I have an object, and I need a normal version and a bouncy version. The only difference between the two objects is the color palette and the physics material on the child object. This button simply allows me to switch those two properties while in the editor on an instance of the object instead of creating a completely different object.

What happens if you change the property manually? Does it persist?

Try using an export variable to set the type value. That will persist between instances.

I can’t change it in the game scene. IDK how to access the child of an object when you place that object in another scene. If I go to the object’s base scene and change the value on the child, it does seem to change the properties on the instances of the objects even though I have “Local to Scene” selected.

You can enable “editable children” on the scene node in the scene tree panel.

1 Like

I don’t understand. The “type” value does persist between the editor and loading the game. So does the palette swap. The only thing that isn’t persisting between the editor and the game loading is the physics_material on the child, which seems to be reverting to whatever the object’s base scene has set whenever the game is loaded.

I’ll test it out after lunch

Your button calls the change_type method. That’s what changes the physics material.

Define an export variable. Call change_type from the ready method and have it reference the value in the export variable.

You should have mentioned that you’re doing this on instances. You can only override per instance properties that are in instance’s top node, unless you enable and keep “editable children” for the instance. Either that or do something along the lines of what @that_duck said, put every relevant property in the top node, and assign to children in _ready().

2 Likes

I specifically wrote in the post that I don’t want to do that. I want no processing on load because there will be a lot of these objects.

Thank you. This is what I needed. Sorry, I didn’t mention it sooner, I guess I thought that was implied lol my bad. Thank you both for your quick responses. Merry Christmas!

2 Likes

You can toggle “editable children” from your tool script code using Node::set_editable_instance()

2 Likes

If anyone references this, this is what works to set editable_children via code in Godot 4.5. Make sure your script is a “tool” script!

##enable "editable_children" on self
get_parent().set_editable_instance(self, true);
##just to confirm :)
print(get_parent().is_editable_instance(self))
1 Like