mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-05 22:09:56 +03:00
Revamping of the docs organisation for a more coherent TOC
Only the pages were moved so far and some empty ones created,
the up-to-date toctrees come in the next commit.
(cherry picked from commit b408bdb918)
This commit is contained in:
committed by
Rémi Verschelde
parent
6a730cb057
commit
5e05011eae
16
learning/editor/2d_and_3d_keybindings.rst
Normal file
16
learning/editor/2d_and_3d_keybindings.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
.. _doc_2d_and_3d_keybindings:
|
||||
|
||||
2D and 3D keybindings
|
||||
=====================
|
||||
|
||||
2D viewport
|
||||
-----------
|
||||
|
||||
.. image:: /img/keybinds_2d.png
|
||||
|
||||
3D viewport
|
||||
-----------
|
||||
|
||||
.. image:: /img/keybinds_3d.png
|
||||
|
||||
|
||||
147
learning/editor/command_line_tutorial.rst
Normal file
147
learning/editor/command_line_tutorial.rst
Normal file
@@ -0,0 +1,147 @@
|
||||
.. _doc_command_line_tutorial:
|
||||
|
||||
Command line tutorial
|
||||
=====================
|
||||
|
||||
.. highlight:: shell
|
||||
|
||||
Some developers like using the command line extensively. Godot is
|
||||
designed to be friendly to them, so here are the steps for working
|
||||
entirely from the command line. Given the engine relies on little to no
|
||||
external libraries, initialization times are pretty fast, making it
|
||||
suitable for this workflow.
|
||||
|
||||
Path
|
||||
----
|
||||
|
||||
It is recommended that your godot binary is in your PATH environment
|
||||
variable, so it can be executed easily from any place by typing
|
||||
``godot``. You can do so on Linux by placing the Godot binary in
|
||||
``/usr/local/bin`` and making sure it is called ``godot``.
|
||||
|
||||
Creating a project
|
||||
------------------
|
||||
|
||||
Creating a project from the command line is simple, just navigate the
|
||||
shell to the desired place and just make an engine.cfg file exist, even
|
||||
if empty.
|
||||
|
||||
::
|
||||
|
||||
user@host:~$ mkdir newgame
|
||||
user@host:~$ cd newgame
|
||||
user@host:~/newgame$ touch engine.cfg
|
||||
|
||||
That alone makes for an empty Godot project.
|
||||
|
||||
Running the editor
|
||||
------------------
|
||||
|
||||
Running the editor is done by executing godot with the ``-e`` flag. This
|
||||
must be done from within the project directory, or a subdirectory,
|
||||
otherwise the command is ignored and the project manager appears.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot -e
|
||||
|
||||
If a scene has been created and saved, it can be edited later by running
|
||||
the same code with that scene as argument.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot -e scene.xml
|
||||
|
||||
Erasing a scene
|
||||
---------------
|
||||
|
||||
Godot is friends with your filesystem, and will not create extra
|
||||
metadata files, simply use ``rm`` to erase a file. Make sure nothing
|
||||
references that scene, or else an error will be thrown upon opening.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ rm scene.xml
|
||||
|
||||
Running the game
|
||||
----------------
|
||||
|
||||
To run the game, simply execute Godot within the project directory or
|
||||
subdirectory.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot
|
||||
|
||||
When a specific scene needs to be tested, pass that scene to the command
|
||||
line.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot scene.xml
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Catching errors in the command line can be a difficult task because they
|
||||
just fly by. For this, a command line debugger is provided by adding
|
||||
``-d``. It works for both running the game or a simple scene.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot -d
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot -d scene.xml
|
||||
|
||||
Exporting
|
||||
---------
|
||||
|
||||
Exporting the project from the command line is also supported. This is
|
||||
specially useful for continuous integration setups. The version of Godot
|
||||
that is headless (server build, no video) is ideal for this.
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot -export "Linux X11" /var/builds/project
|
||||
user@host:~/newgame$ godot -export Android /var/builds/project.apk
|
||||
|
||||
The platform names recognized by the ``-export`` switch are the same as
|
||||
displayed in the export wizard of the editor. To get a list of supported
|
||||
platforms from the command line, just try exporting to a non-recognized
|
||||
platform and the full listing of platforms your configuration supports
|
||||
will be shown.
|
||||
|
||||
To export a debug version of the game, use the ``-export_debug`` switch
|
||||
instead of ``-export``. Their parameters and usage are the same.
|
||||
|
||||
Running a script
|
||||
----------------
|
||||
|
||||
It is possible to run a simple .gd script from the command line. This
|
||||
feature is specially useful in very large projects, for batch
|
||||
conversion of assets or custom import/export.
|
||||
|
||||
The script must inherit from SceneTree or MainLoop.
|
||||
|
||||
Here is a simple example of how it works:
|
||||
|
||||
.. code:: python
|
||||
|
||||
#sayhello.gd
|
||||
extends SceneTree
|
||||
|
||||
func _init():
|
||||
print("Hello!")
|
||||
quit()
|
||||
|
||||
And how to run it:
|
||||
|
||||
::
|
||||
|
||||
user@host:~/newgame$ godot -s sayhello.gd
|
||||
Hello!
|
||||
|
||||
If no engine.cfg exists at the path, current path is assumed to be the
|
||||
current working directory (unless ``-path`` is specified).
|
||||
0
learning/editor/debugging.rst
Normal file
0
learning/editor/debugging.rst
Normal file
0
learning/editor/ue4_to_godot.rst
Normal file
0
learning/editor/ue4_to_godot.rst
Normal file
187
learning/editor/unity_to_godot.rst
Normal file
187
learning/editor/unity_to_godot.rst
Normal file
@@ -0,0 +1,187 @@
|
||||
.. _unity3D_to_godot:
|
||||
|
||||
.. references :
|
||||
.. https://wiki.unrealengine.com/Unity3D_Developer's_Guide_to_Unreal_Engine_4
|
||||
.. https://docs.unrealengine.com/latest/INT/GettingStarted/FromUnity/
|
||||
|
||||
From Unity3D to Godot Engine
|
||||
============================
|
||||
|
||||
This guide provides an overview of Godot Engine from the viewpoint of a Unity user, and aims to help you migrate your existing Unity experience into the world of Godot.
|
||||
|
||||
Differences
|
||||
-----------
|
||||
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
| | Unity | Godot |
|
||||
+===================+===================================================================================+================================================================================================================+
|
||||
| License | Proprietary, closed, free license with revenue caps and usage restrictions | MIT License, free and fully open souce without any restriction |
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
| OS (editor) | Windows, OSX, Linux (unofficial and unsupported) | Windows, X11 (Linux, *BSD), Haiku, OSX |
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
| OS (export) | | Desktop: Windows, Linux/SteamOS, OSX | | Desktop: Windows, X11, OSX |
|
||||
| | | Mobile: Android, iOS, Windows Phone, Tizen, | | Mobile: Android, iOS, Blackberry (deprecated) |
|
||||
| | | Web: WebGL | | Web: WebGL, HTML5 (via emscripten, broken) |
|
||||
| | | Consoles: PS4, PS Vita, XBox One, XBox 360, WiiU, 3DS | |
|
||||
| | | VR: Occulus Rift, SteamVR, Google Cardboard, Playstation VR, Gear VR, HoloLens | |
|
||||
| | | TV: AndroidTV, Samsung SMARTTV, tvOS | |
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
| Scene system | | Component/Scene (GameObject > Component) | Scene tree and nodes, allowing scenes to be nested and/or inherit other scenes |
|
||||
| | | Prefabs | |
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
| Third-party tools | Visual Studio or SharpEditor | | Android SDK for Android export |
|
||||
| | | | External editors are possible |
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
| Killer features | | Huge community | | Scene System |
|
||||
| | | Large assets store | | Animation Pipeline |
|
||||
| | | | Easy to write Shaders |
|
||||
| | | | Debug on Device |
|
||||
| | | |
|
||||
| | | |
|
||||
+-------------------+-----------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
, , ,
|
||||
|
||||
The editor
|
||||
----------
|
||||
|
||||
Godot Engine provides a rich-featured editor that allows you to build your games. The pictures below display both editors with colored blocks to indicate common functionalities.
|
||||
|
||||
.. image:: /img/unity-gui-overlay.png
|
||||
.. image:: /img/godot-gui-overlay.png
|
||||
|
||||
|
||||
Note that Godot editor allows you to dock each panel at the side of the scene editor you wish.
|
||||
|
||||
While both editors may seem similar, there are many differences below the surface. Both let you organize the project using the filesystem, but Godot approach is simpler, with a single configuration file, minimalist text format, and no metadata. All this contributes to Godot being much friendlier to VCS systems such as Git, Subversion or Mercurial.
|
||||
|
||||
Godot's Scene panel is similar to Unity's Hierarchy panel but, as each node has a specific function, the approach used by Godot is more visually descriptive. In other words, it's easier to understand what a specific scene does at a glance.
|
||||
|
||||
The Inspector in Godot is more minimalist and designed to only show properties. Thanks to this, objects can export a much larger amount of useful parameters to the user, without having to hide functionality in language APIs. As a plus, Godot allows animating any of those properties visually, so changing colors, textures, enumerations or even links to resources in real-time is possible without involving code.
|
||||
|
||||
Finally, the Toolbar at the top of the screen is similar in the sense that it allows controlling the project playback, but projects in Godot run in a separate window, as they don't execute inside the editor (but the tree and objects can still be explored in the debugger window).
|
||||
|
||||
This approach has the disadvantage that the running game can't be explored from different angles (though this may be supported in the future, and displaying collision gizmos in the running game is already possible), but in exchange has several advantages:
|
||||
|
||||
- Running the project and closing it is very fast (Unity has to save, run the project, close the project and then reload the previous state).
|
||||
- Live editing is a lot more useful, because changes done to the editor take effect immediately in the game, and are not lost (nor have to be synced) when the game is closed. This allows fantastic workflows, like creating levels while you play them.
|
||||
- The editor is more stable, because the game runs in a separate process.
|
||||
|
||||
Finally, the top toolbar includes a menu for remote debugging. These options make it simple to deploy to a device (connected phone, tablet or browser via HTML5), and debug/live edit on it after the game was exported.
|
||||
|
||||
The scene system
|
||||
----------------
|
||||
|
||||
This is the most important difference between Unity and Godot, and actually the favourite feature of most Godot users.
|
||||
|
||||
Unity's scene system consist in embedding all the required assets in a scene, and link them together by setting components and scripts to them.
|
||||
|
||||
Godot's scene system is different: it actually consists in a tree made of nodes. Each node serves a purpose: Sprite, Mesh, Light... Basically, this is similar to Unity scene system. However, each node can have multiple children, which make each a subscene of the main scene. This means you can compose a whole scene with different scenes, stored in different files.
|
||||
|
||||
For example, think of a platformer level. You would compose it with multiple elements:
|
||||
|
||||
- Bricks
|
||||
- Coins
|
||||
- The player
|
||||
- The enemies
|
||||
|
||||
|
||||
In Unity, you would put all the GameObjects in the scene: the player, multiple instances of enemies, bricks everywhere to form the ground of the level, and multiple instances of coins all over the level. You would then add various components to each element to link them and add logic in the level: for example, you'd add a BoxCollider2D to all the elements of the scene so that they can collide. This principle is different in Godot.
|
||||
|
||||
In Godot, you would split your whole scene into 3 separate, smaller scenes, which you would then instance in the main scene.
|
||||
|
||||
1. First, a scene for the Player alone.
|
||||
|
||||
Consider the player as a reusable element in other levels. It is composed of one node in particular: an AnimatedSprite node, which contains the sprite textures to form various animations (for example, walking animation)
|
||||
|
||||
2. Second, a scene for the Enemy.
|
||||
|
||||
There again, an enemy is a reusable element in other levels. It is almost the same as the Player node - the only differences are the script (that manages IA, mostly) and sprite textures used by the AnimatedSprite.
|
||||
|
||||
3. Lastly, the Level scene.
|
||||
|
||||
It is composed of Bricks (for platforms), Coins (for the player to grab) and a certain number of instances of the previous Enemy scene. These will be different, separate enemies, whose behaviour and appearance will be the same as defined in the Enemy scene. Each instance is then considered as a node in the Level scene tree. Of course, you can set different properties for each enemy node (to change its color for example).
|
||||
|
||||
Finally, the main scene would then be composed of one root node with 2 children: a Player instance node, and a Level instance node.
|
||||
The root node can be anything, generally a "root" type such as "Node" which is the most global type, or "Node2D" (root type of all 2D-related nodes), "Spatial" (root type of all 3D-related nodes) or "Control" (root type of all GUI-related nodes).
|
||||
|
||||
|
||||
As you can see, every scene is organized as a tree. The same goes for nodes' properties: you don't *add* a collision component to a node to make it collidable like Unity does. Instead, you make this node a *child* of a new specific node that has collision properties. Godot features various collision types nodes, depending of the use (see the `Physics introduction <../tutorials/2d/physics_introduction>`_).
|
||||
|
||||
- Question: What are the advantages of this system? Wouldn't this system potentially increase the depth of the scene tree? Besides, Unity allows organizing GameObjects by putting them in empty GameObjects.
|
||||
|
||||
- First, this system is closer to the well-known Object-Oriented paradigm: Godot provides a number of nodes which are not clearly "Game Objects", but they provide their children with their own capabilities: this is inheritance.
|
||||
- Second, it allows the extraction a subtree of scene to make it a scene of its own, which answers to the second and third questions: even if a scene tree gets too deep, it can be split into smaller subtrees. This also allows a better solution for reusability, as you can include any subtree as a child of any node. Putting multiple nodes in an empty GameObject in Unity does not provide the same possibility, apart from a visual organization.
|
||||
|
||||
|
||||
These are the most important concepts you need to remind: "node", "parent node" and "child node".
|
||||
|
||||
|
||||
Project organization
|
||||
--------------------
|
||||
|
||||
.. image:: /img/unity-project-organization-example.png
|
||||
|
||||
We previously observed that there is no perfect solution to set a project architecture. Any solution will work for Unity and Godot, so this point has a lesser importance.
|
||||
|
||||
However, we often observe a common architecture for Unity projects, which consists in having one Assets folder in the root directory, that contains various folders, one per type of asset: Audio, Graphics, Models, Materials, Scripts, Scenes, etc.
|
||||
|
||||
As described before, Godot scene system allows splitting scenes in smaller scenes. Since each scene and subscene is actually one scene file in the project, we recommend organizing your project a bit differently. This wiki provides a page for this: `Project Organization <engine/project_organization.html>`_.
|
||||
|
||||
|
||||
Where are my prefabs?
|
||||
---------------------
|
||||
|
||||
The concept of prefabs as provided by Unity is a 'template' element of the scene. It is reusable, and each instance of the prefab that exists in the scene has an existence of its own, but all of them have the same properties as defined by the prefab.
|
||||
|
||||
Godot does not provide prefabs as such, but this functionality is here again filled thanks to its scene system: as we saw the scene system is organized as a tree. Godot allows you to save a subtree of a scene as its own scene, thus saved in its own file. This new scene can then be instanced as many times as you want. Any change you make to this new, separate scene will be applied to its instances. However, any change you make to the instance will not have any impact on the 'template' scene.
|
||||
|
||||
.. image:: /img/save-branch-as-scene.png
|
||||
|
||||
To be precise, you can modify the parameters of the instance in the Inspector panel. However, the nodes that compose this instance are locked and you can unlock them if you need to by clicking the clapperboard icon next to the instance in the Scene tree, and select "Editable children" in the menu. You don't need to do this to add new children nodes to this node, but remember that these new children will belong to the instance, not the 'template' scene. If you want to add new children to all the instances of your 'template' scene, then you need to add it once in the 'template' scene.
|
||||
|
||||
.. image:: /img/editable-children.png
|
||||
|
||||
Glossary correspondance
|
||||
-----------------------
|
||||
|
||||
GameObject -> Node
|
||||
Add a component -> Inheriting
|
||||
Prefab -> Externalized branch
|
||||
|
||||
|
||||
Scripting : From C# to GDScript
|
||||
-------------------------------
|
||||
|
||||
Design
|
||||
^^^^^^
|
||||
|
||||
As you may know already, Unity supports 2 scripting languages for its API: C# and Javascript (called UnityScript). Both languages can be used in the same project (but not in the same file, of course). Choosing one instead of the other is a matter of personal taste, as performances seem not to be affected that much by the use of Javascript as long as the project remains small. C# benefits from its integration with Visual Studio and other specific features, such as static typing.
|
||||
|
||||
Godot provides its own scripting language: GDScript. This language borrows its syntax to Python, but is not related to it. If you wonder about why GDScript and not Lua, C# or any other, please read `GDScript <gdscript>`_ and `FAQ <faq>`_ pages. GDScript is strongly attached to Godot API, but it is really easy to learn: between 1 evening for an experimented programmer and 1 week for a complete beginner.
|
||||
|
||||
Unity allows you to attach as many scripts as you want to a GameObject. Each script adds a behaviour to the GameObject: for example, you can attach a script so that it reacts to the player's controls, and another that controls its specific game logic.
|
||||
|
||||
In Godot, you can only attach one script per node. You can use either an external GDScript file, or include it directly in the node. If you need to attach more scripts to one node, then you may consider 2 solutions, depending on your scene and on what you want to achieve:
|
||||
|
||||
- either add a new node between your target node and its current parent, then add a script to this new node.
|
||||
- or, your can split your target node into multiple children and attach one script to each of them.
|
||||
|
||||
As you can see, it can be easy to turn a scene tree to a mess. This is why it is important to have a real reflection, and consider splitting a complicated scene into multiple, smaller branches.
|
||||
|
||||
Connections : groups and signals
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can control nodes by accessing them using a script, and call functions (built-in or user-defined) on them. But there's more: you can also place them in a group and call a function on all nodes contained it this group! This is explained in `this page <../tutorials/step_by_step/scripting_continued#groups>`_.
|
||||
|
||||
But there's more! Certain nodes throw signals when certain actions happen. You can connect these signals to call a specific function when they happen. Note that you can define your own signals and send them whenever you want. This feature is documented `here <gdscript.html#signals>`_.
|
||||
|
||||
|
||||
|
||||
Using Godot in C++
|
||||
------------------
|
||||
|
||||
Just for your information, Godot also allows you to develop your project directly in C++ by using its API, which is not possible with Unity at the moment. As an example, you can consider Godot Engine's editor as a "game" written in C++ using Godot API.
|
||||
|
||||
If you are interested in using Godot in C++, you may want to start reading the `Developing in C++ <_developing.html>`_ page.
|
||||
|
||||
99
learning/features/2d/2d_transforms.rst
Normal file
99
learning/features/2d/2d_transforms.rst
Normal file
@@ -0,0 +1,99 @@
|
||||
.. _doc_viewport_and_canvas_transforms:
|
||||
|
||||
Viewport and canvas transforms
|
||||
==============================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial is created after a topic that is a little dark for most
|
||||
users, and explains all the 2D transforms going on for nodes from the
|
||||
moment they draw their content locally to the time they are drawn into
|
||||
the screen.
|
||||
|
||||
Canvas transform
|
||||
----------------
|
||||
|
||||
As mentioned in the previous tutorial, :ref:`doc_canvas_layers`, every
|
||||
CanvasItem node (remember that Node2D and Control based nodes use
|
||||
CanvasItem as their common root) will reside in a *Canvas Layer*. Every
|
||||
canvas layer has a transform (translation, rotation, scale, etc.) that
|
||||
can be accessed as a :ref:`Matrix32 <class_Matrix32>`.
|
||||
|
||||
Also covered in the previous tutorial, nodes are drawn by default in Layer 0,
|
||||
in the built-in canvas. To put nodes in a different layer, a :ref:`CanvasLayer
|
||||
<class_CanvasLayer>` node can be used.
|
||||
|
||||
Global canvas transform
|
||||
-----------------------
|
||||
|
||||
Viewports also have a Global Canvas transform (also a
|
||||
:ref:`Matrix32 <class_Matrix32>`). This is the master transform and
|
||||
affects all individual *Canvas Layer* transforms. Generally this
|
||||
transform is not of much use, but is used in the CanvasItem Editor
|
||||
in Godot's editor.
|
||||
|
||||
Stretch transform
|
||||
-----------------
|
||||
|
||||
Finally, viewports have a *Stretch Transform*, which is used when
|
||||
resizing or stretching the screen. This transform is used internally (as
|
||||
described in :ref:`doc_multiple_resolutions`), but can also be manually set
|
||||
on each viewport.
|
||||
|
||||
Input events received in the :ref:`MainLoop._input_event() <class_MainLoop__input_event>`
|
||||
callback are multiplied by this transform, but lack the ones above. To
|
||||
convert InputEvent coordinates to local CanvasItem coordinates, the
|
||||
:ref:`CanvasItem.make_input_local() <class_CanvasItem_make_input_local>`
|
||||
function was added for convenience.
|
||||
|
||||
Transform order
|
||||
---------------
|
||||
|
||||
For a coordinate in CanvasItem local properties to become an actual
|
||||
screen coordinate, the following chain of transforms must be applied:
|
||||
|
||||
.. image:: /img/viewport_transforms2.png
|
||||
|
||||
Transform functions
|
||||
-------------------
|
||||
|
||||
Obtaining each transform can be achieved with the following functions:
|
||||
|
||||
+----------------------------------+--------------------------------------------------------------------------------------+
|
||||
| Type | Transform |
|
||||
+==================================+======================================================================================+
|
||||
| CanvasItem | :ref:`CanvasItem.get_global_transform() <class_CanvasItem_get_global_transform>` |
|
||||
+----------------------------------+--------------------------------------------------------------------------------------+
|
||||
| CanvasLayer | :ref:`CanvasItem.get_canvas_transform() <class_CanvasItem_get_canvas_transform>` |
|
||||
+----------------------------------+--------------------------------------------------------------------------------------+
|
||||
| CanvasLayer+GlobalCanvas+Stretch | :ref:`CanvasItem.get_viewport_transform() <class_CanvasItem_get_viewport_transform>` |
|
||||
+----------------------------------+--------------------------------------------------------------------------------------+
|
||||
|
||||
Finally then, to convert a CanvasItem local coordinates to screen
|
||||
coordinates, just multiply in the following order:
|
||||
|
||||
::
|
||||
|
||||
var screen_coord = get_viewport_transform() * ( get_global_transform() * local_pos )
|
||||
|
||||
Keep in mind, however, that it is generally not desired to work with
|
||||
screen coordinates. The recommended approach is to simply work in Canvas
|
||||
coordinates (``CanvasItem.get_global_transform()``), to allow automatic
|
||||
screen resolution resizing to work properly.
|
||||
|
||||
Feeding custom input events
|
||||
---------------------------
|
||||
|
||||
It is often desired to feed custom input events to the scene tree. With
|
||||
the above knowledge, to correctly do this, it must be done the following
|
||||
way:
|
||||
|
||||
::
|
||||
|
||||
var local_pos = Vector2(10,20) # local to Control/Node2D
|
||||
var ie = InputEvent()
|
||||
ie.type = InputEvent.MOUSE_BUTTON
|
||||
ie.button_index = BUTTON_LEFT
|
||||
ie.pos = get_viewport_transform() * (get_global_transform() * local_pos)
|
||||
get_tree().input_event(ie)
|
||||
76
learning/features/2d/canvas_layers.rst
Normal file
76
learning/features/2d/canvas_layers.rst
Normal file
@@ -0,0 +1,76 @@
|
||||
.. _doc_canvas_layers:
|
||||
|
||||
Canvas layers
|
||||
=============
|
||||
|
||||
Viewport and Canvas items
|
||||
-------------------------
|
||||
|
||||
Regular 2D nodes, such as :ref:`Node2D <class_Node2D>` or
|
||||
:ref:`Control <class_Control>` both inherit from
|
||||
:ref:`CanvasItem <class_CanvasItem>`, which is the base for all 2D
|
||||
nodes. CanvasItems can be arranged in trees and they will inherit
|
||||
their transform. This means that when moving the parent, the children
|
||||
will be moved too.
|
||||
|
||||
These nodes are placed as direct or indirect children to a
|
||||
:ref:`Viewport <class_Viewport>`, and will be displayed through it.
|
||||
|
||||
Viewport has a property "canvas_transform"
|
||||
:ref:`Viewport.set_canvas_transform() <class_Viewport_set_canvas_transform>`,
|
||||
which allows to transform all the CanvasItem hierarchy by a custom
|
||||
:ref:`Matrix32 <class_Matrix32>` transform. Nodes such as
|
||||
:ref:`Camera2D <class_Camera2D>`, work by changing that transform.
|
||||
|
||||
Changing the canvas transform is useful because it is a lot more
|
||||
efficient than moving the root canvas item (and hence the whole scene).
|
||||
Canvas transform is a simple matrix that offsets the whole 2D drawing,
|
||||
so it's the most efficient way to do scrolling.
|
||||
|
||||
Not enough...
|
||||
-------------
|
||||
|
||||
But this is not enough. There are often situations where the game or
|
||||
application may not want *everything* transformed by the canvas
|
||||
transform. Examples of this are:
|
||||
|
||||
- **Parallax Backgrounds**: Backgrounds that move slower than the rest
|
||||
of the stage.
|
||||
- **HUD**: Head's up display, or user interface. If the world moves,
|
||||
the life counter, score, etc. must stay static.
|
||||
- **Transitions**: Effects used for transitions (fades, blends) may
|
||||
also want it to remain at a fixed location.
|
||||
|
||||
How can these problems be solved in a single scene tree?
|
||||
|
||||
CanvasLayers
|
||||
------------
|
||||
|
||||
The answer is :ref:`CanvasLayer <class_CanvasLayer>`,
|
||||
which is a node that adds a separate 2D rendering layer for all its
|
||||
children and grand-children. Viewport children will draw by default at
|
||||
layer "0", while a CanvasLayer will draw at any numeric layer. Layers
|
||||
with a greater number will be drawn above those with a smaller number.
|
||||
CanvasLayers also have their own transform, and do not depend of the
|
||||
transform of other layers. This allows the UI to be fixed in-place,
|
||||
while the world moves.
|
||||
|
||||
An example of this is creating a parallax background. This can be done
|
||||
with a CanvasLayer at layer "-1". The screen with the points, life
|
||||
counter and pause button can also be created at layer "1".
|
||||
|
||||
Here's a diagram of how it looks:
|
||||
|
||||
.. image:: /img/canvaslayers.png
|
||||
|
||||
CanvasLayers are independent of tree order, and they only depend on
|
||||
their layer number, so they can be instantiated when needed.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
Even though there shouldn't be any performance limitation, it is not
|
||||
advised to use excessive amount of layers to arrange drawing order of
|
||||
nodes. The most optimal way will always be arranging them by tree order.
|
||||
2d nodes also have a property for controlling their drawing order
|
||||
(see :ref:`Node2D.set_z() <class_Node2D_set_z>`).
|
||||
256
learning/features/2d/custom_drawing_in_2d.rst
Normal file
256
learning/features/2d/custom_drawing_in_2d.rst
Normal file
@@ -0,0 +1,256 @@
|
||||
.. _doc_custom_drawing_in_2d:
|
||||
|
||||
Custom drawing in 2D
|
||||
====================
|
||||
|
||||
Why?
|
||||
----
|
||||
|
||||
Godot has nodes to draw sprites, polygons, particles, and all sorts of
|
||||
stuff. For most cases this is enough, but not always. Before crying in fear,
|
||||
angst, and rage because a node to draw that-specific-something does not exist... it would
|
||||
be good to know that it is possible to easily make any 2D node (be it
|
||||
:ref:`Control <class_Control>` or :ref:`Node2D <class_Node2D>`
|
||||
based) draw custom commands. It is *really* easy to do it too.
|
||||
|
||||
But...
|
||||
------
|
||||
|
||||
Custom drawing manually in a node is *really* useful. Here are some
|
||||
examples why:
|
||||
|
||||
- Drawing shapes or logic that is not handled by nodes (example: making
|
||||
a node that draws a circle, an image with trails, a special kind of
|
||||
animated polygon, etc).
|
||||
- Visualizations that are not that compatible with nodes: (example: a
|
||||
tetris board). The tetris example uses a custom draw function to draw
|
||||
the blocks.
|
||||
- Managing drawing logic of a large amount of simple objects (in the
|
||||
hundreds of thousands). Using a thousand nodes is probably not nearly
|
||||
as efficient as drawing, but a thousand of draw calls are cheap.
|
||||
Check the "Shower of Bullets" demo as example.
|
||||
- Making a custom UI control. There are plenty of controls available,
|
||||
but it's easy to run into the need to make a new, custom one.
|
||||
|
||||
OK, how?
|
||||
--------
|
||||
|
||||
Add a script to any :ref:`CanvasItem <class_CanvasItem>`
|
||||
derived node, like :ref:`Control <class_Control>` or
|
||||
:ref:`Node2D <class_Node2D>`. Override the _draw() function.
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
func _draw():
|
||||
#your draw commands here
|
||||
pass
|
||||
|
||||
Draw commands are described in the :ref:`CanvasItem <class_CanvasItem>`
|
||||
class reference. There are plenty of them.
|
||||
|
||||
Updating
|
||||
--------
|
||||
|
||||
The _draw() function is only called once, and then the draw commands
|
||||
are cached and remembered, so further calls are unnecessary.
|
||||
|
||||
If re-drawing is required because a state or something else changed,
|
||||
simply call :ref:`CanvasItem.update() <class_CanvasItem_update>`
|
||||
in that same node and a new _draw() call will happen.
|
||||
|
||||
Here is a little more complex example. A texture variable that will be
|
||||
redrawn if modified:
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
export var texture setget _set_texture
|
||||
|
||||
func _set_texture(value):
|
||||
#if the texture variable is modified externally,
|
||||
#this callback is called.
|
||||
texture=value #texture was changed
|
||||
update() #update the node
|
||||
|
||||
func _draw():
|
||||
draw_texture(texture,Vector2())
|
||||
|
||||
In some cases, it may be desired to draw every frame. For this, just
|
||||
call update() from the _process() callback, like this:
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
func _draw():
|
||||
#your draw commands here
|
||||
pass
|
||||
|
||||
func _process(delta):
|
||||
update()
|
||||
|
||||
func _ready():
|
||||
set_process(true)
|
||||
|
||||
An example: drawing circular arcs
|
||||
----------------------------------
|
||||
|
||||
We will now use the custom drawing functionality of the Godot Engine to draw something that Godot doesn't provide functions for. As an example, Godot provides a draw_circle() function that draws a whole circle. However, what about drawing a portion of a circle? You will have to code a function to perform this, and draw it yourself.
|
||||
|
||||
Arc function
|
||||
^^^^^^^^^^^^
|
||||
|
||||
|
||||
An arc is defined by its support circle parameters, that is: the center position, and the radius. And the arc itself is then defined by the angle it starts from, and the angle it stops at. These are the 4 parameters we have to provide to our drawing. We'll also provide the color value so we can draw the arc in different colors if we wish.
|
||||
|
||||
Basically, drawing a shape on screen requires it to be decomposed into a certain number of points linked one to the following one. As you can imagine, the more points your shape is made of, the smoother it will appear, but the heavier it will be in terms of processing cost. In general, if your shape is huge (or in 3D, close to the camera), it will require more points to be drawn without showing angular-looking. On the contrary, if you shape is small (or in 3D, far from the camera), you may reduce its number of points to save processing costs. This is called *Level of Detail (LoD)*. In our example, we will simply use a fixed number of points, no matter the radius.
|
||||
|
||||
::
|
||||
|
||||
func draw_circle_arc( center, radius, angle_from, angle_to, color ):
|
||||
var nb_points = 32
|
||||
var points_arc = Vector2Array()
|
||||
|
||||
for i in range(nb_points+1):
|
||||
var angle_point = angle_from + i*(angle_to-angle_from)/nb_points - 90
|
||||
var point = center + Vector2( cos(deg2rad(angle_point)), sin(deg2rad(angle_point)) ) * radius
|
||||
points_arc.push_back( point )
|
||||
|
||||
for indexPoint in range(nb_points):
|
||||
draw_line(points_arc[indexPoint], points_arc[indexPoint+1], color)
|
||||
|
||||
Remember the number of points our shape has to be decomposed into? We fixed this number in the nb_points variable to a value of 32. Then, we initialize an empty Vector2Array, which is simply an array of Vector2.
|
||||
|
||||
Next step consists in computing the actual positions of these 32 points that compose arc. This is done in the first for-loop: we iterate over the number of points we want to compute the positions, plus one to include the last point. We first determine the angle of each point, between the starting and ending angles.
|
||||
|
||||
The reason why each angle is reduced of 90° is that we will compute 2D positions out of each angle using trigonometry (you know, cosine and sine stuff...). However, to be simple, cos() and sin() use radians, not degrees. The angle of 0° (0 radian) starts at 3 o'clock, although we want to start counting at 0 o'clock. So, we just reduce each angle of 90° in order to start counting from 0'clock.
|
||||
|
||||
The actual position of a point located on a circle at angle 'angle' (in radians) is given by Vector2(cos(angle), sin(angle)). Since cos() and sin() return values between -1 and 1, the position is located on a circle of radius 1. To have this position on our support circle, which has a radius of 'radius', we simply need to multiply the position by 'radius'. Finally, we need to position our support circle at the 'center' position, which is performed by adding it to our Vector2 value. Finally, we insert the point in the Vector2Array which was previously defined.
|
||||
|
||||
Now, we need to actually draw our points. As you can imagine, we will not simply draw our 32 points: we need to draw everything that is between each of them. We could have computed every point ourselves using the previous method, and draw it one by one, but this it too complicated and inefficient (except if explicitly needed). So, we simply draw lines between each pair of points. Unless the radius of our support circle is very big, the length of each line between a pair of points will never be long enough to see them. If this happens, we simply would need to increase the number of points.
|
||||
|
||||
Draw the arc on screen
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
We now have a function that draws stuff on screen: it is time to call it in the _draw() function.
|
||||
|
||||
::
|
||||
|
||||
func _draw():
|
||||
var center = Vector2(200,200)
|
||||
var radius = 80
|
||||
var angle_from = 75
|
||||
var angle_to = 195
|
||||
var color = Color(1.0, 0.0, 0.0)
|
||||
draw_circle_arc( center, radius, angle_from, angle_to, color )
|
||||
|
||||
Result:
|
||||
|
||||
.. image:: /img/result_drawarc.png
|
||||
|
||||
|
||||
|
||||
Arc polygon function
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
We can take this a step further and write a function that draws the plain portion of the disc defined by the arc, not only its shape. The method is exactly the same a previously, except that we draw a polygon instead of lines:
|
||||
|
||||
::
|
||||
|
||||
func draw_circle_arc_poly( center, radius, angle_from, angle_to, color ):
|
||||
var nb_points = 32
|
||||
var points_arc = Vector2Array()
|
||||
points_arc.push_back(center)
|
||||
var colors = ColorArray([color])
|
||||
|
||||
for i in range(nb_points+1):
|
||||
var angle_point = angle_from + i*(angle_to-angle_from)/nb_points - 90
|
||||
points_arc.push_back(center + Vector2( cos( deg2rad(angle_point) ), sin( deg2rad(angle_point) ) ) * radius)
|
||||
draw_polygon(points_arc, colors)
|
||||
|
||||
|
||||
.. image:: /img/result_drawarc_poly.png
|
||||
|
||||
Dynamic custom drawing
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Alright, we are now able to draw custom stuff on screen. However, it is very static: let's make this shape turn around the center. The solution to do this is simply to change the angle_from and angle_to values over time. For our example, we will simply increment them by 50. This increment value has to remain constant, else the rotation speed will change accordingly.
|
||||
|
||||
First, we have to make both angle_from and angle_to variables global at the top of our script. Also note that you can store them in other nodes and access them using get_node().
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
var rotation_ang = 50
|
||||
var angle_from = 75
|
||||
var angle_to = 195
|
||||
|
||||
|
||||
|
||||
We make these values change in the _process(delta) function. To activate this function, we need to call set_process(true) in the _ready() function.
|
||||
|
||||
We also increment our angle_from and angle_to values here. However, we must not forget to wrap() the resulting values between 0 and 360°! That is, if the angle is 361°, then it is actually 1°. If you don't wrap these values, the script will work correctly but angles values will grow bigger and bigger over time, until they reach the maximum integer value Godot can manage (2^31 - 1). When this happens, Godot may crash or produce unexpected behavior. Since Godot doesn't provide a wrap() function, we'll create it here, as it is relatively simple.
|
||||
|
||||
Finally, we must not forget to call the update() function, which automatically calls _draw(). This way, you can control when you want to refresh the frame.
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
set_process(true)
|
||||
|
||||
func wrap(value, min_val, max_val):
|
||||
var f1 = value - min_val
|
||||
var f2 = max_val - min_val
|
||||
return fmod(f1, f2) + min_val
|
||||
|
||||
func _process(delta):
|
||||
angle_from += rotation_ang
|
||||
angle_to += rotation_ang
|
||||
|
||||
# we only wrap angles if both of them are bigger than 360
|
||||
if (angle_from > 360 && angle_to > 360):
|
||||
angle_from = wrap(angle_from, 0, 360)
|
||||
angle_to = wrap(angle_to, 0, 360)
|
||||
update()
|
||||
|
||||
Also, don't forget to modify the _draw() function to make use of these variables:
|
||||
::
|
||||
|
||||
func _draw():
|
||||
var center = Vector2(200,200)
|
||||
var radius = 80
|
||||
var color = Color(1.0, 0.0, 0.0)
|
||||
|
||||
draw_circle_arc( center, radius, angle_from, angle_to, color )
|
||||
|
||||
Let's run!
|
||||
It works, but the arc is rotating insanely fast! What's wrong?
|
||||
|
||||
The reason is that your GPU is actually displaying the frames as fast as he can. We need to "normalize" the drawing by this speed. To achieve, we have to make use of the 'delta' parameter of the _process() function. 'delta' contains the time elapsed between the two last rendered frames. It is generally small (about 0.0003 seconds, but this depends on your hardware). So, using 'delta' to control your drawing ensures your program to run at the same speed on every hardware.
|
||||
|
||||
In our case, we simply need to multiply our 'rotation_ang' variable by 'delta' in the _process() function. This way, our 2 angles will be increased by a much smaller value, which directly depends on the rendering speed.
|
||||
|
||||
::
|
||||
|
||||
func _process(delta):
|
||||
angle_from += rotation_ang * delta
|
||||
angle_to += rotation_ang * delta
|
||||
|
||||
# we only wrap angles if both of them are bigger than 360
|
||||
if (angle_from > 360 && angle_to > 360):
|
||||
angle_from = wrap(angle_from, 0, 360)
|
||||
angle_to = wrap(angle_to, 0, 360)
|
||||
update()
|
||||
|
||||
Let's run again! This time, the rotation displays fine!
|
||||
|
||||
Tools
|
||||
-----
|
||||
|
||||
Drawing your own nodes might also be desired while running them in the
|
||||
editor, to use as preview or visualization of some feature or
|
||||
behavior.
|
||||
|
||||
Remember to just use the "tool" keyword at the top of the script
|
||||
(check the :ref:`doc_gdscript` reference if you forgot what this does).
|
||||
260
learning/features/2d/particle_systems_2d.rst
Normal file
260
learning/features/2d/particle_systems_2d.rst
Normal file
@@ -0,0 +1,260 @@
|
||||
.. _doc_particle_systems_2d:
|
||||
|
||||
Particle Systems (2D)
|
||||
=====================
|
||||
|
||||
Intro
|
||||
-----
|
||||
|
||||
A simple (but flexible enough for most uses) particle system is
|
||||
provided. Particle systems are used to simulate complex physical effects
|
||||
such as sparks, fire, magic particles, smoke, mist, magic, etc.
|
||||
|
||||
The idea is that a "particle" is emitted at a fixed interval and with a
|
||||
fixed lifetime. During his lifetime, every particle will have the same
|
||||
base behavior. What makes every particle different and provides a more
|
||||
organic look is the "randomness" associated to each parameter. In
|
||||
essence, creating a particle system means setting base physics
|
||||
parameters and then adding randomness to them.
|
||||
|
||||
Particles2D
|
||||
~~~~~~~~~~~
|
||||
|
||||
Particle systems are added to the scene via the
|
||||
:ref:`Particles2D <class_Particles2D>`
|
||||
node. They are enabled by default and start emitting white points
|
||||
downwards (as affected by the gravity). This provides a reasonable
|
||||
starting point to start adapting it to our needs.
|
||||
|
||||
.. image:: /img/particles1.png
|
||||
|
||||
Texture
|
||||
~~~~~~~
|
||||
|
||||
A particle system uses a single texture (in the future this might be
|
||||
extended to animated textures via spritesheet). The texture is set via
|
||||
the relevant texture property:
|
||||
|
||||
.. image:: /img/particles2.png
|
||||
|
||||
Physics variables
|
||||
-----------------
|
||||
|
||||
Before taking a look at the global parameters for the particle system,
|
||||
let's first see what happens when the physics variables are tweaked.
|
||||
|
||||
Direction
|
||||
---------
|
||||
|
||||
This is the base angle at which particles emit. Default is 0 (down):
|
||||
|
||||
.. image:: /img/paranim1.gif
|
||||
|
||||
Changing it will change the emissor direction, but gravity will still
|
||||
affect them:
|
||||
|
||||
.. image:: /img/paranim2.gif
|
||||
|
||||
This parameter is useful because, by rotating the node, gravity will
|
||||
also be rotated. Changing direction keeps them separate.
|
||||
|
||||
Spread
|
||||
------
|
||||
|
||||
Spread is the angle at which particles will randomly be emitted.
|
||||
Increasing the spread will increase the angle. A spread of 180 will emit
|
||||
in all directions.
|
||||
|
||||
.. image:: /img/paranim3.gif
|
||||
|
||||
Linear velocity
|
||||
---------------
|
||||
|
||||
Linear velocity is the speed at which particles will be emitted (in
|
||||
pixels/sec). Speed might later be modified by gravity or other
|
||||
accelerations (as described further below).
|
||||
|
||||
.. image:: /img/paranim4.gif
|
||||
|
||||
Spin velocity
|
||||
-------------
|
||||
|
||||
Spin velocity is the speed at which particles turn around their center
|
||||
(in degrees/sec).
|
||||
|
||||
.. image:: /img/paranim5.gif
|
||||
|
||||
Orbit velocity
|
||||
--------------
|
||||
|
||||
Orbit velocity is used to make particles turn around their center.
|
||||
|
||||
.. image:: /img/paranim6.gif
|
||||
|
||||
Gravity direction & strength
|
||||
----------------------------
|
||||
|
||||
Gravity can be modified as in direction and strength. Gravity affects
|
||||
every particle currently alive.
|
||||
|
||||
.. image:: /img/paranim7.gif
|
||||
|
||||
Radial acceleration
|
||||
-------------------
|
||||
|
||||
If this acceleration is positive, particles are accelerated away from
|
||||
the center. If negative, they are absorbed towards it.
|
||||
|
||||
.. image:: /img/paranim8.gif
|
||||
|
||||
Tangential acceleration
|
||||
-----------------------
|
||||
|
||||
This acceleration will use the tangent vector to the center. Combining
|
||||
with radial acceleration can do nice effects.
|
||||
|
||||
.. image:: /img/paranim9.gif
|
||||
|
||||
Damping
|
||||
-------
|
||||
|
||||
Damping applies friction to the particles, forcing them to stop. It is
|
||||
specially useful for sparks or explosions, which usually begin with a
|
||||
high linear velocity and then stop as they fade.
|
||||
|
||||
.. image:: /img/paranim10.gif
|
||||
|
||||
Initial angle
|
||||
-------------
|
||||
|
||||
Determines the initial angle of the particle (in degress). This parameter
|
||||
is mostly useful randomized.
|
||||
|
||||
.. image:: /img/paranim11.gif
|
||||
|
||||
Initial & final size
|
||||
--------------------
|
||||
|
||||
Determines the initial and final scales of the particle.
|
||||
|
||||
.. image:: /img/paranim12.gif
|
||||
|
||||
Color phases
|
||||
------------
|
||||
|
||||
Particles can use up to 4 color phases. Each color phase can include
|
||||
transparency.
|
||||
|
||||
Phases must provide an offset value from 0 to 1, and always in
|
||||
ascending order. For example, a color will begin at offset 0 and end
|
||||
in offset 1, but 4 colors might use different offsets, such as 0, 0.2,
|
||||
0.8 and 1.0 for the different phases:
|
||||
|
||||
.. image:: /img/particlecolorphases.png
|
||||
|
||||
Will result in:
|
||||
|
||||
.. image:: /img/paranim13.gif
|
||||
|
||||
Global parameters
|
||||
-----------------
|
||||
|
||||
These parameters affect the behavior of the entire system.
|
||||
|
||||
Lifetime
|
||||
--------
|
||||
|
||||
The time in seconds that every particle will stay alive. When lifetime
|
||||
ends, a new particle is created to replace it.
|
||||
|
||||
Lifetime: 0.5
|
||||
|
||||
.. image:: /img/paranim14.gif
|
||||
|
||||
Lifetime: 4.0
|
||||
|
||||
.. image:: /img/paranim15.gif
|
||||
|
||||
Timescale
|
||||
---------
|
||||
|
||||
It happens often that the effect achieved is perfect, except too fast or
|
||||
too slow. Timescale helps adjust the overall speed.
|
||||
|
||||
Timescale everything 2x:
|
||||
|
||||
.. image:: /img/paranim16.gif
|
||||
|
||||
Preprocess
|
||||
----------
|
||||
|
||||
Particle systems begin with 0 particles emitted, then start emitting.
|
||||
This can be an inconvenience when just loading a scene and systems like
|
||||
a torch, mist, etc begin emitting the moment you enter. Preprocess is
|
||||
used to let the system process a given amount of seconds before it is
|
||||
actually shown the first time.
|
||||
|
||||
Emit timeout
|
||||
------------
|
||||
|
||||
This variable will switch emission off after given amount of seconds
|
||||
being on. When zero, itś disabled.
|
||||
|
||||
Offset
|
||||
------
|
||||
|
||||
Allows to move the emission center away from the center
|
||||
|
||||
Half extents
|
||||
------------
|
||||
|
||||
Makes the center (by default 1 pixel) wider, to the size in pixels
|
||||
desired. Particles will emit randomly inside this area.
|
||||
|
||||
.. image:: /img/paranim17.gif
|
||||
|
||||
It is also possible to set an emission mask by using this value. Check
|
||||
the "Particles" menu on the 2D scene editor viewport and select your
|
||||
favorite texture. Opaque pixels will be used as potential emission
|
||||
location, while transparent ones will be ignored:
|
||||
|
||||
.. image:: /img/paranim19.gif
|
||||
|
||||
Local space
|
||||
-----------
|
||||
|
||||
By default this option is on, and it means that the space that particles
|
||||
are emitted to is contained within the node. If the node is moved, all
|
||||
particles are moved with it:
|
||||
|
||||
.. image:: /img/paranim20.gif
|
||||
|
||||
If disabled, particles will emit to global space, meaning that if the
|
||||
node is moved, the emissor is moved too:
|
||||
|
||||
.. image:: /img/paranim21.gif
|
||||
|
||||
Explosiveness
|
||||
-------------
|
||||
|
||||
If lifetime is 1 and there are 10 particles, it means every particle
|
||||
will be emitted every 0.1 seconds. The explosiveness parameter changes
|
||||
this, and forces particles to be emitted all together. Ranges are:
|
||||
|
||||
- 0: Emit all particles together.
|
||||
- 1: Emit particles at equal interval.
|
||||
|
||||
Values in the middle are also allowed. This feature is useful for
|
||||
creating explosions or sudden bursts of particles:
|
||||
|
||||
.. image:: /img/paranim18.gif
|
||||
|
||||
Randomness
|
||||
----------
|
||||
|
||||
All physics parameters can be randomized. Random variables go from 0 to
|
||||
1. the formula to randomize a parameter is:
|
||||
|
||||
::
|
||||
|
||||
initial_value = param_value + param_value*randomness
|
||||
165
learning/features/2d/using_tilemaps.rst
Normal file
165
learning/features/2d/using_tilemaps.rst
Normal file
@@ -0,0 +1,165 @@
|
||||
.. _doc_using_tilemaps:
|
||||
|
||||
Using tilemaps
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Tilemaps are a simple and quick way to make 2D game levels. Basically,
|
||||
you start with bunch of reference tiles (or pieces) that can be put in a
|
||||
grid, as many times each as desired:
|
||||
|
||||
.. image:: /img/tilemap.png
|
||||
|
||||
Collisions can also be added to the tiles, allowing for both 2D side
|
||||
scrolling and top down games.
|
||||
|
||||
Making a tileset
|
||||
----------------
|
||||
|
||||
To begin, a tileset needs to be made. Here are some tiles for it.
|
||||
They are all in the same image because artists will often prefer this.
|
||||
Having them as separate images also works.
|
||||
|
||||
.. image:: /img/tileset.png
|
||||
|
||||
Create a new project and move the above png image into the directory.
|
||||
|
||||
We will be creating a :ref:`TileSet <class_TileSet>`
|
||||
resource. While this resource exports properties, it's pretty difficult
|
||||
to get complex data into it and maintain it. Here is what it would look like to
|
||||
manually edit the resource:
|
||||
|
||||
.. image:: /img/tileset_edit_resource.png
|
||||
|
||||
There's enough properties to get by, and with some effort editing this
|
||||
way can work, but the easiest way to edit and maintain a tileset is exporting
|
||||
it from a specially-crafted scene!
|
||||
|
||||
TileSet scene
|
||||
-------------
|
||||
|
||||
Create a new scene with a regular node or node2d as root. For each tile,
|
||||
add a sprite as a child. Since tiles here are 50x50, enabling snap might be
|
||||
a good idea.
|
||||
|
||||
If more than one tile is present in the source image, make sure to use
|
||||
the region property of the sprite to adjust which part of the texture is being
|
||||
used.
|
||||
|
||||
Finally, make sure to name your sprite node correctly, this will ensure
|
||||
that, in subsequent edits to the tileset (for example, if added
|
||||
collision, changed the region, etc), the tile will still be **identified
|
||||
correctly and updated**. This name should be unique.
|
||||
|
||||
Sounds like a lot of requirements, so here's a screenshot that shows
|
||||
where everything of relevance is:
|
||||
|
||||
.. image:: /img/tile_example.png
|
||||
|
||||
Continue adding all the tiles, adjusting the offsets if needed (if you have
|
||||
multiple tiles in a single source image). Again, remember that their names must
|
||||
be unique.
|
||||
|
||||
.. image:: /img/tile_example2.png
|
||||
|
||||
Collision
|
||||
---------
|
||||
|
||||
To add collision to a tile, create a StaticBody2D child for each sprite.
|
||||
This is a static collision node. Then, as a child of the StaticBody2D,
|
||||
create a CollisionShape2D or CollisionPolygon. The latter is recommended
|
||||
because it is easier to edit:
|
||||
|
||||
.. image:: /img/tile_example3.png
|
||||
|
||||
Finally, edit the polygon, this will give the tile a collision.
|
||||
**Remember to use snap!**. Using snap will make sure collision polygons
|
||||
are aligned properly, allowing a character to walk seamlessly from tile
|
||||
to tile. Also **do not scale or move** the collision and/or collision
|
||||
polygon nodes. leave them at offset 0,0, with scale 1,1 and rotation 0
|
||||
respect to the parent sprite.
|
||||
|
||||
.. image:: /img/tile_example4.png
|
||||
|
||||
Keep adding collisions to tiles until we are done. Note that BG is just
|
||||
a background, so it should not have a collision.
|
||||
|
||||
.. image:: /img/tile_example5.png
|
||||
|
||||
OK! We're done! Remember to save this scene for future edit, call it
|
||||
"tileset_edit.scn" or something like that.
|
||||
|
||||
Exporting a TileSet
|
||||
-------------------
|
||||
|
||||
With the scene created and opened in the editor, next step will be to
|
||||
create a tileset. Use Scene > Convert To > Tile Set from the Scene Menu:
|
||||
|
||||
.. image:: /img/tileset_export.png
|
||||
|
||||
Then choose a filename, like "mytiles.res". Make sure the "Merge With
|
||||
Existing" option is toggled on. This way, every time the tileset
|
||||
resource file is overwritten, existing tiles are merged and updated
|
||||
(they are referenced by their unique name, so again, **name your tiles
|
||||
properly**).
|
||||
|
||||
.. image:: /img/tileset_merge.png
|
||||
|
||||
Using the TileSet in a TileMap
|
||||
------------------------------
|
||||
|
||||
Create a new scene, use any node or node2d as root, then create a
|
||||
:ref:`TileMap <class_TileMap>` as
|
||||
a child.
|
||||
|
||||
.. image:: /img/tilemap_scene.png
|
||||
|
||||
Go to the tileset property of this node and assign the one created in
|
||||
previous steps:
|
||||
|
||||
.. image:: /img/tileset_property.png
|
||||
|
||||
Also set the cell size to '50', since that is the size used by the
|
||||
tiles. Quadrant size is a tuning value, which means that the engine will
|
||||
draw and cull the tilemap in blocks of 16x16 tiles. This value is
|
||||
usually fine and does not need to be changed, but can be used to tune
|
||||
performance in specific cases (if you know what you are doing).
|
||||
|
||||
Painting your world
|
||||
-------------------
|
||||
|
||||
With all set, make sure the TileMap node is selected. A red grid will
|
||||
appear on screen, allowing to paint on it with the selected tile on the
|
||||
left palette.
|
||||
|
||||
.. image:: /img/tile_example6.png
|
||||
|
||||
To avoid moving and selecting the tilemap node accidentally (something
|
||||
common given it's a huge node), it is recommended that you lock it,
|
||||
using the lock button:
|
||||
|
||||
.. image:: /img/tile_lock.png
|
||||
|
||||
Offset and scaling artifacts
|
||||
----------------------------
|
||||
|
||||
When using a single texture for all the tiles, scaling the tileset (or
|
||||
even moving to a non pixel-aligned location) will most likely result in
|
||||
filtering artifacts like this:
|
||||
|
||||
.. image:: /img/tileset_filter.png
|
||||
|
||||
This can't be avoided, as it is the way the hardware bilinear filter
|
||||
works. So, to avoid this situation, there are a few workarounds, try the
|
||||
ones that look better for you:
|
||||
|
||||
- Use a single image for each tile, this will remove all artifacts but
|
||||
can be more cumbersome to implement, so try the options below first.
|
||||
- Disable filtering for either the tileset texture or the entire image
|
||||
loader (see the :ref:`doc_managing_image_files` asset pipeline tutorial).
|
||||
- Enable pixel snap (set: "Scene > Project Settings >
|
||||
Display/use_2d_pixel_snap" to true).
|
||||
- Viewport Scaling can often help with shrinking the map (see the
|
||||
:ref:`doc_viewports` tutorial).
|
||||
189
learning/features/3d/3d_performance_and_limitations.rst
Normal file
189
learning/features/3d/3d_performance_and_limitations.rst
Normal file
@@ -0,0 +1,189 @@
|
||||
.. _doc_3d_performance_and_limitations:
|
||||
|
||||
3D performance and limitations
|
||||
==============================
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Godot follows a balanced performance philosophy. In performance world,
|
||||
there are always trade-offs, which consist in trading speed for
|
||||
usability and flexibility. Some practical examples of this are:
|
||||
|
||||
- Rendering objects efficiently in high amounts is easy, but when a
|
||||
large scene must be rendered it can become inefficient. To solve
|
||||
this, visibility computation must be added to the rendering, which
|
||||
makes rendering less efficient, but at the same time less objects are
|
||||
rendered, so efficiency overall improves.
|
||||
- Configuring the properties of every material for every object that
|
||||
needs to be renderer is also slow. To solve this, objects are sorted
|
||||
by material to reduce the costs, but at the same time sorting has a
|
||||
cost.
|
||||
- In 3D physics a similar situation happens. The best algorithms to
|
||||
handle large amounts of physics objects (such as SAP) are very slow
|
||||
at insertion/removal of objects and ray-casting. Algorithms that
|
||||
allow faster insertion and removal, as well as ray-casting will not
|
||||
be able to handle as many active objects.
|
||||
|
||||
And there are many more examples of this! Game engines strive to be
|
||||
general purpose in nature, so balanced algorithms are always favored
|
||||
over algorithms that might be the fast in some situations and slow in
|
||||
others.. or algorithms that are fast but make usability more difficult.
|
||||
|
||||
Godot is not an exception and, while it is designed to have backends
|
||||
swappable for different algorithms, the default ones (or more like, the
|
||||
only ones that are there for now) prioritize balance and flexibility
|
||||
over performance.
|
||||
|
||||
With this clear, the aim of this tutorial is to explain how to get the
|
||||
maximum performance out of Godot.
|
||||
|
||||
Rendering
|
||||
~~~~~~~~~
|
||||
|
||||
3D rendering is one of the most difficult areas to get performance from,
|
||||
so this section will have a list of tips.
|
||||
|
||||
Reuse shaders and materials
|
||||
---------------------------
|
||||
|
||||
Godot renderer is a little different to what is out there. It's designed
|
||||
to minimize GPU state changes as much as possible.
|
||||
:ref:`FixedMaterial <class_FixedMaterial>`
|
||||
does a good job at reusing materials that need similar shaders but, if
|
||||
custom shaders are used, make sure to reuse them as much as possible.
|
||||
Godot's priorities will be like this:
|
||||
|
||||
- **Reusing Materials**: The less amount of different materials in the
|
||||
scene, the faster the rendering will be. If a scene has a huge amount
|
||||
of objects (in the hundreds or thousands) try reusing the materials
|
||||
or in the worst case use atlases.
|
||||
- **Reusing Shaders**: If materials can't be reused, at least try to
|
||||
re-use shaders (or FixedMaterials with different parameters but same
|
||||
configuration).
|
||||
|
||||
If a scene has, for example, 20.000 objects with 20.000 different
|
||||
materials each, rendering will be really slow. If the same scene has
|
||||
20.000 objects, but only uses 100 materials, rendering will be blazing
|
||||
fast.
|
||||
|
||||
Pixels cost vs vertex cost
|
||||
--------------------------
|
||||
|
||||
It is a common thought that the lower the polygons in a model, the
|
||||
faster it will be rendered. This is *really* relative and depends on
|
||||
many factors.
|
||||
|
||||
On a modern PC and consoles, vertex cost is low. Very low. GPUs
|
||||
originally only rendered triangles, so all the vertices:
|
||||
|
||||
1. Had to be transformed by the CPU (including clipping).
|
||||
|
||||
2. Had to be sent to the GPU memory from the main RAM.
|
||||
|
||||
Nowadays, all this is handled inside the GPU, so the performance is
|
||||
extremely high. 3D artists usually have the wrong feeling about
|
||||
polycount performance because 3D DCCs (such as Blender, Max, etc.) need
|
||||
to keep geometry in CPU memory in order for it to be edited, reducing
|
||||
actual performance. Truth is, a model rendered by a 3D engine is much
|
||||
more optimal than how 3D DCCs display them.
|
||||
|
||||
On mobile devices, the story is different. PC and Console GPUs are
|
||||
brute-force monsters that can pull as much electricity as they need from
|
||||
the power grid. Mobile GPUs are limited to a tiny battery, so they need
|
||||
to be a lot more power efficient.
|
||||
|
||||
To be more efficient, mobile GPUs attempt to avoid *overdraw*. This
|
||||
means, the same pixel on the screen being rendered (as in, with lighting
|
||||
calculation, etc.) more than once. Imagine a town with several buildings,
|
||||
GPUs don't really know what is visible and what is hidden until they
|
||||
draw it. A house might be drawn and then another house in front of it
|
||||
(rendering happened twice for the same pixel!). PC GPUs normally don't
|
||||
care much about this and just throw more pixel processors to the
|
||||
hardware to increase performance (but this also increases power
|
||||
consumption).
|
||||
|
||||
On mobile, pulling more power is not an option, so a technique called
|
||||
"Tile Based Rendering" is used (almost every mobile hardware uses a
|
||||
variant of it), which divide the screen into a grid. Each cell keeps the
|
||||
list of triangles drawn to it and sorts them by depth to minimize
|
||||
*overdraw*. This technique improves performance and reduces power
|
||||
consumption, but takes a toll on vertex performance. As a result, less
|
||||
vertices and triangles can be processed for drawing.
|
||||
|
||||
Generally, this is not so bad, but there is a corner case on mobile that
|
||||
must be avoided, which is to have small objects with a lot of geometry
|
||||
within a small portion of the screen. This forces mobile GPUs to put a
|
||||
lot of strain on a single screen cell, considerably decreasing
|
||||
performance (as all the other cells must wait for it to complete in
|
||||
order to display the frame).
|
||||
|
||||
To make it short, do not worry about vertex count so much on mobile, but
|
||||
avoid concentration of vertices in small parts of the screen. If, for
|
||||
example, a character, NPC, vehicle, etc. is far away (so it looks tiny),
|
||||
use a smaller level of detail (LOD) model instead.
|
||||
|
||||
An extra situation where vertex cost must be considered is objects that
|
||||
have extra processing per vertex, such as:
|
||||
|
||||
- Skinning (skeletal animation)
|
||||
- Morphs (shape keys)
|
||||
- Vertex Lit Objects (common on mobile)
|
||||
|
||||
Texture compression
|
||||
-------------------
|
||||
|
||||
Godot offers to compress textures of 3D models when imported (VRAM
|
||||
compression). Video RAM compression is not as efficient in size as PNG
|
||||
or JPG when stored, but increase performance enormously when drawing.
|
||||
|
||||
This is because the main goal of texture compression is bandwidth
|
||||
reduction between memory and the GPU.
|
||||
|
||||
In 3D, the shapes of objects depend more on the geometry than the
|
||||
texture, so compression is generally not noticeable. In 2D, compression
|
||||
depends more on shapes inside the textures, so the artifacting resulting
|
||||
from the compression is more noticeable.
|
||||
|
||||
As a warning, most Android devices do not support texture compression of
|
||||
textures with transparency (only opaque), so keep this in mind.
|
||||
|
||||
Transparent objects
|
||||
-------------------
|
||||
|
||||
As mentioned before, Godot sorts objects by material and shader to
|
||||
improve performance. This, however, can not be done on transparent
|
||||
objects. Transparent objects are rendered from back to front to make
|
||||
blending with what is behind work. As a result, please try to keep
|
||||
transparent objects to a minimum! If an object has a small section with
|
||||
transparency, try to make that section a separate material.
|
||||
|
||||
Level of detail (LOD)
|
||||
---------------------
|
||||
|
||||
As also mentioned before, using objects with less vertices can improve
|
||||
performance in some cases. Godot has a very simple system to use level
|
||||
of detail,
|
||||
:ref:`GeometryInstance <class_GeometryInstance>`
|
||||
based objects have a visibility range that can be defined. Having
|
||||
several GeometryInstance objects in different ranges works as LOD.
|
||||
|
||||
Use instancing (MultiMesh)
|
||||
--------------------------
|
||||
|
||||
If several identical objects have to be drawn in the same place or
|
||||
nearby, try using :ref:`MultiMesh <class_MultiMesh>`
|
||||
instead. MultiMesh allows drawing of dozens of thousands of objects at
|
||||
very little performance cost, making it ideal for flocks, grass,
|
||||
particles, etc.
|
||||
|
||||
Bake lighting
|
||||
-------------
|
||||
|
||||
Small lights are usually not a performance issue. Shadows a little more.
|
||||
In general, if several lights need to affect a scene, it's ideal to bake
|
||||
it (:ref:`doc_light_baking`). Baking can also improve the scene quality by
|
||||
adding indirect light bounces.
|
||||
|
||||
If working on mobile, baking to texture is recommended, since this
|
||||
method is even faster.
|
||||
183
learning/features/3d/fixed_materials.rst
Normal file
183
learning/features/3d/fixed_materials.rst
Normal file
@@ -0,0 +1,183 @@
|
||||
.. _doc_fixed_materials:
|
||||
|
||||
Fixed materials
|
||||
===============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Fixed materials (originally Fixed Pipeline Materials) are the most
|
||||
common type of materials, using the most common material options found
|
||||
in 3D DCCs (such as Maya, 3DS Max or Blender). The big advantage of
|
||||
using them is that 3D artists are very familiar with this layout. They
|
||||
also allow to try out different things quickly without the need of
|
||||
writing shaders. Fixed Materials inherit from
|
||||
:ref:`Material <class_Material>`,
|
||||
which also has several options. If you haven't read it before, reading
|
||||
the :ref:`doc_materials` tutorial is recommended.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
Here is the list of all the options available for fixed materials:
|
||||
|
||||
.. image:: /img/fixed_materials.png
|
||||
|
||||
From this point, every option will be explained in detail:
|
||||
|
||||
Fixed flags
|
||||
-----------
|
||||
|
||||
These are a set of flags that control general aspects of the material.
|
||||
|
||||
Use alpha
|
||||
~~~~~~~~~
|
||||
|
||||
This flag needs to be active for transparent materials to blend with
|
||||
what is behind, otherwise display will always be opaque. Do not enable
|
||||
this flag unless the material really needs it, because it can severely
|
||||
affect performance and quality. Materials with transparency also won't
|
||||
cast shadows (unless they contain opaque areas and the "opaque
|
||||
pre-pass" hint is turned on, see the :ref:`doc_materials` tutorial for more
|
||||
information).
|
||||
|
||||
.. image:: /img/fixed_material_alpha.png
|
||||
|
||||
Use vertex colors
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Vertex color painting is a very common technique to add detail to a
|
||||
geometry. 3D DCCs all support this, and many even support baking
|
||||
occlusion to it. Godot allows this information to be used in the fixed
|
||||
material by modulating the diffuse color when enabled.
|
||||
|
||||
.. image:: /img/fixed_material_vcols.png
|
||||
|
||||
Point size
|
||||
~~~~~~~~~~
|
||||
|
||||
Point size is used to set the point size (in pixels) for when rendering
|
||||
points. This feature is mostly used in tools and HUDs.
|
||||
|
||||
Discard alpha
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
When alpha is enabled (see above) the transparent pixels are blended
|
||||
with what is behind them. In some combinations (of using alpha to
|
||||
render depth) it may be possible that transparent pixels cover other
|
||||
objects.
|
||||
|
||||
If this is the case, enable this option for the material. This option
|
||||
is often used in combination with "opaque pre-pass" hint (see the
|
||||
:ref:`doc_materials` tutorial for more information).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
Diffuse, specular, emission and specular exponent
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These are the base colors for the material.
|
||||
|
||||
- Diffuse color is responsible for the light that reaches the material,
|
||||
and is diffusely back-scattered then. This color varies by the angle between
|
||||
the face and the light and the distance to the light source
|
||||
(in the case of spot and omni lights). It is
|
||||
the color that best represents the material. It can also have alpha
|
||||
(transparency).
|
||||
- Specular color is the color of the reflected light and responsible
|
||||
for shines. It is affected by the specular exponent.
|
||||
- Emission is the color of the light generated within the material
|
||||
(although it will not lit anything else around unless baking). This
|
||||
color is constant.
|
||||
- Specular Exponent (or "Shininess"/"Intensity" in some 3D DCCs) is the
|
||||
way light is reflected. If the value is high, light is reflected
|
||||
completely, otherwise it is diffused more and more.
|
||||
|
||||
Below is an example of how they interact:
|
||||
|
||||
.. image:: /img/fixed_material_colors.png
|
||||
|
||||
Shader & shader param
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Regular shader materials allow custom lighting code. Fixed materials
|
||||
come with four predefined shader types:
|
||||
|
||||
- **Lambert**: The standard diffuse light, where the amount of light is
|
||||
proportional to the angle from the light emitter.
|
||||
- **Wrap**: A variation on Lambert, where the "coverage" of the light
|
||||
can be changed. This is useful for many types of materials such as
|
||||
wood, clay, hair, etc.
|
||||
- **Velvet**: This is similar to Lambert, but adds light scattering in
|
||||
the edges. It's useful for leathers and some types of metals.
|
||||
- **Toon**: Standard toon shading with a coverage parameter. The
|
||||
specular component also becomes toon-ized.
|
||||
|
||||
.. image:: /img/fixed_material_shader.png
|
||||
|
||||
Detail & detail mix
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Detail is a second diffuse texture which can be mixed with the first one
|
||||
(more on textures later!). Detail blend and mix control how these are
|
||||
added together, here's an example of what detail textures are for:
|
||||
|
||||
.. image:: /img/fixed_material_detail.png
|
||||
|
||||
Normal depth
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Normal depth controls the strength and the direction of normal-mapping.
|
||||
If it is set to 1 (the default), the un-scaled normal map is applied.
|
||||
Values larger than 1 make normal-mapping more pronounced (dents and bumps
|
||||
become larger), while values smaller than 1 reduce the effect. A normal
|
||||
depth of 0 disables normal-mapping. Negative values invert the normal map
|
||||
so dents become bumps and vice versa. Here is an example showing the
|
||||
influence of the normal depth on the outcome:
|
||||
|
||||
.. image:: /img/fixed_material_normal_depth.png
|
||||
|
||||
Glow
|
||||
~~~~
|
||||
|
||||
This value controls how much of the color is sent to the glow buffer. It
|
||||
can be greater than 1 for a stronger effect. For glow to work, a
|
||||
WorldEnvironment must exist with Glow activated.
|
||||
|
||||
.. image:: /img/fixed_material_glow.png
|
||||
|
||||
Blend mode
|
||||
~~~~~~~~~~
|
||||
|
||||
Objects are usually blended in Mix mode. Other blend modes (Add and Sub)
|
||||
exist for special cases (usually particle effects, light rays, etc.) but
|
||||
materials can be set to them:
|
||||
|
||||
.. image:: /img/fixed_material_blend.png
|
||||
|
||||
Point size, line width
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When drawing points or lines, the size of them can be adjusted here per
|
||||
material.
|
||||
|
||||
Textures
|
||||
--------
|
||||
|
||||
Almost all of the parameters above can have a texture assigned to them.
|
||||
There are four options to where they can get their UV coordinates:
|
||||
|
||||
- **UV Coordinates (UV Array)**: This is the regular UV coordinate
|
||||
array that was imported with the model.
|
||||
- **UV x UV XForm**: UV Coordinates multiplied by the UV Xform matrix.
|
||||
- **UV2 Coordinates**: Some imported models might have come with a
|
||||
second set of UV coordinates. These are common for detail textures or
|
||||
for baked light textures.
|
||||
- **Sphere**: Spherical coordinates (difference of the normal at the
|
||||
pixel by the camera normal).
|
||||
|
||||
The value of every pixel of the texture is multiplied by the original
|
||||
parameter. This means that if a texture is loaded for diffuse, it will
|
||||
be multiplied by the color of the diffuse color parameter. Same applies
|
||||
to all the others except for specular exponent, which is replaced.
|
||||
165
learning/features/3d/high_dynamic_range.rst
Normal file
165
learning/features/3d/high_dynamic_range.rst
Normal file
@@ -0,0 +1,165 @@
|
||||
.. _doc_high_dynamic_range:
|
||||
|
||||
High dynamic range
|
||||
==================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Normally, an artist does all the 3D modelling, then all the texturing,
|
||||
looks at his or her awesome looking model in the 3D DCC and says "looks
|
||||
fantastic, ready for integration!" then goes into the game, lighting is
|
||||
setup and the game runs.
|
||||
|
||||
So where does all this HDR stuff thing come from? The idea is that
|
||||
instead of dealing with colors that go from black to white (0 to 1), we
|
||||
use colors whiter than white (for example, 0 to 8 times white).
|
||||
|
||||
To be more practical, imagine that in a regular scene, the intensity
|
||||
of a light (generally 1.0) is set to 5.0. The whole scene will turn
|
||||
very bright (towards white) and look horrible.
|
||||
|
||||
After this the luminance of the scene is computed by averaging the
|
||||
luminance of every pixel of it, and this value is used to bring the
|
||||
scene back to normal ranges. This last operation is called
|
||||
tone-mapping. Finally, we are at a similar place from where we
|
||||
started:
|
||||
|
||||
.. image:: /img/hdr_tonemap.png
|
||||
|
||||
Except the scene is more contrasted, because there is a higher light
|
||||
range in play. What is this all useful for? The idea is that the scene
|
||||
luminance will change while you move through the world, allowing
|
||||
situations like this to happen:
|
||||
|
||||
.. image:: /img/hdr_cave.png
|
||||
|
||||
Additionally, it is possible to set a threshold value to send to the
|
||||
glow buffer depending on the pixel luminance. This allows for more
|
||||
realistic light bleeding effects in the scene.
|
||||
|
||||
Linear color space
|
||||
------------------
|
||||
|
||||
The problem with this technique is that computer monitors apply a
|
||||
gamma curve to adapt better to the way the human eye sees. Artists
|
||||
create their art on the screen too, so their art has an implicit gamma
|
||||
curve applied to it.
|
||||
|
||||
The color space where images created in computer monitors exist is
|
||||
called "sRGB". All visual content that people have on their computers
|
||||
or download from the internet (such as pictures, movies, etc.)
|
||||
is in this colorspace.
|
||||
|
||||
.. image:: /img/hdr_gamma.png
|
||||
|
||||
The mathematics of HDR require that we multiply the scene by different
|
||||
values to adjust the luminance and exposure to different light ranges,
|
||||
and this curve gets in the way as we need colors in linear space for
|
||||
this.
|
||||
|
||||
Linear color space & asset pipeline
|
||||
-----------------------------------
|
||||
|
||||
Working in HDR is not just pressing a switch. First, imported image
|
||||
assets must be converted to linear space on import. There are two ways
|
||||
to do this:
|
||||
|
||||
SRGB -> linear conversion on image import
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the most compatible way of using linear-space assets and it will
|
||||
work everywhere including all mobile devices. The main issue with this
|
||||
is loss of quality, as sRGB exists to avoid this same problem. Using 8
|
||||
bits per channel to represent linear colors is inefficient from the
|
||||
point of view of the human eye. These textures might be later compressed
|
||||
too, which makes the problem worse.
|
||||
|
||||
In any case though, this is the easy solution that works everywhere.
|
||||
|
||||
Hardware sRGB -> linear conversion.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the most correct way to use assets in linear-space, as the
|
||||
texture sampler on the GPU will do the conversion after reading the
|
||||
texel using floating point. This works fine on PC and consoles, but most
|
||||
mobile devices do no support it, or do not support it on compressed
|
||||
texture format (iOS for example).
|
||||
|
||||
Linear -> sRGB at the end.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After all the rendering is done, the linear-space rendered image must be
|
||||
converted back to sRGB. To do this, simply enable sRGB conversion in the
|
||||
current :ref:`Environment <class_Environment>` (more on that below).
|
||||
|
||||
Keep in mind that sRGB [STRIKEOUT:> Linear and Linear]> sRGB conversions
|
||||
must always be **both** enabled. Failing to enable one of them will
|
||||
result in horrible visuals suitable only for avant garde experimental
|
||||
indie games.
|
||||
|
||||
Parameters of HDR
|
||||
-----------------
|
||||
|
||||
HDR is found in the :ref:`Environment <class_Environment>`
|
||||
resource. These are found most of the time inside a
|
||||
:ref:`WorldEnvironment <class_WorldEnvironment>`
|
||||
node, or set in a camera. There are many parameters for HDR:
|
||||
|
||||
.. image:: /img/hdr_parameters.png
|
||||
|
||||
ToneMapper
|
||||
~~~~~~~~~~
|
||||
|
||||
The ToneMapper is the heart of the algorithm. Many options for
|
||||
tonemappers are provided:
|
||||
|
||||
- Linear: Simplest tonemapper. It does its job for adjusting scene
|
||||
brightness, but if the differences in light are too big, it will
|
||||
cause colors to be too saturated.
|
||||
- Log: Similar to linear, but not as extreme.
|
||||
- Reinhardt: Classical tonemapper (modified so it will not desaturate
|
||||
as much)
|
||||
- ReinhardtAutoWhite: Same as above, but uses the max scene luminance
|
||||
to adjust the white value.
|
||||
|
||||
Exposure
|
||||
~~~~~~~~
|
||||
|
||||
The same exposure parameter as in real cameras. Controls how much light
|
||||
enters the camera. Higher values will result in a brighter scene and
|
||||
lower values will result in a darker scene.
|
||||
|
||||
White
|
||||
~~~~~
|
||||
|
||||
Maximum value of white.
|
||||
|
||||
Glow threshold
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Determine above which value (from 0 to 1 after the scene is tonemapped),
|
||||
light will start bleeding.
|
||||
|
||||
Glow scale
|
||||
~~~~~~~~~~
|
||||
|
||||
Determine how much light will bleed.
|
||||
|
||||
Min luminance
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Lower bound value of light for the scene at which the tonemapper stops
|
||||
working. This allows dark scenes to remain dark.
|
||||
|
||||
Max luminance
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Upper bound value of light for the scene at which the tonemapper stops
|
||||
working. This allows bright scenes to remain saturated.
|
||||
|
||||
Exposure adjustment speed
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Auto-exposure will change slowly and will take a while to adjust (like
|
||||
in real cameras). Bigger values means faster adjustment.
|
||||
59
learning/features/3d/importing_3d_meshes.rst
Normal file
59
learning/features/3d/importing_3d_meshes.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
.. _doc_importing_3d_meshes:
|
||||
|
||||
Importing 3D meshes
|
||||
===================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Godot supports a flexible and powerful :ref:`3D scene importer
|
||||
<doc_importing_3d_scenes>` that allows for full scene importing. For a lot of
|
||||
artists and developers this is more than enough. However, many do not like this
|
||||
workflow as much and prefer to import individual 3D Meshes and build the scenes
|
||||
inside the Godot 3D editor themselves. (Note that for more advanced features
|
||||
such as skeletal animation, there is no option to the 3D Scene Importer).
|
||||
|
||||
The 3D mesh import workflow is simple and works using the OBJ file
|
||||
format. The imported meshes result in a .msh binary file which the user
|
||||
can put into a :ref:`class_meshinstance`, which in turn can be placed
|
||||
somewhere in the edited scene.
|
||||
|
||||
Importing
|
||||
---------
|
||||
|
||||
Importing is done through the Import 3D Mesh menu:
|
||||
|
||||
.. image:: /img/mesh_import.png
|
||||
|
||||
Which opens the Mesh import window:
|
||||
|
||||
.. image:: /img/mesh_dialog.png
|
||||
|
||||
This dialog allows the import of one more more OBJ files into a target
|
||||
path. OBJ files are converted to .msh files. Files are imported without
|
||||
any material on them, material has to be added by the user (see the
|
||||
:ref:`doc_fixed_materials` tutorial). If the external OBJ file is changed it
|
||||
will be re-imported, while keeping the newly assigned material.
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
A few options are present. Normals are needed for regular shading, while
|
||||
Tangents are needed if you plan to use normal-mapping on the material. In
|
||||
general, OBJ files describe how to be shaded very well, but an option to
|
||||
force smooth shading is available.
|
||||
|
||||
Finally, there is an option to weld vertices. Given OBJ files are
|
||||
text-based, it is common to find some of these with vertices that do not
|
||||
match, which results in strange shading. The weld vertices option merges
|
||||
vertices that are too close to keep proper smooth shading.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Mesh resources (what this importer imports to) are used inside MeshInstance
|
||||
nodes. Simply set them to the Mesh property of them.
|
||||
|
||||
.. image:: /img/3dmesh_instance.png
|
||||
|
||||
And that is it.
|
||||
434
learning/features/3d/importing_3d_scenes.rst
Normal file
434
learning/features/3d/importing_3d_scenes.rst
Normal file
@@ -0,0 +1,434 @@
|
||||
.. _doc_importing_3d_scenes:
|
||||
|
||||
Importing 3D scenes
|
||||
===================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Most game engines just import 3D objects, which may contain skeletons or
|
||||
animations, and then all further work is done in the engine UI, like
|
||||
object placement, full scene animations, etc. In Godot, given the node
|
||||
system is very similar to how 3D DCC tools (such as Maya, 3DS Max or Blender)
|
||||
work, full 3D scenes can be imported in all their glory. Additionally, by using
|
||||
a simple language tag system, it is possible to specify that objects are
|
||||
imported as several things, such as collidable, rooms and portals, vehicles
|
||||
and wheels, LOD distances, billboards, etc.
|
||||
|
||||
This allows for some interesting features:
|
||||
|
||||
- Importing simple scenes, rigged objects, animations, etc.
|
||||
- Importing full scenes. Entire scenarios can be created and updated in
|
||||
the 3D DCC and imported to Godot each time they change, then only
|
||||
little editing is needed from the engine side.
|
||||
- Full cutscenes can be imported, including multiple character
|
||||
animation, lighting, camera motion, etc.
|
||||
- Scenes can be further edited and scripted in the engine, where
|
||||
shaders and environment effects can be added, enemies can be
|
||||
instanced, etc. The importer will update geometry changes if the
|
||||
source scene changes but keep the local changes too (in real-time
|
||||
while using the Godot editor!)
|
||||
- Textures can be all batch-imported and updated when the source scene
|
||||
changes.
|
||||
|
||||
This is achieved by using a very simple language tag that will be
|
||||
explained in detail later.
|
||||
|
||||
Exporting DAE files
|
||||
-------------------
|
||||
|
||||
Why not FBX?
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Most game engines use the FBX format for importing 3D scenes, which is
|
||||
definitely one of the most standardized in the industry. However, this
|
||||
format requires the use of a closed library from Autodesk which is
|
||||
distributed with a more restrictive licensing terms than Godot. The plan
|
||||
is, sometime in the future, to implement an external conversion binary,
|
||||
but meanwhile FBX is not really supported.
|
||||
|
||||
Exporting DAE files from Maya and 3DS Max
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Autodesk added built-in collada support to Maya and 3DS Max, but it's
|
||||
really broken and should not be used. The best way to export this format
|
||||
is by using the
|
||||
`OpenCollada <https://github.com/KhronosGroup/OpenCOLLADA/wiki/OpenCOLLADA-Tools>`__
|
||||
plugins. They work really well, although they are not always up-to date
|
||||
with the latest version of the software.
|
||||
|
||||
Exporting DAE files from Blender
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Blender also has built-in collada support, but it's really broken and
|
||||
should not be used either.
|
||||
|
||||
Godot provides a `Python
|
||||
Plugin <https://github.com/godotengine/collada-exporter>`__
|
||||
that will do a much better job at exporting the scenes.
|
||||
|
||||
The import process
|
||||
------------------
|
||||
|
||||
Import process begins with the 3D scene import menu:
|
||||
|
||||
.. image:: /img/3dimp_menu.png
|
||||
|
||||
That opens what is probably the biggest of all the import dialogs:
|
||||
|
||||
.. image:: /img/3dimp_dialog.png
|
||||
|
||||
Many options exist in there, so each section will be explained as
|
||||
follows:
|
||||
|
||||
Source & target paths
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To import, two options are needed. The first is a source .dae file
|
||||
(.dae stands for Collada. More import formats will eventually added,
|
||||
but Collada is the most complete open format as of this writing).
|
||||
|
||||
A target folder needs to be provided, so the importer can import the
|
||||
scene there. The imported scene will have the same filename as the
|
||||
source one, except for the .scn extension, so make sure you pick good
|
||||
names when you export!
|
||||
|
||||
The textures will be copied and converted. Textures in 3D applications
|
||||
are usually just PNG or JPG files. Godot will convert them to video
|
||||
memory texture compression format (s3tc, pvrtc, ericsson, etc.) by
|
||||
default to improve performance and save resources.
|
||||
|
||||
Since the original textures, 3D file and textures are usually not needed,
|
||||
it's recommended to keep them outside the project. For some hints on
|
||||
how to do this the best way, you can check the :ref:`doc_project_organization`
|
||||
tutorial.
|
||||
|
||||
Two options for textures are provided. They can be copied to the same
|
||||
place as the scene, or they can be copied to a common path (configurable
|
||||
in the project settings). If you choose this, make sure no two textures
|
||||
are named the same.
|
||||
|
||||
3D rigging tips
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Before going into the options, here are some tips for making sure your
|
||||
rigs import properly
|
||||
|
||||
- Only up to 4 weights are imported per vertex, if a vertex depends of
|
||||
more than 4 bones, only the 4 most important bones (the one with the
|
||||
most weight) will be imported. For most models this usually works
|
||||
fine, but just keep it in mind.
|
||||
- Do not use non-uniform scale in bone animation, as this will likely
|
||||
not import properly. Try to accomplish the same effect with more
|
||||
bones.
|
||||
- When exporting from Blender, make sure that objects modified by a
|
||||
skeleton are children of it. Many objects can be modified by a single
|
||||
skeleton, but they all should be direct children.
|
||||
- The same way, when using Blender, make sure that the relative
|
||||
transform of children nodes to the skeleton is zero (no rotation, no
|
||||
translation, no scale. All zero and scale at 1.0). The position of
|
||||
both objects (the little orange dot) should be at the same place.
|
||||
|
||||
3D import options
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
This section contains many options to change the way import workflow
|
||||
works. Some (like HDR) will be better explained in other sections, but
|
||||
in general a pattern can be visible in the options and that is, many of
|
||||
the options end with "-something". For example:
|
||||
|
||||
- Remove Nodes (-noimp)
|
||||
- Set Alpha in Materials (-alpha)
|
||||
- Create Collisions (-col).
|
||||
|
||||
This means that the object names in the 3D DCC need to have those
|
||||
options appended at the end for the importer to tell what they are. When
|
||||
imported, Godot will convert them to what they are meant to be.
|
||||
|
||||
**Note:** Maya users must use “_" (underscore) instead of "-" (minus).
|
||||
|
||||
Here is an example of how a scene in the 3D DCC looks (using Blender),
|
||||
and how it is imported to Godot:
|
||||
|
||||
.. image:: /img/3dimp_blender.png
|
||||
|
||||
Notice that:
|
||||
|
||||
- The camera was imported normally.
|
||||
- A Room was created (-room).
|
||||
- A Portal was created (-portal).
|
||||
- The Mesh got static collision added (-col).
|
||||
- The Light was not imported (-noimp).
|
||||
|
||||
Options in detail
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Following is a list of most import options and what they do in more
|
||||
detail.
|
||||
|
||||
Remove nodes (-noimp)
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Node names that have this at the end will be removed at import time, mo
|
||||
matter their type. Erasing them afterwards is most of the times
|
||||
pointless because the will be restored if the source scene changes.
|
||||
|
||||
Import animations
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some scene formats (.dae) support one or more animations. If this is
|
||||
checked, an :ref:`class_animationplayer` node will be
|
||||
created, containing the animations.
|
||||
|
||||
Compress geometry
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This option (disabled [STRIKEOUT:or more like, always enabled] at the
|
||||
moment at the time of writing this) will compress geometry so it takes
|
||||
less space and renders faster (at the cost of less precision).
|
||||
|
||||
Force generation of tangent arrays
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The importer detects when you have used a normalmap texture, or when the
|
||||
source file contains tangent/binormal information. These arrays are
|
||||
needed for normalmapping to work, and most exporters know what they do
|
||||
when they export this. However, it might be possible to run into source
|
||||
scenes that do not have this information which, as a result, make
|
||||
normal-mapping not work. If you notice that normal-maps do not work when
|
||||
importing the scene, turn this on!
|
||||
|
||||
SRGB -> linear of diffuse textures
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When rendering using HDR (High Dynamic Range) it might be desirable to
|
||||
use linear-space textures to achieve a more real-life lighting.
|
||||
Otherwise, colors may saturate and contrast too much when exposure
|
||||
changes. This option must be used together with the SRGB option in
|
||||
:ref:`class_worldenvironment`. The texture import
|
||||
options also have the option to do this conversion, but if this one is
|
||||
turned on, conversion will always be done to diffuse textures (usually
|
||||
what is desired). For more information, read the :ref:`doc_high_dynamic_range`
|
||||
tutorial.
|
||||
|
||||
Set alpha in materials (-alpha)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When working with most 3D DCCs, its pretty obvious when a texture is
|
||||
transparent and has opacity and this rarely affects the workflow or
|
||||
final rendering. However, when dealing with real-time rendering,
|
||||
materials with alpha blending are usually less optimal to draw, so they
|
||||
must be explicitly marked as such.
|
||||
|
||||
Originally Godot detected this based on whether if the source texture
|
||||
had an alpha channel, but most image manipulation applications like Photoshop or
|
||||
Gimp will export this channel anyway even if not used. Code was added
|
||||
later to check manually if there really was any transparency in the
|
||||
texture, but artists will anyway and very often lay uvmaps into opaque
|
||||
parts of a texture and leave unused areas (where no UV exists)
|
||||
transparent, making this detection worthless.
|
||||
|
||||
Finally, it was decided that it's best to import everything as opaque
|
||||
and leave artists to fix materials that need transparency when it's
|
||||
obvious that they are not looking right (see the :ref:`doc_materials`
|
||||
tutorial).
|
||||
|
||||
As a helper, since every 3D DCC allows naming the materials and keeping
|
||||
their name upon export, the (-alpha) modifier in their name will hint
|
||||
the 3D scene importer in Godot that this material will use the alpha
|
||||
channel for transparency.
|
||||
|
||||
Set vert. color in materials (-vcol)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most 3D DCCs support vertex color painting. This is generally applied as
|
||||
multiplication or screen blending. However, it is also often the case
|
||||
that your exporter will export this information as all 1s, or export it
|
||||
as something else and you will not realize it. Since most of the cases
|
||||
this option is not desired, just add this to any material to confirm
|
||||
that vertex colors are desired.
|
||||
|
||||
Create collisions (-col, -colonly)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Option "-col" will work only for Mesh nodes. If it is detected, a child
|
||||
static collision node will be added, using the same geometry as the mesh.
|
||||
|
||||
However, it is often the case that the visual geometry is too complex or
|
||||
too un-smooth for collisions, which end up not working well. To solve
|
||||
this, the "-colonly" modifier exists, which will remove the mesh upon
|
||||
import and create a :ref:`class_staticbody` collision instead.
|
||||
This helps the visual mesh and actual collision to be separated.
|
||||
|
||||
Option "-colonly" can be also used with Blender's empty objects.
|
||||
On import it will create a :ref:`class_staticbody` with
|
||||
collision node as a child. Collision node will have one of predefined shapes,
|
||||
depending on the Blender's empty draw type:
|
||||
|
||||
.. image:: /img/3dimp_BlenderEmptyDrawTypes.png
|
||||
|
||||
- Single arrow will create :ref:`class_rayshape`
|
||||
- Cube will create :ref:`class_boxshape`
|
||||
- Image will create :ref:`class_planeshape`
|
||||
- Sphere (and other non-listed) will create :ref:`class_sphereshape`
|
||||
|
||||
For better visibility in Blender's editor user can set "X-Ray" option on collision
|
||||
empties and set some distinct color for them in User Preferences / Themes / 3D View / Empty.
|
||||
|
||||
Create rooms (-room)
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is used to create a room. As a general rule, any node that is a
|
||||
child of this node will be considered inside the room (including
|
||||
portals).
|
||||
|
||||
.. For more information about rooms/portals, look at the [[Portals and Rooms]] tutorial.
|
||||
|
||||
There are two ways in which this modifier can be used. The first is
|
||||
using a Dummy/Empty node in the 3D application with the "-room" tag. For this to
|
||||
work, the "interior" of the room must be closed (geometry of the
|
||||
children should contain walls, roof, floor, etc. and the only holes to
|
||||
the outside should be covered with portals). The importer will then
|
||||
create a simplified version of the geometry for the room.
|
||||
|
||||
The second way is to use the "-room" modifier on a mesh node. This will
|
||||
use the mesh as the base for the BSP tree that contains the room bounds.
|
||||
Make sure that the mesh shape is **closed**, all normals **point
|
||||
outside** and that the geometry is **not self-intersecting**, otherwise
|
||||
the bounds may be computed wrong (BSP Trees are too picky and difficult
|
||||
to work with, which is why they are barely used anymore..).
|
||||
|
||||
Anyway, the room will need portals, which are described next.
|
||||
|
||||
Create portals (-portal)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Portals are the view to look outside a room. They are always some flat
|
||||
shape on the surface of a room. If the portal is left alone, it is used
|
||||
to activate occlusion when looking inside<->outside the room.
|
||||
|
||||
.. Again, more information on the [[Portals and Rooms]] tutorial.
|
||||
|
||||
Basically, the conditions to make and import a portal from the 3D DCC
|
||||
are:
|
||||
|
||||
- It should be a child of a room.
|
||||
- It should lay on the surface of the room (this doesn't need to be
|
||||
super exact, just make it as close as you can by eye and Godot will
|
||||
adjust it)
|
||||
- It must be a flat, convex shape, any flat and convex shape is okay, no
|
||||
matter the axis or size.
|
||||
- Normals for the flat shape faces must **all point towards the
|
||||
OUTSIDE** of the room.
|
||||
|
||||
Here is how it usually looks:
|
||||
|
||||
.. image:: /img/3dimp_portal.png
|
||||
|
||||
To connect to rooms, simply make two identical portals for both rooms
|
||||
and place them overlapped. This does not need to be perfectly exact,
|
||||
again, as Godot will fix it.
|
||||
|
||||
[..]
|
||||
^^^^
|
||||
|
||||
The rest of the tags in this section should be rather obvious, or will
|
||||
be documented/changed in the future.
|
||||
|
||||
Double-sidedness
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Collada and other formats support specifying the double-sidedness of
|
||||
the geometry (in other words, when not double-sided, back-faces are
|
||||
not drawn). Godot supports this option per Material, not per Geometry.
|
||||
|
||||
When exporting from 3D DCCs that work with per-object double-sidedness
|
||||
(such as Blender of Maya), make sure that the double sided objects do
|
||||
not share a material with the single sided ones or the importer will
|
||||
not be able to discern.
|
||||
|
||||
Animation options
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some things to keep in mind when importing animations. 3D DCCs allow
|
||||
animating with curves for every x,y,z component, doing IK constraints
|
||||
and other stuff. When imported for real-time, animations are sampled
|
||||
(at small intervals) so all this information is lost. Sampled
|
||||
animations are fast to process, but can use considerable amounts of
|
||||
memory.
|
||||
|
||||
Because of this, the "Optimize" option exists but, in some cases, this
|
||||
option might break an animation, so make it sure to disable it if
|
||||
you notice any issues.
|
||||
|
||||
Some animations are meant to be cycled (like walk animations) if this is
|
||||
the case, animation names that end in "-cycle" or "-loop" are
|
||||
automatically set to loop.
|
||||
|
||||
Import script
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Creating a script to parse the imported scene is actually really simple.
|
||||
This is great for post processing, changing materials, doing funny stuff
|
||||
with the geometry, etc.
|
||||
|
||||
Create a script that basically looks like this:
|
||||
|
||||
::
|
||||
|
||||
tool # needed so it runs in editor
|
||||
extends EditorScenePostImport
|
||||
|
||||
func post_import(scene):
|
||||
# do your stuff here
|
||||
return scene # remember to return the imported scene
|
||||
|
||||
The post-import function takes the imported scene as parameter (the
|
||||
parameter is actually the root node of the scene).
|
||||
|
||||
Update logic
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Other types of resources (like samples, meshes, fonts, images, etc.) are
|
||||
re-imported entirely when changed and user changes are not kept.
|
||||
|
||||
Because of 3D Scenes can be really complex, they use a different update
|
||||
strategy. The user might have done local changes to take advantage of
|
||||
the engine features and it would be really frustrating if everything is
|
||||
lost on re-import because the source asset changed.
|
||||
|
||||
This led to the implementation of a special update strategy. The idea
|
||||
behind is that the user will not lose anything he or she did, and only
|
||||
added data or data that can't be edited inside Godot will be updated.
|
||||
|
||||
It works like this:
|
||||
|
||||
Strategy
|
||||
^^^^^^^^
|
||||
|
||||
Upon changes on the source asset (ie: .dae), and on re-import, the
|
||||
editor will remember the way the scene originally was, and will track
|
||||
your local changes like renaming nodes, moving them or reparenting them.
|
||||
Finally, the following will be updated:
|
||||
|
||||
- Mesh Data will be replaced by the data from the updated scene.
|
||||
- Materials will be kept if they were not modified by the user.
|
||||
- Portal and Room shapes will be replaced by the ones from the updated
|
||||
scene.
|
||||
- If the user moved a node inside Godot, the transform will be kept. If
|
||||
the user moved a node in the source asset, the transform will be
|
||||
replaced. Finally, if the node was moved in both places, the
|
||||
transform will be combined.
|
||||
|
||||
In general, if the user deletes anything from the imported scene (node,
|
||||
mesh, material, etc.), updating the source asset will restore what was
|
||||
deleted. This is a good way to revert local changes to anything. If you
|
||||
really don't want a node anymore in the scene, either delete it from
|
||||
both places or add the "-noimp" tag to it in the source asset.
|
||||
|
||||
Fresh re-import
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
It can also happen that the source asset changed beyond recognition and
|
||||
a full fresh re-import is desired. If so, simply re-open the 3D scene
|
||||
import dialog from the Import -> Re-Import menu and perform re-import.
|
||||
236
learning/features/3d/introduction_to_3d.rst
Normal file
236
learning/features/3d/introduction_to_3d.rst
Normal file
@@ -0,0 +1,236 @@
|
||||
.. _doc_introduction_to_3d:
|
||||
|
||||
Introduction to 3D
|
||||
==================
|
||||
|
||||
Creating a 3D game can be challenging. That extra Z coordinate makes
|
||||
many of the common techniques that helped to make 2D games simple no
|
||||
longer work. To aid in this transition, it is worth mentioning that
|
||||
Godot uses very similar APIs for 2D and 3D. Most nodes are the same and
|
||||
are present in both 2D and 3D versions. In fact, it is worth checking
|
||||
the 3D platformer tutorial, or the 3D kinematic character tutorials,
|
||||
which are almost identical to their 2D counterparts.
|
||||
|
||||
In 3D, math is a little more complex than in 2D, so also checking the
|
||||
:ref:`doc_vector_math` in the wiki (which were specially created for game
|
||||
developers, not mathematicians or engineers) will help pave the way into
|
||||
efficiently developing 3D games.
|
||||
|
||||
Spatial node
|
||||
~~~~~~~~~~~~
|
||||
|
||||
:ref:`Node2D <class_Node2D>` is the base node for 2D.
|
||||
:ref:`Control <class_Control>` is the base node for everything GUI.
|
||||
Following this reasoning, the 3D engine uses the :ref:`Spatial <class_Spatial>`
|
||||
node for everything 3D.
|
||||
|
||||
.. image:: /img/tuto_3d1.png
|
||||
|
||||
Spatial nodes have a local transform, which is relative to the parent
|
||||
node (as long as the parent node is also **or inherits** of type
|
||||
Spatial). This transform can be accessed as a 4x3
|
||||
:ref:`Transform <class_Transform>`, or as 3 :ref:`Vector3 <class_Vector3>`
|
||||
members representing location, Euler rotation (x,y and z angles) and
|
||||
scale.
|
||||
|
||||
.. image:: /img/tuto_3d2.png
|
||||
|
||||
3D content
|
||||
~~~~~~~~~~
|
||||
|
||||
Unlike 2D, where loading image content and drawing is straightforward,
|
||||
3D is a little more difficult. The content needs to be created with
|
||||
special 3D tool (usually referred to as DCCs) and exported to an
|
||||
exchange file format in order to be imported in Godot (3D formats are
|
||||
not as standardized as images).
|
||||
|
||||
DCC-created models
|
||||
------------------
|
||||
|
||||
There are two pipelines to import 3D models in Godot. The first and most
|
||||
common one is through the :ref:`doc_importing_3d_scenes` importer, which allows to import
|
||||
entire scenes (just as they look in the DCC), including animation,
|
||||
skeletal rigs, blend shapes, etc.
|
||||
|
||||
The second pipeline is through the :ref:`doc_importing_3d_meshes` importer. This
|
||||
second method allows importing simple .OBJ files as mesh resources,
|
||||
which can be then put inside a :ref:`MeshInstance <class_MeshInstance>`
|
||||
node for display.
|
||||
|
||||
Generated geometry
|
||||
------------------
|
||||
|
||||
It is possible to create custom geometry by using the
|
||||
:ref:`Mesh <class_Mesh>` resource directly, simply create your arrays
|
||||
and use the :ref:`Mesh.add_surface() <class_Mesh_add_surface>`
|
||||
function. A helper class is also available, :ref:`SurfaceTool <class_SurfaceTool>`,
|
||||
which provides a more straightforward API and helpers for indexing,
|
||||
generating normals, tangents, etc.
|
||||
|
||||
In any case, this method is meant for generating static geometry (models
|
||||
that will not be updated often), as creating vertex arrays and
|
||||
submitting them to the 3D API has a significant performance cost.
|
||||
|
||||
Immediate geometry
|
||||
------------------
|
||||
|
||||
If, instead, there is a requirement to generate simple geometry that
|
||||
will be updated often, Godot provides a special node,
|
||||
:ref:`ImmediateGeometry <class_ImmediateGeometry>`
|
||||
which provides an OpenGL 1.x style immediate-mode API to create points,
|
||||
lines, triangles, etc.
|
||||
|
||||
2D in 3D
|
||||
--------
|
||||
|
||||
While Godot packs a powerful 2D engine, many types of games use 2D in a
|
||||
3D environment. By using a fixed camera (either orthogonal or
|
||||
perspective) that does not rotate, nodes such as
|
||||
:ref:`Sprite3D <class_Sprite3D>` and
|
||||
:ref:`AnimatedSprite3D <class_AnimatedSprite3D>`
|
||||
can be used to create 2D games that take advantage of mixing with 3D
|
||||
backgrounds, more realistic parallax, lighting/shadow effects, etc.
|
||||
|
||||
The disadvantage is, of course, that added complexity and reduced
|
||||
performance in comparison to plain 2D, as well as the lack of reference
|
||||
of working in pixels.
|
||||
|
||||
Environment
|
||||
~~~~~~~~~~~
|
||||
|
||||
Besides editing a scene, it is often common to edit the environment.
|
||||
Godot provides a :ref:`WorldEnvironment <class_WorldEnvironment>`
|
||||
node that allows changing the background color, mode (as in, put a
|
||||
skybox), and applying several types of built-in post-processing effects.
|
||||
Environments can also be overridden in the Camera.
|
||||
|
||||
3D viewport
|
||||
~~~~~~~~~~~
|
||||
|
||||
Editing 3D scenes is done in the 3D tab. This tab can be selected
|
||||
manually, but it will be automatically enabled when a Spatial node is
|
||||
selected.
|
||||
|
||||
.. image:: /img/tuto_3d3.png
|
||||
|
||||
Default 3D scene navigation controls are similar to Blender (aiming to
|
||||
have some sort of consistency in the free software pipeline..), but
|
||||
options are included to customize mouse buttons and behavior to be
|
||||
similar to other tools in Editor Settings:
|
||||
|
||||
.. image:: /img/tuto_3d4.png
|
||||
|
||||
Coordinate system
|
||||
-----------------
|
||||
|
||||
Godot uses the `metric <http://en.wikipedia.org/wiki/Metric_system>`__
|
||||
system for everything. 3D Physics and other areas are tuned for this, so
|
||||
attempting to use a different scale is usually a bad idea (unless you
|
||||
know what you are doing).
|
||||
|
||||
When working with 3D assets, it's always best to work in the correct
|
||||
scale (set your DCC to metric). Godot allows scaling post-import and,
|
||||
while this works in most cases, in rare situations it may introduce
|
||||
floating point precision issues (and thus, glitches or artifacts) in
|
||||
delicate areas such as rendering or physics. So, make sure your artists
|
||||
always work in the right scale!
|
||||
|
||||
The Y coordinate is used for "up", though for most objects that need
|
||||
alignment (like lights, cameras, capsule collider, vehicle, etc.), the Z
|
||||
axis is used as a "pointing towards" direction. This convention roughly
|
||||
means that:
|
||||
|
||||
- **X** is sides
|
||||
- **Y** is up/down
|
||||
- **Z** is front/back
|
||||
|
||||
Space and manipulation gizmos
|
||||
-----------------------------
|
||||
|
||||
Moving objects in the 3D view is done through the manipulator gizmos.
|
||||
Each axis is represented by a color: Red, Green, Blue represent X,Y,Z
|
||||
respectively. This convention applies to the grid and other gizmos too
|
||||
(and also to the shader language, ordering of components for
|
||||
Vector3,Color,etc.).
|
||||
|
||||
.. image:: /img/tuto_3d5.png
|
||||
|
||||
Some useful keybindings:
|
||||
|
||||
- To snap motion or rotation, press the "s" key while moving, scaling
|
||||
or rotating.
|
||||
- To center the view on the selected object, press the "f" key.
|
||||
|
||||
View menu
|
||||
---------
|
||||
|
||||
The view options are controlled by the "[ view ]" menu. Pay attention to
|
||||
this little menu inside the window because it is often overlooked!
|
||||
|
||||
.. image:: /img/tuto_3d6.png
|
||||
|
||||
Default lighting
|
||||
----------------
|
||||
|
||||
The 3D view has by some default options on lighting:
|
||||
|
||||
- There is a directional light that makes objects visible while editing
|
||||
turned on by default. It is no longer visible when running the game.
|
||||
- There is subtle default environment light to avoid places not reached
|
||||
by the light to remain visible. It is also no longer visible when
|
||||
running the game (and when the default light is turned off).
|
||||
|
||||
These can be turned off by toggling the "Default Light" option:
|
||||
|
||||
.. image:: /img/tuto_3d8.png
|
||||
|
||||
Customizing this (and other default view options) is also possible via
|
||||
the settings menu:
|
||||
|
||||
.. image:: /img/tuto_3d7.png
|
||||
|
||||
Which opens this window, allowing to customize ambient light color and
|
||||
default light direction:
|
||||
|
||||
.. image:: /img/tuto_3d9.png
|
||||
|
||||
Cameras
|
||||
-------
|
||||
|
||||
No matter how many objects are placed in 3D space, nothing will be
|
||||
displayed unless a :ref:`Camera <class_Camera>` is
|
||||
also added to the scene. Cameras can either work in orthogonal or
|
||||
perspective projections:
|
||||
|
||||
.. image:: /img/tuto_3d10.png
|
||||
|
||||
Cameras are associated and only display to a parent or grand-parent
|
||||
viewport. Since the root of the scene tree is a viewport, cameras will
|
||||
display on it by default, but if sub-viewports (either as render target
|
||||
or picture-in-picture) are desired, they need their own children cameras
|
||||
to display.
|
||||
|
||||
.. image:: /img/tuto_3d11.png
|
||||
|
||||
When dealing with multiple cameras, the following rules are followed for
|
||||
each viewport:
|
||||
|
||||
- If no cameras are present in the scene tree, the first one that
|
||||
enters it will become the active camera. Further cameras entering the
|
||||
scene will be ignored (unless they are set as *current*).
|
||||
- If a camera has the "*current*" property set, it will be used
|
||||
regardless of any other camera in the scene. If the property is set,
|
||||
it will become active, replacing the previous camera.
|
||||
- If an active camera leaves the scene tree, the first camera in
|
||||
tree-order will take its place.
|
||||
|
||||
Lights
|
||||
------
|
||||
|
||||
There is no limitation on the number of lights nor of types of lights in
|
||||
Godot. As many as desired can be added (as long as performance allows). Shadow
|
||||
maps are, however, limited. The more they are used, the less the quality
|
||||
overall.
|
||||
|
||||
It is possible to use :ref:`doc_light_baking`, to avoid using large amount of
|
||||
real-time lights and improve performance.
|
||||
119
learning/features/3d/materials.rst
Normal file
119
learning/features/3d/materials.rst
Normal file
@@ -0,0 +1,119 @@
|
||||
.. _doc_materials:
|
||||
|
||||
Materials
|
||||
=========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Materials can be applied to most visible 3D objects. Basically they
|
||||
describe how light interacts with that object. There are many
|
||||
types of materials, but the main ones are the
|
||||
:ref:`FixedMaterial <class_FixedMaterial>` and the
|
||||
:ref:`ShaderMaterial <class_ShaderMaterial>`.
|
||||
Tutorials for each of them exist :ref:`doc_fixed_materials` and :ref:`doc_shader_materials`.
|
||||
|
||||
This tutorial is about the basic properties shared between them.
|
||||
|
||||
.. image:: /img/material_flags.png
|
||||
|
||||
Flags
|
||||
-----
|
||||
|
||||
Materials, no matter which type they are, have an associated set of flags.
|
||||
Their use will be explained in the following.
|
||||
|
||||
Visible
|
||||
~~~~~~~
|
||||
|
||||
Toggles whether the material is visible. If unchecked, the object will
|
||||
not be shown.
|
||||
|
||||
Double sided & inverted faces
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Godot by default only shows geometry faces (triangles) when their front-side
|
||||
faces the camera. If looking at the front-side of a face, its vertices
|
||||
have to be oriented clockwise by definition. For closed objects, the
|
||||
back-side of faces is never visible because they are hidden by other
|
||||
faces. SO not drawing invisible triangles (whose vertices are oriented
|
||||
counter-clockwise on the view plane) saves a lot of GPU power.
|
||||
|
||||
However, for flat or open objects, the back-side of faces might be visible
|
||||
and needs to be drawn as well. The "double sided" flag makes sure that no matter the facing,
|
||||
the triangle will always be drawn. It is also possible to invert this
|
||||
check and draw only counter-clockwise looking faces, though it's not
|
||||
very useful except for a few cases (like drawing outlines).
|
||||
|
||||
Unshaded
|
||||
~~~~~~~~
|
||||
|
||||
Objects are always black unless light affects them, and their shading
|
||||
changes according to the type and direction of lights. When this flag is
|
||||
turned on, the diffuse color is displayed right the same as it appears
|
||||
in the texture or parameter:
|
||||
|
||||
.. image:: /img/material_unshaded.png
|
||||
|
||||
On top
|
||||
~~~~~~
|
||||
|
||||
When this flag is turned on, the object will be drawn after everything
|
||||
else has been drawn and without a depth test. This is generally useful
|
||||
for objects which shall never be hidden by other objects such as HUD effects
|
||||
or gizmos.
|
||||
|
||||
Ligthmap on UV2
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
When using lightmapping (see the :ref:`doc_light_baking` tutorial), this option
|
||||
determines that the lightmap should be accessed on the UV2 array instead
|
||||
of regular UV.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
Some parameters also exist for controlling drawing and blending:
|
||||
|
||||
Blend mode
|
||||
~~~~~~~~~~
|
||||
|
||||
Objects are usually blended in Mix mode. Other blend modes (Add and Sub)
|
||||
exist for special cases (usually particle effects, light rays, etc.) but
|
||||
materials can be set to them:
|
||||
|
||||
.. image:: /img/fixed_material_blend.png
|
||||
|
||||
Line width
|
||||
~~~~~~~~~~
|
||||
|
||||
When drawing lines, the size of them can be adjusted here per material.
|
||||
|
||||
Depth draw mode
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This is a tricky but very useful setting. By default, opaque objects are
|
||||
drawn using the depth buffer and translucent objects are not (but are
|
||||
sorted by depth). This behavior can be changed here. The options are:
|
||||
|
||||
- **Always**: Draw objects with depth always, even those with alpha.
|
||||
This often results in glitches like the one in the first image (which
|
||||
is why it's not the default).
|
||||
- **Opaque Only**: Draw objects with depth only when they are opaque,
|
||||
and do not set depth for alpha. This is the default because it's fast,
|
||||
but it's not the most correct setting. Objects with transparency that
|
||||
self-intersect will always look wrong, especially those that mix
|
||||
opaque and transparent areas, like grass, tree leaves, etc. Objects
|
||||
with transparency also can't cast shadows, this is evident in the
|
||||
second image.
|
||||
- **Alpha Pre-Pass**: The same as above, but a depth pass is performed
|
||||
for the opaque areas of objects with transparency. This makes objects
|
||||
with transparency look much more correct. In the third image it is
|
||||
evident how the leaves cast shadows between them and into the floor.
|
||||
This setting is turned off by default because, while on PC this is
|
||||
not very costly, mobile devices suffer a lot when this setting is
|
||||
turned on, so use it with care.
|
||||
- **Never**: Never use the depth buffer for this material. This is
|
||||
mostly useful in combination with the "On Top" flag explained above.
|
||||
|
||||
.. image:: /img/material_depth_draw.png
|
||||
90
learning/features/3d/using_gridmaps.rst
Normal file
90
learning/features/3d/using_gridmaps.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
.. _doc_using_gridmaps:
|
||||
|
||||
Using gridmaps
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
:ref:`Gridmaps <class_GridMap>` are a simple and fast way to create 3D
|
||||
game levels. Think of it as a 3D version of the :ref:`TileMap<doc_using_tilemaps>`
|
||||
node. Similarly, you start with a predefined library of 3D meshes that
|
||||
can be put on a grid, just like if you were building a level with an
|
||||
unlimited amount of Lego blocks.
|
||||
|
||||
Collisions can also be added to the meshes, just like you would do with
|
||||
the tiles of a tilemap.
|
||||
|
||||
Creating a MeshLibrary
|
||||
----------------------
|
||||
|
||||
To begin, you need a :ref:`class_MeshLibrary`, which is a collection
|
||||
of meshes that can be used in the gridmap. Here are some meshes you can
|
||||
use to set it up.
|
||||
|
||||
.. image:: /img/meshes.png
|
||||
|
||||
Open a new scene and create a root node (this is important, as without
|
||||
the root node, it will not be able to generate the MeshLibrary!). Then,
|
||||
create a :ref:`class_MeshInstance` node:
|
||||
|
||||
.. image:: /img/mesh_meshlib.png
|
||||
|
||||
If you don't need to apply physics to the building blocks, that's all
|
||||
you need to do. In most cases though, you will need your block to
|
||||
generate collisions, so let's see how to add them.
|
||||
|
||||
Collisions
|
||||
----------
|
||||
|
||||
To assign a :ref:`class_CollisionShape` and :ref:`class_PhysicsBody`
|
||||
to the meshes, the simplest way is to do it while creating the
|
||||
MeshLibrary. Alternatively, you can also edit an existing MeshLibrary
|
||||
from within the GridMap inspector, but only CollisionShapes can be
|
||||
defined there and not PhysicsBody.
|
||||
|
||||
To give the meshes a CollisionShape, you simply add children nodes to
|
||||
the MeshInstance node. You would typically add the desired PhysicsBody
|
||||
and CollisionShape in this order:
|
||||
|
||||
.. image:: /img/collide_mesh_meshlib.png
|
||||
|
||||
You can adjust the order according to your needs.
|
||||
|
||||
Exporting the MeshLibrary
|
||||
-------------------------
|
||||
|
||||
To export, go to ``Scene > Convert To.. > MeshLibrary..``, and save it
|
||||
as a resource.
|
||||
|
||||
.. image:: /img/export_meshlib.png
|
||||
|
||||
You are now ready to use the GridMap node.
|
||||
|
||||
Using the MeshLibrary in a GridMap
|
||||
----------------------------------
|
||||
|
||||
Create a new scene using any node as root, then add a Gridmap node.
|
||||
Then, load the MeshLibrary that you just exported.
|
||||
|
||||
.. image:: /img/load_meshlib.png
|
||||
|
||||
Now, you can build your own level as you see best fit. Use left click
|
||||
to add tiles and right click to remove them. You can adjust the floor
|
||||
level when you need to put meshes at specific heights.
|
||||
|
||||
.. image:: /img/gridmap.png
|
||||
|
||||
As mentioned above, you can also define new CollisionShapes at this
|
||||
stage by doing the following steps:
|
||||
|
||||
.. image:: /img/load_collisionshape.png
|
||||
|
||||
There you are!
|
||||
|
||||
Reminder
|
||||
--------
|
||||
|
||||
- Be cautious before scaling meshes if you are not using uniform
|
||||
meshes.
|
||||
- There are many ways to make use of gridmaps, be creative!
|
||||
355
learning/features/animation/cutout_animation.rst
Normal file
355
learning/features/animation/cutout_animation.rst
Normal file
@@ -0,0 +1,355 @@
|
||||
.. _doc_cutout_animation:
|
||||
|
||||
Cutout animation
|
||||
================
|
||||
|
||||
What is it?
|
||||
~~~~~~~~~~~
|
||||
|
||||
Cut-out is a technique of animating in 2D where pieces of paper (or
|
||||
similar material) are cut in special shapes and laid one over the other.
|
||||
The papers are animated and photographed, frame by frame using a stop
|
||||
motion technique (more info
|
||||
`here <http://en.wikipedia.org/wiki/Cutout_animation>`__).
|
||||
|
||||
With the advent of the digital age, this technique became possible using
|
||||
computers, which resulted in an increased amount of animation TV shows
|
||||
using digital Cut-out. Notable examples are `South
|
||||
Park <http://en.wikipedia.org/wiki/South_Park>`__ or `Jake and the Never
|
||||
Land
|
||||
Pirates <http://en.wikipedia.org/wiki/Jake_and_the_Never_Land_Pirates>`__
|
||||
.
|
||||
|
||||
In video games, this technique also become very popular. Examples of
|
||||
this are `Paper
|
||||
Mario <http://en.wikipedia.org/wiki/Super_Paper_Mario>`__ or `Rayman
|
||||
Origins <http://en.wikipedia.org/wiki/Rayman_Origins>`__ .
|
||||
|
||||
Cutout in Godot
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Godot provides a few tools for working with these kind of assets, but
|
||||
its overall design makes it ideal for the workflow. The reason is that,
|
||||
unlike other tools meant for this, Godot has the following advantages:
|
||||
|
||||
- **The animation system is fully integrated with the engine**: This
|
||||
means, animations can control much more than just motion of objects,
|
||||
such as textures, sprite sizes, pivots, opacity, color modulation,
|
||||
etc. Everything can be animated and blended.
|
||||
- **Mix with Traditional**: AnimatedSprite allows traditional animation
|
||||
to be mixed, very useful for complex objects, such as shape of hands
|
||||
and foot, changing facial expression, etc.
|
||||
- **Custom Shaped Elements**: Can be created with
|
||||
:ref:`Polygon2D <class_Polygon2D>`
|
||||
allowing the mixing of UV animation, deformations, etc.
|
||||
- **Particle Systems**: Can also be mixed with the traditional
|
||||
animation hierarchy, useful for magic effects, jetpacks, etc.
|
||||
- **Custom Colliders**: Set colliders and influence areas in different
|
||||
parts of the skeletons, great for bosses, fighting games, etc.
|
||||
- **Animation Tree**: Allows complex combinations and blendings of
|
||||
several animations, the same way it works in 3D.
|
||||
|
||||
And much more!
|
||||
|
||||
Making of GBot!
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
For this tutorial, we will use as demo content the pieces of the
|
||||
`GBot <https://www.youtube.com/watch?v=S13FrWuBMx4&list=UUckpus81gNin1aV8WSffRKw>`__
|
||||
character, created by Andreas Esau.
|
||||
|
||||
.. image:: /img/tuto_cutout_walk.gif
|
||||
|
||||
Get your assets: :download:`gbot_resources.zip </files/gbot_resources.zip>`.
|
||||
|
||||
Setting up the rig
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create an empty Node2D as root of the scene, we will work under it:
|
||||
|
||||
.. image:: /img/tuto_cutout1.png
|
||||
|
||||
OK, the first node of the model that we will create will be the hip.
|
||||
Generally, both in 2D and 3D, the hip is the root of the skeleton. This
|
||||
makes it easier to animate:
|
||||
|
||||
.. image:: /img/tuto_cutout2.png
|
||||
|
||||
Next will be the torso. The torso needs to be a child of the hip, so
|
||||
create a child sprite and load the torso, later accommodate it properly:
|
||||
|
||||
.. image:: /img/tuto_cutout3.png
|
||||
|
||||
This looks good. Let's see if our hierarchy works as a skeleton by
|
||||
rotating the torso:
|
||||
|
||||
.. image:: /img/tutovec_torso1.gif
|
||||
|
||||
Ouch, that doesn't look good! The rotation pivot is wrong, this means
|
||||
it needs to be adjusted.
|
||||
|
||||
This small little cross in the middle of the
|
||||
:ref:`Sprite <class_Sprite>` is
|
||||
the rotation pivot:
|
||||
|
||||
.. image:: /img/tuto_cutout4.png
|
||||
|
||||
Adjusting the pivot
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The pivot can be adjusted by changing the *offset* property in the
|
||||
Sprite:
|
||||
|
||||
.. image:: /img/tuto_cutout5.png
|
||||
|
||||
However, there is a way to do it more *visually*. While hovering over the
|
||||
desired pivot point, simply press the "v" key to move the pivot there for the
|
||||
selected Sprite. Alternately, there is a tool in the tool bar that has a
|
||||
similar function.
|
||||
|
||||
.. image:: /img/tutovec_torso2.gif
|
||||
|
||||
Now it looks good! Let's continue adding body pieces, starting by the
|
||||
right arm. Make sure to put the sprites in hierarchy, so their rotations
|
||||
and translations are relative to the parent:
|
||||
|
||||
.. image:: /img/tuto_cutout6.png
|
||||
|
||||
This seems easy, so continue with the left arm. The rest should be
|
||||
simple! Or maybe not:
|
||||
|
||||
.. image:: /img/tuto_cutout7.png
|
||||
|
||||
Right. Remember your tutorials, Luke. In 2D, parent nodes appear below
|
||||
children nodes. Well, this sucks. It seems Godot does not support cutout
|
||||
rigs after all. Come back next year, maybe for 3.0.. no wait. Just
|
||||
Kidding! It works just fine.
|
||||
|
||||
But how can this problem be solved? We want the left arm to appear behind
|
||||
the hip and the torso. For this, we can move the nodes behind the hip
|
||||
(note that you can bypass this by setting the Node2D Z property, but then you
|
||||
won't learn about all this!):
|
||||
|
||||
.. image:: /img/tuto_cutout8.png
|
||||
|
||||
But then, we lose the hierarchy layout, which allows to control the
|
||||
skeleton like.. a skeleton. Is there any hope?.. Of Course!
|
||||
|
||||
RemoteTransform2D node
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Godot provides a special node, :ref:`RemoteTransform2D <class_RemoteTransform2D>`.
|
||||
This node will transform nodes that are sitting somewhere else in the
|
||||
hierarchy, by applying the transform to the remote nodes.
|
||||
|
||||
This enables to have a visibility order independent from the
|
||||
hierarchy.
|
||||
|
||||
Simply create two more nodes as children from torso, remote_arm_l and
|
||||
remote_hand_l and link them to the actual sprites:
|
||||
|
||||
.. image:: /img/tuto_cutout9.png
|
||||
|
||||
Moving the remote transform nodes will move the sprites, allowing you to
|
||||
easily animate and pose the character:
|
||||
|
||||
.. image:: /img/tutovec_torso4.gif
|
||||
|
||||
Completing the skeleton
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Complete the skeleton by following the same steps for the rest of the
|
||||
parts. The resulting scene should look similar to this:
|
||||
|
||||
.. image:: /img/tuto_cutout10.png
|
||||
|
||||
The resulting rig will be easy to animate. By selecting the nodes and
|
||||
rotating them you can animate forward kinematics (FK) efficiently.
|
||||
|
||||
For simple objects and rigs this is fine, however the following problems
|
||||
are common:
|
||||
|
||||
- Selecting sprites can become difficult for complex rigs, and the
|
||||
scene tree ends being used due to the difficulty of clicking over the
|
||||
proper sprite.
|
||||
- Inverse Kinematics is often desired for extremities.
|
||||
|
||||
To solve these problems, Godot supports a simple method of skeletons.
|
||||
|
||||
Skeletons
|
||||
~~~~~~~~~
|
||||
|
||||
Godot doesn't actually support *true* Skeketons, but it does feature a
|
||||
helper to create "bones" between nodes. This is enough for most cases,
|
||||
but the way it works is not completely obvious.
|
||||
|
||||
|
||||
|
||||
As an example, let's turn the right arm into a skeleton. To create
|
||||
skeletons, a chain of nodes must be selected from top to bottom:
|
||||
|
||||
.. image:: /img/tuto_cutout11.png
|
||||
|
||||
Then, the option to create a skeleton is located at Edit > Make Bones:
|
||||
|
||||
.. image:: /img/tuto_cutout12.png
|
||||
|
||||
This will add bones covering the arm, but the result is not quite what
|
||||
is expected.
|
||||
|
||||
.. image:: /img/tuto_cutout13.png
|
||||
|
||||
It looks like the bones are shifted up in the hierarchy. The hand
|
||||
connects to the arm, and the arm to the body. So the question is:
|
||||
|
||||
- Why does the hand lack a bone?
|
||||
- Why does the arm connect to the body?
|
||||
|
||||
This might seem strange at first, but will make sense later on. In
|
||||
traditional skeleton systems, bones have a position, an orientation and
|
||||
a length. In Godot, bones are mostly helpers so they connect the current
|
||||
node with the parent. Because of this, **toggling a node as a bone will
|
||||
just connect it to the parent**.
|
||||
|
||||
So, with this knowledge. Let's do the same again so we have an actual,
|
||||
useful skeleton.
|
||||
|
||||
The first step is creating an endpoint node. Any kind of node will do,
|
||||
but :ref:`Position2D <class_Position2D>` is preferred because it's
|
||||
visible in the editor. The endpoint node will ensure that the last bone
|
||||
has orientation.
|
||||
|
||||
.. image:: /img/tuto_cutout14.png
|
||||
|
||||
Now select the whole chain, from the endpoint to the arm and create
|
||||
bones:
|
||||
|
||||
.. image:: /img/tuto_cutout15.png
|
||||
|
||||
The result resembles a skeleton a lot more, and now the arm and forearm
|
||||
can be selected and animated.
|
||||
|
||||
Finally, create endpoints in all meaningful extremities and connect the
|
||||
whole skeleton with bones up to the hip:
|
||||
|
||||
.. image:: /img/tuto_cutout16.png
|
||||
|
||||
Finally! the whole skeleton is rigged! On close look, it is noticeable
|
||||
that there is a second set of endpoints in the hands. This will make
|
||||
sense soon.
|
||||
|
||||
Now that a whole skeleton is rigged, the next step is setting up the IK
|
||||
chains. IK chains allow for more natural control of extremities.
|
||||
|
||||
IK chains
|
||||
~~~~~~~~~
|
||||
|
||||
IK chains are a powerful animation tool. Imagine you want to pose a character's foot in a specific position on the ground. Without IK chains, each motion of the foot would require rotating and positioning several other bones. This would be quite complex and lead to imprecise results.
|
||||
|
||||
What if we could move the foot and let the rest of the leg self-adjust?
|
||||
|
||||
This type of posing is called IK (Inverse Kinematic).
|
||||
|
||||
To create an IK chain, simply select a chain of bones from endpoint to
|
||||
the base for the chain. For example, to create an IK chain for the right
|
||||
leg, select the following:
|
||||
|
||||
.. image:: /img/tuto_cutout17.png
|
||||
|
||||
Then enable this chain for IK. Go to Edit > Make IK Chain.
|
||||
|
||||
.. image:: /img/tuto_cutout18.png
|
||||
|
||||
As a result, the base of the chain will turn *Yellow*.
|
||||
|
||||
.. image:: /img/tuto_cutout19.png
|
||||
|
||||
Once the IK chain is set-up, simply grab any of the bones in the
|
||||
extremity, any child or grand-child of the base of the chain and try to
|
||||
grab it and move it. Result will be pleasant, satisfaction warranted!
|
||||
|
||||
.. image:: /img/tutovec_torso5.gif
|
||||
|
||||
Animation
|
||||
~~~~~~~~~
|
||||
|
||||
The following section will be a collection of tips for creating
|
||||
animation for your rigs. If unsure about how the animation system in
|
||||
Godot works, refresh it by checking again the :ref:`doc_animations`.
|
||||
|
||||
2D animation
|
||||
------------
|
||||
|
||||
When doing animation in 2D, a helper will be present in the top menu.
|
||||
This helper only appears when the animation editor window is opened:
|
||||
|
||||
.. image:: /img/tuto_cutout20.png
|
||||
|
||||
The key button will insert location/rotation/scale keyframes to the
|
||||
selected objects or bones. This depends on the mask enabled. Green items
|
||||
will insert keys while red ones will not, so modify the key insertion
|
||||
mask to your preference.
|
||||
|
||||
Rest pose
|
||||
~~~~~~~~~
|
||||
|
||||
These kind of rigs do not have a "rest" pose, so it's recommended to
|
||||
create a reference rest pose in one of the animations.
|
||||
|
||||
Simply do the following steps:
|
||||
|
||||
1. Make sure the rig is in "rest" (not doing any specific pose).
|
||||
|
||||
2. Create a new animation, rename it to "rest".
|
||||
|
||||
3. Select all nodes (box selection should work fine).
|
||||
|
||||
4. Select "loc" and "rot" on the top menu.
|
||||
|
||||
5. Push the key button. Keys will be inserted for everything, creating
|
||||
a default pose.
|
||||
|
||||
.. image:: /img/tuto_cutout21.png
|
||||
|
||||
Rotation
|
||||
~~~~~~~~
|
||||
|
||||
Animating these models means only modifying the rotation of the nodes.
|
||||
Location and scale are rarely used, with the only exception of moving
|
||||
the entire rig from the hip (which is the root node).
|
||||
|
||||
As a result, when inserting keys, only the "rot" button needs to be
|
||||
pressed most of the time:
|
||||
|
||||
.. image:: /img/tuto_cutout22.png
|
||||
|
||||
This will avoid the creation of extra animation tracks for the position
|
||||
that will remain unused.
|
||||
|
||||
Keyframing IK
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
When editing IK chains, is is not necessary to select the whole chain to
|
||||
add keyframes. Selecting the endpoint of the chain and inserting a
|
||||
keyframe will automatically insert keyframes until the chain base too.
|
||||
This makes the task of animating extremities much simpler.
|
||||
|
||||
Moving sprites above and behind others.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
RemoteTransform2D works in most cases, but sometimes it is really
|
||||
necessary to have a node above and below others during an animation. To
|
||||
aid on this the "Behind Parent" property exists on any Node2D:
|
||||
|
||||
.. image:: /img/tuto_cutout23.png
|
||||
|
||||
Batch setting transition curves
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When creating really complex animations and inserting lots of keyframes,
|
||||
editing the individual keyframe curves for each can become an endless
|
||||
task. For this, the Animation Editor has a small menu where changing all
|
||||
the curves is easy. Just select every single keyframe and (generally)
|
||||
apply the "Out-In" transition curve to smooth the animation:
|
||||
|
||||
.. image:: /img/tuto_cutout24.png
|
||||
80
learning/features/gui/bbcode_in_richtextlabel.rst
Normal file
80
learning/features/gui/bbcode_in_richtextlabel.rst
Normal file
@@ -0,0 +1,80 @@
|
||||
.. _doc_bbcode_in_richtextlabel:
|
||||
|
||||
BBCode in RichTextLabel
|
||||
=======================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
:ref:`class_RichTextLabel` allows to display complex text markup in a control.
|
||||
It has a built-in API for generating the markup, but can also parse a BBCode.
|
||||
|
||||
Note that the BBCode tags can also be used to some extent in the
|
||||
:ref:`XML source of the class reference <doc_updating_the_class_reference>`.
|
||||
|
||||
Setting up
|
||||
----------
|
||||
|
||||
For RichTextLabel to work properly, it must be set up. This means loading
|
||||
the intended fonts in the relevant properties:
|
||||
|
||||
.. image:: /img/rtl_setup.png
|
||||
|
||||
Reference
|
||||
---------
|
||||
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| Command | Tag | Description |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **bold** | ``[b]{text}[/b]`` | Makes {text} bold. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **italics** | ``[i]{text}[/i]`` | Makes {text} italics. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **underline** | ``[u]{text}[/u]`` | Makes {text} underline. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **code** | ``[code]{text}[/code]`` | Makes {text} monospace. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **center** | ``[center]{text}[/center]`` | Makes {text} centered. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **right** | ``[right]{text}[/right]`` | Makes {text} right-aligned. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **fill** | ``[fill]{text}[/fill]`` | Makes {text} fill width. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **indent** | ``[indent]{text}[/indent]`` | Increase indent level of {text}. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **url** | ``[url]{url}[/url]`` | Show {url} as such. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **url (ref)** | ``[url=<url>]{text}[/url]`` | Makes {text} reference <url>. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **image** | ``[img=<path>][/img]`` | Insert image at resource <path>. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **font** | ``[font=<path>]{text}[/font]`` | Use custom font at <path> for {text}. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
| **color** | ``[color=<code/name>]{text}[/color]`` | Change {text} color, use # format such as #ff00ff or name. |
|
||||
+-----------------+--------------------------------------------+--------------------------------------------------------------+
|
||||
|
||||
Built-in color names
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
List of valid color names for the [color=<name>] tag:
|
||||
|
||||
- aqua
|
||||
- black
|
||||
- blue
|
||||
- fuchsia
|
||||
- gray
|
||||
- green
|
||||
- lime
|
||||
- maroon
|
||||
- navy
|
||||
- purple
|
||||
- red
|
||||
- silver
|
||||
- teal
|
||||
- white
|
||||
- yellow
|
||||
|
||||
Hexadecimal color codes
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Any valid 6 digit hexadecimal code is supported. e.g: [color=#ffffff]white[/color]
|
||||
143
learning/features/gui/custom_gui_controls.rst
Normal file
143
learning/features/gui/custom_gui_controls.rst
Normal file
@@ -0,0 +1,143 @@
|
||||
.. _doc_custom_gui_controls:
|
||||
|
||||
Custom GUI controls
|
||||
===================
|
||||
|
||||
So many controls...
|
||||
-------------------
|
||||
|
||||
Yet there are never enough. Creating your own custom controls that act
|
||||
just the way you want them is an obsession of almost every GUI
|
||||
programmer. Godot provides plenty of them, but they may not work exactly
|
||||
the way you want. Before contacting the developers with a pull-request
|
||||
to support diagonal scrollbars, at least it will be good to know how to
|
||||
create these controls easily from script.
|
||||
|
||||
Drawing
|
||||
-------
|
||||
|
||||
For drawing, it is recommended to check the :ref:`doc_custom_drawing_in_2d` tutorial.
|
||||
The same applies. Some functions are worth mentioning due to their
|
||||
usefulness when drawing, so they will be detailed next:
|
||||
|
||||
Checking control size
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Unlike 2D nodes, "size" is very important with controls, as it helps to
|
||||
organize them in proper layouts. For this, the
|
||||
:ref:`Control.get_size() <class_Control_get_size>`
|
||||
method is provided. Checking it during _draw() is vital to ensure
|
||||
everything is kept in-bounds.
|
||||
|
||||
Checking focus
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Some controls (such as buttons or text editors) might provide input
|
||||
focus for keyboard or joypad input. Examples of this are entering text
|
||||
or pressing a button. This is controlled with the
|
||||
:ref:`Control.set_focus_mode() <class_Control_set_focus_mode>`
|
||||
function. When drawing, and if the control supports input focus, it is
|
||||
always desired to show some sort of indicator (highight, box, etc) to
|
||||
indicate that this is the currently focused control. To check for this
|
||||
status, the :ref:`Control.has_focus() <class_Control_has_focus>`
|
||||
exists. Example
|
||||
|
||||
::
|
||||
|
||||
func _draw():
|
||||
if (has_focus()):
|
||||
draw_selected()
|
||||
else:
|
||||
draw_normal()
|
||||
|
||||
Sizing
|
||||
------
|
||||
|
||||
As mentioned before, size is very important to controls. This allows
|
||||
them to lay out properly, when set into grids, containers, or anchored.
|
||||
Controls most of the time provide a *minimum size* to help to properly
|
||||
lay them out. For example, if controls are placed vertically on top of
|
||||
each other using a :ref:`VBoxContainer <class_VBoxContainer>`,
|
||||
the minimum size will make sure your custom control is not squished by
|
||||
the other controls in the container.
|
||||
|
||||
To provide this callback, just override
|
||||
:ref:`Control.get_minimum_size() <class_Control_get_minimum_size>`,
|
||||
for example:
|
||||
|
||||
::
|
||||
|
||||
func get_minimum_size():
|
||||
return Vector2(30,30)
|
||||
|
||||
Or alternatively, set it via function:
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
set_custom_minimum_size( Vector2(30,30) )
|
||||
|
||||
Input
|
||||
-----
|
||||
|
||||
Controls provide a few helpers to make managing input events much easier
|
||||
than regular nodes.
|
||||
|
||||
Input events
|
||||
~~~~~~~~~~~~
|
||||
|
||||
There are a few tutorials about input before this one, but it's worth
|
||||
mentioning that controls have a special input method that only works
|
||||
when:
|
||||
|
||||
- The mouse pointer is over the control.
|
||||
- The button was pressed over this control (control always
|
||||
captures input until button is released)
|
||||
- Control provides keyboard/joypad focus via
|
||||
:ref:`Control.set_focus_mode() <class_Control_set_focus_mode>`.
|
||||
|
||||
This function is
|
||||
:ref:`Control._input_event() <class_Control__input_event>`.
|
||||
Simply override it in your control. No processing needs to be set.
|
||||
|
||||
::
|
||||
|
||||
extends Control
|
||||
|
||||
func _input_event(ev):
|
||||
if (ev.type==InputEvent.MOUSE_BUTTON and ev.button_index==BUTTON_LEFT and ev.pressed):
|
||||
print("Left mouse button was pressed!")
|
||||
|
||||
For more information about events themselves, check the :ref:`doc_inputevent`
|
||||
tutorial.
|
||||
|
||||
Notifications
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Controls also have many useful notifications for which no callback
|
||||
exists, but can be checked with the _notification callback:
|
||||
|
||||
::
|
||||
|
||||
func _notification(what):
|
||||
|
||||
if (what==NOTIFICATION_MOUSE_ENTER):
|
||||
pass # mouse entered the area of this control
|
||||
elif (what==NOTIFICATION_MOUSE_EXIT):
|
||||
pass # mouse exited the area of this control
|
||||
elif (what==NOTIFICATION_FOCUS_ENTER):
|
||||
pass # control gained focus
|
||||
elif (what==NOTIFICATION_FOCUS_EXIT):
|
||||
pass # control lost focus
|
||||
elif (what==NOTIFICATION_THEME_CHANGED):
|
||||
pass # theme used to draw the control changed
|
||||
# update and redraw is recommended if using a theme
|
||||
elif (what==NOTIFICATION_VISIBILITY_CHANGED):
|
||||
pass # control became visible/invisible
|
||||
# check new status with is_visible()
|
||||
elif (what==NOTIFICATION_RESIZED):
|
||||
pass # control changed size, check new size
|
||||
# with get_size()
|
||||
elif (what==NOTIFICATION_MODAL_CLOSED):
|
||||
pass # for modal popups, notification
|
||||
# that the popup was closed
|
||||
0
learning/features/gui/gui_containers.rst
Normal file
0
learning/features/gui/gui_containers.rst
Normal file
161
learning/features/gui/gui_skinning.rst
Normal file
161
learning/features/gui/gui_skinning.rst
Normal file
@@ -0,0 +1,161 @@
|
||||
.. _doc_gui_skinning:
|
||||
|
||||
GUI skinning
|
||||
============
|
||||
|
||||
Oh beautiful GUI!
|
||||
-----------------
|
||||
|
||||
This tutorial is about advanced skinning of an user interface. Most
|
||||
games generally don't need this, as they end up just relying on
|
||||
:ref:`Label <class_Label>`, :ref:`TextureFrame <class_TextureFrame>`,
|
||||
:ref:`TextureButton <class_TextureButton>` and
|
||||
:ref:`TextureProgress <class_TextureProgress>`.
|
||||
|
||||
However, many types of games often need complex user interfaces, like
|
||||
MMOs, traditional RPGs, Simulators, Strategy, etc. These kind of
|
||||
interfaces are also common in some games that include editors to create
|
||||
content, or interfaces for network connectivity.
|
||||
|
||||
Godot user interface uses these kind of controls with the default theme,
|
||||
but they can be skinned to resemble pretty much any kind of user
|
||||
interface.
|
||||
|
||||
Theme
|
||||
-----
|
||||
|
||||
The GUI is skinned through the :ref:`Theme <class_Theme>`
|
||||
resource. Theme contains all the information required to change the
|
||||
entire visual styling of all controls. Theme options are named, so it's
|
||||
not obvious which name changes what (specialy from code), but several
|
||||
tools are provided. The ultimate place to look at what each theme option
|
||||
is for each control, which will always be more up to date than any
|
||||
documentation is the file `scene/resources/default_theme/default_theme.cpp
|
||||
<https://github.com/godotengine/godot/blob/master/scene/resources/default_theme/default_theme.cpp>`__.
|
||||
The rest of this document will explain the different tools used to
|
||||
customize the theme.
|
||||
|
||||
A Theme can be applied to any control in the scene. As a result, all
|
||||
children and grand-children controls will use that same theme too
|
||||
(unless another theme is specified further down the tree). If a value is
|
||||
not found in a theme, it will be searched in themes higher up in the
|
||||
hierarchy towards the root. If nothing was found, the default theme is
|
||||
used. This system allows for flexible overriding of themes in complex
|
||||
user interfaces.
|
||||
|
||||
Theme options
|
||||
-------------
|
||||
|
||||
Each kind of option in a theme can be:
|
||||
|
||||
- **An integer constant**: A single numerical constant. Generally used
|
||||
to define spacing between compoments or alignment.
|
||||
- **A Color**: A single color, with or without transparency. Colors are
|
||||
usually applied to fonts and icons.
|
||||
- **A Texture**: A single image. Textures are not often used, but when
|
||||
they are they represent handles to pick or icons in a complex control
|
||||
(such as file dialog).
|
||||
- **A Font**: Every control that uses text can be assigned the fonts
|
||||
used to draw strings.
|
||||
- **A StyleBox**: Stylebox is a resource that defines how to draw a
|
||||
panel in varying sizes (more information on them later).
|
||||
|
||||
Every option is associated to:
|
||||
|
||||
- A name (the name of the option)
|
||||
- A Control (the name of the control)
|
||||
|
||||
An example usage:
|
||||
|
||||
::
|
||||
|
||||
var t = Theme.new()
|
||||
t.set_color("font_color","Label",Color(1.0,1.0,1.0))
|
||||
|
||||
var l = Label.new()
|
||||
l.set_theme(t)
|
||||
|
||||
In the example above, a new theme is created. The "font_color" option
|
||||
is changed and then applied to a label. As a result, the label (and all
|
||||
children and grand children labels) will use that color.
|
||||
|
||||
It is possible to override those options without using the theme
|
||||
directly and only for a specific control by using the override API in
|
||||
:ref:`Control.add_color_override() <class_Control_add_color_override>`:
|
||||
|
||||
::
|
||||
|
||||
var l = Label.new()
|
||||
l.add_color_override("font_color",Color(1.0,1.0,1.0))
|
||||
|
||||
In the inline help of Godot (in the script tab) you can check which theme options
|
||||
are overrideable, or check the :ref:`Control <class_Control>` class reference.
|
||||
|
||||
Customizing a control
|
||||
---------------------
|
||||
|
||||
If only a few controls need to be skinned, it is often not necessary to
|
||||
create a new theme. Controls offer their theme options as special kinds
|
||||
of properties. If checked, overriding will take place:
|
||||
|
||||
.. image:: /img/themecheck.png
|
||||
|
||||
As can be see in the image above, theme options have little check-boxes.
|
||||
If checked, they can be used to override the value of the theme just for
|
||||
that control.
|
||||
|
||||
Creating a theme
|
||||
----------------
|
||||
|
||||
The simplest way to create a theme is to edit a theme resource. Create a
|
||||
Theme from the resource menu, the editor will appear immediately.
|
||||
Following to this, save it (to, as example, mytheme.thm):
|
||||
|
||||
.. image:: /img/themecheck.png
|
||||
|
||||
This will create an empty theme that can later be loaded and assigned to
|
||||
controls.
|
||||
|
||||
Example: theming a button
|
||||
--------------------------
|
||||
|
||||
Take some assets (:download:`skin_assets.zip </files/skin_assets.zip>`),
|
||||
go to the "theme" menu and select "Add Class Item":
|
||||
|
||||
.. image:: /img/themeci.png
|
||||
|
||||
A menu will appear promting the type of control to create. Select
|
||||
"Button":
|
||||
|
||||
.. image:: /img/themeci2.png
|
||||
|
||||
Immediately, all button theme options will appear in the property
|
||||
editor, where they can be edited:
|
||||
|
||||
.. image:: /img/themeci3.png
|
||||
|
||||
Select the "normal" stylebox and create a new "StyleBoxTexture", then
|
||||
edit it. A texture stylebox basically contains a texture and the size of
|
||||
the margins that will not stretch when the texture is stretched. This is
|
||||
called "3x3" stretching:
|
||||
|
||||
.. image:: /img/sb1.png
|
||||
|
||||
Repeat the steps and add the other assets. There is no hover or disabled
|
||||
image in the example files, so use the same stylebox as in normal. Set
|
||||
the supplied font as the button font and change the font color to black.
|
||||
Soon, your button will look different and retro:
|
||||
|
||||
.. image:: /img/sb2.png
|
||||
|
||||
Save this theme to the .thm file. Go to the 2D editor and create a few
|
||||
buttons:
|
||||
|
||||
.. image:: /img/skinbuttons1.png
|
||||
|
||||
Now, go to the root node of the scene and locate the "theme" property,
|
||||
replace it by the theme that was just created. It should look like this:
|
||||
|
||||
.. image:: /img/skinbuttons2.png
|
||||
|
||||
Congratulations! You have created a reusable GUI Theme!
|
||||
42
learning/features/gui/size_and_anchors.rst
Normal file
42
learning/features/gui/size_and_anchors.rst
Normal file
@@ -0,0 +1,42 @@
|
||||
.. _doc_size_and_anchors:
|
||||
|
||||
Size and anchors
|
||||
----------------
|
||||
|
||||
If a game was to be always run in the same device and at the same
|
||||
resolution, positioning controls would be a simple matter of setting the
|
||||
position and size of each one of them. Unfortunately, it is rarely the
|
||||
case.
|
||||
|
||||
Only TVs nowadays have a standard resolution and aspect ratio.
|
||||
Everything else, from computer monitors to tablets, portable consoles
|
||||
and mobile phones have different resolutions and aspect ratios.
|
||||
|
||||
There are several ways to handle this, but for now let's just imagine
|
||||
that the screen resolution has changed and the controls need to be
|
||||
re-positioned. Some will need to follow the bottom of the screen, others
|
||||
the top of the screen, or maybe the right or left margins.
|
||||
|
||||
.. image:: /img/anchors.png
|
||||
|
||||
This is done by editing the *margin* properties of controls. Each
|
||||
control has four margins: left, right, bottom and top. By default all of
|
||||
them represent a distance in pixels relative to the top-left corner of
|
||||
the parent control or (in case there is no parent control) the viewport.
|
||||
|
||||
.. image:: /img/margin.png
|
||||
|
||||
When horizontal (left,right) and/or vertical (top,bottom) anchors are
|
||||
changed to END, the margin values become relative to the bottom-right
|
||||
corner of the parent control or viewport.
|
||||
|
||||
.. image:: /img/marginend.png
|
||||
|
||||
Here the control is set to expand its bottom-right corner with that of
|
||||
the parent, so when re-sizing the parent, the control will always cover
|
||||
it, leaving a 20 pixel margin:
|
||||
|
||||
.. image:: /img/marginaround.png
|
||||
|
||||
Finally, there is also a ratio option, where 0 means left, 1 means right
|
||||
and anything in between is interpolated.
|
||||
158
learning/features/inputs/inputevent.rst
Normal file
158
learning/features/inputs/inputevent.rst
Normal file
@@ -0,0 +1,158 @@
|
||||
.. _doc_inputevent:
|
||||
|
||||
InputEvent
|
||||
==========
|
||||
|
||||
What is it?
|
||||
-----------
|
||||
|
||||
Managing input is usually complex, no matter the OS or platform. To ease
|
||||
this a little, a special built-in type is provided, :ref:`InputEvent <class_InputEvent>`.
|
||||
This datatype can be configured to contain several types of input
|
||||
events. Input Events travel through the engine and can be received in
|
||||
multiple locations, depending on the purpose.
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
Every input event is originated from the user/player (though it's
|
||||
possible to generate an InputEvent and feed them back to the engine,
|
||||
which is useful for gestures). The OS object for each platform will read
|
||||
events from the device, then feed them to MainLoop. As :ref:`SceneTree <class_SceneTree>`
|
||||
is the default MainLoop implementation, events are fed to it. Godot
|
||||
provides a function to get the current SceneTree object :
|
||||
**get_tree()**.
|
||||
|
||||
But SceneTree does not know what to do with the event, so it will give
|
||||
it to the viewports, starting by the "root" :ref:`Viewport <class_Viewport>` (the first
|
||||
node of the scene tree). Viewport does quite a lot of stuff with the
|
||||
received input, in order:
|
||||
|
||||
.. image:: /img/input_event_flow.png
|
||||
|
||||
1. First of all, the standard _input function
|
||||
will be called in any node with input processing enabled (enable with
|
||||
:ref:`Node.set_process_input() <class_Node_set_process_input>` and override
|
||||
:ref:`Node._input() <class_Node__input>`). If any function consumes the event, it can
|
||||
call :ref:`SceneTree.set_input_as_handled() <class_SceneTree_set_input_as_handled>`, and the event will
|
||||
not spread any more. This ensures that you can filter all events of interest, even before the GUI.
|
||||
For gameplay input, the _unhandled_input() is generally a better fit, because it allows the GUI to intercept the events.
|
||||
2. Second, it will try to feed the input to the GUI, and see if any
|
||||
control can receive it. If so, the :ref:`Control <class_Control>` will be called via the
|
||||
virtual function :ref:`Control._input_event() <class_Control__input_event>` and the signal
|
||||
"input_event" will be emitted (this function is re-implementable by
|
||||
script by inheriting from it). If the control wants to "consume" the
|
||||
event, it will call :ref:`Control.accept_event() <class_Control_accept_event>` and the event will
|
||||
not spread any more.
|
||||
3. If so far no one consumed the event, the unhandled input callback
|
||||
will be called (enable with
|
||||
:ref:`Node.set_process_unhandled_input() <class_Node_set_process_unhandled_input>` and override
|
||||
:ref:`Node._unhandled_input() <class_Node__unhandled_input>`). If any function consumes the
|
||||
event, it can call :ref:`SceneTree.set_input_as_handled() <class_SceneTree_set_input_as_handled>`, and the
|
||||
event will not spread any more. The unhandled input callback is ideal for full-screen gameplay events, so they are not received when a GUI is active.
|
||||
4. If no one wanted the event so far, and a :ref:`Camera <class_Camera>` is assigned
|
||||
to the Viewport, a ray to the physics world (in the ray direction from
|
||||
the click) will be cast. If this ray hits an object, it will call the
|
||||
:ref:`CollisionObject._input_event() <class_CollisionObject__input_event>` function in the relevant
|
||||
physics object (bodies receive this callback by default, but areas do
|
||||
not. This can be configured through :ref:`Area <class_Area>` properties).
|
||||
5. Finally, if the event was unhandled, it will be passed to the next
|
||||
Viewport in the tree, otherwise it will be ignored.
|
||||
|
||||
Anatomy of an InputEvent
|
||||
------------------------
|
||||
|
||||
:ref:`InputEvent <class_InputEvent>` is just a base built-in type, it does not represent
|
||||
anything and only contains some basic information, such as event ID
|
||||
(which is increased for each event), device index, etc.
|
||||
|
||||
InputEvent has a "type" member. By assigning it, it can become
|
||||
different types of input event. Every type of InputEvent has different
|
||||
properties, according to its role.
|
||||
|
||||
Example of changing event type.
|
||||
|
||||
::
|
||||
|
||||
# create event
|
||||
var ev = InputEvent()
|
||||
# set type index
|
||||
ev.type = InputEvent.MOUSE_BUTTON
|
||||
# button_index is only available for the above type
|
||||
ev.button_index = BUTTON_LEFT
|
||||
|
||||
There are several types of InputEvent, described in the table below:
|
||||
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| Event | Type Index | Description |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEvent <class_InputEvent>` | NONE | Empty Input Event. |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventKey <class_InputEventKey>` | KEY | Contains a scancode and unicode value, |
|
||||
| | | as well as modifiers. |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventMouseButton <class_InputEventMouseButton>` | MOUSE_BUTTON | Contains click information, such as |
|
||||
| | | button, modifiers, etc. |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventMouseMotion <class_InputEventMouseMotion>` | MOUSE_MOTION | Contains motion information, such as |
|
||||
| | | relative, absolute positions and speed. |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventJoystickMotion <class_InputEventJoystickMotion>` | JOYSTICK_MOTION | Contains Joystick/Joypad analog axis |
|
||||
| | | information. |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventJoystickButton <class_InputEventJoystickButton>` | JOYSTICK_BUTTON | Contains Joystick/Joypad button |
|
||||
| | | information. |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventScreenTouch <class_InputEventScreenTouch>` | SCREEN_TOUCH | Contains multi-touch press/release |
|
||||
| | | information. (only available on mobile |
|
||||
| | | devices) |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventScreenDrag <class_InputEventScreenDrag>` | SCREEN_DRAG | Contains multi-touch drag information. |
|
||||
| | | (only available on mobile devices) |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
| :ref:`InputEventAction <class_InputEventAction>` | SCREEN_ACTION | Contains a generic action. These events |
|
||||
| | | are often generated by the programmer |
|
||||
| | | as feedback. (more on this below) |
|
||||
+-------------------------------------------------------------------+--------------------+-----------------------------------------+
|
||||
|
||||
Actions
|
||||
-------
|
||||
|
||||
An InputEvent may or may not represent a pre-defined action. Actions are
|
||||
useful because they abstract the input device when programming the game
|
||||
logic. This allows for:
|
||||
|
||||
- The same code to work on different devices with different inputs (e.g.,
|
||||
keyboard on PC, Joypad on console).
|
||||
- Input to be reconfigured at run-time.
|
||||
|
||||
Actions can be created from the Project Settings menu in the Actions
|
||||
tab. Read :ref:`doc_simple_2d_game-input_actions_setup` for an
|
||||
explanation on how the action editor works.
|
||||
|
||||
Any event has the methods :ref:`InputEvent.is_action() <class_InputEvent_is_action>`,
|
||||
:ref:`InputEvent.is_pressed() <class_InputEvent_is_pressed>` and :ref:`InputEvent <class_InputEvent>`.
|
||||
|
||||
Alternatively, it may be desired to supply the game back with an action
|
||||
from the game code (a good example of this is detecting gestures).
|
||||
SceneTree (derived from MainLoop) has a method for this:
|
||||
:ref:`MainLoop.input_event() <class_MainLoop_input_event>`. You would normally use it like this:
|
||||
|
||||
::
|
||||
|
||||
var ev = InputEvent()
|
||||
ev.type = InputEvent.ACTION
|
||||
# set as move_left, pressed
|
||||
ev.set_as_action("move_left", true)
|
||||
# feedback
|
||||
get_tree().input_event(ev)
|
||||
|
||||
InputMap
|
||||
--------
|
||||
|
||||
Customizing and re-mapping input from code is often desired. If your
|
||||
whole workflow depends on actions, the :ref:`InputMap <class_InputMap>` singleton is
|
||||
ideal for reassigning or creating different actions at run-time. This
|
||||
singleton is not saved (must be modified manually) and its state is run
|
||||
from the project settings (engine.cfg). So any dynamic system of this
|
||||
type needs to store settings in the way the programmer best sees fit.
|
||||
49
learning/features/inputs/mouse_and_input_coordinates.rst
Normal file
49
learning/features/inputs/mouse_and_input_coordinates.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
.. _doc_mouse_and_input_coordinates:
|
||||
|
||||
Mouse and input coordinates
|
||||
===========================
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
The reason for this small tutorial is to clear up many common mistakes
|
||||
about input coordinates, obtaining mouse position and screen resolution,
|
||||
etc.
|
||||
|
||||
Hardware display coordinates
|
||||
----------------------------
|
||||
|
||||
Using hardware coordinates makes sense in the case of writing complex
|
||||
UIs meant to run on PC, such as editors, MMOs, tools, etc. Yet, it does
|
||||
not make as much sense outside of that scope.
|
||||
|
||||
Viewport display coordinates
|
||||
----------------------------
|
||||
|
||||
Godot uses viewports to display content, and viewports can be scaled by
|
||||
several options (see :ref:`doc_multiple_resolutions` tutorial). Use, then, the
|
||||
functions in nodes to obtain the mouse coordinates and viewport size,
|
||||
for example:
|
||||
|
||||
::
|
||||
|
||||
func _input(ev):
|
||||
# Mouse in viewport coordinates
|
||||
|
||||
if (ev.type==InputEvent.MOUSE_BUTTON):
|
||||
print("Mouse Click/Unclick at: ",ev.pos)
|
||||
elif (ev.type==InputEvent.MOUSE_MOTION):
|
||||
print("Mouse Motion at: ",ev.pos)
|
||||
|
||||
# Print the size of the viewport
|
||||
|
||||
print("Viewport Resolution is: ",get_viewport_rect().size)
|
||||
|
||||
func _ready():
|
||||
set_process_input(true)
|
||||
|
||||
Alternatively it's possible to ask the viewport for the mouse position:
|
||||
|
||||
::
|
||||
|
||||
get_viewport().get_mouse_pos()
|
||||
122
learning/features/lighting/lighting.rst
Normal file
122
learning/features/lighting/lighting.rst
Normal file
@@ -0,0 +1,122 @@
|
||||
.. _doc_lighting:
|
||||
|
||||
Lighting
|
||||
========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Lights emit light that mix with the materials and produces a visible
|
||||
result. Light can come from several types of sources in a scene:
|
||||
|
||||
- From the Material itself, in the form of the emission color (though
|
||||
it does not affect nearby objects unless baked).
|
||||
- Light Nodes: Directional, Omni and Spot.
|
||||
- Ambient Light in the
|
||||
:ref:`Environment <class_Environment>`.
|
||||
- Baked Light (read :ref:`doc_light_baking`).
|
||||
|
||||
The emission color is a material property. You can read more about it
|
||||
in the :ref:`doc_fixed_materials` tutorial.
|
||||
|
||||
Light nodes
|
||||
-----------
|
||||
|
||||
As mentioned before, there are three types of light nodes: Directional,
|
||||
Omni and Spot. Each has different uses and will be described in
|
||||
detail below, but first let's take a look at the common parameters for
|
||||
lights:
|
||||
|
||||
.. image:: /img/light_params.png
|
||||
|
||||
Each one has a specific function:
|
||||
|
||||
- **Enabled**: Light is emitted only if this flag is set.
|
||||
- **Bake Mode**: When using the light baker, the role of this light can
|
||||
be defined in this enumerator. The role will be followed even if the
|
||||
light is disabled, which allows to configure a light and then disable
|
||||
it for baking.
|
||||
- **Energy**: This value is a multiplier for the light, it's specially
|
||||
useful for :ref:`doc_high_dynamic_range` and for Spot and Omni lights, because it can
|
||||
create very bright spots near the emitter.
|
||||
- **Diffuse and Specular**: These light values get multiplied by the
|
||||
material light and diffuse colors. A white value does not mean
|
||||
that light will be white, but that the material color will be kept.
|
||||
- **Operator**: It is possible to make some lights negative for a
|
||||
darkening effect.
|
||||
- **Projector**: Lights can project a texture for the diffuse light
|
||||
(currently only supported in Spot light).
|
||||
|
||||
Directional light
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is the most common type of light and represents a light source
|
||||
very far away (such as the sun). It is also
|
||||
the cheapest light to compute and should be used whenever possible
|
||||
(although it's not the cheapest shadow-map to compute, but more on that
|
||||
later).
|
||||
|
||||
Directional light models an infinite number of parallel light rays
|
||||
covering the whole scene. The directional light node is represented by a big arrow, which
|
||||
indicates the direction of the light rays. However, the position of the node
|
||||
does not affect the lighting at all, and can be anywhere.
|
||||
|
||||
.. image:: /img/light_directional.png
|
||||
|
||||
Every face whose front-side is hit by the light rays is lit, the others stay dark.
|
||||
Most light types
|
||||
have specific parameters but directional lights are pretty simple in
|
||||
nature so they don't.
|
||||
|
||||
Omni light
|
||||
~~~~~~~~~~
|
||||
|
||||
Omni light is a point source that emits light spherically in all directions up to a given
|
||||
radius (distance from the node's position). The radius is a parameter of the light and
|
||||
can be controlled by the user. Just as in real life, the intensity of omni light
|
||||
decreases with the distance and vanishes at the defined radius. Omni light sources
|
||||
should be used to represent lamps or bulbs or any other light source that originates
|
||||
approximately in a point.
|
||||
|
||||
.. image:: /img/light_omni.png
|
||||
|
||||
In reality, the attenuation of omni light is proportional to the squared distance
|
||||
from the point source. This can be easily understood if you imagine a sphere around
|
||||
the omni light with a certain radius. No matter how large the sphere is, the number
|
||||
of rays passing through it is always the same. If the radius of the sphere is doubled,
|
||||
the area of the sphere increases by four. In other words, the density of rays
|
||||
(the number of rays per square area) decreases quadratically with the distance.
|
||||
|
||||
Inverse-quadratic attenuation curves are inconvenient for artists: they
|
||||
never reach zero and have almost infinitely large values near the emitter.
|
||||
So Godot simulates omni light with an artist-controlled exponential curve
|
||||
instead.
|
||||
|
||||
.. image:: /img/light_attenuation.png
|
||||
|
||||
Spot light
|
||||
~~~~~~~~~~
|
||||
|
||||
Spot lights are similar to omni lights, except they emit light only into a cone
|
||||
(or "cutoff"). They are useful to simulate flashlights,
|
||||
car lights, etc. This kind of light is also attenuated towards the
|
||||
opposite direction it points to.
|
||||
|
||||
.. image:: /img/light_spot.png
|
||||
|
||||
Ambient light
|
||||
-------------
|
||||
|
||||
Ambient light can be found in the properties of a WorldEnvironment
|
||||
(remember only one of such can be instanced per scene). Ambient light
|
||||
consists of a uniform light and energy. This light is applied the same
|
||||
to every single pixel of the rendered scene, except to objects that used
|
||||
baked light.
|
||||
|
||||
Baked light
|
||||
-----------
|
||||
|
||||
Baked light stands for pre-computed ambient light. It can serve multiple
|
||||
purposes, such as baking light emitters that are not going to be used in
|
||||
real-time, and baking light bounces from real-time lights to add more
|
||||
realism to a scene (see :ref:`doc_light_baking` tutorial for more information).
|
||||
188
learning/features/lighting/shadow_mapping.rst
Normal file
188
learning/features/lighting/shadow_mapping.rst
Normal file
@@ -0,0 +1,188 @@
|
||||
.. _doc_shadow_mapping:
|
||||
|
||||
Shadow mapping
|
||||
==============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Simply throwing a light is not enough to realistically illuminate a
|
||||
scene. It should be, in theory, but given the way video hardware
|
||||
works, parts of objects that should not be reached by light are lit
|
||||
anyway.
|
||||
|
||||
Most people (including artists), see shadows as something projected by
|
||||
light, as if they were created by the light itself by darkening places
|
||||
that are hidden from the light source.
|
||||
|
||||
This is actually not correct and it's important to understand that
|
||||
shadows are places where light simply does not reach. As a rule (and
|
||||
without counting indirect light) if a light is turned off, the places
|
||||
where shadow appear should remain the same. In other words, shadows
|
||||
should not be seen as something "added" to the scene, but as an area
|
||||
that "remains dark".
|
||||
|
||||
All light types in Godot can use shadow mapping, and all support several
|
||||
different techniques that trade quality by performance. Shadow mapping
|
||||
uses a texture storing the "depth view" of the light and checks against
|
||||
it in real-time for each pixel it renders.
|
||||
|
||||
The bigger the resolution of the shadow map texture, the more detail the
|
||||
shadow has, but more video memory and bandwidth consumed (which means
|
||||
frame-rate goes down).
|
||||
|
||||
Shadows by light type
|
||||
---------------------
|
||||
|
||||
Directional light shadows
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Directional lights can affect a really big area. The bigger the scene,
|
||||
the bigger the affected area. Given the shadow map resolution stays the
|
||||
same, the same amount of shadow pixels cover a bigger area, resulting in
|
||||
blocky shadows. Multiple techniques exist to deal with resolution
|
||||
problems, but the most common one is PSSM (Parallel Split Shadow Maps):
|
||||
|
||||
.. image:: /img/shadow_directional.png
|
||||
|
||||
These techniques divide the view in 2 or 4 sections, and a shadow is
|
||||
rendered for each. This way, close objects can use larger shadow while
|
||||
further away objects will use one in less detail, but in proportion this
|
||||
seems to make the shadow map size increase while it's actually kept the
|
||||
same. Of course, this technique is not free, the more splits the more
|
||||
the performance goes down. On mobile, it is generally inconvenient to
|
||||
use more than 2 splits.
|
||||
|
||||
An alternative technique is PSM (Perspective Shadow Mapping). This
|
||||
technique is much cheaper than PSSM (as cheap as orthogonal), but it
|
||||
only really works for a few camera angles respect to the light. In other
|
||||
words, PSM is only useful for games where the camera direction and light
|
||||
direction are both fixed, and the light is not parallel to the camera
|
||||
(which is when PSM completely breaks).
|
||||
|
||||
Omni light shadows
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Omnidirectional lights are also troublesome. How to represent 360 degrees
|
||||
of light with a single texture? There are two alternatives, the first
|
||||
one is to use DPSM (Dual Paraboloid Shadow Mapping). This technique is
|
||||
fast, but it requires DISCARD to be used (which makes it not very usable
|
||||
on mobile). DPSM can also look rather bad if the geometry is not
|
||||
tessellated enough, so more vertices might be necessary if it doesn't
|
||||
look tight. The second option is to simply not use a shadow map, and use
|
||||
a shadow cubemap. This is faster, but requires six passes to render all
|
||||
directions and is not supported on the current (GLES2) renderer.
|
||||
|
||||
.. image:: /img/shadow_omni.png
|
||||
|
||||
As few considerations when using DPSM shadow maps:
|
||||
|
||||
- Keep Slope-Scale on 0.
|
||||
- Use a small value for Z-Offset, if this look wrong, make it smaller.
|
||||
- ESM filtering can improve the look.
|
||||
- The seams between the two halves of the shadow are generally
|
||||
noticeable, so rotate the light to make them show less.
|
||||
|
||||
Spot light shadows
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Spot light shadows are generally the simpler, just needing a single
|
||||
texture and no special techniques.
|
||||
|
||||
.. image:: /img/shadow_spot.png
|
||||
|
||||
Shadows parameters
|
||||
------------------
|
||||
|
||||
The fact that shadows are actually a texture can generate several
|
||||
problems. The most common is Z fighting (lines at the edge of the
|
||||
objects that cast the shadows. There are two ways to fix this, the first
|
||||
is to tweak the offset parameters, and the second is to use a filtered
|
||||
shadow algorithm, which generally looks better and has not as many
|
||||
glitches, but consumes more GPU time.
|
||||
|
||||
Adjusting z-offset
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
So, you have decided to go with non-filtered shadows because they are
|
||||
faster, you want a little more detail or maybe you just like the sexy
|
||||
saw-like shadow outlines because they remind you of your favorite
|
||||
previous-gen games. Truth is, this can be kind of be a pain, but most of the
|
||||
time it can be adjusted to have nice results. There is no magic number and
|
||||
whatever result you come up will be different from scene to scene, it
|
||||
just takes a while of tweaking. Let's go step by step.
|
||||
|
||||
First step is to turn on the shadows, let's assume that both Z-Offset
|
||||
and Z-Slope-Scale are at 0. You will be greeted by this:
|
||||
|
||||
.. image:: /img/shadow_offset_1.png
|
||||
|
||||
Holy crap, the shadow is all over the place and extremely glitchy! This
|
||||
happens because the shadow is fighting with the same geometry that is
|
||||
casting it. This is called "self-shadowing". To avoid this meaningless
|
||||
fight, you realize you need to make peace between the shadow and the
|
||||
geometry, so you push back the shadow a little by increasing the shadow
|
||||
Z-Offset. This improves things a lot:
|
||||
|
||||
.. image:: /img/shadow_offset_2.png
|
||||
|
||||
But it's not quite perfect, self shadowing did not disappear completely.
|
||||
So close to perfection but still not there.. so in a turn of greed you
|
||||
increase the Z-Offset even more!
|
||||
|
||||
.. image:: /img/shadow_offset_3.png
|
||||
|
||||
And it gets rid of those self-shadowings! Hooray! Except something is
|
||||
wrong.. oh, right. Being pushed back too much, the shadows start
|
||||
disconnecting from their casters, which looks pretty awful. Ok, you go
|
||||
back to the previous Z-offset.
|
||||
|
||||
This is when Z-Slope-Scale comes to save the day. This setting makes
|
||||
shadow caster objects thinner, so the borders don't self-shadow:
|
||||
|
||||
.. image:: /img/shadow_offset_4.png
|
||||
|
||||
Aha! Finally something that looks acceptable. It's perfectly acceptable
|
||||
and you can perfectly ship a game that looks like this (imagine you are
|
||||
looking at Final Fantasy quality art btw, not this horrible attempt at 3D
|
||||
modelling). There may be very tiny bits left of self shadowing that no
|
||||
one cares about, so your inextinguishable greed kicks in again and you
|
||||
raise the Z-Slope Scale again:
|
||||
|
||||
.. image:: /img/shadow_offset_5.png
|
||||
|
||||
Well, that was too much, shadows casted are way too thin and don't look
|
||||
good anymore. Well, though luck, the previous setting was good anyway,
|
||||
let's accept that perfection does not exist and move on to something
|
||||
else.
|
||||
|
||||
Important!
|
||||
~~~~~~~~~~
|
||||
|
||||
If you are using shadow maps with directional lights, make sure that
|
||||
the *view distance* of the *camera* is set to an *optimal range*. This
|
||||
means, if the distance between your camera and the visible end of the
|
||||
scene is 100, then set the view distance to that value. If a greater
|
||||
than necessary value is used, the shadow maps will lose detail as they
|
||||
will try to cover a bigger area.
|
||||
|
||||
So, always make sure to use the optimal range!
|
||||
|
||||
Shadow filtering
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Raw shadows are blocky. Increasing their resolution just makes smaller
|
||||
blocks, but they are still blocks.
|
||||
|
||||
Godot offers a few ways to filter them (shadow in the example is
|
||||
low-resolution on purpose!):
|
||||
|
||||
.. image:: /img/shadow_filter_options.png
|
||||
|
||||
PCF5 and PCF13 are simple texture-space filtering. Will make the texture
|
||||
a little more acceptable but still needs considerable resolution for it
|
||||
to look good.
|
||||
|
||||
ESM is a more complex filter and has a few more tweaking parameters. ESM
|
||||
uses shadow blurring (amount of blur passes and multiplier can be
|
||||
adjusted).
|
||||
502
learning/features/math/matrices_and_transforms.rst
Normal file
502
learning/features/math/matrices_and_transforms.rst
Normal file
@@ -0,0 +1,502 @@
|
||||
.. _doc_matrices_and_transforms:
|
||||
|
||||
Matrices and transforms
|
||||
=======================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Before reading this tutorial, it is advised to read the previous one
|
||||
about :ref:`doc_vector_math` as this one is a direct continuation.
|
||||
|
||||
This tutorial will be about *transformations* and will cover a little
|
||||
about matrices (but not in-depth).
|
||||
|
||||
Transformations are most of the time applied as translation, rotation
|
||||
and scale so they will be considered as priority here.
|
||||
|
||||
Oriented coordinate system (OCS)
|
||||
--------------------------------
|
||||
|
||||
Imagine we have a spaceship somewhere in space. In Godot this is easy,
|
||||
just move the ship somewhere and rotate it:
|
||||
|
||||
.. image:: /img/tutomat1.png
|
||||
|
||||
Ok, so in 2D this looks simple, a position and an angle for a rotation.
|
||||
But remember, we are grown ups here and don't use angles (plus, angles
|
||||
are not really even that useful when working in 3D).
|
||||
|
||||
We should realize that at some point, someone *designed* this
|
||||
spaceship. Be it for 2D in a drawing such as Paint.net, Gimp,
|
||||
Photoshop, etc. or in 3D through a 3D DCC tool such as Blender, Max,
|
||||
Maya, etc.
|
||||
|
||||
When it was designed, it was not rotated. It was designed in its own
|
||||
*coordinate system*.
|
||||
|
||||
.. image:: /img/tutomat2.png
|
||||
|
||||
This means that the tip of the ship has a coordinate, the fin has
|
||||
another, etc. Be it in pixels (2D) or vertices (3D).
|
||||
|
||||
So, let's recall again that the ship was somewhere in space:
|
||||
|
||||
.. image:: /img/tutomat3.png
|
||||
|
||||
How did it get there? What moved it and rotated it from the place it was
|
||||
designed to its current position? The answer is... a **transform**, the
|
||||
ship was *transformed* from their original position to the new one. This
|
||||
allows the ship to be displayed where it is.
|
||||
|
||||
But transform is too generic of a term to describe this process. To solve this
|
||||
puzzle, we will superimpose the ship's original design position at their
|
||||
current position:
|
||||
|
||||
.. image:: /img/tutomat4.png
|
||||
|
||||
So, we can see that the "design space" has been transformed too. How can
|
||||
we best represent this transformation? Let's use 3 vectors for this (in
|
||||
2D), a unit vector pointing towards X positive, a unit vector pointing
|
||||
towards Y positive and a translation.
|
||||
|
||||
.. image:: /img/tutomat5.png
|
||||
|
||||
Let's call the 3 vectors "X", "Y" and "Origin", and let's also
|
||||
superimpose them over the ship so it makes more sense:
|
||||
|
||||
.. image:: /img/tutomat6.png
|
||||
|
||||
Ok, this is nicer, but it still does not make sense. What do X,Y and
|
||||
Origin have to do with how the ship got there?
|
||||
|
||||
Well, let's take the point from top tip of the ship as reference:
|
||||
|
||||
.. image:: /img/tutomat7.png
|
||||
|
||||
And let's apply the following operation to it (and to all the points in
|
||||
the ship too, but we'll track the top tip as our reference point):
|
||||
|
||||
::
|
||||
|
||||
var new_pos = pos - origin
|
||||
|
||||
Doing this to the selected point will move it back to the center:
|
||||
|
||||
.. image:: /img/tutomat8.png
|
||||
|
||||
This was expected, but then let's do something more interesting. Use the
|
||||
dot product of X and the point, and add it to the dot product of Y and
|
||||
the point:
|
||||
|
||||
::
|
||||
|
||||
var final_pos = x.dot(new_pos) + y.dot(new_pos)
|
||||
|
||||
Then what we have is.. wait a minute, it's the ship in its design
|
||||
position!
|
||||
|
||||
.. image:: /img/tutomat9.png
|
||||
|
||||
How did this black magic happen? The ship was lost in space, and now
|
||||
it's back home!
|
||||
|
||||
It might seem strange, but it does have plenty of logic. Remember, as
|
||||
we have seen in the :ref:`doc_vector_math`, what
|
||||
happened is that the distance to X axis, and the distance to Y axis
|
||||
were computed. Calculating distance in a direction or plane was one of
|
||||
the uses for the dot product. This was enough to obtain back the
|
||||
design coordinates for every point in the ship.
|
||||
|
||||
So, what he have been working with so far (with X, Y and Origin) is an
|
||||
*Oriented Coordinate System\*. X an Y are the **Basis**, and \*Origin*
|
||||
is the offset.
|
||||
|
||||
Basis
|
||||
-----
|
||||
|
||||
We know what the Origin is. It's where the 0,0 (origin) of the design
|
||||
coordinate system ended up after being transformed to a new position.
|
||||
This is why it's called *Origin*, But in practice, it's just an offset
|
||||
to the new position.
|
||||
|
||||
The Basis is more interesting. The basis is the direction of X and Y in the OCS
|
||||
from the new, transformed location. It tells what has changed, in either 2D or
|
||||
3D. The Origin (offset) and Basis (direction) communicate "Hey, the original X
|
||||
and Y axes of your design are *right here*, pointing towards *these
|
||||
directions*."
|
||||
|
||||
So, let's change the representation of the basis. Instead of 2 vectors,
|
||||
let's use a *matrix*.
|
||||
|
||||
.. image:: /img/tutomat10.png
|
||||
|
||||
The vectors are up there in the matrix, horizontally. The next problem
|
||||
now is that.. what is this matrix thing? Well, we'll assume you've never
|
||||
heard of a matrix.
|
||||
|
||||
Transforms in Godot
|
||||
-------------------
|
||||
|
||||
This tutorial will not explain matrix math (and their operations) in
|
||||
depth, only its practical use. There is plenty of material for that,
|
||||
which should be a lot simpler to understand after completing this
|
||||
tutorial. We'll just explain how to use transforms.
|
||||
|
||||
Matrix32
|
||||
--------
|
||||
|
||||
:ref:`Matrix32 <class_Matrix32>` is a 3x2 matrix. It has 3 Vector2 elements and
|
||||
it's used for 2D. The "X" axis is the element 0, "Y" axis is the element 1 and
|
||||
"Origin" is element 2. It's not divided in basis/origin for convenience, due to
|
||||
its simplicity.
|
||||
|
||||
::
|
||||
|
||||
var m = Matrix32()
|
||||
var x = m[0] # 'X'
|
||||
var y = m[1] # 'Y'
|
||||
var o = m[2] # 'Origin'
|
||||
|
||||
|
||||
Most operations will be explained with this datatype (Matrix32), but the
|
||||
same logic applies to 3D.
|
||||
|
||||
Identity
|
||||
--------
|
||||
|
||||
By default, Matrix32 is created as an "identity" matrix. This means:
|
||||
|
||||
- 'X' Points right: Vector2(1,0)
|
||||
- 'Y' Points up (or down in pixels): Vector2(0,1)
|
||||
- 'Origin' is the origin Vector2(0,0)
|
||||
|
||||
.. image:: /img/tutomat11.png
|
||||
|
||||
It's easy to guess that an *identity* matrix is just a matrix that
|
||||
aligns the transform to its parent coordinate system. It's an *OCS*
|
||||
that hasn't been translated, rotated or scaled. All transform types in
|
||||
Godot are created with *identity*.
|
||||
|
||||
Operations
|
||||
----------
|
||||
|
||||
Rotation
|
||||
--------
|
||||
|
||||
Rotating Matrix32 is done by using the "rotated" function:
|
||||
|
||||
::
|
||||
|
||||
var m = Matrix32()
|
||||
m = m.rotated(PI/2) # rotate 90°
|
||||
|
||||
.. image:: /img/tutomat12.png
|
||||
|
||||
Translation
|
||||
-----------
|
||||
|
||||
There are two ways to translate a Matrix32, the first one is just moving
|
||||
the origin:
|
||||
|
||||
::
|
||||
|
||||
# Move 2 units to the right
|
||||
var m = Matrix32()
|
||||
m = m.rotated(PI/2) # rotate 90°
|
||||
m[2]+=Vector2(2,0)
|
||||
|
||||
.. image:: /img/tutomat13.png
|
||||
|
||||
This will always work in global coordinates.
|
||||
|
||||
If instead, translation is desired in *local* coordinates of the
|
||||
matrix (towards where the *basis* is oriented), there is the
|
||||
:ref:`Matrix32.translated() <class_Matrix32_translated>`
|
||||
method:
|
||||
|
||||
::
|
||||
|
||||
# Move 2 units towards where the basis is oriented
|
||||
var m = Matrix32()
|
||||
m = m.rotated(PI/2) # rotate 90°
|
||||
m=m.translated( Vector2(2,0) )
|
||||
|
||||
.. image:: /img/tutomat14.png
|
||||
|
||||
Scale
|
||||
-----
|
||||
|
||||
A matrix can be scaled too. Scaling will multiply the basis vectors by a
|
||||
vector (X vector by x component of the scale, Y vector by y component of
|
||||
the scale). It will leave the origin alone:
|
||||
|
||||
::
|
||||
|
||||
# Make the basis twice its size.
|
||||
var m = Matrix32()
|
||||
m = m.scaled( Vector2(2,2) )
|
||||
|
||||
.. image:: /img/tutomat15.png
|
||||
|
||||
These kind of operations in matrices are accumulative. It means every
|
||||
one starts relative to the previous one. For those who have been living
|
||||
on this planet long enough, a good reference of how transform works is
|
||||
this:
|
||||
|
||||
.. image:: /img/tutomat16.png
|
||||
|
||||
A matrix is used similarly to a turtle. The turtle most likely had a
|
||||
matrix inside (and you are likely learning this many years *after*
|
||||
discovering Santa is not real).
|
||||
|
||||
Transform
|
||||
---------
|
||||
|
||||
Transform is the act of switching between coordinate systems. To convert
|
||||
a position (either 2D or 3D) from "designer" coordinate system to the
|
||||
OCS, the "xform" method is used.
|
||||
|
||||
::
|
||||
|
||||
var new_pos = m.xform(pos)
|
||||
|
||||
And only for basis (no translation):
|
||||
|
||||
::
|
||||
|
||||
var new_pos = m.basis_xform(pos)
|
||||
|
||||
Post - multiplying is also valid:
|
||||
|
||||
::
|
||||
|
||||
var new_pos = m * pos
|
||||
|
||||
Inverse transform
|
||||
-----------------
|
||||
|
||||
To do the opposite operation (what we did up there with the rocket), the
|
||||
"xform_inv" method is used:
|
||||
|
||||
::
|
||||
|
||||
var new_pos = m.xform_inv(pos)
|
||||
|
||||
Only for Basis:
|
||||
|
||||
::
|
||||
|
||||
var new_pos = m.basis_xform_inv(pos)
|
||||
|
||||
Or pre-multiplication:
|
||||
|
||||
::
|
||||
|
||||
var new_pos = pos * m
|
||||
|
||||
Orthonormal matrices
|
||||
--------------------
|
||||
|
||||
However, if the Matrix has been scaled (vectors are not unit length),
|
||||
or the basis vectors are not orthogonal (90°), the inverse transform
|
||||
will not work.
|
||||
|
||||
In other words, inverse transform is only valid in *orthonormal*
|
||||
matrices. For this, these cases an affine inverse must be computed.
|
||||
|
||||
The transform, or inverse transform of an identity matrix will return
|
||||
the position unchanged:
|
||||
|
||||
::
|
||||
|
||||
# Does nothing, pos is unchanged
|
||||
pos = Matrix32().xform(pos)
|
||||
|
||||
Affine inverse
|
||||
--------------
|
||||
|
||||
The affine inverse is a matrix that does the inverse operation of
|
||||
another matrix, no matter if the matrix has scale or the axis vectors
|
||||
are not orthogonal. The affine inverse is calculated with the
|
||||
affine_inverse() method:
|
||||
|
||||
::
|
||||
|
||||
var mi = m.affine_inverse()
|
||||
var pos = m.xform(pos)
|
||||
pos = mi.xform(pos)
|
||||
# pos is unchanged
|
||||
|
||||
If the matrix is orthonormal, then:
|
||||
|
||||
::
|
||||
|
||||
# if m is orthonormal, then
|
||||
pos = mi.xform(pos)
|
||||
# is the same is
|
||||
pos = m.xform_inv(pos)
|
||||
|
||||
Matrix multiplication
|
||||
---------------------
|
||||
|
||||
Matrices can be multiplied. Multiplication of two matrices "chains"
|
||||
(concatenates) their transforms.
|
||||
|
||||
However, as per convention, multiplication takes place in reverse
|
||||
order.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
var m = more_transforms * some_transforms
|
||||
|
||||
To make it a little clearer, this:
|
||||
|
||||
::
|
||||
|
||||
pos = transform1.xform(pos)
|
||||
pos = transform2.xform(pos)
|
||||
|
||||
Is the same as:
|
||||
|
||||
::
|
||||
|
||||
# note the inverse order
|
||||
pos = (transform2 * transform1).xform(pos)
|
||||
|
||||
However, this is not the same:
|
||||
|
||||
::
|
||||
|
||||
# yields a different results
|
||||
pos = (transform1 * transform2).xform(pos)
|
||||
|
||||
Because in matrix math, A + B is not the same as B + A.
|
||||
|
||||
Multiplication by inverse
|
||||
-------------------------
|
||||
|
||||
Multiplying a matrix by its inverse, results in identity
|
||||
|
||||
::
|
||||
|
||||
# No matter what A is, B will be identity
|
||||
B = A.affine_inverse() * A
|
||||
|
||||
Multiplication by identity
|
||||
--------------------------
|
||||
|
||||
Multiplying a matrix by identity, will result in the unchanged matrix:
|
||||
|
||||
::
|
||||
|
||||
# B will be equal to A
|
||||
B = A * Matrix32()
|
||||
|
||||
Matrix tips
|
||||
-----------
|
||||
|
||||
When using a transform hierarchy, remember that matrix multiplication is
|
||||
reversed! To obtain the global transform for a hierarchy, do:
|
||||
|
||||
::
|
||||
|
||||
var global_xform = parent_matrix * child_matrix
|
||||
|
||||
For 3 levels:
|
||||
|
||||
::
|
||||
|
||||
# due to reverse order, parenthesis are needed
|
||||
var global_xform = gradparent_matrix + (parent_matrix + child_matrix)
|
||||
|
||||
To make a matrix relative to the parent, use the affine inverse (or
|
||||
regular inverse for orthonormal matrices).
|
||||
|
||||
::
|
||||
|
||||
# transform B from a global matrix to one local to A
|
||||
var B_local_to_A = A.affine_inverse() * B
|
||||
|
||||
Revert it just like the example above:
|
||||
|
||||
::
|
||||
|
||||
# transform back local B to global B
|
||||
var B = A * B_local_to_A
|
||||
|
||||
OK, hopefully this should be enough! Let's complete the tutorial by
|
||||
moving to 3D matrices.
|
||||
|
||||
Matrices & transforms in 3D
|
||||
---------------------------
|
||||
|
||||
As mentioned before, for 3D, we deal with 3 :ref:`Vector3 <class_Vector3>`
|
||||
vectors for the rotation matrix, and an extra one for the origin.
|
||||
|
||||
Matrix3
|
||||
-------
|
||||
|
||||
Godot has a special type for a 3x3 matrix, named :ref:`Matrix3 <class_Matrix3>`.
|
||||
It can be used to represent a 3D rotation and scale. Sub vectors can be
|
||||
accessed as:
|
||||
|
||||
::
|
||||
|
||||
var m = Matrix3()
|
||||
var x = m[0] # Vector3
|
||||
var y = m[1] # Vector3
|
||||
var z = m[2] # Vector3
|
||||
|
||||
Or, alternatively as:
|
||||
|
||||
::
|
||||
|
||||
var m = Matrix3()
|
||||
var x = m.x # Vector3
|
||||
var y = m.y # Vector3
|
||||
var z = m.z # Vector3
|
||||
|
||||
Matrix3 is also initialized to Identity by default:
|
||||
|
||||
.. image:: /img/tutomat17.png
|
||||
|
||||
Rotation in 3D
|
||||
--------------
|
||||
|
||||
Rotation in 3D is more complex than in 2D (translation and scale are the
|
||||
same), because rotation is an implicit 2D operation. To rotate in 3D, an
|
||||
*axis*, must be picked. Rotation, then, happens around this axis.
|
||||
|
||||
The axis for the rotation must be a *normal vector*. As in, a vector
|
||||
that can point to any direction, but length must be one (1.0).
|
||||
|
||||
::
|
||||
|
||||
#rotate in Y axis
|
||||
var m3 = Matrix3()
|
||||
m3 = m3.rotated( Vector3(0,1,0), PI/2 )
|
||||
|
||||
Transform
|
||||
---------
|
||||
|
||||
To add the final component to the mix, Godot provides the
|
||||
:ref:`Transform <class_Transform>` type. Transform has two members:
|
||||
|
||||
- *basis* (of type :ref:`Matrix3 <class_Matrix3>`
|
||||
- *origin* (of type :ref:`Vector3 <class_Vector3>`
|
||||
|
||||
Any 3D transform can be represented with Transform, and the separation
|
||||
of basis and origin makes it easier to work translation and rotation
|
||||
separately.
|
||||
|
||||
An example:
|
||||
|
||||
::
|
||||
|
||||
var t = Transform()
|
||||
pos = t.xform(pos) # transform 3D position
|
||||
pos = t.basis.xform(pos) # (only rotate)
|
||||
pos = t.origin + pos (only translate)
|
||||
897
learning/features/math/vector_math.rst
Normal file
897
learning/features/math/vector_math.rst
Normal file
@@ -0,0 +1,897 @@
|
||||
.. _doc_vector_math:
|
||||
|
||||
Vector math
|
||||
===========
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This small tutorial aims to be a short and practical introduction to
|
||||
vector math, useful for 3D but also 2D games. Again, vector math is not
|
||||
only useful for 3D but *also* 2D games. It is an amazing tool once you
|
||||
get the grasp of it and makes programming of complex behaviors much
|
||||
simpler.
|
||||
|
||||
It often happens that young programmers rely too much on the *incorrect*
|
||||
math for solving a wide array of problems, for example using only
|
||||
trigonometry instead of vector of math for 2D games.
|
||||
|
||||
This tutorial will focus on practical usage, with immediate application
|
||||
to the art of game programming.
|
||||
|
||||
Coordinate systems (2D)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Typically, we define coordinates as an (x,y) pair, x representing the
|
||||
horizontal offset and y the vertical one. This makes sense given the
|
||||
screen is just a rectangle in two dimensions. As an example, here is a
|
||||
position in 2D space:
|
||||
|
||||
.. image:: /img/tutovec1.png
|
||||
|
||||
A position can be anywhere in space. The position (0,0) has a name, it's
|
||||
called the **origin**. Remember this term well because it has more
|
||||
implicit uses later. The (0,0) of a n-dimensions coordinate system is
|
||||
the **origin**.
|
||||
|
||||
In vector math, coordinates have two different uses, both equally
|
||||
important. They are used to represent a *position* but also a *vector*.
|
||||
The same position as before, when imagined as a vector, has a different
|
||||
meaning.
|
||||
|
||||
.. image:: /img/tutovec2.png
|
||||
|
||||
When imagined as a vector, two properties can be inferred, the
|
||||
**direction** and the **magnitude**. Every position in space can be a
|
||||
vector, with the exception of the **origin**. This is because
|
||||
coordinates (0,0) can't represent direction (magnitude 0).
|
||||
|
||||
.. image:: /img/tutovec2b.png
|
||||
|
||||
Direction
|
||||
---------
|
||||
|
||||
Direction is simply towards where the vector points to. Imagine an arrow
|
||||
that starts at the **origin** and goes towards a [STRIKEOUT:position].
|
||||
The tip of the arrow is in the position, so it always points outwards,
|
||||
away from the origin. Imagining vectors as arrows helps a lot.
|
||||
|
||||
.. image:: /img/tutovec3b.png
|
||||
|
||||
Magnitude
|
||||
---------
|
||||
|
||||
Finally, the length of the vector is the distance from the origin to the
|
||||
position. Obtaining the length from a vector is easy, just use the
|
||||
`Pythagorean
|
||||
Theorem <http://en.wikipedia.org/wiki/Pythagorean_theorem>`__.
|
||||
|
||||
::
|
||||
|
||||
var len = sqrt( x*x + y*y )
|
||||
|
||||
But... angles?
|
||||
--------------
|
||||
|
||||
But why not using an *angle*? After all, we could also think of a vector
|
||||
as an angle and a magnitude, instead of a direction and a magnitude.
|
||||
Angles also are a more familiar concept.
|
||||
|
||||
To say truth, angles are not that useful in vector math, and most of the
|
||||
time they are not dealt with directly. Maybe they work in 2D, but in 3D
|
||||
a lot of what can usually be done with angles does not work anymore.
|
||||
|
||||
Still, using angles is still not an excuse, even for 2D. Most of what
|
||||
takes a lot of work with angles in 2D, is still much more natural easier
|
||||
to accomplish with vector math. In vector math, angles are useful only
|
||||
as measure, but take little part in the math. So, give up the
|
||||
trigonometry already, prepare to embrace vectors!
|
||||
|
||||
In any case, obtaining an angle from a vector is easy and can be
|
||||
accomplished with trig... er, what was that? I mean, the
|
||||
:ref:`atan2() <class_@GDScript_atan2>` function.
|
||||
|
||||
Vectors in Godot
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
To make examples easier, it is worth explaining how vectors are
|
||||
implemented in GDScript. GDscript has both
|
||||
:ref:`Vector2 <class_Vector2>` and :ref:`Vector3 <class_Vector3>`,
|
||||
for 2D and 3D math respectively. Godot uses Vector classes as both
|
||||
position and direction. They also contain x and y (for 2D) and x, y and
|
||||
z (for 3D) member variables.
|
||||
|
||||
::
|
||||
|
||||
# create a vector with coordinates (2,5)
|
||||
var a = Vector2(2,5)
|
||||
# create a vector and assign x and y manually
|
||||
var b = Vector2()
|
||||
b.x = 7
|
||||
b.y = 8
|
||||
|
||||
When operating with vectors, it is not necessary to operate on the
|
||||
members directly (in fact this is much slower). Vectors support regular
|
||||
arithmetic operations:
|
||||
|
||||
::
|
||||
|
||||
# add a and b
|
||||
var c = a + b
|
||||
# will result in c vector, with value (9,13)
|
||||
|
||||
It is the same as doing:
|
||||
|
||||
::
|
||||
|
||||
var c = Vector2()
|
||||
c.x = a.x + b.x
|
||||
c.y = a.y + b.y
|
||||
|
||||
Except the former is way more efficient and readable.
|
||||
|
||||
Regular arithmetic operations such as addition, subtraction,
|
||||
multiplication and division are supported.
|
||||
|
||||
Vector multiplication and division can also be mixed with single-digit
|
||||
numbers, also named **scalars**.
|
||||
|
||||
::
|
||||
|
||||
# multiplication of vector by scalar
|
||||
var c = a*2.0
|
||||
# will result in c vector, with value (4,10)
|
||||
|
||||
Which is the same as doing
|
||||
|
||||
::
|
||||
|
||||
var c = Vector2()
|
||||
c.x = a.x*2.0
|
||||
c.y = a.y*2.0
|
||||
|
||||
Except, again, the former is way more efficient and readable.
|
||||
|
||||
Perpendicular vectors
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Rotating a 2D vector 90° degrees to either side, left or right, is
|
||||
really easy, just swap x and y, then negate either x or y (direction of
|
||||
rotation depends on which is negated).
|
||||
|
||||
.. image:: /img/tutovec15.png
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
var v = Vector2(0,1)
|
||||
|
||||
# rotate right (clockwise)
|
||||
var v_right = Vector2(v.y, -v.x)
|
||||
|
||||
# rotate left (counter-clockwise)
|
||||
var v_left = Vector2(-v.y, v.x)
|
||||
|
||||
This is a handy trick that is often of use. It is impossible to do with
|
||||
3D vectors, because there are an infinite amount of perpendicular
|
||||
vectors.
|
||||
|
||||
Unit vectors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Ok, so we know what a vector is. It has a **direction** and a
|
||||
**magnitude**. We also know how to use them in Godot. The next step is
|
||||
learning about **unit vectors**. Any vector with **magnitude** of length
|
||||
1 is considered a **unit vector**. In 2D, imagine drawing a circle of
|
||||
radius one. That circle contains all unit vectors in existence for 2
|
||||
dimensions:
|
||||
|
||||
.. image:: /img/tutovec3.png
|
||||
|
||||
So, what is so special about unit vectors? Unit vectors are amazing. In
|
||||
other words, unit vectors have **several, very useful properties**.
|
||||
|
||||
Can't wait to know more about the fantastic properties of unit vectors,
|
||||
but one step at a time. So, how is a unit vector created from a regular
|
||||
vector?
|
||||
|
||||
Normalization
|
||||
-------------
|
||||
|
||||
Taking any vector and reducing its **magnitude** to 1.0 while keeping
|
||||
its **direction** is called **normalization**. Normalization is
|
||||
performed by dividing the x and y (and z in 3D) components of a vector
|
||||
by its magnitude:
|
||||
|
||||
::
|
||||
|
||||
var a = Vector2(2,4)
|
||||
var m = sqrt(a.x*a.x + a.y*a.y)
|
||||
a.x /= m
|
||||
a.y /= m
|
||||
|
||||
As you might have guessed, if the vector has magnitude 0 (meaning, it's
|
||||
not a vector but the **origin** also called *null vector*), a division
|
||||
by zero occurs and the universe goes through a second big bang, except
|
||||
in reverse polarity and then back. As a result, humanity is safe but
|
||||
Godot will print an error. Remember! Vector(0,0) can't be normalized!.
|
||||
|
||||
Of course, Vector2 and Vector3 already provide a method to do this:
|
||||
|
||||
::
|
||||
|
||||
a = a.normalized()
|
||||
|
||||
Dot product
|
||||
~~~~~~~~~~~
|
||||
|
||||
OK, the **dot product** is the most important part of vector math.
|
||||
Without the dot product, Quake would have never been made. This is the
|
||||
most important section of the tutorial, so make sure to grasp it
|
||||
properly. Most people trying to understand vector math give up here
|
||||
because, despite how simple it is, they can't make head or tails from
|
||||
it. Why? Here's why, it's because...
|
||||
|
||||
The dot product takes two vectors and returns a **scalar**:
|
||||
|
||||
::
|
||||
|
||||
var s = a.x*b.x + a.y*b.y
|
||||
|
||||
Yes, pretty much that. Multiply **x** from vector **a** by **x** from
|
||||
vector **b**. Do the same with y and add it together. In 3D it's pretty
|
||||
much the same:
|
||||
|
||||
::
|
||||
|
||||
var s = a.x*b.x + a.y*b.y + a.z*b.z
|
||||
|
||||
I know, it's totally meaningless! You can even do it with a built-in
|
||||
function:
|
||||
|
||||
::
|
||||
|
||||
var s = a.dot(b)
|
||||
|
||||
The order of two vectors does *not* matter, ``a.dot(b)`` returns the
|
||||
same value as ``b.dot(a)``.
|
||||
|
||||
This is where despair begins and books and tutorials show you this
|
||||
formula:
|
||||
|
||||
.. image:: /img/tutovec4.png
|
||||
|
||||
And you realize it's time to give up making 3D games or complex 2D
|
||||
games. How can something so simple be so complex? Someone else will have
|
||||
to make the next Zelda or Call of Duty. Top down RPGs don't look so bad
|
||||
after all. Yeah I hear someone did pretty will with one of those on
|
||||
Steam...
|
||||
|
||||
So this is your moment, this is your time to shine. **DO NOT GIVE UP**!
|
||||
At this point, this tutorial will take a sharp turn and focus on what
|
||||
makes the dot product useful. This is, **why** it is useful. We will
|
||||
focus one by one in the use cases for the dot product, with real-life
|
||||
applications. No more formulas that don't make any sense. Formulas will
|
||||
make sense *once you learn* what they are useful for.
|
||||
|
||||
Siding
|
||||
------
|
||||
|
||||
The first useful and most important property of the dot product is to
|
||||
check what side stuff is looking at. Let's imagine we have any two
|
||||
vectors, **a** and **b**. Any **direction** or **magnitude** (neither
|
||||
**origin**). Does not matter what they are, but let's imagine we compute
|
||||
the dot product between them.
|
||||
|
||||
::
|
||||
|
||||
var s = a.dot(b)
|
||||
|
||||
The operation will return a single floating point number (but since we
|
||||
are in vector world, we call them **scalar**, will keep using that term
|
||||
from now on). This number will tell us the following:
|
||||
|
||||
- If the number is greater than zero, both are looking towards the same
|
||||
direction (the angle between them is < 90° degrees).
|
||||
- If the number is less than zero, both are looking towards opposite
|
||||
direction (the angle between them is > 90° degrees).
|
||||
- If the number is zero, vectors are shaped in L (the angle between
|
||||
them *is* 90° degrees).
|
||||
|
||||
.. image:: /img/tutovec5.png
|
||||
|
||||
So let's think of a real use-case scenario. Imagine Snake is going
|
||||
through a forest, and then there is an enemy nearby. How can we quickly
|
||||
tell if the enemy has seen discovered Snake? In order to discover him,
|
||||
the enemy must be able to *see* Snake. Let's say, then that:
|
||||
|
||||
- Snake is in position **A**.
|
||||
- The enemy is in position **B**.
|
||||
- The enemy is *facing* towards direction vector **F**.
|
||||
|
||||
.. image:: /img/tutovec6.png
|
||||
|
||||
So, let's create a new vector **BA** that goes from the guard (**B**) to
|
||||
Snake (**A**), by subtracting the two:
|
||||
|
||||
::
|
||||
|
||||
var BA = A - B
|
||||
|
||||
.. image:: /img/tutovec7.png
|
||||
|
||||
Ideally, if the guard was looking straight towards snake, to make eye to
|
||||
eye contact, it would do it in the same direction as vector BA.
|
||||
|
||||
If the dot product between **F** and **BA** is greater than 0, then
|
||||
Snake will be discovered. This happens because we will be able to tell
|
||||
that the guard is facing towards him:
|
||||
|
||||
::
|
||||
|
||||
if (BA.dot(F) > 0):
|
||||
print("!")
|
||||
|
||||
Seems Snake is safe so far.
|
||||
|
||||
Siding with unit vectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Ok, so now we know that dot product between two vectors will let us know
|
||||
if they are looking towards the same side, opposite sides or are just
|
||||
perpendicular to each other.
|
||||
|
||||
This works the same with all vectors, no matter the magnitude so **unit
|
||||
vectors** are not the exception. However, using the same property with
|
||||
unit vectors yields an even more interesting result, as an extra
|
||||
property is added:
|
||||
|
||||
- If both vectors are facing towards the exact same direction (parallel
|
||||
to each other, angle between them is 0°), the resulting scalar is
|
||||
**1**.
|
||||
- If both vectors are facing towards the exact opposite direction
|
||||
(parallel to each other, but angle between them is 180°), the
|
||||
resulting scalar is **-1**.
|
||||
|
||||
This means that dot product between unit vectors is always between the
|
||||
range of 1 and -1. So Again...
|
||||
|
||||
- If their angle is **0°** dot product is **1**.
|
||||
- If their angle is **90°**, then dot product is **0**.
|
||||
- If their angle is **180°**, then dot product is **-1**.
|
||||
|
||||
Uh.. this is oddly familiar... seen this before... where?
|
||||
|
||||
Let's take two unit vectors. The first one is pointing up, the second
|
||||
too but we will rotate it all the way from up (0°) to down (180°
|
||||
degrees)...
|
||||
|
||||
.. image:: /img/tutovec8.png
|
||||
|
||||
While plotting the resulting scalar!
|
||||
|
||||
.. image:: /img/tutovec9.png
|
||||
|
||||
Aha! It all makes sense now, this is a
|
||||
`Cosine <http://mathworld.wolfram.com/Cosine.html>`__ function!
|
||||
|
||||
We can say that, then, as a rule...
|
||||
|
||||
The **dot product** between two **unit vectors** is the **cosine** of
|
||||
the **angle** between those two vectors. So, to obtain the angle between
|
||||
two vectors, we must do:
|
||||
|
||||
::
|
||||
|
||||
var angle_in_radians = acos( a.dot(b) )
|
||||
|
||||
What is this useful for? Well obtaining the angle directly is probably
|
||||
not as useful, but just being able to tell the angle is useful for
|
||||
reference. One example is in the `Kinematic
|
||||
Character <https://github.com/godotengine/godot-demo-projects/blob/master/2d/kinematic_char/player.gd#L79>`__
|
||||
demo, when the character moves in a certain direction then we hit an
|
||||
object. How to tell if what we hit is the floor?
|
||||
|
||||
By comparing the normal of the collision point with a previously
|
||||
computed angle.
|
||||
|
||||
The beauty of this is that the same code works exactly the same and
|
||||
without modification in
|
||||
`3D <https://github.com/godotengine/godot-demo-projects/blob/master/3d/kinematic_char/cubio.gd#L57>`__.
|
||||
Vector math is, in a great deal, dimension-amount-independent, so adding
|
||||
or removing an axis only adds very little complexity.
|
||||
|
||||
Planes
|
||||
~~~~~~
|
||||
|
||||
The dot product has another interesting property with unit vectors.
|
||||
Imagine that perpendicular to that vector (and through the origin)
|
||||
passes a plane. Planes divide the entire space into positive
|
||||
(over the plane) and negative (under the plane), and (contrary to
|
||||
popular belief) you can also use their math in 2D:
|
||||
|
||||
.. image:: /img/tutovec10.png
|
||||
|
||||
Unit vectors that are perpendicular to a surface (so, they describe the
|
||||
orientation of the surface) are called **unit normal vectors**. Though,
|
||||
usually they are just abbreviated as \*normals. Normals appear in
|
||||
planes, 3D geometry (to determine where each face or vertex is siding),
|
||||
etc. A **normal** *is* a **unit vector**, but it's called *normal*
|
||||
because of its usage. (Just like we call Origin to (0,0)!).
|
||||
|
||||
It's as simple as it looks. The plane passes by the origin and the
|
||||
surface of it is perpendicular to the unit vector (or *normal*). The
|
||||
side towards the vector points to is the positive half-space, while the
|
||||
other side is the negative half-space. In 3D this is exactly the same,
|
||||
except that the plane is an infinite surface (imagine an infinite, flat
|
||||
sheet of paper that you can orient and is pinned to the origin) instead
|
||||
of a line.
|
||||
|
||||
Distance to plane
|
||||
-----------------
|
||||
|
||||
Now that it's clear what a plane is, let's go back to the dot product.
|
||||
The dot product between a **unit vector** and any **point in space**
|
||||
(yes, this time we do dot product between vector and position), returns
|
||||
the **distance from the point to the plane**:
|
||||
|
||||
::
|
||||
|
||||
var distance = normal.dot(point)
|
||||
|
||||
But not just the absolute distance, if the point is in the negative half
|
||||
space the distance will be negative, too:
|
||||
|
||||
.. image:: /img/tutovec11.png
|
||||
|
||||
This allows us to tell which side of the plane a point is.
|
||||
|
||||
Away from the origin
|
||||
--------------------
|
||||
|
||||
I know what you are thinking! So far this is nice, but *real* planes are
|
||||
everywhere in space, not only passing through the origin. You want real
|
||||
*plane* action and you want it *now*.
|
||||
|
||||
Remember that planes not only split space in two, but they also have
|
||||
*polarity*. This means that it is possible to have perfectly overlapping
|
||||
planes, but their negative and positive half-spaces are swapped.
|
||||
|
||||
With this in mind, let's describe a full plane as a **normal** *N* and a
|
||||
**distance from the origin** scalar *D*. Thus, our plane is represented
|
||||
by N and D. For example:
|
||||
|
||||
.. image:: /img/tutovec12.png
|
||||
|
||||
For 3D math, Godot provides a :ref:`Plane <class_Plane>`
|
||||
built-in type that handles this.
|
||||
|
||||
Basically, N and D can represent any plane in space, be it for 2D or 3D
|
||||
(depending on the amount of dimensions of N) and the math is the same
|
||||
for both. It's the same as before, but D is the distance from the origin
|
||||
to the plane, travelling in N direction. As an example, imagine you want
|
||||
to reach a point in the plane, you will just do:
|
||||
|
||||
::
|
||||
|
||||
var point_in_plane = N*D
|
||||
|
||||
This will stretch (resize) the normal vector and make it touch the
|
||||
plane. This math might seem confusing, but it's actually much simpler
|
||||
than it seems. If we want to tell, again, the distance from the point to
|
||||
the plane, we do the same but adjusting for distance:
|
||||
|
||||
::
|
||||
|
||||
var distance = N.dot(point) - D
|
||||
|
||||
The same thing, using a built-in function:
|
||||
|
||||
::
|
||||
|
||||
var distance = plane.distance_to(point)
|
||||
|
||||
This will, again, return either a positive or negative distance.
|
||||
|
||||
Flipping the polarity of the plane is also very simple, just negate both
|
||||
N and D. This will result in a plane in the same position, but with
|
||||
inverted negative and positive half spaces:
|
||||
|
||||
::
|
||||
|
||||
N = -N
|
||||
D = -D
|
||||
|
||||
Of course, Godot also implements this operator in :ref:`Plane <class_Plane>`,
|
||||
so doing:
|
||||
|
||||
::
|
||||
|
||||
var inverted_plane = -plane
|
||||
|
||||
Will work as expected.
|
||||
|
||||
So, remember, a plane is just that and its main practical use is
|
||||
calculating the distance to it. So, why is it useful to calculate the
|
||||
distance from a point to a plane? It's extremely useful! Let's see some
|
||||
simple examples..
|
||||
|
||||
Constructing a plane in 2D
|
||||
--------------------------
|
||||
|
||||
Planes clearly don't come out of nowhere, so they must be built.
|
||||
Constructing them in 2D is easy, this can be done from either a normal
|
||||
(unit vector) and a point, or from two points in space.
|
||||
|
||||
In the case of a normal and a point, most of the work is done, as the
|
||||
normal is already computed, so just calculate D from the dot product of
|
||||
the normal and the point.
|
||||
|
||||
::
|
||||
|
||||
var N = normal
|
||||
var D = normal.dot(point)
|
||||
|
||||
For two points in space, there are actually two planes that pass through
|
||||
them, sharing the same space but with normal pointing to the opposite
|
||||
directions. To compute the normal from the two points, the direction
|
||||
vector must be obtained first, and then it needs to be rotated 90°
|
||||
degrees to either side:
|
||||
|
||||
::
|
||||
|
||||
# calculate vector from a to b
|
||||
var dvec = (point_b - point_a).normalized()
|
||||
# rotate 90 degrees
|
||||
var normal = Vector2(dvec.y,-dev.x)
|
||||
# or alternatively
|
||||
# var normal = Vector2(-dvec.y,dev.x)
|
||||
# depending the desired side of the normal
|
||||
|
||||
The rest is the same as the previous example, either point_a or
|
||||
point_b will work since they are in the same plane:
|
||||
|
||||
::
|
||||
|
||||
var N = normal
|
||||
var D = normal.dot(point_a)
|
||||
# this works the same
|
||||
# var D = normal.dot(point_b)
|
||||
|
||||
Doing the same in 3D is a little more complex and will be explained
|
||||
further down.
|
||||
|
||||
Some examples of planes
|
||||
-----------------------
|
||||
|
||||
Here is a simple example of what planes are useful for. Imagine you have
|
||||
a `convex <http://www.mathsisfun.com/definitions/convex.html>`__
|
||||
polygon. For example, a rectangle, a trapezoid, a triangle, or just any
|
||||
polygon where faces that don't bend inwards.
|
||||
|
||||
For every segment of the polygon, we compute the plane that passes by
|
||||
that segment. Once we have the list of planes, we can do neat things,
|
||||
for example checking if a point is inside the polygon.
|
||||
|
||||
We go through all planes, if we can find a plane where the distance to
|
||||
the point is positive, then the point is outside the polygon. If we
|
||||
can't, then the point is inside.
|
||||
|
||||
.. image:: /img/tutovec13.png
|
||||
|
||||
Code should be something like this:
|
||||
|
||||
::
|
||||
|
||||
var inside = true
|
||||
for p in planes:
|
||||
# check if distance to plane is positive
|
||||
if (N.dot(point) - D > 0):
|
||||
inside = false
|
||||
break # with one that fails, it's enough
|
||||
|
||||
Pretty cool, huh? But this gets much better! With a little more effort,
|
||||
similar logic will let us know when two convex polygons are overlapping
|
||||
too. This is called the Separating Axis Theorem (or SAT) and most
|
||||
physics engines use this to detect collision.
|
||||
|
||||
The idea is really simple! With a point, just checking if a plane
|
||||
returns a positive distance is enough to tell if the point is outside.
|
||||
With another polygon, we must find a plane where *all the **other**
|
||||
polygon points* return a positive distance to it. This check is
|
||||
performed with the planes of A against the points of B, and then with
|
||||
the planes of B against the points of A:
|
||||
|
||||
.. image:: /img/tutovec14.png
|
||||
|
||||
Code should be something like this:
|
||||
|
||||
::
|
||||
|
||||
var overlapping = true
|
||||
|
||||
for p in planes_of_A:
|
||||
var all_out = true
|
||||
for v in points_of_B:
|
||||
if (p.distance_to(v) < 0):
|
||||
all_out = false
|
||||
break
|
||||
|
||||
if (all_out):
|
||||
# a separating plane was found
|
||||
# do not continue testing
|
||||
overlapping = false
|
||||
break
|
||||
|
||||
if (overlapping):
|
||||
# only do this check if no separating plane
|
||||
# was found in planes of A
|
||||
for p in planes_of_B:
|
||||
var all_out = true
|
||||
for v in points_of_A:
|
||||
if (p.distance_to(v) < 0):
|
||||
all_out = false
|
||||
break
|
||||
|
||||
if (all_out):
|
||||
overlapping = false
|
||||
break
|
||||
|
||||
if (overlapping):
|
||||
print("Polygons Collided!")
|
||||
|
||||
As you can see, planes are quite useful, and this is the tip of the
|
||||
iceberg. You might be wondering what happens with non convex polygons.
|
||||
This is usually just handled by splitting the concave polygon into
|
||||
smaller convex polygons, or using a technique such as BSP (which is not
|
||||
used much nowadays).
|
||||
|
||||
Cross product
|
||||
-------------
|
||||
|
||||
Quite a lot can be done with the dot product! But the party would not be
|
||||
complete without the cross product. Remember back at the beginning of
|
||||
this tutorial? Specifically how to obtain a perpendicular (rotated 90
|
||||
degrees) vector by swapping x and y, then negating either of them for
|
||||
right (clockwise) or left (counter-clockwise) rotation? That ended up
|
||||
being useful for calculating a 2D plane normal from two points.
|
||||
|
||||
As mentioned before, no such thing exists in 3D because a 3D vector has
|
||||
infinite perpendicular vectors. It would also not make sense to obtain a
|
||||
3D plane from 2 points, as 3 points are needed instead.
|
||||
|
||||
To aid in this kind stuff, the brightest minds of humanity's top
|
||||
mathematicians brought us the **cross product**.
|
||||
|
||||
The cross product takes two vectors and returns another vector. The
|
||||
returned third vector is always perpendicular to the first two. The
|
||||
source vectors, of course, must not be the same, and must not be
|
||||
parallel or opposite, else the resulting vector will be (0,0,0):
|
||||
|
||||
.. image:: /img/tutovec16.png
|
||||
|
||||
The formula for the cross product is:
|
||||
|
||||
::
|
||||
|
||||
var c = Vector3()
|
||||
c.x = (a.y * b.z) - (a.z * b.y)
|
||||
c.y = (a.z * b.x) - (a.x * b.z)
|
||||
c.z = (a.x * b.y) - (a.y * b.x)
|
||||
|
||||
This can be simplified, in Godot, to:
|
||||
|
||||
::
|
||||
|
||||
var c = a.cross(b)
|
||||
|
||||
However, unlike the dot product, doing ``a.cross(b)`` and ``b.cross(a)``
|
||||
will yield different results. Specifically, the returned vector will be
|
||||
negated in the second case. As you might have realized, this coincides
|
||||
with creating perpendicular vectors in 2D. In 3D, there are also two
|
||||
possible perpendicular vectors to a pair of 2D vectors.
|
||||
|
||||
Also, the resulting cross product of two unit vectors is *not* a unit
|
||||
vector. Result will need to be renormalized.
|
||||
|
||||
Area of a triangle
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Cross product can be used to obtain the surface area of a triangle in
|
||||
3D. Given a triangle consisting of 3 points, **A**, **B** and **C**:
|
||||
|
||||
.. image:: /img/tutovec17.png
|
||||
|
||||
Take any of them as a pivot and compute the adjacent vectors to the
|
||||
other two points. As example, we will use B as a pivot:
|
||||
|
||||
::
|
||||
|
||||
var BA = A - B
|
||||
var BC = C - B
|
||||
|
||||
.. image:: /img/tutovec18.png
|
||||
|
||||
Compute the cross product between **BA** and **BC** to obtain the
|
||||
perpendicular vector **P**:
|
||||
|
||||
::
|
||||
|
||||
var P = BA.cross(BC)
|
||||
|
||||
.. image:: /img/tutovec19.png
|
||||
|
||||
The length (magnitude) of **P** is the surface area of the parallelogram
|
||||
built by the two vectors **BA** and **BC**, therefore the surface area
|
||||
of the triangle is half of it.
|
||||
|
||||
::
|
||||
|
||||
var area = P.length()/2
|
||||
|
||||
Plane of the triangle
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
With **P** computed from the previous step, normalize it to get the
|
||||
normal of the plane.
|
||||
|
||||
::
|
||||
|
||||
var N = P.normalized()
|
||||
|
||||
And obtain the distance by doing the dot product of P with any of the 3
|
||||
points of the **ABC** triangle:
|
||||
|
||||
::
|
||||
|
||||
var D = P.dot(A)
|
||||
|
||||
Fantastic! You computed the plane from a triangle!
|
||||
|
||||
Here's some useful info (that you can find in Godot source code anyway).
|
||||
Computing a plane from a triangle can result in 2 planes, so a sort of
|
||||
convention needs to be set. This usually depends (in video games and 3D
|
||||
visualization) to use the front-facing side of the triangle.
|
||||
|
||||
In Godot, front-facing triangles are those that, when looking at the
|
||||
camera, are in clockwise order. Triangles that look Counter-clockwise
|
||||
when looking at the camera are not drawn (this helps to draw less, so
|
||||
the back-part of the objects is not drawn).
|
||||
|
||||
To make it a little clearer, in the image below, the triangle **ABC**
|
||||
appears clock-wise when looked at from the *Front Camera*, but to the
|
||||
*Rear Camera* it appears counter-clockwise so it will not be drawn.
|
||||
|
||||
.. image:: /img/tutovec20.png
|
||||
|
||||
Normals of triangles often are sided towards the direction they can be
|
||||
viewed from, so in this case, the normal of triangle ABC would point
|
||||
towards the front camera:
|
||||
|
||||
.. image:: /img/tutovec21.png
|
||||
|
||||
So, to obtain N, the correct formula is:
|
||||
|
||||
::
|
||||
|
||||
# clockwise normal from triangle formula
|
||||
var N = (A-C).cross(A-B).normalized()
|
||||
# for counter-clockwise:
|
||||
# var N = (A-B).cross(A-C).normalized()
|
||||
var D = N.dot(A)
|
||||
|
||||
Collision detection in 3D
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is another bonus bit, a reward for being patient and keeping up
|
||||
with this long tutorial. Here is another piece of wisdom. This might
|
||||
not be something with a direct use case (Godot already does collision
|
||||
detection pretty well) but It's a really cool algorithm to understand
|
||||
anyway, because it's used by almost all physics engines and collision
|
||||
detection libraries :)
|
||||
|
||||
Remember that converting a convex shape in 2D to an array of 2D planes
|
||||
was useful for collision detection? You could detect if a point was
|
||||
inside any convex shape, or if two 2D convex shapes were overlapping.
|
||||
|
||||
Well, this works in 3D too, if two 3D polyhedral shapes are colliding,
|
||||
you won't be able to find a separating plane. If a separating plane is
|
||||
found, then the shapes are definitely not colliding.
|
||||
|
||||
To refresh a bit a separating plane means that all vertices of polygon A
|
||||
are in one side of the plane, and all vertices of polygon B are in the
|
||||
other side. This plane is always one of the face-planes of either
|
||||
polygon A or polygon B.
|
||||
|
||||
In 3D though, there is a problem to this approach, because it is
|
||||
possible that, in some cases a separating plane can't be found. This is
|
||||
an example of such situation:
|
||||
|
||||
.. image:: /img/tutovec22.png
|
||||
|
||||
To avoid it, some extra planes need to be tested as separators, these
|
||||
planes are the cross product between the edges of polygon A and the
|
||||
edges of polygon B
|
||||
|
||||
.. image:: /img/tutovec23.png
|
||||
|
||||
So the final algorithm is something like:
|
||||
|
||||
::
|
||||
|
||||
var overlapping = true
|
||||
|
||||
for p in planes_of_A:
|
||||
var all_out = true
|
||||
for v in points_of_B:
|
||||
if (p.distance_to(v) < 0):
|
||||
all_out = false
|
||||
break
|
||||
|
||||
if (all_out):
|
||||
# a separating plane was found
|
||||
# do not continue testing
|
||||
overlapping = false
|
||||
break
|
||||
|
||||
if (overlapping):
|
||||
# only do this check if no separating plane
|
||||
# was found in planes of A
|
||||
for p in planes_of_B:
|
||||
var all_out = true
|
||||
for v in points_of_A:
|
||||
if (p.distance_to(v) < 0):
|
||||
all_out = false
|
||||
break
|
||||
|
||||
if (all_out):
|
||||
overlapping = false
|
||||
break
|
||||
|
||||
if (overlapping):
|
||||
for ea in edges_of_A:
|
||||
for eb in edges_of_B:
|
||||
var n = ea.cross(eb)
|
||||
if (n.length() == 0):
|
||||
continue
|
||||
|
||||
var max_A = -1e20 # tiny number
|
||||
var min_A = 1e20 # huge number
|
||||
|
||||
# we are using the dot product directly
|
||||
# so we can map a maximum and minimum range
|
||||
# for each polygon, then check if they
|
||||
# overlap.
|
||||
|
||||
for v in points_of_A:
|
||||
var d = n.dot(v)
|
||||
if (d > max_A):
|
||||
max_A = d
|
||||
if (d < min_A):
|
||||
min_A = d
|
||||
|
||||
var max_B = -1e20 # tiny number
|
||||
var min_B = 1e20 # huge number
|
||||
|
||||
for v in points_of_B:
|
||||
var d = n.dot(v)
|
||||
if (d > max_B):
|
||||
max_B = d
|
||||
if (d < min_B):
|
||||
min_B = d
|
||||
|
||||
if (min_A > max_B or min_B > max_A):
|
||||
# not overlapping!
|
||||
overlapping = false
|
||||
break
|
||||
|
||||
if (not overlapping):
|
||||
break
|
||||
|
||||
if (overlapping):
|
||||
print("Polygons collided!")
|
||||
|
||||
This was all! Hope it was helpful, and please give feedback and let know
|
||||
if something in this tutorial is not clear! You should be now ready for
|
||||
the next challenge... :ref:`doc_matrices_and_transforms`!
|
||||
299
learning/features/misc/background_loading.rst
Normal file
299
learning/features/misc/background_loading.rst
Normal file
@@ -0,0 +1,299 @@
|
||||
.. _doc_background_loading:
|
||||
|
||||
Background loading
|
||||
==================
|
||||
|
||||
When switching the main scene of your game (for example going to a new
|
||||
level), you might want to show a loading screen with some indication
|
||||
that progress is being made. The main load method
|
||||
(``ResourceLoader::load`` or just ``load`` from gdscript) blocks your
|
||||
thread while the resource is being loaded, so It's not good. This
|
||||
document discusses the ``ResourceInteractiveLoader`` class for smoother
|
||||
load screens.
|
||||
|
||||
ResourceInteractiveLoader
|
||||
-------------------------
|
||||
|
||||
The ``ResourceInteractiveLoader`` class allows you to load a resource in
|
||||
stages. Every time the method ``poll`` is called, a new stage is loaded,
|
||||
and control is returned to the caller. Each stage is generally a
|
||||
sub-resource that is loaded by the main resource. For example, if you're
|
||||
loading a scene that loads 10 images, each image will be one stage.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Usage is generally as follows
|
||||
|
||||
Obtaining a ResourceInteractiveLoader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(String p_path);
|
||||
|
||||
This method will give you a ResourceInteractiveLoader that you will use
|
||||
to manage the load operation.
|
||||
|
||||
Polling
|
||||
~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
Error ResourceInteractiveLoader::poll();
|
||||
|
||||
Use this method to advance the progress of the load. Each call to
|
||||
``poll`` will load the next stage of your resource. Keep in mind that
|
||||
each stage is one entire "atomic" resource, such as an image, or a mesh,
|
||||
so it will take several frames to load.
|
||||
|
||||
Returns ``OK`` on no errors, ``ERR_FILE_EOF`` when loading is finished.
|
||||
Any other return value means there was an error and loading has stopped.
|
||||
|
||||
Load progress (optional)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To query the progress of the load, use the following methods:
|
||||
|
||||
::
|
||||
|
||||
int ResourceInteractiveLoader::get_stage_count() const;
|
||||
int ResourceInteractiveLoader::get_stage() const;
|
||||
|
||||
``get_stage_count`` returns the total number of stages to load.
|
||||
``get_stage`` returns the current stage being loaded.
|
||||
|
||||
Forcing completion (optional)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
Error ResourceInteractiveLoader::wait();
|
||||
|
||||
Use this method if you need to load the entire resource in the current
|
||||
frame, without any more steps.
|
||||
|
||||
Obtaining the resource
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
Ref<Resource> ResourceInteractiveLoader::get_resource();
|
||||
|
||||
If everything goes well, use this method to retrieve your loaded
|
||||
resource.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
This example demostrates how to load a new scene. Consider it in the
|
||||
context of the :ref:`doc_singletons_autoload` example.
|
||||
|
||||
First we setup some variables and initialize the ``current_scene``
|
||||
with the main scene of the game:
|
||||
|
||||
::
|
||||
|
||||
var loader
|
||||
var wait_frames
|
||||
var time_max = 100 # msec
|
||||
var current_scene
|
||||
|
||||
func _ready():
|
||||
var root = get_tree().get_root()
|
||||
current_scene = root.get_child(root.get_child_count() -1)
|
||||
|
||||
The function ``goto_scene`` is called from the game when the scene
|
||||
needs to be switched. It requests an interactive loader, and calls
|
||||
``set_progress(true)`` to start polling the loader in the ``_progress``
|
||||
callback. It also starts a "loading" animation, which can show a
|
||||
progress bar or loading screen, etc.
|
||||
|
||||
::
|
||||
|
||||
func goto_scene(path): # game requests to switch to this scene
|
||||
loader = ResourceLoader.load_interactive(path)
|
||||
if loader == null: # check for errors
|
||||
show_error()
|
||||
return
|
||||
set_process(true)
|
||||
|
||||
current_scene.queue_free() # get rid of the old scene
|
||||
|
||||
# start your "loading..." animation
|
||||
get_node("animation").play("loading")
|
||||
|
||||
wait_frames = 1
|
||||
|
||||
``_process`` is where the loader is polled. ``poll`` is called, and then
|
||||
we deal with the return value from that call. ``OK`` means keep polling,
|
||||
``ERR_FILE_EOF`` means load is done, anything else means there was an
|
||||
error. Also note we skip one frame (via ``wait_frames``, set on the
|
||||
``goto_scene`` function) to allow the loading screen to show up.
|
||||
|
||||
Note how use use ``OS.get_ticks_msec`` to control how long we block the
|
||||
thread. Some stages might load really fast, which means we might be able
|
||||
to cram more than one call to ``poll`` in one frame, some might take way
|
||||
more than your value for ``time_max``, so keep in mind we won't have
|
||||
precise control over the timings.
|
||||
|
||||
::
|
||||
|
||||
func _process(time):
|
||||
if loader == null:
|
||||
# no need to process anymore
|
||||
set_process(false)
|
||||
return
|
||||
|
||||
if wait_frames > 0: # wait for frames to let the "loading" animation to show up
|
||||
wait_frames -= 1
|
||||
return
|
||||
|
||||
var t = OS.get_ticks_msec()
|
||||
while OS.get_ticks_msec() < t + time_max: # use "time_max" to control how much time we block this thread
|
||||
|
||||
# poll your loader
|
||||
var err = loader.poll()
|
||||
|
||||
if err == ERR_FILE_EOF: # load finished
|
||||
var resource = loader.get_resource()
|
||||
loader = null
|
||||
set_new_scene(resource)
|
||||
break
|
||||
elif err == OK:
|
||||
update_progress()
|
||||
else: # error during loading
|
||||
show_error()
|
||||
loader = null
|
||||
break
|
||||
|
||||
Some extra helper functions. ``update_progress`` updates a progress bar,
|
||||
or can also update a paused animation (the animation represents the
|
||||
entire load process from beginning to end). ``set_new_scene`` puts the
|
||||
newly loaded scene on the tree. Because it's a scene being loaded,
|
||||
``instance()`` needs to be called on the resource obtained from the
|
||||
loader.
|
||||
|
||||
::
|
||||
|
||||
func update_progress():
|
||||
var progress = float(loader.get_stage()) / loader.get_stage_count()
|
||||
# update your progress bar?
|
||||
get_node("progress").set_progress(progress)
|
||||
|
||||
# or update a progress animation?
|
||||
var len = get_node("animation").get_current_animation_length()
|
||||
|
||||
# call this on a paused animation. use "true" as the second parameter to force the animation to update
|
||||
get_node("animation").seek(progress * len, true)
|
||||
|
||||
func set_new_scene(scene_resource):
|
||||
current_scene = scene_resource.instance()
|
||||
get_node("/root").add_child(current_scene)
|
||||
|
||||
Using multiple threads
|
||||
----------------------
|
||||
|
||||
ResourceInteractiveLoader can be used from multiple threads. A couple of
|
||||
things to keep in mind if you attempt it:
|
||||
|
||||
Use a Semaphore
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
While your thread waits for the main thread to request a new resource,
|
||||
use a Semaphore to sleep (instead of a busy loop or anything similar).
|
||||
|
||||
Not blocking main thread during the polling
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you have a mutex to allow calls from the main thread to your loader
|
||||
class, don't lock it while you call ``poll`` on the loader. When a
|
||||
resource is finished loading, it might require some resources from the
|
||||
low level APIs (VisualServer, etc), which might need to lock the main
|
||||
thread to acquire them. This might cause a deadlock if the main thread
|
||||
is waiting for your mutex while your thread is waiting to load a
|
||||
resource.
|
||||
|
||||
Example class
|
||||
-------------
|
||||
|
||||
You can find an example class for loading resources in threads here:
|
||||
:download:`resource_queue.gd </files/resource_queue.gd>`. Usage is as follows:
|
||||
|
||||
::
|
||||
|
||||
func start()
|
||||
|
||||
Call after you instance the class to start the thread.
|
||||
|
||||
::
|
||||
|
||||
func queue_resource(path, p_in_front = false)
|
||||
|
||||
Queue a resource. Use optional parameter "p_in_front" to put it in
|
||||
front of the queue.
|
||||
|
||||
::
|
||||
|
||||
func cancel_resource(path)
|
||||
|
||||
Remove a resource from the queue, discarding any loading done.
|
||||
|
||||
::
|
||||
|
||||
func is_ready(path)
|
||||
|
||||
Returns true if a resource is done loading and ready to be retrieved.
|
||||
|
||||
::
|
||||
|
||||
func get_progress(path)
|
||||
|
||||
Get the progress of a resource. Returns -1 on error (for example if the
|
||||
resource is not on the queue), or a number between 0.0 and 1.0 with the
|
||||
progress of the load. Use mostly for cosmetic purposes (updating
|
||||
progress bars, etc), use ``is_ready`` to find out if a resource is
|
||||
actually ready.
|
||||
|
||||
::
|
||||
|
||||
func get_resource(path)
|
||||
|
||||
Returns the fully loaded resource, or null on error. If the resource is
|
||||
not done loading (``is_ready`` returns false), it will block your thread
|
||||
and finish the load. If the resource is not on the queue, it will call
|
||||
``ResourceLoader::load`` to load it normally and return it.
|
||||
|
||||
Example:
|
||||
~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
# initialize
|
||||
queue = preload("res://resource_queue.gd").new()
|
||||
queue.start()
|
||||
|
||||
# suppose your game starts with a 10 second custscene, during which the user can't interact with the game.
|
||||
# For that time we know they won't use the pause menu, so we can queue it to load during the cutscene:
|
||||
queue.queue_resource("res://pause_menu.xml")
|
||||
start_curscene()
|
||||
|
||||
# later when the user presses the pause button for the first time:
|
||||
pause_menu = queue.get_resource("res://pause_menu.xml").instance()
|
||||
pause_menu.show()
|
||||
|
||||
# when you need a new scene:
|
||||
queue.queue_resource("res://level_1.xml", true) # use "true" as the second parameter to put it at the front
|
||||
# of the queue, pausing the load of any other resource
|
||||
|
||||
# to check progress
|
||||
if queue.is_ready("res://level_1.xml"):
|
||||
show_new_level(queue.get_resource("res://level_1.xml"))
|
||||
else:
|
||||
update_progress(queue.get_process("res://level_1.xml"))
|
||||
|
||||
# when the user walks away from the trigger zone in your Metroidvania game:
|
||||
queue.cancel_resource("res://zone_2.xml")
|
||||
|
||||
**Note**: this code in its current form is not tested in real world
|
||||
scenarios. Ask punto on IRC (#godotengine on irc.freenode.net) for help.
|
||||
504
learning/features/misc/binary_serialization_api.rst
Normal file
504
learning/features/misc/binary_serialization_api.rst
Normal file
@@ -0,0 +1,504 @@
|
||||
.. _doc_binary_serialization_api:
|
||||
|
||||
Binary serialization API
|
||||
========================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Godot has a simple serialization API based on Variant. It's used for
|
||||
converting data types to an array of bytes efficiently. This API is used
|
||||
in the functions ``get_var`` and ``store_var`` of :ref:`class_File`
|
||||
as well as the packet APIs for :ref:`class_PacketPeer`. This format
|
||||
is not used for binary scenes and resources.
|
||||
|
||||
Packet specification
|
||||
--------------------
|
||||
|
||||
The packet is designed to be always padded to 4 bytes. All values are
|
||||
little endian encoded. All packets have a 4 byte header representing an
|
||||
integer, specifying the type of data:
|
||||
|
||||
+--------+--------------------------+
|
||||
| Type | Value |
|
||||
+========+==========================+
|
||||
| 0 | null |
|
||||
+--------+--------------------------+
|
||||
| 1 | bool |
|
||||
+--------+--------------------------+
|
||||
| 2 | integer |
|
||||
+--------+--------------------------+
|
||||
| 3 | float |
|
||||
+--------+--------------------------+
|
||||
| 4 | string |
|
||||
+--------+--------------------------+
|
||||
| 5 | vector2 |
|
||||
+--------+--------------------------+
|
||||
| 6 | rect2 |
|
||||
+--------+--------------------------+
|
||||
| 7 | vector3 |
|
||||
+--------+--------------------------+
|
||||
| 8 | matrix32 |
|
||||
+--------+--------------------------+
|
||||
| 9 | plane |
|
||||
+--------+--------------------------+
|
||||
| 10 | quaternion |
|
||||
+--------+--------------------------+
|
||||
| 11 | aabb (rect3) |
|
||||
+--------+--------------------------+
|
||||
| 12 | matrix3x3 |
|
||||
+--------+--------------------------+
|
||||
| 13 | transform (matrix 4x3) |
|
||||
+--------+--------------------------+
|
||||
| 14 | color |
|
||||
+--------+--------------------------+
|
||||
| 15 | image |
|
||||
+--------+--------------------------+
|
||||
| 16 | node path |
|
||||
+--------+--------------------------+
|
||||
| 17 | rid (unsupported) |
|
||||
+--------+--------------------------+
|
||||
| 18 | object (unsupported) |
|
||||
+--------+--------------------------+
|
||||
| 19 | input event |
|
||||
+--------+--------------------------+
|
||||
| 20 | dictionary |
|
||||
+--------+--------------------------+
|
||||
| 21 | array |
|
||||
+--------+--------------------------+
|
||||
| 22 | ByteArray |
|
||||
+--------+--------------------------+
|
||||
| 23 | IntArray |
|
||||
+--------+--------------------------+
|
||||
| 24 | FloatArray |
|
||||
+--------+--------------------------+
|
||||
| 25 | StringArray |
|
||||
+--------+--------------------------+
|
||||
| 26 | Vector2Array |
|
||||
+--------+--------------------------+
|
||||
| 27 | Vector3Array |
|
||||
+--------+--------------------------+
|
||||
| 28 | ColorArray |
|
||||
+--------+--------------------------+
|
||||
|
||||
Following this is the actual packet contents, which varies for each type
|
||||
of packet:
|
||||
|
||||
0: null
|
||||
~~~~~~~
|
||||
|
||||
1: bool
|
||||
~~~~~~~
|
||||
|
||||
+----------+-------+-----------+---------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+===========================+
|
||||
| 4 | 4 | Integer | 0 for False, 1 for True |
|
||||
+----------+-------+-----------+---------------------------+
|
||||
|
||||
2: integer
|
||||
~~~~~~~~~~
|
||||
|
||||
+----------+-------+-----------+--------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+==========================+
|
||||
| 4 | 4 | Integer | Signed, 32-Bit Integer |
|
||||
+----------+-------+-----------+--------------------------+
|
||||
|
||||
3: float
|
||||
~~~~~~~~
|
||||
|
||||
+----------+-------+---------+-------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+=========================+
|
||||
| 4 | 4 | Float | IEE 754 32-Bits Float |
|
||||
+----------+-------+---------+-------------------------+
|
||||
|
||||
4: string
|
||||
~~~~~~~~~
|
||||
|
||||
+----------+-------+-----------+----------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+============================+
|
||||
| 4 | 4 | Integer | String Length (in Bytes) |
|
||||
+----------+-------+-----------+----------------------------+
|
||||
| 8 | X | Bytes | UTF-8 Encoded String |
|
||||
+----------+-------+-----------+----------------------------+
|
||||
|
||||
This field is padded to 4 bytes.
|
||||
|
||||
5: vector2
|
||||
~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+================+
|
||||
| 4 | 4 | Float | X Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 8 | 4 | Float | Y Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
|
||||
6: rect2
|
||||
~~~~~~~~
|
||||
|
||||
+----------+-------+---------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+================+
|
||||
| 4 | 4 | Float | X Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 8 | 4 | Float | Y Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 12 | 4 | Float | X Size |
|
||||
+----------+-------+---------+----------------+
|
||||
| 16 | 4 | Float | Y Size |
|
||||
+----------+-------+---------+----------------+
|
||||
|
||||
7: vector3
|
||||
~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+================+
|
||||
| 4 | 4 | Float | X Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 8 | 4 | Float | Y Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 12 | 4 | Float | Z Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
|
||||
8: matrix32
|
||||
~~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+---------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+===============+
|
||||
| 4 | 4 | Float | [0][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 8 | 4 | Float | [0][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 12 | 4 | Float | [1][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 16 | 4 | Float | [1][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 20 | 4 | Float | [2][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 24 | 4 | Float | [2][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
|
||||
9: plane
|
||||
~~~~~~~~
|
||||
|
||||
+----------+-------+---------+---------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+===============+
|
||||
| 4 | 4 | Float | Normal X |
|
||||
+----------+-------+---------+---------------+
|
||||
| 8 | 4 | Float | Normal Y |
|
||||
+----------+-------+---------+---------------+
|
||||
| 12 | 4 | Float | Normal Z |
|
||||
+----------+-------+---------+---------------+
|
||||
| 16 | 4 | Float | Distance |
|
||||
+----------+-------+---------+---------------+
|
||||
|
||||
10: quaternion
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+---------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+===============+
|
||||
| 4 | 4 | Float | Imaginary X |
|
||||
+----------+-------+---------+---------------+
|
||||
| 8 | 4 | Float | Imaginary Y |
|
||||
+----------+-------+---------+---------------+
|
||||
| 12 | 4 | Float | Imaginary Z |
|
||||
+----------+-------+---------+---------------+
|
||||
| 16 | 4 | Float | Real W |
|
||||
+----------+-------+---------+---------------+
|
||||
|
||||
11: aabb (rect3)
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+================+
|
||||
| 4 | 4 | Float | X Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 8 | 4 | Float | Y Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 12 | 4 | Float | Z Coordinate |
|
||||
+----------+-------+---------+----------------+
|
||||
| 16 | 4 | Float | X Size |
|
||||
+----------+-------+---------+----------------+
|
||||
| 20 | 4 | Float | Y Size |
|
||||
+----------+-------+---------+----------------+
|
||||
| 24 | 4 | Float | Z Size |
|
||||
+----------+-------+---------+----------------+
|
||||
|
||||
12: matrix3x3
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+---------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+===============+
|
||||
| 4 | 4 | Float | [0][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 8 | 4 | Float | [0][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 12 | 4 | Float | [0][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 16 | 4 | Float | [1][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 20 | 4 | Float | [1][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 24 | 4 | Float | [1][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 28 | 4 | Float | [2][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 32 | 4 | Float | [2][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 36 | 4 | Float | [2][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
|
||||
13: transform (matrix 4x3)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+---------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+===============+
|
||||
| 4 | 4 | Float | [0][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 8 | 4 | Float | [0][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 12 | 4 | Float | [0][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 16 | 4 | Float | [1][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 20 | 4 | Float | [1][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 24 | 4 | Float | [1][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 28 | 4 | Float | [2][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 32 | 4 | Float | [2][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 36 | 4 | Float | [2][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 40 | 4 | Float | [3][0] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 44 | 4 | Float | [3][1] |
|
||||
+----------+-------+---------+---------------+
|
||||
| 48 | 4 | Float | [3][2] |
|
||||
+----------+-------+---------+---------------+
|
||||
|
||||
14: color
|
||||
~~~~~~~~~
|
||||
|
||||
+----------+-------+---------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+================+
|
||||
| 4 | 4 | Float | Red (0..1) |
|
||||
+----------+-------+---------+----------------+
|
||||
| 8 | 4 | Float | Green (0..1) |
|
||||
+----------+-------+---------+----------------+
|
||||
| 12 | 4 | Float | Blue (0..1) |
|
||||
+----------+-------+---------+----------------+
|
||||
| 16 | 4 | Float | Alpha (0..1) |
|
||||
+----------+-------+---------+----------------+
|
||||
|
||||
15: image
|
||||
~~~~~~~~~
|
||||
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+=====================+=======+===========+==================================================+
|
||||
| 4 | 4 | Integer | Format (see FORMAT\_\* in "Image":class_image |
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
| 8 | 4 | Integer | Mip-Maps (0 means no mip-maps). |
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
| 12 | 4 | Integer | Width (Pixels) |
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
| 16 | 4 | Integer | Height (Pixels) |
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
| 20 | 4 | Integer | Data Length |
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
| 24..24+DataLength | 1 | Byte | Image Data |
|
||||
+---------------------+-------+-----------+--------------------------------------------------+
|
||||
|
||||
This field is padded to 4 bytes.
|
||||
|
||||
16: node path
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+-----------+-----------------------------------------------------------------------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+=========================================================================================+
|
||||
| 4 | 4 | Integer | String Length, or New Format (val&0x80000000!=0 and NameCount=val&0x7FFFFFFF) |
|
||||
+----------+-------+-----------+-----------------------------------------------------------------------------------------+
|
||||
|
||||
For old format:
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
+----------+-------+---------+------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+=========+========================+
|
||||
| 8 | X | Bytes | UTF-8 Encoded String |
|
||||
+----------+-------+---------+------------------------+
|
||||
|
||||
Padded to 4 bytes.
|
||||
|
||||
For new format:
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
+----------+-------+-----------+-------------------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+=====================================+
|
||||
| 4 | 4 | Integer | Sub-Name Count |
|
||||
+----------+-------+-----------+-------------------------------------+
|
||||
| 8 | 4 | Integer | Flags (absolute: val&1 != 0 ) |
|
||||
+----------+-------+-----------+-------------------------------------+
|
||||
|
||||
For each Name and Sub-Name
|
||||
|
||||
+----------+-------+-----------+------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+========================+
|
||||
| X+0 | 4 | Integer | String Length |
|
||||
+----------+-------+-----------+------------------------+
|
||||
| X+4 | X | Bytes | UTF-8 Encoded String |
|
||||
+----------+-------+-----------+------------------------+
|
||||
|
||||
Every name string is padded to 4 bytes.
|
||||
|
||||
17: rid (unsupported)
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
18: object (unsupported)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
19: input event
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
20: dictionary
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+-----------+---------------------------------------------------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+=====================================================================+
|
||||
| 4 | 4 | Integer | val&0x7FFFFFFF = elements, val&0x80000000 = shared (bool) |
|
||||
+----------+-------+-----------+---------------------------------------------------------------------+
|
||||
|
||||
Then what follows is, for amount of "elements", pairs of key and value,
|
||||
one after the other, using this same format.
|
||||
|
||||
21: array
|
||||
~~~~~~~~~
|
||||
|
||||
+----------+-------+-----------+---------------------------------------------------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+=====================================================================+
|
||||
| 4 | 4 | Integer | val&0x7FFFFFFF = elements, val&0x80000000 = shared (bool) |
|
||||
+----------+-------+-----------+---------------------------------------------------------------------+
|
||||
|
||||
Then what follows is, for amount of "elements", values one after the
|
||||
other, using this same format.
|
||||
|
||||
22: :ref:`class_ByteArray`
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
+---------------+-------+-----------+------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+===============+=======+===========+========================+
|
||||
| 4 | 4 | Integer | Array Length (Bytes) |
|
||||
+---------------+-------+-----------+------------------------+
|
||||
| 8..8+length | 1 | Byte | Byte (0..255) |
|
||||
+---------------+-------+-----------+------------------------+
|
||||
|
||||
The array data is padded to 4 bytes.
|
||||
|
||||
23: :ref:`class_IntArray`
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
+------------------+-------+-----------+---------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==================+=======+===========+===========================+
|
||||
| 4 | 4 | Integer | Array Length (Integers) |
|
||||
+------------------+-------+-----------+---------------------------+
|
||||
| 8..8+length\*4 | 4 | Integer | 32 Bits Signed Integer |
|
||||
+------------------+-------+-----------+---------------------------+
|
||||
|
||||
24: :ref:`class_FloatArray`
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
+------------------+-------+-----------+---------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==================+=======+===========+===========================+
|
||||
| 4 | 4 |Integer | Array Length (Floats) |
|
||||
+------------------+-------+-----------+---------------------------+
|
||||
| 8..8+length\*4 | 4 |Integer | 32 Bits IEE 754 Float |
|
||||
+------------------+-------+-----------+---------------------------+
|
||||
|
||||
25: :ref:`class_StringArray`
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
+----------+-------+-----------+--------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+==========================+
|
||||
| 4 | 4 | Integer | Array Length (Strings) |
|
||||
+----------+-------+-----------+--------------------------+
|
||||
|
||||
For each String:
|
||||
|
||||
+----------+-------+-----------+------------------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+==========+=======+===========+========================+
|
||||
| X+0 | 4 | Integer | String Length |
|
||||
+----------+-------+-----------+------------------------+
|
||||
| X+4 | X | Bytes | UTF-8 Encoded String |
|
||||
+----------+-------+-----------+------------------------+
|
||||
|
||||
Every string is is padded to 4 bytes.
|
||||
|
||||
26: :ref:`class_Vector2Array`
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
+-------------------+-------+-----------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+===================+=======+===========+================+
|
||||
| 4 | 4 | Integer | Array Length |
|
||||
+-------------------+-------+-----------+----------------+
|
||||
| 8..8+length\*8 | 4 | Float | X Coordinate |
|
||||
+-------------------+-------+-----------+----------------+
|
||||
| 8..12+length\*8 | 4 | Float | Y Coordinate |
|
||||
+-------------------+-------+-----------+----------------+
|
||||
|
||||
27: :ref:`class_Vector3Array`
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+====================+=======+===========+================+
|
||||
| 4 | 4 | Integer | Array Length |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..8+length\*12 | 4 | Float | X Coordinate |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..12+length\*12 | 4 | Float | Y Coordinate |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..16+length\*12 | 4 | Float | Z Coordinate |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
|
||||
28: :ref:`class_ColorArray`
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| Offset | Len | Type | Description |
|
||||
+====================+=======+===========+================+
|
||||
| 4 | 4 | Integer | Array Length |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..8+length\*16 | 4 | Float | Red (0..1) |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..12+length\*16 | 4 | Float | Green (0..1) |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..16+length\*16 | 4 | Float | Blue (0..1) |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
| 8..20+length\*16 | 4 | Float | Alpha (0..1) |
|
||||
+--------------------+-------+-----------+----------------+
|
||||
39
learning/features/misc/data_paths.rst
Normal file
39
learning/features/misc/data_paths.rst
Normal file
@@ -0,0 +1,39 @@
|
||||
.. _doc_data_paths:
|
||||
|
||||
Data paths
|
||||
==========
|
||||
|
||||
Path separators
|
||||
---------------
|
||||
|
||||
For the sake of supporting as many platforms as possible, Godot only
|
||||
accepts unix style path separators (``/``). These work everywhere,
|
||||
including Windows.
|
||||
|
||||
A path like: ``C:\Projects`` will become ``C:/Projects``.
|
||||
|
||||
Resource path
|
||||
-------------
|
||||
|
||||
As mentioned before. Godot considers that a project exists at any
|
||||
given folder that contains an "engine.cfg" text file, even if such
|
||||
file is empty.
|
||||
|
||||
Accessing project files can be done by opening any path with ``res://``
|
||||
as a base. For example, a texture located in the root of the project
|
||||
folder may be opened from the following path: ``res://sometexture.png``.
|
||||
|
||||
Userdata path (persistent data)
|
||||
-------------------------------
|
||||
|
||||
While the project is running, it is a very common scenario that the
|
||||
resource path will be read-only, due to it being inside a package,
|
||||
self contained executable, or system wide install location.
|
||||
|
||||
Storing persistent files in such scenarios should be done by using the
|
||||
``user://`` prefix, for example: ``user://gamesave.txt``.
|
||||
|
||||
In some devices (for example, mobile ad consoles) this path is unique
|
||||
for the app. Under desktop operating systems, the engine uses the
|
||||
typical ~/.Name (check the project name under the settings) in OSX and
|
||||
Linux, and APPDATA/Name for Windows.
|
||||
59
learning/features/misc/encrypting_save_games.rst
Normal file
59
learning/features/misc/encrypting_save_games.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
.. _doc_encrypting_save_games:
|
||||
|
||||
Encrypting save games
|
||||
=====================
|
||||
|
||||
Why?
|
||||
----
|
||||
|
||||
Because the world today is not the world of yesterday. A capitalist
|
||||
oligarchy runs the world and forces us to consume in order to keep the
|
||||
gears of this rotten society on track. As such, the biggest market for
|
||||
video game consumption today is the mobile one. It is a market of poor
|
||||
souls forced to compulsively consume digital content in order to forget
|
||||
the misery of their every day life, commute, or just any other brief
|
||||
free moment they have that they are not using to produce goods or
|
||||
services for the ruling class. These individuals need to keep focusing
|
||||
on their video games (because not doing so will produce them a
|
||||
tremendous existential angst), so they go as far as spending money on
|
||||
them to extend their experience, and their preferred way of doing so is
|
||||
through in-app purchases and virtual currency.
|
||||
|
||||
But, imagine if someone was to find a way to edit the saved games and
|
||||
assign the items and currency without effort? This would be terrible,
|
||||
because it would help players consume the content much faster, and as
|
||||
such run out of it sooner than expected. If this happens they will have
|
||||
nothing that avoids them to think, and the tremendous agony of realizing
|
||||
their own irrelevance would again take over their life.
|
||||
|
||||
No, we definitely do not want this to happen, so let's see how to
|
||||
encrypt savegames and protect the world order.
|
||||
|
||||
How?
|
||||
----
|
||||
|
||||
The class :ref:`File <class_File>` is simple to use, just open a
|
||||
location and read/write data (integers, strings and variants). To create
|
||||
an encrypted file, a passphrase must be provided, like this:
|
||||
|
||||
::
|
||||
|
||||
var f = File.new()
|
||||
var err = f.open_encrypted_with_pass("user://savedata.bin", File.WRITE, "mypass")
|
||||
f.store_var(game_state)
|
||||
f.close()
|
||||
|
||||
This will make the file unreadable to users, but will still not avoid
|
||||
them to share savefiles. To solve this, using the device unique id or
|
||||
some unique user identifier is needed, for example:
|
||||
|
||||
::
|
||||
|
||||
var f = File.new()
|
||||
var err = f.open_encrypted_with_pass("user://savedata.bin", File.WRITE, OS.get_unique_ID())
|
||||
f.store_var(game_state)
|
||||
f.close()
|
||||
|
||||
Note that ``OS.get_unique_ID()`` only works on iOS and Android.
|
||||
|
||||
This is all! Thanks for your cooperation, citizen.
|
||||
37
learning/features/misc/handling_quit_requests.rst
Normal file
37
learning/features/misc/handling_quit_requests.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
.. _doc_handling_quit_requests:
|
||||
|
||||
Handling quit requests
|
||||
======================
|
||||
|
||||
Quitting
|
||||
--------
|
||||
|
||||
Most platforms have the option to request the application to quit. On
|
||||
desktops, this is usually done with the "x" icon on the window titlebar.
|
||||
On Android, the back button is used to quit when on the main screen (and
|
||||
to go back otherwise).
|
||||
|
||||
Handling the notification
|
||||
-------------------------
|
||||
|
||||
The :ref:`MainLoop <class_MainLoop>`
|
||||
has a special notification that is sent to all nodes when quit is
|
||||
requested: MainLoop.NOTIFICATION_WM_QUIT.
|
||||
|
||||
Handling it is done as follows (on any node):
|
||||
|
||||
::
|
||||
|
||||
func _notification(what):
|
||||
if (what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST):
|
||||
get_tree().quit() # default behavior
|
||||
|
||||
When developing mobile apps, quitting is not desired unless the user is
|
||||
on the main screen, so the behavior can be changed.
|
||||
|
||||
It is important to note that by default, Godot apps have the built-in
|
||||
behavior to quit when quit is requested, this can be changed:
|
||||
|
||||
::
|
||||
|
||||
get_tree().set_auto_accept_quit(false)
|
||||
107
learning/features/misc/internationalizing_games.rst
Normal file
107
learning/features/misc/internationalizing_games.rst
Normal file
@@ -0,0 +1,107 @@
|
||||
.. _doc_internationalizing_games:
|
||||
|
||||
Internationalizing games
|
||||
========================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Sería excelente que el mundo hablara solo un idioma. Unfortunately for
|
||||
us developers, that is not the case. While not generally a big
|
||||
requirement when developing indie or niche games, it is also very common
|
||||
that games going into a more massive market require localization.
|
||||
|
||||
Godot offers many tools to make this process more straightforward, so
|
||||
this tutorial is more like a collection of tips and tricks.
|
||||
|
||||
Localization is usually done by specific studios hired for the job and,
|
||||
despite the huge amount of software and file formats available for this,
|
||||
the most common way to do localization to this day is still with
|
||||
spreadsheets. The process of creating the spreadsheets and importing
|
||||
them is already covered in the :ref:`doc_importing_translations` tutorial, so this
|
||||
one could be seen more like a follow up to that one.
|
||||
|
||||
Configuring the imported translation
|
||||
------------------------------------
|
||||
|
||||
The translations can get updated and re-imported when they change, but
|
||||
they still have to be added to the project. This is done in Scene
|
||||
> Project Settings > Localization:
|
||||
|
||||
.. image:: /img/localization_dialog.png
|
||||
|
||||
This dialog allows to add or remove translations project-wide.
|
||||
|
||||
Localizing resources
|
||||
--------------------
|
||||
|
||||
It is also possible to instruct Godot to open alternative versions of
|
||||
assets (resources) depending on the current language. For this the
|
||||
"Remaps" tab exists:
|
||||
|
||||
.. image:: /img/localization_remaps.png
|
||||
|
||||
Select the resource to be remapped, and the alternatives for each
|
||||
locale.
|
||||
|
||||
Converting keys to text
|
||||
-----------------------
|
||||
|
||||
Some controls such as :ref:`Button <class_Button>`, :ref:`Label <class_Label>`,
|
||||
etc. will automatically fetch a translation each time they are set a key
|
||||
instead of a text. For example, if a label is assigned
|
||||
"MAIN_SCREEN_GREETING1" and a key to different languages exists in the
|
||||
translations, this will be automatically converted. This process is done
|
||||
upon load though, so if the project in question has a dialog that allows
|
||||
changing the language in the settings, the scenes (or at least the
|
||||
settings scene) will have to be re-loaded for new text to have effect.
|
||||
|
||||
For code, the :ref:`Object.tr() <class_Object_tr>`
|
||||
function can be used. This will just look-up the text into the
|
||||
translations and convert it if found:
|
||||
|
||||
::
|
||||
|
||||
level.set_text(tr("LEVEL_5_NAME"))
|
||||
status.set_text(tr("GAME_STATUS_" + str(status_index)))
|
||||
|
||||
Making controls resizeable
|
||||
--------------------------
|
||||
|
||||
The same text in different languages can vary greatly in length. For
|
||||
this, make sure to read the tutorial on :ref:`doc_size_and_anchors`, as having
|
||||
dynamically adjusted control sizes may help.
|
||||
:ref:`Container <class_Container>` can be very useful, as well as the multiple options in
|
||||
:ref:`Label <class_Label>` for text wrapping.
|
||||
|
||||
TranslationServer
|
||||
-----------------
|
||||
|
||||
Godot has a server for handling the low level translation management
|
||||
called the :ref:`TranslationServer <class_TranslationServer>`.
|
||||
Translations can be added or removed during run-time, and the current
|
||||
language be changed too.
|
||||
|
||||
Command line
|
||||
------------
|
||||
|
||||
Language can be tested when running Godot from command line. For
|
||||
example, to test a game in french, the following arguments can be
|
||||
supplied:
|
||||
|
||||
::
|
||||
|
||||
c:\MyGame> godot -lang fr
|
||||
|
||||
Translating the project name
|
||||
----------------------------
|
||||
|
||||
The project name becomes the app name when exporting to different
|
||||
operating systems and platforms. To specify the project name in more
|
||||
than one language, create a new setting application/name in the project
|
||||
settings dialog and append the locale identifier to it. For example:
|
||||
|
||||
.. image:: /img/localized_name.png
|
||||
|
||||
As always, If you don't know the code of a language or zone, :ref:`check the
|
||||
list <doc_locales>`.
|
||||
317
learning/features/misc/locales.rst
Normal file
317
learning/features/misc/locales.rst
Normal file
@@ -0,0 +1,317 @@
|
||||
.. _doc_locales:
|
||||
|
||||
Locales
|
||||
=======
|
||||
|
||||
This is the list of supported locales and variants in the engine. It's
|
||||
based on the Unix standard locale strings:
|
||||
|
||||
+--------------+------------------------------------+
|
||||
| Locale | Language and Variant |
|
||||
+==============+====================================+
|
||||
| ar | Arabic |
|
||||
+--------------+------------------------------------+
|
||||
| ar_AE | Arabic (United Arab Emirates) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_BH | Arabic (Bahrain) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_DZ | Arabic (Algeria) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_EG | Arabic (Egypt) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_IQ | Arabic (Iraq) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_JO | Arabic (Jordan) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_KW | Arabic (Kuwait) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_LB | Arabic (Lebanon) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_LY | Arabic (Libya) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_MA | Arabic (Morocco) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_OM | Arabic (Oman) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_QA | Arabic (Qatar) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_SA | Arabic (Saudi Arabia) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_SD | Arabic (Sudan) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_SY | Arabic (Syria) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_TN | Arabic (Tunisia) |
|
||||
+--------------+------------------------------------+
|
||||
| ar_YE | Arabic (Yemen) |
|
||||
+--------------+------------------------------------+
|
||||
| be | Belarusian |
|
||||
+--------------+------------------------------------+
|
||||
| be_BY | Belarusian (Belarus) |
|
||||
+--------------+------------------------------------+
|
||||
| bg | Bulgarian |
|
||||
+--------------+------------------------------------+
|
||||
| bg_BG | Bulgarian (Bulgaria) |
|
||||
+--------------+------------------------------------+
|
||||
| ca | Catalan |
|
||||
+--------------+------------------------------------+
|
||||
| ca_ES | Catalan (Spain) |
|
||||
+--------------+------------------------------------+
|
||||
| cs | Czech |
|
||||
+--------------+------------------------------------+
|
||||
| cs_CZ | Czech (Czech Republic) |
|
||||
+--------------+------------------------------------+
|
||||
| da | Danish |
|
||||
+--------------+------------------------------------+
|
||||
| da_DK | Danish (Denmark) |
|
||||
+--------------+------------------------------------+
|
||||
| de | German |
|
||||
+--------------+------------------------------------+
|
||||
| de_AT | German (Austria) |
|
||||
+--------------+------------------------------------+
|
||||
| de_CH | German (Switzerland) |
|
||||
+--------------+------------------------------------+
|
||||
| de_DE | German (Germany) |
|
||||
+--------------+------------------------------------+
|
||||
| de_LU | German (Luxembourg) |
|
||||
+--------------+------------------------------------+
|
||||
| el | Greek |
|
||||
+--------------+------------------------------------+
|
||||
| el_CY | Greek (Cyprus) |
|
||||
+--------------+------------------------------------+
|
||||
| el_GR | Greek (Greece) |
|
||||
+--------------+------------------------------------+
|
||||
| en | English |
|
||||
+--------------+------------------------------------+
|
||||
| en_AU | English (Australia) |
|
||||
+--------------+------------------------------------+
|
||||
| en_CA | English (Canada) |
|
||||
+--------------+------------------------------------+
|
||||
| en_GB | English (United Kingdom) |
|
||||
+--------------+------------------------------------+
|
||||
| en_IE | English (Ireland) |
|
||||
+--------------+------------------------------------+
|
||||
| en_IN | English (India) |
|
||||
+--------------+------------------------------------+
|
||||
| en_MT | English (Malta) |
|
||||
+--------------+------------------------------------+
|
||||
| en_NZ | English (New Zealand) |
|
||||
+--------------+------------------------------------+
|
||||
| en_PH | English (Philippines) |
|
||||
+--------------+------------------------------------+
|
||||
| en_SG | English (Singapore) |
|
||||
+--------------+------------------------------------+
|
||||
| en_US | English (United States) |
|
||||
+--------------+------------------------------------+
|
||||
| en_ZA | English (South Africa) |
|
||||
+--------------+------------------------------------+
|
||||
| es | Spanish |
|
||||
+--------------+------------------------------------+
|
||||
| es_AR | Spanish (Argentina) |
|
||||
+--------------+------------------------------------+
|
||||
| es_BO | Spanish (Bolivia) |
|
||||
+--------------+------------------------------------+
|
||||
| es_CL | Spanish (Chile) |
|
||||
+--------------+------------------------------------+
|
||||
| es_CO | Spanish (Colombia) |
|
||||
+--------------+------------------------------------+
|
||||
| es_CR | Spanish (Costa Rica) |
|
||||
+--------------+------------------------------------+
|
||||
| es_DO | Spanish (Dominican Republic) |
|
||||
+--------------+------------------------------------+
|
||||
| es_EC | Spanish (Ecuador) |
|
||||
+--------------+------------------------------------+
|
||||
| es_ES | Spanish (Spain) |
|
||||
+--------------+------------------------------------+
|
||||
| es_GT | Spanish (Guatemala) |
|
||||
+--------------+------------------------------------+
|
||||
| es_HN | Spanish (Honduras) |
|
||||
+--------------+------------------------------------+
|
||||
| es_MX | Spanish (Mexico) |
|
||||
+--------------+------------------------------------+
|
||||
| es_NI | Spanish (Nicaragua) |
|
||||
+--------------+------------------------------------+
|
||||
| es_PA | Spanish (Panama) |
|
||||
+--------------+------------------------------------+
|
||||
| es_PE | Spanish (Peru) |
|
||||
+--------------+------------------------------------+
|
||||
| es_PR | Spanish (Puerto Rico) |
|
||||
+--------------+------------------------------------+
|
||||
| es_PY | Spanish (Paraguay) |
|
||||
+--------------+------------------------------------+
|
||||
| es_SV | Spanish (El Salvador) |
|
||||
+--------------+------------------------------------+
|
||||
| es_US | Spanish (United States) |
|
||||
+--------------+------------------------------------+
|
||||
| es_UY | Spanish (Uruguay) |
|
||||
+--------------+------------------------------------+
|
||||
| es_VE | Spanish (Venezuela) |
|
||||
+--------------+------------------------------------+
|
||||
| et | Estonian |
|
||||
+--------------+------------------------------------+
|
||||
| et_EE | Estonian (Estonia) |
|
||||
+--------------+------------------------------------+
|
||||
| fi | Finnish |
|
||||
+--------------+------------------------------------+
|
||||
| fi_FI | Finnish (Finland) |
|
||||
+--------------+------------------------------------+
|
||||
| fr | French |
|
||||
+--------------+------------------------------------+
|
||||
| fr_BE | French (Belgium) |
|
||||
+--------------+------------------------------------+
|
||||
| fr_CA | French (Canada) |
|
||||
+--------------+------------------------------------+
|
||||
| fr_CH | French (Switzerland) |
|
||||
+--------------+------------------------------------+
|
||||
| fr_FR | French (France) |
|
||||
+--------------+------------------------------------+
|
||||
| fr_LU | French (Luxembourg) |
|
||||
+--------------+------------------------------------+
|
||||
| ga | Irish |
|
||||
+--------------+------------------------------------+
|
||||
| ga_IE | Irish (Ireland) |
|
||||
+--------------+------------------------------------+
|
||||
| hi | Hindi (India) |
|
||||
+--------------+------------------------------------+
|
||||
| hi_IN | Hindi (India) |
|
||||
+--------------+------------------------------------+
|
||||
| hr | Croatian |
|
||||
+--------------+------------------------------------+
|
||||
| hr_HR | Croatian (Croatia) |
|
||||
+--------------+------------------------------------+
|
||||
| hu | Hungarian |
|
||||
+--------------+------------------------------------+
|
||||
| hu_HU | Hungarian (Hungary) |
|
||||
+--------------+------------------------------------+
|
||||
| in | Indonesian |
|
||||
+--------------+------------------------------------+
|
||||
| in_ID | Indonesian (Indonesia) |
|
||||
+--------------+------------------------------------+
|
||||
| is | Icelandic |
|
||||
+--------------+------------------------------------+
|
||||
| is_IS | Icelandic (Iceland) |
|
||||
+--------------+------------------------------------+
|
||||
| it | Italian |
|
||||
+--------------+------------------------------------+
|
||||
| it_CH | Italian (Switzerland) |
|
||||
+--------------+------------------------------------+
|
||||
| it_IT | Italian (Italy) |
|
||||
+--------------+------------------------------------+
|
||||
| iw | Hebrew |
|
||||
+--------------+------------------------------------+
|
||||
| iw_IL | Hebrew (Israel) |
|
||||
+--------------+------------------------------------+
|
||||
| ja | Japanese |
|
||||
+--------------+------------------------------------+
|
||||
| ja_JP | Japanese (Japan) |
|
||||
+--------------+------------------------------------+
|
||||
| ja_JP_JP | Japanese (Japan,JP) |
|
||||
+--------------+------------------------------------+
|
||||
| ko | Korean |
|
||||
+--------------+------------------------------------+
|
||||
| ko_KR | Korean (South Korea) |
|
||||
+--------------+------------------------------------+
|
||||
| lt | Lithuanian |
|
||||
+--------------+------------------------------------+
|
||||
| lt_LT | Lithuanian (Lithuania) |
|
||||
+--------------+------------------------------------+
|
||||
| lv | Latvian |
|
||||
+--------------+------------------------------------+
|
||||
| lv_LV | Latvian (Latvia) |
|
||||
+--------------+------------------------------------+
|
||||
| mk | Macedonian |
|
||||
+--------------+------------------------------------+
|
||||
| mk_MK | Macedonian (Macedonia) |
|
||||
+--------------+------------------------------------+
|
||||
| ms | Malay |
|
||||
+--------------+------------------------------------+
|
||||
| ms_MY | Malay (Malaysia) |
|
||||
+--------------+------------------------------------+
|
||||
| mt | Maltese |
|
||||
+--------------+------------------------------------+
|
||||
| mt_MT | Maltese (Malta) |
|
||||
+--------------+------------------------------------+
|
||||
| nl | Dutch |
|
||||
+--------------+------------------------------------+
|
||||
| nl_BE | Dutch (Belgium) |
|
||||
+--------------+------------------------------------+
|
||||
| nl_NL | Dutch (Netherlands) |
|
||||
+--------------+------------------------------------+
|
||||
| no | Norwegian |
|
||||
+--------------+------------------------------------+
|
||||
| no_NO | Norwegian (Norway) |
|
||||
+--------------+------------------------------------+
|
||||
| no_NO_NY | Norwegian (Norway,Nynorsk) |
|
||||
+--------------+------------------------------------+
|
||||
| pl | Polish |
|
||||
+--------------+------------------------------------+
|
||||
| pl_PL | Polish (Poland) |
|
||||
+--------------+------------------------------------+
|
||||
| pt | Portuguese |
|
||||
+--------------+------------------------------------+
|
||||
| pt_BR | Portuguese (Brazil) |
|
||||
+--------------+------------------------------------+
|
||||
| pt_PT | Portuguese (Portugal) |
|
||||
+--------------+------------------------------------+
|
||||
| ro | Romanian |
|
||||
+--------------+------------------------------------+
|
||||
| ro_RO | Romanian (Romania) |
|
||||
+--------------+------------------------------------+
|
||||
| ru | Russian |
|
||||
+--------------+------------------------------------+
|
||||
| ru_RU | Russian (Russia) |
|
||||
+--------------+------------------------------------+
|
||||
| sk | Slovak |
|
||||
+--------------+------------------------------------+
|
||||
| sk_SK | Slovak (Slovakia) |
|
||||
+--------------+------------------------------------+
|
||||
| sl | Slovenian |
|
||||
+--------------+------------------------------------+
|
||||
| sl_SI | Slovenian (Slovenia) |
|
||||
+--------------+------------------------------------+
|
||||
| sq | Albanian |
|
||||
+--------------+------------------------------------+
|
||||
| sq_AL | Albanian (Albania) |
|
||||
+--------------+------------------------------------+
|
||||
| sr | Serbian |
|
||||
+--------------+------------------------------------+
|
||||
| sr_BA | Serbian (Bosnia and Herzegovina) |
|
||||
+--------------+------------------------------------+
|
||||
| sr_CS | Serbian (Serbia and Montenegro) |
|
||||
+--------------+------------------------------------+
|
||||
| sr_ME | Serbian (Montenegro) |
|
||||
+--------------+------------------------------------+
|
||||
| sr_RS | Serbian (Serbia) |
|
||||
+--------------+------------------------------------+
|
||||
| sv | Swedish |
|
||||
+--------------+------------------------------------+
|
||||
| sv_SE | Swedish (Sweden) |
|
||||
+--------------+------------------------------------+
|
||||
| th | Thai |
|
||||
+--------------+------------------------------------+
|
||||
| th_TH | Thai (Thailand) |
|
||||
+--------------+------------------------------------+
|
||||
| th_TH_TH | Thai (Thailand,TH) |
|
||||
+--------------+------------------------------------+
|
||||
| tr | Turkish |
|
||||
+--------------+------------------------------------+
|
||||
| tr_TR | Turkish (Turkey) |
|
||||
+--------------+------------------------------------+
|
||||
| uk | Ukrainian |
|
||||
+--------------+------------------------------------+
|
||||
| uk_UA | Ukrainian (Ukraine) |
|
||||
+--------------+------------------------------------+
|
||||
| vi | Vietnamese |
|
||||
+--------------+------------------------------------+
|
||||
| vi_VN | Vietnamese (Vietnam) |
|
||||
+--------------+------------------------------------+
|
||||
| zh | Chinese |
|
||||
+--------------+------------------------------------+
|
||||
| zh_CN | Chinese (China) |
|
||||
+--------------+------------------------------------+
|
||||
| zh_HK | Chinese (Hong Kong) |
|
||||
+--------------+------------------------------------+
|
||||
| zh_SG | Chinese (Singapore) |
|
||||
+--------------+------------------------------------+
|
||||
| zh_TW | Chinese (Taiwan) |
|
||||
+--------------+------------------------------------+
|
||||
96
learning/features/misc/pausing_games.rst
Normal file
96
learning/features/misc/pausing_games.rst
Normal file
@@ -0,0 +1,96 @@
|
||||
.. _doc_pausing_games:
|
||||
|
||||
Pausing games
|
||||
=============
|
||||
|
||||
Pause?
|
||||
------
|
||||
|
||||
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.
|
||||
However this is not as simple as it seems. The game might be stopped,
|
||||
but it might be desirable that some menus and animations continue
|
||||
working.
|
||||
|
||||
Implementing a fine-grained control for what can be paused (and what can
|
||||
not) is a lot of work, so a simple framework for pausing is provided in
|
||||
Godot.
|
||||
|
||||
How pausing works
|
||||
-----------------
|
||||
|
||||
To set pause mode, the pause state must be set. This is done by calling
|
||||
:ref:`SceneTree.set_pause() <class_SceneTree_set_pause>`
|
||||
with a "true" argument:
|
||||
|
||||
::
|
||||
|
||||
get_tree().set_pause(true)
|
||||
|
||||
Doing so will have the following behavior:
|
||||
|
||||
- 2D and 3D physics will be stopped.
|
||||
- _process and _fixed_process will not be called anymore in nodes.
|
||||
- _input and _input_event will not be called anymore either.
|
||||
|
||||
This effectively stops the whole game. Calling this function from a
|
||||
script, by default, will result in an unrecoverable state (nothing will
|
||||
work anymore!).
|
||||
|
||||
White-listing nodes
|
||||
-------------------
|
||||
|
||||
Before enabling pause, make sure that nodes that must keep working
|
||||
during pause are white-listed. This is done by editing the "Pause Mode"
|
||||
property in a node:
|
||||
|
||||
.. image:: /img/pausemode.png
|
||||
|
||||
By default all nodes have this property in the "Inherit" state. This
|
||||
means, that they will only process (or not) depending on what this same
|
||||
property is set on the parent node. If the parent is set to "Inherit" ,
|
||||
then the grandparent will be checked and so on. Ultimately, 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.
|
||||
|
||||
So the three possible states for a node are:
|
||||
|
||||
- **Inherit**: Process depending on the state of the parent,
|
||||
grandparent, etc. The first parent that has a non-Inherit state.
|
||||
- **Stop**: Stop the node no matter what (and children in Inherit
|
||||
mode). When paused this node will not process.
|
||||
- **Process**: Process the node no matter what (and children in Inherit
|
||||
mode). Paused or not this node will process.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
An example of this is creating a popup or panel with controls inside,
|
||||
and set its pause mode to "Process" then just hide it:
|
||||
|
||||
.. image:: /img/pause_popup.png
|
||||
|
||||
Just 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.
|
||||
|
||||
::
|
||||
|
||||
func _on_pause_button_pressed():
|
||||
get_tree().set_pause(true)
|
||||
get_node("pause_popup").show()
|
||||
|
||||
To remove the pause, just do the opposite when the pause screen is
|
||||
closed:
|
||||
|
||||
::
|
||||
|
||||
func _on_pause_popup_close_pressed():
|
||||
get_node("pause_popup").hide()
|
||||
get_tree().set_pause(false)
|
||||
|
||||
And that should be all!
|
||||
155
learning/features/misc/saving_games.rst
Normal file
155
learning/features/misc/saving_games.rst
Normal file
@@ -0,0 +1,155 @@
|
||||
.. _doc_saving_games:
|
||||
|
||||
Saving games
|
||||
============
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Save games can be complicated. It can be desired to store more
|
||||
information than the current level or number of stars earned on a level.
|
||||
More advanced save games may need to store additional information about
|
||||
an arbitrary number of objects. This will allow the save function to
|
||||
scale as the game grows more complex.
|
||||
|
||||
Identify persistent objects
|
||||
---------------------------
|
||||
|
||||
First we should identify what objects we want to keep between game
|
||||
sessions and what information we want to keep from those objects. For
|
||||
this tutorial, we will use groups to mark and handle objects to be saved
|
||||
but other methods are certainly possible.
|
||||
|
||||
We will start by adding objects we wish to save to the "Persist" group.
|
||||
As in the :ref:`doc_scripting_continued` tutorial, we can do this through
|
||||
the GUI or through script. Let's add the relevant nodes using the GUI:
|
||||
|
||||
.. image:: /img/groups.png
|
||||
|
||||
Once this is done when we need to save the game we can get all objects
|
||||
to save them and then tell them all to save with this script:
|
||||
|
||||
::
|
||||
|
||||
var savenodes = get_tree().get_nodes_in_group("Persist")
|
||||
for i in savenodes:
|
||||
# Now we can call our save function on each node.
|
||||
|
||||
Serializing
|
||||
-----------
|
||||
|
||||
The next step is to serialize the data. This makes it much easier to
|
||||
read and store to disk. In this case, we're assuming each member of
|
||||
group Persist is an instanced node and thus has a path. GDScript
|
||||
has helper functions for this, such as :ref:`Dictionary.to_json()
|
||||
<class_Dictionary_to_json>` and :ref:`Dictionary.parse_json()
|
||||
<class_Dictionary_parse_json>`, so we will use a dictionary. Our node needs to
|
||||
contain a save function that returns this data. The save function will look
|
||||
like this:
|
||||
|
||||
::
|
||||
|
||||
func save():
|
||||
var savedict = {
|
||||
filename=get_filename(),
|
||||
parent=get_parent().get_path(),
|
||||
posx=get_pos().x, #Vector2 is not supported by json
|
||||
posy=get_pos().y,
|
||||
attack=attack,
|
||||
defense=defense,
|
||||
currenthealth=currenthealth,
|
||||
maxhealth=maxhealth,
|
||||
damage=damage,
|
||||
regen=regen,
|
||||
experience=experience,
|
||||
TNL=TNL,
|
||||
level=level,
|
||||
AttackGrowth=AttackGrowth,
|
||||
DefenseGrowth=DefenseGrowth,
|
||||
HealthGrowth=HealthGrowth,
|
||||
isalive=isalive,
|
||||
last_attack=last_attack
|
||||
}
|
||||
return savedict
|
||||
|
||||
This gives us a dictionary with the style
|
||||
``{ "variable_name":that_variables_value }`` which will be useful when
|
||||
loading.
|
||||
|
||||
Saving and reading data
|
||||
-----------------------
|
||||
|
||||
As covered in the :ref:`doc_filesystem` tutorial, we'll need to open a file
|
||||
and write to it and then later read from it. Now that we have a way to
|
||||
call our groups and get their relevant data, let's use to_json() to
|
||||
convert it into an easily stored string and store them in a file. Doing
|
||||
it this way ensures that each line is its own object so we have an easy
|
||||
way to pull the data out of the file as well.
|
||||
|
||||
::
|
||||
|
||||
# Note: This can be called from anywhere inside the tree. This function is path independent.
|
||||
# Go through everything in the persist category and ask them to return a dict of relevant variables
|
||||
func save_game():
|
||||
var savegame = File.new()
|
||||
savegame.open("user://savegame.save", File.WRITE)
|
||||
var savenodes = get_tree().get_nodes_in_group("Persist")
|
||||
for i in savenodes:
|
||||
var nodedata = i.save()
|
||||
savegame.store_line(nodedata.to_json())
|
||||
savegame.close()
|
||||
|
||||
Game saved! Loading is fairly simple as well. For that we'll read each
|
||||
line, use parse_json() to read it back to a dict, and then iterate over
|
||||
the dict to read our values. But we'll need to first create the object
|
||||
and we can use the filename and parent values to achieve that. Here is our
|
||||
load function:
|
||||
|
||||
::
|
||||
|
||||
# Note: This can be called from anywhere inside the tree. This function is path independent.
|
||||
func load_game():
|
||||
var savegame = File.new()
|
||||
if !savegame.file_exists("user://savegame.save"):
|
||||
return #Error! We don't have a save to load
|
||||
|
||||
# We need to revert the game state so we're not cloning objects during loading. This will vary wildly depending on the needs of a project, so take care with this step.
|
||||
# For our example, we will accomplish this by deleting savable objects.
|
||||
var savenodes = get_tree().get_nodes_in_group("Persist")
|
||||
for i in savenodes:
|
||||
i.queue_free()
|
||||
|
||||
# Load the file line by line and process that dictionary to restore the object it represents
|
||||
var currentline = {} # dict.parse_json() requires a declared dict.
|
||||
savegame.open("user://savegame.save", File.READ)
|
||||
while (!savegame.eof_reached()):
|
||||
currentline.parse_json(savegame.get_line())
|
||||
# First we need to create the object and add it to the tree and set its position.
|
||||
var newobject = load(currentline["filename"]).instance()
|
||||
get_node(currentline["parent"]).add_child(newobject)
|
||||
newobject.set_pos(Vector2(currentline["posx"],currentline["posy"]))
|
||||
# Now we set the remaining variables.
|
||||
for i in currentline.keys():
|
||||
if (i == "filename" or i == "parent" or i == "posx" or i == "posy"):
|
||||
continue
|
||||
newobject.set(i, currentline[i])
|
||||
savegame.close()
|
||||
|
||||
And now we can save and load an arbitrary number of objects laid out
|
||||
almost anywhere across the scene tree! Each object can store different
|
||||
data depending on what it needs to save.
|
||||
|
||||
Some notes
|
||||
----------
|
||||
|
||||
We may have glossed over a step, but setting the game state to one fit
|
||||
to start loading data can be very complicated. This step will need to be
|
||||
heavily customized based on the needs of an individual project.
|
||||
|
||||
This implementation assumes no Persist objects are children of other
|
||||
Persist objects. Doing so would create invalid paths. If this is one of
|
||||
the needs of a project this needs to be considered. Saving objects in
|
||||
stages (parent objects first) so they are available when child objects
|
||||
are loaded will make sure they're available for the add_child() call.
|
||||
There will also need to be some way to link children to parents as the
|
||||
nodepath will likely be invalid.
|
||||
100
learning/features/networking/http_client_class.rst
Normal file
100
learning/features/networking/http_client_class.rst
Normal file
@@ -0,0 +1,100 @@
|
||||
.. _doc_http_client_class:
|
||||
|
||||
HTTP client class
|
||||
=================
|
||||
|
||||
Here's an example of using the :ref:`HTTPClient <class_HTTPClient>`
|
||||
class. It's just a script, so it can be run by executing:
|
||||
|
||||
::
|
||||
|
||||
c:\godot> godot -s http_test.gd
|
||||
|
||||
It will connect and fetch a website.
|
||||
|
||||
::
|
||||
|
||||
extends SceneTree
|
||||
|
||||
# HTTPClient demo
|
||||
# This simple class can do HTTP requests, it will not block but it needs to be polled
|
||||
|
||||
func _init():
|
||||
|
||||
var err=0
|
||||
var http = HTTPClient.new() # Create the Client
|
||||
|
||||
var err = http.connect("www.php.net",80) # Connect to host/port
|
||||
assert(err==OK) # Make sure connection was OK
|
||||
|
||||
# Wait until resolved and connected
|
||||
while( http.get_status()==HTTPClient.STATUS_CONNECTING or http.get_status()==HTTPClient.STATUS_RESOLVING):
|
||||
http.poll()
|
||||
print("Connecting..")
|
||||
OS.delay_msec(500)
|
||||
|
||||
assert( http.get_status() == HTTPClient.STATUS_CONNECTED ) # Could not connect
|
||||
|
||||
# Some headers
|
||||
|
||||
var headers=[
|
||||
"User-Agent: Pirulo/1.0 (Godot)",
|
||||
"Accept: */*"
|
||||
]
|
||||
|
||||
err = http.request(HTTPClient.METHOD_GET,"/ChangeLog-5.php",headers) # Request a page from the site (this one was chunked..)
|
||||
|
||||
assert( err == OK ) # Make sure all is OK
|
||||
|
||||
while (http.get_status() == HTTPClient.STATUS_REQUESTING):
|
||||
# Keep polling until the request is going on
|
||||
http.poll()
|
||||
print("Requesting..")
|
||||
OS.delay_msec(500)
|
||||
|
||||
|
||||
assert( http.get_status() == HTTPClient.STATUS_BODY or http.get_status() == HTTPClient.STATUS_CONNECTED ) # Make sure request finished well.
|
||||
|
||||
print("response? ",http.has_response()) # Site might not have a response.
|
||||
|
||||
|
||||
if (http.has_response()):
|
||||
# If there is a response..
|
||||
|
||||
var headers = http.get_response_headers_as_dictionary() # Get response headers
|
||||
print("code: ",http.get_response_code()) # Show response code
|
||||
print("**headers:\\n",headers) # Show headers
|
||||
|
||||
# Getting the HTTP Body
|
||||
|
||||
if (http.is_response_chunked()):
|
||||
# Does it use chunks?
|
||||
print("Response is Chunked!")
|
||||
else:
|
||||
# Or just plain Content-Length
|
||||
var bl = http.get_response_body_length()
|
||||
print("Response Length: ",bl)
|
||||
|
||||
# This method works for both anyway
|
||||
|
||||
var rb = RawArray() # Array that will hold the data
|
||||
|
||||
while(http.get_status()==HTTPClient.STATUS_BODY):
|
||||
# While there is body left to be read
|
||||
http.poll()
|
||||
var chunk = http.read_response_body_chunk() # Get a chunk
|
||||
if (chunk.size()==0):
|
||||
# Got nothing, wait for buffers to fill a bit
|
||||
OS.delay_usec(1000)
|
||||
else:
|
||||
rb = rb + chunk # Append to read buffer
|
||||
|
||||
|
||||
# Done!
|
||||
|
||||
print("bytes got: ",rb.size())
|
||||
var text = rb.get_string_from_ascii()
|
||||
print("Text: ",text)
|
||||
|
||||
|
||||
quit()
|
||||
72
learning/features/networking/ssl_certificates.rst
Normal file
72
learning/features/networking/ssl_certificates.rst
Normal file
@@ -0,0 +1,72 @@
|
||||
.. _doc_ssl_certificates:
|
||||
|
||||
SSL certificates
|
||||
================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
It is often desired to use SSL connections for communications to avoid
|
||||
"man in the middle" attacks. Godot has a connection wrapper,
|
||||
:ref:`StreamPeerSSL <class_StreamPeerSSL>`,
|
||||
which can take a regular connection and add security around it. The
|
||||
:ref:`HTTPClient <class_HTTPClient>`
|
||||
class also supports HTTPS by using this same wrapper.
|
||||
|
||||
For SSL to work, certificates need to be provided. A .crt file must be
|
||||
specified in the project settings:
|
||||
|
||||
.. image:: /img/ssl_certs.png
|
||||
|
||||
This file should contain any number of public certificates in
|
||||
http://en.wikipedia.org/wiki/Privacy-enhanced_Electronic_Mail format.
|
||||
|
||||
Of course, remember to add .crt as filter so the exporter recognizes
|
||||
this when exporting your project.
|
||||
|
||||
.. image:: /img/add_crt.png
|
||||
|
||||
There are two ways to obtain certificates:
|
||||
|
||||
Approach 1: self signed cert
|
||||
----------------------------
|
||||
|
||||
The first approach is the simplest, just generate a private and public
|
||||
key pair, and put the public pair in the .crt file (again, in PEM
|
||||
format). The private key should go to your server.
|
||||
|
||||
OpenSSL has `some
|
||||
documentation <https://www.openssl.org/docs/HOWTO/keys.txt>`__ about
|
||||
this. This approach also **does not require domain validation** nor
|
||||
requires you to spend a considerable amount of money in purchasing
|
||||
certificates from a CA.
|
||||
|
||||
Approach 2: CA cert
|
||||
-------------------
|
||||
|
||||
The second approach consists of using a certificate authority (CA)
|
||||
such as Verisign, Geotrust, etc. This is a more cumbersome process,
|
||||
but it's more "official" and ensures your identity is clearly
|
||||
represented.
|
||||
|
||||
Unless you are working with large companies or corporations, or need
|
||||
to connect to someone else's servers (i.e., connecting to Google or some
|
||||
other REST API provider via HTTPS) this method is not as useful.
|
||||
|
||||
Also, when using a CA issued cert, **you must enable domain
|
||||
validation**, to ensure the domain you are connecting to is the one
|
||||
intended, otherwise any website can issue any certificate in the same CA
|
||||
and it will work.
|
||||
|
||||
If you are using Linux, you can use the supplied certs file, generally
|
||||
located in:
|
||||
|
||||
::
|
||||
|
||||
/etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
This file allows HTTPS connections to virtually any website (i.e.,
|
||||
Google, Microsoft, etc.).
|
||||
|
||||
Or just pick any of the more specific certificates there if you are
|
||||
connecting to a specific one.
|
||||
249
learning/features/physics/kinematic_character_2d.rst
Normal file
249
learning/features/physics/kinematic_character_2d.rst
Normal file
@@ -0,0 +1,249 @@
|
||||
.. _doc_kinematic_character_2d:
|
||||
|
||||
Kinematic Character (2D)
|
||||
========================
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Yes, the name sounds strange. "Kinematic Character". What is that?
|
||||
The reason is that when physics engines came out, they were called
|
||||
"Dynamics" engines (because they dealt mainly with collision
|
||||
responses). Many attempts were made to create a character controller
|
||||
using the dynamics engines but it wasn't as easy as it seems. Godot
|
||||
has one of the best implementations of dynamic character controller
|
||||
you can find (as it can be seen in the 2d/platformer demo), but using
|
||||
it requieres a considerable level of skill and understanding of
|
||||
physics engines (or a lot of patience with trial and error).
|
||||
|
||||
Some physics engines such as Havok seem to swear by dynamic character
|
||||
controllers as the best alternative, while others (PhysX) would rather
|
||||
promote the Kinematic one.
|
||||
|
||||
So, what is really the difference?:
|
||||
|
||||
- A **dynamic character controller** uses a rigid body with infinite
|
||||
inertial tensor. Basically, it's a rigid body that can't rotate.
|
||||
Physics engines always let objects collide, then solve their
|
||||
collisions all together. This makes dynamic character controllers
|
||||
able to interact with other physics objects seamlessly (as seen in
|
||||
the platformer demo), however these interactions are not always
|
||||
predictable. Collisions also can take more than one frame to be
|
||||
solved, so a few collisions may seem to displace a tiny bit. Those
|
||||
problems can be fixed, but require a certain amount of skill.
|
||||
- A **kinematic character controller** is assumed to always begin in a
|
||||
non-colliding state, and will always move to a non colliding state.
|
||||
If it starts in a colliding state, it will try to free itself (like
|
||||
rigid bodies do) but this is the exception, not the rule. This makes
|
||||
their control and motion a lot more predictable and easier to
|
||||
program. However, as a downside, they can't directly interact with
|
||||
other physics objects (unless done by hand in code).
|
||||
|
||||
This short tutorial will focus on the kinematic character controller.
|
||||
Basically, the oldschool way of handling collisions (which is not
|
||||
necessarily simpler under the hood, but well hidden and presented as a
|
||||
nice and simple API).
|
||||
|
||||
Fixed process
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
To manage the logic of a kinematic body or character, it is always
|
||||
advised to use fixed process, which is called the same amount of times
|
||||
per second, always. This makes physics and motion calculation work in a
|
||||
more predictable way than using regular process, which might have spikes
|
||||
or lose precision if the frame rate is too high or too low.
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody2D
|
||||
|
||||
func _fixed_process(delta):
|
||||
pass
|
||||
|
||||
func _ready():
|
||||
set_fixed_process(true)
|
||||
|
||||
Scene setup
|
||||
~~~~~~~~~~~
|
||||
|
||||
To have something to test, here's the scene (from the tilemap tutorial):
|
||||
:download:`kbscene.zip </files/kbscene.zip>`. We'll be creating a new scene
|
||||
for the character. Use the robot sprite and create a scene like this:
|
||||
|
||||
.. image:: /img/kbscene.png
|
||||
|
||||
Let's add a circular collision shape to the collision body, create a new
|
||||
CircleShape2D in the shape property of CollisionShape2D. Set the radius
|
||||
to 30:
|
||||
|
||||
.. image:: /img/kbradius.png
|
||||
|
||||
**Note: As mentioned before in the physics tutorial, the physics engine
|
||||
can't handle scale on most types of shapes (only collision polygons,
|
||||
planes and segments work), so always change the parameters (such as
|
||||
radius) of the shape instead of scaling it. The same is also true for
|
||||
the kinematic/rigid/static bodies themselves, as their scale affect the
|
||||
shape scale.**
|
||||
|
||||
Now create a script for the character, the one used as an example
|
||||
above should work as a base.
|
||||
|
||||
Finally, instance that character scene in the tilemap, and make the
|
||||
map scene the main one, so it runs when pressing play.
|
||||
|
||||
.. image:: /img/kbinstance.png
|
||||
|
||||
Moving the Kinematic character
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Go back to the character scene, and open the script, the magic begins
|
||||
now! Kinematic body will do nothing by default, but it has a really
|
||||
useful function called :ref:`KinematicBody2D.move() <class_KinematicBody2D_move>`.
|
||||
This function takes a :ref:`Vector2 <class_Vector2>` as
|
||||
an argument, and tries to apply that motion to the kinematic body. If a
|
||||
collision happens, it stops right at the moment of the collision.
|
||||
|
||||
So, let's move our sprite downwards until it hits the floor:
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody2D
|
||||
|
||||
func _fixed_process(delta):
|
||||
move( Vector2(0,1) ) #move down 1 pixel per physics frame
|
||||
|
||||
func _ready():
|
||||
set_fixed_process(true)
|
||||
|
||||
The result is that the character will move, but stop right when
|
||||
hitting the floor. Pretty cool, huh?
|
||||
|
||||
The next step will be adding gravity to the mix, this way it behaves a
|
||||
little more like an actual game character:
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody2D
|
||||
|
||||
const GRAVITY = 200.0
|
||||
var velocity = Vector2()
|
||||
|
||||
func _fixed_process(delta):
|
||||
|
||||
velocity.y += delta * GRAVITY
|
||||
|
||||
var motion = velocity * delta
|
||||
move( motion )
|
||||
|
||||
func _ready():
|
||||
set_fixed_process(true)
|
||||
|
||||
Now the character falls smoothly. Let's make it walk to the sides, left
|
||||
and right when touching the directional keys. Remember that the values
|
||||
being used (for speed at least) is pixels/second.
|
||||
|
||||
This adds simple walking support by pressing left and right:
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody2D
|
||||
|
||||
const GRAVITY = 200.0
|
||||
const WALK_SPEED = 200
|
||||
|
||||
var velocity = Vector2()
|
||||
|
||||
func _fixed_process(delta):
|
||||
|
||||
velocity.y += delta * GRAVITY
|
||||
|
||||
if (Input.is_action_pressed("ui_left")):
|
||||
velocity.x = -WALK_SPEED
|
||||
elif (Input.is_action_pressed("ui_right")):
|
||||
velocity.x = WALK_SPEED
|
||||
else:
|
||||
velocity.x = 0
|
||||
|
||||
var motion = velocity * delta
|
||||
move(motion)
|
||||
|
||||
func _ready():
|
||||
set_fixed_process(true)
|
||||
|
||||
And give it a try.
|
||||
|
||||
Problem?
|
||||
~~~~~~~~
|
||||
|
||||
And... it doesn't work very well. If you go to the left against a wall,
|
||||
it gets stuck unless you release the arrow key. Once it is on the floor,
|
||||
it also gets stuck and it won't walk. What is going on??
|
||||
|
||||
The answer is, what it seems like it should be simple, it isn't that
|
||||
simple in reality. If the motion can't be completed, the character will
|
||||
stop moving. It's as simple as that. This diagram should illustrate
|
||||
better what is going on:
|
||||
|
||||
.. image:: /img/motion_diagram.png
|
||||
|
||||
Basically, the desired motion vector will never complete because it hits
|
||||
the floor and the wall too early in the motion trajectory and that makes
|
||||
it stop there. Remember that even though the character is on the floor,
|
||||
the gravity is always turning the motion vector downwards.
|
||||
|
||||
Solution!
|
||||
~~~~~~~~~
|
||||
|
||||
The solution? This situation is solved by "sliding" by the collision
|
||||
normal. KinematicBody2D provides two useful functions:
|
||||
|
||||
- :ref:`KinematicBody2D.is_colliding() <class_KinematicBody2D_is_colliding>`
|
||||
- :ref:`KinematicBody2D.get_collision_normal() <class_KinematicBody2D_get_collision_normal>`
|
||||
|
||||
So what we want to do is this:
|
||||
|
||||
.. image:: /img/motion_reflect.png
|
||||
|
||||
When colliding, the function ``move()`` returns the "remainder" of the
|
||||
motion vector. That means, if the motion vector is 40 pixels, but
|
||||
collision happened at 10 pixels, the same vector but 30 pixels long is
|
||||
returned.
|
||||
|
||||
The correct way to solve the motion is, then, to slide by the normal
|
||||
this way:
|
||||
|
||||
::
|
||||
|
||||
func _fixed_process(delta):
|
||||
|
||||
velocity.y += delta * GRAVITY
|
||||
if (Input.is_action_pressed("ui_left")):
|
||||
velocity.x = - WALK_SPEED
|
||||
elif (Input.is_action_pressed("ui_right")):
|
||||
velocity.x = WALK_SPEED
|
||||
else:
|
||||
velocity.x = 0
|
||||
|
||||
var motion = velocity * delta
|
||||
motion = move(motion)
|
||||
|
||||
if (is_colliding()):
|
||||
var n = get_collision_normal()
|
||||
motion = n.slide(motion)
|
||||
velocity = n.slide(velocity)
|
||||
move(motion)
|
||||
|
||||
|
||||
func _ready():
|
||||
set_fixed_process(true)
|
||||
|
||||
Note that not only the motion has been modified but also the velocity.
|
||||
This makes sense as it helps keep the new direction too.
|
||||
|
||||
The normal can also be used to detect that the character is on floor, by
|
||||
checking the angle. If the normal points up (or at least, within a
|
||||
certain threshold), the character can be determined to be there.
|
||||
|
||||
A more complete demo can be found in the demo zip distributed with the
|
||||
engine, or in the
|
||||
https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_char.
|
||||
441
learning/features/physics/physics_introduction.rst
Normal file
441
learning/features/physics/physics_introduction.rst
Normal file
@@ -0,0 +1,441 @@
|
||||
.. _doc_physics_introduction:
|
||||
|
||||
Physics introduction
|
||||
====================
|
||||
|
||||
Our world is made of tangible matter. In our world, a piano can't go
|
||||
through a wall when going into a house. It needs to use the door. Video
|
||||
games are often like the the real world and Pac-Man can't go through the
|
||||
walls of his maze (although he can teleport from the left to the right
|
||||
side of the screen and back).
|
||||
|
||||
Anyway, moving sprites around is nice but one day they have to collide
|
||||
properly, so let's get to the point.
|
||||
|
||||
Shapes
|
||||
------
|
||||
|
||||
The base collidable object in Godot's 2D world is a
|
||||
:ref:`Shape2D <class_Shape2D>`.
|
||||
There are many types of shapes, all of them inherit this base class:
|
||||
|
||||
- :ref:`CircleShape2D <class_CircleShape2D>`
|
||||
- :ref:`RectangleShape2D <class_RectangleShape2D>`
|
||||
- :ref:`CapsuleShape2D <class_CapsuleShape2D>`
|
||||
- :ref:`ConvexPolygonShape2D <class_ConvexPolygonShape2D>`
|
||||
- :ref:`ConcavePolygonShape2D <class_ConcavePolygonShape2D>`
|
||||
- etc. (there are others check the class list).
|
||||
|
||||
Shapes are of type
|
||||
:ref:`Resource <class_Resource>`,
|
||||
but they can be created via code easily. For example:
|
||||
|
||||
::
|
||||
|
||||
# Create a circle
|
||||
var c = CircleShape2D.new()
|
||||
c.set_radius(20)
|
||||
|
||||
# Create a box
|
||||
var b = RectangleShape2D.new()
|
||||
b.set_extents(Vector2(20,10))
|
||||
|
||||
The main use for shapes is checking collision/intersection and getting
|
||||
resolution information. Shapes are mostly convex, (except the
|
||||
concavepolygon one, which is just a list of segments to check collision
|
||||
against). This collision check is done easily with the built-in
|
||||
functions like:
|
||||
|
||||
::
|
||||
|
||||
# Check if there is a collision between two shapes, each with a transform
|
||||
if b.collide(b_xform, a, a_xform):
|
||||
print("OMG Collision!")
|
||||
|
||||
Godot will return correct collision and collision info from the
|
||||
different calls to the Shape2D api. Collision between all shapes and
|
||||
transforms can be done this way, or even obtaining contact information,
|
||||
motion casting, etc.
|
||||
|
||||
Transforming shapes
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As seen before in the collide functions, 2D shapes in Godot can be
|
||||
transformed by using a regular :ref:`Matrix32 <class_Matrix32>`
|
||||
transform, meaning the functions can check for collisions while the
|
||||
shapes are scaled, moved and
|
||||
rotated. The only limitation to this is that shapes with curved sections
|
||||
(such as circle and capsule) can only be scaled uniformly. This means
|
||||
that circle or capsule shapes scaled in the form of an ellipse **will
|
||||
not work properly**. This is a limitation on the collision algorithm
|
||||
used (SAT), so make sure that your circle and capsule shapes are always
|
||||
scaled uniformly!
|
||||
|
||||
.. image:: /img/shape_rules.png
|
||||
|
||||
When problems begin
|
||||
-------------------
|
||||
|
||||
Even though this sounds good, reality is that collision detection alone
|
||||
is usually not enough in most scenarios. Many problems start arising as
|
||||
long as the development of the game is in progress:
|
||||
|
||||
Too many combinations!
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Games have several dozens, hundreds, thousands! of objects that can
|
||||
collide and be collided. The typical approach is to test everything
|
||||
against everything in two for loops like this:
|
||||
|
||||
::
|
||||
|
||||
for i in colliders:
|
||||
for j in colliders:
|
||||
if (i.collides(j)):
|
||||
do_collision_code()
|
||||
|
||||
But this scales really bad. Let's imagine there are only 100 objects in
|
||||
the game. This means that 100\*100=10000 collisions will need to be
|
||||
tested each frame. This is a lot!
|
||||
|
||||
Visual aid
|
||||
~~~~~~~~~~
|
||||
|
||||
Most of the time, creating a shape via code is not enough. We need to
|
||||
visually place it over a sprite, draw a collision polygon, etc. It is
|
||||
obvious that we need nodes to create the proper collision shapes in a
|
||||
scene.
|
||||
|
||||
Collision resolution
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Imagine we solved the collision issue, we can tell easily and quickly
|
||||
which shapes overlap. If many of them are dynamic objects that move
|
||||
around, or move according to newtonian physics, solving a collision of
|
||||
multiple objects can be really difficult code-wise.
|
||||
|
||||
Introducing... Godot's physics engine!
|
||||
--------------------------------------
|
||||
|
||||
To solve all these problems, Godot has a physics and collision engine
|
||||
that is well integrated into the scene system, yet it allows different
|
||||
levels and layers of functionality. The built-in physics engine can be
|
||||
used for:
|
||||
|
||||
- Simple Collision Detection: See :ref:`Shape2D <class_Shape2D>`
|
||||
API.
|
||||
- Scene Kinematics: Handle shapes, collisions, broadphase, etc as
|
||||
nodes. See :ref:`Area2D <class_Area2D>`.
|
||||
- Scene Physics: Rigid bodies and constraints as nodes. See
|
||||
:ref:`RigidBody2D <class_RigidBody2D>`, and the joint nodes.
|
||||
|
||||
Units of measure
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
It is often a problem when integrating a 2D physics engine to a game
|
||||
that such engines are optimized to work using meters as unit of measure.
|
||||
Godot uses a built-in custom 2D physics engine that is designed to
|
||||
function properly in pixels, so all units and default values used for
|
||||
stabilization are tuned for this, making development more
|
||||
straightforward.
|
||||
|
||||
CollisionObject2D
|
||||
-----------------
|
||||
|
||||
:ref:`CollisionObject2D <class_CollisionObject2D>`
|
||||
is the (virtual) base node for everything that can be collided in 2D.
|
||||
Area2D, StaticBody2D, KinematicBody2D and RigidBody2D all inherit from
|
||||
it. This node contains a list of shapes (Shape2D) and a relative
|
||||
transform. This means that all collisionable objects in Godot can use
|
||||
multiple shapes at different transforms (offset/scale/rotation). Just
|
||||
remember that, as mentioned before, **non-uniform scale will not work
|
||||
for circle and capsule shapes**.
|
||||
|
||||
.. image:: /img/collision_inheritance.png
|
||||
|
||||
A CollisionObject2D comes in different flavors: StaticBody2D, RigidBody2D,
|
||||
KinematicBody2D and Area2D. However, before we dig into them, let's have
|
||||
a look how to define the shape of a collision object.
|
||||
There are two special nodes for that.
|
||||
|
||||
CollisionShape2D
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
This node is a helper node, which must be created as a direct child of a
|
||||
CollisionObject2D-derived node: :ref:`Area2D <class_Area2D>`,
|
||||
:ref:`StaticBody2D <class_StaticBody2D>`, :ref:`KinematicBody2D <class_KinematicBody2D>`,
|
||||
:ref:`RigidBody2D <class_RigidBody2D>`.
|
||||
|
||||
The purpose of a CollisionShape2D instance is to add collision shapes to
|
||||
its parent class. Multiple children of type CollisionShape2D can be added to a
|
||||
CollisionObject2D-derived object with the effect that the parent will
|
||||
simply get more collision shapes. When a CollisionShape2D is edited (or added, moved,
|
||||
deleted) the list of shapes in the parent node is updated.
|
||||
|
||||
At run time, though, this node does not exist and it can't be accessed with
|
||||
``get_node()``. This is because it is a helper node only for editing the collision shapes.
|
||||
To access the shapes created at runtime, use the CollisionObject2D API directly.
|
||||
|
||||
As an example, here's the scene from the platformer, containing an
|
||||
Area2D (named 'coin') having two children: a CollisionShape2D (named 'collision')
|
||||
and a sprite (called 'sprite'):
|
||||
|
||||
.. image:: /img/area2dcoin.png
|
||||
|
||||
CollisionPolygon2D
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This one is similar to CollisionShape2D, except that instead of
|
||||
assigning a shape, a polygon can be edited (drawn by the user) to
|
||||
determine the shape. The polygon can be convex or concave, it doesn't
|
||||
matter.
|
||||
|
||||
Here is another scene involving a CollisionPolygon2D: A StaticBody2D has
|
||||
been added as a child of a sprite so that the collision object moves together
|
||||
with the sprite. In turn, the CollisionPolygon is a child of StaticBody2D, meaning it
|
||||
adds collision shapes to it.
|
||||
|
||||
.. image:: /img/spritewithcollision.png
|
||||
|
||||
The CollisionPolygon2D will decompose the user-defined polygon into conves shapes
|
||||
convex shapes (shapes can only be convex, remember?) before adding them to
|
||||
the CollisionObject2D. The following image shows such a decomposition:
|
||||
|
||||
.. image:: /img/decomposed.png
|
||||
|
||||
Triggers
|
||||
~~~~~~~~
|
||||
|
||||
A CollisionShape2D or CollisionPolygon2D can be set as a trigger by setting
|
||||
the boolean flag with the same name. When
|
||||
used in a RigidBody2D or KinematicBody2D, "trigger" shapes take part
|
||||
in collision detection but are unaffected by physics (they don't block
|
||||
movement etc).
|
||||
Defining a collision shape as a trigger is mostly useful in two situations:
|
||||
|
||||
- Collisions for a specific shape shall be disabled.
|
||||
- An Area2D shall send ``body_enter`` and ``body_exit`` signals when the
|
||||
trigger shape enters it (useful in several situations).
|
||||
|
||||
StaticBody2D
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The simplest node in the physics engine is the :ref:`StaticBody2D <class_StaticBody2D>`.
|
||||
This node takes part in collision detection. However, it does not move or rotate
|
||||
after a collision, so physics do not influence them. The node is static.
|
||||
Other nodes can collide against it and will be influenced accordingly.
|
||||
|
||||
The platformer example uses StaticBody2D objects for the floors and walls.
|
||||
These are the static parts of a level and shall stay right where they
|
||||
are even if the player jumps onto them.
|
||||
|
||||
KinematicBody2D
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Similar to StaticBody2D, objects of type :ref:`KinematicBody2D <class_KinematicBody2D>`
|
||||
are not affected by physics (although they take part in collision detection, of course).
|
||||
However, KinematicBody2D are not static but can be moved via code or an animation.
|
||||
They have two main uses:
|
||||
|
||||
- **Simulated Motion**: When these bodies are moved manually, either
|
||||
from code or from an :ref:`AnimationPlayer <class_AnimationPlayer>`
|
||||
(with process mode set to fixed!), the physics will automatically
|
||||
compute an estimate of their linear and angular velocity. This makes
|
||||
them very useful for moving platforms or other
|
||||
AnimationPlayer-controlled objects (like a door, a bridge that opens,
|
||||
etc). As an example, the 2d/platformer demo uses them for moving
|
||||
platforms.
|
||||
- **Kinematic Characters**: KinematicBody2D also has an API for moving
|
||||
objects (the ``move()`` function) while performing collision tests. This
|
||||
makes them really useful to implement characters that collide against
|
||||
a world, but that don't require advanced physics. There is a tutorial
|
||||
about it :ref:`doc_kinematic_character_2d`.
|
||||
|
||||
RigidBody2D
|
||||
~~~~~~~~~~~
|
||||
|
||||
This type of body simulates Newtonian physics. It has mass, friction,
|
||||
bounce, and the (0,0) coordinates simulate the center of mass. When real
|
||||
physics are needed, :ref:`RigidBody2D <class_RigidBody2D>`
|
||||
is the node to use. The motion of this body is affected by gravity
|
||||
and/or other bodies.
|
||||
|
||||
Rigid bodies are usually active all the time, but when they end up in
|
||||
resting position and don't move for a while, they are put to sleep until
|
||||
something else wakes them up. This saves an enormous amount of CPU.
|
||||
|
||||
RigidBody2D nodes update their transform constantly, as it is generated
|
||||
by the simulation from a position, linear velocity and angular velocity.
|
||||
As a result, [STRIKEOUT:this node can't be scaled]. Scaling the children
|
||||
nodes should work fine though.
|
||||
|
||||
A RigidBody2D has a ``Mode`` flag to change its behavior (something
|
||||
which is very common in games). It can behave like a rigid body,
|
||||
a character (a rigid body without the ability to rotate so that it is
|
||||
always upright), a kinematic body or a static body. This flag can be
|
||||
changed dynamically according to the current situation. For example,
|
||||
an enemy frozen by an ice beam becomes a static body.
|
||||
|
||||
The best way to interact with a RigidBody2D is during the force
|
||||
integration callback by overriding the function
|
||||
|
||||
::
|
||||
|
||||
func _integrate_forces(state):
|
||||
[use state to change the object]
|
||||
|
||||
The "state" parameter is of type :ref:`Physics2DDirectBodyState <class_Physics2DDirectBodyState>`.
|
||||
Please do not use the state object outside the callback as it will
|
||||
result in an error.
|
||||
|
||||
During the evaluation of the aforementioned function, the physics engine
|
||||
synchronizes state with the scene and allows full modification of the
|
||||
object's parameters. Since physics may run in its own thread, parameter
|
||||
changes outside that callback will not take place until the next frame.
|
||||
|
||||
Contact reporting
|
||||
-----------------
|
||||
|
||||
In general, RigidBody2D will not keep track of the contacts, because
|
||||
this can require a huge amount of memory if thousands of rigid bodies
|
||||
are in the scene. To get contacts reported, simply increase the amount
|
||||
of the "contacts reported" property from zero to a meaningful value
|
||||
(depending on how many you are expecting to get). The contacts can be
|
||||
later obtained via the
|
||||
:ref:`Physics2DDirectBodyState.get_contact_count() <class_Physics2DDirectBodyState_get_contact_count>`
|
||||
and related functions.
|
||||
|
||||
Contact monitoring via signals is also available (signals similar to the
|
||||
ones in Area2D, described below) via a boolean property.
|
||||
|
||||
Area2D
|
||||
~~~~~~
|
||||
|
||||
Areas in Godot physics have three main roles:
|
||||
|
||||
1. Override the space parameters for objects entering them (ie.
|
||||
gravity, gravity direction, gravity type, density, etc).
|
||||
|
||||
2. Monitor when rigid or kinematic bodies enter or exit the area.
|
||||
|
||||
3. Monitor other areas (this is the simplest way to get overlap test)
|
||||
|
||||
The second function is the most common. For it to work, the "monitoring"
|
||||
property must be enabled (it is by default). There are two types of
|
||||
signals emitted by this node:
|
||||
|
||||
::
|
||||
|
||||
# Simple, high level notification
|
||||
body_enter(body:PhysicsBody2D)
|
||||
body_exit(body:PhysicsBody2D)
|
||||
area_enter(area:Area2D)
|
||||
area_exit(body:Area2D)
|
||||
|
||||
# Low level shape-based notification
|
||||
# Notifies which shape specifically in both the body and area are in contact
|
||||
body_enter_shape(body_id:int,body:PhysicsBody2D,body_shape_index:int,area_shape_index:idx)
|
||||
body_exit_shape(body_id:int,body:PhysicsBody2D,body_shape_index:int,area_shape_index:idx)
|
||||
area_enter_shape(area_id:int,area:Area2D,area_shape_index:int,self_shape_index:idx)
|
||||
area_exit_shape(area_id:int,area:Area2D,area_shape_index:int,self_shape_index:idx)
|
||||
|
||||
By default, areas also receive mouse/touchscreen input, providing a
|
||||
lower-level way than controls to implement this kind of input in a game.
|
||||
Bodies support this but it's disabled by default.
|
||||
|
||||
In case of overlap, who receives collision information?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Remember that not every combination of two bodies can "report" contacts.
|
||||
Static bodies are passive and will not report contacts when hit.
|
||||
Kinematic Bodies will report contacts but only against Rigid/Character
|
||||
bodies. Area2D will report overlap (not detailed contacts) with bodies
|
||||
and with other areas. The following table should make it more visual:
|
||||
|
||||
+-------------------+-------------+-----------------+-----------------+---------------+--------+
|
||||
| **Type** | *RigidBody* | *CharacterBody* | *KinematicBody* | *StaticBody* | *Area* |
|
||||
+===================+=============+=================+=================+===============+========+
|
||||
| **RigidBody** | Both | Both | Both | Rigidbody | Area |
|
||||
+-------------------+-------------+-----------------+-----------------+---------------+--------+
|
||||
| **CharacterBody** | Both | Both | Both | CharacterBody | Area |
|
||||
+-------------------+-------------+-----------------+-----------------+---------------+--------+
|
||||
| **KinematicBody** | Both | Both | None | None | Area |
|
||||
+-------------------+-------------+-----------------+-----------------+---------------+--------+
|
||||
| **StaticBody** | RigidBody | CharacterBody | None | None | None |
|
||||
+-------------------+-------------+-----------------+-----------------+---------------+--------+
|
||||
| **Area** | Area | Area | Area | None | Both |
|
||||
+-------------------+-------------+-----------------+-----------------+---------------+--------+
|
||||
|
||||
Physics global variables
|
||||
------------------------
|
||||
|
||||
A few global variables can be tweaked in the project settings for
|
||||
adjusting how 2D physics works:
|
||||
|
||||
.. image:: /img/physics2d_options.png
|
||||
|
||||
Leaving them alone is best (except for the gravity, that needs to be
|
||||
adjusted in most games), but there is one specific parameter that might
|
||||
need tweaking which is the "cell_size". Godot 2D physics engine used by
|
||||
default a space hashing algorithm that divides space in cells to compute
|
||||
close collision pairs more efficiently.
|
||||
|
||||
If a game uses several colliders that are really small and occupy a
|
||||
small portion of the screen, it might be necessary to shrink that value
|
||||
(always to a power of 2) to improve efficiency. Likewise if a game uses
|
||||
few large colliders that span a huge map (of several screens of size),
|
||||
increasing that value a bit might help save resources.
|
||||
|
||||
Fixed process callback
|
||||
----------------------
|
||||
|
||||
The physics engine may spawn multiple threads to improve performance, so
|
||||
it can use up to a full frame to process physics. Because of this, when
|
||||
accessing physics variables such as position, linear velocity, etc. they
|
||||
might not be representative of what is going on in the current frame.
|
||||
|
||||
To solve this, Godot has a fixed process callback, which is like process
|
||||
but it's called once per physics frame (by default 60 times per second).
|
||||
During this time, the physics engine is in *synchronization* state and
|
||||
can be accessed directly and without delays.
|
||||
|
||||
To enable a fixed process callback, use the ``set_fixed_process()``
|
||||
function, example:
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody2D
|
||||
|
||||
func _fixed_process(delta):
|
||||
move(direction * delta)
|
||||
|
||||
func _ready():
|
||||
set_fixed_process(true)
|
||||
|
||||
Casting rays and motion queries
|
||||
-------------------------------
|
||||
|
||||
It is very often desired to "explore" the world around from our code.
|
||||
Throwing rays is the most common way to do it. The simplest way to do
|
||||
this is by using the RayCast2D node, which will throw a ray every frame
|
||||
and record the intersection.
|
||||
|
||||
At the moment there isn't a high level API for this, so the physics
|
||||
server must be used directly. For this, the
|
||||
:ref:`Physics2DDirectspaceState <class_Physics2DDirectspaceState>`
|
||||
class must be used. To obtain it, the following steps must be taken:
|
||||
|
||||
1. It must be used inside the ``_fixed_process()`` callback, or at
|
||||
``_integrate_forces()``
|
||||
|
||||
2. The 2D RIDs for the space and physics server must be obtained.
|
||||
|
||||
The following code should work:
|
||||
|
||||
::
|
||||
|
||||
func _fixed_process(delta):
|
||||
var space = get_world_2d().get_space()
|
||||
var space_state = Physics2DServer.space_get_direct_state(space)
|
||||
|
||||
Enjoy doing space queries!
|
||||
169
learning/features/physics/ray-casting.rst
Normal file
169
learning/features/physics/ray-casting.rst
Normal file
@@ -0,0 +1,169 @@
|
||||
.. _doc_ray-casting:
|
||||
|
||||
Ray-casting
|
||||
===========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
One of the most common tasks in game development is casting a ray (or
|
||||
custom shaped object) and checking what it hits. This enables complex
|
||||
behaviors, AI, etc. to take place. This tutorial will explain how to
|
||||
do this in 2D and 3D.
|
||||
|
||||
Godot stores all the low level game information in servers, while the
|
||||
scene is just a frontend. As such, ray casting is generally a
|
||||
lower-level task. For simple raycasts, node such as
|
||||
:ref:`RayCast <class_RayCast>` and :ref:`RayCast2D <class_RayCast2D>`
|
||||
will work, as they will return every frame what the result of a raycast
|
||||
is.
|
||||
|
||||
Many times, though, ray-casting needs to be a more interactive process
|
||||
so a way to do this by code must exist.
|
||||
|
||||
Space
|
||||
-----
|
||||
|
||||
In the physics world, Godot stores all the low level collision and
|
||||
physics information in a *space*. The current 2d space (for 2D Physics)
|
||||
can be obtained by calling
|
||||
:ref:`CanvasItem.get_world_2d().get_space() <class_CanvasItem_get_world_2d>`.
|
||||
For 3D, it's :ref:`Spatial.get_world().get_space() <class_Spatial_get_world>`.
|
||||
|
||||
The resulting space :ref:`RID <class_RID>` can be used in
|
||||
:ref:`PhysicsServer <class_PhysicsServer>` and
|
||||
:ref:`Physics2DServer <class_Physics2DServer>` respectively for 3D and 2D.
|
||||
|
||||
Accessing space
|
||||
--------------
|
||||
|
||||
Godot physics runs by default in the same thread as game logic, but may
|
||||
be set to run on a separate thread to work more efficiently. Due to
|
||||
this, the only time accessing space is safe is during the
|
||||
:ref:`Node._fixed_process() <class_Node__fixed_process>`
|
||||
callback. Accessing it from outside this function may result in an error
|
||||
due to space being *locked*.
|
||||
|
||||
To perform queries into physics space, the
|
||||
:ref:`Physics2DDirectSpaceState <class_Physics2DDirectSpaceState>`
|
||||
and :ref:`PhysicsDirectSpaceState <class_PhysicsDirectSpaceState>`
|
||||
must be used.
|
||||
|
||||
In code, for 2D spacestate, this code must be used:
|
||||
|
||||
::
|
||||
|
||||
func _fixed_process(delta):
|
||||
var space_rid = get_world_2d().get_space()
|
||||
var space_state = Physics2DServer.space_get_direct_state(space_rid)
|
||||
|
||||
Of course, there is a simpler shortcut:
|
||||
|
||||
::
|
||||
|
||||
func _fixed_process(delta):
|
||||
var space_state = get_world_2d().get_direct_space_state()
|
||||
|
||||
For 3D:
|
||||
|
||||
::
|
||||
|
||||
func _fixed_process(delta):
|
||||
var space_state = get_world().get_direct_space_state()
|
||||
|
||||
Raycast query
|
||||
-------------
|
||||
|
||||
For performing a 2D raycast query, the method
|
||||
:ref:`Physics2DDirectSpaceState.intersect_ray() <class_Physics2DDirectSpaceState_intersect_ray>`
|
||||
must be used, for example:
|
||||
|
||||
::
|
||||
|
||||
func _fixed_process(delta):
|
||||
var space_state = get_world().get_direct_space_state()
|
||||
# use global coordinates, not local to node
|
||||
var result = space_state.intersect_ray( Vector2(0,0), Vector2(50,100) )
|
||||
|
||||
Result is a dictionary. If the ray didn't hit anything, the dictionary will
|
||||
be empty. If it did hit something it will contain collision information:
|
||||
|
||||
::
|
||||
|
||||
if (not result.empty()):
|
||||
print("Hit at point: ",result.position)
|
||||
|
||||
The collision result dictionary, when something hit, has this format:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
position:Vector2 # point in world space for collision
|
||||
normal:Vector2 # normal in world space for collision
|
||||
collider:Object # Object collided or null (if unassociated)
|
||||
collider_id:ObjectID # Object it collided against
|
||||
rid:RID # RID it collided against
|
||||
shape:int # shape index of collider
|
||||
metadata:Variant() # metadata of collider
|
||||
}
|
||||
|
||||
# in case of 3D, Vector3 is returned.
|
||||
|
||||
Collision exceptions
|
||||
--------------------
|
||||
|
||||
It is a very common case to attempt casting a ray from a character or
|
||||
another game scene to try to infer properties of the world around it.
|
||||
The problem with this is that the same character has a collider, so the
|
||||
ray can never leave the origin (it will keep hitting its own collider),
|
||||
as evidenced in the following image.
|
||||
|
||||
.. image:: /img/raycast_falsepositive.png
|
||||
|
||||
To avoid self-intersection, the intersect_ray() function can take an
|
||||
optional third parameter which is an array of exceptions. This is an
|
||||
example of how to use it from a KinematicBody2D or any other
|
||||
collisionobject based node:
|
||||
|
||||
::
|
||||
|
||||
extends KinematicBody2D
|
||||
|
||||
func _fixed_process(delta):
|
||||
var space_state = get_world().get_direct_space_state()
|
||||
var result = space_state.intersect_ray( get_global_pos(), enemy_pos, [ self ] )
|
||||
|
||||
The extra argument is a list of exceptions, can be objects or RIDs.
|
||||
|
||||
3D ray casting from screen
|
||||
--------------------------
|
||||
|
||||
Casting a ray from screen to 3D physics space is useful for object
|
||||
picking. There is not much of a need to do this because
|
||||
:ref:`CollisionObject <class_CollisionObject>`
|
||||
has an "input_event" signal that will let you know when it was clicked,
|
||||
but in case there is any desire to do it manually, here's how.
|
||||
|
||||
To cast a ray from the screen, the :ref:`Camera <class_Camera>` node
|
||||
is needed. Camera can be in two projection modes, perspective and
|
||||
orthogonal. Because of this, both the ray origin and direction must be
|
||||
obtained. (origin changes in orthogonal, while direction changes in
|
||||
perspective):
|
||||
|
||||
.. image:: /img/raycast_projection.png
|
||||
|
||||
To obtain it using a camera, the following code can be used:
|
||||
|
||||
::
|
||||
|
||||
const ray_length = 1000
|
||||
|
||||
func _input(ev):
|
||||
if ev.type==InputEvent.MOUSE_BUTTON and ev.pressed and ev.button_index==1:
|
||||
|
||||
var camera = get_node("camera")
|
||||
var from = camera.project_ray_origin(ev.pos)
|
||||
var to = from + camera.project_ray_normal(ev.pos) * ray_length
|
||||
|
||||
Of course, remember that during ``_input()``, space may be locked, so save
|
||||
your query for ``_fixed_process()``.
|
||||
124
learning/features/platform/android_in_app_purchases.rst
Normal file
124
learning/features/platform/android_in_app_purchases.rst
Normal file
@@ -0,0 +1,124 @@
|
||||
.. _doc_android_in_app_purchases:
|
||||
|
||||
Android in-app purchases
|
||||
========================
|
||||
|
||||
.. highlight:: shell
|
||||
|
||||
Godot engine has integrated GooglePaymentsV3 module with which we can implement in-app purchases in our game.
|
||||
|
||||
The Godot engine demo project repository has an android-iap example project. It includes a gdscript interface for android IAP.
|
||||
|
||||
Check the repository here https://github.com/godotengine/godot-demo-projects
|
||||
|
||||
Find the iap.gd script in
|
||||
|
||||
::
|
||||
|
||||
godot-demo-projects/misc/android_iap
|
||||
|
||||
|
||||
Add it to the Autoload list and name it as IAP so that we can reference it anywhere in the game.
|
||||
|
||||
Getting the product details
|
||||
---------------------------
|
||||
|
||||
When starting our game, we will need to get the item details from Google such as the product price, description and localized price string etc.
|
||||
|
||||
::
|
||||
|
||||
#First listen to the sku details update callback
|
||||
IAP.connect("sku_details_complete",self,"sku_details_complete")
|
||||
|
||||
#Then ask google the details for these items
|
||||
IAP.sku_details_query(["pid1","pid2"]) #pid1 and pid2 are our product ids entered in Googleplay dashboard
|
||||
|
||||
|
||||
#This will be called when sku details are retrieved successfully
|
||||
func sku_details_complete():
|
||||
print(IAP.sku_details) #This will print the details as JSON format, refer the format in iap.gd
|
||||
print(IAP.sku_details["pid1"].price) #print formatted localized price
|
||||
|
||||
We can use the IAP details to display the title, price and/or description on our shop scene.
|
||||
|
||||
Check if user purchased an item
|
||||
-------------------------------
|
||||
|
||||
When starting our game, we can check if the user has purchased any product. YOU SHOULD DO THIS ONLY AFTER 2/3 SECONDS AFTER YOUR GAME IS LOADED. If we do this as the first thing when the game is launched, IAP might not be initialized and our game will crash on start.
|
||||
|
||||
::
|
||||
|
||||
#Add a listener first
|
||||
IAP.connect("has_purchased",self,"iap_has_purchased")
|
||||
IAP.request_purchased() #Ask Google for all purchased items
|
||||
|
||||
#This will call for each and every user purchased products
|
||||
func iap_has_purchased(item_name):
|
||||
print(item_name) #print the name of purchased items
|
||||
|
||||
|
||||
Google IAP policy says the game should restore the user's purchases if the user replaces his phone or reinstall the same app. We can use the above code to do check what products the user has purchased and we can make our game respond accordingly.
|
||||
|
||||
Simple Purchase
|
||||
---------------
|
||||
We can put this purchase logic on a product's buy button.
|
||||
|
||||
::
|
||||
|
||||
#First listen for purchase_success callback
|
||||
IAP.connect("purchase_success",self,"purchase_success_callback")
|
||||
|
||||
#Then call purchase like this
|
||||
IAP.purchase("pid1") #replace pid1 with your product id
|
||||
IAP.purchase("pid2") #replace pid2 with your another product id
|
||||
|
||||
#This function will be called when the purchase is a success
|
||||
func purchase_success_callback(item):
|
||||
print(item + " has purchased")
|
||||
|
||||
We can also implement other signals for the purchase flow and improve the user experience as you needed.
|
||||
|
||||
``purchase_fail`` - When the purchase is failed due to any reason
|
||||
|
||||
``purchase_cancel`` - When the user cancels the purchase
|
||||
|
||||
``purchase_owned`` - When the user already bought the product earlier
|
||||
|
||||
|
||||
Consumables and Non-Consumables
|
||||
-------------------------------
|
||||
|
||||
There are two types of products - consumables and non-consumables.
|
||||
**Consumables** are purchased and used, for eg: healing potions which can be purchased again and again.
|
||||
**Non-consumables** are one time purchases, for eg: Level packs.
|
||||
|
||||
Google doesn't have this separation in their dashboard. If our product is a consumable, and if a user has purchased it, it will not be available for purchase until it is consumed. So we should call the consume method for our consumables and don't call consume for your non-consumables.
|
||||
|
||||
::
|
||||
|
||||
IAP.connnect("consume_success",self,"on_consume_success")
|
||||
IAP.consume("pid")
|
||||
|
||||
func on_consume_success(item):
|
||||
print(item + " consumed")
|
||||
|
||||
If our game has only consumables, we don't have to do this. We can set it to consume the item automatically after a purchase.
|
||||
::
|
||||
IAP.set_auto_consume(true)
|
||||
|
||||
If our game has only non-consumables, we can
|
||||
::
|
||||
IAP.set_auto_consume(false)
|
||||
|
||||
We should set the auto consume value only once when the game starts.
|
||||
|
||||
Testing
|
||||
-------
|
||||
If we add a gmail id as a tester in Google dashboard, that tester can purchase items and he will not be charged. Another way to test IAP is using redeem codes generated by us for our game because the purchase flow is the same.
|
||||
|
||||
Third way of testing is in development side. If we put the product ids as shown below, we will get a static fixed response according to the product id. This is a quick way of testing things before going to the dashboard.
|
||||
|
||||
- android.test.purchased
|
||||
- android.test.canceled
|
||||
- android.test.refunded
|
||||
- android.test.item_unavailable
|
||||
427
learning/features/platform/services_for_ios.rst
Normal file
427
learning/features/platform/services_for_ios.rst
Normal file
@@ -0,0 +1,427 @@
|
||||
.. _doc_services_for_ios:
|
||||
|
||||
Services for iOS
|
||||
================
|
||||
|
||||
At the moment, there are 2 iOS APIs partially implemented, GameCenter
|
||||
and Storekit. Both use the same model of asynchronous calls explained
|
||||
below.
|
||||
|
||||
Asynchronous methods
|
||||
--------------------
|
||||
|
||||
When requesting an asynchronous operation, the method will look like
|
||||
this:
|
||||
|
||||
::
|
||||
|
||||
Error purchase(Variant p_params);
|
||||
|
||||
The parameter will usually be a Dictionary, with the information
|
||||
necessary to make the request, and the call will have 2 phases. First,
|
||||
the method will immediately return an Error value. If the Error is not
|
||||
'OK', the call operation is completed, with an error probably caused
|
||||
locally (no internet connection, API incorrectly configured, etc). If
|
||||
the error value is 'OK', a response event will be produced and added to
|
||||
the 'pending events' queue. Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
func on_purchase_pressed():
|
||||
var result = InAppStore.purchase( { "product_id": "my_product" } )
|
||||
if result == OK:
|
||||
animation.play("busy") # show the "waiting for response" animation
|
||||
else:
|
||||
show_error()
|
||||
|
||||
# put this on a 1 second timer or something
|
||||
func check_events():
|
||||
while InAppStore.get_pending_event_count() > 0:
|
||||
var event = InAppStore.pop_pending_event()
|
||||
if event.type == "purchase":
|
||||
if event.result == "ok":
|
||||
show_success(event.product_id)
|
||||
else:
|
||||
show_error()
|
||||
|
||||
Remember that when a call returns OK, the API will *always* produce an
|
||||
event through the pending_event interface, even if it's an error, or a
|
||||
network timeout, etc. You should be able to, for example, safely block
|
||||
the interface waiting for a reply from the server. If any of the APIs
|
||||
don't behave this way it should be treated as a bug.
|
||||
|
||||
The pending event interface consists of 2 methods:
|
||||
|
||||
- ``get_pending_event_count()``
|
||||
Returns the number of pending events on the queue.
|
||||
|
||||
- ``Variant pop_pending_event()``
|
||||
Pops the first event from the queue and returns it.
|
||||
|
||||
Store Kit
|
||||
---------
|
||||
|
||||
Implemented in platform/iphone/in_app_store.mm
|
||||
|
||||
The Store Kit API is accessible through the "InAppStore" singleton (will
|
||||
always be available from gdscript). It is initialized automatically. It
|
||||
has 2 methods for purchasing:
|
||||
|
||||
- ``Error purchase(Variant p_params);``
|
||||
- ``Error request_product_info(Variant p_params);``
|
||||
|
||||
and the pending_event interface
|
||||
|
||||
::
|
||||
|
||||
int get_pending_event_count();
|
||||
Variant pop_pending_event();
|
||||
|
||||
purchase
|
||||
~~~~~~~~
|
||||
|
||||
Purchases a product id through the Store Kit API.
|
||||
|
||||
Parameters
|
||||
^^^^^^^^^^
|
||||
|
||||
Takes a Dictionary as a parameter, with one field, ``product_id``, a
|
||||
string with your product id. Example:
|
||||
|
||||
::
|
||||
|
||||
var result = InAppStore.purchase( { "product_id": "my_product" } )
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On error:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "purchase",
|
||||
"result": "error",
|
||||
"product_id": "the product id requested"
|
||||
}
|
||||
|
||||
On success:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "purchase",
|
||||
"result": "ok",
|
||||
"product_id": "the product id requested"
|
||||
}
|
||||
|
||||
request_product_info
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Requests the product info on a list of product IDs.
|
||||
|
||||
Parameters
|
||||
^^^^^^^^^^
|
||||
|
||||
Takes a Dictionary as a parameter, with one field, ``product_ids``, a
|
||||
string array with a list of product ids. Example:
|
||||
|
||||
::
|
||||
|
||||
var result = InAppStore.request_product_info( { "product_ids": ["my_product1", "my_product2"] } )
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "product_info",
|
||||
"result": "ok",
|
||||
"invalid_ids": [ list of requested ids that were invalid ],
|
||||
"ids": [ list of ids that were valid ],
|
||||
"titles": [ list of valid product titles (corresponds with list of valid ids) ],
|
||||
"descriptions": [ list of valid product descriptions ] ,
|
||||
"prices": [ list of valid product prices ],
|
||||
"localized_prices": [ list of valid product localized prices ],
|
||||
}
|
||||
|
||||
Game Center
|
||||
-----------
|
||||
|
||||
Implemented in platform/iphone/game_center.mm
|
||||
|
||||
The Game Center API is available through the "GameCenter" singleton. It
|
||||
has 6 methods:
|
||||
|
||||
- ``Error post_score(Variant p_score);``
|
||||
- ``Erroraward_achievement(Variant p_params);``
|
||||
- ``Error reset_achievements();``
|
||||
- ``Error request_achievements();``
|
||||
- ``Error request_achievement_descriptions();``
|
||||
- ``Error show_game_center(Variant p_params);``
|
||||
|
||||
plus the standard pending event interface.
|
||||
|
||||
post_score
|
||||
~~~~~~~~~~
|
||||
|
||||
Posts a score to a Game Center leaderboard.
|
||||
|
||||
Parameters
|
||||
^^^^^^^^^^
|
||||
|
||||
Takes a Dictionary as a parameter, with 2 fields:
|
||||
|
||||
- ``score`` a float number
|
||||
- ``category`` a string with the category name
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
var result = GameCenter.post_score( { "value": 100, "category": "my_leaderboard", } )
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On error:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "post_score",
|
||||
"result": "error",
|
||||
"error_code": the value from NSError::code,
|
||||
"error_description": the value from NSError::localizedDescription,
|
||||
}
|
||||
|
||||
On success:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "post_score",
|
||||
"result": "ok",
|
||||
}
|
||||
|
||||
award_achievement
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Modifies the progress of a Game Center achievement.
|
||||
|
||||
Parameters
|
||||
^^^^^^^^^^
|
||||
|
||||
Takes a Dictionary as a parameter, with 3 fields:
|
||||
|
||||
- ``name`` (string) the achievement name
|
||||
- ``progress`` (float) the achievement progress from 0.0 to 100.0
|
||||
(passed to ``GKAchievement::percentComplete``)
|
||||
- ``show_completion_banner`` (bool) whether Game Center should display
|
||||
an achievement banner at the top of the screen
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
var result = award_achievement( { "name": "hard_mode_completed", "progress": 6.1 } )
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On error:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "award_achievement",
|
||||
"result": "error",
|
||||
"error_code": the error code taken from NSError::code,
|
||||
}
|
||||
|
||||
On success:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "award_achievement",
|
||||
"result": "ok",
|
||||
}
|
||||
|
||||
reset_achievements
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clears all Game Center achievements. The function takes no parameters.
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On error:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "reset_achievements",
|
||||
"result": "error",
|
||||
"error_code": the value from NSError::code
|
||||
}
|
||||
|
||||
On success:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "reset_achievements",
|
||||
"result": "ok",
|
||||
}
|
||||
|
||||
request_achievements
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Request all the Game Center achievements the player has made progress
|
||||
on. The function takes no parameters.
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On error:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "achievements",
|
||||
"result": "error",
|
||||
"error_code": the value from NSError::code
|
||||
}
|
||||
|
||||
On success:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "achievements",
|
||||
"result": "ok",
|
||||
"names": [ list of the name of each achievement ],
|
||||
"progress": [ list of the progress made on each achievement ]
|
||||
}
|
||||
|
||||
request_achievement_descriptions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Request the descriptions of all existing Game Center achievements
|
||||
regardless of progress. The function takes no parameters.
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On error:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "achievement_descriptions",
|
||||
"result": "error",
|
||||
"error_code": the value from NSError::code
|
||||
}
|
||||
|
||||
On success:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "achievement_descriptions",
|
||||
"result": "ok",
|
||||
"names": [ list of the name of each achievement ],
|
||||
"titles": [ list of the title of each achievement ]
|
||||
"unachieved_descriptions": [ list of the description of each achievement when it is unachieved ]
|
||||
"achieved_descriptions": [ list of the description of each achievement when it is achieved ]
|
||||
"maximum_points": [ list of the points earned by completing each achievement ]
|
||||
"hidden": [ list of booleans indicating whether each achievement is initially visible ]
|
||||
"replayable": [ list of booleans indicating whether each achievement can be earned more than once ]
|
||||
}
|
||||
|
||||
show_game_center
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Displays the built in Game Center overlay showing leaderboards,
|
||||
achievements, and challenges.
|
||||
|
||||
Parameters
|
||||
^^^^^^^^^^
|
||||
|
||||
Takes a Dictionary as a parameter, with 2 fields:
|
||||
|
||||
- ``view`` (string) (optional) the name of the view to present. Accepts
|
||||
"default", "leaderboards", "achievements", or "challenges". Defaults
|
||||
to "default".
|
||||
- ``leaderboard_name`` (string) (optional) the name of the leaderboard
|
||||
to present. Only used when "view" is "leaderboards" (or "default" is
|
||||
configured to show leaderboards). If not specified, Game Center will
|
||||
display the aggregate leaderboard.
|
||||
|
||||
Examples:
|
||||
|
||||
::
|
||||
|
||||
var result = show_game_center( { "view": "leaderboards", "leaderboard_name": "best_time_leaderboard" } )
|
||||
var result = show_game_center( { "view": "achievements" } )
|
||||
|
||||
Response event
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The response event will be a dictionary with the following fields:
|
||||
|
||||
On close:
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"type": "show_game_center",
|
||||
"result": "ok",
|
||||
}
|
||||
|
||||
Multi-platform games
|
||||
--------------------
|
||||
|
||||
When working on a multi-platform game, you won't always have the
|
||||
"GameCenter" singleton available (for example when running on PC or
|
||||
Android). Because the gdscript compiler looks up the singletons at
|
||||
compile time, you can't just query the singletons to see and use what
|
||||
you need inside a conditional block, you need to also define them as
|
||||
valid identifiers (local variable or class member). This is an example
|
||||
of how to work around this in a class:
|
||||
|
||||
.. code:: python
|
||||
|
||||
var GameCenter = null # define it as a class member
|
||||
|
||||
func post_score(p_score):
|
||||
if GameCenter == null:
|
||||
return
|
||||
GameCenter.post_score( { "value": p_score, "category": "my_leaderboard" } )
|
||||
|
||||
func check_events():
|
||||
while GameCenter.get_pending_event_count() > 0:
|
||||
# do something with events here
|
||||
pass
|
||||
|
||||
func _ready():
|
||||
# check if the singleton exists
|
||||
if Globals.has_singleton("GameCenter"):
|
||||
GameCenter = Globals.get_singleton("GameCenter")
|
||||
# connect your timer here to the "check_events" function
|
||||
109
learning/features/shading/screen-reading_shaders.rst
Normal file
109
learning/features/shading/screen-reading_shaders.rst
Normal file
@@ -0,0 +1,109 @@
|
||||
.. _doc_screen-reading_shaders:
|
||||
|
||||
Screen-reading shaders
|
||||
======================
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Very often it is desired to make a shader that reads from the same
|
||||
screen it's writing to. 3D APIs such as OpenGL or DirectX make this very
|
||||
difficult because of internal hardware limitations. GPUs are extremely
|
||||
parallel, so reading and writing causes all sort of cache and coherency
|
||||
problems. As a result, not even the most modern hardware supports this
|
||||
properly.
|
||||
|
||||
The workaround is to make a copy of the screen, or a part of the screen,
|
||||
to a back-buffer and then read from it while drawing. Godot provides a
|
||||
few tools that makes this process easy!
|
||||
|
||||
TexScreen shader instruction
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Godot :ref:`doc_shading_language` has a special instruction, "texscreen", it takes as
|
||||
parameter the UV of the screen and returns a vec3 RGB with the color. A
|
||||
special built-in varying: SCREEN_UV can be used to obtain the UV for
|
||||
the current fragment. As a result, this simple 2D fragment shader:
|
||||
|
||||
::
|
||||
|
||||
COLOR=vec4( texscreen(SCREEN_UV), 1.0 );
|
||||
|
||||
results in an invisible object, because it just shows what lies behind.
|
||||
The same shader using the visual editor looks like this:
|
||||
|
||||
.. image:: /img/texscreen_visual_shader.png
|
||||
|
||||
TexScreen example
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Texscreen instruction can be used for a lot of things. There is a
|
||||
special demo for *Screen Space Shaders*, that you can download to see
|
||||
and learn. One example is a simple shader to adjust brightness, contrast
|
||||
and saturation:
|
||||
|
||||
::
|
||||
|
||||
uniform float brightness = 1.0;
|
||||
uniform float contrast = 1.0;
|
||||
uniform float saturation = 1.0;
|
||||
|
||||
vec3 c = texscreen(SCREEN_UV);
|
||||
|
||||
c.rgb = mix(vec3(0.0), c.rgb, brightness);
|
||||
c.rgb = mix(vec3(0.5), c.rgb, contrast);
|
||||
c.rgb = mix(vec3(dot(vec3(1.0), c.rgb)*0.33333), c.rgb, saturation);
|
||||
|
||||
COLOR.rgb = c;
|
||||
|
||||
Behind the scenes
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
While this seems magical, it's not. The Texscreen instruction, when
|
||||
first found in a node that is about to be drawn, does a full-screen
|
||||
copy to a back-buffer. Subsequent nodes that use texscreen() in
|
||||
shaders will not have the screen copied for them, because this ends up
|
||||
being very inefficient.
|
||||
|
||||
As a result, if shaders that use texscreen() overlap, the second one
|
||||
will not use the result of the first one, resulting in unexpected
|
||||
visuals:
|
||||
|
||||
.. image:: /img/texscreen_demo1.png
|
||||
|
||||
In the above image, the second sphere (top right) is using the same
|
||||
source for texscreen() as the first one below, so the first one
|
||||
"disappears", or is not visible.
|
||||
|
||||
To correct this, a
|
||||
:ref:`BackBufferCopy <class_BackBufferCopy>`
|
||||
node can be instanced between both spheres. BackBufferCopy can work by
|
||||
either specifying a screen region or the whole screen:
|
||||
|
||||
.. image:: /img/texscreen_bbc.png
|
||||
|
||||
With correct back-buffer copying, the two spheres blend correctly:
|
||||
|
||||
.. image:: /img/texscreen_demo2.png
|
||||
|
||||
Back-buffer logic
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
So, to make it clearer, here's how the backbuffer copying logic works in
|
||||
Godot:
|
||||
|
||||
- If a node uses the texscreen(), the entire screen is copied to the
|
||||
back buffer before drawing that node. This only happens the first
|
||||
time, subsequent nodes do not trigger this.
|
||||
- If a BackBufferCopy node was processed before the situation in the
|
||||
point above (even if texscreen() was not used), this behavior
|
||||
described in the point above does not happen. In other words,
|
||||
automatic copying of the entire screen only happens if texscreen() is
|
||||
used in a node for the first time and no BackBufferCopy node (not
|
||||
disabled) was found before in tree-order.
|
||||
- BackBufferCopy can copy either the entire screen or a region. If set
|
||||
to only a region (not the whole screen) and your shader uses pixels
|
||||
not in the region copied, the result of that read is undefined
|
||||
(most likely garbage from previous frames). In other words, it's
|
||||
possible to use BackBufferCopy to copy back a region of the screen
|
||||
and then use texscreen() on a different region. Avoid this behavior!
|
||||
64
learning/features/shading/shader_materials.rst
Normal file
64
learning/features/shading/shader_materials.rst
Normal file
@@ -0,0 +1,64 @@
|
||||
.. _doc_shader_materials:
|
||||
|
||||
Shader materials
|
||||
================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
For the most common cases, :ref:`doc_fixed_materials` are enough to create the
|
||||
desired textures or look and feel. Shader materials are a step beyond
|
||||
that, adding a huge amount of flexibility. With them, it is possible to:
|
||||
|
||||
- Create procedural textures.
|
||||
- Create complex texture blendings.
|
||||
- Create animated materials, or materials that change with time.
|
||||
- Create refractive effects or other advanced effects.
|
||||
- Create special lighting shaders for more exotic materials.
|
||||
- Animate vertices, like tree leaves or grass.
|
||||
- And much more!
|
||||
|
||||
Traditionally, most engines will ask you to learn GLSL, HLSL or CG,
|
||||
which are pretty complex for the skillset of most artists. Godot uses a
|
||||
simplified version of a shader language that will detect errors as you
|
||||
type, so you can see your edited shaders in real-time. Additionally, it
|
||||
is possible to edit shaders using a visual, node-based graph editor.
|
||||
|
||||
Creating a ShaderMaterial
|
||||
-------------------------
|
||||
|
||||
Create a new ShaderMaterial in some object of your choice. Go to the
|
||||
"Shader" property, then create a new "MaterialShader" (use
|
||||
"MaterialShaderGraph" for access to the visual graph editor):
|
||||
|
||||
.. image:: /img/shader_material_create.png
|
||||
|
||||
Edit the newly created shader, and the shader editor will open:
|
||||
|
||||
.. image:: /img/shader_material_editor.png
|
||||
|
||||
There are three code tabs open, the first is for the vertex shader, the
|
||||
second for the fragment and the third for the lighting. The shader
|
||||
language is documented in :ref:`doc_shading_language` so a small example will be
|
||||
presented next.
|
||||
|
||||
Create a very simple fragment shader that writes a color:
|
||||
|
||||
::
|
||||
|
||||
uniform color col;
|
||||
DIFFUSE = col.rgb;
|
||||
|
||||
Code changes take place in real-time. If the code is modified, it will
|
||||
be instantly recompiled and the object will be updated. If a typo is
|
||||
made, the editor will notify of the compilation failure:
|
||||
|
||||
.. image:: /img/shader_material_typo.png
|
||||
|
||||
Finally, go back and edit the material, and the exported uniform will be
|
||||
instantly visible:
|
||||
|
||||
.. image:: /img/shader_material_col.png
|
||||
|
||||
This allows to very quickly create custom, complex materials for every
|
||||
type of object.
|
||||
518
learning/features/shading/shading_language.rst
Normal file
518
learning/features/shading/shading_language.rst
Normal file
@@ -0,0 +1,518 @@
|
||||
.. _doc_shading_language:
|
||||
|
||||
Shading language
|
||||
================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Godot uses a simplified shader language (almost a subset of GLSL).
|
||||
Shaders can be used for:
|
||||
|
||||
- Materials
|
||||
- Post-Processing
|
||||
- 2D
|
||||
|
||||
and are divided in *Vertex*, *Fragment* and *Light* sections.
|
||||
|
||||
Language
|
||||
--------
|
||||
|
||||
Typing
|
||||
~~~~~~
|
||||
|
||||
The language is statically type and supports only a few operations.
|
||||
Arrays, classes, structures, etc are not supported. Several built-in
|
||||
datatypes are provided:
|
||||
|
||||
Data types
|
||||
~~~~~~~~~~
|
||||
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| DataType | Description |
|
||||
+===================+==============================================================+
|
||||
| *void* | Void |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *bool* | boolean (true or false) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *float* | floating point |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *vec2* | 2-component vector, float subindices (x,y or r,g ) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *vec3* | 3-component vector, float subindices (x,y,z or r,g,b ) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *vec4*, *color* | 4-component vector, float subindices (x,y,z,w or r,g,b,a ) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *mat2* | 2x2 matrix, vec3 subindices (x,y) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *mat3* | 3x3 matrix, vec3 subindices (x,y,z) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *mat4* | 4x4 matrix, vec4 subindices (x,y,z,w) |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *texture* | texture sampler, can only be used as uniform |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
| *cubemap* | cubemap sampler, can only be used as uniform |
|
||||
+-------------------+--------------------------------------------------------------+
|
||||
|
||||
Syntax
|
||||
~~~~~~
|
||||
|
||||
The syntax is similar to C, with statements ending with ``;`` and comments
|
||||
as ``//`` and ``/* */``. Example:
|
||||
|
||||
::
|
||||
|
||||
float a = 3;
|
||||
vec3 b;
|
||||
b.x = a;
|
||||
|
||||
Swizzling
|
||||
~~~~~~~~~
|
||||
|
||||
It is possible to use swizzling to reassigning subindices or groups of
|
||||
subindices, in order:
|
||||
|
||||
::
|
||||
|
||||
vec3 a = vec3(1,2,3);
|
||||
vec3 b = a.zyx; // b will contain vec3(3,2,1)
|
||||
vec2 c = a.xy; // c will contain vec2(1,2)
|
||||
vec4 d = a.xyzz; // d will contain vec4(1,2,3,3)
|
||||
|
||||
Constructors
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Constructors take the regular amount of elements, but can also accept
|
||||
less if the element has more subindices, for example:
|
||||
|
||||
::
|
||||
|
||||
vec3 a = vec3(1,vec2(2,3));
|
||||
vec3 b = vec3(a);
|
||||
vec3 c = vec3(vec2(2,3),1);
|
||||
vec4 d = vec4(a,5);
|
||||
mat3 m = mat3(a,b,c);
|
||||
|
||||
Conditionals
|
||||
~~~~~~~~~~~~
|
||||
|
||||
For now, only the ``if`` conditional is supported. Example:
|
||||
|
||||
::
|
||||
|
||||
if (a < b) {
|
||||
c = b;
|
||||
}
|
||||
|
||||
Uniforms
|
||||
~~~~~~~~
|
||||
|
||||
A variable can be declared as uniform. In this case, its value will
|
||||
come from outside the shader (it will be the responsibility of the
|
||||
material or whatever using the shader to provide it).
|
||||
|
||||
::
|
||||
|
||||
uniform vec3 direction;
|
||||
uniform color tint;
|
||||
|
||||
vec3 result = tint.rgb * direction;
|
||||
|
||||
Functions
|
||||
~~~~~~~~~
|
||||
|
||||
Simple support for functions is provided. Functions can't access
|
||||
uniforms or other shader variables.
|
||||
|
||||
::
|
||||
|
||||
vec3 addtwo(vec3 a, vec3 b) {
|
||||
return a+b;
|
||||
}
|
||||
|
||||
vec3 c = addtwo(vec3(1,1,1), vec3(2,2,2));
|
||||
|
||||
Built-in functions
|
||||
------------------
|
||||
|
||||
Several built-in functions are provided for convenience, listed as
|
||||
follows:
|
||||
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| Function | Description |
|
||||
+=======================================================================+=============================================+
|
||||
| float *sin* ( float ) | Sine |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *cos* ( float ) | Cosine |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *tan* ( float ) | Tangent |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *asin* ( float ) | arc-Sine |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *acos* ( float ) | arc-Cosine |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *atan* ( float ) | arc-Tangent |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *pow* ( vec\_type, float ) | Power |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *pow* ( vec\_type, vec\_type ) | Power (Vec. Exponent) |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *exp* ( vec\_type ) | Base-e Exponential |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *log* ( vec\_type ) | Natural Logarithm |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *sqrt* ( vec\_type ) | Square Root |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *abs* ( vec\_type ) | Absolute |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *sign* ( vec\_type ) | Sign |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *floor* ( vec\_type ) | Floor |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *trunc* ( vec\_type ) | Trunc |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *ceil* ( vec\_type ) | Ceiling |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *fract* ( vec\_type ) | Fractional |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *mod* ( vec\_type,vec\_type ) | Remainder |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *min* ( vec\_type,vec\_type ) | Minimum |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *min* ( vec\_type,vec\_type ) | Maximum |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *clamp* ( vec\_type value,vec\_type min, vec\_type max ) | Clamp to Min-Max |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *mix* ( vec\_type a,vec\_type b, float c ) | Linear Interpolate |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *mix* ( vec\_type a,vec\_type b, vec\_type c ) | Linear Interpolate (Vector Coef.) |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *step* ( vec\_type a,vec\_type b) | \` a[i] < b[i] ? 0.0 : 1.0\` |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *smoothstep* ( vec\_type a,vec\_type b,vec\_type c) | |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *length* ( vec\_type ) | Vector Length |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *distance* ( vec\_type, vec\_type ) | Distance between vector. |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| float *dot* ( vec\_type, vec\_type ) | Dot Product |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec3 *cross* ( vec3, vec3 ) | Cross Product |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec\_type *normalize* ( vec\_type ) | Normalize to unit length |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| vec3 *reflect* ( vec3, vec3 ) | Reflect |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| color *tex* ( texture, vec2 ) | Read from a texture in normalized coords |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| color *texcube* ( texture, vec3 ) | Read from a cubemap |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
| color *texscreen* ( vec2 ) | Read from screen (generates a copy) |
|
||||
+-----------------------------------------------------------------------+---------------------------------------------+
|
||||
|
||||
Built-in variables
|
||||
------------------
|
||||
|
||||
Depending on the shader type, several built-in variables are available,
|
||||
listed as follows:
|
||||
|
||||
Material (3D) - VertexShader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| Variable | Description |
|
||||
+====================================+===========================================+
|
||||
| const vec3 *SRC\_VERTEX* | Model-Space Vertex |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const vec3 *SRC\_NORMAL* | Model-Space Normal |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const vec3 *SRC\_TANGENT* | Model-Space Tangent |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const float *SRC\_BINORMALF* | Direction to Compute Binormal |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| vec3 *VERTEX* | View-Space Vertex |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| vec3 *NORMAL* | View-Space Normal |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| vec3 *TANGENT* | View-Space Tangent |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| vec3 *BINORMAL* | View-Space Binormal |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| vec2 *UV* | UV |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| vec2 *UV2* | UV2 |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| color *COLOR* | Vertex Color |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| out vec4 *VAR1* | Varying 1 Output |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| out vec4 *VAR2* | Varying 2 Output |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| out float *SPEC\_EXP* | Specular Exponent (for Vertex Lighting) |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| out float *POINT\_SIZE* | Point Size (for points) |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const mat4 *WORLD\_MATRIX* | Object World Matrix |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const mat4 *INV\_CAMERA\_MATRIX* | Inverse Camera Matrix |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const mat4 *PROJECTION\_MATRIX* | Projection Matrix |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const mat4 *MODELVIEW\_MATRIX* | (InvCamera \* Projection) |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const float *INSTANCE\_ID* | Instance ID (for multimesh) |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
| const float *TIME* | Time (in seconds) |
|
||||
+------------------------------------+-------------------------------------------+
|
||||
|
||||
Material (3D) - FragmentShader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| Variable | Description |
|
||||
+==================================+==================================================================================+
|
||||
| const vec3 *VERTEX* | View-Space vertex |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec4 *POSITION* | View-Space Position |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec3 *NORMAL* | View-Space Normal |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec3 *TANGENT* | View-Space Tangent |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec3 *BINORMAL* | View-Space Binormal |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec3 *NORMALMAP* | Alternative to NORMAL, use for normal texture output. |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec3 *NORMALMAP\_DEPTH* | Complementary to the above, allows changing depth of normalmap. |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec2 *UV* | UV |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec2 *UV2* | UV2 |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const color *COLOR* | Vertex Color |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec4 *VAR1* | Varying 1 |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec4 *VAR2* | Varying 2 |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec2 *SCREEN\_UV* | Screen Texture Coordinate (for using with texscreen) |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const float *TIME* | Time (in seconds) |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| const vec2 *POINT\_COORD* | UV for point, when drawing point sprites. |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out vec3 *DIFFUSE* | Diffuse Color |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out vec4 *DIFFUSE\_ALPHA* | Diffuse Color with Alpha (using this sends geometry to alpha pipeline) |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out vec3 *SPECULAR* | Specular Color |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out vec3 *EMISSION* | Emission Color |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out float *SPEC\_EXP* | Specular Exponent (Fragment Version) |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out float *GLOW* | Glow |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
| out mat4 *INV\_CAMERA\_MATRIX* | Inverse camera matrix, can be used to obtain world coords (see example below). |
|
||||
+----------------------------------+----------------------------------------------------------------------------------+
|
||||
|
||||
Material (3D) - LightShader
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+--------------------------------+-------------------------------+
|
||||
| Variable | Description |
|
||||
+================================+===============================+
|
||||
| const vec3 *NORMAL* | View-Space normal |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec3 *LIGHT\_DIR* | View-Space Light Direction |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec3 *EYE\_VEC* | View-Space Eye-Point Vector |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec3 *DIFFUSE* | Material Diffuse Color |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec3 *LIGHT\_DIFFUSE* | Light Diffuse Color |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec3 *SPECULAR* | Material Specular Color |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec3 *LIGHT\_SPECULAR* | Light Specular Color |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const float *SPECULAR\_EXP* | Specular Exponent |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec1 *SHADE\_PARAM* | Generic Shade Parameter |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const vec2 *POINT\_COORD* | Current UV for Point Sprite |
|
||||
+--------------------------------+-------------------------------+
|
||||
| out vec2 *LIGHT* | Resulting Light |
|
||||
+--------------------------------+-------------------------------+
|
||||
| const float *TIME* | Time (in seconds) |
|
||||
+--------------------------------+-------------------------------+
|
||||
|
||||
CanvasItem (2D) - VertexShader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| Variable | Description |
|
||||
+===================================+============================================================================================+
|
||||
| const vec2 *SRC\_VERTEX* | CanvasItem space vertex. |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| vec2 *UV* | UV |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| out vec2 *VERTEX* | Output LocalSpace vertex. |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| out vec2 *WORLD\_VERTEX* | Output WorldSpace vertex. (use this or the one above) |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| color *COLOR* | Vertex Color |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| out vec4 *VAR1* | Varying 1 Output |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| out vec4 *VAR2* | Varying 2 Output |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| out float *POINT\_SIZE* | Point Size (for points) |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| const mat4 *WORLD\_MATRIX* | Object World Matrix |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| const mat4 *EXTRA\_MATRIX* | Extra (user supplied) matrix via CanvasItem.draw\_set\_transform(). Identity by default. |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| const mat4 *PROJECTION\_MATRIX* | Projection Matrix (model coords to screen). |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
| const float *TIME* | Time (in seconds) |
|
||||
+-----------------------------------+--------------------------------------------------------------------------------------------+
|
||||
|
||||
CanvasItem (2D) - FragmentShader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| Variable | Description |
|
||||
+=====================================+==============================================================================+
|
||||
| const vec4 *SRC\_COLOR* | Vertex color |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| const vec4 *POSITION* | Screen Position |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| vec2 *UV* | UV |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| out color *COLOR* | Output Color |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| out vec3 *NORMAL* | Optional Normal (used for 2D Lighting) |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| out vec3 *NORMALMAP* | Optional Normal in standard normalmap format (flipped y and Z from 0 to 1) |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| out float *NORMALMAP\_DEPTH* | Depth option for above normalmap output, default value is 1.0 |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| const texture *TEXTURE* | Current texture in use for CanvasItem |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| const vec2 *TEXTURE\_PIXEL\_SIZE* | Pixel size for current 2D texture |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| in vec4 *VAR1* | Varying 1 Output |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| in vec4 *VAR2* | Varying 2 Output |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| const vec2 *SCREEN\_UV* | Screen Texture Coordinate (for using with texscreen) |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| const vec2 *POINT\_COORD* | Current UV for Point Sprite |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
| const float *TIME* | Time (in seconds) |
|
||||
+-------------------------------------+------------------------------------------------------------------------------+
|
||||
|
||||
CanvasItem (2D) - LightShader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| Variable | Description |
|
||||
+=====================================+===============================================================================+
|
||||
| const vec4 *POSITION* | Screen Position |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| in vec3 *NORMAL* | Input Normal |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| in vec2 *UV* | UV |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| in color *COLOR* | Input Color |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const texture *TEXTURE* | Current texture in use for CanvasItem |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const vec2 *TEXTURE\_PIXEL\_SIZE* | Pixel size for current 2D texture |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| in vec4 *VAR1* | Varying 1 Output |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| in vec4 *VAR2* | Varying 2 Output |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const vec2 *SCREEN\_UV* | Screen Texture Coordinate (for using with texscreen) |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const vec2 *POINT\_COORD* | Current UV for Point Sprite |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const float *TIME* | Time (in seconds) |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| vec2 *LIGHT\_VEC* | Vector from light to fragment, can be modified to alter shadow computation. |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const float *LIGHT\_HEIGHT* | Height of Light |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const color *LIGHT\_COLOR* | Color of Light |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| const color *LIGHT\_SHADOW\_COLOR* | Color of Light shadow |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| vec2 *LIGHT\_UV* | UV for light image |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| color *SHADOW* | Light shadow color override |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
| out vec4 *LIGHT* | Light Output (shader is ignored if this is not used) |
|
||||
+-------------------------------------+-------------------------------------------------------------------------------+
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Material that reads a texture, a color and multiples them, fragment
|
||||
program:
|
||||
|
||||
::
|
||||
|
||||
uniform color modulate;
|
||||
uniform texture source;
|
||||
|
||||
DIFFUSE = modulate.rgb * tex(source, UV).rgb;
|
||||
|
||||
Material that glows from red to white:
|
||||
|
||||
::
|
||||
|
||||
DIFFUSE = vec3(1,0,0) + vec3(1,1,1) * mod(TIME, 1.0);
|
||||
|
||||
Standard Blinn Lighting Shader
|
||||
|
||||
::
|
||||
|
||||
float NdotL = max(0.0, dot(NORMAL, LIGHT_DIR));
|
||||
vec3 half_vec = normalize(LIGHT_DIR + EYE_VEC);
|
||||
float eye_light = max(dot(NORMAL, half_vec), 0.0);
|
||||
LIGHT = LIGHT_DIFFUSE + DIFFUSE + NdotL;
|
||||
if (NdotL > 0.0) {
|
||||
LIGHT += LIGHT_SPECULAR + SPECULAR + pow(eye_light, SPECULAR_EXP);
|
||||
};
|
||||
|
||||
Obtaining world-space normal and position in material fragment program:
|
||||
|
||||
::
|
||||
|
||||
// Use reverse multiply because INV_CAMERA_MATRIX is world2cam
|
||||
|
||||
vec4 invcamx = INV_CAMERA_MATRIX.x;
|
||||
vec4 invcamy = INV_CAMERA_MATRIX.y;
|
||||
vec4 invcamz = INV_CAMERA_MATRIX.z;
|
||||
vec4 invcamw = INV_CAMERA_MATRIX.w;
|
||||
|
||||
mat3 invcam = mat3(invcamx.xyz, invcamy.xyz, invcamz.xyz);
|
||||
|
||||
vec3 world_normal = NORMAL * invcam;
|
||||
vec3 world_pos = (VERTEX - invcamw.xyz) * invcam;
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
- **Do not** use DIFFUSE_ALPHA unless you really intend to use
|
||||
transparency. Transparent materials must be sorted by depth and slow
|
||||
down the rendering pipeline. For opaque materials, just use DIFFUSE.
|
||||
- **Do not** use DISCARD unless you really need it. Discard makes
|
||||
rendering slower, specially on mobile devices.
|
||||
- TIME may reset after a while (may last an hour or so), it's meant
|
||||
for effects that vary over time.
|
||||
- In general, every built-in variable not used results in less shader
|
||||
code generated, so writing a single giant shader with a lot of code
|
||||
and optional scenarios is often not a good idea.
|
||||
90
learning/features/viewports/multiple_resolutions.rst
Normal file
90
learning/features/viewports/multiple_resolutions.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
.. _doc_multiple_resolutions:
|
||||
|
||||
Multiple resolutions
|
||||
====================
|
||||
|
||||
Base resolution
|
||||
---------------
|
||||
|
||||
A base screen resolution for the project can be specified in the project
|
||||
settings.
|
||||
|
||||
.. image:: /img/screenres.png
|
||||
|
||||
However, what it does is not completely obvious. When running on PC, the
|
||||
engine will attempt to set this resolution (or use something smaller if
|
||||
it fails). On mobile, consoles or devices with a fixed resolution or
|
||||
full screen rendering, this resolution will be ignored and the native
|
||||
resolution will be used instead. To compensate for this, Godot offers
|
||||
many ways to control how the screen will resize and stretch to different
|
||||
screen sizes.
|
||||
|
||||
Resizing
|
||||
--------
|
||||
|
||||
There are several types of devices, with several types of screens, which
|
||||
in turn have different pixel density and resolutions. Handling all of
|
||||
them can be a lot of work, so Godot tries to make the developer's life a
|
||||
little easier. The :ref:`Viewport <class_Viewport>`
|
||||
node has several functions to handle resizing, and the root node of the
|
||||
scene tree is always a viewport (scenes loaded are instanced as a child
|
||||
of it, and it can always be accessed by calling
|
||||
``get_tree().get_root()`` or ``get_node("/root")``).
|
||||
|
||||
In any case, while changing the root Viewport params is probably the
|
||||
most flexible way to deal with the problem, it can be a lot of work,
|
||||
code and guessing, so Godot provides a simple set of parameters in the
|
||||
project settings to handle multiple resolutions.
|
||||
|
||||
Stretch settings
|
||||
----------------
|
||||
|
||||
Stretch settings are located in the project settings, it's just a bunch
|
||||
of configuration variables that provide several options:
|
||||
|
||||
.. image:: /img/stretchsettings.png
|
||||
|
||||
Stretch mode
|
||||
------------
|
||||
|
||||
- **Disabled**: The first is the stretch mode. By default this is
|
||||
disabled, which means no stretching happens (the bigger the screen or
|
||||
window, the bigger the resolution, always matching pixels 1:1).
|
||||
- **2D**: In this mode, the resolution specified in display/width and
|
||||
display/height in the project settings will be stretched to cover the
|
||||
whole screen. This means that 3D will be unaffected (will just render
|
||||
to higher-res) and 2D will also be rendered at higher-res, just
|
||||
enlarged.
|
||||
- **Viewport**: Viewport scaling is different, the root
|
||||
:ref:`Viewport <class_Viewport>`
|
||||
is set as a render target, and still renders precisely to the
|
||||
resolution specified in the ``display/`` section of the project
|
||||
settings. Finally, this viewport is copied and scaled to fit the
|
||||
screen. This mode is useful when working with pixel-precise games, or
|
||||
just for the sake of rendering to a lower resolution for improving
|
||||
performance.
|
||||
|
||||
.. image:: /img/stretch.png
|
||||
|
||||
Stretch aspect
|
||||
--------------
|
||||
|
||||
- **Ignore**: Ignore the aspect ratio when stretching the screen. This
|
||||
means that the original resolution will be stretched to fit the new
|
||||
one, even if it's wider or narrower.
|
||||
- **Keep**: Keep aspect ratio when stretching the screen. This means
|
||||
that the original resolution will be kept when fitting the new one,
|
||||
and black bars will be added to the sides or the top/bottom of the
|
||||
screen.
|
||||
- **Keep Width**: Keep aspect ratio when stretching the screen, but if
|
||||
the resulting screen is taller than the specified resolution, it will
|
||||
be stretched vertically (and more vertical resolution will be
|
||||
reported in the viewport, proportionally). This is usually the best
|
||||
option for creating GUIs or HUDs that scale, so some controls can be
|
||||
anchored to the bottom (:ref:`doc_size_and_anchors`).
|
||||
- **Keep Height**: Keep aspect ratio when stretching the screen, but if
|
||||
the resulting screen is wider than the specified resolution, it will
|
||||
be stretched horizontally (and more horizontal resolution will be
|
||||
reported in the viewport, proportionally). This is usually the best
|
||||
option for 2D games that scroll horizontally (like runners or
|
||||
platformers).
|
||||
184
learning/features/viewports/viewports.rst
Normal file
184
learning/features/viewports/viewports.rst
Normal file
@@ -0,0 +1,184 @@
|
||||
.. _doc_viewports:
|
||||
|
||||
Viewports
|
||||
=========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Godot has a small but very useful feature called viewports. Viewports
|
||||
are, as they name implies, rectangles where the world is drawn. They
|
||||
have three main uses, but can flexibly adapted to a lot more. All this
|
||||
is done via the :ref:`Viewport <class_Viewport>` node.
|
||||
|
||||
.. image:: /img/viewportnode.png
|
||||
|
||||
The main uses in question are:
|
||||
|
||||
- **Scene Root**: The root of the active scene is always a Viewport.
|
||||
This is what displays the scenes created by the user. (You should
|
||||
know this by having read previous tutorials!)
|
||||
- **Sub-Viewports**: These can be created when a Viewport is a child of
|
||||
a :ref:`Control <class_Control>`.
|
||||
- **Render Targets**: Viewports can be set to "RenderTarget" mode. This
|
||||
means that the viewport is not directly visible, but its contents
|
||||
can be accessed via a :ref:`Texture <class_Texture>`.
|
||||
|
||||
Input
|
||||
-----
|
||||
|
||||
Viewports are also responsible of delivering properly adjusted and
|
||||
scaled input events to all its children nodes. Both the root viewport
|
||||
and sub-viewports do this automatically, but render targets do not.
|
||||
Because of this, the user must do it manually via the
|
||||
:ref:`Viewport.input() <class_Viewport_input>` function if needed.
|
||||
|
||||
Listener
|
||||
--------
|
||||
|
||||
Godot supports 3D sound (in both 2D and 3D nodes), more on this can be
|
||||
found in another tutorial (one day..). For this type of sound to be
|
||||
audible, the viewport needs to be enabled as a listener (for 2D or 3D).
|
||||
If you are using a custom viewport to display your world, don't forget
|
||||
to enable this!
|
||||
|
||||
Cameras (2D & 3D)
|
||||
-----------------
|
||||
|
||||
When using a 2D or 3D :ref:`Camera <class_Camera>` /
|
||||
:ref:`Camera2D <class_Camera2D>`, cameras will always display on the
|
||||
closest parent viewport (going towards the root). For example, in the
|
||||
following hierarchy:
|
||||
|
||||
- Viewport
|
||||
|
||||
- Camera
|
||||
|
||||
Camera will display on the parent viewport, but in the following one:
|
||||
|
||||
- Camera
|
||||
|
||||
- Viewport
|
||||
|
||||
It will not (or may display in the root viewport if this is a subscene).
|
||||
|
||||
There can be only one active camera per viewport, so if there is more
|
||||
than one, make sure that the desired one has the "current" property set,
|
||||
or make it the current camera by calling:
|
||||
|
||||
::
|
||||
|
||||
camera.make_current()
|
||||
|
||||
Scale & stretching
|
||||
------------------
|
||||
|
||||
Viewports have a "rect" property. X and Y are often not used (only the
|
||||
root viewport really uses them), while WIDTH AND HEIGHT represent the
|
||||
size of the viewport in pixels. For Sub-Viewports, these values are
|
||||
overridden by the ones from the parent control, but for render targets
|
||||
this sets their resolution.
|
||||
|
||||
It is also possible to scale the 2D content and make it believe the
|
||||
viewport resolution is other than the one specified in the rect, by
|
||||
calling:
|
||||
|
||||
::
|
||||
|
||||
viewport.set_size_override(w,h) #custom size for 2D
|
||||
viewport.set_size_override_stretch(true/false) #enable stretch for custom size
|
||||
|
||||
The root viewport uses this for the stretch options in the project
|
||||
settings.
|
||||
|
||||
Worlds
|
||||
------
|
||||
|
||||
For 3D, a Viewport will contain a :ref:`World <class_World>`. This
|
||||
is basically the universe that links physics and rendering together.
|
||||
Spatial-base nodes will register using the World of the closest
|
||||
viewport. By default, newly created viewports do not contain a World but
|
||||
use the same as a parent viewport (root viewport does contain one
|
||||
though, which is the one objects are rendered to by default). A world can
|
||||
be set in a viewport using the "world" property, and that will separate
|
||||
all children nodes of that viewport from interacting with the parent
|
||||
viewport world. This is specially useful in scenarios where, for
|
||||
example, you might want to show a separate character in 3D imposed over
|
||||
the game (like in Starcraft).
|
||||
|
||||
As a helper for situations where you want to create viewports that
|
||||
display single objects and don't want to create a world, viewport has
|
||||
the option to use its own World. This is very useful when you want to
|
||||
instance 3D characters or objects in the 2D world.
|
||||
|
||||
For 2D, each Viewport always contains its own :ref:`World2D <class_World2D>`.
|
||||
This suffices in most cases, but in case sharing them may be desired, it
|
||||
is possible to do so by calling the viewport API manually.
|
||||
|
||||
Capture
|
||||
-------
|
||||
|
||||
It is possible to query a capture of the viewport contents. For the root
|
||||
viewport this is effectively a screen capture. This is done with the
|
||||
following API:
|
||||
|
||||
::
|
||||
|
||||
# queues a screen capture, will not happen immediately
|
||||
viewport.queue_screen_capture()
|
||||
|
||||
After a frame or two (check _process()), the capture will be ready,
|
||||
get it back by using:
|
||||
|
||||
::
|
||||
|
||||
var capture = viewport.get_screen_capture()
|
||||
|
||||
If the returned image is empty, capture still didn't happen, wait a
|
||||
little more, as this API is asyncronous.
|
||||
|
||||
Sub-viewport
|
||||
------------
|
||||
|
||||
If the viewport is a child of a control, it will become active and
|
||||
display anything it has inside. The layout is something like this:
|
||||
|
||||
- Control
|
||||
|
||||
- Viewport
|
||||
|
||||
The viewport will cover the area of its parent control completely.
|
||||
|
||||
.. image:: /img/subviewport.png
|
||||
|
||||
Render target
|
||||
-------------
|
||||
|
||||
To set as a render target, just toggle the "render target" property of
|
||||
the viewport to enabled. Note that whatever is inside will not be
|
||||
visible in the scene editor. To display the contents, the render target
|
||||
texture must be used. This can be requested via code using (for
|
||||
example):
|
||||
|
||||
::
|
||||
|
||||
var rtt = viewport.get_render_target_texture()
|
||||
sprite.set_texture(rtt)
|
||||
|
||||
By default, re-rendering of the render target happens when the render
|
||||
target texture has been drawn in a frame. If visible, it will be
|
||||
rendered, otherwise it will not. This behavior can be changed to manual
|
||||
rendering (once), or always render, no matter if visible or not.
|
||||
|
||||
A few classes are created to make this easier in most common cases
|
||||
inside the editor:
|
||||
|
||||
- :ref:`ViewportSprite <class_ViewportSprite>` (for 2D).
|
||||
- ViewportQuad (for 3D).
|
||||
- ViewportFrame (for GUI).
|
||||
|
||||
*TODO: Review the doc, ViewportQuad and ViewportFrame don't exist in 2.0.*
|
||||
|
||||
Make sure to check the viewport demos! Viewport folder in the demos
|
||||
archive available to download, or
|
||||
https://github.com/godotengine/godot-demo-projects/tree/master/viewport
|
||||
97
learning/getting_started/animations.rst
Normal file
97
learning/getting_started/animations.rst
Normal file
@@ -0,0 +1,97 @@
|
||||
.. _doc_animations:
|
||||
|
||||
Animations
|
||||
==========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial will explain how everything is animated in Godot. Godot's
|
||||
animation system is extremely powerful and flexible.
|
||||
|
||||
To begin, let's just use the scene from the previous tutorial (:ref:`doc_splash_screen`).
|
||||
The goal is, to add a simple animation to it. Here's a copy
|
||||
just in case: :download:`robisplash.zip </files/robisplash.zip>`.
|
||||
|
||||
Creating the animation
|
||||
----------------------
|
||||
|
||||
First of all, add an :ref:`AnimationPlayer <class_AnimationPlayer>`
|
||||
node to the scene as a child of "bg" (the root node):
|
||||
|
||||
.. image:: /img/animplayer.png
|
||||
|
||||
When a node of this type is selected, the animation editor panel will
|
||||
appear:
|
||||
|
||||
.. image:: /img/animpanel.png
|
||||
|
||||
So, it's time to create a new animation! Press the new animation button
|
||||
and name the animation "intro".
|
||||
|
||||
.. image:: /img/animnew.png
|
||||
|
||||
After the animation has been created, it's time to edit it, by
|
||||
pressing the "edit" button:
|
||||
|
||||
.. image:: /img/animedit.png
|
||||
|
||||
Editing the animation
|
||||
---------------------
|
||||
|
||||
Now this is when the magic happens! Several things happen when the
|
||||
"edit" button is pressed, the first one is that the animation editor
|
||||
appears above the animation panel. (In Godot 2.x, this button has been
|
||||
removed, instead, click on the 'animation' toggle at the bottom right
|
||||
for similar functionality.)
|
||||
|
||||
.. image:: /img/animeditor.png
|
||||
|
||||
The second and most important, is that the property editor enters
|
||||
"animation editing" mode. In this mode, a key icon appears next to
|
||||
every property of the property editor. This means that, in Godot, *any
|
||||
property of any object* can be animated:
|
||||
|
||||
.. image:: /img/propertykeys.png
|
||||
|
||||
Making the logo appear
|
||||
----------------------
|
||||
|
||||
The logo will appear from the top of the screen. After selecting
|
||||
the animation player, the editor panel will stay visible until
|
||||
manually hidden (or the animation node is erased). Taking advantage of
|
||||
this, select the "logo" node and go to the "pos" property, move it up
|
||||
to position: 114,-400.
|
||||
|
||||
Once in this position, press the key button next to the property:
|
||||
|
||||
.. image:: /img/keypress.png
|
||||
|
||||
As the track is new, a dialog will appear asking to create it. Confirm
|
||||
it!
|
||||
|
||||
.. image:: /img/addtrack.png
|
||||
|
||||
The keyframe will be added in the animation player editor:
|
||||
|
||||
.. image:: /img/keyadded.png
|
||||
|
||||
Move the editor cursor to the end, by clicking here:
|
||||
|
||||
.. image:: /img/move_cursor.png
|
||||
|
||||
Change the logo position to 114,0 and add a keyframe again. With two
|
||||
keyframes, the animation happens.
|
||||
|
||||
.. image:: /img/animation.png
|
||||
|
||||
Pressing Play on the animation panel will make the logo descend. To test
|
||||
it by running the scene, the autoplay button can tag the animation to
|
||||
start automatically when the scene starts:
|
||||
|
||||
.. image:: /img/autoplay.png
|
||||
|
||||
And finally, when running the scene, the animation should look like
|
||||
this:
|
||||
|
||||
.. image:: /img/out.gif
|
||||
111
learning/getting_started/filesystem.rst
Normal file
111
learning/getting_started/filesystem.rst
Normal file
@@ -0,0 +1,111 @@
|
||||
.. _doc_filesystem:
|
||||
|
||||
File system
|
||||
==========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
File systems are yet another hot topic in engine development. The
|
||||
file system manages how the 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 together.
|
||||
|
||||
Initial versions of the Godot engine (and previous iterations before it was
|
||||
named Godot) used a database. Assets were stored in it and assigned an
|
||||
ID. Other approaches were tried as well, such as local databases, files with
|
||||
metadata, etc. In the end the simple approach won and now Godot stores
|
||||
all assets as files in the 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.
|
||||
|
||||
In general the Godot file system avoids using metadata files. The reason for
|
||||
this is simple, existing asset managers and VCSs are simply much better than
|
||||
anything we can implement, so Godot tries the best to play along with SVN,
|
||||
Git, Mercurial, Perforce, etc.
|
||||
|
||||
Example of a file system contents:
|
||||
|
||||
::
|
||||
|
||||
/engine.cfg
|
||||
/enemy/enemy.scn
|
||||
/enemy/enemy.gd
|
||||
/enemy/enemysprite.png
|
||||
/player/player.gd
|
||||
|
||||
engine.cfg
|
||||
----------
|
||||
|
||||
The engine.cfg 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 engine.cfg 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\engine.cfg`` needs to be typed as
|
||||
``c:/project/engine.cfg``.
|
||||
|
||||
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
|
||||
engine.cfg is located, so in fact ``res://engine.cfg`` 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 often still needed for various 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.
|
||||
|
||||
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 very 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.
|
||||
|
||||
The second is that under Windows and OSX 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 just fine on their platorm, 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 defines a naming convention for files when
|
||||
working with Godot! One simple fool-proof convention is to only allow lowercase
|
||||
file and path names.
|
||||
176
learning/getting_started/gui_tutorial.rst
Normal file
176
learning/getting_started/gui_tutorial.rst
Normal file
@@ -0,0 +1,176 @@
|
||||
.. _doc_gui_tutorial:
|
||||
|
||||
GUI tutorial
|
||||
============
|
||||
|
||||
Introduction
|
||||
~~~~~~~~~~~~
|
||||
|
||||
If there is something that most programmers hate with passion, it is
|
||||
programming graphical user interfaces (GUIs). It's boring, tedious and
|
||||
unchallenging. Several aspects make matters worse, such as:
|
||||
|
||||
- Pixel alignment of UI elements is difficult (such that it looks just
|
||||
like the designer intends).
|
||||
- UIs are changed constantly due to design and usability issues that
|
||||
appear during testing.
|
||||
- Handling proper screen re-sizing for different display resolutions.
|
||||
- Animating several screen components, to make it look less static.
|
||||
|
||||
GUI programming is one of the leading causes of programmer burnout.
|
||||
During the development of Godot (and previous engine iterations),
|
||||
several techniques and philosophies for UI development were put into
|
||||
practice, such as immediate mode, containers, anchors, scripting, etc.
|
||||
This was always done with the main goal of reducing the stress
|
||||
programmers had to face while putting together user interfaces.
|
||||
|
||||
In the end, the resulting UI subsystem in Godot is an efficient solution
|
||||
to this problem, and works by mixing together a few different
|
||||
approaches. While the learning curve is a little steeper than in other
|
||||
toolkits, developers can put together complex user interfaces in very
|
||||
little time, by sharing the same set of tools with designers and
|
||||
animators.
|
||||
|
||||
Control
|
||||
~~~~~~~
|
||||
|
||||
The basic node for UI elements is :ref:`Control <class_Control>`
|
||||
(sometimes called "Widget" or "Box" in other toolkits). Every node that
|
||||
provides user interface functionality descends from it.
|
||||
|
||||
When controls are put in a scene tree as a child of another control,
|
||||
its coordinates (position, size) are always relative to the parent.
|
||||
This sets the basis for editing complex user interfaces quickly and
|
||||
visually.
|
||||
|
||||
Input and drawing
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Controls receive input events by means of the
|
||||
:ref:`Control._input_event() <class_Control__input_event>`
|
||||
callback. Only one control, the one in focus, will receive
|
||||
keyboard/joypad events (see
|
||||
:ref:`Control.set_focus_mode() <class_Control_set_focus_mode>`
|
||||
and :ref:`Control.grab_focus() <class_Control_grab_focus>`).
|
||||
|
||||
Mouse motion events are received by the control directly below the mouse
|
||||
pointer. When a control receives a mouse button pressed event, all
|
||||
subsequent motion events are received by the pressed control until that
|
||||
button is released, even if the pointer moves outside the control
|
||||
boundary.
|
||||
|
||||
Like any class that inherits from :ref:`CanvasItem <class_CanvasItem>`
|
||||
(Control does), a :ref:`CanvasItem._draw() <class_CanvasItem__draw>`
|
||||
callback will be received at the beginning and every time the control
|
||||
needs to be redrawn (the programmer needs to call
|
||||
:ref:`CanvasItem.update() <class_CanvasItem_update>`
|
||||
to enqueue the CanvasItem for redraw). If the control is not visible
|
||||
(yet another CanvasItem property), the control does not receive any
|
||||
input.
|
||||
|
||||
In general though, the programmer does not need to deal with drawing and
|
||||
input events directly when building UIs (that is more useful when
|
||||
creating custom controls). Instead, controls emit different kinds of
|
||||
signals with contextual information for when action occurs. For
|
||||
example, a :ref:`Button <class_Button>` emits
|
||||
a "pressed" signal when pressed, a :ref:`Slider <class_Slider>` will
|
||||
emit a "value_changed" when dragged, etc.
|
||||
|
||||
Custom control mini tutorial
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before going into more depth, creating a custom control will be a good
|
||||
way to get the picture on how controls works, as they are not as
|
||||
complex as it might seem.
|
||||
|
||||
Additionally, even though Godot comes with dozens of controls for
|
||||
different purposes, it happens often that it's easier to attain a
|
||||
specific functionality by creating a new one.
|
||||
|
||||
To begin, create a single-node scene. The node is of type "Control" and
|
||||
has a certain area of the screen in the 2D editor, like this:
|
||||
|
||||
.. image:: /img/singlecontrol.png
|
||||
|
||||
Add a script to that node, with the following code:
|
||||
|
||||
::
|
||||
|
||||
extends Control
|
||||
|
||||
var tapped=false
|
||||
|
||||
func _draw():
|
||||
|
||||
var r = Rect2( Vector2(), get_size() )
|
||||
if (tapped):
|
||||
draw_rect(r, Color(1,0,0) )
|
||||
else:
|
||||
draw_rect(r, Color(0,0,1) )
|
||||
|
||||
func _input_event(ev):
|
||||
|
||||
if (ev.type==InputEvent.MOUSE_BUTTON and ev.pressed):
|
||||
tapped=true
|
||||
update()
|
||||
|
||||
Then run the scene. When the rectangle is clicked/tapped, it will change
|
||||
color from blue to red. That synergy between the events and the drawing
|
||||
is pretty much how most controls work internally.
|
||||
|
||||
.. image:: /img/ctrl_normal.png
|
||||
|
||||
.. image:: /img/ctrl_tapped.png
|
||||
|
||||
UI complexity
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
As mentioned before, Godot includes dozens of controls ready for use
|
||||
in a user interface. Such controls are divided in two categories. The
|
||||
first is a small set of controls that work well for creating most game
|
||||
user interfaces. The second (and most controls are of this type) are
|
||||
meant for complex user interfaces and uniform skinning through styles. A
|
||||
description is presented as follows to help understand which one should
|
||||
be used in which case.
|
||||
|
||||
Simplified UI controls
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This set of controls is enough for most games, where complex
|
||||
interactions or ways to present information are not necessary. They can
|
||||
be skinned easily with regular textures.
|
||||
|
||||
- :ref:`Label <class_Label>`: Node used for showing text.
|
||||
- :ref:`TextureFrame <class_TextureFrame>`: Displays a single texture,
|
||||
which can be scaled or kept fixed.
|
||||
- :ref:`TextureButton <class_TextureButton>`: Displays a simple textured
|
||||
button, states such as pressed, hover, disabled, etc. can be set.
|
||||
- :ref:`TextureProgress <class_TextureProgress>`: Displays a single
|
||||
textured progress bar.
|
||||
|
||||
Additionally, re-positioning of controls is most efficiently done with
|
||||
anchors in this case (see the :ref:`doc_size_and_anchors` tutorial for more
|
||||
information).
|
||||
|
||||
In any case, it will happen often that even for simple games, more
|
||||
complex UI behaviors are required. An example of this is a scrolling
|
||||
list of elements (for a high score table, for example), which needs a
|
||||
:ref:`ScrollContainer <class_ScrollContainer>`
|
||||
and a :ref:`VBoxContainer <class_VBoxContainer>`.
|
||||
These kind of more advanced controls can be mixed with the regular ones
|
||||
seamlessly (they are all controls after all).
|
||||
|
||||
Complex UI controls
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The rest of the controls (and there are dozens of them!) are meant for
|
||||
another set of scenarios, most commonly:
|
||||
|
||||
- Games that require complex UIs, such as PC RPGs, MMOs, strategy,
|
||||
sims, etc.
|
||||
- Creating custom development tools to speed up content creation.
|
||||
- Creating Godot Editor Plugins, to extend the engine functionality.
|
||||
|
||||
Re-positioning controls for these kind of interfaces is more commonly
|
||||
done with containers (see the :ref:`doc_size_and_anchors` tutorial for more
|
||||
information).
|
||||
113
learning/getting_started/instancing.rst
Normal file
113
learning/getting_started/instancing.rst
Normal file
@@ -0,0 +1,113 @@
|
||||
.. _doc_instancing:
|
||||
|
||||
Instancing
|
||||
==========
|
||||
|
||||
Rationale
|
||||
---------
|
||||
|
||||
Having a scene and throwing nodes into it might work for small projects,
|
||||
but as a project grows, more and more nodes are used and it can quickly
|
||||
become unmanageable. To solve this, Godot allows a project to be
|
||||
separated in several scenes. This, however, does not work the same way
|
||||
as in other game engines. In fact, it's quite different, so please do
|
||||
not skip this tutorial!
|
||||
|
||||
To recap: A scene is a collection of nodes organized as a tree, where
|
||||
they can have only one single node as the tree root.
|
||||
|
||||
.. image:: /img/tree.png
|
||||
|
||||
In Godot, a scene can be created and saved to disk. As many scenes
|
||||
can be created and saved as desired.
|
||||
|
||||
.. image:: /img/instancingpre.png
|
||||
|
||||
Afterwards, while editing an existing or a new scene, other scenes can
|
||||
be instanced as part of it:
|
||||
|
||||
.. image:: /img/instancing.png
|
||||
|
||||
In the above picture, Scene B was added to Scene A as an instance. It
|
||||
may seem weird at first, but at the end of this tutorial it will make
|
||||
complete sense!
|
||||
|
||||
Instancing, step by step
|
||||
------------------------
|
||||
|
||||
To learn how to do instancing, let's start with downloading a sample
|
||||
project: :download:`instancing.zip </files/instancing.zip>`.
|
||||
|
||||
Unzip this scene in any place of your preference. Then, add this scene to
|
||||
the project manager using the 'Import' option:
|
||||
|
||||
.. image:: /img/importproject.png
|
||||
|
||||
Simply browse to inside the project location and open the "engine.cfg"
|
||||
file. The new project will appear on the list of projects. Edit the
|
||||
project by using the 'Edit' option.
|
||||
|
||||
This project contains two scenes "ball.scn" and "container.scn". The
|
||||
ball scene is just a ball with physics, while container scene has a
|
||||
nicely shaped collision, so balls can be thrown in there.
|
||||
|
||||
.. image:: /img/ballscene.png
|
||||
|
||||
.. image:: /img/contscene.png
|
||||
|
||||
Open the container scene, then select the root node:
|
||||
|
||||
.. image:: /img/controot.png
|
||||
|
||||
Afterwards, push the link shaped button, this is the instancing button!
|
||||
|
||||
.. image:: /img/continst.png
|
||||
|
||||
Select the ball scene (ball.scn), the ball should appear in the origin
|
||||
(0,0), move it to around the center
|
||||
|
||||
of the scene, like this:
|
||||
|
||||
.. image:: /img/continstanced.png
|
||||
|
||||
Press Play and Voila!
|
||||
|
||||
.. image:: /img/playinst.png
|
||||
|
||||
The instanced ball fell to the bottom of the pit.
|
||||
|
||||
A little more
|
||||
-------------
|
||||
|
||||
There can be as many instances as desired in a scene, just try
|
||||
instancing more balls, or duplicating them (ctrl-D or duplicate button):
|
||||
|
||||
.. image:: /img/instmany.png
|
||||
|
||||
Then try running the scene again:
|
||||
|
||||
.. image:: /img/instmanyrun.png
|
||||
|
||||
Cool, huh? This is how instancing works.
|
||||
|
||||
Editing instances
|
||||
-----------------
|
||||
|
||||
Select one of the many copies of the balls and go to the property
|
||||
editor. Let's make it bounce a lot more, so look for the bounce
|
||||
parameter and set it to 1.0:
|
||||
|
||||
.. image:: /img/instedit.png
|
||||
|
||||
The next it will happen is that a green "revert" button appears. When
|
||||
this button is present, it means we modified a property from the
|
||||
instanced scene to override for a specific value in this instance. Even
|
||||
if that property is modified in the original scene, the custom value
|
||||
will always overwrite it. Pressing the revert button will restore the
|
||||
property to the original value that came from the scene.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
Instancing seems handy, but there is more to it than meets the eye!
|
||||
The next part of the instancing tutorial should cover the rest..
|
||||
77
learning/getting_started/instancing_continued.rst
Normal file
77
learning/getting_started/instancing_continued.rst
Normal file
@@ -0,0 +1,77 @@
|
||||
.. _doc_instancing_continued:
|
||||
|
||||
Instancing (continued)
|
||||
======================
|
||||
|
||||
Recap
|
||||
-----
|
||||
|
||||
Instancing has many handy uses. At a glance, with instancing you have:
|
||||
|
||||
- The ability to subdivide scenes and make them easier to manage.
|
||||
- A more flexible alternative to prefabs (and much more powerful given
|
||||
instances work at many levels).
|
||||
- A way to design more complex game flows or even UIs (UI Elements are
|
||||
nodes in Godot too).
|
||||
|
||||
Design language
|
||||
---------------
|
||||
|
||||
But the real strong point of instancing scenes is that it works as an
|
||||
excellent design language. This is pretty much what makes Godot special
|
||||
and different to any other engine out there. The entire engine was designed
|
||||
from the ground up around this concept.
|
||||
|
||||
When making games with Godot, the recommended approach is to leave aside
|
||||
other design patterns such as MVC or Entity-Relationship diagrams and
|
||||
start thinking games in a more natural way. Start by imagining the
|
||||
visible elements in a game, the ones that can be named not by just a
|
||||
programmer but by anyone.
|
||||
|
||||
For example, here's how a simple shooter game can be imagined:
|
||||
|
||||
.. image:: /img/shooter_instancing.png
|
||||
|
||||
It's pretty easy to come up with a diagram like this for almost any kind
|
||||
of game. Just write down the elements that come to mind, and then the
|
||||
arrows that represent ownership.
|
||||
|
||||
Once this diagram exists, making a game is about creating a scene for
|
||||
each of those nodes, and use instancing (either by code or from the editor) to represent ownership.
|
||||
|
||||
Most of the time programming games (or software in general) is spent
|
||||
designing an architecture and fitting game components to that
|
||||
architecture. Designing based on scenes replaces that and makes
|
||||
development much faster and more straightforward, allowing to
|
||||
concentrate on the game itself. Scene/Instancing based design is
|
||||
extremely efficient at saving a large part of that work, since most of
|
||||
the components designed map directly to a scene. This way, none or
|
||||
little architectural code is needed.
|
||||
|
||||
The following is a more complex example, an open-world type of game with
|
||||
lots of assets and parts that interact:
|
||||
|
||||
.. image:: /img/openworld_instancing.png
|
||||
|
||||
Make some rooms with furniture, then connect them. Make a house later,
|
||||
and use those rooms as the interior.
|
||||
|
||||
The house can be part of a citadel, which has many houses. Finally the
|
||||
citadel can be put on the world map terrain. Add also guards and other
|
||||
NPCs to the citadel by previously creating their scenes.
|
||||
|
||||
With Godot, games can grow as quickly as desired, as all it needs is
|
||||
to create and instance more scenes. The editor UI is designed to be
|
||||
operated by non programmers too, so a typical team development process
|
||||
can involve 3D or 2D artists, level designers, game designers, animators,
|
||||
etc all working with the editor interface.
|
||||
|
||||
Information overload!
|
||||
---------------------
|
||||
|
||||
Do not worry too much, the important part of this tutorial is to create
|
||||
awareness on how scenes and instancing are used in real life. The best
|
||||
way to understand all this is to make some games.
|
||||
|
||||
Everything will become very obvious when put to practice, so please do
|
||||
not scratch your head and go on to the next tutorial!
|
||||
144
learning/getting_started/resources.rst
Normal file
144
learning/getting_started/resources.rst
Normal file
@@ -0,0 +1,144 @@
|
||||
.. _doc_resources:
|
||||
|
||||
Resources
|
||||
=========
|
||||
|
||||
Nodes and resources
|
||||
-------------------
|
||||
|
||||
So far, :ref:`Nodes <class_Node>`
|
||||
have been the most important datatype in Godot, as most of the behaviors
|
||||
and features of the engine are implemented through them. There is
|
||||
another datatype that is equally important:
|
||||
:ref:`Resource <class_Resource>`.
|
||||
|
||||
Where *Nodes* focus on behaviors, such as drawing a sprite, drawing a
|
||||
3D model, physics, GUI controls, etc,
|
||||
|
||||
**Resources** are mere **data containers**. This means that they don't
|
||||
do any action nor process any information. Resources just contain
|
||||
data.
|
||||
|
||||
Examples of resources are
|
||||
:ref:`Texture <class_Texture>`,
|
||||
:ref:`Script <class_Script>`,
|
||||
:ref:`Mesh <class_Mesh>`,
|
||||
:ref:`Animation <class_Animation>`,
|
||||
:ref:`Sample <class_Sample>`,
|
||||
:ref:`AudioStream <class_AudioStream>`,
|
||||
:ref:`Font <class_Font>`,
|
||||
:ref:`Translation <class_Translation>`,
|
||||
etc.
|
||||
|
||||
When Godot saves or loads (from disk) a scene (.scn or .xml), an image
|
||||
(png, jpg), a script (.gd) or pretty much anything, that file is
|
||||
considered a resource.
|
||||
|
||||
When a resource is loaded from disk, **it is always loaded once**. That
|
||||
means, if there is a copy of that resource already loaded in memory,
|
||||
trying to load the resource again will just return the same copy again
|
||||
and again. This corresponds with the fact that resources are just data
|
||||
containers, so there is no need to have them duplicated.
|
||||
|
||||
Typically, every object in Godot (Node, Resource, or anything else) can
|
||||
export properties, properties can be of many types (like a string,
|
||||
integer, Vector2, etc) and one of those types can be a resource. This
|
||||
means that both nodes and resources can contain resources as properties.
|
||||
To make it a little more visual:
|
||||
|
||||
.. image:: /img/nodes_resources.png
|
||||
|
||||
External vs built-in
|
||||
--------------------
|
||||
|
||||
The resource properties can reference resources in two ways,
|
||||
*external* (on disk) or **built-in**.
|
||||
|
||||
To be more specific, here's a :ref:`Texture <class_Texture>`
|
||||
in a :ref:`Sprite <class_Sprite>` node:
|
||||
|
||||
.. image:: /img/spriteprop.png
|
||||
|
||||
Pressing the ">" button on the right side of the preview allows to
|
||||
view and edit the resources properties. One of the properties (path)
|
||||
shows where it comes from. In this case, it comes from a png image.
|
||||
|
||||
.. image:: /img/resourcerobi.png
|
||||
|
||||
When the resource comes from a file, it is considered an *external*
|
||||
resource. If the path property is erased (or it never had a path to
|
||||
begin with), it is considered a built-in resource.
|
||||
|
||||
For example, if the path \`"res://robi.png"\` is erased from the "path"
|
||||
property in the above example, and then the scene is saved, the resource
|
||||
will be saved inside the .scn scene file, no longer referencing the
|
||||
external "robi.png". However, even if saved as built-in, and even though
|
||||
the scene can be instanced multiple times, the resource will always
|
||||
be loaded only once. That means, different Robi robot scenes instanced
|
||||
at the same time will still share the same image.
|
||||
|
||||
Loading resources from code
|
||||
---------------------------
|
||||
|
||||
Loading resources from code is easy. There are two ways to do it. The
|
||||
first is to use load(), like this:
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
var res = load("res://robi.png") # resource is loaded when line is executed
|
||||
get_node("sprite").set_texture(res)
|
||||
|
||||
The second way is more optimal, but only works with a string constant
|
||||
parameter, because it loads the resource at compile-time.
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
var res = preload("res://robi.png") # resource is loaded at compile time
|
||||
get_node("sprite").set_texture(res)
|
||||
|
||||
Loading scenes
|
||||
--------------
|
||||
|
||||
Scenes are also resources, but there is a catch. Scenes saved to disk
|
||||
are resources of type :ref:`PackedScene <class_PackedScene>`,
|
||||
this means that the scene is packed inside a resource.
|
||||
|
||||
To obtain an instance of the scene, the method
|
||||
:ref:`PackedScene.instance() <class_PackedScene_instance>`
|
||||
must be used.
|
||||
|
||||
::
|
||||
|
||||
func _on_shoot():
|
||||
var bullet = preload("res://bullet.scn").instance()
|
||||
add_child(bullet)
|
||||
|
||||
This method creates the nodes in hierarchy, configures them (sets all
|
||||
the properties) and returns the root node of the scene, which can be
|
||||
added to any other node.
|
||||
|
||||
The approach has several advantages. As the
|
||||
:ref:`PackedScene.instance() <class_PackedScene_instance>`
|
||||
function is pretty fast, adding extra content to the scene can be done
|
||||
efficiently. New enemies, bullets, effects, etc can be added or
|
||||
removed quickly, without having to load them again from disk each
|
||||
time. It is important to remember that, as always, images, meshes, etc
|
||||
are all shared between the scene instances.
|
||||
|
||||
Freeing resources
|
||||
-----------------
|
||||
|
||||
Resource extends from :ref:`Reference <class_Reference>`.
|
||||
As such, when a resource is no longer in use, it will automatically free
|
||||
itself. Since, in most cases, Resources are contained in Nodes, scripts
|
||||
or other resources, when a node is removed or freed, all the children
|
||||
resources are freed too.
|
||||
|
||||
Scripting
|
||||
---------
|
||||
|
||||
Like any object in Godot, not just nodes, resources can be scripted,
|
||||
too. However, there isn't generally much of an advantage, as resources
|
||||
are just data containers.
|
||||
153
learning/getting_started/scene_tree.rst
Normal file
153
learning/getting_started/scene_tree.rst
Normal file
@@ -0,0 +1,153 @@
|
||||
.. _doc_scene_tree:
|
||||
|
||||
SceneTree
|
||||
=========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This is where things start getting abstract, but don't panic, as
|
||||
there's not really more depth than this.
|
||||
|
||||
In previous tutorials, everything revolves around the concept of
|
||||
nodes, scenes are made of them, and they become active once they enter
|
||||
the *scene tree*.
|
||||
|
||||
This concept deserves going into a little more detail. In fact, the
|
||||
scene system is not even a core component of Godot, as it is possible to
|
||||
skip it and write a script (or C++ code) that talks directly to the
|
||||
servers. But making a game that way would be a lot of work.
|
||||
|
||||
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-syncronized callback), fixed
|
||||
(physics-synchronized callback), and input. Again, this is really low
|
||||
level and when making games in Godot, writing your own MainLoop does not
|
||||
even make 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.
|
||||
|
||||
In any case, 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 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 simply calling
|
||||
:ref:`Node.get_tree() <class_Node_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:
|
||||
|
||||
::
|
||||
|
||||
get_tree().get_root() # access via scenemainloop
|
||||
get_node("/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,
|
||||
notifications, play sound, groups, etc. When they are removed from the
|
||||
*scene tree*, they lose access.
|
||||
|
||||
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 smaller 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 grand-child 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_change_scene>`
|
||||
function:
|
||||
|
||||
::
|
||||
|
||||
func _my_level_was_completed():
|
||||
get_tree().change_scene("res://levels/level2.scn")
|
||||
|
||||
This is a quick and useful way to switch scenes, but has the drawback
|
||||
that the game will stall until the new scene is loaded and running. At
|
||||
some point in your game, it may be desired to create proper loading
|
||||
screens with progress bar, animated indicators or thread (background)
|
||||
loading. This must be done manually using autoloads (see next chapter!)
|
||||
and :ref:`doc_background_loading`.
|
||||
212
learning/getting_started/scenes_and_nodes.rst
Normal file
212
learning/getting_started/scenes_and_nodes.rst
Normal file
@@ -0,0 +1,212 @@
|
||||
.. _doc_scenes_and_nodes:
|
||||
|
||||
Scenes and nodes
|
||||
================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
.. image:: /img/chef.png
|
||||
|
||||
Imagine for a second that you are not a game developer anymore. Instead,
|
||||
you're a chef! Change your hipster outfit for a toque and a double
|
||||
breasted jacket. Now, instead of making games, you create new and
|
||||
delicious recipes for your guests.
|
||||
|
||||
So, how does a chef create a recipe? Recipes are divided into two
|
||||
sections: the first is the ingredients and the second is the
|
||||
instructions to prepare it. This way, anyone can follow the recipe and
|
||||
savor your magnificent creation.
|
||||
|
||||
Making games in Godot feels pretty much the same way. Using the engine
|
||||
feels like being in a kitchen. In this kitchen, *nodes* are like a
|
||||
refrigerator full of fresh ingredients to cook with.
|
||||
|
||||
There are many types of nodes, some show images, others play sound,
|
||||
other nodes display 3D models, etc. There's dozens of them.
|
||||
|
||||
Nodes
|
||||
-----
|
||||
|
||||
But let's start with the basics. A node is a basic element for creating a
|
||||
game, it has the following characteristics:
|
||||
|
||||
- Has a name.
|
||||
- Has editable properties.
|
||||
- Can receive a callback to process every frame.
|
||||
- Can be extended (to have more functions).
|
||||
- Can be added to other nodes as children.
|
||||
|
||||
.. image:: /img/tree.png
|
||||
|
||||
The last one is very important. Nodes can have other nodes as
|
||||
children. When arranged in this way, the nodes become a **tree**.
|
||||
|
||||
In Godot, the ability to arrange nodes in this way creates a powerful
|
||||
tool for organizing projects. Since different nodes have different
|
||||
functions, combining them allows for creation of more complex functions.
|
||||
|
||||
This is probably not clear yet and makes little sense, but everything
|
||||
will click a few sections ahead. The most important fact to remember for
|
||||
now is that nodes exist and can be arranged this way.
|
||||
|
||||
Scenes
|
||||
------
|
||||
|
||||
.. image:: /img/scene_tree_example.png
|
||||
|
||||
Now that the concept of nodes has been defined, the next logical
|
||||
step is to explain what a Scene is.
|
||||
|
||||
A scene is composed of a group of nodes organized hierarchically (in
|
||||
tree fashion). It has the following properties:
|
||||
|
||||
- A scene always has only one root node.
|
||||
- Scenes can be saved to disk and loaded back.
|
||||
- Scenes can be *instanced* (more on that later).
|
||||
- Running a game means running a scene.
|
||||
- There can be several scenes in a project, but for it to start, one of
|
||||
them must be selected to be loaded first.
|
||||
|
||||
Basically, the Godot editor is a **scene editor**. It has plenty of
|
||||
tools for editing 2D and 3D scenes as well as user interfaces, but the
|
||||
editor is based on the concept of editing a scene and the nodes
|
||||
that compose it.
|
||||
|
||||
Creating a new project
|
||||
----------------------
|
||||
|
||||
Theory is boring, so let's change the subject and go practical. Following a
|
||||
long tradition in tutorials, the first project will be a Hello World.
|
||||
For this, the editor will be used.
|
||||
|
||||
When the godot executable is run outside a project, the Project Manager
|
||||
appears. This helps developers manage their projects.
|
||||
|
||||
.. image:: /img/project_manager.png
|
||||
|
||||
To create a new project, the "New Project" option must be used. Choose
|
||||
and create a path for the project and specify the project name:
|
||||
|
||||
.. image:: /img/create_new_project.png
|
||||
|
||||
Editor
|
||||
------
|
||||
|
||||
Once the "New Project" is created, the next step is opening it. This
|
||||
will open the Godot editor. Here is how the editor looks when freshly
|
||||
opened:
|
||||
|
||||
.. image:: /img/empty_editor.png
|
||||
|
||||
As mentioned before, making games in Godot feels like being in a
|
||||
kitchen, so let's open the refrigerator and add some fresh nodes to the
|
||||
project. We'll begin with a Hello World! To do this, the "New Node"
|
||||
button must be pressed (looks like a plus symbol):
|
||||
|
||||
.. image:: /img/newnode_button.png
|
||||
|
||||
This will open the Create Node dialog, showing the long list of nodes
|
||||
that can be created:
|
||||
|
||||
.. image:: /img/node_classes.png
|
||||
|
||||
From there, select the "Label" node first. Searching for it is probably
|
||||
the quickest way:
|
||||
|
||||
.. image:: /img/node_search_label.png
|
||||
|
||||
And finally, create the Label! A lot happens when Create is pressed:
|
||||
|
||||
.. image:: /img/editor_with_label.png
|
||||
|
||||
First of all, the scene is changed to the 2D editor (because Label is
|
||||
a 2D Node type), and the Label appears, selected, at the top left
|
||||
corner of the viewport.
|
||||
|
||||
The node appears in the scene tree editor (box in the top left
|
||||
corner), and the label properties appear in the Inspector (box on the
|
||||
right side).
|
||||
|
||||
The next step will be to change the "Text" Property of the label, let's
|
||||
change it to "Hello, World!":
|
||||
|
||||
.. image:: /img/hw.png
|
||||
|
||||
Ok, everything's ready to run the scene! Press the PLAY SCENE Button on
|
||||
the top bar (or hit F6):
|
||||
|
||||
.. image:: /img/playscene.png
|
||||
|
||||
Aaaand... Oops.
|
||||
|
||||
.. image:: /img/neversaved.png
|
||||
|
||||
Scenes need to be saved to be run, so save the scene to something like
|
||||
hello.scn in Scene -> Save:
|
||||
|
||||
.. image:: /img/save_scene.png
|
||||
|
||||
And here's when something funny happens. The file dialog is a special
|
||||
file dialog, and only allows you to save inside the project. The project
|
||||
root is "res://" which means "resource path". This means that files can
|
||||
only be saved inside the project. For the future, when doing file
|
||||
operations in Godot, remember that "res://" is the resource path, and no
|
||||
matter the platform or install location, it is the way to locate where
|
||||
resource files are from inside the game.
|
||||
|
||||
After saving the scene and pressing run scene again, the "Hello, World!"
|
||||
demo should finally execute:
|
||||
|
||||
.. image:: /img/helloworld.png
|
||||
|
||||
Success!
|
||||
|
||||
.. _doc_scenes_and_nodes-configuring_the_project:
|
||||
|
||||
Configuring the project
|
||||
-----------------------
|
||||
|
||||
Ok, It's time to do some configuration to the project. Right now, the
|
||||
only way to run something is to execute the current scene. Projects,
|
||||
however, have several scenes so one of them must be set as the main
|
||||
scene. This scene is the one that will be loaded at the time the project
|
||||
is run.
|
||||
|
||||
These settings are all stored in the engine.cfg file, which is a
|
||||
plaintext file in win.ini format, for easy editing. There are dozens of
|
||||
settings that can be changed in this file to alter how a project executes,
|
||||
so to make matters simpler, a project setting dialog exists, which is
|
||||
sort of a frontend to editing engine.cfg
|
||||
|
||||
To access that dialog, simply go to Scene -> Project Settings.
|
||||
|
||||
Once the window opens, the task will be to select a main scene. This can
|
||||
be done easily by changing the application/main_scene property and
|
||||
selecting 'hello.scn'.
|
||||
|
||||
.. image:: /img/main_scene.png
|
||||
|
||||
With this change, pressing the regular Play button (or F5) will run the
|
||||
project, no matter which scene is being edited.
|
||||
|
||||
Going back to the project settings dialog. This dialog provides a lot
|
||||
of options that can be added to engine.cfg, and shows their default
|
||||
values. If the default value is ok, then there isn't any need to
|
||||
change it.
|
||||
|
||||
When a value is changed, a tick is marked to the left of the name.
|
||||
This means that the property will be saved to the engine.cfg file and
|
||||
remembered.
|
||||
|
||||
As a side note, for future reference and a little out of context (this
|
||||
is the first tutorial after all!), it is also possible to add custom
|
||||
configuration options and read them in run-time using the
|
||||
:ref:`Globals <class_Globals>` singleton.
|
||||
|
||||
To be continued...
|
||||
------------------
|
||||
|
||||
This tutorial talks about "scenes and nodes", but so far there has been
|
||||
only *one* scene and *one* node! Don't worry, the next tutorial will
|
||||
deal with that...
|
||||
224
learning/getting_started/scripting.rst
Normal file
224
learning/getting_started/scripting.rst
Normal file
@@ -0,0 +1,224 @@
|
||||
.. _doc_scripting:
|
||||
|
||||
Scripting
|
||||
=========
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Much has been said about tools that allow users to create video games
|
||||
without programming. It's been a dream for many independent developers
|
||||
to create games without learning how to code. This need has been around
|
||||
for a long time, even inside companies, where game designers wish to
|
||||
have more control of the game flow.
|
||||
|
||||
Many products have been shipped promising a no-programming environment,
|
||||
but the result is often incomplete, too complex or inefficient compared
|
||||
to traditional code. As a result, programming is here to stay for a long
|
||||
time. In fact, the general direction in game engines has been to add
|
||||
tools that try to reduce the amount of code that needs to be written for
|
||||
specific tasks, to speed up development.
|
||||
|
||||
In that sense, Godot has taken some useful design decisions towards that
|
||||
goal. The first and most important is the scene system. The aim of it is
|
||||
not obvious at first, but works well later on. That is, to relieve
|
||||
programmers from the responsibility of architecting code.
|
||||
|
||||
When designing games using the scene system, the whole project is
|
||||
fragmented into *complementary* scenes (not individual ones). Scenes
|
||||
complement each other, instead of being separate. There will be plenty
|
||||
of examples about this later on, but it's very important to remember it.
|
||||
|
||||
For those with a good amount of programming expertise, this means a
|
||||
different design pattern to MVC. Godot promises efficiency at the
|
||||
expense of dropping the MVC habits, which are replaced by the *scenes as
|
||||
a complement* pattern.
|
||||
|
||||
Godot also uses the `extend <http://c2.com/cgi/wiki?EmbedVsExtend>`__
|
||||
pattern for scripting, meaning that scripts extend from all the
|
||||
available engine classes.
|
||||
|
||||
GDScript
|
||||
--------
|
||||
|
||||
:ref:`doc_gdscript` is a dynamically typed scripting language to fit
|
||||
inside Godot. It was designed with the following goals:
|
||||
|
||||
- First and most importantly, making it simple, familiar and as easy to
|
||||
learn as possible.
|
||||
- Making the code readable and error safe. The syntax is mostly
|
||||
borrowed from Python.
|
||||
|
||||
Programmers generally take a few days to learn it, and within two weeks
|
||||
feel comfortable with it.
|
||||
|
||||
As with most dynamically typed languages, the higher productivity
|
||||
(code is easier to learn, faster to write, no compilation, etc) is
|
||||
balanced with a performance penalty. But most critical code is written
|
||||
in C++ already in the engine (vector ops, physics, math, indexing, etc),
|
||||
resulting in a more than sufficient performance for most types of
|
||||
games.
|
||||
|
||||
In any case, if more performance is required, critical sections can be
|
||||
rewritten in C++ and exposed transparently to the script. This allows
|
||||
the replacement of a GDScript class with a C++ class without altering
|
||||
the rest of the game.
|
||||
|
||||
Scripting a scene
|
||||
-----------------
|
||||
|
||||
Before continuing, please make sure to read the :ref:`doc_gdscript` reference.
|
||||
It's a simple language and the reference is short, it will not take
|
||||
more than a few minutes to get an overview of the concepts.
|
||||
|
||||
Scene setup
|
||||
~~~~~~~~~~~
|
||||
|
||||
This tutorial will begin by scripting a simple GUI scene. Use the add
|
||||
node dialog to create the following hierarchy, with the following nodes:
|
||||
|
||||
- Panel
|
||||
|
||||
* Label
|
||||
* Button
|
||||
|
||||
It should look like this in the scene tree:
|
||||
|
||||
.. image:: /img/scripting_scene_tree.png
|
||||
|
||||
Use the 2D editor to position and resize the button and label so that they
|
||||
look like the image below. You can set the text in the Inspector pane.
|
||||
|
||||
.. image:: /img/label_button_example.png
|
||||
|
||||
Finally, save the scene, a fitting name could be "sayhello.scn"
|
||||
|
||||
.. _doc_scripting-adding_a_script:
|
||||
|
||||
Adding a script
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Right click on the panel node, then select "Add Script" in the context
|
||||
menu:
|
||||
|
||||
.. image:: /img/add_script.png
|
||||
|
||||
The script creation dialog will pop up. This dialog allows to select
|
||||
the language, class name, etc. GDScript does not use class names in
|
||||
script files, so that field is not editable. The script should inherit
|
||||
from "Panel" (as it is meant to extend the node, which is of Panel type,
|
||||
this is automatically filled).
|
||||
|
||||
Enter a path name for the script and then select "Create":
|
||||
|
||||
.. image:: /img/script_create.png
|
||||
|
||||
Once this is done, the script will be created and added to the node. You
|
||||
can see this both as an extra icon in the node, as well as in the script
|
||||
property:
|
||||
|
||||
.. image:: /img/script_added.png
|
||||
|
||||
To edit the script, select either of the highlighted buttons.
|
||||
This will bring you to the script editor where an existing template will
|
||||
be included by default:
|
||||
|
||||
.. image:: /img/script_template.png
|
||||
|
||||
There is not much in there. The "_ready()" function is called when the
|
||||
node (and all its children) entered the active scene. (Remember, it's
|
||||
not a constructor, the constructor is "_init()" ).
|
||||
|
||||
The role of the script
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A script adds behavior to a node. It is used to control the
|
||||
node functions as well as other nodes (children, parent, siblings, etc).
|
||||
The local scope of the script is the node (just like in regular
|
||||
inheritance) and the virtual functions of the node are captured by the
|
||||
script.
|
||||
|
||||
.. image:: /img/brainslug.jpg
|
||||
|
||||
Handling a signal
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Signals are used mostly in GUI nodes (although other nodes have them
|
||||
too). Signals are "emitted" when some specific kind of action happens,
|
||||
and can be connected to any function of any script instance. In this
|
||||
step, the "pressed" signal from the button will be connected to a custom
|
||||
function.
|
||||
|
||||
An interface for connecting signals to your scripts exists in the editor.
|
||||
You can access this by selecting the node in the scene tree and then
|
||||
selecting the "Node" tab. Make sure that you have "Signals" selected.
|
||||
|
||||
.. image:: /img/signals.png
|
||||
|
||||
In any case, at this point it is clear that we are interested in
|
||||
the "pressed" signal. Instead of using the visual interface, we will opt
|
||||
to code the connection.
|
||||
|
||||
For this, a function exists that is probably the one most used by Godot
|
||||
programmers, namely :ref:`Node.get_node() <class_Node_get_node>`.
|
||||
This function uses paths to fetch nodes in the current tree or anywhere
|
||||
in the scene, relative to the node holding the script.
|
||||
|
||||
To fetch the button, the following must be used:
|
||||
|
||||
::
|
||||
|
||||
get_node("Button")
|
||||
|
||||
Next, a callback will be added that will change the label's text when
|
||||
the button is pressed:
|
||||
|
||||
::
|
||||
|
||||
func _on_button_pressed():
|
||||
get_node("Label").set_text("HELLO!")
|
||||
|
||||
Finally, the button "pressed" signal will be connected to that callback
|
||||
in _ready(), by using :ref:`Object.connect() <class_Object_connect>`.
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
get_node("Button").connect("pressed",self,"_on_button_pressed")
|
||||
|
||||
The final script should look like this:
|
||||
|
||||
::
|
||||
|
||||
extends Panel
|
||||
|
||||
# member variables here, example:
|
||||
|
||||
# var a=2
|
||||
# var b="textvar"
|
||||
|
||||
func _on_button_pressed():
|
||||
get_node("Label").set_text("HELLO!")
|
||||
|
||||
func _ready():
|
||||
get_node("Button").connect("pressed",self,"_on_button_pressed")
|
||||
|
||||
Running the scene should have the expected result when pressing the
|
||||
button:
|
||||
|
||||
.. image:: /img/scripting_hello.png
|
||||
|
||||
**Note:** As it is a common misunderstanding in this tutorial, let's clarify
|
||||
again that get_node(path) works by returning the *immediate* children of
|
||||
the node controlled by the script (in this case, *Panel*), so *Button*
|
||||
must be a child of *Panel* for the above code to work. To give this
|
||||
clarification more context, if *Button* were a child of *Label*, the code
|
||||
to obtain it would be:
|
||||
|
||||
::
|
||||
|
||||
# not for this case
|
||||
# but just in case
|
||||
get_node("Label/Button")
|
||||
|
||||
Also, remember that nodes are referenced by name, not by type.
|
||||
235
learning/getting_started/scripting_continued.rst
Normal file
235
learning/getting_started/scripting_continued.rst
Normal file
@@ -0,0 +1,235 @@
|
||||
.. _doc_scripting_continued:
|
||||
|
||||
Scripting (continued)
|
||||
=====================
|
||||
|
||||
Processing
|
||||
----------
|
||||
|
||||
Several actions in Godot are triggered by callbacks or virtual
|
||||
functions, so there is no need to check for writing code that runs all
|
||||
the time. Additionally, a lot can be done with animation players.
|
||||
|
||||
However, it is still a very common case to have a script process on every
|
||||
frame. There are two types of processing, idle processing and fixed
|
||||
processing.
|
||||
|
||||
Idle processing is activated with the
|
||||
:ref:`Node.set_process() <class_Node_set_process>`
|
||||
function. Once active, the :ref:`Node._process() <class_Node__process>`
|
||||
callback will be called every frame. Example:
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
set_process(true)
|
||||
|
||||
func _process(delta):
|
||||
# do something...
|
||||
|
||||
The delta parameter describes the time elapsed (in seconds, as
|
||||
floating point) since the previous call to _process().
|
||||
Fixed processing is similar, but only needed for synchronization with
|
||||
the physics engine.
|
||||
|
||||
A simple way to test this is to create a scene with a single Label node,
|
||||
with the following script:
|
||||
|
||||
::
|
||||
|
||||
extends Label
|
||||
|
||||
var accum=0
|
||||
|
||||
func _ready():
|
||||
set_process(true)
|
||||
|
||||
func _process(delta):
|
||||
accum += delta
|
||||
set_text(str(accum))
|
||||
|
||||
Which will show a counter increasing each frame.
|
||||
|
||||
Groups
|
||||
------
|
||||
|
||||
Nodes can be added to groups (as many as desired per node). This is a
|
||||
simple yet useful feature for organizing large scenes. There are two
|
||||
ways to do this, the first is from the UI, from the Groups button under Node-panel:
|
||||
|
||||
.. image:: /img/groups_in_nodes.PNG
|
||||
|
||||
And the second from code. One useful example would be to tag scenes
|
||||
which are enemies.
|
||||
|
||||
::
|
||||
|
||||
func _ready():
|
||||
add_to_group("enemies")
|
||||
|
||||
This way, if the player is discovered sneaking into the secret base,
|
||||
all enemies can be notified about the alarm sounding, by using
|
||||
:ref:`SceneTree.call_group() <class_SceneTree_call_group>`:
|
||||
|
||||
::
|
||||
|
||||
func _on_discovered():
|
||||
get_tree().call_group(0, "guards", "player_was_discovered")
|
||||
|
||||
The above code calls the function "player_was_discovered" on every
|
||||
member of the group "guards".
|
||||
|
||||
Optionally, it is possible to get the full list of "guards" nodes by
|
||||
calling
|
||||
:ref:`SceneTree.get_nodes_in_group() <class_SceneTree_get_nodes_in_group>`:
|
||||
|
||||
::
|
||||
|
||||
var guards = get_tree().get_nodes_in_group("guards")
|
||||
|
||||
More will be added about
|
||||
:ref:`SceneTree <class_SceneTree>`
|
||||
later.
|
||||
|
||||
Notifications
|
||||
-------------
|
||||
|
||||
Godot has a system of notifications. This is usually not needed for
|
||||
scripting, as it's too low level and virtual functions are provided for
|
||||
most of them. It's just good to know they exists. Simply
|
||||
add a
|
||||
:ref:`Object._notification() <class_Object__notification>`
|
||||
function in your script:
|
||||
|
||||
::
|
||||
|
||||
func _notification(what):
|
||||
if (what == NOTIFICATION_READY):
|
||||
print("This is the same as overriding _ready()...")
|
||||
elif (what == NOTIFICATION_PROCESS):
|
||||
var delta = get_process_time()
|
||||
print("This is the same as overriding _process()...")
|
||||
|
||||
The documentation of each class in the :ref:`Class Reference <toc-class-ref>`
|
||||
shows the notifications it can receive. However, for most cases GDScript
|
||||
provides simpler overrideable functions.
|
||||
|
||||
Overrideable functions
|
||||
----------------------
|
||||
|
||||
Nodes provide many useful overrideable functions, which are described as
|
||||
follows:
|
||||
|
||||
::
|
||||
|
||||
func _enter_tree():
|
||||
# When the node enters the _Scene Tree_, it becomes active
|
||||
# and this function is called. Children nodes have not entered
|
||||
# the active scene yet. In general, it's better to use _ready()
|
||||
# for most cases.
|
||||
pass
|
||||
|
||||
func _ready():
|
||||
# This function is called after _enter_tree, but it ensures
|
||||
# that all children nodes have also entered the _Scene Tree_,
|
||||
# and became active.
|
||||
pass
|
||||
|
||||
func _exit_tree():
|
||||
# When the node exits the _Scene Tree_, this function is called.
|
||||
# Children nodes have all exited the _Scene Tree_ at this point
|
||||
# and all became inactive.
|
||||
pass
|
||||
|
||||
func _process(delta):
|
||||
# When set_process() is enabled, this function is called every frame.
|
||||
pass
|
||||
|
||||
func _fixed_process(delta):
|
||||
# When set_fixed_process() is enabled, this is called every physics
|
||||
# frame.
|
||||
pass
|
||||
|
||||
func _paused():
|
||||
# Called when game is paused. After this call, the node will not receive
|
||||
# any more process callbacks.
|
||||
pass
|
||||
|
||||
func _unpaused():
|
||||
# Called when game is unpaused.
|
||||
pass
|
||||
|
||||
As mentioned before, it's best to use these functions.
|
||||
|
||||
Creating nodes
|
||||
--------------
|
||||
|
||||
To create a node from code, just call the .new() method (like for any
|
||||
other class based datatype). Example:
|
||||
|
||||
::
|
||||
|
||||
var s
|
||||
func _ready():
|
||||
s = Sprite.new() # create a new sprite!
|
||||
add_child(s) # add it as a child of this node
|
||||
|
||||
To delete a node, be it inside or outside the scene, free() must be
|
||||
used:
|
||||
|
||||
::
|
||||
|
||||
func _someaction():
|
||||
s.free() # immediately removes the node from the scene and frees it
|
||||
|
||||
When a node is freed, it also frees all its children nodes. Because of
|
||||
this, manually deleting nodes is much simpler than it appears. Just free
|
||||
the base node and everything else in the sub-tree goes away with it.
|
||||
|
||||
However, it might happen very often that we want to delete a node that
|
||||
is currently "blocked", because it is emitting a signal or calling a
|
||||
function. This will result in crashing the game. Running Godot
|
||||
in the debugger often will catch this case and warn you about it.
|
||||
|
||||
The safest way to delete a node is by using
|
||||
:ref:`Node.queue_free() <class_Node_queue_free>`.
|
||||
This erases the node safely during idle.
|
||||
|
||||
::
|
||||
|
||||
func _someaction():
|
||||
s.queue_free() # remove the node and delete it while nothing is happening
|
||||
|
||||
Instancing scenes
|
||||
-----------------
|
||||
|
||||
Instancing a scene from code is pretty easy and done in two steps. The
|
||||
first one is to load the scene from disk.
|
||||
|
||||
::
|
||||
|
||||
var scene = load("res://myscene.scn") # will load when the script is instanced
|
||||
|
||||
Preloading it can be more convenient sometimes, as it happens at parse
|
||||
time.
|
||||
|
||||
::
|
||||
|
||||
var scene = preload("res://myscene.scn") # will load when parsing the script
|
||||
|
||||
But 'scene' is not yet a node containing subnodes. It's packed in a
|
||||
special resource called :ref:`PackedScene <class_PackedScene>`.
|
||||
To create the actual node, the function
|
||||
:ref:`PackedScene.instance() <class_PackedScene_instance>`
|
||||
must be called. This will return the tree of nodes that can be added to
|
||||
the active scene:
|
||||
|
||||
::
|
||||
|
||||
var node = scene.instance()
|
||||
add_child(node)
|
||||
|
||||
The advantage of this two-step process is that a packed scene may be
|
||||
kept loaded and ready to use, so it can be used to create as many
|
||||
instances as desired. This is especially useful to quickly instance
|
||||
several enemies, bullets, etc. in the active scene.
|
||||
271
learning/getting_started/simple_2d_game.rst
Normal file
271
learning/getting_started/simple_2d_game.rst
Normal file
@@ -0,0 +1,271 @@
|
||||
.. _doc_simple_2d_game:
|
||||
|
||||
Simple 2D game
|
||||
==============
|
||||
|
||||
Pong
|
||||
~~~~
|
||||
|
||||
In this tutorial, a basic game of Pong will be created. There are plenty
|
||||
of more complex examples in the demos included with the engine, but this
|
||||
should get you introduced to the basic functionalities for 2D Games.
|
||||
|
||||
To begin with, run the Godot Engine and start a new project.
|
||||
|
||||
Assets
|
||||
~~~~~~
|
||||
|
||||
Some assets are included for this tutorial:
|
||||
:download:`pong_assets.zip </files/pong_assets.zip>`. Unzip its content
|
||||
in your project folder.
|
||||
|
||||
Scene setup
|
||||
~~~~~~~~~~~
|
||||
|
||||
For the sake of the old times, the game will be in 640x400 pixels
|
||||
resolution. This can be configured in the Project Settings (see
|
||||
:ref:`doc_scenes_and_nodes-configuring_the_project`) under Scene/Project
|
||||
settings menu. The default background color should be set to black:
|
||||
|
||||
.. image:: /img/clearcolor.png
|
||||
|
||||
Create a :ref:`class_Node2D` node for the project root. Node2D is the
|
||||
base type for the 2D engine. After this, add some sprites
|
||||
(:ref:`class_Sprite` node) for the left and right paddles, the separator
|
||||
and ball. You can set a custom name for each node, and set the texture
|
||||
for each sprite in the Inspector.
|
||||
|
||||
.. image:: /img/pong_nodes.png
|
||||
|
||||
Set nodes positions:
|
||||
- "left" node: (67, 183)
|
||||
- "right" node: (577, 187)
|
||||
- "separator" node: (320, 200)
|
||||
- "ball" node: (320, 188)
|
||||
|
||||
|
||||
The final scene layout should look similar to this (note: the ball is in
|
||||
the middle!):
|
||||
|
||||
.. image:: /img/pong_layout.png
|
||||
|
||||
|
||||
Save the scene as "pong.tscn" and set it as the main scene in the
|
||||
project
|
||||
properties.
|
||||
|
||||
.. _doc_simple_2d_game-input_actions_setup:
|
||||
|
||||
Input actions setup
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Video games can be played using various input methods: Keyboard, Joypad,
|
||||
Mouse, Touchscreen (multitouch)... Godot is able to use them all.
|
||||
However, it would be interesting to define the inputs as "Input Actions"
|
||||
instead of hardware actions that you'd manage separately. This way, any
|
||||
input method can be used: each of them only require the user to connect
|
||||
buttons to game actions that you defined.
|
||||
|
||||
This is Pong. The only input that matters is for the pads going up and
|
||||
down.
|
||||
|
||||
Open the project properties dialog again (Scene/Project settings), but
|
||||
this time move to the
|
||||
"Input Map" tab.
|
||||
|
||||
In this tab, add 4 actions:
|
||||
``left_move_up``, ``left_move_down``, ``right_move_up``,
|
||||
``right_move_down``.
|
||||
Assign the keys that you desire. A/Z (for the left player) and Up/Down
|
||||
(for the right player) as keys
|
||||
should work in most cases.
|
||||
|
||||
.. image:: /img/inputmap.png
|
||||
|
||||
Script
|
||||
~~~~~~
|
||||
|
||||
Create a script for the root node of the scene and open it (as explained
|
||||
in :ref:`doc_scripting-adding_a_script`). This script inherits Node2D:
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
First things first, we need to define some members for our script so it
|
||||
can store useful values. Such values are the dimensions of the screen, the pad
|
||||
and the initial direction of the ball.
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
# Member variables
|
||||
var screen_size
|
||||
var pad_size
|
||||
var direction = Vector2(1.0, 0.0)
|
||||
|
||||
func _ready():
|
||||
pass
|
||||
|
||||
|
||||
As you know, the ``_ready()`` function is the first function called
|
||||
(after ``_enter_tree()`` which we don't need here). In this function,
|
||||
two things have to be done. The first one is to enable
|
||||
processing: this is the purpose of the ``set_process(true)`` function.
|
||||
The second one is to initalize our two member variables.
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
# Member variables
|
||||
var screen_size
|
||||
var pad_size
|
||||
var direction = Vector2(1.0, 0.0)
|
||||
|
||||
func _ready():
|
||||
screen_size = get_viewport_rect().size
|
||||
pad_size = get_node("left").get_texture().get_size()
|
||||
set_process(true)
|
||||
|
||||
We initialize the ``pad_size`` variable by getting one of the pads nodes
|
||||
(the left one here), and obtain its texture size. The ``screen_size`` is
|
||||
initialized using the ``get_viewport_rect()`` which returns a Rect
|
||||
object corresponding to the game window, and we store its size.
|
||||
|
||||
|
||||
Now, we need to add some other members to our script in order to make
|
||||
our ball move.
|
||||
|
||||
::
|
||||
|
||||
extends Node2D
|
||||
|
||||
# Member variables
|
||||
var screen_size
|
||||
var pad_size
|
||||
var direction = Vector2(1.0, 0.0)
|
||||
|
||||
# Constant for pad speed (in pixels/second)
|
||||
const INITIAL_BALL_SPEED = 80
|
||||
# Speed of the ball (also in pixels/second)
|
||||
var ball_speed = INITIAL_BALL_SPEED
|
||||
# Constant for pads speed
|
||||
const PAD_SPEED = 150
|
||||
|
||||
func _ready():
|
||||
screen_size = get_viewport_rect().size
|
||||
pad_size = get_node("left").get_texture().get_size()
|
||||
set_process(true)
|
||||
|
||||
|
||||
|
||||
Finally, the ``_process()`` function. All the code below is contained by
|
||||
this function.
|
||||
|
||||
We have to init some useful values for computation. The first one is the
|
||||
ball position (from the node), the second one is the rectangle
|
||||
(``Rect2``) for each pad. These rectangles will be used for collision
|
||||
tests between the ball and the pads. Sprites center their textures by
|
||||
default, so a small adjustment of ``pad_size / 2`` must be added.
|
||||
|
||||
::
|
||||
|
||||
func _process(delta):
|
||||
var ball_pos = get_node("ball").get_pos()
|
||||
var left_rect = Rect2( get_node("left").get_pos() - pad_size*0.5, pad_size )
|
||||
var right_rect = Rect2( get_node("right").get_pos() - pad_size*0.5, pad_size )
|
||||
|
||||
Now, let's add some movement to the ball in the ``_process()`` function.
|
||||
Since the ball position is stored in the ``ball_pos`` variable,
|
||||
integrating it is simple:
|
||||
|
||||
::
|
||||
|
||||
# Integrate new ball postion
|
||||
ball_pos += direction * ball_speed * delta
|
||||
|
||||
This code line is called at each iteration of the ``_process()``
|
||||
function. That means the ball position will be updated at each new
|
||||
frame.
|
||||
|
||||
Now that the ball has a new position, we need to test if it
|
||||
collides with anything, that is the window borders and the pads. First,
|
||||
the floor and the roof:
|
||||
|
||||
::
|
||||
|
||||
# Flip when touching roof or floor
|
||||
if ((ball_pos.y < 0 and direction.y < 0) or (ball_pos.y > screen_size.y and direction.y > 0)):
|
||||
direction.y = -direction.y
|
||||
|
||||
Second, the pads: if one of the pads is touched, we need to invert the
|
||||
direction of the ball on the X axis so it goes back, and define a new
|
||||
random Y direction using the ``randf()`` function. We also increase its
|
||||
speed a little.
|
||||
|
||||
::
|
||||
|
||||
# Flip, change direction and increase speed when touching pads
|
||||
if ((left_rect.has_point(ball_pos) and direction.x < 0) or (right_rect.has_point(ball_pos) and direction.x > 0)):
|
||||
direction.x = -direction.x
|
||||
direction.y = randf()*2.0 - 1
|
||||
direction = direction.normalized()
|
||||
ball_speed *= 1.1
|
||||
|
||||
Finally, if the ball went out of the screen, it's game over. That is, we test if
|
||||
the X position of the ball is less than 0 or greater than the screen
|
||||
width. If so, the game restarts:
|
||||
|
||||
::
|
||||
|
||||
# Check gameover
|
||||
if (ball_pos.x < 0 or ball_pos.x > screen_size.x):
|
||||
ball_pos = screen_size*0.5
|
||||
ball_speed = INITIAL_BALL_SPEED
|
||||
direction = Vector2(-1, 0)
|
||||
|
||||
Once everything is done, the node is updated with the new position of
|
||||
the ball, which was computed before:
|
||||
|
||||
::
|
||||
|
||||
get_node("ball").set_pos(ball_pos)
|
||||
|
||||
Next, we allow the pads to move. We only update their position according
|
||||
to player input. This is done using the Input class:
|
||||
|
||||
::
|
||||
|
||||
# Move left pad
|
||||
var left_pos = get_node("left").get_pos()
|
||||
|
||||
if (left_pos.y > 0 and Input.is_action_pressed("left_move_up")):
|
||||
left_pos.y += -PAD_SPEED * delta
|
||||
if (left_pos.y < screen_size.y and Input.is_action_pressed("left_move_down")):
|
||||
left_pos.y += PAD_SPEED * delta
|
||||
|
||||
get_node("left").set_pos(left_pos)
|
||||
|
||||
# Move right pad
|
||||
var right_pos = get_node("right").get_pos()
|
||||
|
||||
if (right_pos.y > 0 and Input.is_action_pressed("right_move_up")):
|
||||
right_pos.y += -PAD_SPEED * delta
|
||||
if (right_pos.y < screen_size.y and Input.is_action_pressed("right_move_down")):
|
||||
right_pos.y += PAD_SPEED * delta
|
||||
|
||||
get_node("right").set_pos(right_pos)
|
||||
|
||||
We use the four actions previously defined in the Input actions setup
|
||||
section above. When the player activates the respective key, the
|
||||
corresponding action is triggered. As soon as this happens, we simply
|
||||
compute a new position for the pad in the desired direction and apply it
|
||||
to the node.
|
||||
|
||||
That's it! A simple Pong was written with a few lines of code.
|
||||
178
learning/getting_started/singletons_autoload.rst
Normal file
178
learning/getting_started/singletons_autoload.rst
Normal file
@@ -0,0 +1,178 @@
|
||||
.. _doc_singletons_autoload:
|
||||
|
||||
Singletons (AutoLoad)
|
||||
=====================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Scene singletons are very useful, catering to a common use case where you need
|
||||
to store persistent information between scenes.
|
||||
|
||||
Albeit very powerful, the scene system by itself has a few drawbacks:
|
||||
|
||||
- There is no common place to store information (e.g. a player's items etc.)
|
||||
required by more than one scene.
|
||||
- While it is possible for a scene that loads and unloads other scenes as
|
||||
its children to store information common to these child scenes, it is no
|
||||
longer possible to run these scenes by themselves and expect them to work
|
||||
correctly.
|
||||
- While information can be stored to disk in \`user://\` and this information
|
||||
can be loaded by scenes that require it, continuously saving and loading this
|
||||
data when changing scenes is cumbersome and may be slow.
|
||||
|
||||
However there is still a need in Godot to create parts of a scene that:
|
||||
|
||||
- Are always loaded, no matter which scene is opened from the editor
|
||||
- Can store global variables, such as player information, items, money
|
||||
etc. and share information between scenes
|
||||
- Can handle switching scenes and transitions
|
||||
- Acts like a singleton, since GDScript does not support global variables by design.
|
||||
|
||||
Auto-loading nodes and scripts caters to this need.
|
||||
|
||||
AutoLoad
|
||||
--------
|
||||
|
||||
You can use AutoLoad to load a scene, or a script that inherits from Node (a node
|
||||
will be created and the script will be set to it).
|
||||
|
||||
To autoload a scene or script, select Scene > Project Settings from the menu and switch
|
||||
to the AutoLoad tab. Each entry in the list requires a name, which is used as the name
|
||||
of the node, and the node is always added to the root viewport before any other scenes
|
||||
are loaded.
|
||||
|
||||
.. image:: /img/singleton.png
|
||||
|
||||
This means that any node can access a singleton named "playervariables" with:
|
||||
|
||||
::
|
||||
|
||||
var player_vars = get_node("/root/playervariables")
|
||||
|
||||
Custom scene switcher
|
||||
---------------------
|
||||
|
||||
This short tutorial will explain how to make a scene switcher using
|
||||
autoload. For simple scene switching, the
|
||||
:ref:`SceneTree.change_scene() <class_SceneTree_change_scene>`
|
||||
method suffices (described in :ref:`doc_scene_tree`), so this method is for
|
||||
more complex behavior when switching between scenes.
|
||||
|
||||
First download the template from here:
|
||||
:download:`autoload.zip </files/autoload.zip>`, then open it.
|
||||
|
||||
Two scenes are present, scene_a.scn and scene_b.scn on an otherwise
|
||||
empty project. Each are identical and contain a button connected to a
|
||||
callback for switching to the other scene. When the project runs, it
|
||||
starts in scene_a.scn. However, this currently does nothing and pressing the
|
||||
button does not work.
|
||||
|
||||
global.gd
|
||||
---------
|
||||
|
||||
First of all, create a global.gd script. The easy way to create a
|
||||
resource from scratch is from the new resource button in the inspector tab:
|
||||
|
||||
.. image:: /img/newscript.png
|
||||
|
||||
Save the script as `global.gd`:
|
||||
|
||||
.. image:: /img/saveasscript.png
|
||||
|
||||
The script should open in the script editor. The next step is to add
|
||||
it to AutoLoad list. Select Scene > Project Settings from the menu,
|
||||
switch to the AutoLoad tab and add a new entry with name "global" that
|
||||
points to this file:
|
||||
|
||||
.. image:: /img/addglobal.png
|
||||
|
||||
Now, whenever you run any of your scenes, the script is always loaded.
|
||||
|
||||
Returning to our script, the current scene needs to be fetched in the
|
||||
`_ready()` function. Both the current scene and `global.gd` are children of
|
||||
root, but the autoloaded nodes are always first. This means that the
|
||||
last child of root is always the loaded scene.
|
||||
|
||||
Note: Make sure that global.gd extends Node, otherwise it won't be
|
||||
loaded!
|
||||
|
||||
::
|
||||
|
||||
extends Node
|
||||
|
||||
var current_scene = null
|
||||
|
||||
func _ready():
|
||||
var root = get_tree().get_root()
|
||||
current_scene = root.get_child( root.get_child_count() -1 )
|
||||
|
||||
Next up is the function for changing the scene. This function frees the
|
||||
current scene and replaces it with the requested one.
|
||||
|
||||
::
|
||||
|
||||
func goto_scene(path):
|
||||
|
||||
# This function will usually be called from a signal callback,
|
||||
# or some other function from the running scene.
|
||||
# Deleting the current scene at this point might be
|
||||
# a bad idea, because it may be inside of a callback or function of it.
|
||||
# The worst case will be a crash or unexpected behavior.
|
||||
|
||||
# The way around this is deferring the load to a later time, when
|
||||
# it is ensured that no code from the current scene is running:
|
||||
|
||||
call_deferred("_deferred_goto_scene",path)
|
||||
|
||||
|
||||
func _deferred_goto_scene(path):
|
||||
|
||||
# Immediately free the current scene,
|
||||
# there is no risk here.
|
||||
current_scene.free()
|
||||
|
||||
# Load new scene
|
||||
var s = ResourceLoader.load(path)
|
||||
|
||||
# Instance the new scene
|
||||
current_scene = s.instance()
|
||||
|
||||
# Add it to the active scene, as child of root
|
||||
get_tree().get_root().add_child(current_scene)
|
||||
|
||||
# optional, to make it compatible with the SceneTree.change_scene() API
|
||||
get_tree().set_current_scene( current_scene )
|
||||
|
||||
As mentioned in the comments above, we really want to avoid the
|
||||
situation of having the current scene being deleted while being used
|
||||
(code from functions of it being run), so using
|
||||
:ref:`Object.call_deferred() <class_Object_call_deferred>`
|
||||
is desired at this point. The result is that execution of the commands
|
||||
in the second function will happen at a later time when no code from
|
||||
the current scene is running.
|
||||
|
||||
Finally, all that is left is to fill the empty functions in scene_a.gd
|
||||
and scene_b.gd:
|
||||
|
||||
::
|
||||
|
||||
#add to scene_a.gd
|
||||
|
||||
func _on_goto_scene_pressed():
|
||||
get_node("/root/global").goto_scene("res://scene_b.scn")
|
||||
|
||||
and
|
||||
|
||||
::
|
||||
|
||||
#add to scene_b.gd
|
||||
|
||||
func _on_goto_scene_pressed():
|
||||
get_node("/root/global").goto_scene("res://scene_a.scn")
|
||||
|
||||
Now if you run the project, you can switch between both scenes by pressing
|
||||
the button!
|
||||
|
||||
To load scenes with a progress bar, check out the next tutorial,
|
||||
:ref:`doc_background_loading`
|
||||
43
learning/getting_started/splash_screen.rst
Normal file
43
learning/getting_started/splash_screen.rst
Normal file
@@ -0,0 +1,43 @@
|
||||
.. _doc_splash_screen:
|
||||
|
||||
Splash screen
|
||||
=============
|
||||
|
||||
Tutorial
|
||||
--------
|
||||
|
||||
This is a simple tutorial to establish the basic idea of how the GUI
|
||||
subsystem works. The goal is to create a really simple, static
|
||||
splash screen.
|
||||
|
||||
Following is a file with the assets that will be used. These can be added directly to your project folder - no need to import them:
|
||||
|
||||
:download:`robisplash_assets.zip </files/robisplash_assets.zip>`.
|
||||
|
||||
Setting up
|
||||
----------
|
||||
|
||||
Set the display resolution to 800x450 in Project Settings, and set up a new scene like this:
|
||||
|
||||
.. image:: /img/robisplashscene.png
|
||||
|
||||
.. image:: /img/robisplashpreview.png
|
||||
|
||||
The nodes "background" and "logo" are of :ref:`TextureFrame <class_TextureFrame>`
|
||||
type. These have a special property for setting the texture to be
|
||||
displayed, just load the corresponding file.
|
||||
|
||||
.. image:: /img/texframe.png
|
||||
|
||||
The node "start" is a :ref:`TextureButton <class_TextureButton>`.
|
||||
It takes several images for different states, but only the normal and
|
||||
pressed will be supplied in this example:
|
||||
|
||||
.. image:: /img/texbutton.png
|
||||
|
||||
Finally, the node "copyright" is a :ref:`Label <class_Label>`.
|
||||
A custom font can be set for labels by editing the following property:
|
||||
|
||||
.. image:: /img/label.png
|
||||
|
||||
As a side note, the font was imported from a TTF, see :ref:`doc_importing_fonts`.
|
||||
478
learning/scripting/gdscript/gdscript_advanced.rst
Normal file
478
learning/scripting/gdscript/gdscript_advanced.rst
Normal file
@@ -0,0 +1,478 @@
|
||||
.. _doc_gdscript_more_efficiently:
|
||||
|
||||
GDScript more efficiently
|
||||
=========================
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
This tutorial aims to be a quick reference for how to use GDScript more
|
||||
efficiently. It focuses in common cases specific to the language, but
|
||||
also covers a lot related to using dynamically typed languages.
|
||||
|
||||
It's meant to be specially useful for programmers without previous or
|
||||
little experience of dynamically typed languages.
|
||||
|
||||
Dynamic nature
|
||||
--------------
|
||||
|
||||
Pros & cons of dynamic typing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
GDScript is a Dynamically Typed language. As such, it's main advantages
|
||||
are that:
|
||||
|
||||
- Language is very simple 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 cons 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 games very 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 to still create the
|
||||
entire game in GDScript and add small bits of C++ in the areas that need
|
||||
a 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:
|
||||
|
||||
::
|
||||
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
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 later 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:: 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:: 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 the vector 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`.
|
||||
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
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 always a very powerful 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 on 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 very 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[Vector(1,1)] = SHIP
|
||||
board[Vector(1,2)] = SHIP
|
||||
board[Vector(1,3)] = SHIP
|
||||
|
||||
func missile(pos):
|
||||
|
||||
if pos in board: # something at that pos
|
||||
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 dictionaries resemble python dictionaries, it also supports Lua
|
||||
style syntax an indexing, which makes it very useful for writing initial
|
||||
states and quick structs:
|
||||
|
||||
::
|
||||
|
||||
# same example, lua-style support
|
||||
# this syntax is a lot more readable and usable
|
||||
|
||||
var d = {
|
||||
name = "john",
|
||||
age = 22
|
||||
}
|
||||
|
||||
print("Name: ", d.name, " Age: ", d.age) # used "." based indexing
|
||||
|
||||
# indexing
|
||||
|
||||
d.mother = "rebecca" # this doesn't work (use syntax below to add a key:value pair)
|
||||
d["mother"] = "rebecca" # this works
|
||||
d.name = "caroline" # if key exists, assignment does work, this is why it's like a quick struct.
|
||||
|
||||
For & while
|
||||
-----------
|
||||
|
||||
Iterating in some statically typed languages can be quite complex:
|
||||
|
||||
::
|
||||
|
||||
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 examples:
|
||||
|
||||
::
|
||||
|
||||
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):
|
||||
|
||||
for i in range(5, 10):
|
||||
|
||||
for i in range(5, 10, 2):
|
||||
|
||||
And backwards looping is done through a negative counter:
|
||||
|
||||
::
|
||||
|
||||
for(int i=10; i>0; i--) {}
|
||||
|
||||
becomes
|
||||
|
||||
::
|
||||
|
||||
for i in range(10, 0, -1):
|
||||
|
||||
While
|
||||
-----
|
||||
|
||||
while() loops are the same everywhere:
|
||||
|
||||
::
|
||||
|
||||
var i = 0
|
||||
|
||||
while(i < strings.size()):
|
||||
print(strings[i])
|
||||
i += 1
|
||||
|
||||
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:
|
||||
|
||||
::
|
||||
|
||||
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. Anyway though, there exists
|
||||
the possibility of the object being hit not having a smash() function.
|
||||
Some dynamically typed languages simply ignore a method call when it
|
||||
doesn't exist (like Objective C), but GDScript is more strict, 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.
|
||||
1214
learning/scripting/gdscript/gdscript_basics.rst
Normal file
1214
learning/scripting/gdscript/gdscript_basics.rst
Normal file
File diff suppressed because it is too large
Load Diff
194
learning/scripting/gdscript/gdscript_format_string.rst
Normal file
194
learning/scripting/gdscript/gdscript_format_string.rst
Normal file
@@ -0,0 +1,194 @@
|
||||
.. _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 are other `format specifiers`_.
|
||||
|
||||
|
||||
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 placeholder. 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%"
|
||||
67
learning/workflow/assets/exporting_images.rst
Normal file
67
learning/workflow/assets/exporting_images.rst
Normal file
@@ -0,0 +1,67 @@
|
||||
.. _doc_exporting_images:
|
||||
|
||||
Exporting images
|
||||
================
|
||||
|
||||
It is often desired to do an operation to all or a group of images upon
|
||||
export. Godot provides some tools for this. Examples of such operations
|
||||
are:
|
||||
|
||||
- Converting all images from a lossless format to a lossy one (ie: png
|
||||
-> WebP) for greater compression.
|
||||
- Shrinking all images to half the size, to create a low resolution
|
||||
build for smaller screens.
|
||||
- Create an atlas for a group of images and crop them, for higher
|
||||
performance and less memory usage.
|
||||
|
||||
Image export options
|
||||
--------------------
|
||||
|
||||
In the "Project Export Settings" dialog, go to the Images tab:
|
||||
|
||||
.. image:: /img/exportimages.png
|
||||
|
||||
In this dialog the image extensions for conversion can be selected, and
|
||||
operations can be performed that apply to all images (except those in
|
||||
groups, see the next section for those):
|
||||
|
||||
- **Convert Image Format**: Probably the most useful operation is to
|
||||
convert to Lossy (WebP) to save disk space. For lossy, a Quality bar
|
||||
can set the quality/vs size ratio.
|
||||
- **Shrink**: This allows to shrink all images by a given amount. It's
|
||||
useful to export a game to half or less resolution for special
|
||||
devices.
|
||||
- **Compress Formats**: Allows to select which image exensions to
|
||||
convert.
|
||||
|
||||
On export, Godot will perform the desired operation. The first export
|
||||
might be really slow, but subsequent exports will be fast, as the
|
||||
converted images will be cached.
|
||||
|
||||
Image group export options
|
||||
--------------------------
|
||||
|
||||
This section is similar to the previous one, except it can operate on a
|
||||
selected group of images. When a image is in a group, the settings from
|
||||
the global export options are overridden by the ones from the group. An
|
||||
image can only be in one group at the same time. So if the image is in
|
||||
another group different to the current one being edited, it will not be
|
||||
selectable.
|
||||
|
||||
.. image:: /img/imagegroup.png
|
||||
|
||||
Atlas
|
||||
~~~~~
|
||||
|
||||
Grouping images allows a texture atlas to be created. When this mode is
|
||||
active, a button to preview the resulting atlas becomes available. Make
|
||||
sure that atlases don't become too big, as some hardware will not
|
||||
support textures bigger than 2048x2048 pixels. If this happens, just
|
||||
create another atlas.
|
||||
|
||||
The atlas can be useful to speed up drawing of some scenes, as state
|
||||
changes are minimized when drawing from it (through unlike other
|
||||
engines, Godot is designed so state changes do not affect it as much).
|
||||
Textures added to an atlas get cropped (empty spaces around the image
|
||||
are removed), so this is another reason to use them (save space). If
|
||||
unsure, though, just leave that option disabled.
|
||||
167
learning/workflow/assets/import_process.rst
Normal file
167
learning/workflow/assets/import_process.rst
Normal file
@@ -0,0 +1,167 @@
|
||||
.. _doc_import_process:
|
||||
|
||||
Import process
|
||||
==============
|
||||
|
||||
What is it for?
|
||||
---------------
|
||||
|
||||
When Godot was created, it was probably after several failed and not so
|
||||
failed engine attempts (well, each attempt failed a little less.. and so
|
||||
on). One of the most difficult areas of creating game engines is
|
||||
managing the import process. That means, getting the assets that artists
|
||||
make into the game, in a way that functions optimally.
|
||||
|
||||
Artists use certain tools and formats, and programmers would rather have
|
||||
their data into a different format. This is because artists put their
|
||||
focus on creating assets with the best quality possible, while
|
||||
programmers have to make sure they actually run at decent speed (or run
|
||||
at all), use a certain amount of memory, and don't take ages loading
|
||||
from disk.
|
||||
|
||||
One would think that just writing a converter/importer would be enough,
|
||||
but this is not all there is to it. The same way programmers iterate
|
||||
several times over their code, artists keep making changes to their
|
||||
assets. This generates some bottleneck, because *someone* has to keep
|
||||
re-importing that artwork right? And importing assets is often something
|
||||
that has to be agreed by both parties, as the programmer needs to decide
|
||||
how the artwork is imported and the artists needs to see how it looks.
|
||||
|
||||
The goal to establishing an import process is that both can agree on how
|
||||
the rules under which the assets are going to be imported the first
|
||||
time, and the system will apply those rules automatically each time the
|
||||
asset is re-imported.
|
||||
|
||||
Godot does not do the re-import process automatically, though. It gives
|
||||
the team the option to do it at any time ( a red icon on the top right
|
||||
of the screen, allows the ability to do it at any desired time).
|
||||
|
||||
Does it always work?
|
||||
--------------------
|
||||
|
||||
The aim of the import system is that it works well enough for most
|
||||
common cases and projects. What is there has been tested and seems to
|
||||
cover most needs.
|
||||
|
||||
However, as mentioned before, this is one of the most difficult areas of
|
||||
writing a game engine. It may happen often (specially on large projects,
|
||||
ports, or projects with unusual requirement) that what is provided is
|
||||
not enough. It's easy to say that the engine is open source and that the
|
||||
programmer should make their own if they don't like what is there, but
|
||||
that would be making a huge disservice to the users and not the right
|
||||
attitude. Because of that, we made sure to provide as many tools and
|
||||
helpers as possible to support a custom import process, for example:
|
||||
|
||||
- Access to the internals of almost all data structures is provided to
|
||||
the scripting and C++ API, as well as saving and loading in all
|
||||
supported file formats.
|
||||
- Some importers (like the 3D asset importer) support scripts to modify
|
||||
the data being imported.
|
||||
- Support for creating custom import plugins is also provided, even for
|
||||
replacing the existing ones.
|
||||
- If all else fails, Godot supports adding custom resource loaders,
|
||||
to load data in alternative formats, without intermediate conversion.
|
||||
|
||||
Both the import system and the custom tools provided will improve over
|
||||
time as more use cases are revealed to us.
|
||||
|
||||
Importing assets
|
||||
----------------
|
||||
|
||||
Source asset location
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To begin, it is a good idea to define where the original assets created
|
||||
by the artists (before they are imported) will be located. Normally,
|
||||
Godot does not mind much about the location, but if the project has
|
||||
several developers, it is a good idea to understand the simple rule for
|
||||
it to work for everyone.
|
||||
|
||||
First of all, it would be really good for this location to **not** be
|
||||
inside the project path (where engine.cfg is located, or any
|
||||
sub-folder). Godot expects regular resources in there, and may consider
|
||||
many of the files used as source art as regular resources. This would
|
||||
lead to it bundling all of them when the project is exported, something
|
||||
which is undesired.
|
||||
|
||||
Now that it is clear that this location must be outside the project
|
||||
folder, the rule that Godot uses to reference external assets can be
|
||||
explained. When an asset is imported, the engine stores a relative path
|
||||
from the project path to the asset (In windows, this works as long as
|
||||
they are on the same drive, otherwise an absolute path is stored). This
|
||||
ensures that the same asset can be re-imported in another computer.
|
||||
|
||||
The usual approach to this, when using a VCS such as Subversion,
|
||||
Perforce or GIT, is to create the project in a subfolder, so both it and
|
||||
the source assets can be committed to a same repository. For example:
|
||||
|
||||
Repository layout:
|
||||
|
||||
::
|
||||
|
||||
source_assets/sfx/explosion.wav
|
||||
source_assets/sfx/crash.wav
|
||||
source_assets/fonts/myfont.ttf
|
||||
source_assets/translation/strings.csv
|
||||
source_assets/art/niceart.psd
|
||||
game/engine.cfg
|
||||
|
||||
In the above example, artists, musician, translators, etc. can work in
|
||||
the source_assets/ folder, then import the assets to the game/ folder.
|
||||
When the repository is updated, anyone can re-import the assets if they
|
||||
changed.
|
||||
|
||||
Import dialogs
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Godot provides for importing several types of assets, all of them can be
|
||||
accessed from the import dialog:
|
||||
|
||||
.. image:: /img/import.png
|
||||
|
||||
Each of the dialog shares a similar function, a source file (or several
|
||||
of them) must be provided, as well as a target destination inside the
|
||||
project folders. Once imported, Godot saves this information as metadata
|
||||
in the imported asset itself.
|
||||
|
||||
.. image:: /img/importdialogs.png
|
||||
|
||||
More information about each specific type of asset can be found in
|
||||
specific sections, such as `Importing Textures <import_textures>`__.
|
||||
|
||||
Tracking changes and re-importing
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Godot tracks changes in the source assets constantly. If at least one
|
||||
asset has been found to be modified (md5 is different than when it was
|
||||
imported), a small red indicator will appear in the top right corner of
|
||||
the screen.
|
||||
|
||||
.. image:: /img/changes.png
|
||||
|
||||
From that moment onward, the user can choose to re-import at any given
|
||||
time by clicking on the red-icon. When this action is done, a dialog
|
||||
will pop-up showing which resources can be re-imported (all selected
|
||||
by default).
|
||||
|
||||
Accepting that dialog will immediately re-import the resources and
|
||||
will update any of them currently in use in the editor (like a
|
||||
texture, model or audio file).
|
||||
|
||||
.. image:: /img/changed.png
|
||||
|
||||
Manually re-importing
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The re-import process is automatic, but it may be desired at some point
|
||||
to change the settings of an already imported file, so it can be
|
||||
re-imported differently. For this, the Import Settings window is
|
||||
provided.
|
||||
|
||||
.. image:: /img/isettings.png
|
||||
|
||||
This screen allows the user to re-open the corresponding import-window
|
||||
to re-import that asset again, with the ability to change any of the
|
||||
settings.
|
||||
|
||||
.. image:: /img/reimported.png
|
||||
113
learning/workflow/assets/importing_audio_samples.rst
Normal file
113
learning/workflow/assets/importing_audio_samples.rst
Normal file
@@ -0,0 +1,113 @@
|
||||
.. _doc_importing_audio_samples:
|
||||
|
||||
Importing audio samples
|
||||
=======================
|
||||
|
||||
Why importing?
|
||||
--------------
|
||||
|
||||
Importing Audio Samples into the game engine is a process that should be
|
||||
easier than it really is. Most readers are probably thinking "Why not
|
||||
just copy the wav files to a folder inside the project and be over
|
||||
with it?"
|
||||
|
||||
It's not usually that simple. Most game engines use uncompressed audio
|
||||
(in memory, at least) for sound effects. The reason for this is because
|
||||
it's really cheap to play back and resample. Compressed streamed audio
|
||||
(such as ogg files) takes a large amount of processor to decode so no
|
||||
more than one or two are streamed simultaneously. However, with sound
|
||||
effects, one expects a dozen of them to be playing at the same time in
|
||||
several situations.
|
||||
|
||||
Because of this, sound effects are loaded uncompressed into memory, and
|
||||
here is where the problems begin.
|
||||
|
||||
As is usual with graphics, the situation where programmers don't really
|
||||
know about audio and audio engineers don't know about programming is
|
||||
also common in the industry. This leads to a scenario where a project
|
||||
ends up wasting resources unnecessarily.
|
||||
|
||||
To be more precise, SFX artists tend to work with audio formats that
|
||||
give them a lot of room for tweaking the audio with a low noise floor and
|
||||
minimum aliasing, such as 96kHz, 24 bits. In many cases, they work in
|
||||
stereo too. Added to that, many times they add effects with an infinite
|
||||
or really long fadeout, such as reverb, which leads to apparent trailing
|
||||
silences. Finally, many DAWs also add silence at the beginning when
|
||||
normalizing to wav.
|
||||
|
||||
These often result in extremely large files to integration into a game engine
|
||||
with sound effects taking dozens of megabytes.
|
||||
|
||||
How much does quality matter?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
First of all, it is important to know that Godot has an internal reverb
|
||||
generator. Sound effects can go to four different setups (small, medium
|
||||
and large room, as well as hall), with different send amounts. This saves
|
||||
SFX artists the need to add reverb to the sound effects, reducing their
|
||||
size greatly and ensuring correct trimming. Say no to SFX with baked
|
||||
reverb!
|
||||
|
||||
.. image:: /img/reverb.png
|
||||
|
||||
Another common problem is that, while it's useful for working inside a
|
||||
DAW, high bit depths (24 bits) and high sampling rate (96kHz) are
|
||||
completely unnecessary for use in a game, as there is no `audible
|
||||
difference <http://www.youtube.com/watch?v=cIQ9IXSUzuM>`__. If
|
||||
positional sound is going to be used (for 2D and 3D), the panning and
|
||||
stereo reverb will be provided by the engine, so there is little need
|
||||
for stereo sound. How does this affect the resource usage? Look at the
|
||||
following comparison:
|
||||
|
||||
+---------------------------+---------------------+--------------+
|
||||
| Format | 1 Second of Audio | Frame Size |
|
||||
+===========================+=====================+==============+
|
||||
| 24 bits, 96 kHz, Stereo | 576kb | 12 |
|
||||
+---------------------------+---------------------+--------------+
|
||||
| 16 bits, 44 kHz, Mono | 88kb | 2 |
|
||||
+---------------------------+---------------------+--------------+
|
||||
| 16 bits, IMA-ADPCM | 22kb | 1/2 |
|
||||
+---------------------------+---------------------+--------------+
|
||||
|
||||
As seen, for being no audible difference, the 16 bits, 44kHz, mono conversion
|
||||
takes *6 times less memory* than the 24 bits, 96kHz, Stereo version. The
|
||||
IMA-ADPCM version (using computationally-light audio compression) takes *24
|
||||
times less memory* than what was exported from the DAW.
|
||||
|
||||
Trimming
|
||||
~~~~~~~~
|
||||
|
||||
One last issue that happens often is that the waveform files received
|
||||
have silences at the beginning and at the end. These are inserted by
|
||||
DAWs when saving to a waveform, increase their size unnecessarily and
|
||||
add latency to the moment they are played back. Trimming them solves
|
||||
this, but it takes effort for the SFX artist, as they have to do it in a
|
||||
separate application. In the worst case, they may not even know the
|
||||
silences are being added.
|
||||
|
||||
.. image:: /img/trim.png
|
||||
|
||||
Importing audio samples
|
||||
-----------------------
|
||||
|
||||
Godot has a simple screen for importing audio samples to the engine. SFX
|
||||
artists only have to save the wav files to a folder outside the
|
||||
project, and the import dialog will fix the files for inclusion, as well
|
||||
as doing it automatically every time they are modified and re-imported.
|
||||
|
||||
.. image:: /img/importaudio.png
|
||||
|
||||
In this screen, the quality of the audio can be limited to what is
|
||||
needed, and trimming is done automatically. In addition, several samples
|
||||
can be loaded and batch-converted, just as textures can.
|
||||
|
||||
Looping
|
||||
~~~~~~~
|
||||
|
||||
Godot supports looping in the samples (Tools such as Sound Forge or
|
||||
Audition can add loop points to wav files). This is useful for sound
|
||||
effects such as engines, machine guns, etc. Ping-pong looping is also
|
||||
supported.
|
||||
|
||||
As an alternative, the import screen has a "loop" option that enables
|
||||
looping for the entire sample when importing.
|
||||
115
learning/workflow/assets/importing_fonts.rst
Normal file
115
learning/workflow/assets/importing_fonts.rst
Normal file
@@ -0,0 +1,115 @@
|
||||
.. _doc_importing_fonts:
|
||||
|
||||
Importing fonts
|
||||
===============
|
||||
|
||||
What is a font?
|
||||
---------------
|
||||
|
||||
Fonts in modern operating systems are created as scalable vector
|
||||
graphics. They are stored as a collection of curves (usually one for
|
||||
each character), which are independent of the screen resolution, and
|
||||
stored in standardized file formats, such as TTF (TrueType) or OTF
|
||||
(OpenType).
|
||||
|
||||
Rendering such fonts to bitmaps is a complex process, which employs
|
||||
different methods to convert curves to pixels depending on context and
|
||||
target size. Due to this, this rendering process must be done by using
|
||||
the CPU. Game engines use the GPU to render, and 3D APIs don't really
|
||||
support the means to do this efficiently, so fonts have to be converted
|
||||
to a format that is friendly to the GPU when imported to a project.
|
||||
|
||||
Converting fonts
|
||||
----------------
|
||||
|
||||
This conversion process consists of rendering a vector font to a given
|
||||
point size and storing all the resulting characters in a bitmap texture.
|
||||
The bitmap texture is then used by the GPU to draw a small quad for each
|
||||
character and form readable strings.
|
||||
|
||||
.. image:: /img/bitmapfont.png
|
||||
|
||||
The drawback of this process is that fonts must be pre-imported in the
|
||||
specific sizes that they will use in the project. However, given that
|
||||
that bitmap fonts compress really well, this is not as bad as it sounds.
|
||||
|
||||
Importing a font
|
||||
----------------
|
||||
|
||||
Fonts are imported via the Font import dialog. The dialog will ask for a
|
||||
font, a size, some options and a target resource file to save.
|
||||
|
||||
.. image:: /img/fontimport.png
|
||||
|
||||
The dialog is fully dynamic, which means that any change will be
|
||||
reflected in the font preview window. The user can tweak almost every
|
||||
parameter and get instant feedback on how the font will look.
|
||||
|
||||
Since the resulting font is a bitmap, a few more options were added to
|
||||
make the imported font look even nicer. These options were added to
|
||||
please graphic designers, who love putting gradients, outlines and
|
||||
shadows in fonts, as well as changing all the inter-spaces available :).
|
||||
These options will be explained in the next section.
|
||||
|
||||
Extra spacing
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
It is possible to add more space for:
|
||||
|
||||
- **Characters**, the space between them can be varied.
|
||||
- **"space" character**, so the distance between words is bigger.
|
||||
- **Top and Bottom margins**, this changes the spacing between lines as
|
||||
well as the space between the top and bottom lines and the borders.
|
||||
|
||||
.. image:: /img/fontspacing.png
|
||||
|
||||
Shadows & outline
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Fonts can have a shadow. For this, the font is drawn again, below the original,
|
||||
in a different color, and then blurred with a Gaussian kernel of different
|
||||
sizes. The resulting shadow can be adjusted with an exponential function
|
||||
to make it softer or more like an outline. A second shadow is also
|
||||
provided to create some added effects, like a bump or outline+shadow.
|
||||
|
||||
.. image:: /img/shadowoutline.png
|
||||
|
||||
Gradients
|
||||
~~~~~~~~~
|
||||
|
||||
Gradients are also another of the visual effects that graphic designers
|
||||
often use. To show how much we love them, we added those too. Gradients
|
||||
can be provided as a simple curve between two colors, or a special png
|
||||
file with a hand drawn gradient.
|
||||
|
||||
.. image:: /img/fontgradients.png
|
||||
|
||||
Internationalization
|
||||
--------------------
|
||||
|
||||
Colors, shadows and gradients are beautiful, but it's time we get to
|
||||
serious business. Developing games for Asian markets is a common
|
||||
practice in today's globalized world and app stores.
|
||||
|
||||
Here's when things get tricky with using bitmap fonts. Asian alphabets
|
||||
(Chinese, Japanese and Korean) contain dozens of thousands of
|
||||
characters. Generating bitmap fonts with every single of them is pretty
|
||||
expensive, as the resulting textures are huge. If the font size is small
|
||||
enough, it can be done without much trouble, but when the fonts become
|
||||
bigger, we run out of video ram pretty quickly!
|
||||
|
||||
To solve this, Godot allows the user to specify a text file (in UTF-8
|
||||
format) where it expects to find all the characters that will be used in
|
||||
the project. This seems difficult to provide at first, and more to keep
|
||||
up to date, but it becomes rather easy when one realizes that the .csv
|
||||
with the translations can be used as such source file (see the
|
||||
:ref:`doc_importing_translations` section). As Godot re-imports assets when
|
||||
their dependencies change, both the translation and font files will be
|
||||
updated and re-imported automatically if the translation csv changes.
|
||||
|
||||
Another cool trick for using a text file as limit of which characters
|
||||
can be imported is when using really large fonts. For example, the user
|
||||
might want to use a super large font, but only to show numbers. For
|
||||
this, he or she writes a numbers.txt file that contains "1234567890",
|
||||
and Godot will only limit itself to import data, thus saving a lot of
|
||||
video memory.
|
||||
256
learning/workflow/assets/importing_textures.rst
Normal file
256
learning/workflow/assets/importing_textures.rst
Normal file
@@ -0,0 +1,256 @@
|
||||
.. _doc_importing_textures:
|
||||
|
||||
Importing textures
|
||||
==================
|
||||
|
||||
Do NOT import them in most cases
|
||||
--------------------------------
|
||||
|
||||
In most cases you **don't** want images imported when dealing with 2D
|
||||
and GUI. Just copy them to the filesystem. Read the tutorial on
|
||||
:ref:`doc_managing_image_files` before continuing! For 3D,
|
||||
textures are always imported by the 3D scene importer, so importing
|
||||
those is only useful when importing a texture used for 3D that doesn't
|
||||
come with the 3D scene (for example, in a shader). The flags and options
|
||||
are the same as here, so reading the rest of the document might help
|
||||
too.
|
||||
|
||||
OK, you *might* want to import them
|
||||
-----------------------------------
|
||||
|
||||
So, if you have read the previous tutorial on the texture exporter, the
|
||||
texture importer gives you more fine-grained control on how textures
|
||||
are imported. If you want to change flags such as repeat, filter,
|
||||
mipmaps, fix edges, etc. ***PER texture***, importing them is the best
|
||||
way to accomplish this (since you can't save such flags in a standard
|
||||
image file).
|
||||
|
||||
Lack of MipMaps
|
||||
---------------
|
||||
|
||||
Images in 3D hardware are scaled with a (bi)linear filter, but this
|
||||
method has limitations. When images are shrunk too much, two problems
|
||||
arise:
|
||||
|
||||
- **Aliasing**: Pixels are skipped too much, and the image shows
|
||||
discontinuities. This decreases quality.
|
||||
- **Cache Misses**: Pixels being read are too far apart, so texture
|
||||
cache reads a lot more data than it should. This decreases
|
||||
performance.
|
||||
|
||||
.. image:: /img/imagemipmap.png
|
||||
|
||||
To solve this, mipmaps are created. Mipmaps are versions of the image
|
||||
shrunk by half in both axis, recursively, until the image is 1 pixel of
|
||||
size. When the 3D hardware needs to shrink the image, it finds the
|
||||
largest mipmap it can scale from, and scales from there. This improves
|
||||
performance and image quality.
|
||||
|
||||
.. image:: /img/mipmaps.png
|
||||
|
||||
Godot automatically creates mipmaps upon load for standard image files.
|
||||
This process is time consuming (although not much) and makes load times
|
||||
a little worse. Pre-importing the textures allows the automatic
|
||||
generation of mipmaps.
|
||||
|
||||
Unwanted MipMaps
|
||||
----------------
|
||||
|
||||
Remember the previous point about mipmaps? Yes, they are cool, but
|
||||
mobile GPUs only support them if the textures are in power of 2
|
||||
dimensions (i.e. 256x256 or 512x128). In these platforms, Godot will
|
||||
stretch and enlarge the texture to the closest power of 2 size and then
|
||||
generate the mipmaps. This process takes more of a performance hit and
|
||||
it might degrade the quality a little more.
|
||||
|
||||
Because of this, there are some scenarios when it may be desirable to
|
||||
not use them, and just use a linear filter. One of them is when working
|
||||
with graphical user interfaces (GUIs). Usually they are made of large
|
||||
images and don't stretch much. Even if the screen resolution is in a
|
||||
larger or smaller value than original art, the amount of stretch is not
|
||||
as much and the art can retain the quality. Pre-importing the textures
|
||||
also allows the disabling of mipmap generation.
|
||||
|
||||
Blending artifacts
|
||||
------------------
|
||||
|
||||
The `blending
|
||||
equation <http://en.wikipedia.org/wiki/Alpha_compositing>`__ used by
|
||||
applications like Photoshop is too complex for realtime. There are
|
||||
better approximations such as `pre-multiplied
|
||||
alpha <http://blogs.msdn.com/b/shawnhar/archive/2009/11/06/premultiplied-alpha.aspx?Redirected=true>`__,
|
||||
but they impose more stress in the asset pipeline. In the end, we are
|
||||
left with textures that have artifacts in the edges, because apps such
|
||||
as Photoshop store white pixels in completely transparent areas. Such
|
||||
white pixels end up showing thanks to the texture filter.
|
||||
|
||||
Godot has an option to fix the edges of the image (by painting invisible
|
||||
pixels the same color as the visible neighbours):
|
||||
|
||||
.. image:: /img/fixedborder.png
|
||||
|
||||
However, this must be done every time the image changes. Pre-Importing
|
||||
the textures makes sure that every time the original file changes, this
|
||||
artifact is fixed upon automatic re-import.
|
||||
|
||||
Texture flags
|
||||
-------------
|
||||
|
||||
Textures have flags. The user can choose for them to repeat or clamp to
|
||||
edges (when UVs exceed the 0,0,1,1 boundary). The magnifying filter can
|
||||
also be turned off (for a Minecraft-like effect). Such values can not be
|
||||
edited in standard file formats (png, jpg, etc.), but can be edited and
|
||||
saved in Godot .tex files. Then again, the user may not want to change
|
||||
the values every time the texture changes. Pre-Importing the textures
|
||||
also takes care of that.
|
||||
|
||||
Texture compression
|
||||
-------------------
|
||||
|
||||
Aside from the typical texture compression, which saves space on disk
|
||||
(.png, jpg, etc.), there are also texture compression formats that save
|
||||
space in memory (more specifically video memory. This allows to have
|
||||
much better looking textures in games without running out of memory, and
|
||||
decrease memory bandwidth when reading them so they are a big plus.
|
||||
|
||||
There are several video texture compression formats, none of which are
|
||||
standard. Apple uses PVRTC. PC GPUs, consoles and nVidia Android devices use
|
||||
S3TC (BC), other chipsets use other formats. OpenGL ES 3.0 standardized on ETC
|
||||
format, but we are still a few years away from that working everywhere.
|
||||
|
||||
Still, when using this option, Godot converts and compresses to the
|
||||
relevant format depending on the target platform (as long as the user
|
||||
pre-imported the texture and specified video ram compression!).
|
||||
|
||||
This kind of compression is often not desirable for many types of 2D games
|
||||
and UIs because it is lossy, creating visual artifacts. This is especially
|
||||
noticeable on games that use the trendy vectory social game artwork.
|
||||
However, the fact that it saves space and improves performance may make up for
|
||||
it.
|
||||
|
||||
The 3D scene importer always imports textures with this option turned
|
||||
on.
|
||||
|
||||
Atlases
|
||||
-------
|
||||
|
||||
Remember how mobile GPUs have this limitation of textures having to be
|
||||
in power of 2 sizes to be able to generate mimpmaps for optimum
|
||||
stretching? What if we have a lot of images in different random sizes?
|
||||
All will have to be scaled and mipmapped when loaded (using more CPU and
|
||||
memory) or when imported (taking more storage space). This is probably still
|
||||
OK, but there is a tool that can help improve this situation.
|
||||
|
||||
Atlases are big textures that fit a lot of small textures inside
|
||||
efficiently. Godot supports creating atlases in the importer, and the
|
||||
imported files are just small resources that reference a region of the
|
||||
bigger texture.
|
||||
|
||||
Atlases can be a nice solution to save some space on GUI or 2D artwork
|
||||
by packing everything together. The current importer is not as useful
|
||||
for 3D though (3D Atlases are created differently, and not all 3D
|
||||
models can use them).
|
||||
|
||||
As a small plus, atlases can decrease the amount of "state changes" when
|
||||
drawing. If a lot of objects that are drawn using several different
|
||||
textures are converted to an atlas, then the texture rebinds per object
|
||||
will go from dozens or hundreds to one. This will give the performance a
|
||||
small boost.
|
||||
|
||||
Artists use PSD
|
||||
---------------
|
||||
|
||||
Still wondering whether to use the texture importer or not? Remember
|
||||
that in the end, artists will often use Photoshop anyway, so it may be
|
||||
wiser to just let the import subsystem to take care of importing and
|
||||
converting the PSD files instead of asking the artist to save a png and
|
||||
copy it to the project every time.
|
||||
|
||||
Texture importer
|
||||
----------------
|
||||
|
||||
Finally! It's time to take a look at the texture importer. There are 3
|
||||
options in the import menu. They are pretty much (almost) the same
|
||||
dialog with a different set of defaults.
|
||||
|
||||
.. image:: /img/importtex.png
|
||||
|
||||
When selected, the texture import dialog will appear. This is the
|
||||
default one for 2D textures:
|
||||
|
||||
.. image:: /img/import_images.png
|
||||
|
||||
Each import option has a function, explained as follows:
|
||||
|
||||
Source texture(s)
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
One or more source images can be selected from the same folder (this
|
||||
importer can do batch-conversion). This can be from inside or outside
|
||||
the project.
|
||||
|
||||
Target path
|
||||
~~~~~~~~~~~
|
||||
|
||||
A destination folder must be provided. It must be inside the project, as
|
||||
textures will be converted and saved to it. Extensions will be changed
|
||||
to .tex (Godot resource file for textures), but names will be kept.
|
||||
|
||||
Texture format
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This combo allows to change the texture format (compression in this
|
||||
case):
|
||||
|
||||
.. image:: /img/compressopts.png
|
||||
|
||||
Each of the four options described in this table together with their
|
||||
advantages and disadvantages ( |good| = Best, |bad| =Worst ):
|
||||
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
| | Uncompressed | Compress Lossless (PNG) | Compress Lossy (WebP) | Compress VRAM |
|
||||
+================+========================+===========================+=========================+======================================================+
|
||||
| Description | Stored as raw pixels | Stored as PNG | Stored as WebP | Stored as S3TC/BC,PVRTC/ETC, depending on platform |
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
| Size on Disk | |bad| Large | |regular| Small | |good| Very Small | |regular| Small |
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
| Memory Usage | |bad| Large | |bad| Large | |bad| Large | |good| Small |
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
| Performance | |regular| Normal | |regular| Normal | |regular| Normal | |good| Fast |
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
| Quality Loss | |good| None | |good| None | |regular| Slight | |bad| Moderate |
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
| Load Time | |regular| Normal | |bad| Slow | |bad| Slow | |good| Fast |
|
||||
+----------------+------------------------+---------------------------+-------------------------+------------------------------------------------------+
|
||||
|
||||
Texture options
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Provided are a small amount of options for fine grained import control:
|
||||
|
||||
- **Streaming Format** - This does nothing as of yet, but a texture
|
||||
format for streaming different mipmap levels is planned. Big engines
|
||||
have support for this.
|
||||
- **Fix Border Alpha** - This will fix texture borders to avoid the
|
||||
white auras created by white invisible pixels (see the rant above).
|
||||
- **Alpha Bit Hint** - Godot auto-detects if the texture needs alpha
|
||||
bit support for transparency (instead of full range), which is useful
|
||||
for compressed formats such as BC. This forces alpha to be 0 or 1.
|
||||
- **Compress Extra** - Some VRAM compressions have alternate formats
|
||||
that compress more at the expense of quality (PVRTC2 for example). If
|
||||
this is ticked, texture will be smaller but look worse.
|
||||
- **No MipMaps** - Force imported texture to NOT use mipmaps. This may
|
||||
be desirable in some cases for 2D (as explained in the rant above),
|
||||
though it's NEVER desirable for 3D.
|
||||
- **Repeat** - Texture will repeat when UV coordinates go beyond 1 and
|
||||
below 0. This is often desirable in 3D, but may generate artifacts in
|
||||
2D.
|
||||
- **Filter** - Enables linear filtering when a texture texel is larger
|
||||
than a screen pixel. This is usually turned on, unless it's required
|
||||
for artistic purposes (Minecraft look, for example).
|
||||
|
||||
.. |bad| image:: /img/bad.png
|
||||
|
||||
.. |good| image:: /img/good.png
|
||||
|
||||
.. |regular| image:: /img/regular.png
|
||||
86
learning/workflow/assets/importing_translations.rst
Normal file
86
learning/workflow/assets/importing_translations.rst
Normal file
@@ -0,0 +1,86 @@
|
||||
.. _doc_importing_translations:
|
||||
|
||||
Importing translations
|
||||
======================
|
||||
|
||||
Games and internationalization
|
||||
------------------------------
|
||||
|
||||
The world is full of different markets and cultures and, to maximize
|
||||
profits™, nowadays games are released in several languages. To solve
|
||||
this, internationalized text must be supported in any modern game
|
||||
engine.
|
||||
|
||||
In regular desktop or mobile applications, internationalized text is
|
||||
usually located in resource files (or .po files for GNU stuff). Games,
|
||||
however, can use several orders of magnitude more text than
|
||||
applications, so they must support efficient methods for dealing with
|
||||
loads of multilingual text.
|
||||
|
||||
There are two approaches to generate multilingual language games and
|
||||
applications. Both are based on a key:value system. The first is to use
|
||||
one of the languages as the key (usually English), the second is to use a
|
||||
specific identifier. The first approach is probably easier for
|
||||
development if a game is released first in English, later in other
|
||||
languages, but a complete nightmare if working with many languages at
|
||||
the same time.
|
||||
|
||||
In general, games use the second approach and a unique ID is used for
|
||||
each string. This allows to revise the text while it's being translated
|
||||
to others. The unique ID can be a number, a string, or a string with a
|
||||
number (it's just a unique string anyway).
|
||||
|
||||
Translators also, most of the time prefer to work with spreadsheets
|
||||
(either as a Microsoft Excel file or a shared Google Spreadsheet).
|
||||
|
||||
Translation format
|
||||
------------------
|
||||
|
||||
To complete the picture and allow efficient support for translations,
|
||||
Godot has a special importer that can read csv files. Both Microsoft
|
||||
Excel and Google Spreadsheet can export to this format, so the only
|
||||
requirement is that the files have a special arrangement. The csv files must
|
||||
be saved in utf-8 encoding and be formatted as follows:
|
||||
|
||||
+--------+----------+----------+----------+
|
||||
| | <lang1> | <lang2> | <langN> |
|
||||
+========+==========+==========+==========+
|
||||
| KEY1 | string | string | string |
|
||||
+--------+----------+----------+----------+
|
||||
| KEY2 | string | string | string |
|
||||
+--------+----------+----------+----------+
|
||||
| KEYN | string | string | string |
|
||||
+--------+----------+----------+----------+
|
||||
|
||||
The "lang" tags must represent a language, which must be one of the :ref:`valid
|
||||
locales <doc_locales>` supported by the engine. The "KEY" tags must be
|
||||
unique and represent a string universally (they are usually in
|
||||
uppercase, to differentiate from other strings). Here's an example:
|
||||
|
||||
+---------+------------------+----------------+--------------+
|
||||
| id | en | es | ja |
|
||||
+=========+==================+================+==============+
|
||||
| GREET | Hello, friend! | Hola, Amigo! | こんにちは |
|
||||
+---------+------------------+----------------+--------------+
|
||||
| ASK | How are you? | Cómo está? | 元気ですか |
|
||||
+---------+------------------+----------------+--------------+
|
||||
| BYE | Good Bye | Adiós | さようなら |
|
||||
+---------+------------------+----------------+--------------+
|
||||
|
||||
Import dialog
|
||||
-------------
|
||||
|
||||
The import dialog takes a csv file in the previously described format
|
||||
and generates several compressed translation resource files inside the
|
||||
project.
|
||||
|
||||
Selecting a csv file autodetects the languages from the first row and
|
||||
determines which column represents which language. It is possible to
|
||||
change this manually, by selecting the language for each column.
|
||||
|
||||
.. image:: /img/trans.png
|
||||
|
||||
The import dialog also can add the translation to the list of
|
||||
translations to load when the game runs, specified in engine.cfg (or the
|
||||
project properties). Godot allows loading and removing translations at
|
||||
runtime as well.
|
||||
150
learning/workflow/assets/managing_image_files.rst
Normal file
150
learning/workflow/assets/managing_image_files.rst
Normal file
@@ -0,0 +1,150 @@
|
||||
.. _doc_managing_image_files:
|
||||
|
||||
Managing image files
|
||||
====================
|
||||
|
||||
If you have read the previous tutorials on :ref:`doc_resources` and
|
||||
:ref:`doc_filesystem`, at this point you know that regular image files
|
||||
(.png, .jpg, etc.) are treated as regular resources in Godot.
|
||||
|
||||
Unlike texture resources (.tex files), image files contain no extra
|
||||
information on tiling (texture repeat), mipmaps or filtering. Editing
|
||||
this information and saving the texture back will not have any effect,
|
||||
since such formats can't contain that information.
|
||||
|
||||
Image loader
|
||||
------------
|
||||
|
||||
Loading of images is done by the image loader. The behavior of the
|
||||
loader for all image files can be changed in the Project Settings dialog
|
||||
(Scene -> Project Settings). There is a section with values that
|
||||
are used for all image resources:
|
||||
|
||||
.. image:: /img/imgloader.png
|
||||
|
||||
Image loader options
|
||||
--------------------
|
||||
|
||||
Filter
|
||||
~~~~~~
|
||||
|
||||
Filter is used when the image is stretched more than its original size,
|
||||
so a texel in the image is bigger than a pixel on the screen. Turning
|
||||
off the filter produces a retro-like look:
|
||||
|
||||
.. image:: /img/imagefilter.png
|
||||
|
||||
Repeat
|
||||
~~~~~~
|
||||
|
||||
Repeat is mainly used for 3D textures, so it's off by default (textures
|
||||
are imported with the scenes and usually are not in the project as image
|
||||
files). When using UV coordinates (something not as common in 2D), and
|
||||
the UV value goes beyond the 0,0,1,1 rect, the texture repeats instead
|
||||
of clamping to the edge.
|
||||
|
||||
Mipmaps
|
||||
~~~~~~~
|
||||
|
||||
When the mipmaps option is enabled, Godot will generate mipmaps.
|
||||
Mipmaps are versions of the image shrunk by half in both axis,
|
||||
recursively, until the image is 1 pixel of size. When the 3D hardware
|
||||
needs to shrink the image, it finds the largest mipmap it can scale
|
||||
from, and scales from there. This improves performance and image
|
||||
quality.
|
||||
|
||||
.. image:: /img/mipmaps.png
|
||||
|
||||
When mipmaps are disabled, images start distorting badly when shrunk
|
||||
excessively:
|
||||
|
||||
.. image:: /img/imagemipmap.png
|
||||
|
||||
Alpha blending
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The `blending
|
||||
equation <http://en.wikipedia.org/wiki/Alpha_compositing>`__ used by
|
||||
applications like Photoshop is too complex for real-time. There are
|
||||
better approximations such as `pre-multiplied
|
||||
alpha <http://blogs.msdn.com/b/shawnhar/archive/2009/11/06/premultiplied-alpha.aspx?Redirected=true>`__,
|
||||
but they impose more stress in the asset pipeline. In the end, we are
|
||||
left with textures that have artifacts in the edges, because apps such
|
||||
as Photoshop store white pixels in completely transparent areas. Such
|
||||
white pixels end up showing thanks to the texture filter (when active).
|
||||
|
||||
Godot has an option to fix the edges of the image (by painting invisible
|
||||
pixels the same color as the visible neighbours):
|
||||
|
||||
.. image:: /img/fixedborder.png
|
||||
|
||||
To do this, open the image from the resources tab, or edit it from the
|
||||
property editor from another node or resource, then go to the object
|
||||
options and select "Fix Alpha Edges", then save it.
|
||||
|
||||
.. image:: /img/imagefixalpha.png
|
||||
|
||||
Since fixing this in so many images can be a little annoying, both
|
||||
Texture Import and Image Export can also perform this operation.
|
||||
|
||||
Texture import
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes, it might be desired to change the above settings per image.
|
||||
Unfortunately, the image loader settings are global. Texture flags also
|
||||
can't be saved in a regular .png or .jpg file.
|
||||
|
||||
For such cases, the image can be imported as a texture (.tex), where the
|
||||
individual flags can be changed. Godot also keeps track of the original
|
||||
file and will re-import if it changes.
|
||||
|
||||
Importing also allows conversion to other formats (WebP, or RAM
|
||||
compression) which might be of use in some cases. More information on
|
||||
the :ref:`doc_importing_textures` page.
|
||||
|
||||
Image export
|
||||
~~~~~~~~~~~~
|
||||
|
||||
It is also possible to convert images to other formats (WebP or RAM
|
||||
compression) on export, as well as instructing the exporter to create an
|
||||
Atlas for a set of images. It is also possible to ask the exporter to
|
||||
scale all images (or selected groups).
|
||||
|
||||
More information on the :ref:`doc_exporting_images` page.
|
||||
|
||||
Fixing PNGs iCCP chunk
|
||||
----------------------
|
||||
|
||||
With the upgrade of libpng to 1.6.23, libpng became more strict in terms of
|
||||
enforcing iCC profile correctness. This means that it now warns when it comes
|
||||
across an image with a non-conforming iCC chunk.
|
||||
|
||||
WARNING: _png_warn_function: iCCP: known incorrect sRGB profile
|
||||
|
||||
This can be fixed by either using a tool that exports PNGs with the correct
|
||||
iCC profile (in some tools this profile can even be manually changed on export)
|
||||
or using a tool that removes/fixes the iCC chunks.
|
||||
|
||||
Linux/Mac
|
||||
~~~~~
|
||||
Using ImageMagicks ``convert`` or ``mogrify`` fixes these warnings.
|
||||
To fix all PNGs in a project folder do:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ find . -type f -name "*.png" -exec convert {} {} \;
|
||||
|
||||
``pngcheck`` is also useful in locating the non-conforming images:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
find . -type f -name "*.png" -exec pngcheck {} \;
|
||||
|
||||
Windows
|
||||
~~~~~~~
|
||||
Using `optiPNG <http://optipng.sourceforge.net/>` fixes these warnings on Windows.
|
||||
To fix a PNG inplace do:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
optipng -clobber -strip all file.png
|
||||
63
learning/workflow/export/exporting_for_android.rst
Normal file
63
learning/workflow/export/exporting_for_android.rst
Normal file
@@ -0,0 +1,63 @@
|
||||
.. _doc_exporting_for_android:
|
||||
|
||||
Exporting for Android
|
||||
=====================
|
||||
|
||||
Exporting for Android has fewer requirements than compiling Godot for it. The
|
||||
following steps detail what is needed to setup the SDK and the engine.
|
||||
|
||||
Download the Android SDK
|
||||
------------------------
|
||||
|
||||
Download and install the Android SDK from
|
||||
http://developer.android.com/sdk/index.html
|
||||
|
||||
Install OpenJDK or Oracle JDK
|
||||
-----------------------------
|
||||
|
||||
Download and install OpenJDK or Oracle JDK. Version 6 and 8 are known to
|
||||
work, some users have reported issues with the jarsigner (used to sign the
|
||||
APKs) in JDK 7.
|
||||
|
||||
Create a debug.keystore
|
||||
-----------------------
|
||||
|
||||
Android needs a debug keystore file to install to devices and distribute
|
||||
non-release APKs. If you have used the SDK before and have built
|
||||
projects, ant or eclipse probably generated one for you (In Linux and
|
||||
OSX, you can find it in the ~/.android folder).
|
||||
|
||||
If you can't find it or need to generate one, the keytool command from
|
||||
the JDK can be used for this purpose:
|
||||
|
||||
::
|
||||
|
||||
keytool -keyalg RSA -genkeypair -alias androiddebugkey -keypass android -keystore debug.keystore -storepass android -dname "CN=Android Debug,O=Android,C=US" -validity 9999
|
||||
|
||||
Make sure you have adb
|
||||
----------------------
|
||||
|
||||
Android Debug Bridge (adb) is the command line tool used to communicate with
|
||||
Android devices. It's installed with the SDK, but you may need to install one
|
||||
(any) of the Android API levels for it to be installed in the SDK directory.
|
||||
|
||||
Setting it up in Godot
|
||||
----------------------
|
||||
|
||||
Enter the Editor Settings screen. This screens contains the editor
|
||||
settings for the user account in the computer (It's independent from the
|
||||
project).
|
||||
|
||||
.. image:: /img/editorsettings.png
|
||||
|
||||
Scroll down to the section where the Android settings are located:
|
||||
|
||||
.. image:: /img/androidsdk.png
|
||||
|
||||
In that screen, the path to 3 files needs to be set:
|
||||
|
||||
- The *adb* executable (adb.exe on Windows)
|
||||
- The *jarsigner* executable (from JDK 6 or 8)
|
||||
- The debug *keystore*
|
||||
|
||||
Once that is configured, everything is ready to export to Android!
|
||||
75
learning/workflow/export/exporting_for_ios.rst
Normal file
75
learning/workflow/export/exporting_for_ios.rst
Normal file
@@ -0,0 +1,75 @@
|
||||
.. _doc_exporting_for_ios:
|
||||
|
||||
Exporting for iOS
|
||||
=================
|
||||
|
||||
Exporting for iOS is done manually at the moment. These are the steps to
|
||||
load your game in an XCode project, where you can deploy to a device,
|
||||
publish, etc.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
- Download XCode for iOS
|
||||
- Download the export templates: https://godotengine.org/download
|
||||
- Since there is no automatic deployer yet, unzip export_templates.tpz
|
||||
manually and extract GodotiOSXCode.zip from it.
|
||||
|
||||
The zip contains an XCode project, godot_ios.xcodeproj, an empty
|
||||
data.pck file and the engine executable. Open the project, and modify
|
||||
the game name, icon, organization, provisioning signing certificate
|
||||
identities (??), etc.
|
||||
|
||||
Add your project data
|
||||
---------------------
|
||||
|
||||
Using the Godot editor, :ref:`doc_exporting_for_pc`, to obtain the data.pck
|
||||
file. Replace the empty data.pck in the XCode project with the new one,
|
||||
and run/archive.
|
||||
|
||||
If you want to test your scenes on the iOS device as you edit them, you
|
||||
can add your game directory to the project (instead of data.pck), and
|
||||
add a property "godot_path" to Info.plist, with the name of your
|
||||
directory as its value.
|
||||
|
||||
.. image:: /img/godot_path.png
|
||||
|
||||
Alternatively you can add all the files from your game directly, with
|
||||
"engine.cfg" at the root.
|
||||
|
||||
Loading files from a host
|
||||
-------------------------
|
||||
|
||||
Sometimes your game becomes too big and deploying to the device takes
|
||||
too long every time you run. In that case you can deploy only the engine
|
||||
executable, and serve the game files from your computer.
|
||||
|
||||
Setting up the file host
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On your PC, open the editor, and click the righ-most icon on the
|
||||
top-center group of icons, and select "Enable File Server". The icon
|
||||
turns red. Your PC will open a port and accept connections to serve
|
||||
files from your project's directory (so enable your local firewall
|
||||
accordingly).
|
||||
|
||||
.. image:: /img/rfs_server.png
|
||||
|
||||
Setting up the game
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On XCode, click on your app name (top left, next to the "Stop" button),
|
||||
and select "Edit Scheme". Go to the "Arguments" tab, and add 2
|
||||
arguments, "-rfs" and the IP of your PC.
|
||||
|
||||
.. image:: /img/edit_scheme.png
|
||||
|
||||
When you run, your device will connect to the host and open the files
|
||||
remotely. Note that the directory with the game data ("platformer") is
|
||||
no longer added to the project, only the engine executable.
|
||||
|
||||
Services for iOS
|
||||
----------------
|
||||
|
||||
Special iOS services can be used in Godot. Check out the
|
||||
:ref:`doc_services_for_ios` page.
|
||||
17
learning/workflow/export/exporting_for_pc.rst
Normal file
17
learning/workflow/export/exporting_for_pc.rst
Normal file
@@ -0,0 +1,17 @@
|
||||
.. _doc_exporting_for_pc:
|
||||
|
||||
Exporting for PC
|
||||
================
|
||||
|
||||
The simplest way to distribute a game for PC is to copy the executables
|
||||
(godot.exe on windows, godot on the rest), zip the folder and send it to
|
||||
someone else. However, this is often not desired.
|
||||
|
||||
Godot offers a more elegant approach for PC distribution when using the
|
||||
export system. When exporting for PC (Linux, Windows, Mac), the exporter
|
||||
takes all the project files and creates a "data.pck" file. This file is
|
||||
bundled with a specially optimized binary that is smaller, faster and
|
||||
lacks tools and debugger.
|
||||
|
||||
Optionally, the files can be bundled inside the executable, though this
|
||||
does not always works properly.
|
||||
71
learning/workflow/export/exporting_for_uwp.rst
Normal file
71
learning/workflow/export/exporting_for_uwp.rst
Normal file
@@ -0,0 +1,71 @@
|
||||
.. _doc_exporting_for_uwp:
|
||||
|
||||
Exporting for Universal Windows Platform
|
||||
========================================
|
||||
|
||||
There's no extra requirement to export an ``.appx`` package that can be
|
||||
installed as a Windows App or submited to the Windows Store. Exporting
|
||||
packages also works from any platform, not only on Windows.
|
||||
|
||||
However, if you want to install and run the app, you need to sign it with a
|
||||
trusted signature. Currently, Godot supports no signing of packages and you
|
||||
need to use externals to tools to do so.
|
||||
|
||||
Also, make sure the Publisher name you set when export the package matches
|
||||
the name on the certificate.
|
||||
|
||||
Limitations on Xbox One
|
||||
------------
|
||||
|
||||
As described in `UWP documentation <https://msdn.microsoft.com/en-us/windows/uwp/xbox-apps/system-resource-allocation>`__:
|
||||
|
||||
- available RAM is 1GB (after exceeding it, application will encounter memory allocation failures and will crash)
|
||||
- share of 2-4 CPU cores
|
||||
- share of 45% of GPU power
|
||||
|
||||
Creating a signing certificate
|
||||
------------------------------
|
||||
|
||||
This requires the tools ``MakeCert.exe`` and ``Pvk2Pfx.exe`` which comes
|
||||
with the Windows SDK. If you use Visual Studio, open one of its Developer
|
||||
Prompts since they come with those tools available and in the path.
|
||||
|
||||
You can get more detailed instructions from `Microsof documentation
|
||||
<https://msdn.microsoft.com/en-us/library/windows/desktop/jj835832(v=vs.85).aspx>`__.
|
||||
|
||||
First, run ``MakeCert`` to create a private key::
|
||||
|
||||
MakeCert /n publisherName /r /h 0 /eku "1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.10.3.13" /e expirationDate /sv MyKey.pvk MyKey.cer
|
||||
|
||||
Where ``publisherName`` matches the Publisher Name of your package and
|
||||
``expirationDate`` is in the ``mm/dd/yyyy`` format.
|
||||
|
||||
Next, create a Personal Information Exchange (.pfx) file using ``Pvk2Pfx.exe``::
|
||||
|
||||
Pvk2Pfx /pvk MyKey.pvk /pi pvkPassword /spc MyKey.cer /pfx MyKey.pfx [/po pfxPassword]
|
||||
|
||||
If you don't specify a password with ``/po`` argument, the PFX will have the
|
||||
same password as the private key.
|
||||
|
||||
You also need to trust this certificate to be able to actually install the
|
||||
apps. Open the Command Prompt as Administrator and run the following command::
|
||||
|
||||
Certutil -addStore TrustedPeople MyKey.cer
|
||||
|
||||
Signing the package
|
||||
-------------------
|
||||
|
||||
Using the ``SignTool.exe`` this requires a single command::
|
||||
|
||||
SignTool sign /fd SHA256 /a /f MyKey.pfx /p pfxPassword package.appx
|
||||
|
||||
Installing the package
|
||||
----------------------
|
||||
|
||||
After Windows 10 Anniversary Update you can install packages by just double
|
||||
clicking the ``.appx`` file from the Windows Explorer.
|
||||
|
||||
It's also possible to install using the ``Add-AppxPackage`` PowerShell cmdlet.
|
||||
|
||||
Note that if you don't update the version number, you'll have to uninstall the
|
||||
previous installed package before reinstalling it.
|
||||
90
learning/workflow/export/exporting_for_web.rst
Normal file
90
learning/workflow/export/exporting_for_web.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
.. _doc_exporting_for_web:
|
||||
|
||||
Exporting for the Web
|
||||
=====================
|
||||
|
||||
Exporting for the web generates several files to be served from a web server,
|
||||
including a default HTML page for presentation. A custom HTML file can be
|
||||
used, see :ref:`doc_compiling_for_web`.
|
||||
|
||||
The default HTML file is designed to fit the game perfectly without cutting off
|
||||
parts of the canvas when the browser window is scaled to the game's dimensions.
|
||||
This way it can be inserted into an ``<iframe>`` with the game's size, as is
|
||||
common on most web game hosting sites.
|
||||
|
||||
Serving the files
|
||||
-----------------
|
||||
|
||||
The default ``.html`` file can be used as ``DirectoryIndex`` and can be
|
||||
renamed to e.g. ``index.html`` at any time, its name is never depended on.
|
||||
It can also be inserted into another HTML file as an ``<iframe>`` element.
|
||||
Users must allow **third-party** cookies when playing a game presented in an
|
||||
iframe.
|
||||
|
||||
The ``.mem`` and ``.pck`` files are binary, usually delivered with MIME-type
|
||||
``application/octet-stream``.
|
||||
|
||||
Delivering the files with gzip compression is recommended especially for the
|
||||
``.pck``, ``.asm.js`` and ``.mem`` files, which are usually large in size.
|
||||
|
||||
Export options
|
||||
--------------
|
||||
|
||||
Turning on **Debugging Enabled** when exporting will, in addition to enabling
|
||||
various debug features of the engine, display a debug output below the canvas,
|
||||
displaying JavaScript and engine errors. If controls are
|
||||
enabled as well, display of this output can be toggled.
|
||||
You can also use the browser-integrated developer console, usually opened with
|
||||
the F12 key, which often shows more information, including WebGL errors.
|
||||
|
||||
**Memory Size** is fixed and must thus be set during export. Try using no more
|
||||
than necessary to strain users' browsers as little as possible.
|
||||
|
||||
**Enable Run** will add a button between the *Stop scene* and *Play edited Scene*
|
||||
buttons in the editor to quickly open the game in the default browser for
|
||||
testing.
|
||||
|
||||
The remaining options customize the generated HTML page:
|
||||
|
||||
**Title** is the content of the ``<title>`` element of the page, usually used by
|
||||
browsers as the tab and window name. The title set here is only displayed until
|
||||
the game is started, afterwards the title is set to the application name set in
|
||||
the project settings.
|
||||
|
||||
**Head Include** and **Style Include** are appended into the ``<head>`` and
|
||||
CSS ``<style>`` elements respectively. This allows, for example, linking
|
||||
web fonts for use in the page.
|
||||
|
||||
**Font Family** is the CSS ``font-family`` used on the page, without terminating
|
||||
semicolon.
|
||||
|
||||
**Controls Enabled** toggles display of controls, offering e.g. a toggle for
|
||||
output display in debug mode and a fullscreen button.
|
||||
In the default page, the controls are displayed in the top-right corner on top
|
||||
of the canvas, which can get in the way in games that use the cursor.
|
||||
|
||||
Security restrictions
|
||||
---------------------
|
||||
|
||||
Browsers do not allow arbitrarily **entering full screen** at any time.
|
||||
Instead, these actions have to occur as a response to a JavaScript input event.
|
||||
In Godot, this is most easily done by entering full screen from within an
|
||||
``_input()`` callback.
|
||||
|
||||
Chromium-derived browsers will not load exported projects when
|
||||
**opened locally** per ``file://`` protocol. To get around this, you can start
|
||||
the browser with the ``--allow-file-access-from-files`` flag, or use a local
|
||||
server. Python offers an easy way for this, using ``python -m SimpleHTTPServer``
|
||||
with Python 2 or ``python -m http.server`` with Python 3 will serve the
|
||||
current working directory on ``http://localhost:8000``.
|
||||
|
||||
Locale
|
||||
------
|
||||
|
||||
Godot tries to detect the user's locale using information provided by the
|
||||
browser, but this is rather unreliable. A better way is to use CGI to read the
|
||||
HTTP ``Accept-Language`` header. If you assign its value to the JavaScript
|
||||
property ``Module.locale`` after the ``Module`` objects is created, but before
|
||||
the engine starts, Godot will use that value to initialize the locale.
|
||||
In any case, users should always be offered the option to configure the locale
|
||||
manually.
|
||||
128
learning/workflow/export/exporting_projects.rst
Normal file
128
learning/workflow/export/exporting_projects.rst
Normal file
@@ -0,0 +1,128 @@
|
||||
.. _doc_exporting_projects:
|
||||
|
||||
Exporting projects
|
||||
==================
|
||||
|
||||
Why exporting?
|
||||
--------------
|
||||
|
||||
Originally, Godot did not have any means to export projects. The
|
||||
developers would compile the proper binaries and build the packages for
|
||||
each platform manually.
|
||||
|
||||
When more developers (and even non-programmers) started using it, and
|
||||
when our company started taking more projects at the same time, it
|
||||
became evident that this was a bottleneck.
|
||||
|
||||
On PC
|
||||
~~~~~
|
||||
|
||||
Distributing a game project on PC with Godot is rather easy. Just drop
|
||||
the godot.exe (or godot) binary together in the same place as the
|
||||
engine.cfg file, zip it and you are done. This can be taken advantage of to
|
||||
make custom installers.
|
||||
|
||||
It sounds simple, but there are probably a few reasons why the developer
|
||||
may not want to do this. The first one is that it may not be desirable
|
||||
to distribute loads of files. Some developers may not like curious users
|
||||
peeking at how the game was made, others may just find it inelegant,
|
||||
etc.
|
||||
|
||||
Another reason is that, for distribution, the developer might use a
|
||||
specially compiled binary, which is smaller in size, more optimized and
|
||||
does not include tools inside (like the editor, debugger, etc.).
|
||||
|
||||
Finally, Godot has a simple but efficient system for creating DLCs as
|
||||
extra package files.
|
||||
|
||||
On mobile
|
||||
~~~~~~~~~
|
||||
|
||||
The same scenario in mobile is a little worse. To distribute a project
|
||||
in those devices, a binary for each of those platforms is built, then
|
||||
added to a native project together with the game data.
|
||||
|
||||
This can be troublesome because it means that the developer must be
|
||||
familiarized with the SDK of each platform before even being able to
|
||||
export. In other words, while learning each SDK is always encouraged, it
|
||||
can be frustrating to be forced to do it at an undesired time.
|
||||
|
||||
There is also another problem with this approach, which is the fact that
|
||||
different devices prefer some data in different formats to run. The main
|
||||
example of this is texture compression. All PC hardware uses S3TC (BC)
|
||||
compression and that has been standardized for more than a decade, but
|
||||
mobile devices use different formats for texture compression, such as
|
||||
PVRCT (iOS) or ETC (Android).
|
||||
|
||||
Export dialog
|
||||
-------------
|
||||
|
||||
After many attempts at different export workflows, the current one has
|
||||
proven to work the best. At the time of this writing, not all platforms are
|
||||
supported yet, but the supported platforms continue to grow.
|
||||
|
||||
To open the export dialog, just click the "Export" button:
|
||||
|
||||
.. image:: /img/export.png
|
||||
|
||||
The dialog will open, showing all the supported export platforms:
|
||||
|
||||
.. image:: /img/export_dialog.png
|
||||
|
||||
The default options are often enough to export, so tweaking them is not
|
||||
necessary, but provide extra control. However, many platforms require additional
|
||||
tools (SDKs) to be installed to be able to export. Additionally, Godot
|
||||
needs exports templates installed to create packages. The export dialog
|
||||
will complain when something is missing and will not allow the user to
|
||||
export for that platform until they resolve it:
|
||||
|
||||
.. image:: /img/export_error.png
|
||||
|
||||
At that time, the user is expected to come back to the documentation and follow
|
||||
instructions on how to properly set up that platform.
|
||||
|
||||
Export templates
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Apart from setting up the platform, the export templates must be
|
||||
installed to be able to export projects. They can be obtained as a
|
||||
.tpz (a renamed .zip) file from the `download page of the website
|
||||
<https://www.godotengine.org/download>`_.
|
||||
|
||||
Once downloaded, they can be installed using the "Install Export
|
||||
Templates" option in the editor:
|
||||
|
||||
.. image:: /img/exptemp.png
|
||||
|
||||
Export mode
|
||||
~~~~~~~~~~~
|
||||
|
||||
When exporting, Godot makes a list of all the files to export and then
|
||||
creates the package. There are 3 different modes for exporting:
|
||||
|
||||
- Export every single file in the project
|
||||
- Export only resources (+custom filter), this is default.
|
||||
- Export only selected resources (+custom filter)
|
||||
|
||||
.. image:: /img/expres.png
|
||||
|
||||
- **Export every single file** - This mode exports every single file in
|
||||
the project. This is good to test if something is being forgotten,
|
||||
but developers often have a lot of unrelated stuff around in the dev
|
||||
directory, which makes it a bad idea.
|
||||
|
||||
- **Export only resources** - Only resources are exported. For most
|
||||
projects, this is enough. However many developers like to use custom
|
||||
datafiles in their games. To compensate for this, filters can be
|
||||
added for extra extensions (like, *.txt,*.csv, etc.).
|
||||
|
||||
- **Export only selected resources** - Only select resources from a
|
||||
list are exported. This is probably overkill for most projects, but
|
||||
in some cases it is justified (usually huge projects). This mode
|
||||
offers total control of what is exported. Individual resources can be
|
||||
selected and dependency detection is performed to ensure that
|
||||
everything needed is added. As a plus, this mode allows to
|
||||
"Bundle" scenes and dependencies into a single file, which is
|
||||
*really* useful for games distributed on optical media.
|
||||
|
||||
.. image:: /img/expselected.png
|
||||
33
learning/workflow/export/one-click_deploy.rst
Normal file
33
learning/workflow/export/one-click_deploy.rst
Normal file
@@ -0,0 +1,33 @@
|
||||
.. _doc_one-click_deploy:
|
||||
|
||||
One-click deploy
|
||||
================
|
||||
|
||||
Sounds good, what is it?
|
||||
------------------------
|
||||
|
||||
This feature will pop up automatically once a platform is properly
|
||||
configured and a supported device is connected to the computer. Since
|
||||
things can go wrong at many levels (platform may not be configured
|
||||
correctly, SDK may incorrectly installed, device may be improperly
|
||||
configured, kitty ate the USB cable, etc.), it's good to let the user
|
||||
know that it exists.
|
||||
|
||||
Some platforms (at the time of this writing, only Android and Blackberry
|
||||
10) can detect when a USB device is connected to the computer, and offer
|
||||
the user to automatically export, install and run the project (in debug
|
||||
mode) on the device. This feature is called, in industry buzz-words,
|
||||
"One Click Deploy" (though, it's technically two clicks...).
|
||||
|
||||
Steps for one-click deploy
|
||||
--------------------------
|
||||
|
||||
#. Configure target platform.
|
||||
#. Configure device (make sure it's in developer mode, likes the
|
||||
computer, usb is recognized, usb cable is plugged, etc.).
|
||||
#. Connect the device..
|
||||
#. And voila!
|
||||
|
||||
.. image:: /img/oneclick.png
|
||||
|
||||
Click once.. and deploy!
|
||||
0
learning/workflow/project_setup/cvs.rst
Normal file
0
learning/workflow/project_setup/cvs.rst
Normal file
133
learning/workflow/project_setup/project_organization.rst
Normal file
133
learning/workflow/project_setup/project_organization.rst
Normal file
@@ -0,0 +1,133 @@
|
||||
.. _doc_project_organization:
|
||||
|
||||
Project organization
|
||||
====================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
This tutorial is aimed to propose a simple workflow on how to organize
|
||||
projects. Since Godot allows the programmer to use the file-system as he
|
||||
or she pleases, figuring out a way to organize the projects when
|
||||
starting to use the engine can be a little challenging. Because of this,
|
||||
a simple workflow will be described, which can be used or not, but
|
||||
should work as a starting point.
|
||||
|
||||
Additionally, using version control can be challenging so this
|
||||
proposition will include that too.
|
||||
|
||||
Organization
|
||||
------------
|
||||
|
||||
Other game engines often work by having an asset database, where you can
|
||||
browse images, models, sounds, etc. Godot is more scene-based in nature
|
||||
so most of the time the assets are bundled inside the scenes or just
|
||||
exist as files but are referenced from scenes.
|
||||
|
||||
Importing & game folder
|
||||
-----------------------
|
||||
|
||||
It is very often necessary to use asset importing in Godot. As the
|
||||
source assets for importing are also recognized as resources by the
|
||||
engine, this can become a problem if both are inside the project folder,
|
||||
because at the time of export the exporter will recognize them and
|
||||
export both.
|
||||
|
||||
To solve this, it is a good practice to have your game folder inside
|
||||
another folder (the actual project folder). This allows to have the game
|
||||
assets separated from the source assets, and also allows to use version
|
||||
control (such as svn or git) for both. Here is an example:
|
||||
|
||||
::
|
||||
|
||||
myproject/art/models/house.max
|
||||
myproject/art/models/sometexture.png
|
||||
myproject/sound/door_open.wav
|
||||
myproject/sound/door_close.wav
|
||||
myproject/translations/sheet.csv
|
||||
|
||||
Then also, the game itself is, in this case, inside a game/ folder:
|
||||
|
||||
::
|
||||
|
||||
myproject/game/engine.cfg
|
||||
myproject/game/scenes/house/house.scn
|
||||
myproject/game/scenes/house/sometexture.tex
|
||||
myproject/game/sound/door_open.smp
|
||||
myproject/game/sound/door_close.smp
|
||||
myproject/game/translations/sheet.en.xl
|
||||
myproject/game/translations/sheet.es.xl
|
||||
|
||||
Following this layout, many things can be done:
|
||||
|
||||
- The whole project is still inside a folder (myproject/).
|
||||
- Exporting the project will not export the .wav and .png files which
|
||||
were imported.
|
||||
- myproject/ can be put directly inside a VCS (like svn or git) for
|
||||
version control, both game and source assets are kept track of.
|
||||
- If a team is working on the project, assets can be re-imported by
|
||||
other project members, because Godot keeps track of source assets
|
||||
using relative paths.
|
||||
|
||||
Scene organization
|
||||
------------------
|
||||
|
||||
Inside the game folder, a question that often arises is how to organize
|
||||
the scenes in the filesystem. Many developers try asset-type based
|
||||
organization and end up having a mess after a while, so the best answer
|
||||
is probably to organize them based on how the game works and not based
|
||||
on asset type. Here are some examples.
|
||||
|
||||
If you were organizing your project based on asset type, it would look
|
||||
like this:
|
||||
|
||||
::
|
||||
|
||||
game/engine.cfg
|
||||
game/scenes/scene1.scn
|
||||
game/scenes/scene2.scn
|
||||
game/textures/texturea.png
|
||||
game/textures/another.tex
|
||||
game/sounds/sound1.smp
|
||||
game/sounds/sound2.wav
|
||||
game/music/music1.ogg
|
||||
|
||||
Which is generally a bad idea. When a project starts growing beyond a
|
||||
certain point, this becomes unmanageable. It's really difficult to tell
|
||||
what belongs to what.
|
||||
|
||||
It's generally a better idea to use game-context based organization,
|
||||
something like this:
|
||||
|
||||
::
|
||||
|
||||
game/engine.cfg
|
||||
game/scenes/house/house.scn
|
||||
game/scenes/house/texture.tex
|
||||
game/scenes/valley/canyon.scn
|
||||
game/scenes/valley/rock.scn
|
||||
game/scenes/valley/rock.tex
|
||||
game/scenes/common/tree.scn
|
||||
game/scenes/common/tree.tex
|
||||
game/player/player.scn
|
||||
game/player/player.gd
|
||||
game/npc/theking.scn
|
||||
game/npc/theking.gd
|
||||
game/gui/main_screen/main_sceen.scn
|
||||
game/gui/options/options.scn
|
||||
|
||||
This model or similar models allows projects to grow to really large
|
||||
sizes and still be completely manageable. Notice that everything is
|
||||
based on parts of the game that can be named or described, like the
|
||||
settings screen or the valley. Since everything in Godot is done with
|
||||
scenes, and everything that can be named or described can be a scene,
|
||||
this workflow is very smooth and easygoing.
|
||||
|
||||
Cache files
|
||||
-----------
|
||||
|
||||
Godot uses a hidden file called ".fscache" at the root of the project.
|
||||
On it, it caches project files and is used to quickly know when one is
|
||||
modified. Make sure to **not commit this file** to git or svn, as it
|
||||
contains local information and might confuse another editor instance in
|
||||
another computer.
|
||||
Reference in New Issue
Block a user