diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index dfd4a5c2d5a5..d2b95fda20a4 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2704,11 +2704,14 @@ Copies the viewport to a region of the screen specified by [code]rect[/code]. If [method viewport_set_render_direct_to_screen] is [code]true[/code], then the viewport does not use a framebuffer and the contents of the viewport are rendered directly to screen. However, note that the root viewport is drawn last, therefore it will draw over the screen. Accordingly, you must set the root viewport to an area that does not cover the area that you have attached this viewport to. For example, you can set the root viewport to not render at all with the following code: - [codeblock] + FIXME: The method seems to be non-existent. + [codeblocks] + [gdscript] func _ready(): get_viewport().set_attach_to_screen_rect(Rect2()) $Viewport.set_attach_to_screen_rect(Rect2(0, 0, 600, 600)) - [/codeblock] + [/gdscript] + [/codeblocks] Using this can result in significant optimization, especially on lower-end devices. However, it comes at the cost of having to manage your viewports manually. For a further optimization see, [method viewport_set_render_direct_to_screen]. diff --git a/doc/classes/RichTextEffect.xml b/doc/classes/RichTextEffect.xml index 726b26fbc7b3..edab35f162ee 100644 --- a/doc/classes/RichTextEffect.xml +++ b/doc/classes/RichTextEffect.xml @@ -6,10 +6,16 @@ A custom effect for use with [RichTextLabel]. [b]Note:[/b] For a [RichTextEffect] to be usable, a BBCode tag must be defined as a member variable called [code]bbcode[/code] in the script. - [codeblock] + [codeblocks] + [gdscript] # The RichTextEffect will be usable like this: `[example]Some text[/example]` var bbcode = "example" - [/codeblock] + [/gdscript] + [csharp] + // The RichTextEffect will be usable like this: `[example]Some text[/example]` + public string bbcode = "example"; + [/csharp] + [/codeblocks] [b]Note:[/b] As soon as a [RichTextLabel] contains at least one [RichTextEffect], it will continuously process the effect unless the project is paused. This may impact battery life negatively. diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index c54e2f4b8879..72a42202c747 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -69,12 +69,22 @@ Returns a [SceneTreeTimer] which will [signal SceneTreeTimer.timeout] after the given time in seconds elapsed in this [SceneTree]. If [code]process_always[/code] is set to [code]false[/code], pausing the [SceneTree] will also pause the timer. Commonly used to create a one-shot delay timer as in the following example: - [codeblock] + [codeblocks] + [gdscript] func some_function(): print("start") yield(get_tree().create_timer(1.0), "timeout") print("end") - [/codeblock] + [/gdscript] + [csharp] + public async void SomeFunction() + { + GD.Print("start"); + await ToSignal(GetTree().CreateTimer(1.0f), "timeout"); + GD.Print("end"); + } + [/csharp] + [/codeblocks] The timer will be automatically freed after its time elapses. diff --git a/doc/classes/SceneTreeTimer.xml b/doc/classes/SceneTreeTimer.xml index a4a83fa65b52..b223bf6821cc 100644 --- a/doc/classes/SceneTreeTimer.xml +++ b/doc/classes/SceneTreeTimer.xml @@ -6,12 +6,22 @@ A one-shot timer managed by the scene tree, which emits [signal timeout] on completion. See also [method SceneTree.create_timer]. As opposed to [Timer], it does not require the instantiation of a node. Commonly used to create a one-shot delay timer as in the following example: - [codeblock] + [codeblocks] + [gdscript] func some_function(): print("Timer started.") yield(get_tree().create_timer(1.0), "timeout") print("Timer ended.") - [/codeblock] + [/gdscript] + [csharp] + public async void SomeFunction() + { + GD.Print("Timer started."); + await ToSignal(GetTree().CreateTimer(1.0f), "timeout"); + GD.Print("Timer ended."); + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/ScriptCreateDialog.xml b/doc/classes/ScriptCreateDialog.xml index aa60ecb12b3e..7185213c4458 100644 --- a/doc/classes/ScriptCreateDialog.xml +++ b/doc/classes/ScriptCreateDialog.xml @@ -5,12 +5,24 @@ The [ScriptCreateDialog] creates script files according to a given template for a given scripting language. The standard use is to configure its fields prior to calling one of the [method Window.popup] methods. - [codeblock] + [codeblocks] + [gdscript] func _ready(): - dialog.config("Node", "res://new_node.gd") # For in-engine types - dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # For script types + var dialog = ScriptCreateDialog.new(); + dialog.config("Node", "res://new_node.gd") # For in-engine types. + dialog.config("\"res://base_node.gd\"", "res://derived_node.gd") # For script types. dialog.popup_centered() - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + var dialog = new ScriptCreateDialog(); + dialog.Config("Node", "res://NewNode.cs"); // For in-engine types. + dialog.Config("\"res://BaseNode.cs\"", "res://DerivedNode.cs"); // For script types. + dialog.PopupCentered(); + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml index a08d4f5b4497..7e2481f4589a 100644 --- a/doc/classes/SpinBox.xml +++ b/doc/classes/SpinBox.xml @@ -6,13 +6,22 @@ SpinBox is a numerical input text field. It allows entering integers and floats. [b]Example:[/b] - [codeblock] + [codeblocks] + [gdscript] var spin_box = SpinBox.new() add_child(spin_box) var line_edit = spin_box.get_line_edit() line_edit.context_menu_enabled = false spin_box.align = LineEdit.ALIGN_RIGHT - [/codeblock] + [/gdscript] + [csharp] + var spinBox = new SpinBox(); + AddChild(spinBox); + var lineEdit = spinBox.GetLineEdit(); + lineEdit.ContextMenuEnabled = false; + spinBox.Align = LineEdit.AlignEnum.Right; + [/csharp] + [/codeblocks] The above code will create a [SpinBox], disable context menu on it and set the text alignment to right. See [Range] class for more options over the [SpinBox]. [b]Note:[/b] [SpinBox] relies on an underlying [LineEdit] node. To theme a [SpinBox]'s background, add theme items for [LineEdit] and customize them. diff --git a/doc/classes/Sprite2D.xml b/doc/classes/Sprite2D.xml index 83235f099180..e4df75367436 100644 --- a/doc/classes/Sprite2D.xml +++ b/doc/classes/Sprite2D.xml @@ -15,12 +15,29 @@ Returns a [Rect2] representing the Sprite2D's boundary in local coordinates. Can be used to detect if the Sprite2D was clicked. Example: - [codeblock] + [codeblocks] + [gdscript] func _input(event): if event is InputEventMouseButton and event.pressed and event.button_index == BUTTON_LEFT: if get_rect().has_point(to_local(event.position)): print("A click!") - [/codeblock] + [/gdscript] + [csharp] + public override void _Input(InputEvent inputEvent) + { + if (inputEvent is InputEventMouseButton inputEventMouse) + { + if (inputEventMouse.Pressed && inputEventMouse.ButtonIndex == (int)ButtonList.Left) + { + if (GetRect().HasPoint(ToLocal(inputEventMouse.Position))) + { + GD.Print("A click!"); + } + } + } + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index a73d3c821233..a1b858acf6ec 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -212,9 +212,14 @@ Puts a zero-terminated ASCII string into the stream prepended by a 32-bit unsigned integer representing its size. Note: To put an ASCII string without prepending its size, you can use [method put_data]: - [codeblock] + [codeblocks] + [gdscript] put_data("Hello world".to_ascii()) - [/codeblock] + [/gdscript] + [csharp] + PutData("Hello World".ToAscii()); + [/csharp] + [/codeblocks] @@ -261,9 +266,14 @@ Puts a zero-terminated UTF-8 string into the stream prepended by a 32 bits unsigned integer representing its size. Note: To put an UTF-8 string without prepending its size, you can use [method put_data]: - [codeblock] + [codeblocks] + [gdscript] put_data("Hello world".to_utf8()) - [/codeblock] + [/gdscript] + [csharp] + PutData("Hello World".ToUTF8()); + [/csharp] + [/codeblocks] diff --git a/doc/classes/String.xml b/doc/classes/String.xml index c03f6357abb7..a90bcd9eb742 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -163,11 +163,15 @@ Returns the index of the [b]first[/b] case-sensitive occurrence of the specified string in this instance, or [code]-1[/code]. Optionally, the starting search index can be specified, continuing to the end of the string. [b]Note:[/b] If you just want to know whether a string contains a substring, use the [code]in[/code] operator as follows: - [codeblock] - # Will evaluate to `false`. - if "i" in "team": - pass - [/codeblock] + [codeblocks] + [gdscript] + print("i" in "team") # Will print `false`. + [/gdscript] + [csharp] + // C# has no in operator, but we can use `Contains()`. + GD.Print("team".Contains("i")); // Will print `false`. + [/csharp] + [/codeblocks] @@ -354,9 +358,14 @@ Return a [String] which is the concatenation of the [code]parts[/code]. The separator between elements is the string providing this method. Example: - [codeblock] + [codeblocks] + [gdscript] print(", ".join(["One", "Two", "Three", "Four"])) - [/codeblock] + [/gdscript] + [csharp] + GD.Print(String.Join(",", new string[] {"One", "Two", "Three", "Four"})); + [/csharp] + [/codeblocks] @@ -654,13 +663,18 @@ The splits in the returned array are sorted in the same order as the original string, from left to right. If [code]maxsplit[/code] is specified, it defines the number of splits to do from the right up to [code]maxsplit[/code]. The default value of 0 means that all items are split, thus giving the same result as [method split]. Example: - [codeblock] + [codeblocks] + [gdscript] var some_string = "One,Two,Three,Four" var some_array = some_string.rsplit(",", true, 1) print(some_array.size()) # Prints 2 print(some_array[0]) # Prints "Four" print(some_array[1]) # Prints "Three,Two,One" - [/codeblock] + [/gdscript] + [csharp] + // There is no Rsplit. + [/csharp] + [/codeblocks] @@ -723,13 +737,21 @@ Splits the string by a [code]delimiter[/code] string and returns an array of the substrings. The [code]delimiter[/code] can be of any length. If [code]maxsplit[/code] is specified, it defines the number of splits to do from the left up to [code]maxsplit[/code]. The default value of [code]0[/code] means that all items are split. Example: - [codeblock] + [codeblocks] + [gdscript] var some_string = "One,Two,Three,Four" var some_array = some_string.split(",", true, 1) print(some_array.size()) # Prints 2 - print(some_array[0]) # Prints "One" - print(some_array[1]) # Prints "Two,Three,Four" - [/codeblock] + print(some_array[0]) # Prints "Four" + print(some_array[1]) # Prints "Three,Two,One" + [/gdscript] + [csharp] + var someString = "One,Two,Three,Four"; + var someArray = someString.Split(",", true); // This is as close as it gets to Godots API. + GD.Print(someArray[0]); // Prints "Four" + GD.Print(someArray[1]); // Prints "Three,Two,One" + [/csharp] + [/codeblocks] If you need to split strings with more complex rules, use the [RegEx] class instead. diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 331de4284ed1..d145b4ce97ad 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -5,13 +5,22 @@ The [SurfaceTool] is used to construct a [Mesh] by specifying vertex attributes individually. It can be used to construct a [Mesh] from a script. All properties except indices need to be added before calling [method add_vertex]. For example, to add vertex colors and UVs: - [codeblock] + [codeblocks] + [gdscript] var st = SurfaceTool.new() st.begin(Mesh.PRIMITIVE_TRIANGLES) st.set_color(Color(1, 0, 0)) st.set_uv(Vector2(0, 0)) st.set_vertex(Vector3(0, 0, 0)) - [/codeblock] + [/gdscript] + [csharp] + var st = new SurfaceTool(); + st.Begin(Mesh.PrimitiveType.Triangles); + st.SetColor(new Color(1, 0, 0)); + st.SetUv(new Vector2(0, 0)); + st.SetVertex(new Vector3(0, 0, 0)); + [/csharp] + [/codeblocks] The above [SurfaceTool] now contains one vertex of a triangle which has a UV coordinate and a specified [Color]. If another vertex were added without calling [method set_uv] or [method set_color], then the last values would be used. Vertex attributes must be passed [b]before[/b] calling [method add_vertex]. Failure to do so will result in an error when committing the vertex information to a mesh. Additionally, the attributes used before the first vertex is added determine the format of the mesh. For example, if you only add UVs to the first vertex, you cannot add color to any of the subsequent vertices. diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 1d50cd60e577..6232a4e146a5 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -402,13 +402,24 @@ Perform a search inside the text. Search flags can be specified in the [enum SearchFlags] enum. Returns an empty [code]Dictionary[/code] if no result was found. Otherwise, returns a [code]Dictionary[/code] containing [code]line[/code] and [code]column[/code] entries, e.g: - [codeblock] - var result = search(key, flags, line, column) - if !result.is_empty(): + [codeblocks] + [gdscript] + var result = search("print", SEARCH_WHOLE_WORDS, 0, 0) + if !result.empty(): # Result found. var line_number = result.line var column_number = result.column - [/codeblock] + [/gdscript] + [csharp] + int[] result = Search("print", (uint)TextEdit.SearchFlags.WholeWords, 0, 0); + if (result.Length > 0) + { + // Result found. + int lineNumber = result[(int)TextEdit.SearchResult.Line]; + int columnNumber = result[(int)TextEdit.SearchResult.Column]; + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index a12ef614e337..c0ee004ca630 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -172,12 +172,22 @@ [b]Note:[/b] Data such as navigation polygons and collision shapes are not immediately updated for performance reasons. If you need these to be immediately updated, you can call [method update_dirty_quadrants]. Overriding this method also overrides it internally, allowing custom logic to be implemented when tiles are placed/removed: - [codeblock] + [codeblocks] + [gdscript] func set_cell(x, y, tile, flip_x=false, flip_y=false, transpose=false, autotile_coord=Vector2()) # Write your custom logic here. # To call the default method: .set_cell(x, y, tile, flip_x, flip_y, transpose, autotile_coord) - [/codeblock] + [/gdscript] + [csharp] + public void SetCell(int x, int y, int tile, bool flipX = false, bool flipY = false, bool transpose = false, Vector2 autotileCoord = new Vector2()) + { + // Write your custom logic here. + // To call the default method: + base.SetCell(x, y, tile, flipX, flipY, transpose, autotileCoord); + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml index 8502707096bf..a09f1bf470ed 100644 --- a/doc/classes/Tree.xml +++ b/doc/classes/Tree.xml @@ -6,16 +6,30 @@ This shows a tree of items that can be selected, expanded and collapsed. The tree can have multiple columns with custom controls like text editing, buttons and popups. It can be useful for structured displays and interactions. Trees are built via code, using [TreeItem] objects to create the structure. They have a single root but multiple roots can be simulated if a dummy hidden root is added. - [codeblock] + [codeblocks] + [gdscript] func _ready(): var tree = Tree.new() var root = tree.create_item() - tree.set_hide_root(true) + tree.hide_root = true var child1 = tree.create_item(root) var child2 = tree.create_item(root) var subchild1 = tree.create_item(child1) subchild1.set_text(0, "Subchild1") - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + var tree = new Tree(); + TreeItem root = tree.CreateItem(); + tree.HideRoot = true; + TreeItem child1 = tree.CreateItem(root); + TreeItem child2 = tree.CreateItem(root); + TreeItem subchild1 = tree.CreateItem(child1); + subchild1.SetText(0, "Subchild1"); + } + [/csharp] + [/codeblocks] To iterate over all the [TreeItem] objects in a [Tree] object, use [method TreeItem.get_next] and [method TreeItem.get_children] after getting the root through [method get_root]. You can use [method Object.free] on a [TreeItem] to remove it from the [Tree]. @@ -152,13 +166,26 @@ Returns the currently edited item. Can be used with [signal item_edited] to get the item that was modified. - [codeblock] + [codeblocks] + [gdscript] func _ready(): $Tree.item_edited.connect(on_Tree_item_edited) func on_Tree_item_edited(): print($Tree.get_edited()) # This item just got edited (e.g. checked). - [/codeblock] + [/gdscript] + [csharp] + public override void _Ready() + { + GetNode<Tree>("Tree").ItemEdited += OnTreeItemEdited; + } + + public void OnTreeItemEdited() + { + GD.Print(GetNode<Tree>("Tree").GetEdited()); // This item just got edited (e.g. checked). + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index 56ccaaf38304..b32bb1e2d9c1 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -7,13 +7,22 @@ Tweens are useful for animations requiring a numerical property to be interpolated over a range of values. The name [i]tween[/i] comes from [i]in-betweening[/i], an animation technique where you specify [i]keyframes[/i] and the computer interpolates the frames that appear between them. [Tween] is more suited than [AnimationPlayer] for animations where you don't know the final values in advance. For example, interpolating a dynamically-chosen camera zoom value is best done with a [Tween] node; it would be difficult to do the same thing with an [AnimationPlayer] node. Here is a brief usage example that makes a 2D node move smoothly between two positions: - [codeblock] + [codeblocks] + [gdscript] var tween = get_node("Tween") tween.interpolate_property($Node2D, "position", Vector2(0, 0), Vector2(100, 100), 1, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT) tween.start() - [/codeblock] + [/gdscript] + [csharp] + var tween = GetNode<Tween>("Tween"); + tween.InterpolateProperty(GetNode<Node2D>("Node2D"), "position", + new Vector2(0, 0), new Vector2(100, 100), 1, + Tween.TransitionType.Linear, Tween.EaseType.InOut); + tween.Start(); + [/csharp] + [/codeblocks] Many methods require a property name, such as [code]"position"[/code] above. You can find the correct property name by hovering over the property in the Inspector. You can also provide the components of a property directly by using [code]"property:component"[/code] (eg. [code]position:x[/code]), where it would only apply to that particular component. Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [url=https://easings.net/]easings.net[/url] for some examples). The second accepts an [enum EaseType] constant, and controls where the [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [constant EASE_IN_OUT], and use the one that looks best. [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/tween_cheatsheet.png]Tween easing and transition types cheatsheet[/url] diff --git a/doc/classes/UDPServer.xml b/doc/classes/UDPServer.xml index aabfed85f091..0fc00f67f84c 100644 --- a/doc/classes/UDPServer.xml +++ b/doc/classes/UDPServer.xml @@ -7,8 +7,9 @@ A simple server that opens a UDP socket and returns connected [PacketPeerUDP] upon receiving new packets. See also [method PacketPeerUDP.connect_to_host]. After starting the server ([method listen]), you will need to [method poll] it at regular intervals (e.g. inside [method Node._process]) for it to process new packets, delivering them to the appropriate [PacketPeerUDP], and taking new connections. Below a small example of how it can be used: - [codeblock] - # server.gd + [codeblocks] + [gdscript] + class_name Server extends Node var server := UDPServer.new() @@ -21,20 +22,57 @@ server.poll() # Important! if server.is_connection_available(): var peer : PacketPeerUDP = server.take_connection() - var pkt = peer.get_packet() + var packet = peer.get_packet() print("Accepted peer: %s:%s" % [peer.get_packet_ip(), peer.get_packet_port()]) - print("Received data: %s" % [pkt.get_string_from_utf8()]) + print("Received data: %s" % [packet.get_string_from_utf8()]) # Reply so it knows we received the message. - peer.put_packet(pkt) + peer.put_packet(packet) # Keep a reference so we can keep contacting the remote peer. peers.append(peer) for i in range(0, peers.size()): pass # Do something with the connected peers. + [/gdscript] + [csharp] + using Godot; + using System; + using System.Collections.Generic; - [/codeblock] - [codeblock] - # client.gd + public class Server : Node + { + public UDPServer Server = new UDPServer(); + public List<PacketPeerUDP> Peers = new List<PacketPeerUDP>(); + + public override void _Ready() + { + Server.Listen(4242); + } + + public override void _Process(float delta) + { + Server.Poll(); // Important! + if (Server.IsConnectionAvailable()) + { + PacketPeerUDP peer = Server.TakeConnection(); + byte[] packet = peer.GetPacket(); + GD.Print($"Accepted Peer: {peer.GetPacketIp()}:{peer.GetPacketPort()}"); + GD.Print($"Received Data: {packet.GetStringFromUTF8()}"); + // Reply so it knows we received the message. + peer.PutPacket(packet); + // Keep a reference so we can keep contacting the remote peer. + Peers.Add(peer); + } + foreach (var peer in Peers) + { + // Do something with the peers. + } + } + } + [/csharp] + [/codeblocks] + [codeblocks] + [gdscript] + class_name Client extends Node var udp := PacketPeerUDP.new() @@ -50,7 +88,37 @@ if udp.get_available_packet_count() > 0: print("Connected: %s" % udp.get_packet().get_string_from_utf8()) connected = true - [/codeblock] + [/gdscript] + [csharp] + using Godot; + using System; + + public class Client : Node + { + public PacketPeerUDP Udp = new PacketPeerUDP(); + public bool Connected = false; + + public override void _Ready() + { + Udp.ConnectToHost("127.0.0.1", 4242); + } + + public override void _Process(float delta) + { + if (!Connected) + { + // Try to contact server + Udp.PutPacket("The Answer Is..42!".ToUTF8()); + } + if (Udp.GetAvailablePacketCount() > 0) + { + GD.Print($"Connected: {Udp.GetPacket().GetStringFromUTF8()}"); + Connected = true; + } + } + } + [/csharp] + [/codeblocks] diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index e8124d0e6ade..a0330de4fada 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -7,7 +7,8 @@ Helper to manage undo/redo operations in the editor or custom tools. It works by registering methods and property changes inside "actions". Common behavior is to create an action, then add do/undo calls to functions or property changes, then committing the action. Here's an example on how to add an action to the Godot editor's own [UndoRedo], from a plugin: - [codeblock] + [codeblocks] + [gdscript] var undo_redo = get_undo_redo() # Method of EditorPlugin. func do_something(): @@ -24,7 +25,37 @@ undo_redo.add_do_property(node, "position", Vector2(100,100)) undo_redo.add_undo_property(node, "position", node.position) undo_redo.commit_action() - [/codeblock] + [/gdscript] + [csharp] + public UndoRedo UndoRedo; + + public override void _Ready() + { + UndoRedo = GetUndoRedo(); // Method of EditorPlugin. + } + + public void DoSomething() + { + // Put your code here. + } + + public void UndoSomething() + { + // Put here the code that reverts what's done by "DoSomething()". + } + + private void OnMyButtonPressed() + { + var node = GetNode<Node2D>("MyNode2D"); + UndoRedo.CreateAction("Move the node"); + UndoRedo.AddDoMethod(this, nameof(DoSomething)); + UndoRedo.AddUndoMethod(this, nameof(UndoSomething)); + UndoRedo.AddDoProperty(node, "position", new Vector2(100, 100)); + UndoRedo.AddUndoProperty(node, "position", node.Position); + UndoRedo.CommitAction(); + } + [/csharp] + [/codeblocks] [method create_action], [method add_do_method], [method add_undo_method], [method add_do_property], [method add_undo_property], and [method commit_action] should be called one after the other, like in the example. Not doing so could lead to crashes. If you don't need to register a method, you can leave [method add_do_method] and [method add_undo_method] out; the same goes for properties. You can also register more than one method/property.