diff --git a/getting_started/step_by_step/scripting_continued.rst b/getting_started/step_by_step/scripting_continued.rst index b794d045a..ba6652b74 100644 --- a/getting_started/step_by_step/scripting_continued.rst +++ b/getting_started/step_by_step/scripting_continued.rst @@ -122,122 +122,6 @@ follows, can be applied to nodes: As mentioned before, it's better to use these functions instead of the notification system. -Creating nodes --------------- - -To create a node from code, call the ``.new()`` method, like for any -other class-based datatype. For example: - - -.. tabs:: - .. code-tab:: gdscript GDScript - - var s - func _ready(): - s = Sprite.new() # Create a new sprite! - add_child(s) # Add it as a child of this node. - - .. code-tab:: csharp - - private Sprite _sprite; - - public override void _Ready() - { - base._Ready(); - - _sprite = new Sprite(); // Create a new sprite! - AddChild(_sprite); // Add it as a child of this node. - } - -To delete a node, be it inside or outside the scene, ``free()`` must be -used: - -.. tabs:: - .. code-tab:: gdscript GDScript - - func _someaction(): - s.free() # Immediately removes the node from the scene and frees it. - - .. code-tab:: csharp - - public void _SomeAction() - { - _sprite.Free(); // Immediately removes the node from the scene and frees it. - } - -When a node is freed, it also frees all its child nodes. Because of -this, manually deleting nodes is much simpler than it appears. Free -the base node and everything else in the subtree goes away with it. - -A situation might occur where we want to delete a node that -is currently "blocked", because it is emitting a signal or calling a -function. This will crash the game. Running Godot -with the debugger will often catch this case and warn you about it. - -The safest way to delete a node is by using -:ref:`Node.queue_free() `. -This erases the node safely during idle. - -.. tabs:: - .. code-tab:: gdscript GDScript - - func _someaction(): - s.queue_free() # Removes the node from the scene and frees it when it becomes safe to do so. - - .. code-tab:: csharp - - public void _SomeAction() - { - _sprite.QueueFree(); // Removes the node from the scene and frees it when it becomes safe to do so. - } - -Instancing scenes ------------------ - -Instancing a scene from code is done in two steps. The -first one is to load the scene from your hard drive: - -.. tabs:: - .. code-tab:: gdscript GDScript - - var scene = load("res://myscene.tscn") # Will load when the script is instanced. - - .. code-tab:: csharp - - var scene = GD.Load("res://myscene.tscn"); // Will load when the script is instanced. - - -Preloading it can be more convenient, as it happens at parse -time (GDScript only): - -.. tabs:: - .. code-tab:: gdscript GDScript - - var scene = preload("res://myscene.tscn") # Will load when parsing the script. - -But ``scene`` is not yet a node. It's packed in a -special resource called :ref:`PackedScene `. -To create the actual node, the function -:ref:`PackedScene.instance() ` -must be called. This will return the tree of nodes that can be added to -the active scene: - -.. tabs:: - .. code-tab:: gdscript GDScript - - var node = scene.instance() - add_child(node) - - .. code-tab:: csharp - - var node = scene.Instance(); - AddChild(node); - -The advantage of this two-step process is that a packed scene may be -kept loaded and ready to use so that you can create as many -instances as desired. This is especially useful to quickly instance -several enemies, bullets, and other entities in the active scene. - .. _doc_scripting_continued_class_name: Register scripts as classes diff --git a/tutorials/scripting/img/nodes_and_scene_instances_player_scene_example.png b/tutorials/scripting/img/nodes_and_scene_instances_player_scene_example.png new file mode 100644 index 000000000..2cc447257 Binary files /dev/null and b/tutorials/scripting/img/nodes_and_scene_instances_player_scene_example.png differ diff --git a/tutorials/scripting/img/nodes_and_scene_instances_ui_scene_example.png b/tutorials/scripting/img/nodes_and_scene_instances_ui_scene_example.png new file mode 100644 index 000000000..f6c1c3d4d Binary files /dev/null and b/tutorials/scripting/img/nodes_and_scene_instances_ui_scene_example.png differ diff --git a/tutorials/scripting/index.rst b/tutorials/scripting/index.rst index 9c5352d92..8e53b5b39 100644 --- a/tutorials/scripting/index.rst +++ b/tutorials/scripting/index.rst @@ -37,6 +37,7 @@ below will help you make the most of Godot. debug/index idle_and_physics_processing groups + nodes_and_scene_instances cross_language_scripting creating_script_templates change_scenes_manually diff --git a/tutorials/scripting/nodes_and_scene_instances.rst b/tutorials/scripting/nodes_and_scene_instances.rst new file mode 100644 index 000000000..cfb07dd5e --- /dev/null +++ b/tutorials/scripting/nodes_and_scene_instances.rst @@ -0,0 +1,205 @@ +.. _doc_nodes_and_scene_instances: + +Nodes and scene instances +========================= + +This guide explains how to get nodes, create nodes, add them as a child, and +instantiate scenes from code. + +Getting nodes +------------- + +You can get a reference to a node by calling the :ref:`Node.get_node() +` method. For this to work, the child node must be +present in the scene tree. Getting it in the parent node's ``_ready()`` function +guarantees that. + +Say you have a scene tree like this, and you want to get a reference to the +Sprite and Camera2D nodes to access them in your script. + +.. image:: img/nodes_and_scene_instances_player_scene_example.png + +To do so, you can use the following code. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var sprite + var camera2d + + func _ready(): + sprite = get_node("Sprite") + camera2d = get_node("Camera2D") + + .. code-tab:: csharp + + private Sprite _sprite; + private Camera2D _camera2d; + + public override void _Ready() + { + base._Ready(); + + _sprite = GetNode("Sprite"); + _camera2d = GetNode("Camera2D"); + } + +Note that you get nodes by their name. For example, if you rename the Sprite +node into Skin, the call to get it would have to be ``get_node("Skin")``. + +Node paths +---------- + +You're not limited to getting a direct child. The ``get_node()`` function +supports paths, a bit like when working with a file browser. Add a slash to +separate nodes. + +Take the following example scene, with the script attached to the UserInterface +node. + +.. image:: img/nodes_and_scene_instances_ui_scene_example.png + +To get the Tween node, you would use the following code. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var tween + + func _ready(): + tween = get_node("ShieldBar/Tween") + + .. code-tab:: csharp + + private Tween _tween; + + public override void _Ready() + { + base._Ready(); + + _tween = GetNode("ShieldBar/Tween"); + } + +.. note:: As with file paths, you can use ".." to get a parent node. The best + practice is to avoid doing that though not to break encapsulation and + keep your code organized. You can also start the path with a forward + slash to make it absolute, in which case your topmost node would be + "/root", the application's predefined root viewport. + +Syntactic sugar +~~~~~~~~~~~~~~~ + +You can use two shorthands to shorten your code in GDScript: putting the +``onready`` keyword before a member variable makes it initialize right before +the ``_ready()`` callback. + +.. code-block:: gdscript + + onready var sprite = get_node("Sprite") + +There is also a short notation for ``get_node()``: the dollar sign, "$". You +place it before the name or path of the node you want to get. + +.. code-block:: gdscript + + onready var sprite = $Sprite + onready var tween = $ShieldBar/Tween + +Creating nodes +-------------- + +To create a node from code, call its ``new()`` method like for any other +class-based datatype. + +You can store the newly created node's reference in a variable and call +``add_child()`` to add it as a child of the node to which you attached the +script. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var sprite + + func _ready(): + var sprite = Sprite.new() # Create a new Sprite. + add_child(sprite) # Add it as a child of this node. + + .. code-tab:: csharp + + private Sprite _sprite; + + public override void _Ready() + { + base._Ready(); + + _sprite = new Sprite(); // Create a new Sprite. + AddChild(_sprite); // Add it as a child of this node. + } + +To delete a node and free it from memory, you can call its ``queue_free()`` +method. Doing so queues the node for deletion at the end of the current frame +after it finished processing. At that point, the engine removes the node from +the scene and frees the object in memory. + +.. tabs:: + .. code-tab:: gdscript GDScript + + sprite.queue_free() + + .. code-tab:: csharp + + _sprite.QueueFree(); + +You can alternatively call ``free()`` to immediately destroy the node. You +should do this with care as any reference to it will instantly become ``null``. +We recommend using ``queue_free()`` unless you know what you're doing. + +When you free a node, it also frees all its children. Thanks to this, to delete +an entire branch of the scene tree, you only have to free the topmost parent +node. + +Instancing scenes +----------------- + +Instancing a scene from code happens in two steps: + +1. Loading the scene from the hard drive. +2. Creating an instance of the loaded :ref:`PackedScene ` + resource. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var scene = load("res://MyScene.tscn") + + .. code-tab:: csharp + + var scene = GD.Load("res://MyScene.tscn"); + +Preloading it can improve the user's experience as it happens when parsing the +script. This feature is only available with GDScript. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var scene = preload("res://MyScene.tscn") + +At that point, ``scene`` is a packed scene resource, not a node. To create the +actual node, you need to call :ref:`PackedScene.instance() +`. It returns a tree of nodes that you can +add as a child. + +.. tabs:: + .. code-tab:: gdscript GDScript + + var instance = scene.instance() + add_child(instance) + + .. code-tab:: csharp + + var instance = scene.(); + AddChild(instance); + +The advantage of this two-step process is you can keep a packed scene loaded and +create new instances on the fly. For example, to quickly instance several +enemies or bullets.