How to register changes to a scene in editor plugin?

Godot Version

4.2.1

Question

If I move an object in the current scene and quit godot, it will show a popup warning that there are changes that are not saved.

if I have this code in my editor plugin:
var myObject = get_tree().edited_scene_root.find_child('MyObject', false, false) myObject.position = Vector3(0,10,0)
then quit godot I do not get the popup. How can I fix this? Why are the scene changes not registering as a change in scene even tho it has changed in exactly the same way, but one is by code?

Modifications are only registered if you use the EditorUndoRedoManager

I think this is what I need… but im not finding much in the docs. is there a simple example you can show for my code above?
var myObject = get_tree().edited_scene_root.find_child('MyObject', false, false) myObject.position = Vector3(0,10,0)

It is a simple code moving an object up to 10 on y axis.
how do I register this move as a EditorUndoRedoManager?

You can check the UndoRedo documentation which has examples

Here’s an example:

@tool
extends EditorPlugin


var button:Button


func _enter_tree() -> void:
	button = Button.new()
	button.text = "Click me"
	button.pressed.connect(func():
		var selection = EditorInterface.get_selection()
		var selected_nodes = selection.get_selected_nodes()
		if not selected_nodes:
			return

		var node = selected_nodes[0]
		if not node is Node2D:
			return

		var undo_redo = get_undo_redo()
		undo_redo.create_action("Translate a Node2D down")
		undo_redo.add_do_property(node, "position", node.position + Vector2(0, 10))
		undo_redo.add_undo_property(node, "position", node.position + Vector2(0, -10))
		undo_redo.commit_action()
	)

	add_control_to_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, button)


func _exit_tree() -> void:
	if is_instance_valid(button):
		remove_control_from_container(EditorPlugin.CONTAINER_CANVAS_EDITOR_MENU, button)
		button.queue_free()

Im sorry to say that none of this work :frowning:
there are several issues here.
1->

var undo_redo = get_undo_redo()
	
undo_redo.create_action("do a method")
undo_redo.add_do_reference(myObject)

undo_redo.add_do_method(myObject, do_test)
undo_redo.add_undo_method(myObject, do_test)

this does not work at all. for some reason, add_do_method requires a string instead of a callback. which makes no sense! anyways I gave up and did this instead which seems to work:

var undo_redo = get_undo_redo()
	
undo_redo.create_action("Move the node")
undo_redo.add_do_reference(myObject)

undo_redo.add_do_property(myObject, "position", Vector3(0,22,0))
undo_redo.add_undo_property(myObject, "position", myObject.position)

this works fine and moves the object. when pressing CTL-Z/CTL-Y on keyboard you can undo/redo so works great.

if you do NOT undo, and quit Godot… you still do not get a warning to save. loading Godot and all the changes are lost. SO either way this does not work for what I need…

any other ideas? it would be appreciate. I could just force save my scene every time I make a change in my editor plugin script… but of course this could lead to undesirable result (example: we make a bunch of changes in the scene, then use the editor plugin and the changes made previous are saved. We decide to quit because we are too lazy to undo all the many changes and reload to realize all was saved and could not revert to a previous save of our project!)

It’s explained in the documentation:

The manager’s API is mostly the same as in UndoRedo, so you can refer to its documentation for more examples. The main difference is that EditorUndoRedoManager uses object + method name for actions, instead of Callable.

In the example I posted before it works as expected.

1 Like

ok, thanks for all your hard work.
I will try again with the code you wrote and see, Id like to get the save popup to appear!

Also, I did notice the

EditorUndoRedoManager uses object + method name for actions, instead of [Callable]

and I tried to pass the method I created’s name as a string. it didnt seem to work. the method didn’t do anything tho, just print(“does work?”). I didnt get the printed string in output… but I can try again perhaps…

the object is the object that has the method_name for example:

func do_thing():
	#...
	undo_redo.add_do_method(self, "do_test", myObject)
	undo_redo.add_undo_method(self, "do_test", myObject)
	#...
	
	
func do_test(object:Node):
	print("test %s" % object.name)
1 Like

ok… i seem to be making some progress. I got the save popup to show up and I got your code to work with the method. I noticed you put self, “methodName”, object) I did not know that is the way. I thought object goes first…
but i got the correct syntax and all seems ok now. Thanks a lot for your help

I have one more question though: how to add parameters? I imagine like this:

func do_thing():
	#...
	undo_redo.add_do_method(self, "do_test", myObject, "do", 10)
	undo_redo.add_undo_method(self, "do_test", myObject, "undo", 0)
	#...
	
	
func do_test(object:Node, name:String, value:int):
	print("test %s" % object.name)
        print( name + " -> value = " + str(value) )

EDIT: I JUST TRIED THIS AND EVERYTHING WORKS FINE. THANKS!!

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