Move remaining files out of step_by_step/
Moved those to scripting/: - filesystem - resources - singletons_autoload - scene_tree export to export/ and godot_design_philosophy to introduction/
BIN
tutorials/scripting/files/autoload.zip
Normal file
111
tutorials/scripting/filesystem.rst
Normal file
@@ -0,0 +1,111 @@
|
||||
.. _doc_filesystem:
|
||||
|
||||
File system
|
||||
===========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
A file system manages how assets are stored and how they are accessed.
|
||||
A well-designed file system also allows multiple developers to edit the
|
||||
same source files and assets while collaborating. Godot stores
|
||||
all assets as files in its file system.
|
||||
|
||||
Implementation
|
||||
--------------
|
||||
|
||||
The file system stores resources on disk. Anything, from a script, to a scene or a
|
||||
PNG image is a resource to the engine. If a resource contains properties
|
||||
that reference other resources on disk, the paths to those resources are also
|
||||
included. If a resource has sub-resources that are built-in, the resource is
|
||||
saved in a single file together with all the bundled sub-resources. For
|
||||
example, a font resource is often bundled together with the font textures.
|
||||
|
||||
The Godot file system avoids using metadata files. Existing asset managers and VCSs
|
||||
are better than anything we can implement, so Godot tries its best to play along
|
||||
with SVN, Git, Mercurial, Perforce, etc.
|
||||
|
||||
Example of file system contents:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
/project.godot
|
||||
/enemy/enemy.tscn
|
||||
/enemy/enemy.gd
|
||||
/enemy/enemysprite.png
|
||||
/player/player.gd
|
||||
|
||||
project.godot
|
||||
-------------
|
||||
|
||||
The ``project.godot`` file is the project description file, and it is always found
|
||||
at the root of the project. In fact, its location defines where the root is. This
|
||||
is the first file that Godot looks for when opening a project.
|
||||
|
||||
This file contains the project configuration in plain text, using the win.ini
|
||||
format. Even an empty ``project.godot`` can function as a basic definition of
|
||||
a blank project.
|
||||
|
||||
Path delimiter
|
||||
--------------
|
||||
|
||||
Godot only supports ``/`` as a path delimiter. This is done for
|
||||
portability reasons. All operating systems support this, even Windows,
|
||||
so a path such as ``C:\project\project.godot`` needs to be typed as
|
||||
``C:/project/project.godot``.
|
||||
|
||||
Resource path
|
||||
-------------
|
||||
|
||||
When accessing resources, using the host OS file system layout can be
|
||||
cumbersome and non-portable. To solve this problem, the special path
|
||||
``res://`` was created.
|
||||
|
||||
The path ``res://`` will always point at the project root (where
|
||||
``project.godot`` is located, so ``res://project.godot`` is always
|
||||
valid).
|
||||
|
||||
This file system is read-write only when running the project locally from
|
||||
the editor. When exported or when running on different devices (such as
|
||||
phones or consoles, or running from DVD), the file system will become
|
||||
read-only and writing will no longer be permitted.
|
||||
|
||||
User path
|
||||
---------
|
||||
|
||||
Writing to disk is still needed for tasks such as saving game state or
|
||||
downloading content packs. To this end, the engine ensures that there is a
|
||||
special path ``user://`` that is always writable. This path resolves
|
||||
differently depending on the OS the project is running on. Local path
|
||||
resolution is further explained in :ref:`doc_data_paths`.
|
||||
|
||||
Host file system
|
||||
----------------
|
||||
|
||||
Alternatively host file system paths can also be used, but this is not recommended
|
||||
for a released product as these paths are not guaranteed to work on all platforms.
|
||||
However, using host file system paths can be useful when writing development
|
||||
tools in Godot.
|
||||
|
||||
Drawbacks
|
||||
---------
|
||||
|
||||
There are some drawbacks to this simple file system design. The first issue is that
|
||||
moving assets around (renaming them or moving them from one path to another inside
|
||||
the project) will break existing references to these assets. These references will
|
||||
have to be re-defined to point at the new asset location.
|
||||
|
||||
To avoid this, do all your move, delete and rename operations from within Godot, on
|
||||
the FileSystem dock. Never move assets from outside Godot, or dependencies will have
|
||||
to be fixed manually (Godot detects this and helps you fix them anyway, but why
|
||||
go the hard route?).
|
||||
|
||||
The second is that, under Windows and macOS, file and path names are case insensitive.
|
||||
If a developer working in a case insensitive host file system saves an asset as ``myfile.PNG``,
|
||||
but then references it as ``myfile.png``, it will work fine on their platform, but not
|
||||
on other platforms, such as Linux, Android, etc. This may also apply to exported binaries,
|
||||
which use a compressed package to store all files.
|
||||
|
||||
It is recommended that your team clearly define a naming convention for files when
|
||||
working with Godot. One simple fool-proof convention is to only allow lowercase
|
||||
file and path names.
|
||||
BIN
tutorials/scripting/img/activescene.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
tutorials/scripting/img/autoload_example.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
tutorials/scripting/img/autoload_runtime.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
tutorials/scripting/img/autoload_script.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
tutorials/scripting/img/autoload_tab.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
tutorials/scripting/img/autoload_tutorial1.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
tutorials/scripting/img/nodes_resources.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
tutorials/scripting/img/resourcerobi.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
tutorials/scripting/img/singleton.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
tutorials/scripting/img/spriteprop.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
tutorials/scripting/img/toptobottom.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
@@ -30,6 +30,8 @@ Some features are specific to the engine and are available in all supported
|
||||
languages. Whether you code in GDScript, C#, or another language, the pages
|
||||
below will help you make the most of Godot.
|
||||
|
||||
.. To split and organize better, into some related toctrees?
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-scripting-core-features
|
||||
@@ -45,3 +47,7 @@ below will help you make the most of Godot.
|
||||
change_scenes_manually
|
||||
instancing_with_signals
|
||||
pausing_games
|
||||
filesystem
|
||||
resources
|
||||
singletons_autoload
|
||||
scene_tree
|
||||
|
||||
368
tutorials/scripting/resources.rst
Normal file
@@ -0,0 +1,368 @@
|
||||
.. _doc_resources:
|
||||
|
||||
Resources
|
||||
=========
|
||||
|
||||
Nodes and resources
|
||||
-------------------
|
||||
|
||||
Up to this tutorial, we focused on the :ref:`Node <class_Node>`
|
||||
class in Godot as that's the one you use to code behavior and
|
||||
most of the engine's features rely on it. There is
|
||||
another datatype that is just as important:
|
||||
:ref:`Resource <class_Resource>`.
|
||||
|
||||
*Nodes* give you functionality: they draw sprites, 3D models, simulate physics,
|
||||
arrange user interfaces, etc. **Resources** are **data containers**. They don't
|
||||
do anything on their own: instead, nodes use the data contained in resources.
|
||||
|
||||
Anything Godot saves or loads from disk is a resource. Be it a scene (a ``.tscn``
|
||||
or an ``.scn`` file), an image, a script... Here are some ``Resource`` examples:
|
||||
:ref:`Texture <class_Texture>`, :ref:`Script <class_Script>`, :ref:`Mesh
|
||||
<class_Mesh>`, :ref:`Animation <class_Animation>`, :ref:`AudioStream
|
||||
<class_AudioStream>`, :ref:`Font <class_Font>`, :ref:`Translation
|
||||
<class_Translation>`.
|
||||
|
||||
When the engine loads a resource from disk, **it only loads it once**. If a copy
|
||||
of that resource is already in memory, trying to load the resource again will
|
||||
return the same copy every time. As resources only contain data, there is no need
|
||||
to duplicate them.
|
||||
|
||||
Every object, be it a Node or a Resource, can export properties. There are many
|
||||
types of Properties, like String, integer, Vector2, etc., and any of these types
|
||||
can become a resource. This means that both nodes and resources can contain
|
||||
resources as properties:
|
||||
|
||||
.. image:: img/nodes_resources.png
|
||||
|
||||
External vs built-in
|
||||
--------------------
|
||||
|
||||
There are two ways to save resources. They can be:
|
||||
|
||||
1. **External** to a scene, saved on the disk as individual files.
|
||||
2. **Built-in**, saved inside the ``.tscn`` or the ``.scn`` file they're attached to.
|
||||
|
||||
To be more specific, here's a :ref:`Texture <class_Texture>`
|
||||
in a :ref:`Sprite <class_Sprite>` node:
|
||||
|
||||
.. image:: img/spriteprop.png
|
||||
|
||||
Clicking the resource preview allows us to view and edit the resource's properties.
|
||||
|
||||
.. image:: img/resourcerobi.png
|
||||
|
||||
The path property tells us where the resource comes from. In this case, it comes
|
||||
from a PNG image called ``robi.png``. When the resource comes from a file like
|
||||
this, it is an external resource. If you erase the path or this path is empty,
|
||||
it becomes a built-in resource.
|
||||
|
||||
The switch between built-in and external resources happens when you save the
|
||||
scene. In the example above, if you erase the path ``"res://robi.png"`` and
|
||||
save, Godot will save the image inside the ``.tscn`` scene file.
|
||||
|
||||
.. note::
|
||||
|
||||
Even if you save a built-in resource, when you instance a scene multiple
|
||||
times, the engine will only load one copy of it.
|
||||
|
||||
Loading resources from code
|
||||
---------------------------
|
||||
|
||||
There are two ways to load resources from code. First, you can use the ``load()`` function anytime:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _ready():
|
||||
var res = load("res://robi.png") # Godot loads the Resource when it reads the line.
|
||||
get_node("sprite").texture = res
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
var texture = (Texture)GD.Load("res://robi.png"); // Godot loads the Resource when it reads the line.
|
||||
var sprite = (Sprite)GetNode("sprite");
|
||||
sprite.Texture = texture;
|
||||
}
|
||||
|
||||
You can also ``preload`` resources. Unlike ``load``, this function will read the
|
||||
file from disk and load it at compile-time. As a result, you cannot call preload
|
||||
with a variable path: you need to use a constant string.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _ready():
|
||||
var res = preload("res://robi.png") # Godot loads the resource at compile-time
|
||||
get_node("sprite").texture = res
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// 'preload()' is unavailable in C Sharp.
|
||||
|
||||
Loading scenes
|
||||
--------------
|
||||
|
||||
Scenes are also resources, but there is a catch. Scenes saved to disk are
|
||||
resources of type :ref:`PackedScene <class_PackedScene>`. The
|
||||
scene is packed inside a resource.
|
||||
|
||||
To get an instance of the scene, you have to use the
|
||||
:ref:`PackedScene.instance() <class_PackedScene_method_instance>` method.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _on_shoot():
|
||||
var bullet = preload("res://bullet.tscn").instance()
|
||||
add_child(bullet)
|
||||
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
private PackedScene _bulletScene = (PackedScene)GD.Load("res://bullet.tscn");
|
||||
|
||||
public void OnShoot()
|
||||
{
|
||||
Node bullet = _bulletScene.Instance();
|
||||
AddChild(bullet);
|
||||
}
|
||||
|
||||
This method creates the nodes in the scene's hierarchy, configures them, and
|
||||
returns the root node of the scene. You can then add it as a child of any other
|
||||
node.
|
||||
|
||||
The approach has several advantages. As the :ref:`PackedScene.instance()
|
||||
<class_PackedScene_method_instance>` function is fast, you can create new
|
||||
enemies, bullets, effects, etc. without having to load them again from disk each
|
||||
time. Remember that, as always, images, meshes, etc. are all shared between the
|
||||
scene instances.
|
||||
|
||||
Freeing resources
|
||||
-----------------
|
||||
|
||||
When a ``Resource`` is no longer in use, it will automatically free itself.
|
||||
Since, in most cases, Resources are contained in Nodes, when you free a node,
|
||||
the engine frees all the resources it owns as well if no other node uses them.
|
||||
|
||||
Creating your own resources
|
||||
---------------------------
|
||||
|
||||
Like any Object in Godot, users can also script Resources. Resource scripts
|
||||
inherit the ability to freely translate between object properties and serialized
|
||||
text or binary data (\*.tres, \*.res). They also inherit the reference-counting
|
||||
memory management from the Reference type.
|
||||
|
||||
This comes with many distinct advantages over alternative data
|
||||
structures, such as JSON, CSV, or custom TXT files. Users can only import these
|
||||
assets as a :ref:`Dictionary <class_Dictionary>` (JSON) or as a
|
||||
:ref:`File <class_File>` to parse. What sets Resources apart is their
|
||||
inheritance of :ref:`Object <class_Object>`, :ref:`Reference <class_Reference>`,
|
||||
and :ref:`Resource <class_Resource>` features:
|
||||
|
||||
- They can define constants, so constants from other data fields or objects are not needed.
|
||||
|
||||
- They can define methods, including setter/getter methods for properties. This allows for abstraction and encapsulation of the underlying data. If the Resource script's structure needs to change, the game using the Resource need not also change.
|
||||
|
||||
- They can define signals, so Resources can trigger responses to changes in the data they manage.
|
||||
|
||||
- They have defined properties, so users know 100% that their data will exist.
|
||||
|
||||
- Resource auto-serialization and deserialization is a built-in Godot Engine feature. Users do not need to implement custom logic to import/export a resource file's data.
|
||||
|
||||
- Resources can even serialize sub-Resources recursively, meaning users can design even more sophisticated data structures.
|
||||
|
||||
- Users can save Resources as version-control-friendly text files (\*.tres). Upon exporting a game, Godot serializes resource files as binary files (\*.res) for increased speed and compression.
|
||||
|
||||
- Godot Engine's Inspector renders and edits Resource files out-of-the-box. As such, users often do not need to implement custom logic to visualize or edit their data. To do so, double-click the resource file in the FileSystem dock or click the folder icon in the Inspector and open the file in the dialog.
|
||||
|
||||
- They can extend **other** resource types besides just the base Resource.
|
||||
|
||||
Godot makes it easy to create custom Resources in the Inspector.
|
||||
|
||||
1. Create a plain Resource object in the Inspector. This can even be a type that derives Resource, so long as your script is extending that type.
|
||||
2. Set the ``script`` property in the Inspector to be your script.
|
||||
|
||||
The Inspector will now display your Resource script's custom properties. If one edits
|
||||
those values and saves the resource, the Inspector serializes the custom properties
|
||||
too! To save a resource from the Inspector, click the Inspector's tools menu (top right),
|
||||
and select "Save" or "Save As...".
|
||||
|
||||
If the script's language supports :ref:`script classes <doc_gdscript_basics_class_name>`,
|
||||
then it streamlines the process. Defining a name for your script alone will add it to
|
||||
the Inspector's creation dialog. This will auto-add your script to the Resource
|
||||
object you create.
|
||||
|
||||
Let's see some examples.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# bot_stats.gd
|
||||
extends Resource
|
||||
export(int) var health
|
||||
export(Resource) var sub_resource
|
||||
export(Array, String) var strings
|
||||
|
||||
func _init(p_health = 0, p_sub_resource = null, p_strings = []):
|
||||
health = p_health
|
||||
sub_resource = p_sub_resource
|
||||
strings = p_strings
|
||||
|
||||
# bot.gd
|
||||
extends KinematicBody
|
||||
|
||||
export(Resource) var stats
|
||||
|
||||
func _ready():
|
||||
# Uses an implicit, duck-typed interface for any 'health'-compatible resources.
|
||||
if stats:
|
||||
print(stats.health) # Prints '10'.
|
||||
.. code-tab:: csharp
|
||||
|
||||
// BotStats.cs
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
namespace ExampleProject {
|
||||
public class BotStats : Resource
|
||||
{
|
||||
[Export]
|
||||
public int Health { get; set; }
|
||||
|
||||
[Export]
|
||||
public Resource SubResource { get; set; }
|
||||
|
||||
[Export]
|
||||
public String[] Strings { get; set; }
|
||||
|
||||
public BotStats(int health = 0, Resource subResource = null, String[] strings = null)
|
||||
{
|
||||
Health = health;
|
||||
SubResource = subResource;
|
||||
Strings = strings ?? new String[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bot.cs
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
namespace ExampleProject {
|
||||
public class Bot : KinematicBody
|
||||
{
|
||||
[Export]
|
||||
public Resource Stats;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (Stats != null && Stats is BotStats botStats) {
|
||||
GD.Print(botStats.Health); // Prints '10'.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Resource scripts are similar to Unity's ScriptableObjects. The Inspector
|
||||
provides built-in support for custom resources. If desired though, users
|
||||
can even design their own Control-based tool scripts and combine them
|
||||
with an :ref:`EditorPlugin <class_EditorPlugin>` to create custom
|
||||
visualizations and editors for their data.
|
||||
|
||||
Unreal Engine 4's DataTables and CurveTables are also easy to recreate with
|
||||
Resource scripts. DataTables are a String mapped to a custom struct, similar
|
||||
to a Dictionary mapping a String to a secondary custom Resource script.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# bot_stats_table.gd
|
||||
extends Resource
|
||||
|
||||
const BotStats = preload("bot_stats.gd")
|
||||
|
||||
var data = {
|
||||
"GodotBot": BotStats.new(10), # Creates instance with 10 health.
|
||||
"DifferentBot": BotStats.new(20) # A different one with 20 health.
|
||||
}
|
||||
|
||||
func _init():
|
||||
print(data)
|
||||
.. code-tab:: csharp
|
||||
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
public class BotStatsTable : Resource
|
||||
{
|
||||
private Godot.Dictionary<String, BotStats> _stats = new Godot.Dictionary<String, BotStats>();
|
||||
|
||||
public BotStatsTable()
|
||||
{
|
||||
_stats["GodotBot"] = new BotStats(10); // Creates instance with 10 health.
|
||||
_stats["DifferentBot"] = new BotStats(20); // A different one with 20 health.
|
||||
GD.Print(_stats);
|
||||
}
|
||||
}
|
||||
|
||||
Instead of just inlining the Dictionary values, one could also, alternatively...
|
||||
|
||||
1. Import a table of values from a spreadsheet and generate these key-value pairs, or...
|
||||
|
||||
2. Design a visualization within the editor and create a simple plugin that adds it
|
||||
to the Inspector when you open these types of Resources.
|
||||
|
||||
CurveTables are the same thing, except mapped to an Array of float values
|
||||
or a :ref:`Curve <class_Curve>`/:ref:`Curve2D <class_Curve2D>` resource object.
|
||||
|
||||
.. warning::
|
||||
|
||||
Beware that resource files (\*.tres/\*.res) will store the path of the script
|
||||
they use in the file. When loaded, they will fetch and load this script as an
|
||||
extension of their type. This means that trying to assign a subclass, i.e. an
|
||||
inner class of a script (such as using the ``class`` keyword in GDScript) won't
|
||||
work. Godot will not serialize the custom properties on the script subclass properly.
|
||||
|
||||
In the example below, Godot would load the ``Node`` script, see that it doesn't
|
||||
extend ``Resource``, and then determine that the script failed to load for the
|
||||
Resource object since the types are incompatible.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Node
|
||||
|
||||
class MyResource:
|
||||
extends Resource
|
||||
export var value = 5
|
||||
|
||||
func _ready():
|
||||
var my_res = MyResource.new()
|
||||
|
||||
# This will NOT serialize the 'value' property.
|
||||
ResourceSaver.save("res://my_res.tres", my_res)
|
||||
.. code-tab:: csharp
|
||||
using System;
|
||||
using Godot;
|
||||
|
||||
public class MyNode : Node
|
||||
{
|
||||
public class MyResource : Resource
|
||||
{
|
||||
[Export]
|
||||
public int Value { get; set; } = 5;
|
||||
}
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
var res = new MyResource();
|
||||
|
||||
// This will NOT serialize the 'Value' property.
|
||||
ResourceSaver.Save("res://MyRes.tres", res);
|
||||
}
|
||||
}
|
||||
179
tutorials/scripting/scene_tree.rst
Normal file
@@ -0,0 +1,179 @@
|
||||
.. _doc_scene_tree:
|
||||
|
||||
SceneTree
|
||||
=========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In previous tutorials, everything revolved around the concept of
|
||||
nodes. Scenes are collections of nodes. They become active once
|
||||
they enter the *scene tree*.
|
||||
|
||||
MainLoop
|
||||
--------
|
||||
|
||||
The way Godot works internally is as follows. There is the
|
||||
:ref:`OS <class_OS>` class,
|
||||
which is the only instance that runs at the beginning. Afterwards, all
|
||||
drivers, servers, scripting languages, scene system, etc are loaded.
|
||||
|
||||
When initialization is complete, :ref:`OS <class_OS>` needs to be
|
||||
supplied a :ref:`MainLoop <class_MainLoop>`
|
||||
to run. Up to this point, all this is internals working (you can check
|
||||
main/main.cpp file in the source code if you are ever interested to
|
||||
see how this works internally).
|
||||
|
||||
The user program, or game, starts in the MainLoop. This class has a few
|
||||
methods, for initialization, idle (frame-synchronized callback), fixed
|
||||
(physics-synchronized callback), and input. Again, this is low
|
||||
level and when making games in Godot, writing your own MainLoop seldom makes sense.
|
||||
|
||||
SceneTree
|
||||
---------
|
||||
|
||||
One of the ways to explain how Godot works is that it's a high level
|
||||
game engine over a low level middleware.
|
||||
|
||||
The scene system is the game engine, while the :ref:`OS <class_OS>`
|
||||
and servers are the low level API.
|
||||
|
||||
The scene system provides its own main loop to OS,
|
||||
:ref:`SceneTree <class_SceneTree>`.
|
||||
This is automatically instanced and set when running a scene, no need
|
||||
to do any extra work.
|
||||
|
||||
It's important to know that this class exists because it has a few
|
||||
important uses:
|
||||
|
||||
- It contains the root :ref:`Viewport <class_Viewport>`, to which a
|
||||
scene is added as a child when it's first opened to become
|
||||
part of the *Scene Tree* (more on that next).
|
||||
- It contains information about the groups and has the means to call all
|
||||
nodes in a group or get a list of them.
|
||||
- It contains some global state functionality, such as setting pause
|
||||
mode or quitting the process.
|
||||
|
||||
When a node is part of the Scene Tree, the
|
||||
:ref:`SceneTree <class_SceneTree>`
|
||||
singleton can be obtained by calling
|
||||
:ref:`Node.get_tree() <class_Node_method_get_tree>`.
|
||||
|
||||
Root viewport
|
||||
-------------
|
||||
|
||||
The root :ref:`Viewport <class_Viewport>`
|
||||
is always at the top of the scene. From a node, it can be obtained in
|
||||
two different ways:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
get_tree().get_root() # Access via scene main loop.
|
||||
get_node("/root") # Access via absolute path.
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
GetTree().GetRoot(); // Access via scene main loop.
|
||||
GetNode("/root"); // Access via absolute path.
|
||||
|
||||
This node contains the main viewport. Anything that is a child of a
|
||||
:ref:`Viewport <class_Viewport>`
|
||||
is drawn inside of it by default, so it makes sense that the top of all
|
||||
nodes is always a node of this type otherwise nothing would be seen.
|
||||
|
||||
While other viewports can be created in the scene (for split-screen
|
||||
effects and such), this one is the only one that is never created by the
|
||||
user. It's created automatically inside SceneTree.
|
||||
|
||||
Scene tree
|
||||
----------
|
||||
|
||||
When a node is connected, directly or indirectly, to the root
|
||||
viewport, it becomes part of the *scene tree*.
|
||||
|
||||
This means that as explained in previous tutorials, it will get the
|
||||
_enter_tree() and _ready() callbacks (as well as _exit_tree()).
|
||||
|
||||
.. image:: img/activescene.png
|
||||
|
||||
When nodes enter the *Scene Tree*, they become active. They get access
|
||||
to everything they need to process, get input, display 2D and 3D visuals,
|
||||
receive and send notifications, play sounds, etc. When they are removed from the
|
||||
*scene tree*, they lose these abilities.
|
||||
|
||||
Tree order
|
||||
----------
|
||||
|
||||
Most node operations in Godot, such as drawing 2D, processing, or getting
|
||||
notifications are done in tree order. This means that parents and
|
||||
siblings with a lower rank in the tree order will get notified before
|
||||
the current node.
|
||||
|
||||
.. image:: img/toptobottom.png
|
||||
|
||||
"Becoming active" by entering the *Scene Tree*
|
||||
----------------------------------------------
|
||||
|
||||
#. A scene is loaded from disk or created by scripting.
|
||||
#. The root node of that scene (only one root, remember?) is added as
|
||||
either a child of the "root" Viewport (from SceneTree), or to any
|
||||
of its descendants.
|
||||
#. Every node of the newly added scene, will receive the "enter_tree"
|
||||
notification ( ``_enter_tree()`` callback in GDScript) in
|
||||
top-to-bottom order.
|
||||
#. An extra notification, "ready" ( ``_ready()`` callback in GDScript)
|
||||
is provided for convenience, when a node and all its children are
|
||||
inside the active scene.
|
||||
#. When a scene (or part of it) is removed, they receive the "exit
|
||||
scene" notification ( ``_exit_tree()`` callback in GDScript) in
|
||||
bottom-to-top order
|
||||
|
||||
Changing current scene
|
||||
----------------------
|
||||
|
||||
After a scene is loaded, it is often desired to change this scene for
|
||||
another one. The simple way to do this is to use the
|
||||
:ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>`
|
||||
function:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _my_level_was_completed():
|
||||
get_tree().change_scene("res://levels/level2.tscn")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void _MyLevelWasCompleted()
|
||||
{
|
||||
GetTree().ChangeScene("res://levels/level2.tscn");
|
||||
}
|
||||
|
||||
Rather than using file paths, one can also use ready-made
|
||||
:ref:`PackedScene <class_PackedScene>` resources using the equivalent
|
||||
function
|
||||
:ref:`SceneTree.change_scene_to(PackedScene scene) <class_SceneTree_method_change_scene_to>`:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var next_scene = preload("res://levels/level2.tscn")
|
||||
|
||||
func _my_level_was_completed():
|
||||
get_tree().change_scene_to(next_scene)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void _MyLevelWasCompleted()
|
||||
{
|
||||
var nextScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn");
|
||||
GetTree().ChangeSceneTo(nextScene);
|
||||
}
|
||||
|
||||
These are quick and useful ways to switch scenes but have the drawback
|
||||
that the game will stall until the new scene is loaded and running. At
|
||||
some point in the development of your game, it may be preferable to create proper loading
|
||||
screens with progress bar, animated indicators or thread (background)
|
||||
loading. This must be done manually using autoloads (see next chapter)
|
||||
and :ref:`doc_background_loading`.
|
||||
293
tutorials/scripting/singletons_autoload.rst
Normal file
@@ -0,0 +1,293 @@
|
||||
.. _doc_singletons_autoload:
|
||||
|
||||
Singletons (AutoLoad)
|
||||
=====================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Godot's scene system, while powerful and flexible, has a drawback: there is no
|
||||
method for storing information (e.g. a player's score or inventory) that is
|
||||
needed by more than one scene.
|
||||
|
||||
It's possible to address this with some workarounds, but they come with their
|
||||
own limitations:
|
||||
|
||||
- You can use a "master" scene that loads and unloads other scenes as
|
||||
its children. However, this means you can no longer run those scenes
|
||||
individually and expect them to work correctly.
|
||||
- Information can be stored to disk in ``user://`` and then loaded by scenes
|
||||
that require it, but frequently saving and loading data is cumbersome and
|
||||
may be slow.
|
||||
|
||||
The `Singleton pattern <https://en.wikipedia.org/wiki/Singleton_pattern>`_ is
|
||||
a useful tool for solving the common use case where you need to store
|
||||
persistent information between scenes. In our case, it's possible to reuse the
|
||||
same scene or class for multiple singletons as long as they have different
|
||||
names.
|
||||
|
||||
Using this concept, you can create objects that:
|
||||
|
||||
- Are always loaded, no matter which scene is currently running.
|
||||
- Can store global variables such as player information.
|
||||
- Can handle switching scenes and between-scene transitions.
|
||||
- *Act* like a singleton, since GDScript does not support global variables by design.
|
||||
|
||||
Autoloading nodes and scripts can give us these characteristics.
|
||||
|
||||
.. note::
|
||||
|
||||
Godot won't make an AutoLoad a "true" singleton as per the singleton design
|
||||
pattern. It may still be instanced more than once by the user if desired.
|
||||
|
||||
AutoLoad
|
||||
--------
|
||||
|
||||
You can create an AutoLoad to load a scene or a script that inherits from
|
||||
:ref:`class_Node`.
|
||||
|
||||
.. note::
|
||||
|
||||
When autoloading a script, a :ref:`class_Node` will be created and the script will be
|
||||
attached to it. This node will be added to the root viewport before any
|
||||
other scenes are loaded.
|
||||
|
||||
.. image:: img/singleton.png
|
||||
|
||||
To autoload a scene or script, select **Project > Project Settings** from the
|
||||
menu and switch to the **AutoLoad** tab.
|
||||
|
||||
.. image:: img/autoload_tab.png
|
||||
|
||||
Here you can add any number of scenes or scripts. Each entry in the list
|
||||
requires a name, which is assigned as the node's ``name`` property. The order of
|
||||
the entries as they are added to the global scene tree can be manipulated using
|
||||
the up/down arrow keys.
|
||||
|
||||
.. image:: img/autoload_example.png
|
||||
|
||||
This means that any node can access a singleton named "PlayerVariables" with:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var player_vars = get_node("/root/PlayerVariables")
|
||||
player_vars.health -= 10
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
var playerVariables = (PlayerVariables)GetNode("/root/PlayerVariables");
|
||||
playerVariables.Health -= 10; // Instance field.
|
||||
|
||||
If the **Enable** column is checked (which is the default), then the singleton can
|
||||
be accessed directly without requiring ``get_node()``:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
PlayerVariables.health -= 10
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Static members can be accessed by using the class name.
|
||||
PlayerVariables.Health -= 10;
|
||||
|
||||
Note that autoload objects (scripts and/or scenes) are accessed just like any
|
||||
other node in the scene tree. In fact, if you look at the running scene tree,
|
||||
you'll see the autoloaded nodes appear:
|
||||
|
||||
.. image:: img/autoload_runtime.png
|
||||
|
||||
Custom scene switcher
|
||||
---------------------
|
||||
|
||||
This tutorial will demonstrate building a scene switcher using autoloads.
|
||||
For basic scene switching, you can use the
|
||||
:ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>`
|
||||
method (see :ref:`doc_scene_tree` for details). However, if you need more
|
||||
complex behavior when changing scenes, this method provides more functionality.
|
||||
|
||||
To begin, download the template from here:
|
||||
:download:`autoload.zip <files/autoload.zip>` and open it in Godot.
|
||||
|
||||
The project contains two scenes: ``Scene1.tscn`` and ``Scene2.tscn``. Each
|
||||
scene contains a label displaying the scene name and a button with its
|
||||
``pressed()`` signal connected. When you run the project, it starts in
|
||||
``Scene1.tscn``. However, pressing the button does nothing.
|
||||
|
||||
Global.gd
|
||||
~~~~~~~~~
|
||||
|
||||
Switch to the **Script** tab and create a new script called ``Global.gd``.
|
||||
Make sure it inherits from ``Node``:
|
||||
|
||||
.. image:: img/autoload_script.png
|
||||
|
||||
The next step is to add this script to the autoLoad list. Open
|
||||
**Project > Project Settings** from the menu, switch to the **AutoLoad** tab and
|
||||
select the script by clicking the browse button or typing its path:
|
||||
``res://Global.gd``. Press **Add** to add it to the autoload list:
|
||||
|
||||
.. image:: img/autoload_tutorial1.png
|
||||
|
||||
Now whenever we run any scene in the project, this script will always be loaded.
|
||||
|
||||
Returning to the script, it needs to fetch the current scene in the
|
||||
`_ready()` function. Both the current scene (the one with the button) and
|
||||
``Global.gd`` are children of root, but autoloaded nodes are always first. This
|
||||
means that the last child of root is always the loaded scene.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Node
|
||||
|
||||
var current_scene = null
|
||||
|
||||
func _ready():
|
||||
var root = get_tree().get_root()
|
||||
current_scene = root.get_child(root.get_child_count() - 1)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public class Global : Godot.Node
|
||||
{
|
||||
public Node CurrentScene { get; set; }
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Viewport root = GetTree().GetRoot();
|
||||
CurrentScene = root.GetChild(root.GetChildCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Now we need a function for changing the scene. This function needs to free the
|
||||
current scene and replace it with the requested one.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func goto_scene(path):
|
||||
# This function will usually be called from a signal callback,
|
||||
# or some other function in the current scene.
|
||||
# Deleting the current scene at this point is
|
||||
# a bad idea, because it may still be executing code.
|
||||
# This will result in a crash or unexpected behavior.
|
||||
|
||||
# The solution is to defer the load to a later time, when
|
||||
# we can be sure that no code from the current scene is running:
|
||||
|
||||
call_deferred("_deferred_goto_scene", path)
|
||||
|
||||
|
||||
func _deferred_goto_scene(path):
|
||||
# It is now safe to remove the current scene
|
||||
current_scene.free()
|
||||
|
||||
# Load the new scene.
|
||||
var s = ResourceLoader.load(path)
|
||||
|
||||
# Instance the new scene.
|
||||
current_scene = s.instance()
|
||||
|
||||
# Add it to the active scene, as child of root.
|
||||
get_tree().get_root().add_child(current_scene)
|
||||
|
||||
# Optionally, to make it compatible with the SceneTree.change_scene() API.
|
||||
get_tree().set_current_scene(current_scene)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void GotoScene(string path)
|
||||
{
|
||||
// This function will usually be called from a signal callback,
|
||||
// or some other function from the current scene.
|
||||
// Deleting the current scene at this point is
|
||||
// a bad idea, because it may still be executing code.
|
||||
// This will result in a crash or unexpected behavior.
|
||||
|
||||
// The solution is to defer the load to a later time, when
|
||||
// we can be sure that no code from the current scene is running:
|
||||
|
||||
CallDeferred(nameof(DeferredGotoScene), path);
|
||||
}
|
||||
|
||||
public void DeferredGotoScene(string path)
|
||||
{
|
||||
// It is now safe to remove the current scene
|
||||
CurrentScene.Free();
|
||||
|
||||
// Load a new scene.
|
||||
var nextScene = (PackedScene)GD.Load(path);
|
||||
|
||||
// Instance the new scene.
|
||||
CurrentScene = nextScene.Instance();
|
||||
|
||||
// Add it to the active scene, as child of root.
|
||||
GetTree().GetRoot().AddChild(CurrentScene);
|
||||
|
||||
// Optionally, to make it compatible with the SceneTree.change_scene() API.
|
||||
GetTree().SetCurrentScene(CurrentScene);
|
||||
}
|
||||
|
||||
Using :ref:`Object.call_deferred() <class_Object_method_call_deferred>`,
|
||||
the second function will only run once all code from the current scene has
|
||||
completed. Thus, the current scene will not be removed while it is
|
||||
still being used (i.e. its code is still running).
|
||||
|
||||
Finally, we need to fill the empty callback functions in the two scenes:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Add to 'Scene1.gd'.
|
||||
|
||||
func _on_Button_pressed():
|
||||
Global.goto_scene("res://Scene2.tscn")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Add to 'Scene1.cs'.
|
||||
|
||||
public void OnButtonPressed()
|
||||
{
|
||||
var global = (Global)GetNode("/root/Global");
|
||||
global.GotoScene("res://Scene2.tscn");
|
||||
}
|
||||
|
||||
and
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Add to 'Scene2.gd'.
|
||||
|
||||
func _on_Button_pressed():
|
||||
Global.goto_scene("res://Scene1.tscn")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Add to 'Scene2.cs'.
|
||||
|
||||
public void OnButtonPressed()
|
||||
{
|
||||
var global = (Global)GetNode("/root/Global");
|
||||
global.GotoScene("res://Scene1.tscn");
|
||||
}
|
||||
|
||||
Run the project and test that you can switch between scenes by pressing
|
||||
the button.
|
||||
|
||||
.. note::
|
||||
|
||||
When scenes are small, the transition is instantaneous. However, if your
|
||||
scenes are more complex, they may take a noticeable amount of time to appear.
|
||||
To learn how to handle this, see the next tutorial: :ref:`doc_background_loading`.
|
||||
|
||||
Alternatively, if the loading time is relatively short (less than 3 seconds or so),
|
||||
you can display a "loading plaque" by showing some kind of 2D element just before
|
||||
changing the scene. You can then hide it just after the scene is changed. This can
|
||||
be used to indicate to the player that a scene is being loaded.
|
||||