docs
This commit is contained in:
parent
6faa7bf05d
commit
95934d1c1c
3622 changed files with 9989830 additions and 0 deletions
320
_sources/tutorials/scripting/c_sharp/c_sharp_basics.rst.txt
Normal file
320
_sources/tutorials/scripting/c_sharp/c_sharp_basics.rst.txt
Normal file
|
@ -0,0 +1,320 @@
|
|||
.. _doc_c_sharp:
|
||||
|
||||
C# basics
|
||||
=========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
.. warning:: C# support is a new feature available since Godot 3.0.
|
||||
As such, you may still run into some issues, or find spots
|
||||
where the documentation could be improved.
|
||||
Please report issues with C# in Godot on the
|
||||
`engine GitHub page <https://github.com/godotengine/godot/issues>`_,
|
||||
and any documentation issues on the
|
||||
`documentation GitHub page <https://github.com/godotengine/godot-docs/issues>`_.
|
||||
|
||||
This page provides a brief introduction to C#, both what it is and
|
||||
how to use it in Godot. Afterwards, you may want to look at
|
||||
:ref:`how to use specific features <doc_c_sharp_features>`, read about the
|
||||
:ref:`differences between the C# and the GDScript API <doc_c_sharp_differences>`
|
||||
and (re)visit the :ref:`Scripting section <doc_scripting>` of the
|
||||
step-by-step tutorial.
|
||||
|
||||
C# is a high-level programming language developed by Microsoft. In Godot,
|
||||
it is implemented with the Mono 6.x .NET framework, including full support
|
||||
for C# 8.0. Mono is an open source implementation of Microsoft's .NET Framework
|
||||
based on the ECMA standards for C# and the Common Language Runtime.
|
||||
A good starting point for checking its capabilities is the
|
||||
`Compatibility <http://www.mono-project.com/docs/about-mono/compatibility/>`_
|
||||
page in the Mono documentation.
|
||||
|
||||
.. note:: This is **not** a full-scale tutorial on the C# language as a whole.
|
||||
If you aren't already familiar with its syntax or features,
|
||||
see the
|
||||
`Microsoft C# guide <https://docs.microsoft.com/en-us/dotnet/csharp/index>`_
|
||||
or look for a suitable introduction elsewhere.
|
||||
|
||||
.. _doc_c_sharp_setup:
|
||||
|
||||
Setting up C# for Godot
|
||||
-----------------------
|
||||
|
||||
Prerequisites
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Install the latest stable version of the
|
||||
`.NET SDK <https://dotnet.microsoft.com/download>`__, previously known as the
|
||||
.NET Core SDK.
|
||||
|
||||
From Godot 3.2.3 onwards, installing Mono SDK is not a requirement anymore,
|
||||
except it is required if you are building the engine from source.
|
||||
|
||||
Godot bundles the parts of Mono needed to run already compiled games.
|
||||
However, Godot does not bundle the tools required to build and compile
|
||||
games, such as MSBuild and the C# compiler. These are
|
||||
included in the .NET SDK, which needs to be installed separately.
|
||||
|
||||
In summary, you must have installed the .NET SDK
|
||||
**and** the Mono-enabled version of Godot.
|
||||
|
||||
Additional notes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Be sure to install the 64-bit version of the SDK(s)
|
||||
if you are using the 64-bit version of Godot.
|
||||
|
||||
If you are building Godot from source, install the latest stable version of
|
||||
`Mono <https://www.mono-project.com/download/stable/>`__, and make sure to
|
||||
follow the steps to enable Mono support in your build as outlined in the
|
||||
:ref:`doc_compiling_with_mono` page.
|
||||
|
||||
Configuring an external editor
|
||||
------------------------------
|
||||
|
||||
C# support in Godot's built-in script editor is minimal. Consider using an
|
||||
external IDE or editor, such as `Visual Studio Code <https://code.visualstudio.com/>`__
|
||||
or MonoDevelop. These provide autocompletion, debugging, and other
|
||||
useful features for C#. To select an external editor in Godot,
|
||||
click on **Editor → Editor Settings** and scroll down to
|
||||
**Mono**. Under **Mono**, click on **Editor**, and select your
|
||||
external editor of choice. Godot currently supports the following
|
||||
external editors:
|
||||
|
||||
- Visual Studio 2019
|
||||
- Visual Studio Code
|
||||
- MonoDevelop
|
||||
- Visual Studio for Mac
|
||||
- JetBrains Rider
|
||||
|
||||
See the following sections for how to configure an external editor:
|
||||
|
||||
JetBrains Rider
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
After reading the "Prerequisites" section, you can download and install
|
||||
`JetBrains Rider <https://www.jetbrains.com/rider/download>`__.
|
||||
|
||||
In Godot's **Editor → Editor Settings** menu:
|
||||
|
||||
- Set **Mono** -> **Editor** -> **External Editor** to **JetBrains Rider**.
|
||||
- Set **Mono** -> **Builds** -> **Build Tool** to **dotnet CLI**.
|
||||
|
||||
In Rider:
|
||||
|
||||
- Set **MSBuild version** to **.NET Core**.
|
||||
- Install the **Godot support** plugin.
|
||||
|
||||
Visual Studio Code
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After reading the "Prerequisites" section, you can download and install
|
||||
`Visual Studio Code <https://code.visualstudio.com/download>`__ (aka VS Code).
|
||||
|
||||
In Godot's **Editor → Editor Settings** menu:
|
||||
|
||||
- Set **Mono** -> **Editor** -> **External Editor** to **Visual Studio Code**.
|
||||
- Set **Mono** -> **Builds** -> **Build Tool** to **dotnet CLI**.
|
||||
|
||||
In Visual Studio Code:
|
||||
|
||||
- Install the `C# <https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp>`__ extension.
|
||||
- Install the `Mono Debug <https://marketplace.visualstudio.com/items?itemName=ms-vscode.mono-debug>`__ extension.
|
||||
- Install the `C# Tools for Godot <https://marketplace.visualstudio.com/items?itemName=neikeq.godot-csharp-vscode>`__ extension.
|
||||
|
||||
.. note:: If you are using Linux you need to install the
|
||||
`Mono SDK <https://www.mono-project.com/download/stable/#download-lin>`__
|
||||
for the C# tools plugin to work.
|
||||
|
||||
To configure a project for debugging open the Godot project folder in VS Code.
|
||||
Go to the Run tab and click on **Add Configuration...**. Select **C# Godot**
|
||||
from the dropdown menu. Open the ``tasks.json`` and ``launch.json`` files that
|
||||
were created. Change the executable setting in ``launch.json`` and command
|
||||
settings in ``tasks.json`` to your Godot executable path. Now, when you start
|
||||
the debugger in VS Code, your Godot project will run.
|
||||
|
||||
Visual Studio (Windows only)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Download and install the latest version of
|
||||
`Visual Studio <https://visualstudio.microsoft.com/downloads/>`__.
|
||||
Visual Studio will include the required SDKs if you have the correct
|
||||
workloads selected, so you don't need to manually install the things
|
||||
listed in the "Prerequisites" section.
|
||||
|
||||
While installing Visual Studio, select these workloads:
|
||||
|
||||
- Mobile development with .NET
|
||||
- .NET Core cross-platform development
|
||||
|
||||
In Godot's **Editor → Editor Settings** menu:
|
||||
|
||||
- Set **Mono** -> **Editor** -> **External Editor** to **Visual Studio**.
|
||||
- Set **Mono** -> **Builds** -> **Build Tool** to **dotnet CLI**.
|
||||
|
||||
Next, you can download the Godot Visual Studio extension from github
|
||||
`here <https://github.com/godotengine/godot-csharp-visualstudio/releases>`__.
|
||||
Double click on the downloaded file and follow the installation process.
|
||||
|
||||
.. note:: The option to debug your game in Visual Studio may not appear after
|
||||
installing the extension. To enable debugging, there is a
|
||||
`workaround for Visual Studio 2019 <https://github.com/godotengine/godot-csharp-visualstudio/issues/10#issuecomment-720153256>`__.
|
||||
There is
|
||||
`a separate issue about this problem in Visual Studio 2022 <https://github.com/godotengine/godot-csharp-visualstudio/issues/28>`__.
|
||||
|
||||
.. note:: If you see an error like "Unable to find package Godot.NET.Sdk",
|
||||
your NuGet configuration may be incorrect and need to be fixed.
|
||||
|
||||
A simple way to fix the NuGet configuration file is to regenerate it.
|
||||
In a file explorer window, go to ``%AppData%\NuGet``. Rename or delete
|
||||
the ``NuGet.Config`` file. When you build your Godot project again,
|
||||
the file will be automatically created with default values.
|
||||
|
||||
Creating a C# script
|
||||
--------------------
|
||||
|
||||
After you successfully set up C# for Godot, you should see the following option
|
||||
when selecting **Attach Script** in the context menu of a node in your scene:
|
||||
|
||||
.. image:: img/attachcsharpscript.png
|
||||
|
||||
Note that while some specifics change, most concepts work the same
|
||||
when using C# for scripting. If you're new to Godot, you may want to follow
|
||||
the tutorials on :ref:`doc_scripting` at this point.
|
||||
While some places in the documentation still lack C# examples, most concepts
|
||||
can be transferred easily from GDScript.
|
||||
|
||||
Project setup and workflow
|
||||
--------------------------
|
||||
|
||||
When you create the first C# script, Godot initializes the C# project files
|
||||
for your Godot project. This includes generating a C# solution (``.sln``)
|
||||
and a project file (``.csproj``), as well as some utility files and folders
|
||||
(``.mono`` and ``Properties/AssemblyInfo.cs``).
|
||||
All of these but ``.mono`` are important and should be committed to your
|
||||
version control system. ``.mono`` can be safely added to the ignore list of your VCS.
|
||||
When troubleshooting, it can sometimes help to delete the ``.mono`` folder
|
||||
and let it regenerate.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Here's a blank C# script with some comments to demonstrate how it works.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public class YourCustomClass : Node
|
||||
{
|
||||
// Member variables here, example:
|
||||
private int a = 2;
|
||||
private string b = "textvar";
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
// Called every time the node is added to the scene.
|
||||
// Initialization here.
|
||||
GD.Print("Hello from C# to Godot :)");
|
||||
}
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
// Called every frame. Delta is time since the last frame.
|
||||
// Update game logic here.
|
||||
}
|
||||
}
|
||||
|
||||
As you can see, functions normally in global scope in GDScript like Godot's
|
||||
``print`` function are available in the ``GD`` class which is part of
|
||||
the ``Godot`` namespace. For a list of methods in the ``GD`` class, see the
|
||||
class reference pages for
|
||||
:ref:`@GDScript <class_@gdscript>` and :ref:`@GlobalScope <class_@globalscope>`.
|
||||
|
||||
.. note::
|
||||
Keep in mind that the class you wish to attach to your node should have the same
|
||||
name as the ``.cs`` file. Otherwise, you will get the following error
|
||||
and won't be able to run the scene:
|
||||
*"Cannot find class XXX for script res://XXX.cs"*
|
||||
|
||||
General differences between C# and GDScript
|
||||
-------------------------------------------
|
||||
|
||||
The C# API uses ``PascalCase`` instead of ``snake_case`` in GDScript/C++.
|
||||
Where possible, fields and getters/setters have been converted to properties.
|
||||
In general, the C# Godot API strives to be as idiomatic as is reasonably possible.
|
||||
|
||||
For more information, see the :ref:`doc_c_sharp_differences` page.
|
||||
|
||||
.. warning::
|
||||
|
||||
You need to (re)build the project assemblies whenever you want to see new
|
||||
exported variables or signals in the editor. This build can be manually
|
||||
triggered by clicking the word **Build** in the top right corner of the
|
||||
editor. You can also click **Mono** at the bottom of the editor window
|
||||
to reveal the Mono panel, then click the **Build Project** button.
|
||||
|
||||
You will also need to rebuild the project assemblies to apply changes in
|
||||
"tool" scripts.
|
||||
|
||||
Current gotchas and known issues
|
||||
--------------------------------
|
||||
|
||||
As C# support is quite new in Godot, there are some growing pains and things
|
||||
that need to be ironed out. Below is a list of the most important issues
|
||||
you should be aware of when diving into C# in Godot, but if in doubt, also
|
||||
take a look over the official
|
||||
`issue tracker for Mono issues <https://github.com/godotengine/godot/labels/topic%3Amono>`_.
|
||||
|
||||
- Writing editor plugins is possible, but it is currently quite convoluted.
|
||||
- State is currently not saved and restored when hot-reloading,
|
||||
with the exception of exported variables.
|
||||
- Attached C# scripts should refer to a class that has a class name
|
||||
that matches the file name.
|
||||
- There are some methods such as ``Get()``/``Set()``, ``Call()``/``CallDeferred()``
|
||||
and signal connection method ``Connect()`` that rely on Godot's ``snake_case`` API
|
||||
naming conventions.
|
||||
So when using e.g. ``CallDeferred("AddChild")``, ``AddChild`` will not work because
|
||||
the API is expecting the original ``snake_case`` version ``add_child``. However, you
|
||||
can use any custom properties or methods without this limitation.
|
||||
|
||||
|
||||
Exporting Mono projects is supported for desktop platforms (Linux, Windows and
|
||||
macOS), Android, HTML5, and iOS. The only platform not supported yet is UWP.
|
||||
|
||||
Performance of C# in Godot
|
||||
--------------------------
|
||||
|
||||
According to some preliminary `benchmarks <https://github.com/cart/godot3-bunnymark>`_,
|
||||
the performance of C# in Godot — while generally in the same order of magnitude
|
||||
— is roughly **~4×** that of GDScript in some naive cases. C++ is still
|
||||
a little faster; the specifics are going to vary according to your use case.
|
||||
GDScript is likely fast enough for most general scripting workloads.
|
||||
C# is faster, but requires some expensive marshalling when talking to Godot.
|
||||
|
||||
Using NuGet packages in Godot
|
||||
-----------------------------
|
||||
|
||||
`NuGet <https://www.nuget.org/>`_ packages can be installed and used with Godot,
|
||||
as with any C# project. Many IDEs are able to add packages directly.
|
||||
They can also be added manually by adding the package reference in
|
||||
the ``.csproj`` file located in the project root:
|
||||
|
||||
.. code-block:: xml
|
||||
:emphasize-lines: 2
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
...
|
||||
</Project>
|
||||
|
||||
As of Godot 3.2.3, Godot automatically downloads and sets up newly added NuGet
|
||||
packages the next time it builds the project.
|
||||
|
||||
Profiling your C# code
|
||||
----------------------
|
||||
|
||||
- `Mono log profiler <https://www.mono-project.com/docs/debug+profile/profile/profiler/>`_ is available for Linux and macOS. Due to a Mono change, it does not work on Windows currently.
|
||||
- External Mono profiler like `JetBrains dotTrace <https://www.jetbrains.com/profiler/>`_ can be used as described `here <https://github.com/godotengine/godot/pull/34382>`_.
|
358
_sources/tutorials/scripting/c_sharp/c_sharp_differences.rst.txt
Normal file
358
_sources/tutorials/scripting/c_sharp/c_sharp_differences.rst.txt
Normal file
|
@ -0,0 +1,358 @@
|
|||
.. _doc_c_sharp_differences:
|
||||
|
||||
C# API differences to GDScript
|
||||
==============================
|
||||
|
||||
This is a (incomplete) list of API differences between C# and GDScript.
|
||||
|
||||
General differences
|
||||
-------------------
|
||||
|
||||
As explained in the :ref:`doc_c_sharp`, C# generally uses ``PascalCase`` instead
|
||||
of the ``snake_case`` used in GDScript and C++.
|
||||
|
||||
Global scope
|
||||
------------
|
||||
|
||||
Global functions and some constants had to be moved to classes, since C#
|
||||
does not allow declaring them in namespaces.
|
||||
Most global constants were moved to their own enums.
|
||||
|
||||
Constants
|
||||
^^^^^^^^^
|
||||
|
||||
Global constants were moved to their own enums.
|
||||
For example, ``ERR_*`` constants were moved to the ``Error`` enum.
|
||||
|
||||
Special cases:
|
||||
|
||||
======================= ===========================================================
|
||||
GDScript C#
|
||||
======================= ===========================================================
|
||||
``SPKEY`` ``GD.SpKey``
|
||||
``TYPE_*`` ``Variant.Type`` enum
|
||||
``OP_*`` ``Variant.Operator`` enum
|
||||
======================= ===========================================================
|
||||
|
||||
Math functions
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Math global functions, like ``abs``, ``acos``, ``asin``, ``atan`` and ``atan2``, are
|
||||
located under ``Mathf`` as ``Abs``, ``Acos``, ``Asin``, ``Atan`` and ``Atan2``.
|
||||
The ``PI`` constant can be found as ``Mathf.Pi``.
|
||||
|
||||
Random functions
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Random global functions, like ``rand_range`` and ``rand_seed``, are located under ``GD``.
|
||||
Example: ``GD.RandRange`` and ``GD.RandSeed``.
|
||||
|
||||
Other functions
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Many other global functions like ``print`` and ``var2str`` are located under ``GD``.
|
||||
Example: ``GD.Print`` and ``GD.Var2Str``.
|
||||
|
||||
Exceptions:
|
||||
|
||||
=========================== =======================================================
|
||||
GDScript C#
|
||||
=========================== =======================================================
|
||||
``weakref(obj)`` ``Object.WeakRef(obj)``
|
||||
``is_instance_valid(obj)`` ``Object.IsInstanceValid(obj)``
|
||||
=========================== =======================================================
|
||||
|
||||
Tips
|
||||
^^^^
|
||||
|
||||
Sometimes it can be useful to use the ``using static`` directive. This directive allows
|
||||
to access the members and nested types of a class without specifying the class name.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
using static Godot.GD;
|
||||
|
||||
public class Test
|
||||
{
|
||||
static Test()
|
||||
{
|
||||
Print("Hello"); // Instead of GD.Print("Hello");
|
||||
}
|
||||
}
|
||||
|
||||
Export keyword
|
||||
--------------
|
||||
|
||||
Use the ``[Export]`` attribute instead of the GDScript ``export`` keyword.
|
||||
This attribute can also be provided with optional :ref:`PropertyHint<enum_@GlobalScope_PropertyHint>` and ``hintString`` parameters.
|
||||
Default values can be set by assigning a value.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
using Godot;
|
||||
|
||||
public class MyNode : Node
|
||||
{
|
||||
[Export]
|
||||
private NodePath _nodePath;
|
||||
|
||||
[Export]
|
||||
private string _name = "default";
|
||||
|
||||
[Export(PropertyHint.Range, "0,100000,1000,or_greater")]
|
||||
private int _income;
|
||||
|
||||
[Export(PropertyHint.File, "*.png,*.jpg")]
|
||||
private string _icon;
|
||||
}
|
||||
|
||||
Signal keyword
|
||||
--------------
|
||||
|
||||
Use the ``[Signal]`` attribute to declare a signal instead of the GDScript ``signal`` keyword.
|
||||
This attribute should be used on a `delegate`, whose name signature will be used to define the signal.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
[Signal]
|
||||
delegate void MySignal(string willSendsAString);
|
||||
|
||||
See also: :ref:`doc_c_sharp_signals`.
|
||||
|
||||
`onready` keyword
|
||||
-----------------
|
||||
|
||||
GDScript has the ability to defer the initialization of a member variable until the ready function
|
||||
is called with `onready` (cf. :ref:`doc_gdscript_onready_keyword`).
|
||||
For example:
|
||||
|
||||
.. code-block:: gdscript
|
||||
|
||||
onready var my_label = get_node("MyLabel")
|
||||
|
||||
However C# does not have this ability. To achieve the same effect you need to do this.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
private Label _myLabel;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_myLabel = GetNode<Label>("MyLabel");
|
||||
}
|
||||
|
||||
Singletons
|
||||
----------
|
||||
|
||||
Singletons are available as static classes rather than using the singleton pattern.
|
||||
This is to make code less verbose than it would be with an ``Instance`` property.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
Input.IsActionPressed("ui_down")
|
||||
|
||||
However, in some very rare cases this is not enough. For example, you may want
|
||||
to access a member from the base class ``Godot.Object``, like ``Connect``.
|
||||
For such use cases we provide a static property named ``Singleton`` that returns
|
||||
the singleton instance. The type of this instance is ``Godot.Object``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
Input.Singleton.Connect("joy_connection_changed", this, nameof(Input_JoyConnectionChanged));
|
||||
|
||||
String
|
||||
------
|
||||
|
||||
Use ``System.String`` (``string``). Most of Godot's String methods are
|
||||
provided by the ``StringExtensions`` class as extension methods.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
string upper = "I LIKE SALAD FORKS";
|
||||
string lower = upper.ToLower();
|
||||
|
||||
There are a few differences, though:
|
||||
|
||||
* ``erase``: Strings are immutable in C#, so we cannot modify the string
|
||||
passed to the extension method. For this reason, ``Erase`` was added as an
|
||||
extension method of ``StringBuilder`` instead of string.
|
||||
Alternatively, you can use ``string.Remove``.
|
||||
* ``IsSubsequenceOf``/``IsSubsequenceOfi``: An additional method is provided,
|
||||
which is an overload of ``IsSubsequenceOf``, allowing you to explicitly specify
|
||||
case sensitivity:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
str.IsSubsequenceOf("ok"); // Case sensitive
|
||||
str.IsSubsequenceOf("ok", true); // Case sensitive
|
||||
str.IsSubsequenceOfi("ok"); // Case insensitive
|
||||
str.IsSubsequenceOf("ok", false); // Case insensitive
|
||||
|
||||
* ``Match``/``Matchn``/``ExprMatch``: An additional method is provided besides
|
||||
``Match`` and ``Matchn``, which allows you to explicitly specify case sensitivity:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
str.Match("*.txt"); // Case sensitive
|
||||
str.ExprMatch("*.txt", true); // Case sensitive
|
||||
str.Matchn("*.txt"); // Case insensitive
|
||||
str.ExprMatch("*.txt", false); // Case insensitive
|
||||
|
||||
Basis
|
||||
-----
|
||||
|
||||
Structs cannot have parameterless constructors in C#. Therefore, ``new Basis()``
|
||||
initializes all primitive members to their default value. Use ``Basis.Identity``
|
||||
for the equivalent of ``Basis()`` in GDScript and C++.
|
||||
|
||||
The following method was converted to a property with a different name:
|
||||
|
||||
==================== ==============================================================
|
||||
GDScript C#
|
||||
==================== ==============================================================
|
||||
``get_scale()`` ``Scale``
|
||||
==================== ==============================================================
|
||||
|
||||
Transform2D
|
||||
-----------
|
||||
|
||||
Structs cannot have parameterless constructors in C#. Therefore, ``new Transform2D()``
|
||||
initializes all primitive members to their default value.
|
||||
Please use ``Transform2D.Identity`` for the equivalent of ``Transform2D()`` in GDScript and C++.
|
||||
|
||||
The following methods were converted to properties with their respective names changed:
|
||||
|
||||
==================== ==============================================================
|
||||
GDScript C#
|
||||
==================== ==============================================================
|
||||
``get_rotation()`` ``Rotation``
|
||||
``get_scale()`` ``Scale``
|
||||
==================== ==============================================================
|
||||
|
||||
Plane
|
||||
-----
|
||||
|
||||
The following method was converted to a property with a *slightly* different name:
|
||||
|
||||
================ ==================================================================
|
||||
GDScript C#
|
||||
================ ==================================================================
|
||||
``center()`` ``Center``
|
||||
================ ==================================================================
|
||||
|
||||
Rect2
|
||||
-----
|
||||
|
||||
The following field was converted to a property with a *slightly* different name:
|
||||
|
||||
================ ==================================================================
|
||||
GDScript C#
|
||||
================ ==================================================================
|
||||
``end`` ``End``
|
||||
================ ==================================================================
|
||||
|
||||
The following method was converted to a property with a different name:
|
||||
|
||||
================ ==================================================================
|
||||
GDScript C#
|
||||
================ ==================================================================
|
||||
``get_area()`` ``Area``
|
||||
================ ==================================================================
|
||||
|
||||
Quat
|
||||
----
|
||||
|
||||
Structs cannot have parameterless constructors in C#. Therefore, ``new Quat()``
|
||||
initializes all primitive members to their default value.
|
||||
Please use ``Quat.Identity`` for the equivalent of ``Quat()`` in GDScript and C++.
|
||||
|
||||
The following methods were converted to a property with a different name:
|
||||
|
||||
===================== =============================================================
|
||||
GDScript C#
|
||||
===================== =============================================================
|
||||
``length()`` ``Length``
|
||||
``length_squared()`` ``LengthSquared``
|
||||
===================== =============================================================
|
||||
|
||||
Array
|
||||
-----
|
||||
|
||||
*This is temporary. PoolArrays will need their own types to be used the way they are meant to.*
|
||||
|
||||
===================== ==============================================================
|
||||
GDScript C#
|
||||
===================== ==============================================================
|
||||
``Array`` ``Godot.Collections.Array``
|
||||
``PoolIntArray`` ``int[]``
|
||||
``PoolByteArray`` ``byte[]``
|
||||
``PoolFloatArray`` ``float[]``
|
||||
``PoolStringArray`` ``String[]``
|
||||
``PoolColorArray`` ``Color[]``
|
||||
``PoolVector2Array`` ``Vector2[]``
|
||||
``PoolVector3Array`` ``Vector3[]``
|
||||
===================== ==============================================================
|
||||
|
||||
``Godot.Collections.Array<T>`` is a type-safe wrapper around ``Godot.Collections.Array``.
|
||||
Use the ``Godot.Collections.Array<T>(Godot.Collections.Array)`` constructor to create one.
|
||||
|
||||
Dictionary
|
||||
----------
|
||||
|
||||
Use ``Godot.Collections.Dictionary``.
|
||||
|
||||
``Godot.Collections.Dictionary<T>`` is a type-safe wrapper around ``Godot.Collections.Dictionary``.
|
||||
Use the ``Godot.Collections.Dictionary<T>(Godot.Collections.Dictionary)`` constructor to create one.
|
||||
|
||||
Variant
|
||||
-------
|
||||
|
||||
``System.Object`` (``object``) is used instead of ``Variant``.
|
||||
|
||||
Communicating with other scripting languages
|
||||
--------------------------------------------
|
||||
|
||||
This is explained extensively in :ref:`doc_cross_language_scripting`.
|
||||
|
||||
Yield
|
||||
-----
|
||||
|
||||
Something similar to GDScript's ``yield`` with a single parameter can be achieved with
|
||||
C#'s `yield keyword <https://docs.microsoft.com/en-US/dotnet/csharp/language-reference/keywords/yield>`_.
|
||||
|
||||
The equivalent of yield on signal can be achieved with async/await and ``Godot.Object.ToSignal``.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
await ToSignal(timer, "timeout");
|
||||
GD.Print("After timeout");
|
||||
|
||||
Other differences
|
||||
-----------------
|
||||
|
||||
``preload``, as it works in GDScript, is not available in C#.
|
||||
Use ``GD.Load`` or ``ResourceLoader.Load`` instead.
|
||||
|
||||
Other differences:
|
||||
|
||||
================ ==================================================================
|
||||
GDScript C#
|
||||
================ ==================================================================
|
||||
``Color8`` ``Color.Color8``
|
||||
``is_inf`` ``float.IsInfinity``
|
||||
``is_nan`` ``float.IsNaN``
|
||||
``dict2inst`` TODO
|
||||
``inst2dict`` TODO
|
||||
================ ==================================================================
|
245
_sources/tutorials/scripting/c_sharp/c_sharp_features.rst.txt
Normal file
245
_sources/tutorials/scripting/c_sharp/c_sharp_features.rst.txt
Normal file
|
@ -0,0 +1,245 @@
|
|||
.. _doc_c_sharp_features:
|
||||
|
||||
C# features
|
||||
===========
|
||||
|
||||
This page provides an overview of the commonly used features of both C# and Godot
|
||||
and how they are used together.
|
||||
|
||||
.. _doc_c_sharp_features_type_conversion_and_casting:
|
||||
|
||||
Type conversion and casting
|
||||
---------------------------
|
||||
|
||||
C# is a statically typed language. Therefore, you can't do the following:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
var mySprite = GetNode("MySprite");
|
||||
mySprite.SetFrame(0);
|
||||
|
||||
The method ``GetNode()`` returns a ``Node`` instance.
|
||||
You must explicitly convert it to the desired derived type, ``Sprite`` in this case.
|
||||
|
||||
For this, you have various options in C#.
|
||||
|
||||
**Casting and Type Checking**
|
||||
|
||||
Throws ``InvalidCastException`` if the returned node cannot be cast to Sprite.
|
||||
You would use it instead of the ``as`` operator if you are pretty sure it won't fail.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
Sprite mySprite = (Sprite)GetNode("MySprite");
|
||||
mySprite.SetFrame(0);
|
||||
|
||||
**Using the AS operator**
|
||||
|
||||
The ``as`` operator returns ``null`` if the node cannot be cast to Sprite,
|
||||
and for that reason, it cannot be used with value types.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
Sprite mySprite = GetNode("MySprite") as Sprite;
|
||||
// Only call SetFrame() if mySprite is not null
|
||||
mySprite?.SetFrame(0);
|
||||
|
||||
**Using the generic methods**
|
||||
|
||||
Generic methods are also provided to make this type conversion transparent.
|
||||
|
||||
``GetNode<T>()`` casts the node before returning it. It will throw an ``InvalidCastException`` if the node cannot be cast to the desired type.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
Sprite mySprite = GetNode<Sprite>("MySprite");
|
||||
mySprite.SetFrame(0);
|
||||
|
||||
``GetNodeOrNull<T>()`` uses the ``as`` operator and will return ``null`` if the node cannot be cast to the desired type.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
Sprite mySprite = GetNodeOrNull<Sprite>("MySprite");
|
||||
// Only call SetFrame() if mySprite is not null
|
||||
mySprite?.SetFrame(0);
|
||||
|
||||
**Type checking using the IS operator**
|
||||
|
||||
To check if the node can be cast to Sprite, you can use the ``is`` operator.
|
||||
The ``is`` operator returns false if the node cannot be cast to Sprite,
|
||||
otherwise it returns true.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
if (GetNode("MySprite") is Sprite)
|
||||
{
|
||||
// Yup, it's a sprite!
|
||||
}
|
||||
|
||||
For more advanced type checking, you can look into `Pattern Matching <https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching>`_.
|
||||
|
||||
.. _doc_c_sharp_signals:
|
||||
|
||||
C# signals
|
||||
----------
|
||||
|
||||
For a complete C# example, see the **Handling a signal** section in the step by step :ref:`doc_scripting` tutorial.
|
||||
|
||||
Declaring a signal in C# is done with the ``[Signal]`` attribute on a delegate.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
[Signal]
|
||||
delegate void MySignal();
|
||||
|
||||
[Signal]
|
||||
delegate void MySignalWithArguments(string foo, int bar);
|
||||
|
||||
These signals can then be connected either in the editor or from code with ``Connect``.
|
||||
If you want to connect a signal in the editor, you need to (re)build the project assemblies to see the new signal. This build can be manually triggered by clicking the “Build” button at the top right corner of the editor window.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void MyCallback()
|
||||
{
|
||||
GD.Print("My callback!");
|
||||
}
|
||||
|
||||
public void MyCallbackWithArguments(string foo, int bar)
|
||||
{
|
||||
GD.Print("My callback with: ", foo, " and ", bar, "!");
|
||||
}
|
||||
|
||||
public void SomeFunction()
|
||||
{
|
||||
instance.Connect("MySignal", this, "MyCallback");
|
||||
instance.Connect(nameof(MySignalWithArguments), this, "MyCallbackWithArguments");
|
||||
}
|
||||
|
||||
Emitting signals is done with the ``EmitSignal`` method.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void SomeFunction()
|
||||
{
|
||||
EmitSignal(nameof(MySignal));
|
||||
EmitSignal("MySignalWithArguments", "hello there", 28);
|
||||
}
|
||||
|
||||
Notice that you can always reference a signal name with the ``nameof`` keyword (applied on the delegate itself).
|
||||
|
||||
It is possible to bind values when establishing a connection by passing a Godot array.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public int Value { get; private set; } = 0;
|
||||
|
||||
private void ModifyValue(int modifier)
|
||||
{
|
||||
Value += modifier;
|
||||
}
|
||||
|
||||
public void SomeFunction()
|
||||
{
|
||||
var plusButton = (Button)GetNode("PlusButton");
|
||||
var minusButton = (Button)GetNode("MinusButton");
|
||||
|
||||
plusButton.Connect("pressed", this, "ModifyValue", new Godot.Collections.Array { 1 });
|
||||
minusButton.Connect("pressed", this, "ModifyValue", new Godot.Collections.Array { -1 });
|
||||
}
|
||||
|
||||
Signals support parameters and bound values of all the `built-in types <https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table>`_ and Classes derived from :ref:`Godot.Object <class_Object>`.
|
||||
Consequently, any ``Node`` or ``Reference`` will be compatible automatically, but custom data objects will need to extend from `Godot.Object` or one of its subclasses.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public class DataObject : Godot.Object
|
||||
{
|
||||
public string Field1 { get; set; }
|
||||
public string Field2 { get; set; }
|
||||
}
|
||||
|
||||
|
||||
Finally, signals can be created by calling ``AddUserSignal``, but be aware that it should be executed before any use of said signals (with ``Connect`` or ``EmitSignal``).
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void SomeFunction()
|
||||
{
|
||||
AddUserSignal("MyOtherSignal");
|
||||
EmitSignal("MyOtherSignal");
|
||||
}
|
||||
|
||||
Preprocessor defines
|
||||
--------------------
|
||||
|
||||
Godot has a set of defines that allow you to change your C# code
|
||||
depending on the environment you are compiling to.
|
||||
|
||||
.. note:: If you created your project before Godot 3.2, you have to modify
|
||||
or regenerate your `csproj` file to use this feature
|
||||
(compare ``<DefineConstants>`` with a new 3.2+ project).
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
|
||||
For example, you can change code based on the platform:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
#if GODOT_SERVER
|
||||
// Don't try to load meshes or anything, this is a server!
|
||||
LaunchServer();
|
||||
#elif GODOT_32 || GODOT_MOBILE || GODOT_WEB
|
||||
// Use simple objects when running on less powerful systems.
|
||||
SpawnSimpleObjects();
|
||||
#else
|
||||
SpawnComplexObjects();
|
||||
#endif
|
||||
}
|
||||
|
||||
Or you can detect which engine your code is in, useful for making cross-engine libraries:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public void MyPlatformPrinter()
|
||||
{
|
||||
#if GODOT
|
||||
GD.Print("This is Godot.");
|
||||
#elif UNITY_5_3_OR_NEWER
|
||||
print("This is Unity.");
|
||||
#else
|
||||
throw new InvalidWorkflowException("Only Godot and Unity are supported.");
|
||||
#endif
|
||||
}
|
||||
|
||||
Full list of defines
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* ``GODOT`` is always defined for Godot projects.
|
||||
|
||||
* One of ``GODOT_64`` or ``GODOT_32`` is defined depending on if the architecture is 64-bit or 32-bit.
|
||||
|
||||
* One of ``GODOT_X11``, ``GODOT_WINDOWS``, ``GODOT_OSX``,
|
||||
``GODOT_ANDROID``, ``GODOT_IOS``, ``GODOT_HTML5``, or ``GODOT_SERVER``
|
||||
depending on the OS. These names may change in the future.
|
||||
These are created from the ``get_name()`` method of the
|
||||
:ref:`OS <class_OS>` singleton, but not every possible OS
|
||||
the method returns is an OS that Godot with Mono runs on.
|
||||
|
||||
When **exporting**, the following may also be defined depending on the export features:
|
||||
|
||||
* One of ``GODOT_PC``, ``GODOT_MOBILE``, or ``GODOT_WEB`` depending on the platform type.
|
||||
|
||||
* One of ``GODOT_ARM64_V8A`` or ``GODOT_ARMEABI_V7A`` on Android only depending on the architecture.
|
||||
|
||||
* One of ``GODOT_ARM64`` or ``GODOT_ARMV7`` on iOS only depending on the architecture.
|
||||
|
||||
* Any of ``GODOT_S3TC``, ``GODOT_ETC``, and ``GODOT_ETC2`` depending on the texture compression type.
|
||||
|
||||
* Any custom features added in the export menu will be capitalized and prefixed: ``foo`` -> ``GODOT_FOO``.
|
||||
|
||||
To see an example project, see the OS testing demo:
|
||||
https://github.com/godotengine/godot-demo-projects/tree/master/misc/os_test
|
324
_sources/tutorials/scripting/c_sharp/c_sharp_style_guide.rst.txt
Normal file
324
_sources/tutorials/scripting/c_sharp/c_sharp_style_guide.rst.txt
Normal file
|
@ -0,0 +1,324 @@
|
|||
.. _doc_c_sharp_styleguide:
|
||||
|
||||
C# style guide
|
||||
==============
|
||||
|
||||
Having well-defined and consistent coding conventions is important for every project, and Godot
|
||||
is no exception to this rule.
|
||||
|
||||
This page contains a coding style guide, which is followed by developers of and contributors to Godot
|
||||
itself. As such, it is mainly intended for those who want to contribute to the project, but since
|
||||
the conventions and guidelines mentioned in this article are those most widely adopted by the users
|
||||
of the language, we encourage you to do the same, especially if you do not have such a guide yet.
|
||||
|
||||
.. note:: This article is by no means an exhaustive guide on how to follow the standard coding
|
||||
conventions or best practices. If you feel unsure of an aspect which is not covered here,
|
||||
please refer to more comprehensive documentation, such as
|
||||
`C# Coding Conventions <https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions>`_ or
|
||||
`Framework Design Guidelines <https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines>`_.
|
||||
|
||||
Language specification
|
||||
----------------------
|
||||
|
||||
Godot currently uses **C# version 7.0** in its engine and example source code. So, before we move to
|
||||
a newer version, care must be taken to avoid mixing language features only available in C# 7.1 or
|
||||
later.
|
||||
|
||||
For detailed information on C# features in different versions, please see
|
||||
`What's New in C# <https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/>`_.
|
||||
|
||||
Formatting
|
||||
----------
|
||||
|
||||
General guidelines
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Use line feed (**LF**) characters to break lines, not CRLF or CR.
|
||||
* Use one line feed character at the end of each file, except for `csproj` files.
|
||||
* Use **UTF-8** encoding without a `byte order mark <https://en.wikipedia.org/wiki/Byte_order_mark>`_.
|
||||
* Use **4 spaces** instead of tabs for indentation (which is referred to as "soft tabs").
|
||||
* Consider breaking a line into several if it's longer than 100 characters.
|
||||
|
||||
|
||||
Line breaks and blank lines
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For a general indentation rule, follow `the "Allman Style" <https://en.wikipedia.org/wiki/Indentation_style#Allman_style>`_
|
||||
which recommends placing the brace associated with a control statement on the next line, indented to
|
||||
the same level:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
// Use this style:
|
||||
if (x > 0)
|
||||
{
|
||||
DoSomething();
|
||||
}
|
||||
|
||||
// NOT this:
|
||||
if (x > 0) {
|
||||
DoSomething();
|
||||
}
|
||||
|
||||
However, you may choose to omit line breaks inside brackets:
|
||||
|
||||
* For simple property accessors.
|
||||
* For simple object, array, or collection initializers.
|
||||
* For abstract auto property, indexer, or event declarations.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
// You may put the brackets in a single line in following cases:
|
||||
public interface MyInterface
|
||||
{
|
||||
int MyProperty { get; set; }
|
||||
}
|
||||
|
||||
public class MyClass : ParentClass
|
||||
{
|
||||
public int Value
|
||||
{
|
||||
get { return 0; }
|
||||
set
|
||||
{
|
||||
ArrayValue = new [] {value};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Insert a blank line:
|
||||
|
||||
* After a list of ``using`` statements.
|
||||
* Between method, properties, and inner type declarations.
|
||||
* At the end of each file.
|
||||
|
||||
Field and constant declarations can be grouped together according to relevance. In that case, consider
|
||||
inserting a blank line between the groups for easier reading.
|
||||
|
||||
Avoid inserting a blank line:
|
||||
|
||||
* After ``{``, the opening brace.
|
||||
* Before ``}``, the closing brace.
|
||||
* After a comment block or a single-line comment.
|
||||
* Adjacent to another blank line.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
using System;
|
||||
using Godot;
|
||||
// Blank line after `using` list.
|
||||
public class MyClass
|
||||
{ // No blank line after `{`.
|
||||
public enum MyEnum
|
||||
{
|
||||
Value,
|
||||
AnotherValue // No blank line before `}`.
|
||||
}
|
||||
// Blank line around inner types.
|
||||
public const int SomeConstant = 1;
|
||||
public const int AnotherConstant = 2;
|
||||
|
||||
private Vector3 _x; // Related constants or fields can be
|
||||
private Vector3 _y; // grouped together.
|
||||
|
||||
private float _width;
|
||||
private float _height;
|
||||
|
||||
public int MyProperty { get; set; }
|
||||
// Blank line around properties.
|
||||
public void MyMethod()
|
||||
{
|
||||
// Some comment.
|
||||
AnotherMethod(); // No blank line after a comment.
|
||||
}
|
||||
// Blank line around methods.
|
||||
public void AnotherMethod()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Using spaces
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Insert a space:
|
||||
|
||||
* Around a binary and tertiary operator.
|
||||
* Between an opening parenthesis and ``if``, ``for``, ``foreach``, ``catch``, ``while``, ``lock`` or ``using`` keywords.
|
||||
* Before and within a single line accessor block.
|
||||
* Between accessors in a single line accessor block.
|
||||
* After a comma which is not at the end of a line.
|
||||
* After a semicolon in a ``for`` statement.
|
||||
* After a colon in a single line ``case`` statement.
|
||||
* Around a colon in a type declaration.
|
||||
* Around a lambda arrow.
|
||||
* After a single-line comment symbol (``//``), and before it if used at the end of a line.
|
||||
|
||||
Do not use a space:
|
||||
|
||||
* After type cast parentheses.
|
||||
* Within single line initializer braces.
|
||||
|
||||
The following example shows a proper use of spaces, according to some of the above mentioned conventions:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public class MyClass<A, B> : Parent<A, B>
|
||||
{
|
||||
public float MyProperty { get; set; }
|
||||
|
||||
public float AnotherProperty
|
||||
{
|
||||
get { return MyProperty; }
|
||||
}
|
||||
|
||||
public void MyMethod()
|
||||
{
|
||||
int[] values = {1, 2, 3, 4}; // No space within initializer brackets.
|
||||
int sum = 0;
|
||||
|
||||
// Single line comment.
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
case 3: return;
|
||||
default:
|
||||
sum += i > 2 ? 0 : 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i += (int)MyProperty; // No space after a type cast.
|
||||
}
|
||||
}
|
||||
|
||||
Naming conventions
|
||||
------------------
|
||||
|
||||
Use **PascalCase** for all namespaces, type names and member level identifiers (i.e. methods, properties,
|
||||
constants, events), except for private fields:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
namespace ExampleProject
|
||||
{
|
||||
public class PlayerCharacter
|
||||
{
|
||||
public const float DefaultSpeed = 10f;
|
||||
|
||||
public float CurrentSpeed { get; set; }
|
||||
|
||||
protected int HitPoints;
|
||||
|
||||
private void CalculateWeaponDamage()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Use **camelCase** for all other identifiers (i.e. local variables, method arguments), and use
|
||||
an underscore (``_``) as a prefix for private fields (but not for methods or properties, as explained above):
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
private Vector3 _aimingAt; // Use a `_` prefix for private fields.
|
||||
|
||||
private void Attack(float attackStrength)
|
||||
{
|
||||
Enemy targetFound = FindTarget(_aimingAt);
|
||||
|
||||
targetFound?.Hit(attackStrength);
|
||||
}
|
||||
|
||||
There's an exception with acronyms which consist of two letters, like ``UI``, which should be written in
|
||||
uppercase letters where PascalCase would be expected, and in lowercase letters otherwise.
|
||||
|
||||
Note that ``id`` is **not** an acronym, so it should be treated as a normal identifier:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public UIManager UI
|
||||
{
|
||||
get { return uiManager; }
|
||||
}
|
||||
|
||||
It is generally discouraged to use a type name as a prefix of an identifier, like ``string strText``
|
||||
or ``float fPower``, for example. An exception is made, however, for interfaces, which
|
||||
**should**, in fact, have an uppercase letter ``I`` prefixed to their names, like ``IInventoryHolder`` or ``IDamageable``.
|
||||
|
||||
Lastly, consider choosing descriptive names and do not try to shorten them too much if it affects
|
||||
readability.
|
||||
|
||||
For instance, if you want to write code to find a nearby enemy and hit it with a weapon, prefer:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
FindNearbyEnemy()?.Damage(weaponDamage);
|
||||
|
||||
Rather than:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
FindNode()?.Change(wpnDmg);
|
||||
|
||||
Member variables
|
||||
----------------
|
||||
|
||||
Don't declare member variables if they are only used locally in a method, as it
|
||||
makes the code more difficult to follow. Instead, declare them as local
|
||||
variables in the method's body.
|
||||
|
||||
Local variables
|
||||
---------------
|
||||
|
||||
Declare local variables as close as possible to their first use. This makes it
|
||||
easier to follow the code, without having to scroll too much to find where the
|
||||
variable was declared.
|
||||
|
||||
Implicitly typed local variables
|
||||
--------------------------------
|
||||
|
||||
Consider using implicitly typing (``var``) for declaration of a local variable, but do so
|
||||
**only when the type is evident** from the right side of the assignment:
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
// You can use `var` for these cases:
|
||||
|
||||
var direction = new Vector2(1, 0);
|
||||
|
||||
var value = (int)speed;
|
||||
|
||||
var text = "Some value";
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
}
|
||||
|
||||
// But not for these:
|
||||
|
||||
var value = GetValue();
|
||||
|
||||
var velocity = direction * 1.5;
|
||||
|
||||
// It's generally a better idea to use explicit typing for numeric values, especially with
|
||||
// the existence of the `real_t` alias in Godot, which can either be double or float
|
||||
// depending on the build configuration.
|
||||
|
||||
var value = 1.5;
|
||||
|
||||
Other considerations
|
||||
--------------------
|
||||
|
||||
* Use explicit access modifiers.
|
||||
* Use properties instead of non-private fields.
|
||||
* Use modifiers in this order:
|
||||
``public``/``protected``/``private``/``internal``/``virtual``/``override``/``abstract``/``new``/``static``/``readonly``.
|
||||
* Avoid using fully-qualified names or ``this.`` prefix for members when it's not necessary.
|
||||
* Remove unused ``using`` statements and unnecessary parentheses.
|
||||
* Consider omitting the default initial value for a type.
|
||||
* Consider using null-conditional operators or type initializers to make the code more compact.
|
||||
* Use safe cast when there is a possibility of the value being a different type, and use direct cast otherwise.
|
11
_sources/tutorials/scripting/c_sharp/index.rst.txt
Normal file
11
_sources/tutorials/scripting/c_sharp/index.rst.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
C#
|
||||
===
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-learn-scripting-C#
|
||||
|
||||
c_sharp_basics
|
||||
c_sharp_features
|
||||
c_sharp_differences
|
||||
c_sharp_style_guide
|
140
_sources/tutorials/scripting/change_scenes_manually.rst.txt
Normal file
140
_sources/tutorials/scripting/change_scenes_manually.rst.txt
Normal file
|
@ -0,0 +1,140 @@
|
|||
.. _doc_change_scenes_manually:
|
||||
|
||||
Change scenes manually
|
||||
======================
|
||||
|
||||
Sometimes it helps to have more control over how one swaps scenes around.
|
||||
As mentioned above, a :ref:`Viewport <class_Viewport>`'s child nodes
|
||||
will render to the image it generates. This holds true even for nodes outside
|
||||
of the "current" scene. Autoloads fall into this category, but so do
|
||||
scenes which one instances and adds to the tree at runtime:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var simultaneous_scene = preload("res://levels/level2.tscn").instance()
|
||||
|
||||
func _add_a_scene_manually():
|
||||
# This is like autoloading the scene, only
|
||||
# it happens after already loading the main scene.
|
||||
get_tree().get_root().add_child(simultaneous_scene)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public PackedScene simultaneousScene;
|
||||
|
||||
public MyClass()
|
||||
{
|
||||
simultaneousScene = (PackedScene)ResourceLoader.Load("res://levels/level2.tscn").instance();
|
||||
}
|
||||
|
||||
public void _AddASceneManually()
|
||||
{
|
||||
// This is like autoloading the scene, only
|
||||
// it happens after already loading the main scene.
|
||||
GetTree().GetRoot().AddChild(simultaneousScene);
|
||||
}
|
||||
|
||||
To complete the cycle and swap out the new scene with the old one,
|
||||
developers have a choice to make. Many strategies exist for removing a scene
|
||||
from view of the :ref:`Viewport <class_Viewport>`. The tradeoffs involve
|
||||
balancing operation speed and memory consumption as well as balancing data
|
||||
access and integrity.
|
||||
|
||||
1. **We can delete the existing scene.**
|
||||
:ref:`SceneTree.change_scene() <class_SceneTree_method_change_scene>` and
|
||||
:ref:`SceneTree.change_scene_to() <class_SceneTree_method_change_scene_to>`
|
||||
will delete the current scene immediately. Developers can also delete the
|
||||
main scene though. Assuming the root node's name is "Main", one could do
|
||||
``get_node("/root/Main").free()`` to delete the whole scene.
|
||||
|
||||
- Unloads memory.
|
||||
|
||||
- Pro: RAM is no longer dragging the dead weight.
|
||||
|
||||
- Con: Returning to that scene is now more expensive since it must be
|
||||
loaded back into memory again (takes time AND memory). Not a problem
|
||||
if returning soon is unnecessary.
|
||||
|
||||
- Con: No longer have access to that scene's data. Not a problem if
|
||||
using that data soon is unnecessary.
|
||||
|
||||
- Note: It can be useful to preserve the data in a soon-to-be-deleted
|
||||
scene by re-attaching one or more of its nodes to a different scene,
|
||||
or even directly to the :ref:`SceneTree <class_SceneTree>`.
|
||||
|
||||
- Processing stops.
|
||||
|
||||
- Pro: No nodes means no process, physics process, or input
|
||||
handling. The CPU is available to work on the new scene's contents.
|
||||
|
||||
- Con: Those nodes' processing and input handling no longer operate.
|
||||
Not a problem if using the updated data is unnecessary.
|
||||
|
||||
2. **We can hide the existing scene.** By changing the visibility or collision
|
||||
detection of the nodes, we can hide the entire node sub-tree from the
|
||||
player's perspective.
|
||||
|
||||
- Memory still exists.
|
||||
|
||||
- Pro: One can still access the data if need be.
|
||||
|
||||
- Pro: There's no need to move any more nodes around to save data.
|
||||
|
||||
- Con: More data is being kept in memory which will be become a problem
|
||||
on memory-sensitive platforms like web or mobile.
|
||||
|
||||
- Processing continues.
|
||||
|
||||
- Pro: Data continues to receive processing updates, so the scene will
|
||||
keep updated any data within it that relies on delta time or frame
|
||||
data.
|
||||
|
||||
- Pro: Nodes are still members of groups (since groups belong to the
|
||||
:ref:`SceneTree <class_SceneTree>`).
|
||||
|
||||
- Con: The CPU's attention is now divided between both scenes. Too much
|
||||
load could result in low frame rates. One should be sure to test
|
||||
performance as they go to ensure the target platform can support the
|
||||
load they are giving it.
|
||||
|
||||
3. **We can remove the existing scene from the tree.** Assign a variable
|
||||
to the existing scene's root node. Then use
|
||||
:ref:`Node.remove_child(Node) <class_Node_method_remove_child>` to detach the entire
|
||||
scene from the tree.
|
||||
|
||||
- Memory still exists (similar pros/cons as with hiding it from view).
|
||||
|
||||
- Processing stops (similar pros/cons as with deleting it completely).
|
||||
|
||||
- Pro: This variation of "hiding" it is much easier to show/hide. Rather
|
||||
than potentially keeping track of multiple changes to the scene, one
|
||||
must only call the one method add/remove_child pair of methods. It is
|
||||
similar to disabling game objects in other engines.
|
||||
|
||||
- Con: Unlike with hiding it from view only, the data contained within
|
||||
the scene will become stale if it relies on delta time, input, groups,
|
||||
or other data that is derived from :ref:`SceneTree <class_SceneTree>`
|
||||
access.
|
||||
|
||||
There are also cases where one may wish to have many scenes present at the same
|
||||
time. Perhaps one is adding their own singleton at runtime, or preserving a
|
||||
a scene's data between scene changes (adding the scene to the root node).
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
get_tree().get_root().add_child(scene)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
GetTree().GetRoot().AddChild(scene);
|
||||
|
||||
Perhaps instead they wish to display multiple scenes at the same time using
|
||||
:ref:`ViewportContainers <class_ViewportContainer>`. This is optimal in
|
||||
cases where the intent is to render different content in different parts of the
|
||||
screen. Minimaps and split-screen multiplayer are good examples.
|
||||
|
||||
Each option will have cases where it is best appropriate, so one must
|
||||
examine the effects of each and determine what path best fits
|
||||
their unique situation.
|
156
_sources/tutorials/scripting/creating_script_templates.rst.txt
Normal file
156
_sources/tutorials/scripting/creating_script_templates.rst.txt
Normal file
|
@ -0,0 +1,156 @@
|
|||
.. _doc_creating_script_templates:
|
||||
|
||||
Creating script templates
|
||||
=========================
|
||||
|
||||
Godot provides a way to use script templates as seen in the
|
||||
``Script Create Dialog`` while creating a new script:
|
||||
|
||||
.. image:: img/script_create_dialog_templates.png
|
||||
|
||||
A set of default script templates is provided by default, but it's also possible
|
||||
to modify existing and create new ones, both per project and the editor.
|
||||
|
||||
Locating the templates
|
||||
----------------------
|
||||
|
||||
There are two places where templates can be managed.
|
||||
|
||||
Editor-defined templates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These are available globally throughout any project. The location of these
|
||||
templates are determined per each OS:
|
||||
|
||||
- Windows: ``%APPDATA%\Godot\script_templates\``
|
||||
- Linux: ``$HOME/.config/godot/script_templates/``
|
||||
- macOS: ``$HOME/Library/Application Support/Godot/script_templates/``
|
||||
|
||||
If no ``script_templates`` is detected, Godot will create a default set of
|
||||
built-in templates automatically, so this logic can be used to reset the default
|
||||
templates in case you've accidentally overwritten them.
|
||||
|
||||
Project-defined templates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The default path to search for templates is the
|
||||
``res://script_templates/`` directory. The path can be changed by configuring
|
||||
the ``editor/script_templates_search_path`` setting in the
|
||||
:ref:`ProjectSettings <class_ProjectSettings>`, both via code and the editor.
|
||||
|
||||
If no ``script_templates`` directory is found within a project, it is simply
|
||||
ignored.
|
||||
|
||||
Language support and overriding behavior
|
||||
----------------------------------------
|
||||
|
||||
Depending on whether a particular language implements a way to generate scripts
|
||||
out of templates, it's possible to create a template which can be recognized by
|
||||
that language according to template's file extension. For GDScript and C#, the
|
||||
extensions must be ``gd`` and ``cs`` respectively.
|
||||
|
||||
.. note:: The script templates have the same extension as the regular script
|
||||
files. This may lead to an issue of a script parser treating those templates as
|
||||
actual scripts within a project. To avoid this, make sure to ignore the
|
||||
directory containing them by creating an empty ``.gdignore`` file. The directory won't be
|
||||
visible throughout the project's filesystem anymore, yet the templates can be
|
||||
modified by an external text editor anytime.
|
||||
|
||||
The built-in editor templates are automatically shadowed by the project-specific
|
||||
templates given both scripts have the same filename.
|
||||
|
||||
Default template
|
||||
----------------
|
||||
|
||||
The ``Default`` template is always generated dynamically per language and cannot
|
||||
be configured nor overridden, but you can use these as the base for creating
|
||||
other templates.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends %BASE%
|
||||
|
||||
|
||||
# Declare member variables here. Examples:
|
||||
# var a%INT_TYPE% = 2
|
||||
# var b%STRING_TYPE% = "text"
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready()%VOID_RETURN%:
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
#func _process(delta%FLOAT_TYPE%)%VOID_RETURN%:
|
||||
# pass
|
||||
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public class %CLASS% : %BASE%
|
||||
{
|
||||
// Declare member variables here. Examples:
|
||||
// private int a = 2;
|
||||
// private string b = "text";
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// // Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
// public override void _Process(float delta)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
}
|
||||
|
||||
List of template placeholders
|
||||
-----------------------------
|
||||
|
||||
The following describes the complete list of built-in template placeholders
|
||||
which are currently implemented.
|
||||
|
||||
Base placeholders
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
+-------------+----------------------------------------------------------------+
|
||||
| Placeholder | Description |
|
||||
+=============+================================================================+
|
||||
| ``%CLASS%`` | The name of the new class (used in C# only). |
|
||||
+-------------+----------------------------------------------------------------+
|
||||
| ``%BASE%`` | The base type a new script inherits from. |
|
||||
+-------------+----------------------------------------------------------------+
|
||||
| ``%TS%`` | Indentation placeholder. The exact type and number of |
|
||||
| | whitespace characters used for indentation is determined by |
|
||||
| | the ``text_editor/indent/type`` and ``text_editor/indent/size``|
|
||||
| | settings in the :ref:`EditorSettings <class_EditorSettings>` |
|
||||
| | respectively. |
|
||||
+-------------+----------------------------------------------------------------+
|
||||
|
||||
Type placeholders
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
These are only relevant for GDScript with static typing. Whether these
|
||||
placeholders are actually replaced is determined by the
|
||||
``text_editor/completion/add_type_hints`` setting in the
|
||||
:ref:`EditorSettings <class_EditorSettings>`.
|
||||
|
||||
+-------------------+--------------+
|
||||
| Placeholder | Value |
|
||||
+===================+==============+
|
||||
| ``%INT_TYPE%`` | ``: int`` |
|
||||
+-------------------+--------------+
|
||||
| ``%STRING_TYPE%`` | ``: String`` |
|
||||
+-------------------+--------------+
|
||||
| ``%FLOAT_TYPE%`` | ``: float`` |
|
||||
+-------------------+--------------+
|
||||
| ``%VOID_RETURN%`` | ``-> void`` |
|
||||
+-------------------+--------------+
|
213
_sources/tutorials/scripting/cross_language_scripting.rst.txt
Normal file
213
_sources/tutorials/scripting/cross_language_scripting.rst.txt
Normal file
|
@ -0,0 +1,213 @@
|
|||
.. _doc_cross_language_scripting:
|
||||
|
||||
Cross-language scripting
|
||||
========================
|
||||
|
||||
Godot allows you to mix and match scripting languages to suit your needs.
|
||||
This means a single project can define nodes in both C# and GDScript.
|
||||
This page will go through the possible interactions between two nodes written
|
||||
in different languages.
|
||||
|
||||
The following two scripts will be used as references throughout this page.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Node
|
||||
|
||||
var str1 : String = "foo"
|
||||
var str2 : String setget ,get_str2
|
||||
|
||||
func get_str2() -> String:
|
||||
return "foofoo"
|
||||
|
||||
func print_node_name(node : Node) -> void:
|
||||
print(node.get_name())
|
||||
|
||||
func print_array(arr : Array) -> void:
|
||||
for element in arr:
|
||||
print(element)
|
||||
|
||||
func print_n_times(msg : String, n : int) -> void:
|
||||
for i in range(n):
|
||||
print(msg)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public class MyCSharpNode : Node
|
||||
{
|
||||
public String str1 = "bar";
|
||||
public String str2 { get { return "barbar"; } }
|
||||
|
||||
public void PrintNodeName(Node node)
|
||||
{
|
||||
GD.Print(node.GetName());
|
||||
}
|
||||
|
||||
public void PrintArray(String[] arr)
|
||||
{
|
||||
foreach (String element in arr)
|
||||
{
|
||||
GD.Print(element);
|
||||
}
|
||||
}
|
||||
|
||||
public void PrintNTimes(String msg, int n)
|
||||
{
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
GD.Print(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Instantiating nodes
|
||||
-------------------
|
||||
|
||||
If you're not using nodes from the scene tree, you'll probably want to
|
||||
instantiate nodes directly from the code.
|
||||
|
||||
Instantiating C# nodes from GDScript
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Using C# from GDScript doesn't need much work. Once loaded
|
||||
(see :ref:`doc_gdscript_classes_as_resources`), the script can be instantiated
|
||||
with :ref:`new() <class_CSharpScript_method_new>`.
|
||||
|
||||
::
|
||||
|
||||
var my_csharp_script = load("res://path_to_cs_file.cs")
|
||||
var my_csharp_node = my_csharp_script.new()
|
||||
print(my_csharp_node.str2) # barbar
|
||||
|
||||
.. warning::
|
||||
|
||||
When creating ``.cs`` scripts, you should always keep in mind that the class
|
||||
Godot will use is the one named like the ``.cs`` file itself. If that class
|
||||
does not exist in the file, you'll see the following error:
|
||||
``Invalid call. Nonexistent function `new` in base``.
|
||||
|
||||
For example, MyCoolNode.cs should contain a class named MyCoolNode.
|
||||
|
||||
The C# class needs to derive a Godot class, for example ``Godot.Object``.
|
||||
Otherwise, the same error will occur.
|
||||
|
||||
You also need to check your ``.cs`` file is referenced in the project's
|
||||
``.csproj`` file. Otherwise, the same error will occur.
|
||||
|
||||
Instantiating GDScript nodes from C#
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
From the C# side, everything work the same way. Once loaded, the GDScript can
|
||||
be instantiated with :ref:`GDScript.New() <class_GDScript_method_new>`.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
GDScript MyGDScript = (GDScript) GD.Load("res://path_to_gd_file.gd");
|
||||
Object myGDScriptNode = (Godot.Object) MyGDScript.New(); // This is a Godot.Object
|
||||
|
||||
Here we are using an :ref:`class_Object`, but you can use type conversion like
|
||||
explained in :ref:`doc_c_sharp_features_type_conversion_and_casting`.
|
||||
|
||||
Accessing fields
|
||||
----------------
|
||||
|
||||
Accessing C# fields from GDScript
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Accessing C# fields from GDScript is straightforward, you shouldn't have
|
||||
anything to worry about.
|
||||
|
||||
::
|
||||
|
||||
print(my_csharp_node.str1) # bar
|
||||
my_csharp_node.str1 = "BAR"
|
||||
print(my_csharp_node.str1) # BAR
|
||||
|
||||
print(my_csharp_node.str2) # barbar
|
||||
# my_csharp_node.str2 = "BARBAR" # This line will hang and crash
|
||||
|
||||
Note that it doesn't matter if the field is defined as a property or an
|
||||
attribute. However, trying to set a value on a property that does not define
|
||||
a setter will result in a crash.
|
||||
|
||||
Accessing GDScript fields from C#
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As C# is statically typed, accessing GDScript from C# is a bit more
|
||||
convoluted, you will have to use :ref:`Object.Get() <class_Object_method_get>`
|
||||
and :ref:`Object.Set() <class_Object_method_set>`. The first argument is the name of the field you want to access.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
GD.Print(myGDScriptNode.Get("str1")); // foo
|
||||
myGDScriptNode.Set("str1", "FOO");
|
||||
GD.Print(myGDScriptNode.Get("str1")); // FOO
|
||||
|
||||
GD.Print(myGDScriptNode.Get("str2")); // foofoo
|
||||
// myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything
|
||||
|
||||
Keep in mind that when setting a field value you should only use types the
|
||||
GDScript side knows about.
|
||||
Essentially, you want to work with built-in types as described in :ref:`doc_gdscript` or classes extending :ref:`class_Object`.
|
||||
|
||||
Calling methods
|
||||
---------------
|
||||
|
||||
Calling C# methods from GDScript
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Again, calling C# methods from GDScript should be straightforward. The
|
||||
marshalling process will do its best to cast the arguments to match
|
||||
function signatures.
|
||||
If that's impossible, you'll see the following error: ``Invalid call. Nonexistent function `FunctionName```.
|
||||
|
||||
::
|
||||
|
||||
my_csharp_node.PrintNodeName(self) # myGDScriptNode
|
||||
# my_csharp_node.PrintNodeName() # This line will fail.
|
||||
|
||||
my_csharp_node.PrintNTimes("Hello there!", 2) # Hello there! Hello there!
|
||||
|
||||
my_csharp_node.PrintArray(["a", "b", "c"]) # a, b, c
|
||||
my_csharp_node.PrintArray([1, 2, 3]) # 1, 2, 3
|
||||
|
||||
Calling GDScript methods from C#
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To call GDScript methods from C# you'll need to use
|
||||
:ref:`Object.Call() <class_Object_method_call>`. The first argument is the
|
||||
name of the method you want to call. The following arguments will be passed
|
||||
to said method.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
myGDScriptNode.Call("print_node_name", this); // my_csharp_node
|
||||
// myGDScriptNode.Call("print_node_name"); // This line will fail silently and won't error out.
|
||||
|
||||
myGDScriptNode.Call("print_n_times", "Hello there!", 2); // Hello there! Hello there!
|
||||
|
||||
// When dealing with functions taking a single array as arguments, we need to be careful.
|
||||
// If we don't cast it into an object, the engine will treat each element of the array as a separate argument and the call will fail.
|
||||
String[] arr = new String[] { "a", "b", "c" };
|
||||
// myGDScriptNode.Call("print_array", arr); // This line will fail silently and won't error out.
|
||||
myGDScriptNode.Call("print_array", (object)arr); // a, b, c
|
||||
myGDScriptNode.Call("print_array", (object)new int[] { 1, 2, 3 }); // 1, 2, 3
|
||||
// Note how the type of each array entry does not matter as long as it can be handled by the marshaller
|
||||
|
||||
.. warning::
|
||||
|
||||
As you can see, if the first argument of the called method is an array,
|
||||
you'll need to cast it as ``object``.
|
||||
Otherwise, each element of your array will be treated as a single argument
|
||||
and the function signature won't match.
|
||||
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
A GDScript file may not inherit from a C# script. Likewise, a C# script may not
|
||||
inherit from a GDScript file. Due to how complex this would be to implement,
|
||||
this limitation is unlikely to be lifted in the future. See
|
||||
`this GitHub issue <https://github.com/godotengine/godot/issues/38352>`__
|
||||
for more information.
|
127
_sources/tutorials/scripting/debug/debugger_panel.rst.txt
Normal file
127
_sources/tutorials/scripting/debug/debugger_panel.rst.txt
Normal file
|
@ -0,0 +1,127 @@
|
|||
.. _doc_debugger_panel:
|
||||
|
||||
Debugger panel
|
||||
==============
|
||||
|
||||
Many of Godot's debugging tools, including the debugger, can be found in the
|
||||
debugger panel at the bottom of the screen. Click on **Debugger** to open it.
|
||||
|
||||
.. image:: img/overview_debugger.png
|
||||
|
||||
The debugger panel is split into several tabs, each focusing on a specific task.
|
||||
|
||||
Debugger
|
||||
++++++++
|
||||
|
||||
The Debugger tab opens automatically when the GDScript compiler reaches
|
||||
a breakpoint in your code.
|
||||
|
||||
It gives you a `stack trace <https://en.wikipedia.org/wiki/Stack_trace>`__,
|
||||
information about the state of the object, and buttons to control the program's
|
||||
execution. When the debugger breaks on a breakpoint, a green triangle arrow is
|
||||
visible in the script editor's gutter. This arrow indicates the line of code the
|
||||
debugger broke on.
|
||||
|
||||
.. tip::
|
||||
|
||||
You can create a breakpoint by clicking the gutter in the left of the script
|
||||
editor (on the left of the line numbers). When hovering this gutter, you
|
||||
will see a transparent red dot appearing, which turns into an opaque red dot
|
||||
after the breakpoint is placed by clicking. Click the red dot again to
|
||||
remove the breakpoint. Breakpoints created this way persist across editor
|
||||
restarts, even if the script wasn't saved when exiting the editor.
|
||||
|
||||
You can also use the ``breakpoint`` keyword in GDScript to create a
|
||||
breakpoint that is stored in the script itself. Unlike breakpoints created by
|
||||
clicking in the gutter, this keyword-based breakpoint is persistent across
|
||||
different machines when using version control.
|
||||
|
||||
You can use the buttons in the top-right corner to:
|
||||
|
||||
- Skip all breakpoints. That way, you can save breakpoints for future
|
||||
debugging sessions.
|
||||
- Copy the current error message.
|
||||
- **Step Into** the code. This button takes you to the next line of code,
|
||||
and if it's a function, it steps line-by-line through the function.
|
||||
- **Step Over** the code. This button goes to the next line of code,
|
||||
but it doesn't step line-by-line through functions.
|
||||
- **Break**. This button pauses the game's execution.
|
||||
- **Continue**. This button resumes the game after a breakpoint or pause.
|
||||
|
||||
.. warning::
|
||||
|
||||
Breakpoints won't break on code if it's
|
||||
:ref:`running in a thread <doc_using_multiple_threads>`.
|
||||
This is a current limitation of the GDScript debugger.
|
||||
|
||||
Errors
|
||||
++++++
|
||||
|
||||
This is where error and warning messages are printed while running the game.
|
||||
|
||||
You can disable specific warnings in **Project Settings > Debug > GDScript**.
|
||||
|
||||
Profiler
|
||||
++++++++
|
||||
|
||||
The debugger comes with three profilers for your processor, network operations,
|
||||
and video memory.
|
||||
|
||||
The profiler is used to show why individual frames take as long as they do
|
||||
to process and render.
|
||||
|
||||
Unlike other debugging tools, the profiler does not start automatically. It can
|
||||
be started at any time during gameplay by pressing the start button. You can
|
||||
even start the profiler before opening the game to profile startup performance.
|
||||
It can also be started and stopped while the game is running without losing
|
||||
information from when it was last running. The information it records won't
|
||||
go away unless you click clear, or close the game, reopen it and start
|
||||
the profiler again.
|
||||
|
||||
After starting and stopping the profiler, you should see things being kept track
|
||||
of on the left and a graph on the right. The items listed on the left are
|
||||
everything that contributes to frame time, and they should each have a value
|
||||
for time and calls for the current frame you are looking at.
|
||||
|
||||
The frame number in the top right tells you which frame you are currently
|
||||
looking at. You can change this by using the up or down arrows, typing in the
|
||||
frame number, or clicking on the graph.
|
||||
|
||||
If you want to add something to your graph or think it looks too cluttered,
|
||||
you can check and uncheck the box next to an item to add or remove it
|
||||
from the graph.
|
||||
|
||||
Network Profiler
|
||||
++++++++++++++++
|
||||
|
||||
The Network Profiler contains a list of all the nodes that communicate over the
|
||||
multiplayer API and, for each one, some counters on the amount of incoming and
|
||||
outgoing network interactions. It also features a bandwidth meter that displays
|
||||
the total bandwidth usage at any given moment.
|
||||
|
||||
Monitors
|
||||
++++++++
|
||||
|
||||
The monitors are graphs of several aspects of the game while its running such as
|
||||
FPS, memory usage, how many nodes are in a scene and more. All monitors keep
|
||||
track of stats automatically, so even if one monitor isn't open while the game
|
||||
is running, you can open it later and see how the values changed.
|
||||
|
||||
Video RAM
|
||||
+++++++++
|
||||
|
||||
The **Video RAM** tab shows the video RAM usage of the game while it is running.
|
||||
It provides a list of every resource using video RAM by resource path, the type
|
||||
of resource it is, what format it is in, and how much Video RAM that resource is
|
||||
using. There is also a total video RAM usage number at the top right of the panel.
|
||||
|
||||
.. image:: img/video_ram.png
|
||||
|
||||
Misc
|
||||
++++
|
||||
|
||||
The **Misc** tab contains tools to identify the control nodes you are clicking
|
||||
at runtime:
|
||||
|
||||
- **Clicked Control** tells you where the clicked node is in the scene tree.
|
||||
- **Clicked Control Type** tells you the type of the node you clicked is.
|
9
_sources/tutorials/scripting/debug/index.rst.txt
Normal file
9
_sources/tutorials/scripting/debug/index.rst.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
Debug
|
||||
=====
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-learn-features-debug
|
||||
|
||||
overview_of_debugging_tools
|
||||
debugger_panel
|
|
@ -0,0 +1,152 @@
|
|||
.. _doc_overview_of_debugging_tools:
|
||||
|
||||
Overview of debugging tools
|
||||
===========================
|
||||
|
||||
This guide will give you an overview of the available debugging tools in the
|
||||
engine.
|
||||
|
||||
Godot comes with a powerful debugger and profilers to track down bugs, inspect
|
||||
your game at runtime, monitor essential metrics, and measure performances.
|
||||
It also offers options to visualize collision boxes and navigation polygons
|
||||
in the running game.
|
||||
|
||||
Finally, you have options to debug the game running on a remote device
|
||||
and to reload changes to your scenes or your code while the game is running.
|
||||
|
||||
Debugger Panel
|
||||
--------------
|
||||
|
||||
Many of Godot's debugging tools are part of the Debugger panel, which you can
|
||||
find information about in :ref:`doc_debugger_panel`.
|
||||
|
||||
Debug menu options
|
||||
------------------
|
||||
|
||||
There are a few common debug options you can toggle on or off when running
|
||||
your game in the editor, which can help you in debugging your game.
|
||||
|
||||
You can find these options in the **Debug** editor menu.
|
||||
|
||||
.. image:: img/overview_debug.png
|
||||
|
||||
Here are the descriptions of the options:
|
||||
|
||||
Deploy with Remote Debug
|
||||
++++++++++++++++++++++++
|
||||
|
||||
When exporting and deploying, the resulting executable will attempt to connect
|
||||
to the IP of your computer for debugging.
|
||||
|
||||
Small Deploy with Network FS
|
||||
++++++++++++++++++++++++++++
|
||||
|
||||
This option speeds up testing for games with a large footprint on remote devices.
|
||||
|
||||
When **Small Deploy with Network FS** is on, instead of exporting the full game,
|
||||
deploying the game builds a minimal executable. The editor then provides files
|
||||
from the project over the network.
|
||||
|
||||
Also, on Android, the game is deployed using the USB cable to speed up deployment.
|
||||
|
||||
Visible Collision Shapes
|
||||
++++++++++++++++++++++++
|
||||
|
||||
This option makes collision shapes and raycast nodes visible in the running game.
|
||||
|
||||
Visible Navigation
|
||||
++++++++++++++++++
|
||||
|
||||
Navigation meshes and polygons will be visible on the running game.
|
||||
|
||||
Sync Scene Changes
|
||||
++++++++++++++++++
|
||||
|
||||
With this option, any change you make to a scene in the editor at runtime
|
||||
appears instantly. When used remotely on a device, this is more efficient
|
||||
with the network filesystem.
|
||||
|
||||
Sync Script Changes
|
||||
+++++++++++++++++++
|
||||
|
||||
Any script that is saved will be reloaded on the running game. When used
|
||||
remotely on a device, this is more efficient with the network filesystem.
|
||||
|
||||
Script editor debug tools and options
|
||||
-------------------------------------
|
||||
|
||||
The script editor has its own set of debug tools for use with breakpoints and
|
||||
two options. The breakpoint tools can also be found in the **Debugger** tab
|
||||
of the debugger.
|
||||
|
||||
.. tip::
|
||||
|
||||
You can create a breakpoint by clicking the gutter in the left of the script
|
||||
editor (on the left of the line numbers). When hovering this gutter, you
|
||||
will see a transparent red dot appearing, which turns into an opaque red dot
|
||||
after the breakpoint is placed by clicking. Click the red dot again to
|
||||
remove the breakpoint. Breakpoints created this way persist across editor
|
||||
restarts, even if the script wasn't saved when exiting the editor.
|
||||
|
||||
You can also use the ``breakpoint`` keyword in GDScript to create a
|
||||
breakpoint that is stored in the script itself. Unlike breakpoints created by
|
||||
clicking in the gutter, this keyword-based breakpoint is persistent across
|
||||
different machines when using version control.
|
||||
|
||||
.. image:: img/overview_script_editor.png
|
||||
|
||||
The **Break** button causes a break in the script like a breakpoint would.
|
||||
**Continue** makes the game continue after pausing at a breakpoint.
|
||||
**Step Over** goes to the next line of code, and **Step Into** goes into
|
||||
a function if possible. Otherwise, it does the same thing as **Step Over**.
|
||||
|
||||
The **Keep Debugger Open** option keeps the debugger open after a scene
|
||||
has been closed. And the **Debug with External Editor** option lets you
|
||||
debug your game with an external editor.
|
||||
|
||||
When the debugger breaks on a breakpoint, a green triangle arrow is visible in
|
||||
the script editor's gutter. This arrow indicates the line of code the debugger
|
||||
broke on.
|
||||
|
||||
.. warning::
|
||||
|
||||
Breakpoints won't break on code if it's
|
||||
:ref:`running in a thread <doc_using_multiple_threads>`.
|
||||
This is a current limitation of the GDScript debugger.
|
||||
|
||||
Debug project settings
|
||||
----------------------
|
||||
|
||||
In the project settings, there is a **Debug** category with three subcategories
|
||||
which control different things.
|
||||
|
||||
Settings
|
||||
++++++++
|
||||
|
||||
These are some general settings such as printing the current FPS
|
||||
to the **Output** panel, the maximum amount of functions when profiling
|
||||
and others.
|
||||
|
||||
GDScript
|
||||
++++++++
|
||||
|
||||
These settings allow you to toggle specific GDScript warnings, such as for
|
||||
unused variables. You can also turn off warnings completely.
|
||||
|
||||
Shapes
|
||||
++++++
|
||||
|
||||
Shapes are where you can adjust the color of shapes that only appear for
|
||||
debugging purposes, such as collision and navigation shapes.
|
||||
|
||||
Remote in scene dock
|
||||
--------------------
|
||||
|
||||
When running a game in the editor two options appear at the top of the **Scene**
|
||||
dock, **Remote** and **Local**. While using **Remote** you can inspect or change
|
||||
the nodes' parameters in the running project.
|
||||
|
||||
.. image:: img/overview_remote.png
|
||||
|
||||
.. note:: Some editor settings related to debugging can be found inside
|
||||
the **Editor Settings**, under the **Network > Debug** and **Debugger** sections.
|
208
_sources/tutorials/scripting/evaluating_expressions.rst.txt
Normal file
208
_sources/tutorials/scripting/evaluating_expressions.rst.txt
Normal file
|
@ -0,0 +1,208 @@
|
|||
.. _doc_evaluating_expressions:
|
||||
|
||||
Evaluating expressions
|
||||
======================
|
||||
|
||||
Godot provides an :ref:`class_Expression` class you can use to evaluate expressions.
|
||||
|
||||
An expression can be:
|
||||
|
||||
- A mathematical expression such as ``(2 + 4) * 16/4.0``.
|
||||
- A built-in method call like ``deg2rad(90)``.
|
||||
- A method call on an user-provided script like ``update_health()``,
|
||||
if ``base_instance`` is set to a value other than ``null`` when calling
|
||||
:ref:`Expression.execute() <class_Expression_method_execute>`.
|
||||
|
||||
.. note::
|
||||
|
||||
The Expression class is independent from GDScript.
|
||||
It's available even if you compile Godot with the GDScript module disabled.
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
|
||||
To evaluate a mathematical expression, use::
|
||||
|
||||
var expression = Expression.new()
|
||||
expression.parse("20 + 10*2 - 5/2.0")
|
||||
var result = expression.execute()
|
||||
print(result) # 37.5
|
||||
|
||||
The following operators are available:
|
||||
|
||||
+------------------------+-------------------------------------------------------------------------------------+
|
||||
| Operator | Notes |
|
||||
+========================+=====================================================================================+
|
||||
| Addition ``+`` | Can also be used to concatenate strings and arrays: |
|
||||
| | - ``"hello" + " world"`` = ``hello world`` |
|
||||
| | - ``[1, 2] + [3, 4]`` = ``[1, 2, 3, 4]`` |
|
||||
+------------------------+-------------------------------------------------------------------------------------+
|
||||
| Subtraction (``-``) | |
|
||||
+------------------------+-------------------------------------------------------------------------------------+
|
||||
| Multiplication (``*``) | |
|
||||
+------------------------+-------------------------------------------------------------------------------------+
|
||||
| Division (``/``) | Performs and integer division if both operands are integers. |
|
||||
| | If at least one of them is a floating-point number, returns a floating-point value. |
|
||||
+------------------------+-------------------------------------------------------------------------------------+
|
||||
| Modulo (``%``) | Returns the remainder of an integer division. |
|
||||
+------------------------+-------------------------------------------------------------------------------------+
|
||||
|
||||
Spaces around operators are optional. Also, keep in mind the usual
|
||||
`order of operations <https://en.wikipedia.org/wiki/Order_of_operations>`__
|
||||
applies. Use parentheses to override the order of operations if needed.
|
||||
|
||||
All the Variant types supported in Godot can be used: integers, floating-point
|
||||
numbers, strings, arrays, dictionaries, colors, vectors, …
|
||||
|
||||
Arrays and dictionaries can be indexed like in GDScript::
|
||||
|
||||
# Returns 1.
|
||||
[1, 2][0]
|
||||
|
||||
# Returns 3. Negative indices can be used to count from the end of the array.
|
||||
[1, 3][-1]
|
||||
|
||||
# Returns "green".
|
||||
{"favorite_color": "green"}["favorite_color"]
|
||||
|
||||
# All 3 lines below return 7.0 (Vector3 is floating-point).
|
||||
Vector3(5, 6, 7)[2]
|
||||
Vector3(5, 6, 7)["z"]
|
||||
Vector3(5, 6, 7).z
|
||||
|
||||
Passing variables to an expression
|
||||
----------------------------------
|
||||
|
||||
You can pass variables to an expression. These variables will then
|
||||
become available in the expression's "context" and will be substituted when used
|
||||
in the expression::
|
||||
|
||||
var expression = Expression.new()
|
||||
# Define the variable names first in the second parameter of `parse()`.
|
||||
# In this example, we use `x` for the variable name.
|
||||
expression.parse("20 + 2 * x", ["x"])
|
||||
# Then define the variable values in the first parameter of `execute()`.
|
||||
# Here, `x` is assigned the integer value 5.
|
||||
var result = expression.execute([5])
|
||||
print(result) # 30
|
||||
|
||||
Both the variable names and variable values **must** be specified as an array,
|
||||
even if you only define one variable. Also, variable names are **case-sensitive**.
|
||||
|
||||
Setting a base instance for the expression
|
||||
------------------------------------------
|
||||
|
||||
By default, an expression has a base instance of ``null``. This means the
|
||||
expression has no base instance associated to it.
|
||||
|
||||
When calling :ref:`Expression.execute() <class_Expression_method_execute>`,
|
||||
you can set the value of the ``base_instance`` parameter to a specific object
|
||||
instance such as ``self``, another script instance or even a singleton::
|
||||
|
||||
func double(number):
|
||||
return number * 2
|
||||
|
||||
|
||||
func _ready():
|
||||
var expression = Expression.new()
|
||||
expression.parse("double(10)")
|
||||
|
||||
# This won't work since we're not passing the current script as the base instance.
|
||||
var result = expression.execute([], null)
|
||||
print(result) # null
|
||||
|
||||
# This will work since we're passing the current script (i.e. self)
|
||||
# as the base instance.
|
||||
result = expression.execute([], self)
|
||||
print(result) # 20
|
||||
|
||||
Associating a base instance allows doing the following:
|
||||
|
||||
- Reference the instance's constants (``const``) in the expression.
|
||||
- Reference the instance's member variables (``var``) in the expression.
|
||||
- Call methods defined in the instance and use their return values in the expression.
|
||||
|
||||
.. warning::
|
||||
|
||||
Setting a base instance to a value other than ``null`` allows referencing
|
||||
constants, member variables, and calling all methods defined in the script
|
||||
attached to the instance. Allowing users to enter expressions may allow
|
||||
cheating in your game, or may even introduce security vulnerabilities if you
|
||||
allow arbitrary clients to run expressions on other players' devices.
|
||||
|
||||
Example script
|
||||
--------------
|
||||
|
||||
The script below demonstrates what the Expression class is capable of::
|
||||
|
||||
const DAYS_IN_YEAR = 365
|
||||
var script_member_variable = 1000
|
||||
|
||||
|
||||
func _ready():
|
||||
# Constant mathexpression.
|
||||
evaluate("2 + 2")
|
||||
# Math expression with variables.
|
||||
evaluate("x + y", ["x", "y"], [60, 100])
|
||||
|
||||
# Call built-in method (hardcoded in the Expression class).
|
||||
evaluate("deg2rad(90)")
|
||||
|
||||
# Call user method (defined in the script).
|
||||
# We can do this because the expression execution is bound to `self`
|
||||
# in the `evaluate()` method.
|
||||
# Since this user method returns a value, we can use it in math expressions.
|
||||
evaluate("call_me() + DAYS_IN_YEAR + script_member_variable")
|
||||
evaluate("call_me(42)")
|
||||
evaluate("call_me('some string')")
|
||||
|
||||
|
||||
func evaluate(command, variable_names = [], variable_values = []) -> void:
|
||||
var expression = Expression.new()
|
||||
var error = expression.parse(command, variable_names)
|
||||
if error != OK:
|
||||
push_error(expression.get_error_text())
|
||||
return
|
||||
|
||||
var result = expression.execute(variable_values, self)
|
||||
|
||||
if not expression.has_execute_failed():
|
||||
print(str(result))
|
||||
|
||||
|
||||
func call_me(argument = null):
|
||||
print("\nYou called 'call_me()' in the expression text.")
|
||||
if argument:
|
||||
print("Argument passed: %s" % argument)
|
||||
|
||||
# The method's return value is also the expression's return value.
|
||||
return 0
|
||||
|
||||
The output from the script will be::
|
||||
|
||||
4
|
||||
160
|
||||
1.570796
|
||||
|
||||
You called 'call_me()' in the expression text.
|
||||
1365
|
||||
|
||||
You called 'call_me()' in the expression text.
|
||||
Argument passed: 42
|
||||
0
|
||||
|
||||
You called 'call_me()' in the expression text.
|
||||
Argument passed: some string
|
||||
0
|
||||
|
||||
Built-in functions
|
||||
------------------
|
||||
|
||||
Most methods available in the :ref:`class_@GDScript` scope are available in the
|
||||
Expression class, even if no base instance is bound to the expression.
|
||||
The same parameters and return types are available.
|
||||
|
||||
However, unlike GDScript, parameters are **always required** even if they're
|
||||
specified as being optional in the class reference. In contrast, this
|
||||
restriction on arguments doesn't apply to user-made functions when you bind a
|
||||
base instance to the expression.
|
111
_sources/tutorials/scripting/filesystem.rst.txt
Normal file
111
_sources/tutorials/scripting/filesystem.rst.txt
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.
|
555
_sources/tutorials/scripting/gdnative/gdnative_c_example.rst.txt
Normal file
555
_sources/tutorials/scripting/gdnative/gdnative_c_example.rst.txt
Normal file
|
@ -0,0 +1,555 @@
|
|||
.. _doc_gdnative_c_example:
|
||||
|
||||
GDNative C example
|
||||
==================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial will introduce you to the bare minimum required to create GDNative
|
||||
modules. This should be your starting point into the world of GDNative.
|
||||
Understanding the contents of this tutorial will help you in understanding all
|
||||
that is to come after this.
|
||||
|
||||
Before we begin, you can download the source code to the example object we
|
||||
describe below in the `GDNative-demos repository
|
||||
<https://github.com/godotengine/gdnative-demos/tree/master/c/simple>`_.
|
||||
|
||||
This example project also contains a SConstruct file that makes compiling a
|
||||
little easier, but in this tutorial we'll be doing things by hand to
|
||||
understand the process.
|
||||
|
||||
:ref:`GDNative <class_GDNative>` can be used to create several types of
|
||||
additions to Godot, using interfaces such as
|
||||
:ref:`PluginScript <class_PluginScript>` or
|
||||
:ref:`ARVRInterfaceGDNative <class_ARVRInterfaceGDNative>`. In this tutorial we
|
||||
are going to look at creating a :ref:`NativeScript <class_NativeScript>`
|
||||
module. NativeScript allows you to write logic in C or C++ in a similar fashion
|
||||
as you would write a GDScript file. We'll be creating the C equivalent of this
|
||||
GDScript:
|
||||
|
||||
::
|
||||
|
||||
extends Reference
|
||||
|
||||
var data
|
||||
|
||||
func _ready():
|
||||
data = "World from GDScript!"
|
||||
|
||||
func get_data():
|
||||
return data
|
||||
|
||||
Future tutorials will focus on the other types of GDNative modules and explain
|
||||
when and how to use each of them.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
Before we start you'll need a few things:
|
||||
|
||||
1) A Godot executable for your target version.
|
||||
2) A C compiler. On Linux, install ``gcc`` or ``clang`` from your package
|
||||
manager. On macOS, you can install Xcode from the Mac App Store. On Windows,
|
||||
you can use Visual Studio 2015 or later, or MinGW-w64.
|
||||
3) A Git clone of the `godot-headers
|
||||
repository <https://github.com/godotengine/godot-headers.git>`_: these are
|
||||
the C headers for Godot's public API exposed to GDNative.
|
||||
|
||||
For the latter, we suggest that you create a dedicated folder for this GDNative
|
||||
example project, open a terminal in that folder and execute:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
git clone https://github.com/godotengine/godot-headers.git --branch=3.4
|
||||
|
||||
This will download the required files into that folder.
|
||||
|
||||
.. tip::
|
||||
|
||||
If you plan to use Git for your GDNative project, you can also add
|
||||
``godot-headers`` as a Git submodule.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``godot-headers`` repository has different branches. As Godot evolves,
|
||||
so does GDNative. While we try to preserve compatibility between version,
|
||||
you should always build your GDNative module against headers matching the
|
||||
Godot stable branch (e.g. ``3.4``) and ideally actual release (e.g.
|
||||
``3.4.4-stable``) that you use.
|
||||
GDNative modules built against older versions of the Godot headers *may*
|
||||
work with newer versions of the engine, but not the other way around.
|
||||
|
||||
The ``master`` branch of the ``godot-headers`` repository is kept in line with
|
||||
the ``master`` branch of Godot and thus contains the GDNative class and
|
||||
structure definitions that will work with the latest development builds.
|
||||
|
||||
If you want to write a GDNative module for a stable version of Godot, look at
|
||||
the available Git tags (with ``git tags``) for the one matching your engine
|
||||
version. In the ``godot-headers`` repository, such tags are prefixed with
|
||||
``godot-``, so you can e.g. checkout the ``godot-3.4.4-stable`` tag for use with
|
||||
Godot 3.4.4. In your cloned repository, you can do:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
git checkout godot-3.4.4-stable
|
||||
|
||||
If a tag matching your stable release is missing for any reason, you can fall
|
||||
back to the matching stable branch (e.g. ``3.4``), which you would also check
|
||||
out with ``git checkout 3.4``.
|
||||
|
||||
If you are building Godot from source with your own changes that impact
|
||||
GDNative, you can find the updated class and structure definition in
|
||||
``<godotsource>/modules/gdnative/include``
|
||||
|
||||
Our C source
|
||||
------------
|
||||
|
||||
Let's start by writing our main code. Eventually, we want to end up with a file
|
||||
structure that looks along those lines:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
+ <your development folder>
|
||||
+ godot-headers
|
||||
- <lots of files here>
|
||||
+ simple
|
||||
+ bin
|
||||
- libsimple.dll/so/dylib
|
||||
- libsimple.gdnlib
|
||||
- simple.gdns
|
||||
main.tscn
|
||||
project.godot
|
||||
+ src
|
||||
- simple.c
|
||||
|
||||
Open up Godot and create a new project called "simple" alongside your
|
||||
``godot-headers`` Git clone. This will create the ``simple`` folder and
|
||||
``project.godot`` file. Then manually create a ``src`` folder alongside the
|
||||
``simple`` folder, and a ``bin`` subfolder in the ``simple`` folder.
|
||||
|
||||
We're going to start by having a look at what our ``simple.c`` file contains.
|
||||
Now, for our example here we're making a single C source file without a header
|
||||
to keep things simple. Once you start writing bigger projects it is advisable
|
||||
to break your project up into multiple files. That however falls outside of the
|
||||
scope of this tutorial.
|
||||
|
||||
We'll be looking at the source code bit by bit so all the parts below should all
|
||||
be put together into one big file. Each section will be explained as we add it.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include <gdnative_api_struct.gen.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
const godot_gdnative_core_api_struct *api = NULL;
|
||||
const godot_gdnative_ext_nativescript_api_struct *nativescript_api = NULL;
|
||||
|
||||
The above code includes the GDNative API struct header and a standard header
|
||||
that we will use further down for string operations.
|
||||
It then defines two pointers to two different structs. GDNative supports a large
|
||||
collection of functions for calling back into the main Godot executable. In
|
||||
order for your module to have access to these functions, GDNative provides your
|
||||
application with a struct containing pointers to all these functions.
|
||||
|
||||
To keep this implementation modular and easily extendable, the core functions
|
||||
are available directly through the "core" API struct, but additional functions
|
||||
have their own "GDNative structs" that are accessible through extensions.
|
||||
|
||||
In our example, we access one of these extension to gain access to the functions
|
||||
specifically needed for NativeScript.
|
||||
|
||||
A NativeScript behaves like any other script in Godot. Because the NativeScript
|
||||
API is rather low level, it requires the library to specify many things more
|
||||
verbosely than other scripting systems, such as GDScript. When a NativeScript
|
||||
instance gets created, a library-given constructor gets called. When that
|
||||
instance gets destroyed, the given destructor will be executed.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void *simple_constructor(godot_object *p_instance, void *p_method_data);
|
||||
void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data);
|
||||
godot_variant simple_get_data(godot_object *p_instance, void *p_method_data,
|
||||
void *p_user_data, int p_num_args, godot_variant **p_args);
|
||||
|
||||
These are forward declarations for the functions we'll be implementing for our
|
||||
object. A constructor and destructor is needed. Additionally, the object will
|
||||
have a single method called ``get_data``.
|
||||
|
||||
Next up is the first of the entry points Godot will call when our dynamic
|
||||
library is loaded. These methods are all prefixed with ``godot_`` (you can
|
||||
change this later on) followed by their name. ``gdnative_init`` is a function
|
||||
that initializes our dynamic library. Godot will give it a pointer to a
|
||||
structure that contains various bits of information we may find useful among
|
||||
which the pointers to our API structures.
|
||||
|
||||
For any additional API structures we need to loop through our extensions array
|
||||
and check the type of extension.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *p_options) {
|
||||
api = p_options->api_struct;
|
||||
|
||||
// Now find our extensions.
|
||||
for (int i = 0; i < api->num_extensions; i++) {
|
||||
switch (api->extensions[i]->type) {
|
||||
case GDNATIVE_EXT_NATIVESCRIPT: {
|
||||
nativescript_api = (godot_gdnative_ext_nativescript_api_struct *)api->extensions[i];
|
||||
}; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Next up is ``gdnative_terminate`` which is called before the library is
|
||||
unloaded. Godot will unload the library when no object uses it anymore. Here,
|
||||
you can do any cleanup you may need to do. For our example, we're simply going
|
||||
to clear our API pointers.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *p_options) {
|
||||
api = NULL;
|
||||
nativescript_api = NULL;
|
||||
}
|
||||
|
||||
Finally, we have ``nativescript_init`` which is the most important function we'll
|
||||
need today. This function will be called by Godot as part of loading a GDNative
|
||||
library and communicates back to the engine what objects we make available.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void GDN_EXPORT godot_nativescript_init(void *p_handle) {
|
||||
godot_instance_create_func create = { NULL, NULL, NULL };
|
||||
create.create_func = &simple_constructor;
|
||||
|
||||
godot_instance_destroy_func destroy = { NULL, NULL, NULL };
|
||||
destroy.destroy_func = &simple_destructor;
|
||||
|
||||
nativescript_api->godot_nativescript_register_class(p_handle, "SIMPLE", "Reference",
|
||||
create, destroy);
|
||||
|
||||
godot_instance_method get_data = { NULL, NULL, NULL };
|
||||
get_data.method = &simple_get_data;
|
||||
|
||||
godot_method_attributes attributes = { GODOT_METHOD_RPC_MODE_DISABLED };
|
||||
|
||||
nativescript_api->godot_nativescript_register_method(p_handle, "SIMPLE", "get_data",
|
||||
attributes, get_data);
|
||||
}
|
||||
|
||||
We first tell the engine which classes are implemented by calling
|
||||
``nativescript_register_class``. The first parameter here is the handle pointer
|
||||
given to us. The second is the name of our object class. The third is the type
|
||||
of object in Godot that we 'inherit' from; this is not true inheritance but it's
|
||||
close enough. Finally, our fourth and fifth parameters are descriptions for our
|
||||
constructor and destructor.
|
||||
|
||||
We then tell Godot about our methods (well our one method in this case), by
|
||||
calling ``nativescript_register_method`` for each method of our class. In our
|
||||
case, that is just ``get_data``. Our first parameter is yet again our handle
|
||||
pointer. The second is again the name of the object class we're registering. The
|
||||
third is the name of our function as it will be known to GDScript. The fourth is
|
||||
our attributes setting (see ``godot_method_rpc_mode`` enum in
|
||||
``godot-headers/nativescript/godot_nativescript.h`` for possible values). The
|
||||
fifth and final parameter is a description of which function to call when the
|
||||
method gets called.
|
||||
|
||||
The description struct ``instance_method`` contains the function pointer to the
|
||||
function itself as first field. The other two fields in these structs are for
|
||||
specifying per-method userdata. The second is the ``method_data`` field which is
|
||||
passed on every function call as the ``p_method_data`` argument. This is useful
|
||||
to reuse one function for different methods on possibly multiple different
|
||||
script-classes. If the ``method_data`` value is a pointer to memory that needs
|
||||
to be freed, the third ``free_func`` field can contain a pointer to a function
|
||||
that will free that memory. That free function gets called when the script
|
||||
itself (not instance!) gets unloaded (so usually at library-unload time).
|
||||
|
||||
Now, it's time to start working on the functions of our object. First, we define
|
||||
a structure that we use to store the member data of an instance of our GDNative
|
||||
class.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
typedef struct user_data_struct {
|
||||
char data[256];
|
||||
} user_data_struct;
|
||||
|
||||
And then, we define our constructor. All we do in our constructor is allocate
|
||||
memory for our structure and fill it with some data. Note that we use Godot's
|
||||
memory functions so the memory gets tracked and then return the pointer to our
|
||||
new structure. This pointer will act as our instance identifier in case multiple
|
||||
objects are instantiated.
|
||||
|
||||
This pointer will be passed to any of our functions related to our object as a
|
||||
parameter called ``p_user_data``, and can both be used to identify our instance
|
||||
and to access its member data.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void *simple_constructor(godot_object *p_instance, void *p_method_data) {
|
||||
user_data_struct *user_data = api->godot_alloc(sizeof(user_data_struct));
|
||||
strcpy(user_data->data, "World from GDNative!");
|
||||
|
||||
return user_data;
|
||||
}
|
||||
|
||||
Our destructor is called when Godot is done with our object and we free our
|
||||
instances' member data.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
void simple_destructor(godot_object *p_instance, void *p_method_data, void *p_user_data) {
|
||||
api->godot_free(p_user_data);
|
||||
}
|
||||
|
||||
And finally, we implement our ``get_data`` function. Data is always sent and
|
||||
returned as variants so in order to return our data, which is a string, we first
|
||||
need to convert our C string to a Godot string object, and then copy that string
|
||||
object into the variant we are returning.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
godot_variant simple_get_data(godot_object *p_instance, void *p_method_data,
|
||||
void *p_user_data, int p_num_args, godot_variant **p_args) {
|
||||
godot_string data;
|
||||
godot_variant ret;
|
||||
user_data_struct *user_data = (user_data_struct *)p_user_data;
|
||||
|
||||
api->godot_string_new(&data);
|
||||
api->godot_string_parse_utf8(&data, user_data->data);
|
||||
api->godot_variant_new_string(&ret, &data);
|
||||
api->godot_string_destroy(&data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Strings are heap-allocated in Godot, so they have a destructor which frees the
|
||||
memory. Destructors are named ``godot_TYPENAME_destroy``. When a Variant gets
|
||||
created with a String, it references the String. That means that the original
|
||||
String can be "destroyed" to decrease the ref-count. If that does not happen the
|
||||
String memory will leak since the ref-count will never be zero and the memory
|
||||
never deallocated. The returned variant gets automatically destroyed by Godot.
|
||||
|
||||
.. note::
|
||||
|
||||
In more complex operations it can be confusing the keep track of which value
|
||||
needs to be deallocated and which does not. As a general rule: call
|
||||
``godot_TYPENAME_destroy`` when a C++ destructor would be called instead.
|
||||
The String destructor would be called in C++ after the Variant was created,
|
||||
so the same is necessary in C.
|
||||
|
||||
The variant we return is destroyed automatically by Godot.
|
||||
|
||||
And that is the whole source code of our module.
|
||||
|
||||
Compiling
|
||||
---------
|
||||
|
||||
We now need to compile our source code. As mentioned our example project on
|
||||
GitHub contains a SCons configuration that does all the hard work for you, but
|
||||
for our tutorial here we are going to call the compilers directly.
|
||||
|
||||
Assuming you are sticking to the folder structure suggested above, it is best to
|
||||
open a terminal session in the ``src`` folder and execute the commands from
|
||||
there. Make sure to create the ``bin`` folder before you proceed.
|
||||
|
||||
On Linux:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
gcc -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.o
|
||||
gcc -rdynamic -shared simple.o -o ../simple/bin/libsimple.so
|
||||
|
||||
On macOS:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
clang -std=c11 -fPIC -c -I../godot-headers simple.c -o simple.os
|
||||
clang -dynamiclib simple.os -o ../simple/bin/libsimple.dylib
|
||||
|
||||
On Windows:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
cl /Fosimple.obj /c simple.c /nologo -EHsc -DNDEBUG /MD /I. /I..\godot-headers
|
||||
link /nologo /dll /out:..\simple\bin\libsimple.dll /implib:..\simple\bin\libsimple.lib simple.obj
|
||||
|
||||
.. note::
|
||||
|
||||
On the Windows build you also end up with a ``libsimple.lib`` library. This
|
||||
is a library that you can compile into a project to provide access to the
|
||||
DLL. We get it as a byproduct and we do not need it :)
|
||||
When exporting your game for release this file will be ignored.
|
||||
|
||||
Creating the GDNativeLibrary (``.gdnlib``) file
|
||||
-----------------------------------------------
|
||||
|
||||
With our module compiled, we now need to create a corresponding
|
||||
:ref:`GDNativeLibrary <class_GDNativeLibrary>` resource with ``.gdnlib``
|
||||
extension which we place alongside our dynamic libraries. This file tells Godot
|
||||
what dynamic libraries are part of our module and need to be loaded per
|
||||
platform.
|
||||
|
||||
We can use Godot to generate this file, so open the "simple" project in the
|
||||
editor.
|
||||
|
||||
Start by clicking the create resource button in the Inspector:
|
||||
|
||||
.. image:: img/new_resource.gif
|
||||
|
||||
And select ``GDNativeLibrary``:
|
||||
|
||||
.. image:: img/gdnativelibrary_resource.png
|
||||
|
||||
You should see a contextual editor appear in the bottom panel. Use the "Expand
|
||||
Bottom Panel" button in the bottom right to expand it to full height:
|
||||
|
||||
.. image:: img/gdnativelibrary_editor.png
|
||||
|
||||
General properties
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the Inspector, you have various properties to control loading the library.
|
||||
|
||||
If *Load Once* is enabled, our library is loaded only once and each individual
|
||||
script that uses our library will use the same data. Any variable you define
|
||||
globally will be accessible from any instance of your object you create. If
|
||||
*Load Once* is disabled, a new copy of the library is loaded into memory each
|
||||
time a script accesses the library.
|
||||
|
||||
If *Singleton* is enabled, our library is automatically loaded and a function
|
||||
called ``godot_gdnative_singleton`` is called. We'll leave that for another
|
||||
tutorial.
|
||||
|
||||
The *Symbol Prefix* is a prefix for our core functions, such as ``godot_`` in
|
||||
``godot_nativescript_init`` seen earlier. If you use multiple GDNative libraries
|
||||
that you wish to statically link, you will have to use different prefixes. This
|
||||
again is a subject to dive into deeper in a separate tutorial, it is only needed
|
||||
at this time for deployment to iOS as this platform does not like dynamic
|
||||
libraries.
|
||||
|
||||
*Reloadable* defines whether the library should be reloaded when the editor
|
||||
loses and gains focus, typically to pick up new or modified symbols from any
|
||||
change made to the library externally.
|
||||
|
||||
Platform libraries
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The GDNativeLibrary editor plugin lets you configure two things for each
|
||||
platform and architecture that you aim to support.
|
||||
|
||||
The *Dynamic Library* column (``entry`` section in the saved file) tells us for
|
||||
each platform and feature combination which dynamic library has to be loaded.
|
||||
This also informs the exporter which files need to be exported when exporting to
|
||||
a specific platform.
|
||||
|
||||
The *Dependencies* column (also ``dependencies`` section) tells Godot what other
|
||||
files need to be exported for each platform in order for our library to work.
|
||||
Say that your GDNative module uses another DLL to implement functionality from a
|
||||
3rd party library, this is where you list that DLL.
|
||||
|
||||
For our example, we only built libraries for Linux, macOS and/or Windows, so you
|
||||
can link them in the relevant fields by clicking the folder button. If you built
|
||||
all three libraries, you should have something like this:
|
||||
|
||||
.. image:: img/gdnativelibrary_editor_complete.png
|
||||
|
||||
Saving the resource
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We can then save our GDNativeLibrary resource as ``bin/libsimple.gdnlib`` with
|
||||
the Save button in the Inspector:
|
||||
|
||||
.. image:: img/gdnativelibrary_save.png
|
||||
|
||||
The file is saved in a text-based format and should have contents similar to
|
||||
this:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[general]
|
||||
|
||||
singleton=false
|
||||
load_once=true
|
||||
symbol_prefix="godot_"
|
||||
reloadable=true
|
||||
|
||||
[entry]
|
||||
|
||||
OSX.64="res://bin/libsimple.dylib"
|
||||
OSX.32="res://bin/libsimple.dylib"
|
||||
Windows.64="res://bin/libsimple.dll"
|
||||
X11.64="res://bin/libsimple.so"
|
||||
|
||||
[dependencies]
|
||||
|
||||
OSX.64=[ ]
|
||||
OSX.32=[ ]
|
||||
Windows.64=[ ]
|
||||
X11.64=[ ]
|
||||
|
||||
Creating the NativeScript (``.gdns``) file
|
||||
------------------------------------------
|
||||
|
||||
With our ``.gdnlib`` file we've told Godot how to load our library, now we need
|
||||
to tell it about our "SIMPLE" object class. We do this by creating a
|
||||
:ref:`NativeScript <class_NativeScript>` resource file with ``.gdns`` extension.
|
||||
|
||||
Like done for the GDNativeLibrary resource, click the button to create a new
|
||||
resource in the Inspector and select ``NativeScript``:
|
||||
|
||||
.. image:: img/nativescript_resource.png
|
||||
|
||||
The inspector will show a few properties that we need to fill. As *Class Name*
|
||||
we enter "SIMPLE" which is the object class name that we declared in our C
|
||||
source when calling ``godot_nativescript_register_class``. We also need to
|
||||
select our ``.gdnlib`` file by clicking on *Library* and selecting *Load*:
|
||||
|
||||
.. image:: img/nativescript_library.png
|
||||
|
||||
.. note::
|
||||
|
||||
The *Class Name* must have the same spelling as the one given in ``godot_nativescript_init``
|
||||
when registering the class.
|
||||
|
||||
|
||||
Finally, click on the save icon and save this as ``bin/simple.gdns``:
|
||||
|
||||
.. image:: img/save_gdns.gif
|
||||
|
||||
Now it's time to build our scene. Add a Control node to your scene as your root
|
||||
and call it ``main``. Then add a Button and a Label as child nodes. Place them
|
||||
somewhere nice on screen and give your button a name.
|
||||
|
||||
.. image:: img/c_main_scene_layout.png
|
||||
|
||||
Select the control node and attach a script to it:
|
||||
|
||||
.. image:: img/add_main_script.gif
|
||||
|
||||
Next link up the ``pressed`` signal on the button to your script:
|
||||
|
||||
.. image:: img/connect_button_signal.gif
|
||||
|
||||
Don't forget to save your scene, call it ``main.tscn``.
|
||||
|
||||
Now we can implement our ``main.gd`` code:
|
||||
|
||||
::
|
||||
|
||||
extends Control
|
||||
|
||||
# load the Simple library
|
||||
onready var data = preload("res://bin/simple.gdns").new()
|
||||
|
||||
func _on_Button_pressed():
|
||||
$Label.text = "Data = " + data.get_data()
|
||||
|
||||
After all that, our project should work. The first time you run it Godot will
|
||||
ask you what your main scene is and you select your ``main.tscn`` file and
|
||||
presto:
|
||||
|
||||
.. image:: img/c_sample_result.png
|
|
@ -0,0 +1,657 @@
|
|||
.. _doc_gdnative_cpp_example:
|
||||
|
||||
GDNative C++ example
|
||||
====================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial builds on top of the information given in the
|
||||
:ref:`GDNative C example <doc_gdnative_c_example>`, so we highly recommend you
|
||||
read that first.
|
||||
|
||||
The C++ bindings for GDNative are built on top of the NativeScript GDNative API
|
||||
and provide a nicer way to "extend" nodes in Godot using C++. This is equivalent
|
||||
to writing scripts in GDScript, but in C++ instead.
|
||||
|
||||
You can download the full example we'll be creating in this tutorial `on
|
||||
GitHub <https://github.com/BastiaanOlij/gdnative_cpp_example>`__.
|
||||
|
||||
Setting up the project
|
||||
----------------------
|
||||
|
||||
There are a few prerequisites you'll need:
|
||||
|
||||
- a Godot 3.x executable,
|
||||
- a C++ compiler,
|
||||
- SCons as a build tool,
|
||||
- a copy of the `godot-cpp
|
||||
repository <https://github.com/godotengine/godot-cpp>`__.
|
||||
|
||||
See also :ref:`Compiling <toc-devel-compiling>` as the build tools are identical
|
||||
to the ones you need to compile Godot from source.
|
||||
|
||||
You can download these repositories from GitHub or let Git do the work for you.
|
||||
Note that these repositories now have different branches for different versions
|
||||
of Godot. GDNative modules written for an earlier version of Godot will work in
|
||||
newer versions (with the exception of one breaking change in ARVR interfaces
|
||||
between 3.0 and 3.1) but not vice versa so make sure you download the correct
|
||||
branch. Also note that the version of Godot you use to generate the ``api.json``
|
||||
with becomes your minimum version.
|
||||
|
||||
.. note::
|
||||
|
||||
`GDExtension <https://godotengine.org/article/introducing-gd-extensions>`__
|
||||
has been merged in the ``master`` branch of godot-cpp,
|
||||
but it is only compatible with the upcoming Godot 4.0.
|
||||
Therefore, you need to use the ``3.x`` branch of godot-cpp to use GDNative
|
||||
and follow this example.
|
||||
|
||||
This tutorial covers only GDNative in Godot 3.x, *not* GDExtension in Godot 4.0.
|
||||
|
||||
If you are versioning your project using Git, it is a good idea to add them as
|
||||
Git submodules:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mkdir gdnative_cpp_example
|
||||
cd gdnative_cpp_example
|
||||
git init
|
||||
git submodule add -b 3.x https://github.com/godotengine/godot-cpp
|
||||
cd godot-cpp
|
||||
git submodule update --init
|
||||
|
||||
If you decide to just download the repositories or clone them into your project
|
||||
folder, make sure to keep the folder layout identical to the one described here,
|
||||
as much of the code we'll be showcasing here assumes the project follows this
|
||||
layout.
|
||||
|
||||
Do make sure you clone recursive to pull in both repositories:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mkdir gdnative_cpp_example
|
||||
cd gdnative_cpp_example
|
||||
git clone --recursive -b 3.x https://github.com/godotengine/godot-cpp
|
||||
|
||||
.. note::
|
||||
|
||||
``godot-cpp`` now includes ``godot-headers`` as a nested submodule, if you've
|
||||
manually downloaded them please make sure to place ``godot-headers`` inside
|
||||
of the ``godot-cpp`` folder.
|
||||
|
||||
You don't have to do it this way, but we've found it easiest to manage. If you
|
||||
decide to download the repositories or clone them into your folder,
|
||||
make sure to keep the folder layout the same as we've setup here. Much of
|
||||
the code we'll be showcasing here assumes the project has this layout.
|
||||
|
||||
If you cloned the example from the link specified in the introduction, the
|
||||
submodules are not automatically initialized. You will need to execute the
|
||||
following commands:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
cd gdnative_cpp_example
|
||||
git submodule update --init --recursive
|
||||
|
||||
This will clone these two repositories into your project folder.
|
||||
|
||||
Building the C++ bindings
|
||||
-------------------------
|
||||
|
||||
Now that we've downloaded our prerequisites, it is time to build the C++
|
||||
bindings.
|
||||
|
||||
The repository contains a copy of the metadata for the current Godot release,
|
||||
but if you need to build these bindings for a newer version of Godot, simply
|
||||
call the Godot executable:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
godot --gdnative-generate-json-api api.json
|
||||
|
||||
Place the resulting ``api.json`` file in the project folder and add
|
||||
``use_custom_api_file=yes custom_api_file=../api.json`` to the scons command
|
||||
below.
|
||||
|
||||
To generate and compile the bindings, use this command (replacing ``<platform>``
|
||||
with ``windows``, ``linux`` or ``osx`` depending on your OS):
|
||||
|
||||
To speed up compilation, add `-jN` at the end of the SCons command line where `N`
|
||||
is the number of CPU threads you have on your system. The example below uses 4 threads.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
cd godot-cpp
|
||||
scons platform=<platform> generate_bindings=yes -j4
|
||||
cd ..
|
||||
|
||||
This step will take a while. When it is completed, you should have static
|
||||
libraries that can be compiled into your project stored in ``godot-cpp/bin/``.
|
||||
|
||||
.. note::
|
||||
|
||||
You may need to add ``bits=64`` to the command on Windows or Linux.
|
||||
|
||||
Creating a simple plugin
|
||||
------------------------
|
||||
|
||||
Now it's time to build an actual plugin. We'll start by creating an empty Godot
|
||||
project in which we'll place a few files.
|
||||
|
||||
Open Godot and create a new project. For this example, we will place it in a
|
||||
folder called ``demo`` inside our GDNative module's folder structure.
|
||||
|
||||
In our demo project, we'll create a scene containing a Node called "Main" and
|
||||
we'll save it as ``main.tscn``. We'll come back to that later.
|
||||
|
||||
Back in the top-level GDNative module folder, we're also going to create a
|
||||
subfolder called ``src`` in which we'll place our source files.
|
||||
|
||||
You should now have ``demo``, ``godot-cpp``, ``godot-headers``, and ``src``
|
||||
directories in your GDNative module.
|
||||
|
||||
In the ``src`` folder, we'll start with creating our header file for the
|
||||
GDNative node we'll be creating. We will name it ``gdexample.h``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#ifndef GDEXAMPLE_H
|
||||
#define GDEXAMPLE_H
|
||||
|
||||
#include <Godot.hpp>
|
||||
#include <Sprite.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
class GDExample : public Sprite {
|
||||
GODOT_CLASS(GDExample, Sprite)
|
||||
|
||||
private:
|
||||
float time_passed;
|
||||
|
||||
public:
|
||||
static void _register_methods();
|
||||
|
||||
GDExample();
|
||||
~GDExample();
|
||||
|
||||
void _init(); // our initializer called by Godot
|
||||
|
||||
void _process(float delta);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
There are a few things of note to the above. We're including ``Godot.hpp`` which
|
||||
contains all our basic definitions. After that, we include ``Sprite.hpp`` which
|
||||
contains bindings to the Sprite class. We'll be extending this class in our
|
||||
module.
|
||||
|
||||
We're using the namespace ``godot``, since everything in GDNative is defined
|
||||
within this namespace.
|
||||
|
||||
Then we have our class definition, which inherits from our Sprite through a
|
||||
container class. We'll see a few side effects of this later on. The
|
||||
``GODOT_CLASS`` macro sets up a few internal things for us.
|
||||
|
||||
After that, we declare a single member variable called ``time_passed``.
|
||||
|
||||
In the next block we're defining our methods, we obviously have our constructor
|
||||
and destructor defined, but there are two other functions that will likely look
|
||||
familiar to some, and one new method.
|
||||
|
||||
The first is ``_register_methods``, which is a static function that Godot will
|
||||
call to find out which methods can be called on our NativeScript and which
|
||||
properties it exposes. The second is our ``_process`` function, which will work
|
||||
exactly the same as the ``_process`` function you're used to in GDScript. The
|
||||
third is our ``_init`` function which is called after Godot has properly set up
|
||||
our object. It has to exist even if you don't place any code in it.
|
||||
|
||||
Let's implement our functions by creating our ``gdexample.cpp`` file:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include "gdexample.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
}
|
||||
|
||||
GDExample::GDExample() {
|
||||
}
|
||||
|
||||
GDExample::~GDExample() {
|
||||
// add your cleanup here
|
||||
}
|
||||
|
||||
void GDExample::_init() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += delta;
|
||||
|
||||
Vector2 new_position = Vector2(10.0 + (10.0 * sin(time_passed * 2.0)), 10.0 + (10.0 * cos(time_passed * 1.5)));
|
||||
|
||||
set_position(new_position);
|
||||
}
|
||||
|
||||
This one should be straightforward. We're implementing each method of our class
|
||||
that we defined in our header file. Note that the ``register_method`` call
|
||||
**must** expose the ``_process`` method, otherwise Godot will not be able to use
|
||||
it. However, we do not have to tell Godot about our constructor, destructor and
|
||||
``_init`` functions.
|
||||
|
||||
The other method of note is our ``_process`` function, which simply keeps track
|
||||
of how much time has passed and calculates a new position for our sprite using a
|
||||
sine and cosine function. What stands out is calling
|
||||
``owner->set_position`` to call one of the built-in methods of our Sprite. This
|
||||
is because our class is a container class; ``owner`` points to the actual Sprite
|
||||
node our script relates to.
|
||||
|
||||
There is one more C++ file we need; we'll name it ``gdlibrary.cpp``. Our
|
||||
GDNative plugin can contain multiple NativeScripts, each with their own header
|
||||
and source file like we've implemented ``GDExample`` up above. What we need now
|
||||
is a small bit of code that tells Godot about all the NativeScripts in our
|
||||
GDNative plugin.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include "gdexample.h"
|
||||
|
||||
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
|
||||
godot::Godot::gdnative_init(o);
|
||||
}
|
||||
|
||||
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
|
||||
godot::Godot::gdnative_terminate(o);
|
||||
}
|
||||
|
||||
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
|
||||
godot::Godot::nativescript_init(handle);
|
||||
|
||||
godot::register_class<godot::GDExample>();
|
||||
}
|
||||
|
||||
Note that we are not using the ``godot`` namespace here, since the three
|
||||
functions implemented here need to be defined without a namespace.
|
||||
|
||||
The ``godot_gdnative_init`` and ``godot_gdnative_terminate`` functions get
|
||||
called respectively when Godot loads our plugin and when it unloads it. All
|
||||
we're doing here is parse through the functions in our bindings module to
|
||||
initialize them, but you might have to set up more things depending on your
|
||||
needs.
|
||||
|
||||
The important function is the third function called ``godot_nativescript_init``.
|
||||
We first call a function in our bindings library that does its usual stuff.
|
||||
After that, we call the function ``register_class`` for each of our classes in
|
||||
our library.
|
||||
|
||||
Compiling the plugin
|
||||
--------------------
|
||||
|
||||
We cannot easily write by hand a ``SConstruct`` file that SCons would use for
|
||||
building. For the purpose of this example, just use
|
||||
:download:`this hardcoded SConstruct file <files/cpp_example/SConstruct>` we've
|
||||
prepared. We'll cover a more customizable, detailed example on how to use these
|
||||
build files in a subsequent tutorial.
|
||||
|
||||
.. note::
|
||||
|
||||
This ``SConstruct`` file was written to be used with the latest ``godot-cpp``
|
||||
master, you may need to make small changes using it with older versions or
|
||||
refer to the ``SConstruct`` file in the Godot 3.0 documentation.
|
||||
|
||||
Once you've downloaded the ``SConstruct`` file, place it in your GDNative module
|
||||
folder besides ``godot-cpp``, ``godot-headers`` and ``demo``, then run:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
scons platform=<platform>
|
||||
|
||||
You should now be able to find the module in ``demo/bin/<platform>``.
|
||||
|
||||
.. note::
|
||||
|
||||
Here, we've compiled both godot-cpp and our gdexample library as debug
|
||||
builds. For optimized builds, you should compile them using the
|
||||
``target=release`` switch.
|
||||
|
||||
Using the GDNative module
|
||||
-------------------------
|
||||
|
||||
Before we jump back into Godot, we need to create two more files in
|
||||
``demo/bin/``. Both can be created using the Godot editor, but it may be faster
|
||||
to create them directly.
|
||||
|
||||
The first one is a file that lets Godot know what dynamic libraries should be
|
||||
loaded for each platform and is called ``gdexample.gdnlib``.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[general]
|
||||
|
||||
singleton=false
|
||||
load_once=true
|
||||
symbol_prefix="godot_"
|
||||
reloadable=false
|
||||
|
||||
[entry]
|
||||
|
||||
X11.64="res://bin/x11/libgdexample.so"
|
||||
Windows.64="res://bin/win64/libgdexample.dll"
|
||||
OSX.64="res://bin/osx/libgdexample.dylib"
|
||||
|
||||
[dependencies]
|
||||
|
||||
X11.64=[]
|
||||
Windows.64=[]
|
||||
OSX.64=[]
|
||||
|
||||
This file contains a ``general`` section that controls how the module is loaded.
|
||||
It also contains a prefix section which should be left on ``godot_`` for now. If
|
||||
you change this, you'll need to rename various functions that are used as entry
|
||||
points. This was added for the iPhone platform because it doesn't allow dynamic
|
||||
libraries to be deployed, yet GDNative modules are linked statically.
|
||||
|
||||
The ``entry`` section is the important bit: it tells Godot the location of the
|
||||
dynamic library in the project's filesystem for each supported platform. It will
|
||||
also result in *just* that file being exported when you export the project,
|
||||
which means the data pack won't contain libraries that are incompatible with the
|
||||
target platform.
|
||||
|
||||
Finally, the ``dependencies`` section allows you to name additional dynamic
|
||||
libraries that should be included as well. This is important when your GDNative
|
||||
plugin implements someone else's library and requires you to supply a
|
||||
third-party dynamic library with your project.
|
||||
|
||||
If you double click on the ``gdexample.gdnlib`` file within Godot, you'll see
|
||||
there are far more options to set:
|
||||
|
||||
.. image:: img/gdnative_library.png
|
||||
|
||||
The second file we need to create is a file used by each NativeScript we've
|
||||
added to our plugin. We'll name it ``gdexample.gdns`` for our gdexample
|
||||
NativeScript.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[gd_resource type="NativeScript" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://bin/gdexample.gdnlib" type="GDNativeLibrary" id=1]
|
||||
|
||||
[resource]
|
||||
|
||||
resource_name = "gdexample"
|
||||
class_name = "GDExample"
|
||||
library = ExtResource( 1 )
|
||||
|
||||
This is a standard Godot resource; you could just create it directly in your
|
||||
scene, but saving it to a file makes it much easier to reuse it in other places.
|
||||
This resource points to our gdnlib file, so that Godot can know which dynamic
|
||||
library contains our NativeScript. It also defines the ``class_name`` which
|
||||
identifies the NativeScript in our plugin we want to use.
|
||||
|
||||
Time to jump back into Godot. We load up the main scene we created way back in
|
||||
the beginning and now add a Sprite to our scene:
|
||||
|
||||
.. image:: img/gdnative_cpp_nodes.png
|
||||
|
||||
We're going to assign the Godot logo to this sprite as our texture, disable the
|
||||
``centered`` property and drag our ``gdexample.gdns`` file onto the ``script``
|
||||
property of the sprite:
|
||||
|
||||
.. image:: img/gdnative_cpp_sprite.png
|
||||
|
||||
We're finally ready to run the project:
|
||||
|
||||
.. image:: img/gdnative_cpp_animated.gif
|
||||
|
||||
Adding properties
|
||||
-----------------
|
||||
|
||||
GDScript allows you to add properties to your script using the ``export``
|
||||
keyword. In GDNative you have to register the properties and there are two ways
|
||||
of doing this. You can either bind directly to a member or use a setter and
|
||||
getter function.
|
||||
|
||||
.. note::
|
||||
|
||||
There is a third option, just like in GDScript you can directly implement the
|
||||
``_get_property_list``, ``_get`` and ``_set`` methods of an object but that
|
||||
goes far beyond the scope of this tutorial.
|
||||
|
||||
We'll examine both starting with the direct bind. Lets add a property that
|
||||
allows us to control the amplitude of our wave.
|
||||
|
||||
In our ``gdexample.h`` file we simply need to add a member variable like so:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
...
|
||||
private:
|
||||
float time_passed;
|
||||
float amplitude;
|
||||
...
|
||||
|
||||
In our ``gdexample.cpp`` file we need to make a number of changes, we will only
|
||||
show the methods we end up changing, don't remove the lines we're omitting:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
}
|
||||
|
||||
void GDExample::_init() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
amplitude = 10.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
set_position(new_position);
|
||||
}
|
||||
|
||||
Once you compile the module with these changes in place, you will see that a
|
||||
property has been added to our interface. You can now change this property and
|
||||
when you run your project, you will see that our Godot icon travels along a
|
||||
larger figure.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``reloadable`` property in the ``gdexample.gdnlib`` file must be set to
|
||||
``true`` for the Godot editor to automatically pick up the newly added
|
||||
property.
|
||||
|
||||
However, this setting should be used with care, especially when tool classes
|
||||
are used, as the editor might hold objects then that have script instances
|
||||
attached to them that are managed by a GDNative library.
|
||||
|
||||
Let's do the same but for the speed of our animation and use a setter and getter
|
||||
function. Our ``gdexample.h`` header file again only needs a few more lines of
|
||||
code:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
...
|
||||
float amplitude;
|
||||
float speed;
|
||||
...
|
||||
void _process(float delta);
|
||||
void set_speed(float p_speed);
|
||||
float get_speed();
|
||||
...
|
||||
|
||||
This requires a few more changes to our ``gdexample.cpp`` file, again we're only
|
||||
showing the methods that have changed so don't remove anything we're omitting:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
|
||||
}
|
||||
|
||||
void GDExample::_init() {
|
||||
// initialize any variables here
|
||||
time_passed = 0.0;
|
||||
amplitude = 10.0;
|
||||
speed = 1.0;
|
||||
}
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
set_position(new_position);
|
||||
}
|
||||
|
||||
void GDExample::set_speed(float p_speed) {
|
||||
speed = p_speed;
|
||||
}
|
||||
|
||||
float GDExample::get_speed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
Now when the project is compiled, we'll see another property called speed.
|
||||
Changing its value will make the animation go faster or slower.
|
||||
|
||||
For this example, there is no obvious advantage of using a setter and getter.
|
||||
A good reason for a setter would be if you wanted to react on the variable being changed.
|
||||
If you don't need to do something like that, binding the variable is enough.
|
||||
|
||||
Getters and setters become far more useful in more complex scenarios where you
|
||||
need to make additional choices based on the state of your object.
|
||||
|
||||
.. note::
|
||||
|
||||
For simplicity, we've left out the optional parameters in the
|
||||
register_property<class, type> method call. These parameters are
|
||||
``rpc_mode``, ``usage``, ``hint`` and ``hint_string``. These can be used to
|
||||
further configure how properties are displayed and set on the Godot side.
|
||||
|
||||
Modern C++ compilers are able to infer the class and variable type and allow
|
||||
you to omit the ``<GDExample, float>`` part of our ``register_property``
|
||||
method. We've had mixed experiences with this however.
|
||||
|
||||
Signals
|
||||
-------
|
||||
|
||||
Last but not least, signals fully work in GDNative as well. Having your module
|
||||
react to a signal given out by another object requires you to call ``connect``
|
||||
on that object. We can't think of a good example for our wobbling Godot icon, we
|
||||
would need to showcase a far more complete example.
|
||||
|
||||
This is the required syntax:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
some_other_node->connect("the_signal", this, "my_method");
|
||||
|
||||
Note that you can only call ``my_method`` if you've previously registered it in
|
||||
your ``_register_methods`` method.
|
||||
|
||||
Having your object sending out signals is more common. For our wobbling
|
||||
Godot icon, we'll do something silly just to show how it works. We're going to
|
||||
emit a signal every time a second has passed and pass the new location along.
|
||||
|
||||
In our ``gdexample.h`` header file, we need to define a new member ``time_emit``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
...
|
||||
float time_passed;
|
||||
float time_emit;
|
||||
float amplitude;
|
||||
...
|
||||
|
||||
This time, the changes in ``gdexample.cpp`` are more elaborate. First,
|
||||
you'll need to set ``time_emit = 0.0;`` in either our ``_init`` method or in our
|
||||
constructor. We'll look at the other 2 needed changes one by one.
|
||||
|
||||
In our ``_register_methods`` method, we need to declare our signal. This is done
|
||||
as follows:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void GDExample::_register_methods() {
|
||||
register_method("_process", &GDExample::_process);
|
||||
register_property<GDExample, float>("amplitude", &GDExample::amplitude, 10.0);
|
||||
register_property<GDExample, float>("speed", &GDExample::set_speed, &GDExample::get_speed, 1.0);
|
||||
|
||||
register_signal<GDExample>((char *)"position_changed", "node", GODOT_VARIANT_TYPE_OBJECT, "new_pos", GODOT_VARIANT_TYPE_VECTOR2);
|
||||
}
|
||||
|
||||
Here, our ``register_signal`` method can be a single call first taking the
|
||||
signals name, then having pairs of values specifying the parameter name and
|
||||
type of each parameter we'll send along with this signal.
|
||||
|
||||
Next, we'll need to change our ``_process`` method:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void GDExample::_process(float delta) {
|
||||
time_passed += speed * delta;
|
||||
|
||||
Vector2 new_position = Vector2(
|
||||
amplitude + (amplitude * sin(time_passed * 2.0)),
|
||||
amplitude + (amplitude * cos(time_passed * 1.5))
|
||||
);
|
||||
|
||||
set_position(new_position);
|
||||
|
||||
time_emit += delta;
|
||||
if (time_emit > 1.0) {
|
||||
emit_signal("position_changed", this, new_position);
|
||||
|
||||
time_emit = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
After a second has passed, we emit our signal and reset our counter. We can add
|
||||
our parameter values directly to ``emit_signal``.
|
||||
|
||||
Once the GDNative library is compiled, we can go into Godot and select our sprite
|
||||
node. In the **Node** dock, we can find our new signal and link it up by pressing
|
||||
the **Connect** button or double-clicking the signal. We've added a script on
|
||||
our main node and implemented our signal like this:
|
||||
|
||||
.. code-block:: GDScript
|
||||
|
||||
extends Node
|
||||
|
||||
func _on_Sprite_position_changed(node, new_pos):
|
||||
print("The position of " + node.name + " is now " + str(new_pos))
|
||||
|
||||
Every second, we output our position to the console.
|
||||
|
||||
Next steps
|
||||
----------
|
||||
|
||||
The above is only a simple example, but we hope it shows you the basics. You can
|
||||
build upon this example to create full-fledged scripts to control nodes in Godot
|
||||
using C++.
|
||||
|
||||
To edit and recompile the plugin while the Godot editor
|
||||
remains open, re-run the project after the library has finished building.
|
10
_sources/tutorials/scripting/gdnative/index.rst.txt
Normal file
10
_sources/tutorials/scripting/gdnative/index.rst.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
GDNative
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-tutorials-gdnative
|
||||
|
||||
what_is_gdnative
|
||||
gdnative_c_example
|
||||
gdnative_cpp_example
|
106
_sources/tutorials/scripting/gdnative/what_is_gdnative.rst.txt
Normal file
106
_sources/tutorials/scripting/gdnative/what_is_gdnative.rst.txt
Normal file
|
@ -0,0 +1,106 @@
|
|||
.. _doc_what_is_gdnative:
|
||||
|
||||
What is GDNative?
|
||||
=================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
**GDNative** is a Godot-specific technology that lets the engine interact with
|
||||
native `shared libraries <https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries>`__
|
||||
at run-time. You can use it to run native code without compiling it with the engine.
|
||||
|
||||
.. note:: GDNative is *not* a scripting language and has no relation to
|
||||
:ref:`GDScript <doc_gdscript>`.
|
||||
|
||||
Differences between GDNative and C++ modules
|
||||
--------------------------------------------
|
||||
|
||||
You can use both GDNative and :ref:`C++ modules <doc_custom_modules_in_c++>` to
|
||||
run C or C++ code in a Godot project.
|
||||
|
||||
They also both allow you to integrate third-party libraries into Godot. The one
|
||||
you should choose depends on your needs.
|
||||
|
||||
Advantages of GDNative
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Unlike modules, GDNative doesn't require compiling the engine's source code,
|
||||
making it easier to distribute your work. It gives you access to most of the API
|
||||
available to GDScript C#, allowing you to code game logic with full control
|
||||
regarding performance. It's ideal if you need high-performance code you'd like
|
||||
to distribute as an add-on in the :ref:`asset library <doc_what_is_assetlib>`.
|
||||
|
||||
Also:
|
||||
|
||||
- GDNative is not limited to C and C++. Thanks to :ref:`third-party bindings
|
||||
<doc_what_is_gdnative_third_party_bindings>`, you can use it with many other
|
||||
languages.
|
||||
- You can use the same compiled GDNative library in the editor and exported
|
||||
project. With C++ modules, you have to recompile all the export templates you
|
||||
plan to use if you require its functionality at run-time.
|
||||
- GDNative only requires you to compile your library, not the whole engine.
|
||||
That's unlike C++ modules, which are statically compiled into the engine.
|
||||
Every time you change a module, you need to recompile the engine. Even with
|
||||
incremental builds, this process is slower than using GDNative.
|
||||
|
||||
Advantages of C++ modules
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
We recommend :ref:`C++ modules <doc_custom_modules_in_c++>` in cases where
|
||||
GDNative isn't enough:
|
||||
|
||||
- C++ modules provide deeper integration into the engine. GDNative's access is
|
||||
limited to what the scripting API exposes.
|
||||
- You can use C++ modules to provide additional features in a project without
|
||||
carrying native library files around. This extends to exported projects.
|
||||
- C++ modules are supported on all platforms. In contrast, GDNative has only
|
||||
limited support on HTML5 (cannot be used together with multi-threading), and
|
||||
is not supported on Universal Windows Platform (UWP).
|
||||
- C++ modules can be faster than GDNative, especially when the code requires a
|
||||
lot of communication through the scripting API.
|
||||
|
||||
Supported languages
|
||||
-------------------
|
||||
|
||||
The Godot developers officially support the following language bindings for
|
||||
GDNative:
|
||||
|
||||
- C++ :ref:`(tutorial) <doc_gdnative_cpp_example>`
|
||||
- C :ref:`(tutorial) <doc_gdnative_c_example>`
|
||||
|
||||
.. note::
|
||||
|
||||
There are no plans to support additional languages with GDNative officially.
|
||||
That said, the community offers several bindings for other languages (see
|
||||
below).
|
||||
|
||||
.. _doc_what_is_gdnative_third_party_bindings:
|
||||
|
||||
The bindings below are developed and maintained by the community:
|
||||
|
||||
.. Binding developers: Feel free to open a pull request to add your binding if it's well-developed enough to be used in a project.
|
||||
.. Please keep languages sorted in alphabetical order.
|
||||
|
||||
- `D <https://github.com/godot-d/godot-d>`__
|
||||
- `Kotlin <https://github.com/utopia-rise/godot-kotlin-jvm>`__
|
||||
- `Nim <https://github.com/pragmagic/godot-nim>`__
|
||||
- `Python <https://github.com/touilleMan/godot-python>`__
|
||||
- `Rust <https://github.com/godot-rust/godot-rust>`__
|
||||
|
||||
.. note::
|
||||
|
||||
Not all bindings mentioned here may be production-ready. Make sure to
|
||||
research options thoroughly before starting a project with one of those.
|
||||
Also, double-check whether the binding is compatible with the Godot version
|
||||
you're using.
|
||||
|
||||
Version compatibility
|
||||
---------------------
|
||||
|
||||
:ref:`Unlike Godot itself <doc_release_policy>`, GDNative has stricter version
|
||||
compatibility requirements as it relies on low-level *ptrcalls* to function.
|
||||
|
||||
GDNative add-ons compiled for a given Godot version are only guaranteed to work
|
||||
with the same minor release series. For example, a GDNative add-on compiled for
|
||||
Godot 3.4 will only work with Godot 3.4, 3.4.1, 3.4.2… but not Godot 3.3 or 3.5.
|
575
_sources/tutorials/scripting/gdscript/gdscript_advanced.rst.txt
Normal file
575
_sources/tutorials/scripting/gdscript/gdscript_advanced.rst.txt
Normal file
|
@ -0,0 +1,575 @@
|
|||
.. _doc_gdscript_more_efficiently:
|
||||
|
||||
GDScript: An introduction to dynamic languages
|
||||
==============================================
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
This tutorial aims to be a quick reference for how to use GDScript more
|
||||
efficiently. It focuses on common cases specific to the language, but
|
||||
also covers a lot of information on dynamically typed languages.
|
||||
|
||||
It's meant to be especially useful for programmers with little or no previous
|
||||
experience with dynamically typed languages.
|
||||
|
||||
Dynamic nature
|
||||
--------------
|
||||
|
||||
Pros & cons of dynamic typing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
GDScript is a Dynamically Typed language. As such, its main advantages
|
||||
are that:
|
||||
|
||||
- The language is simple and easy to learn.
|
||||
- Most code can be written and changed quickly and without hassle.
|
||||
- Less code written means less errors & mistakes to fix.
|
||||
- Easier to read the code (less clutter).
|
||||
- No compilation is required to test.
|
||||
- Runtime is tiny.
|
||||
- Duck-typing and polymorphism by nature.
|
||||
|
||||
While the main disadvantages are:
|
||||
|
||||
- Less performance than statically typed languages.
|
||||
- More difficult to refactor (symbols can't be traced)
|
||||
- Some errors that would typically be detected at compile time in
|
||||
statically typed languages only appear while running the code
|
||||
(because expression parsing is more strict).
|
||||
- Less flexibility for code-completion (some variable types are only
|
||||
known at run-time).
|
||||
|
||||
This, translated to reality, means that Godot+GDScript are a combination
|
||||
designed to create games quickly and efficiently. For games that are very
|
||||
computationally intensive and can't benefit from the engine built-in
|
||||
tools (such as the Vector types, Physics Engine, Math library, etc), the
|
||||
possibility of using C++ is present too. This allows you to still create most of the
|
||||
game in GDScript and add small bits of C++ in the areas that need
|
||||
a performance boost.
|
||||
|
||||
Variables & assignment
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
All variables in a dynamically typed language are "variant"-like. This
|
||||
means that their type is not fixed, and is only modified through
|
||||
assignment. Example:
|
||||
|
||||
Static:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
int a; // Value uninitialized.
|
||||
a = 5; // This is valid.
|
||||
a = "Hi!"; // This is invalid.
|
||||
|
||||
Dynamic:
|
||||
|
||||
::
|
||||
|
||||
var a # 'null' by default.
|
||||
a = 5 # Valid, 'a' becomes an integer.
|
||||
a = "Hi!" # Valid, 'a' changed to a string.
|
||||
|
||||
As function arguments:
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Functions are of dynamic nature too, which means they can be called with
|
||||
different arguments, for example:
|
||||
|
||||
Static:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void print_value(int value) {
|
||||
|
||||
printf("value is %i\n", value);
|
||||
}
|
||||
|
||||
[..]
|
||||
|
||||
print_value(55); // Valid.
|
||||
print_value("Hello"); // Invalid.
|
||||
|
||||
Dynamic:
|
||||
|
||||
::
|
||||
|
||||
func print_value(value):
|
||||
print(value)
|
||||
|
||||
[..]
|
||||
|
||||
print_value(55) # Valid.
|
||||
print_value("Hello") # Valid.
|
||||
|
||||
Pointers & referencing:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In static languages, such as C or C++ (and to some extent Java and C#),
|
||||
there is a distinction between a variable and a pointer/reference to a
|
||||
variable. The latter allows the object to be modified by other functions
|
||||
by passing a reference to the original one.
|
||||
|
||||
In C# or Java, everything not a built-in type (int, float, sometimes
|
||||
String) is always a pointer or a reference. References are also
|
||||
garbage-collected automatically, which means they are erased when no
|
||||
longer used. Dynamically typed languages tend to use this memory model,
|
||||
too. Some Examples:
|
||||
|
||||
- C++:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void use_class(SomeClass *instance) {
|
||||
|
||||
instance->use();
|
||||
}
|
||||
|
||||
void do_something() {
|
||||
|
||||
SomeClass *instance = new SomeClass; // Created as pointer.
|
||||
use_class(instance); // Passed as pointer.
|
||||
delete instance; // Otherwise it will leak memory.
|
||||
}
|
||||
|
||||
- Java:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
@Override
|
||||
public final void use_class(SomeClass instance) {
|
||||
|
||||
instance.use();
|
||||
}
|
||||
|
||||
public final void do_something() {
|
||||
|
||||
SomeClass instance = new SomeClass(); // Created as reference.
|
||||
use_class(instance); // Passed as reference.
|
||||
// Garbage collector will get rid of it when not in
|
||||
// use and freeze your game randomly for a second.
|
||||
}
|
||||
|
||||
- GDScript:
|
||||
|
||||
::
|
||||
|
||||
func use_class(instance): # Does not care about class type
|
||||
instance.use() # Will work with any class that has a ".use()" method.
|
||||
|
||||
func do_something():
|
||||
var instance = SomeClass.new() # Created as reference.
|
||||
use_class(instance) # Passed as reference.
|
||||
# Will be unreferenced and deleted.
|
||||
|
||||
In GDScript, only base types (int, float, String and PoolArray types)
|
||||
are passed by value to functions (value is copied). Everything else
|
||||
(instances, Arrays, Dictionaries, etc) is passed as reference. Classes
|
||||
that inherit :ref:`class_Reference` (the default if nothing is specified)
|
||||
will be freed when not used, but manual memory management is allowed too
|
||||
if inheriting manually from :ref:`class_Object`.
|
||||
|
||||
.. note::
|
||||
|
||||
A value is **passed by value** when it is copied every time it's specified
|
||||
as a function parameter. One consequence of this is that the function cannot
|
||||
modify the parameter in a way that is visible from outside the function::
|
||||
|
||||
func greet(text):
|
||||
text = "Hello " + text
|
||||
|
||||
func _ready():
|
||||
# Create a String (passed by value and immutable).
|
||||
var example = "Godot"
|
||||
|
||||
# Pass example as a parameter to `greet()`,
|
||||
# which modifies the parameter and does not return any value.
|
||||
greet(example)
|
||||
|
||||
print(example) # Godot
|
||||
|
||||
A value is **passed by reference** when it is *not* copied every time it's
|
||||
specified as a function parameter. This allows modifying a function
|
||||
parameter within a function body (and having the modified value accessible
|
||||
outside the function). The downside is that the data passed as a function
|
||||
parameter is no longer guaranteed to be immutable, which can cause
|
||||
difficult-to-track bugs if not done carefully::
|
||||
|
||||
func greet(text):
|
||||
text.push_front("Hello")
|
||||
|
||||
func _ready():
|
||||
# Create an Array (passed by reference and mutable) containing a String,
|
||||
# instead of a String (passed by value and immutable).
|
||||
var example = ["Godot"]
|
||||
|
||||
# Pass example as a parameter to `greet()`,
|
||||
# which modifies the parameter and does not return any value.
|
||||
greet(example)
|
||||
|
||||
print(example) # [Hello, Godot] (Array with 2 String elements)
|
||||
|
||||
Compared to passing by value, passing by reference can perform better when
|
||||
using large objects since copying large objects in memory can be slow.
|
||||
|
||||
Additionally, in Godot, base types such as String are **immutable**. This
|
||||
means that modifying them will *always* return a copy of the original value,
|
||||
rather than modifying the value in-place.
|
||||
|
||||
Arrays
|
||||
------
|
||||
|
||||
Arrays in dynamically typed languages can contain many different mixed
|
||||
datatypes inside and are always dynamic (can be resized at any time).
|
||||
Compare for example arrays in statically typed languages:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
int *array = new int[4]; // Create array.
|
||||
array[0] = 10; // Initialize manually.
|
||||
array[1] = 20; // Can't mix types.
|
||||
array[2] = 40;
|
||||
array[3] = 60;
|
||||
// Can't resize.
|
||||
use_array(array); // Passed as pointer.
|
||||
delete[] array; // Must be freed.
|
||||
|
||||
// or
|
||||
|
||||
std::vector<int> array;
|
||||
array.resize(4);
|
||||
array[0] = 10; // Initialize manually.
|
||||
array[1] = 20; // Can't mix types.
|
||||
array[2] = 40;
|
||||
array[3] = 60;
|
||||
array.resize(3); // Can be resized.
|
||||
use_array(array); // Passed reference or value.
|
||||
// Freed when stack ends.
|
||||
|
||||
And in GDScript:
|
||||
|
||||
::
|
||||
|
||||
var array = [10, "hello", 40, 60] # Simple, and can mix types.
|
||||
array.resize(3) # Can be resized.
|
||||
use_array(array) # Passed as reference.
|
||||
# Freed when no longer in use.
|
||||
|
||||
In dynamically typed languages, arrays can also double as other
|
||||
datatypes, such as lists:
|
||||
|
||||
::
|
||||
|
||||
var array = []
|
||||
array.append(4)
|
||||
array.append(5)
|
||||
array.pop_front()
|
||||
|
||||
Or unordered sets:
|
||||
|
||||
::
|
||||
|
||||
var a = 20
|
||||
if a in [10, 20, 30]:
|
||||
print("We have a winner!")
|
||||
|
||||
Dictionaries
|
||||
------------
|
||||
|
||||
Dictionaries are a powerful tool in dynamically typed languages.
|
||||
Most programmers that come from statically typed languages (such as C++
|
||||
or C#) ignore their existence and make their life unnecessarily more
|
||||
difficult. This datatype is generally not present in such languages (or
|
||||
only in limited form).
|
||||
|
||||
Dictionaries can map any value to any other value with complete
|
||||
disregard for the datatype used as either key or value. Contrary to
|
||||
popular belief, they are efficient because they can be implemented
|
||||
with hash tables. They are, in fact, so efficient that some languages
|
||||
will go as far as implementing arrays as dictionaries.
|
||||
|
||||
Example of Dictionary:
|
||||
|
||||
::
|
||||
|
||||
var d = {"name": "John", "age": 22} # Simple syntax.
|
||||
print("Name: ", d["name"], " Age: ", d["age"])
|
||||
|
||||
Dictionaries are also dynamic, keys can be added or removed at any point
|
||||
at little cost:
|
||||
|
||||
::
|
||||
|
||||
d["mother"] = "Rebecca" # Addition.
|
||||
d["age"] = 11 # Modification.
|
||||
d.erase("name") # Removal.
|
||||
|
||||
In most cases, two-dimensional arrays can often be implemented more
|
||||
easily with dictionaries. Here's a simple battleship game example:
|
||||
|
||||
::
|
||||
|
||||
# Battleship Game
|
||||
|
||||
const SHIP = 0
|
||||
const SHIP_HIT = 1
|
||||
const WATER_HIT = 2
|
||||
|
||||
var board = {}
|
||||
|
||||
func initialize():
|
||||
board[Vector2(1, 1)] = SHIP
|
||||
board[Vector2(1, 2)] = SHIP
|
||||
board[Vector2(1, 3)] = SHIP
|
||||
|
||||
func missile(pos):
|
||||
if pos in board: # Something at that position.
|
||||
if board[pos] == SHIP: # There was a ship! hit it.
|
||||
board[pos] = SHIP_HIT
|
||||
else:
|
||||
print("Already hit here!") # Hey dude you already hit here.
|
||||
else: # Nothing, mark as water.
|
||||
board[pos] = WATER_HIT
|
||||
|
||||
func game():
|
||||
initialize()
|
||||
missile(Vector2(1, 1))
|
||||
missile(Vector2(5, 8))
|
||||
missile(Vector2(2, 3))
|
||||
|
||||
Dictionaries can also be used as data markup or quick structures. While
|
||||
GDScript's dictionaries resemble python dictionaries, it also supports Lua
|
||||
style syntax and indexing, which makes it useful for writing initial
|
||||
states and quick structs:
|
||||
|
||||
::
|
||||
|
||||
# Same example, lua-style support.
|
||||
# This syntax is a lot more readable and usable.
|
||||
# Like any GDScript identifier, keys written in this form cannot start
|
||||
# with a digit.
|
||||
|
||||
var d = {
|
||||
name = "John",
|
||||
age = 22
|
||||
}
|
||||
|
||||
print("Name: ", d.name, " Age: ", d.age) # Used "." based indexing.
|
||||
|
||||
# Indexing
|
||||
|
||||
d["mother"] = "Rebecca"
|
||||
d.mother = "Caroline" # This would work too to create a new key.
|
||||
|
||||
For & while
|
||||
-----------
|
||||
|
||||
Iterating in some statically typed languages can be quite complex:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
const char* strings = new const char*[50];
|
||||
|
||||
[..]
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
|
||||
printf("Value: %s\n", i, strings[i]);
|
||||
}
|
||||
|
||||
// Even in STL:
|
||||
|
||||
for (std::list<std::string>::const_iterator it = strings.begin(); it != strings.end(); it++) {
|
||||
|
||||
std::cout << *it << std::endl;
|
||||
}
|
||||
|
||||
This is usually greatly simplified in dynamically typed languages:
|
||||
|
||||
::
|
||||
|
||||
for s in strings:
|
||||
print(s)
|
||||
|
||||
Container datatypes (arrays and dictionaries) are iterable. Dictionaries
|
||||
allow iterating the keys:
|
||||
|
||||
::
|
||||
|
||||
for key in dict:
|
||||
print(key, " -> ", dict[key])
|
||||
|
||||
Iterating with indices is also possible:
|
||||
|
||||
::
|
||||
|
||||
for i in range(strings.size()):
|
||||
print(strings[i])
|
||||
|
||||
The range() function can take 3 arguments:
|
||||
|
||||
::
|
||||
|
||||
range(n) # Will go from 0 to n-1.
|
||||
range(b, n) # Will go from b to n-1.
|
||||
range(b, n, s) # Will go from b to n-1, in steps of s.
|
||||
|
||||
Some statically typed programming language examples:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
for (int i = 0; i < 10; i++) {}
|
||||
|
||||
for (int i = 5; i < 10; i++) {}
|
||||
|
||||
for (int i = 5; i < 10; i += 2) {}
|
||||
|
||||
Translate to:
|
||||
|
||||
::
|
||||
|
||||
for i in range(10):
|
||||
pass
|
||||
|
||||
for i in range(5, 10):
|
||||
pass
|
||||
|
||||
for i in range(5, 10, 2):
|
||||
pass
|
||||
|
||||
And backwards looping is done through a negative counter:
|
||||
|
||||
::
|
||||
|
||||
for (int i = 10; i > 0; i--) {}
|
||||
|
||||
Becomes:
|
||||
|
||||
::
|
||||
|
||||
for i in range(10, 0, -1):
|
||||
pass
|
||||
|
||||
While
|
||||
-----
|
||||
|
||||
while() loops are the same everywhere:
|
||||
|
||||
::
|
||||
|
||||
var i = 0
|
||||
|
||||
while i < strings.size():
|
||||
print(strings[i])
|
||||
i += 1
|
||||
|
||||
Custom iterators
|
||||
----------------
|
||||
You can create custom iterators in case the default ones don't quite meet your
|
||||
needs by overriding the Variant class's ``_iter_init``, ``_iter_next``, and ``_iter_get``
|
||||
functions in your script. An example implementation of a forward iterator follows:
|
||||
|
||||
::
|
||||
|
||||
class ForwardIterator:
|
||||
var start
|
||||
var current
|
||||
var end
|
||||
var increment
|
||||
|
||||
func _init(start, stop, increment):
|
||||
self.start = start
|
||||
self.current = start
|
||||
self.end = stop
|
||||
self.increment = increment
|
||||
|
||||
func should_continue():
|
||||
return (current < end)
|
||||
|
||||
func _iter_init(arg):
|
||||
current = start
|
||||
return should_continue()
|
||||
|
||||
func _iter_next(arg):
|
||||
current += increment
|
||||
return should_continue()
|
||||
|
||||
func _iter_get(arg):
|
||||
return current
|
||||
|
||||
And it can be used like any other iterator:
|
||||
|
||||
::
|
||||
|
||||
var itr = ForwardIterator.new(0, 6, 2)
|
||||
for i in itr:
|
||||
print(i) # Will print 0, 2, and 4.
|
||||
|
||||
Make sure to reset the state of the iterator in ``_iter_init``, otherwise nested
|
||||
for-loops that use custom iterators will not work as expected.
|
||||
|
||||
Duck typing
|
||||
-----------
|
||||
|
||||
One of the most difficult concepts to grasp when moving from a
|
||||
statically typed language to a dynamic one is duck typing. Duck typing
|
||||
makes overall code design much simpler and straightforward to write, but
|
||||
it's not obvious how it works.
|
||||
|
||||
As an example, imagine a situation where a big rock is falling down a
|
||||
tunnel, smashing everything on its way. The code for the rock, in a
|
||||
statically typed language would be something like:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
void BigRollingRock::on_object_hit(Smashable *entity) {
|
||||
|
||||
entity->smash();
|
||||
}
|
||||
|
||||
This way, everything that can be smashed by a rock would have to
|
||||
inherit Smashable. If a character, enemy, piece of furniture, small rock
|
||||
were all smashable, they would need to inherit from the class Smashable,
|
||||
possibly requiring multiple inheritance. If multiple inheritance was
|
||||
undesired, then they would have to inherit a common class like Entity.
|
||||
Yet, it would not be very elegant to add a virtual method ``smash()`` to
|
||||
Entity only if a few of them can be smashed.
|
||||
|
||||
With dynamically typed languages, this is not a problem. Duck typing
|
||||
makes sure you only have to define a ``smash()`` function where required
|
||||
and that's it. No need to consider inheritance, base classes, etc.
|
||||
|
||||
::
|
||||
|
||||
func _on_object_hit(object):
|
||||
object.smash()
|
||||
|
||||
And that's it. If the object that hit the big rock has a smash() method,
|
||||
it will be called. No need for inheritance or polymorphism. Dynamically
|
||||
typed languages only care about the instance having the desired method
|
||||
or member, not what it inherits or the class type. The definition of
|
||||
Duck Typing should make this clearer:
|
||||
|
||||
*"When I see a bird that walks like a duck and swims like a duck and
|
||||
quacks like a duck, I call that bird a duck"*
|
||||
|
||||
In this case, it translates to:
|
||||
|
||||
*"If the object can be smashed, don't care what it is, just smash it."*
|
||||
|
||||
Yes, we should call it Hulk typing instead.
|
||||
|
||||
It's possible that the object being hit doesn't have a smash() function.
|
||||
Some dynamically typed languages simply ignore a method call when it
|
||||
doesn't exist, but GDScript is stricter, so checking if the function
|
||||
exists is desirable:
|
||||
|
||||
::
|
||||
|
||||
func _on_object_hit(object):
|
||||
if object.has_method("smash"):
|
||||
object.smash()
|
||||
|
||||
Then, simply define that method and anything the rock touches can be
|
||||
smashed.
|
1751
_sources/tutorials/scripting/gdscript/gdscript_basics.rst.txt
Normal file
1751
_sources/tutorials/scripting/gdscript/gdscript_basics.rst.txt
Normal file
File diff suppressed because it is too large
Load diff
391
_sources/tutorials/scripting/gdscript/gdscript_exports.rst.txt
Normal file
391
_sources/tutorials/scripting/gdscript/gdscript_exports.rst.txt
Normal file
|
@ -0,0 +1,391 @@
|
|||
.. _doc_gdscript_exports:
|
||||
|
||||
GDScript exports
|
||||
================
|
||||
|
||||
Introduction to exports
|
||||
-----------------------
|
||||
|
||||
In Godot, class members can be exported. This means their value gets saved along
|
||||
with the resource (such as the :ref:`scene <class_PackedScene>`) they're
|
||||
attached to. They will also be available for editing in the property editor.
|
||||
Exporting is done by using the ``export`` keyword::
|
||||
|
||||
extends Button
|
||||
|
||||
export var number = 5 # Value will be saved and visible in the property editor.
|
||||
|
||||
An exported variable must be initialized to a constant expression or have an
|
||||
export hint in the form of an argument to the ``export`` keyword (see the
|
||||
*Examples* section below).
|
||||
|
||||
One of the fundamental benefits of exporting member variables is to have
|
||||
them visible and editable in the editor. This way, artists and game designers
|
||||
can modify values that later influence how the program runs. For this, a
|
||||
special export syntax is provided.
|
||||
|
||||
.. note::
|
||||
|
||||
Exporting properties can also be done in other languages such as C#.
|
||||
The syntax varies depending on the language.
|
||||
|
||||
..
|
||||
See ref `doc_c_sharp_exports` for information on C# exports.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
::
|
||||
|
||||
# If the exported value assigns a constant or constant expression,
|
||||
# the type will be inferred and used in the editor.
|
||||
|
||||
export var number = 5
|
||||
|
||||
# Export can take a basic data type as an argument, which will be
|
||||
# used in the editor.
|
||||
|
||||
export(int) var number
|
||||
|
||||
# Export can also take a resource type to use as a hint.
|
||||
|
||||
export(Texture) var character_face
|
||||
export(PackedScene) var scene_file
|
||||
# There are many resource types that can be used this way, try e.g.
|
||||
# the following to list them:
|
||||
export(Resource) var resource
|
||||
|
||||
# Integers and strings hint enumerated values.
|
||||
|
||||
# Editor will enumerate as 0, 1 and 2.
|
||||
export(int, "Warrior", "Magician", "Thief") var character_class
|
||||
# Editor will enumerate with string names.
|
||||
export(String, "Rebecca", "Mary", "Leah") var character_name
|
||||
|
||||
# Named enum values
|
||||
|
||||
# Editor will enumerate as THING_1, THING_2, ANOTHER_THING.
|
||||
enum NamedEnum {THING_1, THING_2, ANOTHER_THING = -1}
|
||||
export(NamedEnum) var x
|
||||
|
||||
# Strings as paths
|
||||
|
||||
# String is a path to a file.
|
||||
export(String, FILE) var f
|
||||
# String is a path to a directory.
|
||||
export(String, DIR) var f
|
||||
# String is a path to a file, custom filter provided as hint.
|
||||
export(String, FILE, "*.txt") var f
|
||||
|
||||
# Using paths in the global filesystem is also possible,
|
||||
# but only in scripts in "tool" mode.
|
||||
|
||||
# String is a path to a PNG file in the global filesystem.
|
||||
export(String, FILE, GLOBAL, "*.png") var tool_image
|
||||
# String is a path to a directory in the global filesystem.
|
||||
export(String, DIR, GLOBAL) var tool_dir
|
||||
|
||||
# The MULTILINE setting tells the editor to show a large input
|
||||
# field for editing over multiple lines.
|
||||
export(String, MULTILINE) var text
|
||||
|
||||
# Limiting editor input ranges
|
||||
|
||||
# Allow integer values from 0 to 20.
|
||||
export(int, 20) var i
|
||||
# Allow integer values from -10 to 20.
|
||||
export(int, -10, 20) var j
|
||||
# Allow floats from -10 to 20 and snap the value to multiples of 0.2.
|
||||
export(float, -10, 20, 0.2) var k
|
||||
# Allow values 'y = exp(x)' where 'y' varies between 100 and 1000
|
||||
# while snapping to steps of 20. The editor will present a
|
||||
# slider for easily editing the value.
|
||||
export(float, EXP, 100, 1000, 20) var l
|
||||
|
||||
# Floats with easing hint
|
||||
|
||||
# Display a visual representation of the 'ease()' function
|
||||
# when editing.
|
||||
export(float, EASE) var transition_speed
|
||||
|
||||
# Colors
|
||||
|
||||
# Color given as red-green-blue value (alpha will always be 1).
|
||||
export(Color, RGB) var col
|
||||
# Color given as red-green-blue-alpha value.
|
||||
export(Color, RGBA) var col
|
||||
|
||||
# Nodes
|
||||
|
||||
# Another node in the scene can be exported as a NodePath.
|
||||
export(NodePath) var node_path
|
||||
# Do take note that the node itself isn't being exported -
|
||||
# there is one more step to call the true node:
|
||||
onready var node = get_node(node_path)
|
||||
|
||||
# Resources
|
||||
|
||||
export(Resource) var resource
|
||||
# In the Inspector, you can then drag and drop a resource file
|
||||
# from the FileSystem dock into the variable slot.
|
||||
|
||||
# Opening the inspector dropdown may result in an
|
||||
# extremely long list of possible classes to create, however.
|
||||
# Therefore, if you specify an extension of Resource such as:
|
||||
export(AnimationNode) var resource
|
||||
# The drop-down menu will be limited to AnimationNode and all
|
||||
# its inherited classes.
|
||||
|
||||
It must be noted that even if the script is not being run while in the
|
||||
editor, the exported properties are still editable. This can be used
|
||||
in conjunction with a :ref:`script in "tool" mode <doc_gdscript_tool_mode>`.
|
||||
|
||||
Exporting bit flags
|
||||
-------------------
|
||||
|
||||
Integers used as bit flags can store multiple ``true``/``false`` (boolean)
|
||||
values in one property. By using the export hint ``int, FLAGS, ...``, they
|
||||
can be set from the editor::
|
||||
|
||||
# Set any of the given flags from the editor.
|
||||
export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0
|
||||
|
||||
You must provide a string description for each flag. In this example, ``Fire``
|
||||
has value 1, ``Water`` has value 2, ``Earth`` has value 4 and ``Wind``
|
||||
corresponds to value 8. Usually, constants should be defined accordingly (e.g.
|
||||
``const ELEMENT_WIND = 8`` and so on).
|
||||
|
||||
Export hints are also provided for the physics and render layers defined in the project settings::
|
||||
|
||||
export(int, LAYERS_2D_PHYSICS) var layers_2d_physics
|
||||
export(int, LAYERS_2D_RENDER) var layers_2d_render
|
||||
export(int, LAYERS_3D_PHYSICS) var layers_3d_physics
|
||||
export(int, LAYERS_3D_RENDER) var layers_3d_render
|
||||
|
||||
Using bit flags requires some understanding of bitwise operations.
|
||||
If in doubt, use boolean variables instead.
|
||||
|
||||
Exporting arrays
|
||||
----------------
|
||||
|
||||
Exported arrays can have initializers, but they must be constant expressions.
|
||||
|
||||
If the exported array specifies a type which inherits from Resource, the array
|
||||
values can be set in the inspector by dragging and dropping multiple files
|
||||
from the FileSystem dock at once.
|
||||
|
||||
::
|
||||
|
||||
# Default value must be a constant expression.
|
||||
|
||||
export var a = [1, 2, 3]
|
||||
|
||||
# Exported arrays can specify type (using the same hints as before).
|
||||
|
||||
export(Array, int) var ints = [1, 2, 3]
|
||||
export(Array, int, "Red", "Green", "Blue") var enums = [2, 1, 0]
|
||||
export(Array, Array, float) var two_dimensional = [[1.0, 2.0], [3.0, 4.0]]
|
||||
|
||||
# You can omit the default value, but then it would be null if not assigned.
|
||||
|
||||
export(Array) var b
|
||||
export(Array, PackedScene) var scenes
|
||||
|
||||
# Arrays with specified types which inherit from resource can be set by
|
||||
# drag-and-dropping multiple files from the FileSystem dock.
|
||||
|
||||
export(Array, Texture) var textures
|
||||
export(Array, PackedScene) var scenes
|
||||
|
||||
# Typed arrays also work, only initialized empty:
|
||||
|
||||
export var vector3s = PoolVector3Array()
|
||||
export var strings = PoolStringArray()
|
||||
|
||||
# Default value can include run-time values, but can't
|
||||
# be exported.
|
||||
|
||||
var c = [a, 2, 3]
|
||||
|
||||
Setting exported variables from a tool script
|
||||
---------------------------------------------
|
||||
|
||||
When changing an exported variable's value from a script in
|
||||
:ref:`doc_gdscript_tool_mode`, the value in the inspector won't be updated
|
||||
automatically. To update it, call
|
||||
:ref:`property_list_changed_notify() <class_Object_method_property_list_changed_notify>`
|
||||
after setting the exported variable's value.
|
||||
|
||||
Advanced exports
|
||||
----------------
|
||||
|
||||
Not every type of export can be provided on the level of the language itself to
|
||||
avoid unnecessary design complexity. The following describes some more or less
|
||||
common exporting features which can be implemented with a low-level API.
|
||||
|
||||
Before reading further, you should get familiar with the way properties are
|
||||
handled and how they can be customized with
|
||||
:ref:`_set() <class_Object_method__get_property_list>`,
|
||||
:ref:`_get() <class_Object_method__get_property_list>`, and
|
||||
:ref:`_get_property_list() <class_Object_method__get_property_list>` methods as
|
||||
described in :ref:`doc_accessing_data_or_logic_from_object`.
|
||||
|
||||
.. seealso:: For binding properties using the above methods in C++, see
|
||||
:ref:`doc_binding_properties_using_set_get_property_list`.
|
||||
|
||||
.. warning:: The script must operate in the ``tool`` mode so the above methods
|
||||
can work from within the editor.
|
||||
|
||||
Properties
|
||||
~~~~~~~~~~
|
||||
|
||||
To understand how to better use the sections below, you should understand
|
||||
how to make properties with advanced exports.
|
||||
|
||||
::
|
||||
|
||||
func _get_property_list():
|
||||
var properties = []
|
||||
# Same as "export(int) var my_property"
|
||||
properties.append({
|
||||
name = "my_property",
|
||||
type = TYPE_INT
|
||||
})
|
||||
return properties
|
||||
|
||||
* The ``_get_property_list()`` function gets called by the inspector. You
|
||||
can override it for more advanced exports. You must return an ``Array``
|
||||
with the contents of the properties for the function to work.
|
||||
|
||||
* ``name`` is the name of the property
|
||||
|
||||
* ``type`` is the type of the property from ``Variant.Type``.
|
||||
|
||||
.. note:: The ``float`` type is called a real (``TYPE_REAL``) in the ``Variant.Type`` enum.
|
||||
|
||||
Attaching variables to properties
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To attach variables to properties (allowing the value of the property to be used
|
||||
in scripts), you need to create a variable with the exact same name as the
|
||||
property or else you may need to override the
|
||||
:ref:`_set() <class_Object_method__get_property_list>` and
|
||||
:ref:`_get() <class_Object_method__get_property_list>` methods. Attaching
|
||||
a variable to to a property also gives you the ability to give it a default state.
|
||||
::
|
||||
|
||||
|
||||
# This variable is determined by the function below.
|
||||
# This variable acts just like a regular gdscript export.
|
||||
var my_property = 5
|
||||
|
||||
func _get_property_list():
|
||||
var properties = []
|
||||
# Same as "export(int) var my_property"
|
||||
properties.append({
|
||||
name = "my_property",
|
||||
type = TYPE_INT
|
||||
})
|
||||
return properties
|
||||
|
||||
Adding default values for properties
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To define default values for advanced exports, you need to override the ``property_can_revert()`` and ``property_get_revert()`` methods.
|
||||
|
||||
* The ``property_can_revert()`` method takes the name of a property and must return ``true`` if the property can be reverted. This will enable the Revert button next to the property in the inspector.
|
||||
|
||||
* The ``property_get_revert()`` method takes the name of a property and must return the default value for that property.
|
||||
|
||||
::
|
||||
|
||||
func _get_property_list():
|
||||
var properties = []
|
||||
properties.append({
|
||||
name = "my_property",
|
||||
type = TYPE_INT
|
||||
})
|
||||
return properties
|
||||
|
||||
func property_can_revert(property):
|
||||
if property == "my_property":
|
||||
return true
|
||||
return false
|
||||
|
||||
func property_get_revert(property):
|
||||
if property == "my_property":
|
||||
return 5
|
||||
|
||||
Adding script categories
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For better visual distinguishing of properties, a special script category can be
|
||||
embedded into the inspector to act as a separator. ``Script Variables`` is one
|
||||
example of a built-in category.
|
||||
::
|
||||
|
||||
func _get_property_list():
|
||||
var properties = []
|
||||
properties.append({
|
||||
name = "Debug",
|
||||
type = TYPE_NIL,
|
||||
usage = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
})
|
||||
|
||||
# Example of adding a property to the script category
|
||||
properties.append({
|
||||
name = "Logging_Enabled",
|
||||
type = TYPE_BOOL
|
||||
})
|
||||
return properties
|
||||
|
||||
* ``name`` is the name of a category to be added to the inspector;
|
||||
|
||||
* Every following property added after the category definition will be a part
|
||||
of the category.
|
||||
|
||||
* ``PROPERTY_USAGE_CATEGORY`` indicates that the property should be treated as a
|
||||
script category specifically, so the type ``TYPE_NIL`` can be ignored as it
|
||||
won't be actually used for the scripting logic, yet it must be defined anyway.
|
||||
|
||||
Grouping properties
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A list of properties with similar names can be grouped.
|
||||
::
|
||||
|
||||
func _get_property_list():
|
||||
var properties = []
|
||||
properties.append({
|
||||
name = "Rotate",
|
||||
type = TYPE_NIL,
|
||||
hint_string = "rotate_",
|
||||
usage = PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SCRIPT_VARIABLE
|
||||
})
|
||||
|
||||
# Example of adding to the group
|
||||
properties.append({
|
||||
name = "rotate_speed",
|
||||
type = TYPE_REAL
|
||||
})
|
||||
|
||||
# This property won't get added to the group
|
||||
# due to not having the "rotate_" prefix.
|
||||
properties.append({
|
||||
name = "trail_color",
|
||||
type = TYPE_COLOR
|
||||
})
|
||||
return properties
|
||||
|
||||
* ``name`` is the name of a group which is going to be displayed as collapsible
|
||||
list of properties;
|
||||
|
||||
* Every following property added after the group property with the prefix
|
||||
(which determined by ``hint_string``) will be shortened. For instance,
|
||||
``rotate_speed`` is going to be shortened to ``speed`` in this case.
|
||||
However, ``movement_speed`` won't be a part of the group and will not
|
||||
be shortened.
|
||||
|
||||
* ``PROPERTY_USAGE_GROUP`` indicates that the property should be treated as a
|
||||
script group specifically, so the type ``TYPE_NIL`` can be ignored as it
|
||||
won't be actually used for the scripting logic, yet it must be defined anyway.
|
|
@ -0,0 +1,281 @@
|
|||
.. _doc_gdscript_printf:
|
||||
|
||||
GDScript format strings
|
||||
=======================
|
||||
|
||||
GDScript offers a feature called *format strings*, which allows reusing text
|
||||
templates to succinctly create different but similar strings.
|
||||
|
||||
Format strings are just like normal strings, except they contain certain
|
||||
placeholder character-sequences. These placeholders can then easily be replaced
|
||||
by parameters handed to the format string.
|
||||
|
||||
As an example, with ``%s`` as a placeholder, the format string ``"Hello %s, how
|
||||
are you?"`` can easily be changed to ``"Hello World, how are you?"``. Notice
|
||||
the placeholder is in the middle of the string; modifying it without format
|
||||
strings could be cumbersome.
|
||||
|
||||
|
||||
Usage in GDScript
|
||||
-----------------
|
||||
|
||||
Examine this concrete GDScript example:
|
||||
|
||||
::
|
||||
|
||||
# Define a format string with placeholder '%s'
|
||||
var format_string = "We're waiting for %s."
|
||||
|
||||
# Using the '%' operator, the placeholder is replaced with the desired value
|
||||
var actual_string = format_string % "Godot"
|
||||
|
||||
print(actual_string)
|
||||
# Output: "We're waiting for Godot."
|
||||
|
||||
Placeholders always start with a ``%``, but the next character or characters,
|
||||
the *format specifier*, determines how the given value is converted to a
|
||||
string.
|
||||
|
||||
The ``%s`` seen in the example above is the simplest placeholder and works for
|
||||
most use cases: it converts the value by the same method by which an implicit
|
||||
String conversion or ``str()`` would convert it. Strings remain unchanged,
|
||||
Booleans turn into either ``"True"`` or ``"False"``, an integral or real number
|
||||
becomes a decimal, other types usually return their data in a human-readable
|
||||
string.
|
||||
|
||||
There is also another way to format text in GDScript, namely the ``String.format()``
|
||||
method. It replaces all occurrences of a key in the string with the corresponding
|
||||
value. The method can handle arrays or dictionaries for the key/value pairs.
|
||||
|
||||
Arrays can be used as key, index, or mixed style (see below examples). Order only
|
||||
matters when the index or mixed style of Array is used.
|
||||
|
||||
A quick example in GDScript:
|
||||
|
||||
::
|
||||
|
||||
# Define a format string
|
||||
var format_string = "We're waiting for {str}"
|
||||
|
||||
# Using the 'format' method, replace the 'str' placeholder
|
||||
var actual_string = format_string.format({"str": "Godot"})
|
||||
|
||||
print(actual_string)
|
||||
# Output: "We're waiting for Godot"
|
||||
|
||||
There are other `format specifiers`_, but they are only applicable when using
|
||||
the ``%`` operator.
|
||||
|
||||
|
||||
Multiple placeholders
|
||||
---------------------
|
||||
|
||||
Format strings may contain multiple placeholders. In such a case, the values
|
||||
are handed in the form of an array, one value per placeholder (unless using a
|
||||
format specifier with ``*``, see `dynamic padding`_):
|
||||
|
||||
::
|
||||
|
||||
var format_string = "%s was reluctant to learn %s, but now he enjoys it."
|
||||
var actual_string = format_string % ["Estragon", "GDScript"]
|
||||
|
||||
print(actual_string)
|
||||
# Output: "Estragon was reluctant to learn GDScript, but now he enjoys it."
|
||||
|
||||
Note the values are inserted in order. Remember all placeholders must be
|
||||
replaced at once, so there must be an appropriate number of values.
|
||||
|
||||
|
||||
Format specifiers
|
||||
-----------------
|
||||
|
||||
There are format specifiers other than ``s`` that can be used in placeholders.
|
||||
They consist of one or more characters. Some of them work by themselves like
|
||||
``s``, some appear before other characters, some only work with certain
|
||||
values or characters.
|
||||
|
||||
|
||||
Placeholder types
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
One and only one of these must always appear as the last character in a format
|
||||
specifier. Apart from ``s``, these require certain types of parameters.
|
||||
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``s`` | **Simple** conversion to String by the same method as implicit |
|
||||
| | String conversion. |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``c`` | A single **Unicode character**. Expects an unsigned 8-bit integer |
|
||||
| | (0-255) for a code point or a single-character string. |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``d`` | A **decimal integral** number. Expects an integral or real number |
|
||||
| | (will be floored). |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``o`` | An **octal integral** number. Expects an integral or real number |
|
||||
| | (will be floored). |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``x`` | A **hexadecimal integral** number with **lower-case** letters. |
|
||||
| | Expects an integral or real number (will be floored). |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``X`` | A **hexadecimal integral** number with **upper-case** letters. |
|
||||
| | Expects an integral or real number (will be floored). |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
| ``f`` | A **decimal real** number. Expects an integral or real number. |
|
||||
+-------+---------------------------------------------------------------------+
|
||||
|
||||
|
||||
Placeholder modifiers
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These characters appear before the above. Some of them work only under certain
|
||||
conditions.
|
||||
|
||||
+---------+-------------------------------------------------------------------+
|
||||
| ``+`` | In number specifiers, **show + sign** if positive. |
|
||||
+---------+-------------------------------------------------------------------+
|
||||
| Integer | Set **padding**. Padded with spaces or with zeroes if integer |
|
||||
| | starts with ``0`` in an integer or real number placeholder. |
|
||||
| | The leading ``0`` is ignored if ``-`` is present. |
|
||||
| | When used after ``.``, see ``.``. |
|
||||
+---------+-------------------------------------------------------------------+
|
||||
| ``.`` | Before ``f``, set **precision** to 0 decimal places. Can be |
|
||||
| | followed up with numbers to change. Padded with zeroes. |
|
||||
+---------+-------------------------------------------------------------------+
|
||||
| ``-`` | **Pad to the right** rather than the left. |
|
||||
+---------+-------------------------------------------------------------------+
|
||||
| ``*`` | **Dynamic padding**, expect additional integral parameter to set |
|
||||
| | padding or precision after ``.``, see `dynamic padding`_. |
|
||||
+---------+-------------------------------------------------------------------+
|
||||
|
||||
|
||||
Padding
|
||||
-------
|
||||
|
||||
The ``.`` (*dot*), ``*`` (*asterisk*), ``-`` (*minus sign*) and digit
|
||||
(``0``-``9``) characters are used for padding. This allows printing several
|
||||
values aligned vertically as if in a column, provided a fixed-width font is
|
||||
used.
|
||||
|
||||
To pad a string to a minimum length, add an integer to the specifier:
|
||||
|
||||
::
|
||||
|
||||
print("%10d" % 12345)
|
||||
# output: " 12345"
|
||||
# 5 leading spaces for a total length of 10
|
||||
|
||||
If the integer starts with ``0``, integral values are padded with zeroes
|
||||
instead of white space:
|
||||
|
||||
::
|
||||
|
||||
print("%010d" % 12345)
|
||||
# output: "0000012345"
|
||||
|
||||
Precision can be specified for real numbers by adding a ``.`` (*dot*) with an
|
||||
integer following it. With no integer after ``.``, a precision of 0 is used,
|
||||
rounding to integral value. The integer to use for padding must appear before
|
||||
the dot.
|
||||
|
||||
::
|
||||
|
||||
# Pad to minimum length of 10, round to 3 decimal places
|
||||
print("%10.3f" % 10000.5555)
|
||||
# Output: " 10000.556"
|
||||
# 1 leading space
|
||||
|
||||
The ``-`` character will cause padding to the right rather than the left,
|
||||
useful for right text alignment:
|
||||
|
||||
::
|
||||
|
||||
print("%-10d" % 12345678)
|
||||
# Output: "12345678 "
|
||||
# 2 trailing spaces
|
||||
|
||||
|
||||
Dynamic padding
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
By using the ``*`` (*asterisk*) character, the padding or precision can be set
|
||||
without modifying the format string. It is used in place of an integer in the
|
||||
format specifier. The values for padding and precision are then passed when
|
||||
formatting:
|
||||
|
||||
::
|
||||
|
||||
var format_string = "%*.*f"
|
||||
# Pad to length of 7, round to 3 decimal places:
|
||||
print(format_string % [7, 3, 8.8888])
|
||||
# Output: " 8.889"
|
||||
# 2 leading spaces
|
||||
|
||||
It is still possible to pad with zeroes in integer placeholders by adding ``0``
|
||||
before ``*``:
|
||||
|
||||
::
|
||||
|
||||
print("%0*d" % [2, 3])
|
||||
# Output: "03"
|
||||
|
||||
|
||||
Escape sequence
|
||||
---------------
|
||||
|
||||
To insert a literal ``%`` character into a format string, it must be escaped to
|
||||
avoid reading it as a placeholder. This is done by doubling the character:
|
||||
|
||||
::
|
||||
|
||||
var health = 56
|
||||
print("Remaining health: %d%%" % health)
|
||||
# Output: "Remaining health: 56%"
|
||||
|
||||
|
||||
Format method examples
|
||||
----------------------
|
||||
|
||||
The following are some examples of how to use the various invocations of the
|
||||
``String.format`` method.
|
||||
|
||||
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| **Type** | **Style** | **Example** | **Result** |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Dictionary | key | ``"Hi, {name} v{version}!".format({"name":"Godette", "version":"3.0"})`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Dictionary | index | ``"Hi, {0} v{1}!".format({"0":"Godette", "1":"3.0"})`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Dictionary | mix | ``"Hi, {0} v{version}!".format({"0":"Godette", "version":"3.0"})`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Array | key | ``"Hi, {name} v{version}!".format([["version","3.0"], ["name","Godette"]])`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Array | index | ``"Hi, {0} v{1}!".format(["Godette","3.0"])`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Array | mix | ``"Hi, {name} v{0}!".format([3.0, ["name","Godette"]])`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
| Array | no index | ``"Hi, {} v{}!".format(["Godette", 3.0], "{}")`` | Hi, Godette v3.0! |
|
||||
+------------+-----------+------------------------------------------------------------------------------+-------------------+
|
||||
|
||||
Placeholders can also be customized when using ``String.format``, here's some
|
||||
examples of that functionality.
|
||||
|
||||
|
||||
+-----------------+------------------------------------------------------+------------------+
|
||||
| **Type** | **Example** | **Result** |
|
||||
+-----------------+------------------------------------------------------+------------------+
|
||||
| Infix (default) | ``"Hi, {0} v{1}".format(["Godette", "3.0"], "{_}")`` | Hi, Godette v3.0 |
|
||||
+-----------------+------------------------------------------------------+------------------+
|
||||
| Postfix | ``"Hi, 0% v1%".format(["Godette", "3.0"], "_%")`` | Hi, Godette v3.0 |
|
||||
+-----------------+------------------------------------------------------+------------------+
|
||||
| Prefix | ``"Hi, %0 v%1".format(["Godette", "3.0"], "%_")`` | Hi, Godette v3.0 |
|
||||
+-----------------+------------------------------------------------------+------------------+
|
||||
|
||||
Combining both the ``String.format`` method and the ``%`` operator could be useful, as
|
||||
``String.format`` does not have a way to manipulate the representation of numbers.
|
||||
|
||||
+---------------------------------------------------------------------------+-------------------+
|
||||
| **Example** | **Result** |
|
||||
+---------------------------------------------------------------------------+-------------------+
|
||||
| ``"Hi, {0} v{version}".format({0:"Godette", "version":"%0.2f" % 3.114})`` | Hi, Godette v3.11 |
|
||||
+---------------------------------------------------------------------------+-------------------+
|
|
@ -0,0 +1,842 @@
|
|||
.. _doc_gdscript_styleguide:
|
||||
|
||||
GDScript style guide
|
||||
====================
|
||||
|
||||
This style guide lists conventions to write elegant GDScript. The goal is to
|
||||
encourage writing clean, readable code and promote consistency across projects,
|
||||
discussions, and tutorials. Hopefully, this will also support the development of
|
||||
auto-formatting tools.
|
||||
|
||||
Since GDScript is close to Python, this guide is inspired by Python's
|
||||
`PEP 8 <https://www.python.org/dev/peps/pep-0008/>`__ programming
|
||||
style guide.
|
||||
|
||||
Style guides aren't meant as hard rulebooks. At times, you may not be able to
|
||||
apply some of the guidelines below. When that happens, use your best judgment,
|
||||
and ask fellow developers for insights.
|
||||
|
||||
In general, keeping your code consistent in your projects and within your team is
|
||||
more important than following this guide to a tee.
|
||||
|
||||
.. note:: Godot's built-in script editor uses a lot of these conventions
|
||||
by default. Let it help you.
|
||||
|
||||
Here is a complete class example based on these guidelines:
|
||||
|
||||
::
|
||||
|
||||
class_name StateMachine
|
||||
extends Node
|
||||
# Hierarchical State machine for the player.
|
||||
# Initializes states and delegates engine callbacks
|
||||
# (_physics_process, _unhandled_input) to the state.
|
||||
|
||||
|
||||
signal state_changed(previous, new)
|
||||
|
||||
export var initial_state = NodePath()
|
||||
var is_active = true setget set_is_active
|
||||
|
||||
onready var _state = get_node(initial_state) setget set_state
|
||||
onready var _state_name = _state.name
|
||||
|
||||
|
||||
func _init():
|
||||
add_to_group("state_machine")
|
||||
|
||||
|
||||
func _ready():
|
||||
connect("state_changed", self, "_on_state_changed")
|
||||
_state.enter()
|
||||
|
||||
|
||||
func _unhandled_input(event):
|
||||
_state.unhandled_input(event)
|
||||
|
||||
|
||||
func _physics_process(delta):
|
||||
_state.physics_process(delta)
|
||||
|
||||
|
||||
func transition_to(target_state_path, msg={}):
|
||||
if not has_node(target_state_path):
|
||||
return
|
||||
|
||||
var target_state = get_node(target_state_path)
|
||||
assert(target_state.is_composite == false)
|
||||
|
||||
_state.exit()
|
||||
self._state = target_state
|
||||
_state.enter(msg)
|
||||
Events.emit_signal("player_state_changed", _state.name)
|
||||
|
||||
|
||||
func set_is_active(value):
|
||||
is_active = value
|
||||
set_physics_process(value)
|
||||
set_process_unhandled_input(value)
|
||||
set_block_signals(not value)
|
||||
|
||||
|
||||
func set_state(value):
|
||||
_state = value
|
||||
_state_name = _state.name
|
||||
|
||||
|
||||
func _on_state_changed(previous, new):
|
||||
print("state changed")
|
||||
emit_signal("state_changed")
|
||||
|
||||
.. _formatting:
|
||||
|
||||
Formatting
|
||||
----------
|
||||
|
||||
Encoding and special characters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Use line feed (**LF**) characters to break lines, not CRLF or CR. *(editor default)*
|
||||
* Use one line feed character at the end of each file. *(editor default)*
|
||||
* Use **UTF-8** encoding without a `byte order mark <https://en.wikipedia.org/wiki/Byte_order_mark>`_. *(editor default)*
|
||||
* Use **Tabs** instead of spaces for indentation. *(editor default)*
|
||||
|
||||
Indentation
|
||||
~~~~~~~~~~~
|
||||
|
||||
Each indent level should be one greater than the block containing it.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
for i in range(10):
|
||||
print("hello")
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
for i in range(10):
|
||||
print("hello")
|
||||
|
||||
for i in range(10):
|
||||
print("hello")
|
||||
|
||||
Use 2 indent levels to distinguish continuation lines from
|
||||
regular code blocks.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
effect.interpolate_property(sprite, "transform/scale",
|
||||
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
|
||||
Tween.TRANS_QUAD, Tween.EASE_OUT)
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
effect.interpolate_property(sprite, "transform/scale",
|
||||
sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
|
||||
Tween.TRANS_QUAD, Tween.EASE_OUT)
|
||||
|
||||
Exceptions to this rule are arrays, dictionaries, and enums. Use a single
|
||||
indentation level to distinguish continuation lines:
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
var party = [
|
||||
"Godot",
|
||||
"Godette",
|
||||
"Steve",
|
||||
]
|
||||
|
||||
var character_dict = {
|
||||
"Name": "Bob",
|
||||
"Age": 27,
|
||||
"Job": "Mechanic",
|
||||
}
|
||||
|
||||
enum Tiles {
|
||||
TILE_BRICK,
|
||||
TILE_FLOOR,
|
||||
TILE_SPIKE,
|
||||
TILE_TELEPORT,
|
||||
}
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
var party = [
|
||||
"Godot",
|
||||
"Godette",
|
||||
"Steve",
|
||||
]
|
||||
|
||||
var character_dict = {
|
||||
"Name": "Bob",
|
||||
"Age": 27,
|
||||
"Job": "Mechanic",
|
||||
}
|
||||
|
||||
enum Tiles {
|
||||
TILE_BRICK,
|
||||
TILE_FLOOR,
|
||||
TILE_SPIKE,
|
||||
TILE_TELEPORT,
|
||||
}
|
||||
|
||||
Trailing comma
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Use a trailing comma on the last line in arrays, dictionaries, and enums. This
|
||||
results in easier refactoring and better diffs in version control as the last
|
||||
line doesn't need to be modified when adding new elements.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
enum Tiles {
|
||||
TILE_BRICK,
|
||||
TILE_FLOOR,
|
||||
TILE_SPIKE,
|
||||
TILE_TELEPORT,
|
||||
}
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
enum Tiles {
|
||||
TILE_BRICK,
|
||||
TILE_FLOOR,
|
||||
TILE_SPIKE,
|
||||
TILE_TELEPORT
|
||||
}
|
||||
|
||||
Trailing commas are unnecessary in single-line lists, so don't add them in this case.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT,}
|
||||
|
||||
Blank lines
|
||||
~~~~~~~~~~~
|
||||
|
||||
Surround functions and class definitions with two blank lines:
|
||||
|
||||
::
|
||||
|
||||
func heal(amount):
|
||||
health += amount
|
||||
health = min(health, max_health)
|
||||
emit_signal("health_changed", health)
|
||||
|
||||
|
||||
func take_damage(amount, effect=null):
|
||||
health -= amount
|
||||
health = max(0, health)
|
||||
emit_signal("health_changed", health)
|
||||
|
||||
Use one blank line inside functions to separate logical sections.
|
||||
|
||||
.. note:: We use a single line between classes and function definitions in the class reference and
|
||||
in short code snippets in this documentation.
|
||||
|
||||
Line length
|
||||
~~~~~~~~~~~
|
||||
|
||||
Keep individual lines of code under 100 characters.
|
||||
|
||||
If you can, try to keep lines under 80 characters. This helps to read the code
|
||||
on small displays and with two scripts opened side-by-side in an external text
|
||||
editor. For example, when looking at a differential revision.
|
||||
|
||||
One statement per line
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Never combine multiple statements on a single line. No, C programmers,
|
||||
not even with a single line conditional statement.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
if position.x > width:
|
||||
position.x = 0
|
||||
|
||||
if flag:
|
||||
print("flagged")
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
if position.x > width: position.x = 0
|
||||
|
||||
if flag: print("flagged")
|
||||
|
||||
The only exception to that rule is the ternary operator:
|
||||
|
||||
::
|
||||
|
||||
next_state = "fall" if not is_on_floor() else "idle"
|
||||
|
||||
Format multiline statements for readability
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you have particularly long ``if`` statements or nested ternary expressions,
|
||||
wrapping them over multiple lines improves readability. Since continuation lines
|
||||
are still part of the same expression, 2 indent levels should be used instead of one.
|
||||
|
||||
GDScript allows wrapping statements using multiple lines using parentheses or
|
||||
backslashes. Parentheses are favored in this style guide since they make for
|
||||
easier refactoring. With backslashes, you have to ensure that the last line
|
||||
never contains a backslash at the end. With parentheses, you don't have to
|
||||
worry about the last line having a backslash at the end.
|
||||
|
||||
When wrapping a conditional expression over multiple lines, the ``and``/``or``
|
||||
keywords should be placed at the beginning of the line continuation, not at the
|
||||
end of the previous line.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
var angle_degrees = 135
|
||||
var quadrant = (
|
||||
"northeast" if angle_degrees <= 90
|
||||
else "southeast" if angle_degrees <= 180
|
||||
else "southwest" if angle_degrees <= 270
|
||||
else "northwest"
|
||||
)
|
||||
|
||||
var position = Vector2(250, 350)
|
||||
if (
|
||||
position.x > 200 and position.x < 400
|
||||
and position.y > 300 and position.y < 400
|
||||
):
|
||||
pass
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
var angle_degrees = 135
|
||||
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"
|
||||
|
||||
var position = Vector2(250, 350)
|
||||
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
|
||||
pass
|
||||
|
||||
Avoid unnecessary parentheses
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Avoid parentheses in expressions and conditional statements. Unless
|
||||
necessary for order of operations or wrapping over multiple lines,
|
||||
they only reduce readability.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
if is_colliding():
|
||||
queue_free()
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
if (is_colliding()):
|
||||
queue_free()
|
||||
|
||||
Boolean operators
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Prefer the plain English versions of boolean operators, as they are the most accessible:
|
||||
|
||||
- Use ``and`` instead of ``&&``.
|
||||
- Use ``or`` instead of ``||``.
|
||||
|
||||
You may also use parentheses around boolean operators to clear any ambiguity.
|
||||
This can make long expressions easier to read.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
if (foo and bar) or baz:
|
||||
print("condition is true")
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
if foo && bar || baz:
|
||||
print("condition is true")
|
||||
|
||||
Comment spacing
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Regular comments should start with a space, but not code that you comment out.
|
||||
This helps differentiate text comments from disabled code.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
# This is a comment.
|
||||
#print("This is disabled code")
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
#This is a comment.
|
||||
# print("This is disabled code")
|
||||
|
||||
.. note::
|
||||
|
||||
In the script editor, to toggle the selected code commented, press
|
||||
:kbd:`Ctrl + K`. This feature adds a single # sign at the start
|
||||
of the selected lines.
|
||||
|
||||
Whitespace
|
||||
~~~~~~~~~~
|
||||
|
||||
Always use one space around operators and after commas. Also, avoid extra spaces
|
||||
in dictionary references and function calls.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
position.x = 5
|
||||
position.y = target_position.y + 10
|
||||
dict["key"] = 5
|
||||
my_array = [4, 5, 6]
|
||||
print("foo")
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
position.x=5
|
||||
position.y = mpos.y+10
|
||||
dict ["key"] = 5
|
||||
myarray = [4,5,6]
|
||||
print ("foo")
|
||||
|
||||
Don't use spaces to align expressions vertically:
|
||||
|
||||
::
|
||||
|
||||
x = 100
|
||||
y = 100
|
||||
velocity = 500
|
||||
|
||||
Quotes
|
||||
~~~~~~
|
||||
|
||||
Use double quotes unless single quotes make it possible to escape fewer
|
||||
characters in a given string. See the examples below:
|
||||
|
||||
::
|
||||
|
||||
# Normal string.
|
||||
print("hello world")
|
||||
|
||||
# Use double quotes as usual to avoid escapes.
|
||||
print("hello 'world'")
|
||||
|
||||
# Use single quotes as an exception to the rule to avoid escapes.
|
||||
print('hello "world"')
|
||||
|
||||
# Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
|
||||
print("'hello' \"world\"")
|
||||
|
||||
Numbers
|
||||
~~~~~~~
|
||||
|
||||
Don't omit the leading or trailing zero in floating-point numbers. Otherwise,
|
||||
this makes them less readable and harder to distinguish from integers at a
|
||||
glance.
|
||||
|
||||
**Good**::
|
||||
|
||||
var float_number = 0.234
|
||||
var other_float_number = 13.0
|
||||
|
||||
**Bad**::
|
||||
|
||||
var float_number = .234
|
||||
var other_float_number = 13.
|
||||
|
||||
Use lowercase for letters in hexadecimal numbers, as their lower height makes
|
||||
the number easier to read.
|
||||
|
||||
**Good**::
|
||||
|
||||
var hex_number = 0xfb8c0b
|
||||
|
||||
**Bad**::
|
||||
|
||||
var hex_number = 0xFB8C0B
|
||||
|
||||
Take advantage of GDScript's underscores in literals to make large numbers more
|
||||
readable.
|
||||
|
||||
**Good**::
|
||||
|
||||
var large_number = 1_234_567_890
|
||||
var large_hex_number = 0xffff_f8f8_0000
|
||||
var large_bin_number = 0b1101_0010_1010
|
||||
# Numbers lower than 1000000 generally don't need separators.
|
||||
var small_number = 12345
|
||||
|
||||
**Bad**::
|
||||
|
||||
var large_number = 1234567890
|
||||
var large_hex_number = 0xfffff8f80000
|
||||
var large_bin_number = 0b110100101010
|
||||
# Numbers lower than 1000000 generally don't need separators.
|
||||
var small_number = 12_345
|
||||
|
||||
.. _naming_conventions:
|
||||
|
||||
Naming conventions
|
||||
------------------
|
||||
|
||||
These naming conventions follow the Godot Engine style. Breaking these will make
|
||||
your code clash with the built-in naming conventions, leading to inconsistent
|
||||
code.
|
||||
|
||||
File names
|
||||
~~~~~~~~~~
|
||||
|
||||
Use snake_case for file names. For named classes, convert the PascalCase class
|
||||
name to snake_case::
|
||||
|
||||
# This file should be saved as `weapon.gd`.
|
||||
class_name Weapon
|
||||
extends Node
|
||||
|
||||
::
|
||||
|
||||
# This file should be saved as `yaml_parser.gd`.
|
||||
class_name YAMLParser
|
||||
extends Object
|
||||
|
||||
This is consistent with how C++ files are named in Godot's source code. This
|
||||
also avoids case sensitivity issues that can crop up when exporting a project
|
||||
from Windows to other platforms.
|
||||
|
||||
Classes and nodes
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use PascalCase for class and node names:
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody
|
||||
|
||||
Also use PascalCase when loading a class into a constant or a variable:
|
||||
|
||||
::
|
||||
|
||||
const Weapon = preload("res://weapon.gd")
|
||||
|
||||
Functions and variables
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use snake\_case to name functions and variables:
|
||||
|
||||
::
|
||||
|
||||
var particle_effect
|
||||
func load_level():
|
||||
|
||||
Prepend a single underscore (\_) to virtual methods functions the user must
|
||||
override, private functions, and private variables:
|
||||
|
||||
::
|
||||
|
||||
var _counter = 0
|
||||
func _recalculate_path():
|
||||
|
||||
Signals
|
||||
~~~~~~~
|
||||
|
||||
Use the past tense to name signals:
|
||||
|
||||
::
|
||||
|
||||
signal door_opened
|
||||
signal score_changed
|
||||
|
||||
Constants and enums
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Write constants with CONSTANT\_CASE, that is to say in all caps with an
|
||||
underscore (\_) to separate words:
|
||||
|
||||
::
|
||||
|
||||
const MAX_SPEED = 200
|
||||
|
||||
Use PascalCase for enum *names* and CONSTANT\_CASE for their members, as they
|
||||
are constants:
|
||||
|
||||
::
|
||||
|
||||
enum Element {
|
||||
EARTH,
|
||||
WATER,
|
||||
AIR,
|
||||
FIRE,
|
||||
}
|
||||
|
||||
|
||||
Code order
|
||||
----------
|
||||
|
||||
This first section focuses on code order. For formatting, see
|
||||
:ref:`formatting`. For naming conventions, see :ref:`naming_conventions`.
|
||||
|
||||
We suggest to organize GDScript code this way:
|
||||
|
||||
::
|
||||
|
||||
01. tool
|
||||
02. class_name
|
||||
03. extends
|
||||
04. # docstring
|
||||
|
||||
05. signals
|
||||
06. enums
|
||||
07. constants
|
||||
08. exported variables
|
||||
09. public variables
|
||||
10. private variables
|
||||
11. onready variables
|
||||
|
||||
12. optional built-in virtual _init method
|
||||
13. built-in virtual _ready method
|
||||
14. remaining built-in virtual methods
|
||||
15. public methods
|
||||
16. private methods
|
||||
|
||||
We optimized the order to make it easy to read the code from top to bottom, to
|
||||
help developers reading the code for the first time understand how it works, and
|
||||
to avoid errors linked to the order of variable declarations.
|
||||
|
||||
This code order follows four rules of thumb:
|
||||
|
||||
1. Properties and signals come first, followed by methods.
|
||||
2. Public comes before private.
|
||||
3. Virtual callbacks come before the class's interface.
|
||||
4. The object's construction and initialization functions, ``_init`` and
|
||||
``_ready``, come before functions that modify the object at runtime.
|
||||
|
||||
|
||||
Class declaration
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the code is meant to run in the editor, place the ``tool`` keyword on the
|
||||
first line of the script.
|
||||
|
||||
Follow with the `class_name` if necessary. You can turn a GDScript file into a
|
||||
global type in your project using this feature. For more information, see
|
||||
:ref:`doc_gdscript`.
|
||||
|
||||
Then, add the `extends` keyword if the class extends a built-in type.
|
||||
|
||||
Following that, you should have the class's optional docstring as comments. You
|
||||
can use that to explain the role of your class to your teammates, how it works,
|
||||
and how other developers should use it, for example.
|
||||
|
||||
::
|
||||
|
||||
class_name MyNode
|
||||
extends Node
|
||||
# A brief description of the class's role and functionality.
|
||||
# Longer description.
|
||||
|
||||
Signals and properties
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Write signal declarations, followed by properties, that is to say, member
|
||||
variables, after the docstring.
|
||||
|
||||
Enums should come after signals, as you can use them as export hints for other
|
||||
properties.
|
||||
|
||||
Then, write constants, exported variables, public, private, and onready
|
||||
variables, in that order.
|
||||
|
||||
::
|
||||
|
||||
signal spawn_player(position)
|
||||
|
||||
enum Jobs {KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN}
|
||||
|
||||
const MAX_LIVES = 3
|
||||
|
||||
export(Jobs) var job = Jobs.KNIGHT
|
||||
export var max_health = 50
|
||||
export var attack = 5
|
||||
|
||||
var health = max_health setget set_health
|
||||
|
||||
var _speed = 300.0
|
||||
|
||||
onready var sword = get_node("Sword")
|
||||
onready var gun = get_node("Gun")
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The GDScript compiler evaluates onready variables right before the ``_ready``
|
||||
callback. You can use that to cache node dependencies, that is to say, to get
|
||||
child nodes in the scene that your class relies on. This is what the example
|
||||
above shows.
|
||||
|
||||
Member variables
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Don't declare member variables if they are only used locally in a method, as it
|
||||
makes the code more difficult to follow. Instead, declare them as local
|
||||
variables in the method's body.
|
||||
|
||||
Local variables
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Declare local variables as close as possible to their first use. This makes it
|
||||
easier to follow the code, without having to scroll too much to find where the
|
||||
variable was declared.
|
||||
|
||||
Methods and static functions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the class's properties come the methods.
|
||||
|
||||
Start with the ``_init()`` callback method, that the engine will call upon
|
||||
creating the object in memory. Follow with the ``_ready()`` callback, that Godot
|
||||
calls when it adds a node to the scene tree.
|
||||
|
||||
These functions should come first because they show how the object is
|
||||
initialized.
|
||||
|
||||
Other built-in virtual callbacks, like ``_unhandled_input()`` and
|
||||
``_physics_process``, should come next. These control the object's main loop and
|
||||
interactions with the game engine.
|
||||
|
||||
The rest of the class's interface, public and private methods, come after that,
|
||||
in that order.
|
||||
|
||||
::
|
||||
|
||||
func _init():
|
||||
add_to_group("state_machine")
|
||||
|
||||
|
||||
func _ready():
|
||||
connect("state_changed", self, "_on_state_changed")
|
||||
_state.enter()
|
||||
|
||||
|
||||
func _unhandled_input(event):
|
||||
_state.unhandled_input(event)
|
||||
|
||||
|
||||
func transition_to(target_state_path, msg={}):
|
||||
if not has_node(target_state_path):
|
||||
return
|
||||
|
||||
var target_state = get_node(target_state_path)
|
||||
assert(target_state.is_composite == false)
|
||||
|
||||
_state.exit()
|
||||
self._state = target_state
|
||||
_state.enter(msg)
|
||||
Events.emit_signal("player_state_changed", _state.name)
|
||||
|
||||
|
||||
func _on_state_changed(previous, new):
|
||||
print("state changed")
|
||||
emit_signal("state_changed")
|
||||
|
||||
|
||||
Static typing
|
||||
-------------
|
||||
|
||||
Since Godot 3.1, GDScript supports :ref:`optional static typing<doc_gdscript_static_typing>`.
|
||||
|
||||
Declared types
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
To declare a variable's type, use ``<variable>: <type>``:
|
||||
|
||||
::
|
||||
|
||||
var health: int = 0
|
||||
|
||||
To declare the return type of a function, use ``-> <type>``:
|
||||
|
||||
::
|
||||
|
||||
func heal(amount: int) -> void:
|
||||
|
||||
Inferred types
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
In most cases you can let the compiler infer the type, using ``:=``:
|
||||
|
||||
::
|
||||
|
||||
var health := 0 # The compiler will use the int type.
|
||||
|
||||
However, in a few cases when context is missing, the compiler falls back to
|
||||
the function's return type. For example, ``get_node()`` cannot infer a type
|
||||
unless the scene or file of the node is loaded in memory. In this case, you
|
||||
should set the type explicitly.
|
||||
|
||||
**Good**:
|
||||
|
||||
::
|
||||
|
||||
onready var health_bar: ProgressBar = get_node("UI/LifeBar")
|
||||
|
||||
Alternatively, you can use the ``as`` keyword to cast the return type, and
|
||||
that type will be used to infer the type of the var.
|
||||
|
||||
.. rst-class:: code-example-good
|
||||
|
||||
::
|
||||
|
||||
onready var health_bar := get_node("UI/LifeBar") as ProgressBar
|
||||
# health_bar will be typed as ProgressBar
|
||||
|
||||
This option is also considered more :ref:`type-safe<doc_gdscript_static_typing_safe_lines>` than the first.
|
||||
|
||||
**Bad**:
|
||||
|
||||
::
|
||||
|
||||
# The compiler can't infer the exact type and will use Node
|
||||
# instead of ProgressBar.
|
||||
onready var health_bar := get_node("UI/LifeBar")
|
19
_sources/tutorials/scripting/gdscript/index.rst.txt
Normal file
19
_sources/tutorials/scripting/gdscript/index.rst.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
GDScript
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-learn-scripting-gdscript
|
||||
|
||||
gdscript_basics
|
||||
gdscript_advanced
|
||||
gdscript_exports
|
||||
gdscript_styleguide
|
||||
static_typing
|
||||
warning_system
|
||||
gdscript_format_string
|
||||
|
||||
.. seealso::
|
||||
|
||||
See :ref:`doc_gdscript_grammar` if you are interested in writing a third-party
|
||||
tool that interacts with GDScript, such as a linter or formatter.
|
386
_sources/tutorials/scripting/gdscript/static_typing.rst.txt
Normal file
386
_sources/tutorials/scripting/gdscript/static_typing.rst.txt
Normal file
|
@ -0,0 +1,386 @@
|
|||
.. _doc_gdscript_static_typing:
|
||||
|
||||
Static typing in GDScript
|
||||
=========================
|
||||
|
||||
In this guide, you will learn:
|
||||
|
||||
- **How to use types in GDScript**
|
||||
- That **static types can help you avoid bugs**
|
||||
|
||||
Where and how you use this new language feature is entirely up to you:
|
||||
you can use it only in some sensitive GDScript files, use it everywhere,
|
||||
or write code like you always did!
|
||||
|
||||
Static types can be used on variables, constants, functions, parameters,
|
||||
and return types.
|
||||
|
||||
.. note::
|
||||
|
||||
Typed GDScript is available since Godot 3.1.
|
||||
|
||||
A brief look at static typing
|
||||
-----------------------------
|
||||
|
||||
With typed GDScript, Godot can detect even more errors as you write
|
||||
code! It gives you and your teammates more information as you're
|
||||
working, as the arguments' types show up when you call a method.
|
||||
|
||||
Imagine you're programming an inventory system. You code an ``Item``
|
||||
node, then an ``Inventory``. To add items to the inventory, the people
|
||||
who work with your code should always pass an ``Item`` to the
|
||||
``Inventory.add`` method. With types, you can enforce this:
|
||||
|
||||
::
|
||||
|
||||
# In 'Item.gd'.
|
||||
class_name Item
|
||||
# In 'Inventory.gd'.
|
||||
class_name Inventory
|
||||
|
||||
|
||||
func add(reference: Item, amount: int = 1):
|
||||
var item = find_item(reference)
|
||||
if not item:
|
||||
item = _instance_item_from_db(reference)
|
||||
|
||||
item.amount += amount
|
||||
|
||||
Another significant advantage of typed GDScript is the new **warning
|
||||
system**. From version 3.1, Godot gives you warnings about your code as
|
||||
you write it: the engine identifies sections of your code that may lead
|
||||
to issues at runtime, but lets you decide whether or not you want to
|
||||
leave the code as it is. More on that in a moment.
|
||||
|
||||
Static types also give you better code completion options. Below, you
|
||||
can see the difference between a dynamic and a static typed completion
|
||||
options for a class called ``PlayerController``.
|
||||
|
||||
You've probably stored a node in a variable before, and typed a dot to
|
||||
be left with no autocomplete suggestions:
|
||||
|
||||
.. figure:: img/typed_gdscript_code_completion_dynamic.png
|
||||
:alt: code completion options for dynamic
|
||||
|
||||
This is due to dynamic code. Godot cannot know what node or value type
|
||||
you're passing to the function. If you write the type explicitly
|
||||
however, you will get all public methods and variables from the node:
|
||||
|
||||
.. figure:: img/typed_gdscript_code_completion_typed.png
|
||||
:alt: code completion options for typed
|
||||
|
||||
In the future, typed GDScript will also increase code performance:
|
||||
Just-In-Time compilation and other compiler improvements are already
|
||||
on the roadmap!
|
||||
|
||||
Overall, typed programming gives you a more structured experience. It
|
||||
helps prevent errors and improves the self-documenting aspect of your
|
||||
scripts. This is especially helpful when you're working in a team or on
|
||||
a long-term project: studies have shown that developers spend most of
|
||||
their time reading other people's code, or scripts they wrote in the
|
||||
past and forgot about. The clearer and the more structured the code, the
|
||||
faster it is to understand, the faster you can move forward.
|
||||
|
||||
How to use static typing
|
||||
------------------------
|
||||
|
||||
To define the type of a variable or a constant, write a colon after the
|
||||
variable's name, followed by its type. E.g. ``var health: int``. This
|
||||
forces the variable's type to always stay the same:
|
||||
|
||||
::
|
||||
|
||||
var damage: float = 10.5
|
||||
const MOVE_SPEED: float = 50.0
|
||||
|
||||
Godot will try to infer types if you write a colon, but you omit the
|
||||
type:
|
||||
|
||||
::
|
||||
|
||||
var life_points := 4
|
||||
var damage := 10.5
|
||||
var motion := Vector2()
|
||||
|
||||
Currently you can use three types of… types:
|
||||
|
||||
1. :ref:`Built-in <doc_gdscript_builtin_types>`
|
||||
2. Core classes and nodes (``Object``, ``Node``, ``Area2D``,
|
||||
``Camera2D``, etc.)
|
||||
3. Your own custom classes. Look at the new :ref:`class_name <doc_gdscript_basics_class_name>`
|
||||
feature to register types in the editor.
|
||||
|
||||
.. note::
|
||||
|
||||
You don't need to write type hints for constants, as Godot sets it automatically from the assigned value. But you can still do so to make the intent of your code clearer.
|
||||
|
||||
Custom variable types
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can use any class, including your custom classes, as types. There
|
||||
are two ways to use them in scripts. The first method is to preload the
|
||||
script you want to use as a type in a constant:
|
||||
|
||||
::
|
||||
|
||||
const Rifle = preload("res://player/weapons/Rifle.gd")
|
||||
var my_rifle: Rifle
|
||||
|
||||
The second method is to use the ``class_name`` keyword when you create.
|
||||
For the example above, your Rifle.gd would look like this:
|
||||
|
||||
::
|
||||
|
||||
class_name Rifle
|
||||
extends Node2D
|
||||
|
||||
If you use ``class_name``, Godot registers the Rifle type globally in
|
||||
the editor, and you can use it anywhere, without having to preload it
|
||||
into a constant:
|
||||
|
||||
::
|
||||
|
||||
var my_rifle: Rifle
|
||||
|
||||
Variable casting
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Type casting is a key concept in typed languages.
|
||||
Casting is the conversion of a value from one type to another.
|
||||
|
||||
Imagine an Enemy in your game, that ``extends Area2D``. You want it to
|
||||
collide with the Player, a ``KinematicBody2D`` with a script called
|
||||
``PlayerController`` attached to it. You use the ``on_body_entered``
|
||||
signal to detect the collision. With typed code, the body you detect is
|
||||
going to be a generic ``PhysicsBody2D``, and not your
|
||||
``PlayerController`` on the ``_on_body_entered`` callback.
|
||||
|
||||
You can check if this ``PhysicsBody2D`` is your Player with the ``as``
|
||||
casting keyword, and using the colon ``:`` again to force the variable
|
||||
to use this type. This forces the variable to stick to the
|
||||
``PlayerController`` type:
|
||||
|
||||
::
|
||||
|
||||
func _on_body_entered(body: PhysicsBody2D) -> void:
|
||||
var player := body as PlayerController
|
||||
if not player:
|
||||
return
|
||||
|
||||
player.damage()
|
||||
|
||||
As we're dealing with a custom type, if the ``body`` doesn't extend
|
||||
``PlayerController``, the ``player``\ variable will be set to ``null``.
|
||||
We can use this to check if the body is the player or not. We will also
|
||||
get full autocompletion on the player variable thanks to that cast.
|
||||
|
||||
.. note::
|
||||
|
||||
If you try to cast with a built-in type and it fails, Godot will throw an error.
|
||||
|
||||
.. _doc_gdscript_static_typing_safe_lines:
|
||||
|
||||
Safe lines
|
||||
^^^^^^^^^^
|
||||
|
||||
You can also use casting to ensure safe lines. Safe lines are a new
|
||||
tool in Godot 3.1 to tell you when ambiguous lines of code are
|
||||
type-safe. As you can mix and match typed and dynamic code, at times,
|
||||
Godot doesn't have enough information to know if an instruction will trigger
|
||||
an error or not at runtime.
|
||||
|
||||
This happens when you get a child node. Let's take a timer for example:
|
||||
with dynamic code, you can get the node with ``$Timer``. GDScript
|
||||
supports `duck-typing <https://stackoverflow.com/a/4205163/8125343>`__,
|
||||
so even if your timer is of type ``Timer``, it is also a ``Node`` and an
|
||||
``Object``, two classes it extends. With dynamic GDScript, you also
|
||||
don't care about the node's type as long as it has the methods you need
|
||||
to call.
|
||||
|
||||
You can use casting to tell Godot the type you expect when you get a
|
||||
node: ``($Timer as Timer)``, ``($Player as KinematicBody2D)``, etc.
|
||||
Godot will ensure the type works and if so, the line number will turn
|
||||
green at the left of the script editor.
|
||||
|
||||
.. figure:: img/typed_gdscript_safe_unsafe_line.png
|
||||
:alt: Unsafe vs Safe Line
|
||||
|
||||
Unsafe line (line 7) vs Safe Lines (line 6 and 8)
|
||||
|
||||
.. note::
|
||||
|
||||
You can turn off safe lines or change their color in the editor settings.
|
||||
|
||||
Define the return type of a function with the arrow ->
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To define the return type of a function, write a dash and a right angle
|
||||
bracket ``->`` after its declaration, followed by the return type:
|
||||
|
||||
::
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
The type ``void`` means the function does not return anything. You can
|
||||
use any type, as with variables:
|
||||
|
||||
::
|
||||
|
||||
func hit(damage: float) -> bool:
|
||||
health_points -= damage
|
||||
return health_points <= 0
|
||||
|
||||
You can also use your own nodes as return types:
|
||||
|
||||
::
|
||||
|
||||
# Inventory.gd
|
||||
|
||||
# Adds an item to the inventory and returns it.
|
||||
func add(reference: Item, amount: int) -> Item:
|
||||
var item: Item = find_item(reference)
|
||||
if not item:
|
||||
item = ItemDatabase.get_instance(reference)
|
||||
|
||||
item.amount += amount
|
||||
return item
|
||||
|
||||
Typed or dynamic: stick to one style
|
||||
------------------------------------
|
||||
|
||||
Typed GDScript and dynamic GDScript can coexist in the same project. But
|
||||
it's recommended to stick to either style for consistency in your codebase,
|
||||
and for your peers. It's easier for everyone to work together if you
|
||||
follow the same guidelines, and faster to read and understand other
|
||||
people's code.
|
||||
|
||||
Typed code takes a little more writing, but you get the benefits we
|
||||
discussed above. Here's an example of the same, empty script, in a
|
||||
dynamic style:
|
||||
|
||||
::
|
||||
|
||||
extends Node
|
||||
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
func _process(delta):
|
||||
pass
|
||||
|
||||
And with static typing:
|
||||
|
||||
::
|
||||
|
||||
extends Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
|
||||
As you can see, you can also use types with the engine's virtual
|
||||
methods. Signal callbacks, like any methods, can also use types. Here's
|
||||
a ``body_entered`` signal in a dynamic style:
|
||||
|
||||
::
|
||||
|
||||
func _on_Area2D_body_entered(body):
|
||||
pass
|
||||
|
||||
And the same callback, with type hints:
|
||||
|
||||
::
|
||||
|
||||
func _on_area_entered(area: CollisionObject2D) -> void:
|
||||
pass
|
||||
|
||||
You're free to replace, e.g. the ``CollisionObject2D``, with your own type,
|
||||
to cast parameters automatically:
|
||||
|
||||
::
|
||||
|
||||
func _on_area_entered(bullet: Bullet) -> void:
|
||||
if not bullet:
|
||||
return
|
||||
|
||||
take_damage(bullet.damage)
|
||||
|
||||
The ``bullet`` variable could hold any ``CollisionObject2D`` here, but
|
||||
we make sure it is our ``Bullet``, a node we created for our project. If
|
||||
it's anything else, like an ``Area2D``, or any node that doesn't extend
|
||||
``Bullet``, the ``bullet`` variable will be ``null``.
|
||||
|
||||
Warning system
|
||||
--------------
|
||||
|
||||
.. note::
|
||||
|
||||
Documentation about the GDScript warning system has been moved to
|
||||
:ref:`doc_gdscript_warning_system`.
|
||||
|
||||
Cases where you can't specify types
|
||||
-----------------------------------
|
||||
|
||||
To wrap up this introduction, let's cover a few cases where you can't
|
||||
use type hints. All the examples below **will trigger errors**.
|
||||
|
||||
You can't use Enums as types:
|
||||
|
||||
::
|
||||
|
||||
enum MoveDirection {UP, DOWN, LEFT, RIGHT}
|
||||
var current_direction: MoveDirection
|
||||
|
||||
You can't specify the type of individual members in an array. This will
|
||||
give you an error:
|
||||
|
||||
::
|
||||
|
||||
var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy]
|
||||
|
||||
You can't force the assignment of types in a ``for`` loop, as each
|
||||
element the ``for`` keyword loops over already has a different type. So you
|
||||
**cannot** write:
|
||||
|
||||
::
|
||||
|
||||
var names = ["John", "Marta", "Samantha", "Jimmy"]
|
||||
for name: String in names:
|
||||
pass
|
||||
|
||||
Two scripts can't depend on each other in a cyclic fashion:
|
||||
|
||||
::
|
||||
|
||||
# Player.gd
|
||||
|
||||
extends Area2D
|
||||
class_name Player
|
||||
|
||||
|
||||
var rifle: Rifle
|
||||
|
||||
::
|
||||
|
||||
# Rifle.gd
|
||||
|
||||
extends Area2D
|
||||
class_name Rifle
|
||||
|
||||
|
||||
var player: Player
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
Typed GDScript is a powerful tool. Available as of version 3.1 of Godot, it
|
||||
helps you write more structured code, avoid common errors, and
|
||||
create scalable systems. In the future, static types will also bring you
|
||||
a nice performance boost thanks to upcoming compiler optimizations.
|
51
_sources/tutorials/scripting/gdscript/warning_system.rst.txt
Normal file
51
_sources/tutorials/scripting/gdscript/warning_system.rst.txt
Normal file
|
@ -0,0 +1,51 @@
|
|||
.. _doc_gdscript_warning_system:
|
||||
|
||||
GDScript warning system
|
||||
=======================
|
||||
|
||||
The GDScript warning system complements :ref:`static typing <doc_gdscript_static_typing>`
|
||||
(but it can work without static typing too). It's here to help you avoid
|
||||
mistakes that are hard to spot during development, and that may lead
|
||||
to runtime errors.
|
||||
|
||||
You can configure warnings in the Project Settings under the section
|
||||
called **Gdscript**:
|
||||
|
||||
.. figure:: img/typed_gdscript_warning_system_settings.png
|
||||
:alt: Warning system project settings
|
||||
|
||||
Warning system project settings
|
||||
|
||||
You can find a list of warnings for the active GDScript file in the
|
||||
script editor's status bar. The example below has 3 warnings:
|
||||
|
||||
.. figure:: img/typed_gdscript_warning_example.png
|
||||
:alt: Warning system example
|
||||
|
||||
Warning system example
|
||||
|
||||
To ignore specific warnings in one file, insert a special comment of the
|
||||
form ``# warning-ignore:warning-id``, or click on the ignore link to the
|
||||
right of the warning's description. Godot will add a comment above the
|
||||
corresponding line and the code won't trigger the corresponding warning
|
||||
anymore:
|
||||
|
||||
.. figure:: img/typed_gdscript_warning_system_ignore.png
|
||||
:alt: Warning system ignore example
|
||||
|
||||
Warning system ignore example
|
||||
|
||||
You can also choose to ignore not just one but all warnings of a certain
|
||||
type in this file with ``# warning-ignore-all:warning-id``. To ignore all
|
||||
warnings of all types in a file add the comment ``# warnings-disable`` to it.
|
||||
|
||||
Warnings won't prevent the game from running, but you can turn them into
|
||||
errors if you'd like. This way your game won't compile unless you fix
|
||||
all warnings. Head to the ``GDScript`` section of the Project Settings to
|
||||
turn on this option. Here's the same file as the previous example with
|
||||
warnings as errors turned on:
|
||||
|
||||
.. figure:: img/typed_gdscript_warning_system_errors.png
|
||||
:alt: Warnings as errors
|
||||
|
||||
Warnings as errors
|
130
_sources/tutorials/scripting/groups.rst.txt
Normal file
130
_sources/tutorials/scripting/groups.rst.txt
Normal file
|
@ -0,0 +1,130 @@
|
|||
.. _doc_groups:
|
||||
|
||||
Groups
|
||||
======
|
||||
|
||||
Groups in Godot work like tags in other software. You can add a node to as many
|
||||
groups as you want. Then, in code, you can use the SceneTree to:
|
||||
|
||||
- Get a list of nodes in a group.
|
||||
- Call a method on all nodes in a group.
|
||||
- Send a notification to all nodes in a group.
|
||||
|
||||
This is a useful feature to organize large scenes and decouple code.
|
||||
|
||||
|
||||
Managing groups
|
||||
---------------
|
||||
|
||||
Groups are created by adding a node to a new group name, and likewise they are
|
||||
removed by removing all nodes from a given group.
|
||||
|
||||
There are two ways to add/remove nodes to groups:
|
||||
|
||||
- During design, by using the Node dock in the editor.
|
||||
- During execution, by calling :ref:`Node.add_to_group() <class_Node_method_add_to_group>`
|
||||
or :ref:`Node.remove_from_group() <class_Node_method_remove_from_group>`.
|
||||
|
||||
|
||||
Using the Node dock
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can add nodes in the current scene to groups using the Groups tab in the
|
||||
Node dock.
|
||||
|
||||
.. image:: img/groups_node_tab.png
|
||||
|
||||
Select one or more nodes in the Scene dock and write the group name in the
|
||||
field, then click Add.
|
||||
|
||||
.. image:: img/groups_add_node_to_group.png
|
||||
|
||||
You should now see the group appear.
|
||||
|
||||
.. image:: img/groups_node_after_adding.png
|
||||
|
||||
In a complex project, you may end up with many groups or large scenes with many
|
||||
nodes. You can add or remove any node to groups using the Group Editor window.
|
||||
To access it, click the Manage Groups button.
|
||||
|
||||
.. image:: img/groups_manage_groups_button.png
|
||||
|
||||
The Group Editor window appears. Here's a screenshot from a complex project to
|
||||
illustrate the tool's purpose.
|
||||
|
||||
.. image:: img/groups_group_editor_window.png
|
||||
|
||||
It has three columns:
|
||||
|
||||
1. A list of groups used by nodes in the current scene.
|
||||
2. A list of nodes that are not part of the selected group.
|
||||
3. A list of nodes in the group.
|
||||
|
||||
The fields at the bottom allow you to add new groups or filter nodes in the
|
||||
second and third columns.
|
||||
|
||||
.. note:: Any node name that's greyed out means the node was added to the group
|
||||
in a different scene and you cannot edit it here. This happens on
|
||||
scene instances in particular.
|
||||
|
||||
Using code
|
||||
~~~~~~~~~~
|
||||
|
||||
You can also manage groups from scripts. The following code adds the node to
|
||||
which you attach the script to the ``guards`` group as soon as it enters the
|
||||
scene tree.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _ready():
|
||||
add_to_group("guards")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
|
||||
AddToGroup("guards");
|
||||
}
|
||||
|
||||
Imagine you're creating an infiltration game. When an
|
||||
enemy spots the player, you want all guards and robots to be on alert.
|
||||
|
||||
In the fictional example below, we use ``SceneTree.call_group()`` to alert all
|
||||
enemies that the player was spotted.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _on_Player_spotted():
|
||||
get_tree().call_group("guards", "enter_alert_mode")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void _OnPlayerDiscovered()
|
||||
{
|
||||
GetTree().CallGroup("guards", "enter_alert_mode");
|
||||
}
|
||||
|
||||
The above code calls the function ``enter_alert_mode`` on every member of the
|
||||
group ``guards``.
|
||||
|
||||
To get the full list of nodes in the ``guards`` group as an array, you can call
|
||||
:ref:`SceneTree.get_nodes_in_group()
|
||||
<class_SceneTree_method_get_nodes_in_group>`:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var guards = get_tree().get_nodes_in_group("guards")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
var guards = GetTree().GetNodesInGroup("guards");
|
||||
|
||||
The :ref:`SceneTree <class_SceneTree>` class provides many more useful methods
|
||||
to interact with scenes, their node hierarchy, and groups. It allows you to
|
||||
switch scenes easily or reload them, quit the game or pause and unpause it. It
|
||||
also provides useful signals.
|
104
_sources/tutorials/scripting/idle_and_physics_processing.rst.txt
Normal file
104
_sources/tutorials/scripting/idle_and_physics_processing.rst.txt
Normal file
|
@ -0,0 +1,104 @@
|
|||
.. _doc_idle_and_physics_processing:
|
||||
|
||||
Idle and Physics Processing
|
||||
===========================
|
||||
|
||||
Games run in a loop. Each frame, you need to update the state of your game world
|
||||
before drawing it on screen. Godot provides two virtual methods in the Node
|
||||
class to do so: :ref:`Node._process() <class_Node_method__process>` and
|
||||
:ref:`Node._physics_process() <class_Node_method__physics_process>`. If you
|
||||
define either or both in a script, the engine will call them automatically.
|
||||
|
||||
There are two types of processing available to you:
|
||||
|
||||
1. **Idle processing** allows you to run code that updates a node every frame,
|
||||
as often as possible.
|
||||
2. **Physics processing** happens at a fixed rate, 60 times per second by
|
||||
default. This is independent of your game's actual framerate, and keeps physics
|
||||
running smoothly. You should use it for anything that involves the physics
|
||||
engine, like moving a body that collides with the environment.
|
||||
|
||||
You can activate idle processing by defining the ``_process()`` method in a
|
||||
script. You can turn it off and back on by calling :ref:`Node.set_process()
|
||||
<class_Node_method_set_process>`.
|
||||
|
||||
The engine calls this method every time it draws a frame:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _process(delta):
|
||||
# Do something...
|
||||
pass
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
// Do something...
|
||||
}
|
||||
|
||||
Keep in mind that the frequency at which the engine calls ``_process()`` depends
|
||||
on your application's framerate, which varies over time and across devices.
|
||||
|
||||
The function's ``delta`` parameter is the time elapsed in seconds since the
|
||||
previous call to ``_process()``. Use this parameter to make calculations
|
||||
independent of the framerate. For example, you should always multiply a speed
|
||||
value by ``delta`` to animate a moving object.
|
||||
|
||||
Physics processing works with a similar virtual function:
|
||||
``_physics_process()``. Use it for calculations that must happen before each
|
||||
physics step, like moving a character that collides with the game world. As
|
||||
mentioned above, ``_physics_process()`` runs at fixed time intervals as much as
|
||||
possible to keep the physics interactions stable. You can change the interval
|
||||
between physics steps in the Project Settings, under Physics -> Common ->
|
||||
Physics Fps. By default, it's set to run 60 times per second.
|
||||
|
||||
The engine calls this method every time it draws a frame:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _physics_process(delta):
|
||||
# Do something...
|
||||
pass
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _PhysicsProcess(float delta)
|
||||
{
|
||||
// Do something...
|
||||
}
|
||||
|
||||
The function ``_process()`` is not synchronized with physics. Its rate depends on
|
||||
hardware and game optimization. It also runs after the physics step in
|
||||
single-threaded games.
|
||||
|
||||
You can see the ``_process()`` function at work by creating a scene with a
|
||||
single Label node, with the following script attached to it:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Label
|
||||
|
||||
var time = 0
|
||||
|
||||
func _process(delta):
|
||||
time += delta
|
||||
text = str(time) # 'text' is a built-in Label property.
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public class CustomLabel : Label
|
||||
{
|
||||
private float _time;
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
_time += delta;
|
||||
Text = _time.ToString(); // 'Text' is a built-in Label property.
|
||||
}
|
||||
}
|
||||
|
||||
When you run the scene, you should see a counter increasing each frame.
|
54
_sources/tutorials/scripting/index.rst.txt
Normal file
54
_sources/tutorials/scripting/index.rst.txt
Normal file
|
@ -0,0 +1,54 @@
|
|||
Scripting
|
||||
=========
|
||||
|
||||
This section covers programming languages and core features to code your games
|
||||
in Godot.
|
||||
|
||||
Here, you will find information that is not already covered in more specific
|
||||
sections. For instance, to learn about inputs, we recommend you to read
|
||||
:ref:`Inputs <toc-learn-features-inputs>`.
|
||||
|
||||
Programming languages
|
||||
---------------------
|
||||
|
||||
The sections below each focus on a given programming language or, in GDNative's
|
||||
case, an interface that works with multiple languages.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-learn-scripting
|
||||
|
||||
gdscript/index
|
||||
visual_script/index
|
||||
c_sharp/index
|
||||
gdnative/index
|
||||
|
||||
Core features
|
||||
-------------
|
||||
|
||||
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
|
||||
|
||||
debug/index
|
||||
idle_and_physics_processing
|
||||
groups
|
||||
nodes_and_scene_instances
|
||||
overridable_functions
|
||||
cross_language_scripting
|
||||
creating_script_templates
|
||||
evaluating_expressions
|
||||
change_scenes_manually
|
||||
instancing_with_signals
|
||||
pausing_games
|
||||
filesystem
|
||||
resources
|
||||
singletons_autoload
|
||||
scene_tree
|
||||
scene_unique_nodes
|
158
_sources/tutorials/scripting/instancing_with_signals.rst.txt
Normal file
158
_sources/tutorials/scripting/instancing_with_signals.rst.txt
Normal file
|
@ -0,0 +1,158 @@
|
|||
.. meta::
|
||||
:keywords: Signal
|
||||
|
||||
.. _doc_instancing_with_signals:
|
||||
|
||||
Instancing with signals
|
||||
=======================
|
||||
|
||||
Signals provide a way to decouple game objects, allowing you to avoid forcing a
|
||||
fixed arrangement of nodes. One sign that a signal might be called for is when
|
||||
you find yourself using ``get_parent()``. Referring directly to a node's parent
|
||||
means that you can't easily move that node to another location in the scene tree.
|
||||
This can be especially problematic when you are instancing objects at runtime
|
||||
and may want to place them in an arbitrary location in the running scene tree.
|
||||
|
||||
Below we'll consider an example of such a situation: firing bullets.
|
||||
|
||||
Shooting example
|
||||
----------------
|
||||
|
||||
Consider a player character that can rotate and shoot towards the mouse. Every
|
||||
time the mouse button is clicked, we create an instance of the bullet at the
|
||||
player's location. See :ref:`doc_instancing` for details.
|
||||
|
||||
We'll use an ``Area2D`` for the bullet, which moves in a straight line at a
|
||||
given velocity:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Area2D
|
||||
|
||||
var velocity = Vector2.ZERO
|
||||
|
||||
func _physics_process(delta):
|
||||
position += velocity * delta
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public class Bullet : Area2D
|
||||
{
|
||||
Vector2 Velocity = new Vector2();
|
||||
|
||||
public override void _PhysicsProcess(float delta)
|
||||
{
|
||||
Position += Velocity * delta;
|
||||
}
|
||||
}
|
||||
|
||||
However, if the bullets are added as children of the player, then they will
|
||||
remain "attached" to the player as it rotates:
|
||||
|
||||
.. image:: img/signals_shoot1.gif
|
||||
|
||||
Instead, we need the bullets to be independent of the player's movement - once
|
||||
fired, they should continue traveling in a straight line and the player can no
|
||||
longer affect them. Instead of being added to the scene tree as a child of the
|
||||
player, it makes more sense to add the bullet as a child of the "main" game
|
||||
scene, which may be the player's parent or even further up the tree.
|
||||
|
||||
You could do this by adding the bullet to the main scene directly:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var bullet_instance = Bullet.instance()
|
||||
get_parent().add_child(bullet_instance)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
Node bulletInstance = Bullet.Instance();
|
||||
GetParent().AddChild(bulletInstance);
|
||||
|
||||
However, this will lead to a different problem. Now if you try to test your
|
||||
"Player" scene independently, it will crash on shooting, because there is no
|
||||
parent node to access. This makes it a lot harder to test your player code
|
||||
independently and also means that if you decide to change your main scene's
|
||||
node structure, the player's parent may no longer be the appropriate node to
|
||||
receive the bullets.
|
||||
|
||||
The solution to this is to use a signal to "emit" the bullets from the player.
|
||||
The player then has no need to "know" what happens to the bullets after that -
|
||||
whatever node is connected to the signal can "receive" the bullets and take the
|
||||
appropriate action to spawn them.
|
||||
|
||||
Here is the code for the player using signals to emit the bullet:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
extends Sprite
|
||||
|
||||
signal shoot(bullet, direction, location)
|
||||
|
||||
var Bullet = preload("res://Bullet.tscn")
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == BUTTON_LEFT and event.pressed:
|
||||
emit_signal("shoot", Bullet, rotation, position)
|
||||
|
||||
func _process(delta):
|
||||
look_at(get_global_mouse_position())
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public class Player : Sprite
|
||||
{
|
||||
[Signal]
|
||||
delegate void Shoot(PackedScene bullet, Vector2 direction, Vector2 location);
|
||||
|
||||
private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
|
||||
|
||||
public override void _Input(InputEvent event)
|
||||
{
|
||||
if (input is InputEventMouseButton mouseButton)
|
||||
{
|
||||
if (mouseButton.ButtonIndex == (int)ButtonList.Left && mouseButton.Pressed)
|
||||
{
|
||||
EmitSignal(nameof(Shoot), _bullet, Rotation, Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override _Process(float delta)
|
||||
{
|
||||
LookAt(GetGlobalMousePosition());
|
||||
}
|
||||
}
|
||||
|
||||
In the main scene, we then connect the player's signal (it will appear in the
|
||||
"Node" tab).
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _on_Player_shoot(Bullet, direction, location):
|
||||
var b = Bullet.instance()
|
||||
add_child(b)
|
||||
b.rotation = direction
|
||||
b.position = location
|
||||
b.velocity = b.velocity.rotated(direction)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void _on_Player_Shoot(PackedScene bullet, Vector2 direction, Vector2 location)
|
||||
{
|
||||
var bulletInstance = (Bullet)bullet.Instance();
|
||||
AddChild(bulletInstance);
|
||||
bulletInstance.Rotation = direction;
|
||||
bulletInstance.Position = location;
|
||||
bulletInstance.Velocity = bulletInstance.Velocity.Rotated(direction);
|
||||
}
|
||||
|
||||
Now the bullets will maintain their own movement independent of the player's
|
||||
rotation:
|
||||
|
||||
.. image:: img/signals_shoot2.gif
|
224
_sources/tutorials/scripting/nodes_and_scene_instances.rst.txt
Normal file
224
_sources/tutorials/scripting/nodes_and_scene_instances.rst.txt
Normal file
|
@ -0,0 +1,224 @@
|
|||
.. _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()
|
||||
<class_Node_method_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.
|
||||
|
||||
If, for example, 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>("Sprite");
|
||||
_camera2d = GetNode<Camera2D>("Camera2D");
|
||||
}
|
||||
|
||||
Note that you get nodes using their name, not their type. Above, "Sprite" and
|
||||
"Camera2D" are the nodes' names in the scene.
|
||||
|
||||
.. image:: img/nodes_and_scene_instances_sprite_node.png
|
||||
|
||||
If you rename the Sprite node as Skin in the Scene dock, you have to change the
|
||||
line that gets the node to ``get_node("Skin")`` in the script.
|
||||
|
||||
.. image:: img/nodes_and_scene_instances_sprite_node_renamed.png
|
||||
|
||||
Node paths
|
||||
----------
|
||||
|
||||
When getting a reference to a node, 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<Tween>("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.
|
||||
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. Firstly, 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 has 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();
|
||||
|
||||
Before calling ``sprite.queue_free()``, the remote scene tree looks like this.
|
||||
|
||||
.. image:: img/nodes_and_scene_instances_remote_tree_with_sprite.png
|
||||
|
||||
After the engine freed the node, the remote scene tree doesn't display the
|
||||
sprite anymore.
|
||||
|
||||
.. image:: img/nodes_and_scene_instances_remote_tree_no_sprite.png
|
||||
|
||||
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
|
||||
-----------------
|
||||
|
||||
Scenes are templates from which you can create as many reproductions as you'd
|
||||
like. This operation is called instancing, and doing it from code happens in two
|
||||
steps:
|
||||
|
||||
1. Loading the scene from the hard drive.
|
||||
2. Creating an instance of the loaded :ref:`PackedScene <class_PackedScene>`
|
||||
resource.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var scene = load("res://MyScene.tscn")
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
var scene = GD.Load<PackedScene>("res://MyScene.tscn");
|
||||
|
||||
Preloading the scene can improve the user's experience as the load operation
|
||||
happens when the compiler reads the script and not at runtime. 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()
|
||||
<class_PackedScene_method_instance>`. It returns a tree of nodes that you can
|
||||
as a child of your current node.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
var instance = scene.instance()
|
||||
add_child(instance)
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
var instance = scene.Instance();
|
||||
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.
|
144
_sources/tutorials/scripting/overridable_functions.rst.txt
Normal file
144
_sources/tutorials/scripting/overridable_functions.rst.txt
Normal file
|
@ -0,0 +1,144 @@
|
|||
.. _doc_overridable_functions:
|
||||
|
||||
Overridable functions
|
||||
=====================
|
||||
|
||||
Godot's Node class provides virtual functions you can override to update nodes
|
||||
every frame or on specific events, like when they enter the scene tree.
|
||||
|
||||
This document presents the ones you'll use most often.
|
||||
|
||||
.. seealso:: Under the hood, these functions rely on Godot's low-level
|
||||
notifications system. To learn more about it, see
|
||||
:ref:`doc_godot_notifications`.
|
||||
|
||||
Two functions allow you to initialize and get nodes, besides the class's
|
||||
constructor: ``_enter_tree()`` and ``_ready()``.
|
||||
|
||||
When the node enters the Scene Tree, it becomes active and the engine calls its
|
||||
``_enter_tree()`` method. That node's children may not be part of the active scene yet. As
|
||||
you can remove and re-add nodes to the scene tree, this function may be called
|
||||
multiple times throughout a node's lifetime.
|
||||
|
||||
Most of the time, you'll use ``_ready()`` instead. This function is called only
|
||||
once in a node's lifetime, after ``_enter_tree()``. ``_ready()`` ensures that all children
|
||||
have entered the scene tree first, so you can safely call ``get_node()`` on it.
|
||||
|
||||
.. seealso:: To learn more about getting node references, read
|
||||
:ref:`doc_nodes_and_scene_instances`.
|
||||
|
||||
Another related callback is ``_exit_tree()``, which the engine calls every time
|
||||
a node exits the scene tree. This can be when you call :ref:`Node.remove_child()
|
||||
<class_Node_method_remove_child>` or when you free a node.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Called every time the node enters the scene tree.
|
||||
func _enter_tree():
|
||||
pass
|
||||
|
||||
# Called when both the node and its children have entered the scene tree.
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
# Called when the node is about to leave the scene tree, after all its
|
||||
# children received the _exit_tree() callback.
|
||||
func _exit_tree():
|
||||
pass
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Called every time the node enters the scene tree.
|
||||
public override void _EnterTree()
|
||||
{
|
||||
base._EnterTree();
|
||||
}
|
||||
|
||||
// Called when both the node and its children have entered the scene tree.
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
}
|
||||
|
||||
// Called when the node is about to leave the scene tree, after all its
|
||||
// children.
|
||||
public override void _ExitTree()
|
||||
{
|
||||
base._ExitTree();
|
||||
}
|
||||
|
||||
The two virtual methods ``_process()`` and ``_physics_process()`` allow you to
|
||||
update the node, every frame and every physics frame respectively. For more
|
||||
information, read the dedicated documentation:
|
||||
:ref:`doc_idle_and_physics_processing`.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Called every frame, as often as possible.
|
||||
func _process(delta):
|
||||
pass
|
||||
|
||||
# Called every physics frame.
|
||||
func _physics_process(delta):
|
||||
pass
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
// Called every frame, as often as possible.
|
||||
base._Process(delta);
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(float delta)
|
||||
{
|
||||
// Called every physics frame.
|
||||
base._PhysicsProcess(delta);
|
||||
}
|
||||
|
||||
Two more essential built-in node callback functions are
|
||||
:ref:`Node._unhandled_input() <class_Node_method__unhandled_input>` and
|
||||
:ref:`Node._input() <class_Node_method__input>`, which you use to both receive
|
||||
and process individual input events. The ``_unhandled_input()`` method receives
|
||||
every key press, mouse click, etc. that have not been handled already in an
|
||||
``_input()`` callback or in a user interface component. You want to use it for
|
||||
gameplay input in general. The ``_input()`` callback allows you to intercept and
|
||||
process input events before ``_unhandled_input()`` gets them.
|
||||
|
||||
To learn more about inputs in Godot, see the :ref:`Input section <toc-learn-features-inputs>`.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
# Called once for every event.
|
||||
func _unhandled_input(event):
|
||||
pass
|
||||
|
||||
# Called once for every event, before _unhandled_input(), allowing you to
|
||||
# consume some events.
|
||||
func _input(event):
|
||||
pass
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
// Called once for every event.
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
base._UnhandledInput(event);
|
||||
}
|
||||
|
||||
// Called once for every event, before _unhandled_input(), allowing you to
|
||||
// consume some events.
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
base._Input(event);
|
||||
}
|
||||
|
||||
There are some more overridable functions like
|
||||
:ref:`Node._get_configuration_warning()
|
||||
<class_Node_method__get_configuration_warning>`. Specialized node types provide
|
||||
more callbacks like :ref:`CanvasItem._draw() <class_CanvasItem_method__draw>` to
|
||||
draw programmatically or :ref:`Control._gui_input()
|
||||
<class_Control_method__gui_input>` to handle clicks and input on UI elements.
|
133
_sources/tutorials/scripting/pausing_games.rst.txt
Normal file
133
_sources/tutorials/scripting/pausing_games.rst.txt
Normal file
|
@ -0,0 +1,133 @@
|
|||
.. _doc_pausing_games:
|
||||
|
||||
Pausing games and process mode
|
||||
==============================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In most games it is desirable to, at some point, interrupt the
|
||||
game to do something else, such as taking a break or changing options.
|
||||
Implementing a fine-grained control for what can be paused (and what cannot)
|
||||
is a lot of work, so a simple framework for pausing is provided in
|
||||
Godot.
|
||||
|
||||
How pausing works
|
||||
-----------------
|
||||
|
||||
To pause the game the pause state must be set. This is done by assigning
|
||||
``true`` to the :ref:`SceneTree.paused <class_SceneTree_property_paused>` property:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
get_tree().paused = true
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
GetTree().Paused = true;
|
||||
|
||||
Doing this will cause two things. First, 2D and 3D physics will be stopped
|
||||
for all nodes. Second, the behavior of certain nodes will stop or start
|
||||
depending on their process mode.
|
||||
|
||||
.. note:: The physics servers can be made active while the game is
|
||||
paused by using their ``set_active`` methods.
|
||||
|
||||
Process Modes
|
||||
-------------
|
||||
|
||||
Each node in Godot has a "Pause Mode" that defines when it processes. It can
|
||||
be found and changed under a node's :ref:`Node <class_Node>` properties in the inspector.
|
||||
|
||||
.. image:: img/pausemode.png
|
||||
|
||||
You can also alter the property with code:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _ready():
|
||||
pause_mode = Node.PAUSE_MODE_PROCESS
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
PauseMode = Node.PauseModeEnum.Process;
|
||||
}
|
||||
|
||||
This is what each mode tells a node to do:
|
||||
|
||||
- **Inherit**: Process depending on the state of the parent, grandparent, etc.
|
||||
The first parent that has a state other than Inherit determines the effective
|
||||
value that will be used.
|
||||
- **Stop**: If the scene tree is paused, pause the node no matter what (and
|
||||
children in Inherit mode). When paused, this node *will not* process.
|
||||
- **Process**: Even if the scene tree is paused, process the node no matter
|
||||
what (and children in Inherit mode). Paused or not, this node *will* process.
|
||||
|
||||
By default, all nodes have this property in the "Inherit" state. If the
|
||||
parent is set to "Inherit", then the grandparent will be checked and so
|
||||
on. If a state can't be found in any of the grandparents, the pause state
|
||||
in SceneTree is used. This means that, by default, when the game is paused
|
||||
every node will be paused. Several things happen when a node stops processing.
|
||||
|
||||
The ``_process``, ``_physics_process``, ``_input``, and ``_input_event`` functions
|
||||
will not be called. However signals still work and cause their connected function to
|
||||
run, even if that function's script is attached to a node that has its pause
|
||||
mode set to "Stop".
|
||||
|
||||
Animation nodes will pause their current animation, audio nodes
|
||||
will pause their current audio stream, and particles will pause. These resume
|
||||
automatically when the game is no longer paused.
|
||||
|
||||
It is important to note that even if a node is processing while the game is
|
||||
paused physics will **NOT** work for it by default. As stated earlier this is
|
||||
because the physics servers are turned off. The physics servers can be made
|
||||
active while the game is paused by using their ``set_active`` methods.
|
||||
|
||||
Pause Menu Example
|
||||
------------------
|
||||
|
||||
Here is an example of a pause menu. Create a popup or panel with controls
|
||||
inside, and set its pause mode to "Process" then hide it. By setting the
|
||||
root of the pause popup to "Process", all children and grandchildren will
|
||||
inherit that state. This way, this branch of the scene tree will continue
|
||||
working when paused.
|
||||
|
||||
Finally, make it so when a pause button is pressed (any button will do),
|
||||
enable the pause and show the pause screen.
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _on_pause_button_pressed():
|
||||
get_tree().paused = true
|
||||
$pause_popup.show()
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void _on_pause_button_pressed()
|
||||
{
|
||||
GetTree().Paused = true;
|
||||
GetNode<Control>("pause_popup").Show();
|
||||
}
|
||||
|
||||
To unpause, do the opposite when the pause screen is
|
||||
closed:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
func _on_pause_popup_close_pressed():
|
||||
$pause_popup.hide()
|
||||
get_tree().paused = false
|
||||
|
||||
.. code-tab:: csharp
|
||||
|
||||
public void _on_pause_popup_close_pressed()
|
||||
{
|
||||
GetNode<Control>("pause_popup").Hide();
|
||||
GetTree().Paused = false;
|
||||
}
|
379
_sources/tutorials/scripting/resources.rst.txt
Normal file
379
_sources/tutorials/scripting/resources.rst.txt
Normal file
|
@ -0,0 +1,379 @@
|
|||
.. _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 = GetNode<Sprite>("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
|
||||
|
||||
# Make sure that every parameter has a default value.
|
||||
# Otherwise, there will be problems with creating and editing
|
||||
# your resource via the inspector.
|
||||
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 partial class BotStats : Resource
|
||||
{
|
||||
[Export]
|
||||
public int Health { get; set; }
|
||||
|
||||
[Export]
|
||||
public Resource SubResource { get; set; }
|
||||
|
||||
[Export]
|
||||
public string[] Strings { get; set; }
|
||||
|
||||
// Make sure you provide a parameterless constructor.
|
||||
// In C#, a parameterless constructor is different from a
|
||||
// constructor with all default values.
|
||||
// Without a parameterless constructor, Godot will have problems
|
||||
// creating and editing your resource via the inspector.
|
||||
public BotStats() : this(0, null, null) {}
|
||||
|
||||
public BotStats(int health, Resource subResource, string[] strings)
|
||||
{
|
||||
Health = health;
|
||||
SubResource = subResource;
|
||||
Strings = strings ?? Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 partial 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 partial class MyNode : Node
|
||||
{
|
||||
public partial 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
_sources/tutorials/scripting/scene_tree.rst.txt
Normal file
179
_sources/tutorials/scripting/scene_tree.rst.txt
Normal file
|
@ -0,0 +1,179 @@
|
|||
.. _doc_scene_tree:
|
||||
|
||||
Using 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
|
||||
child or grandchild of it.
|
||||
#. 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 threaded (background)
|
||||
loading. This must be done manually using :ref:`doc_singletons_autoload`
|
||||
and :ref:`doc_background_loading`.
|
37
_sources/tutorials/scripting/scene_unique_nodes.rst.txt
Normal file
37
_sources/tutorials/scripting/scene_unique_nodes.rst.txt
Normal file
|
@ -0,0 +1,37 @@
|
|||
.. _doc_scene_unique_nodes:
|
||||
|
||||
Scene Unique Nodes
|
||||
==================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
There are times in a project where a node needs to be called
|
||||
from a script. However, its position in the tree might change
|
||||
over time as adjustments are made to a scene, such as a
|
||||
button in a UI scene.
|
||||
|
||||
In situations like this, a node can be turned into a scene
|
||||
unique node to avoid having to update a script every time
|
||||
its path is changed.
|
||||
|
||||
Creating and using them
|
||||
-----------------------
|
||||
|
||||
In the Scene tree dock, right-click on a node and select
|
||||
**Access as Scene Unique Name** in the context menu.
|
||||
|
||||
.. image:: img/unique_name.png
|
||||
|
||||
After checking this, the node will now have a percent symbol (**%**) next
|
||||
to its name in the scene tree:
|
||||
|
||||
.. image:: img/percent.png
|
||||
|
||||
To use a unique node in a script, use the ``%`` symbol and the node's
|
||||
name in the path for ``get_node()``. For example:
|
||||
|
||||
.. tabs::
|
||||
.. code-tab:: gdscript GDScript
|
||||
|
||||
get_node("%RedButton").text = "Hello"
|
305
_sources/tutorials/scripting/singletons_autoload.rst.txt
Normal file
305
_sources/tutorials/scripting/singletons_autoload.rst.txt
Normal file
|
@ -0,0 +1,305 @@
|
|||
.. _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.
|
||||
|
||||
.. tip::
|
||||
|
||||
If you're creating an autoload as part of an editor plugin, consider
|
||||
:ref:`registering it automatically in the Project Settings <doc_making_plugins_autoload>`
|
||||
when the plugin is enabled.
|
||||
|
||||
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. Like regular scenes, the engine will read these nodes
|
||||
in top-to-bottom order.
|
||||
|
||||
.. 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 = GetNode<PlayerVariables>("/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
|
||||
|
||||
.. warning::
|
||||
|
||||
Autoloads must **not** be removed using ``free()`` or ``queue_free()`` at
|
||||
runtime, or the engine will crash.
|
||||
|
||||
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().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().Root;
|
||||
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().root.add_child(current_scene)
|
||||
|
||||
# Optionally, to make it compatible with the SceneTree.change_scene() API.
|
||||
get_tree().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().Root.AddChild(CurrentScene);
|
||||
|
||||
// Optionally, to make it compatible with the SceneTree.change_scene() API.
|
||||
GetTree().CurrentScene = 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 = GetNode<Global>("/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 = GetNode<Global>("/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.
|
|
@ -0,0 +1,112 @@
|
|||
.. _doc_custom_visualscript_nodes:
|
||||
|
||||
Custom VisualScript nodes
|
||||
=========================
|
||||
|
||||
.. warning::
|
||||
|
||||
`Godot 4.0 will remove VisualScript from core entirely. <https://godotengine.org/article/godot-4-will-discontinue-visual-scripting>`__
|
||||
As a result, creating new projects using visual scripting in Godot is not recommended.
|
||||
Future Godot 4.x releases may have VisualScript reimplemented as an extension.
|
||||
|
||||
While Godot 3.x will keep VisualScript supported, we recommend
|
||||
:ref:`trying out GDScript <toc-learn-scripting-gdscript>` instead,
|
||||
especially if you intend to migrate your project to Godot 4.
|
||||
|
||||
Custom nodes are written in GDScript and can then be used in VisualScript.
|
||||
This is useful for offloading complex code to GDScript and reusing it.
|
||||
|
||||
Creating a custom node
|
||||
----------------------
|
||||
|
||||
Create a new script that extends :ref:`class_VisualScriptCustomNode` and put a ``tool`` keyword at the top. This is needed for the script to run in the editor.
|
||||
|
||||
There are some functions that can be implemented to set parameters of the custom node.
|
||||
Only add functions that are needed, a ``_has_input_sequence_port`` function is not necessary if it should return ``false`` for example.
|
||||
|
||||
The most important part of a custom node is the ``_step`` function. The logic of the node is defined there.
|
||||
|
||||
The ``inputs`` parameter holds the value of the input ports.
|
||||
|
||||
The ``outputs`` parameter is an array where the indices represent the output port ids. It can be modified to set the values of the output ports.
|
||||
|
||||
``start_mode`` can be checked to see if it is the first time ``_step`` is called.
|
||||
|
||||
``working_mem`` is persistent each ``_step`` call. It can be used to store information.
|
||||
|
||||
If you want to throw an error, for example if the input types are incorrect, you can return the error message as a string.
|
||||
When everything goes right, return the id of the sequence port which should be called next. If your custom node doesn't have any, just return 0.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
tool
|
||||
extends VisualScriptCustomNode
|
||||
|
||||
# The name of the custom node as it appears in the search.
|
||||
func _get_caption():
|
||||
return "Get Input Direction 2D"
|
||||
|
||||
func _get_category():
|
||||
return "Input"
|
||||
|
||||
# The text displayed after the input port / sequence arrow.
|
||||
func _get_text():
|
||||
return ""
|
||||
|
||||
func _get_input_value_port_count():
|
||||
return 0
|
||||
|
||||
# The types of the inputs per index starting from 0.
|
||||
func _get_input_value_port_type(idx):
|
||||
return TYPE_OBJECT
|
||||
|
||||
func _get_output_value_port_count():
|
||||
return 1
|
||||
|
||||
# The types of outputs per index starting from 0.
|
||||
func _get_output_value_port_type(idx):
|
||||
return TYPE_VECTOR2
|
||||
|
||||
# The text displayed before each output node per index.
|
||||
func _get_output_value_port_name(idx):
|
||||
return "Direction"
|
||||
|
||||
func _has_input_sequence_port():
|
||||
return true
|
||||
|
||||
# The number of output sequence ports to use
|
||||
# (has to be at least one if you have an input sequence port).
|
||||
func _get_output_sequence_port_count():
|
||||
return 1
|
||||
|
||||
func _step(inputs, outputs, start_mode, working_mem):
|
||||
# start_mode can be checked to see if it is the first time _step is called.
|
||||
# This is useful if you only want to do an operation once.
|
||||
|
||||
# working_memory is persistent between _step calls.
|
||||
|
||||
# The inputs array contains the value of the input ports.
|
||||
|
||||
var x = int(Input.is_action_pressed("ui_right")) - int(Input.is_action_pressed("ui_left"))
|
||||
var y = int(Input.is_action_pressed("ui_down")) - int(Input.is_action_pressed("ui_up"))
|
||||
|
||||
# The outputs array is used to set the data of the output ports.
|
||||
|
||||
outputs[0] = Vector2(x, y)
|
||||
|
||||
# Return the error string if an error occurred, else the id of the next sequence port.
|
||||
return 0
|
||||
|
||||
Using a custom node
|
||||
-------------------
|
||||
|
||||
To use the script, add a ``CustomNode``, select it and drag your custom node script into the ``script`` property shown in the inspector.
|
||||
|
||||
.. image:: img/visual_script_custom_node_set_script.png
|
||||
|
||||
Result:
|
||||
|
||||
.. image:: img/visual_script_custom_node_result.png
|
|
@ -0,0 +1,131 @@
|
|||
.. _doc_getting_started_visual_script:
|
||||
|
||||
Getting started with Visual Scripting
|
||||
=====================================
|
||||
|
||||
As with everything in Godot, we prioritize a good experience over copying or integrating third party solutions
|
||||
which might not fit nicely in the current workflow. This led us to write our own version of how we believe
|
||||
this feature would work best with the engine.
|
||||
|
||||
In Godot, a Visual Script fits smoothly together with regular scripts in the Editor tab
|
||||
|
||||
.. image:: img/visual_script1.png
|
||||
|
||||
|
||||
In fact, Visual Scripting integrates so well to Godot that it's hard to believe it was added only
|
||||
in version 3.0. This is because, when editing, the rest of Godot panels and docks act like a
|
||||
palette from where you can drag and drop all sorts of information to the script canvas:
|
||||
|
||||
.. image:: img/visual_script2.png
|
||||
|
||||
|
||||
Creating a script
|
||||
-----------------
|
||||
|
||||
Creating scripts works the same as with other scripting languages: Select any node in the scene
|
||||
and push the "New Script" button at the top right corner of the Scene Tree dock:
|
||||
|
||||
.. image:: img/visual_script3.png
|
||||
|
||||
|
||||
Once it opens, the script type "Visual Script" must be selected from the drop down list. The script extension
|
||||
must be ".vs" (for Visual Script!).
|
||||
|
||||
.. image:: img/visual_script4.png
|
||||
|
||||
|
||||
Finally, the Script Editor will open, allowing you to start editing the visual script:
|
||||
|
||||
.. image:: img/visual_script5.png
|
||||
|
||||
|
||||
Adding a function
|
||||
-----------------
|
||||
|
||||
Unlike other visual scripting implementations, Visual Scripting in Godot is heavily based on functions.
|
||||
This happens because it uses the same interface to communicate with the engine as other scripting engines.
|
||||
In Godot, the scripting interface is universal and all implementations conform to it.
|
||||
|
||||
A function is an individual canvas with nodes connected.
|
||||
|
||||
A single script can contain many functions, each of which will have a canvas of its own, allowing for more organization.
|
||||
|
||||
There are three main ways to add functions in a script:
|
||||
|
||||
Overriding a virtual function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Most types of nodes and other types of objects in Godot contain virtual functions. These are functions that
|
||||
will be called (run your code) when something happens and can be looked up in the reference. Virtual functions
|
||||
are listed when pressing the "Override" icon in the member panel:
|
||||
|
||||
.. image:: img/visual_script6.png
|
||||
|
||||
|
||||
In the following example, a function will be executed when the node is loaded and added to the running scene.
|
||||
For this, the _ready() virtual method will be overridden:
|
||||
|
||||
.. image:: img/visual_script7.png
|
||||
|
||||
|
||||
Finally, a canvas appears for this function, showing the override:
|
||||
|
||||
.. image:: img/visual_script8.png
|
||||
|
||||
|
||||
As some functions expect you to return a value, they will also add a return node where such value is supposed to be
|
||||
provided:
|
||||
|
||||
.. image:: img/visual_script9.png
|
||||
|
||||
|
||||
Connecting a signal to a function
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Nodes in a tree emit signals when something happens. Godot uses signals for all sorts of things.
|
||||
A typical example would be a button that emits a "pressed" signal when actually pressed.
|
||||
|
||||
For this, a node must be selected and the Node tab opened. This will allow inspecting the signals.
|
||||
Once they are displayed, connect the "pressed" signal:
|
||||
|
||||
.. image:: img/visual_script10.png
|
||||
|
||||
|
||||
This will open the connection dialog. In this dialog, you must select the node where the signal will be
|
||||
connected to, and the function that will receive the signal:
|
||||
|
||||
.. image:: img/visual_script11.png
|
||||
|
||||
|
||||
If this is done right, a new function will be created in our script and a signal will automatically be
|
||||
connected to it:
|
||||
|
||||
.. image:: img/visual_script12.png
|
||||
|
||||
|
||||
Creating a function manually
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The last way to create functions is to do it manually. In general, this is not as common unless you
|
||||
really need it. Custom functions work when another (or the same) script calls them manually.
|
||||
The main use cases for this are breaking a larger function up into several manageable chunks and reusing your visual code.
|
||||
|
||||
To create a function manually, push the big "Plus" button, and a new function will be added
|
||||
with a default name:
|
||||
|
||||
.. image:: img/visual_script13.png
|
||||
|
||||
|
||||
This will add a new function, which can be renamed by simply double clicking its name:
|
||||
|
||||
|
||||
.. image:: img/visual_script14.png
|
||||
|
||||
|
||||
To edit the "arguments" this function can get (the values you pass to it when you call this function),
|
||||
simply click the Function node and check the inspector:
|
||||
|
||||
.. image:: img/visual_script15.png
|
||||
|
||||
|
||||
More on that will be explained later in this document.
|
21
_sources/tutorials/scripting/visual_script/index.rst.txt
Normal file
21
_sources/tutorials/scripting/visual_script/index.rst.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
VisualScript
|
||||
============
|
||||
|
||||
.. warning::
|
||||
|
||||
`Godot 4.0 will remove VisualScript from core entirely. <https://godotengine.org/article/godot-4-will-discontinue-visual-scripting>`__
|
||||
As a result, creating new projects using visual scripting in Godot is not recommended.
|
||||
Future Godot 4.x releases may have VisualScript reimplemented as an extension.
|
||||
|
||||
While Godot 3.x will keep VisualScript supported, we recommend
|
||||
:ref:`trying out GDScript <toc-learn-scripting-gdscript>` instead,
|
||||
especially if you intend to migrate your project to Godot 4.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:name: toc-learn-scripting-visual_script
|
||||
|
||||
what_is_visual_scripting
|
||||
getting_started
|
||||
nodes_purposes
|
||||
custom_visualscript_nodes
|
|
@ -0,0 +1,510 @@
|
|||
.. _doc_nodes_purposes_visual_script:
|
||||
|
||||
Nodes and terminology
|
||||
=====================
|
||||
|
||||
Before continuing, it must be noted that the *Node* terminology needs to be used with care.
|
||||
When referring to *Visual Script Nodes* (or generally *Nodes*) this text will refer to the little boxes you connect with lines, which are part of a graph.
|
||||
When referring to *Scene Nodes*, it is implied that the elements that make up a Scene are being referred, which are part of a tree. Their naming is similar but their function is different.
|
||||
When referring to *Node* here, it will be implied that a *Visual Script Node* is referred to unless indicated otherwise.
|
||||
|
||||
.. image:: img/visual_script16.png
|
||||
|
||||
|
||||
Node properties
|
||||
---------------
|
||||
|
||||
Like in most visual scripting implementations, each node has editable properties. In Godot, though, we try to avoid
|
||||
bloating the nodes with editable controls for the sake of readability.
|
||||
|
||||
Nodes still display the required information as text, but editing is done via the *Inspector*. To edit them,
|
||||
select any node and edit its properties in the *Inspector*.
|
||||
|
||||
|
||||
Ports and connections
|
||||
---------------------
|
||||
|
||||
Programming in Godot Visual Scripting is done via *Nodes* and *Port Connections* inside each function.
|
||||
|
||||
|
||||
Ports
|
||||
~~~~~
|
||||
|
||||
Nodes in Godot Visual Scripting have *Ports*. These are endpoints that appear to the
|
||||
left and right of nodes and which can be used to make *Connections*:
|
||||
There are two types of *Ports*: *Sequence* and *Data*.
|
||||
|
||||
.. image:: img/visual_script17.png
|
||||
|
||||
|
||||
*Sequence Ports* indicate the order in which operations are executed.
|
||||
Typically when a *Node* is done processing, it will go to the next node from one of the ports at the right.
|
||||
If nothing is connected, the function may end, or another output *Sequence Port* might be tried (this depends on the node).
|
||||
Thanks to this, you can follow the logic flow within a function by following the white lines.
|
||||
Not every *Node* has *Sequence Ports*. In fact, most do not.
|
||||
|
||||
*Data Ports* ports contain typed values. Types can be any regular Godot types,
|
||||
such as a boolean, an integer, a string, a Vector3, an array, any Object or Scene Node, etc.
|
||||
A *Data Port* on the right side of a node is considered an output, while,
|
||||
a port on the left side is an input. Connecting them allows information to flow to the next node.
|
||||
|
||||
Not all *Data Port* types are compatible and will allow connections, though.
|
||||
Pay special attention to colors and icons, as each type has a different representation:
|
||||
|
||||
.. image:: img/visual_script18.png
|
||||
|
||||
|
||||
Connections
|
||||
~~~~~~~~~~~
|
||||
|
||||
Connecting is a relatively simple process. Drag an *Output Port* towards an *Input Port*.
|
||||
|
||||
.. image:: img/visual_script_connect.gif
|
||||
|
||||
|
||||
Disconnecting takes a bit more practice. Disconnecting in *Data Ports* happens by
|
||||
dragging the *Input* away, while for *Sequence Ports*, this happens by dragging the *Output* away.
|
||||
|
||||
.. image:: img/visual_script_disconnect.gif
|
||||
|
||||
|
||||
This may seem strange at first, but it happens because *Data Ports* are 1:N
|
||||
(A single output port can connect to many inputs), while *Sequence Ports* are N:1
|
||||
(Many sequence outputs can be connected to a single input).
|
||||
|
||||
Connecting to empty space (drag to connect but unpress over empty space) is also context sensitive, it will supply
|
||||
a list of most common operations. For sequences, it will be conditional nodes:
|
||||
|
||||
.. image:: img/visual_script52.png
|
||||
|
||||
|
||||
While, for data, a contextual set/get/call menu will open:
|
||||
|
||||
.. image:: img/visual_script53.png
|
||||
|
||||
|
||||
Adding nodes
|
||||
------------
|
||||
|
||||
Finally! We got to the fun part! But, before explaining in more detail what each type of node does,
|
||||
let's take a short look at how nodes are most commonly added and dealt with.
|
||||
|
||||
|
||||
Accessing scene nodes
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
One of the most common tasks is accessing Scene Tree Nodes (again, not to mistake with *Visual Script Nodes*).
|
||||
Dragging from the Scene Tree and dropping into the canvas will ask you to *call a method* (sometimes referred to as *member function*) on this node.
|
||||
|
||||
.. image:: img/visual_script19.png
|
||||
|
||||
|
||||
While accessing properties is desired in most cases (more on that below), sometimes *calling methods* can be useful too.
|
||||
Methods execute specific actions on objects. In the above case, the mouse pointer can be warped to a position in local
|
||||
coordinates to the control. Another common use case is queueing a node for deletion, which is done with the *queue_free* method.
|
||||
|
||||
.. image:: img/visual_script20.png
|
||||
|
||||
|
||||
Care must be taken that this only works if the scene being edited contains your *Visual Script* in one of the nodes! Otherwise, a warning will be shown.
|
||||
|
||||
|
||||
Accessing scene node properties
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the most common way to edit *Scene Nodes* in Visual Scripting. Select a *Scene Node* from the *Scene Tree*, go to the Inspector, find *the Name* of the property you want to edit (hint, *not* the value!) and drag it to the canvas:
|
||||
|
||||
.. image:: img/visual_script21.png
|
||||
|
||||
|
||||
The result is that this value can be changed from your script by writing to a *Data Port*.
|
||||
|
||||
If instead reading this value is desired, drag the node again but hold :kbd:`Ctrl` (or :kbd:`Cmd` on macOS). This will create a getter:
|
||||
|
||||
.. image:: img/visual_script22.png
|
||||
|
||||
|
||||
In this case, the value can be read from a *Data Port*.
|
||||
|
||||
|
||||
Variables
|
||||
~~~~~~~~~
|
||||
|
||||
Variables are memory containers local to the script which can hold a value. This value can be read from any of the functions of the script or from other scripts via the method described in the previous step.
|
||||
|
||||
To add a Variable, push the "+" button on the *Variables* section of the Members panel. Double-click the new variable to rename it:
|
||||
|
||||
.. image:: img/visual_script23.png
|
||||
|
||||
|
||||
Right-clicking the variable allows you to configure its properties:
|
||||
|
||||
.. image:: img/visual_script24.png
|
||||
|
||||
.. image:: img/visual_script25.png
|
||||
|
||||
|
||||
As it can be seen above, the type and initial value of the variable can be changed, as well as some property hints.
|
||||
Ticking the "Export" option makes the variable visible in the Inspector when selecting the node. This also makes it available to other scripts via the method described in the previous step.
|
||||
|
||||
.. image:: img/visual_script28.png
|
||||
|
||||
|
||||
To use the variable in the script, simply drag it to the canvas to create a getter:
|
||||
|
||||
.. image:: img/visual_script26.png
|
||||
|
||||
|
||||
Likewise, hold :kbd:`Ctrl` (or :kbd:`Cmd` on macOS) to drop a setter:
|
||||
|
||||
.. image:: img/visual_script27.png
|
||||
|
||||
|
||||
Signals
|
||||
~~~~~~~
|
||||
|
||||
It is also possible to create your own signals in a script and use them. For this, do the same steps you did for variables in the previous step, except for *Signals*:
|
||||
|
||||
.. image:: img/visual_script29.png
|
||||
|
||||
|
||||
A signal can also be edited via the right-click menu to customize its arguments:
|
||||
|
||||
.. image:: img/visual_script30.png
|
||||
|
||||
|
||||
The signal you have created will appear in the Inspector, along with the built-in node signals. This allows you to connect it from another script from another *Scene Node*:
|
||||
|
||||
.. image:: img/visual_script31.png
|
||||
|
||||
|
||||
Finally, to emit the signal, simply drag it to the canvas:
|
||||
|
||||
.. image:: img/visual_script32.png
|
||||
|
||||
|
||||
Remember that emitting a signal is a sequenced operation, so it must come from a Sequence port.
|
||||
|
||||
|
||||
Adding more nodes
|
||||
-----------------
|
||||
|
||||
Now that the basics are covered, let's discuss the large amount of utility nodes available for your canvas!
|
||||
Below the member panel, exists the list of all available node types:
|
||||
|
||||
.. image:: img/visual_script33.png
|
||||
|
||||
|
||||
Pressing :kbd:`Ctrl + F` (or :kbd:`Cmd + F` on macOS) allows you to search the list.
|
||||
|
||||
Any of them can be dragged to the scene. Unlike nodes (e.g. dragging a property
|
||||
from the Inspector sets the context to the node being edited automatically), these are added without any "contextual" information, so this has to be done manually.
|
||||
|
||||
.. image:: img/visual_script34.png
|
||||
|
||||
|
||||
Remember that you can check the class reference for what each node does, as they are documented there. That mentioned,
|
||||
a brief overview of node types follows:
|
||||
|
||||
|
||||
Constants
|
||||
~~~~~~~~~
|
||||
|
||||
Constant nodes are nodes that provide values that, while not changing over time, can be useful as reference values.
|
||||
Most of the time they are integer or float.
|
||||
|
||||
.. image:: img/visual_script36.png
|
||||
|
||||
|
||||
The first one is "Constant", which allows you to select any value of any type as constant, from an integer (42) to a String ("Hello!"). In general, this node is not used that often because of default input values in *Data Ports*, but it's good to know it exists.
|
||||
|
||||
The second is the GlobalConstant node, which contains a long list of constants for global types in Godot. In there
|
||||
you can find some useful constants to refer to key names, joystick or mouse buttons, etc.
|
||||
|
||||
The third one is MathConstant, which provides typical mathematical constants, such as PI, E, etc.
|
||||
|
||||
|
||||
Data
|
||||
~~~~
|
||||
|
||||
Data nodes deal with all sorts of access to information. Any information in Godot is accessed via these nodes, so
|
||||
they are some of the most important ones to use and pretty diverse.
|
||||
|
||||
.. image:: img/visual_script37.png
|
||||
|
||||
|
||||
There are many types of nodes of interest here, so a short attempt to describe them will follow:
|
||||
|
||||
|
||||
Action
|
||||
^^^^^^
|
||||
|
||||
Action nodes are vital when dealing with input from a device. You can read more about actions in the (@TODO ACTION TUTE LINK).
|
||||
In the following example below, the control is moved to the right when the "move_right" action is pressed.
|
||||
|
||||
.. image:: img/visual_script38.png
|
||||
|
||||
|
||||
Engine Singleton
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Engine singletons are global interfaces (meaning they can be accessed without a reference; unlike Scene Nodes, they are always available).
|
||||
They have several purposes, but in general, they are useful for low-level access or OS-related access.
|
||||
|
||||
.. image:: img/visual_script39.png
|
||||
|
||||
|
||||
Remember that dragging a connection to empty space will help you call functions or set/get properties on these:
|
||||
|
||||
.. image:: img/visual_script40.png
|
||||
|
||||
|
||||
Local Variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
These are nodes you can use as temporary storage for your graphs. Make sure they all have the same name and type when using them and they will reference the same piece of memory.
|
||||
|
||||
.. image:: img/visual_script41.png
|
||||
|
||||
|
||||
As it can be seen above, there are two nodes available: A simple getter, and a sequenced setter (setting requires a sequence port).
|
||||
|
||||
|
||||
Scene Node
|
||||
^^^^^^^^^^
|
||||
|
||||
This is just a reference to a node in the tree, but it's easier to use this node by dragging the actual node
|
||||
from the scene tree to the canvas (this will create it and configure it).
|
||||
|
||||
|
||||
Self
|
||||
^^^^
|
||||
|
||||
In some rare occasions, it may be desired to pass this Scene Node as argument.
|
||||
It can be used to call functions and set/get properties, or drag nodes (or event the node itself that has the script) from the Scene Tree to the canvas for this.
|
||||
|
||||
|
||||
SceneTree
|
||||
^^^^^^^^^
|
||||
|
||||
This node is similar to the Singleton node because it references the SceneTree, which contains the active scene.
|
||||
SceneTree, however, only works when the node is sitting in the scene and active, otherwise accessing it will
|
||||
return an error.
|
||||
|
||||
SceneTree allows for many low-level things, like setting stretch options, calling groups, make timers, or even
|
||||
load another scene. It's a good class to get familiar with.
|
||||
|
||||
|
||||
Preload
|
||||
^^^^^^^
|
||||
|
||||
This does the same function as preload() in GDScript. It maintains this resource loaded and ready to use. Rather than
|
||||
instancing the node, it's simpler to drag the desired resource from the filesystem dock to the canvas.
|
||||
|
||||
|
||||
Resource Path
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
This node is a simple helper to get a string with a path to a resource you can pick. It's useful in functions that
|
||||
load things from disk.
|
||||
|
||||
|
||||
Comment
|
||||
^^^^^^^
|
||||
|
||||
A Comment node works as a node you can resize to put around other nodes. It will not try to get focus or be brought
|
||||
to top when selecting it. It can also be used to write text on it.
|
||||
|
||||
.. image:: img/visual_script42.png
|
||||
|
||||
|
||||
Flow Control
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Flow control nodes allow the execution to take different branches, usually depending on a
|
||||
given condition.
|
||||
|
||||
.. image:: img/visual_script43.png
|
||||
|
||||
|
||||
Condition
|
||||
^^^^^^^^^
|
||||
|
||||
This is a simple node that checks a bool port. If ``true``, it will go via the "true" sequence port. If ``false``,
|
||||
the second. After going for either of them, it goes via the "done" port. Leaving sequence
|
||||
ports disconnected is fine if not all of them are used.
|
||||
|
||||
|
||||
Iterator
|
||||
^^^^^^^^
|
||||
|
||||
Some data types in Godot (ie, arrays, dictionaries) are iterable. This means that a bit of code can run
|
||||
for each element that it has.
|
||||
|
||||
The Iterator node goes through all elements and, for each of them, it goes via the "each" sequence port,
|
||||
making the element available in the "elem" data port.
|
||||
|
||||
When done, it goes via the "exit" sequence port.
|
||||
|
||||
|
||||
Return
|
||||
^^^^^^
|
||||
|
||||
Some functions can return values. In general for virtual ones, Godot will add the Return node for you.
|
||||
A return node forces the function to end.
|
||||
|
||||
|
||||
Sequence
|
||||
^^^^^^^^
|
||||
|
||||
This node is useful mostly for organizing your graph. It calls its sequence ports in order.
|
||||
|
||||
|
||||
TypeCast
|
||||
^^^^^^^^
|
||||
|
||||
This is a useful and commonly used node. You can use it to cast arguments or other objects
|
||||
to the type you desire. Afterwards, you can even drag the object output to get full completion.
|
||||
|
||||
.. image:: img/visual_script55.png
|
||||
|
||||
|
||||
It is also possible to cast to a script, which will allow complete script properties and functions:
|
||||
|
||||
.. image:: img/visual_script54.png
|
||||
|
||||
|
||||
Switch
|
||||
^^^^^^
|
||||
|
||||
The Switch node is similar to the Condition node, but it matches many values at the same time.
|
||||
|
||||
|
||||
While
|
||||
^^^^^
|
||||
|
||||
This is a more primitive form of iteration. "repeat" sequence output will be called as long as
|
||||
the condition in the "cond" data port is met.
|
||||
|
||||
|
||||
Functions
|
||||
~~~~~~~~~
|
||||
|
||||
Functions are simple helpers, most of the time deterministic. They take some arguments as
|
||||
input and return an output. They are almost never sequenced.
|
||||
|
||||
|
||||
Built-In
|
||||
^^^^^^^^
|
||||
|
||||
There is a list of built-in helpers. The list is almost identical to the one from :ref:`GDScript<class_@GDScript>`. Most of them are mathematical functions, but others can be useful helpers. Make sure to take a look at the list
|
||||
at some point.
|
||||
|
||||
By Type
|
||||
^^^^^^^
|
||||
|
||||
Those are the methods available to basic types. For example, if you want a dot-product, you can search for "dot" instead of the Vector3 category.
|
||||
In most cases just search the list of nodes, it should be faster.
|
||||
|
||||
|
||||
Call
|
||||
^^^^
|
||||
|
||||
This is the generic calling node. It is rarely used directly but by dragging to empty space on an already configured node.
|
||||
|
||||
|
||||
Constructors
|
||||
^^^^^^^^^^^^
|
||||
|
||||
These are all the functions needed to create Godot basic datatypes. For example, If you need to create a Vector3 out of 3 floats, a constructor must be used.
|
||||
|
||||
.. image:: img/visual_script44.png
|
||||
|
||||
|
||||
Destructor
|
||||
^^^^^^^^^^
|
||||
|
||||
This is the opposite to Constructor, it allows to separate any basic type (ie, Vector3) into its sub-elements.
|
||||
|
||||
.. image:: img/visual_script45.png
|
||||
|
||||
|
||||
Emit Signal
|
||||
^^^^^^^^^^^
|
||||
|
||||
Emits signals from any object. In general it's not that useful, as dragging a signal to the canvas works better.
|
||||
|
||||
|
||||
Get/Set
|
||||
^^^^^^^
|
||||
|
||||
Generic Getter/Setter node. Dragging properties from the Inspector works better, as they appear properly configured on drop.
|
||||
|
||||
|
||||
Wait
|
||||
^^^^
|
||||
|
||||
The Wait nodes will suspend execution of the function until something happens (many frames can pass until resuming, in fact).
|
||||
Default nodes allow you to wait for a frame to pass, a fixed frame or a given amount of time until execution is resumed.
|
||||
|
||||
|
||||
Yield
|
||||
^^^^^
|
||||
|
||||
This node completely suspends the execution of the script, and it will make the function return a value that can be used to resume execution.
|
||||
|
||||
|
||||
Yield Signal
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Same as Yield, but will wait until a given signal is emitted.
|
||||
|
||||
|
||||
Index
|
||||
~~~~~
|
||||
|
||||
Generic indexing operator, not often used but it's good that it exists just in case.
|
||||
|
||||
|
||||
Operators
|
||||
~~~~~~~~~
|
||||
|
||||
These are mostly generic operators, such as addition, multiplication, comparison, etc.
|
||||
By default, these mostly accept any datatype (and will throw an error at run-time if the types
|
||||
fed do not match those expected by the operator). It is always recommended to set the right
|
||||
type for operators to catch errors faster and make the graph easier to read.
|
||||
|
||||
.. image:: img/visual_script46.png
|
||||
|
||||
|
||||
Expression Node
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Among the operators, the *Expression* node is the most powerful. If well used, it allows you to enormously simplify
|
||||
visual scripts that are math or logic heavy. Type any expression on it and it will be executed in real-time.
|
||||
|
||||
Expression nodes can:
|
||||
|
||||
- Perform math and logic expressions based on custom inputs (eg: "a*5+b", where a and b are custom inputs):
|
||||
|
||||
.. image:: img/visual_script47.png
|
||||
|
||||
|
||||
- Access local variables or properties:
|
||||
|
||||
.. image:: img/visual_script48.png
|
||||
|
||||
|
||||
- Use most of the existing built-in functions that are available to GDScript, such as ``sin()``, ``cos()``, ``print()``, as well as constructors, such as ``Vector3(x, y, z)``, ``Rect2(...)``, etc.:
|
||||
|
||||
.. image:: img/visual_script49.png
|
||||
|
||||
|
||||
- Call API functions:
|
||||
|
||||
.. image:: img/visual_script50.png
|
||||
|
||||
|
||||
- Use sequenced mode, which makes more sense in case of respecting the processing order:
|
||||
|
||||
.. image:: img/visual_script51.png
|
|
@ -0,0 +1,24 @@
|
|||
.. _doc_what_is_visual_script:
|
||||
|
||||
What is Visual Scripting
|
||||
========================
|
||||
|
||||
Visual Scripting is a tool designed to make the entry barrier to programming
|
||||
much lower. As code is more visual, it needs less abstract thinking to be
|
||||
understood. Any artist, animator, game designer, etc. can look at it and quickly
|
||||
grasp the flow of logic.
|
||||
|
||||
The reason it does not make existing programming obsolete is, simply, that it does not scale as well.
|
||||
It takes considerably more time to create code with it, and it's often more difficult
|
||||
to modify than just writing a few characters.
|
||||
|
||||
With the misunderstanding cleared up, the question that remains is what are the practical
|
||||
uses for Visual Scripting.
|
||||
|
||||
The most common use cases are as follows:
|
||||
|
||||
* Game development beginners who want to learn an engine but have no programming experience yet.
|
||||
* Artists and Game Designers who have no experience in programming and want to create quick prototypes or simple games.
|
||||
* Programmers working in a team that want to make part of the game logic available to Artists or Game Designers in order to offload some of their work.
|
||||
|
||||
These scenarios are far more common than one might think, so this is why Godot has added this feature.
|
Loading…
Add table
Add a link
Reference in a new issue