-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extend the tutorial to explain coordinate systems, transforms of nodes and transform functions
- Loading branch information
Showing
3 changed files
with
159 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,114 +1,205 @@ | ||
:article_outdated: True | ||
.. _doc_2d_transforms: | ||
|
||
.. _doc_viewport_and_canvas_transforms: | ||
|
||
Viewport and canvas transforms | ||
============================== | ||
2D transforms | ||
============= | ||
|
||
Introduction | ||
------------ | ||
|
||
This is an overview of the 2D transforms going on for nodes from the | ||
moment they draw their content locally to the time they are drawn onto | ||
the screen. This overview discusses very low level details of the engine. | ||
This is an overview of the 2D transforms going on for nodes from the moment they draw their content | ||
locally to the time they are drawn onto the screen. This overview discusses very low level details | ||
of the engine and also describes how to use them. | ||
|
||
:ref:`Transform2D <class_Transform2D>` are matrices that convert coordinates from one coordinate | ||
system to an other. In order to use them, it is beneficial to know which coordinate systems are | ||
available in Godot. For a deeper understanding, the :ref:`doc_matrices_and_transforms` tutorial | ||
offers insights to the underlying functionality. | ||
|
||
Canvas transform | ||
---------------- | ||
Godot 2D coordinate systems | ||
--------------------------- | ||
|
||
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:`Transform2D <class_Transform2D>`. | ||
The following graphic gives an overview of Godot 2D coordinate systems and the available | ||
node-transforms, transform-functions and coordinate-system related functions. At the left | ||
is the Window Manager screen, at the right are the :ref:`CanvasItems <class_CanvasItem>`. For | ||
simplicity reasons this graphic doesn't include :ref:`SubViewport <class_SubViewport>`, | ||
:ref:`SubViewportContainer <class_SubViewportContainer>`, :ref:`ParallaxLayer<class_ParallaxLayer>` | ||
and :ref:`ParallaxBackground<class_ParallaxBackground>` all of which also influence transforms. | ||
|
||
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. | ||
The graphic is based on a node tree of the following form: ``Root Window (embed Windows)`` ⇒ | ||
``Window (don't embed Windows)`` ⇒ ``CanvasLayer`` ⇒ ``CanvasItem`` ⇒ ``CanvasItem`` ⇒ ``CanvasItem``. | ||
There are more complex combinations possible, like deeply nested Window and SubViewports, | ||
however this example intends to provide an overview of the methodology in general. | ||
|
||
Global canvas transform | ||
----------------------- | ||
.. image:: img/transforms_overview.webp | ||
:target: ../../_images/transforms_overview.webp | ||
|
||
Viewports also have a Global Canvas transform (also a | ||
:ref:`Transform2D <class_Transform2D>`). 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. | ||
Click graphic to enlarge. | ||
|
||
Stretch transform | ||
----------------- | ||
- **Item Coordinates** | ||
This is the local coordinate system of a :ref:`CanvasItem <class_CanvasItem>`. | ||
|
||
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. | ||
- **Parent Item Coordinates** | ||
This is the local coordinate system of the parent's *CanvasItem*. When positioning | ||
*CanvasItems* in the *Canvas*, they usually inherit the transformations of their parent | ||
*CanvasItems*. An exceptions is :ref:`CanvasItems.top_level <class_CanvasItem_property_top_level>`. | ||
|
||
Input events 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_method_make_input_local>` | ||
function was added for convenience. | ||
- **Canvas Coordinates** | ||
As mentioned in the previous tutorial :ref:`doc_canvas_layers`, there are two types of canvases | ||
and both have a canvas coordinate system. These are also called world coordinates. A *Viewport* | ||
can contain multiple *Canvases* with different coordinate systems. | ||
|
||
Transform order | ||
--------------- | ||
- **Viewport Coordinates** | ||
This is the coordinate system of the :ref:`Viewport <class_Viewport>`. | ||
|
||
- **Camera Coordinates** | ||
This is only used internally for functionality like 3D-camera ray projections. | ||
|
||
- **Embedder Coordinates / Screen Coordinates** | ||
Every *Viewport* (*Window* or *SubViewport*) in the scene tree is embedded either in a | ||
different node or in the Window Manager. This coordinate system's origin is identical to the | ||
top-left corner of the *Window* or *SubViewport* and its scale is the one of the embedder or | ||
the Window Manager. | ||
|
||
If the embedder is the OS Window Manager, then they are also called Screen Coordinates. | ||
|
||
- **Absolute Embedder Coordinates / Absolute Screen Coordinates** | ||
The origin of this coordinate system is the top-left corner of the embedding node or the Window | ||
Manager screen. Its scale is the one of the embedder or the Window Manager. | ||
|
||
For a coordinate in CanvasItem local properties to become an actual | ||
screen coordinate, the following chain of transforms must be applied: | ||
If the embedder is the OS Window Manager, then they are also called Absolute Screen Coordinates. | ||
|
||
.. image:: img/viewport_transforms2.png | ||
Node transforms | ||
--------------- | ||
|
||
Each of the mentioned nodes have one or more transforms associated with them and the combination of | ||
these nodes infer the transforms between the different coordinate systems. With a few exceptions, | ||
the transforms are :ref:`Transform2D <class_Transform2D>` and the following list shows details and | ||
effects of each of them. | ||
|
||
- **CanvasItem transform** | ||
*CanvasItems* are either *Control*-nodes or *Node2D*-nodes. | ||
|
||
For *Control* nodes this transform consists of a :ref:`position <class_Control_property_position>` | ||
relative to the parent's origin and a :ref:`scale <class_Control_property_scale>` and | ||
:ref:`rotation <class_Control_property_rotation>` around a | ||
:ref:`pivot point <class_Control_property_pivot_offset>`. | ||
|
||
For *Node2D* nodes :ref:`transform <class_Node2D_property_transform>` consists of | ||
:ref:`position <class_Node2D_property_position>`, :ref:`rotation <class_Node2D_property_rotation>`, | ||
:ref:`scale <class_Node2D_property_scale>` and :ref:`skew <class_Node2D_property_skew>`. | ||
|
||
The transform affects the item itself and usually also child-*CanvasItems* and in the case of a | ||
*SubViewportContainer* it affects the contained *SubViewport*. | ||
|
||
- **CanvasLayer transform** | ||
The *CanvasLayer's* :ref:`transform <class_CanvasLayer_property_transform>` affects all | ||
*CanvasItems* within the *CanvasLayer*. It doesn't affect other *CanvasLayers* or *Windows* in | ||
its *Viewport*. | ||
|
||
- **CanvasLayer follow viewport transform** | ||
The *follow viewport transform* is an automatically calculated transform, that is based on the | ||
*Viewport's* :ref:`canvas transform <class_Viewport_property_canvas_transform>` and the | ||
*CanvasLayer's* :ref:`follow viewport scale <class_CanvasLayer_property_follow_viewport_scale>` | ||
and can be used, if :ref:`enabled <class_CanvasLayer_property_follow_viewport_enabled>`, to | ||
achieve a pseudo 3D effect. It affects the same child nodes as the *CanvasLayer transform*. | ||
|
||
- **Viewport canvas transform** | ||
The :ref:`canvas transform <class_Viewport_property_canvas_transform>` affects all | ||
*CanvasItems* in the *Viewport's* default canvas. It also affects *CanvasLayers*, that have | ||
follow viewport transform enabled. The *Viewport's* active :ref:`Camera2D <class_Camera2D>` | ||
works by changing this transform. It doesn't affect this *Viewport's* embedded *Windows*. | ||
|
||
- **Viewport global canvas transform** | ||
*Viewports* also have a :ref:`global canvas transform <class_Viewport_property_global_canvas_transform>`. | ||
This is the master transform and affects all individual *Canvas Layer* and embedded *Window* | ||
transforms. This is primarily used in Godot's CanvasItem Editor. | ||
|
||
- **Viewport stretch transform** | ||
Finally, *Viewports* have a *stretch transform*, which is used when resizing or stretching the | ||
viewport. This transform is used for :ref:`Windows <class_Window>` as described in | ||
:ref:`doc_multiple_resolutions`, but can also be manually set on *SubViewports* by means of | ||
:ref:`size <class_SubViewport_property_size>` and | ||
:ref:`size_2d_override <class_SubViewport_property_size_2d_override>`. It's | ||
:ref:`translation <class_Transform2D_method_get_origin>`, | ||
:ref:`rotation <class_Transform2D_method_get_rotation>` and | ||
:ref:`skew <class_Transform2D_method_get_skew>` are the default values and it can only have | ||
non-default :ref:`scale <class_Transform2D_method_get_scale>`. | ||
|
||
- **Window transform** | ||
In order to scale and position the *Window's* content as described in | ||
:ref:`doc_multiple_resolutions`, each :ref:`Window <class_Window>` contains a | ||
*window transform*. It is for example responsible for the black bars at the *Window's* sides so | ||
that the *Viewport* is displayed with a fixed aspect ratio. | ||
|
||
- **Window position** | ||
Every *Window* also has a :ref:`position <class_Window_property_position>` to describe its | ||
position within its embedder. The embedder can be another *Viewport* or the Window Manager. | ||
|
||
- **SubViewportContainer shrink transform** | ||
:ref:`stretch <class_SubViewportContainer_property_stretch>` together with | ||
:ref:`stretch_shrink <class_SubViewportContainer_property_stretch_shrink>` declare for a | ||
*SubViewportContaner* if and by what integer factor the contained *SubViewport* should be | ||
scaled in comparison to the container's size. | ||
|
||
Transform functions | ||
------------------- | ||
|
||
Obtaining each transform can be achieved with the following functions: | ||
The above graphic shows all available transform functions. All transforms are directed from right | ||
to left, this means multiplying a transform with a coordinate results in a coordinate system | ||
further to the left, multiplying the :ref:`affine inverse <class_Transform2D_method_affine_inverse>` | ||
of a transform results in a coordinate system further to the right: | ||
|
||
.. tabs:: | ||
.. code-tab:: gdscript GDScript | ||
|
||
# Called from a CanvasItem. | ||
canvas_pos = get_global_transform() * local_pos | ||
local_pos = get_global_transform().affine_inverse() * canvas_pos | ||
|
||
.. code-tab:: csharp | ||
|
||
+----------------------------------+---------------------------------------------------------------------------------------------+ | ||
| Type | Transform | | ||
+==================================+=============================================================================================+ | ||
| CanvasItem | :ref:`CanvasItem.get_global_transform() <class_CanvasItem_method_get_global_transform>` | | ||
+----------------------------------+---------------------------------------------------------------------------------------------+ | ||
| CanvasLayer | :ref:`CanvasItem.get_canvas_transform() <class_CanvasItem_method_get_canvas_transform>` | | ||
+----------------------------------+---------------------------------------------------------------------------------------------+ | ||
| CanvasLayer+GlobalCanvas+Stretch | :ref:`CanvasItem.get_viewport_transform() <class_CanvasItem_method_get_viewport_transform>` | | ||
+----------------------------------+---------------------------------------------------------------------------------------------+ | ||
// Called from a CanvasItem. | ||
canvasPos = GetGlobalTransform() * localPos; | ||
localPos = GetGlobalTransform().AffineInverse() * canvasPos; | ||
|
||
Finally, then, to convert a CanvasItem local coordinates to screen | ||
coordinates, just multiply in the following order: | ||
Finally, then, to convert a CanvasItem local coordinates to screen coordinates, just multiply in | ||
the following order: | ||
|
||
.. tabs:: | ||
.. code-tab:: gdscript GDScript | ||
|
||
var screen_coord = get_viewport_transform() * (get_global_transform() * local_pos) | ||
var screen_coord = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos | ||
|
||
.. code-tab:: csharp | ||
|
||
var screenCord = (GetViewportTransform() * GetGlobalTransform()).Xform(localPos); | ||
var screenCord = GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas() * localPos; | ||
|
||
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. | ||
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: | ||
It is often desired to feed custom input events to the game. With the above knowledge, to correctly | ||
do this in the focused window, it must be done the following way: | ||
|
||
.. tabs:: | ||
.. code-tab:: gdscript GDScript | ||
|
||
var local_pos = Vector2(10, 20) # local to Control/Node2D | ||
var local_pos = Vector2(10, 20) # Local to Control/Node2D. | ||
var ie = InputEventMouseButton.new() | ||
ie.button_index = BUTTON_LEFT | ||
ie.position = get_viewport_transform() * (get_global_transform() * local_pos) | ||
get_tree().input_event(ie) | ||
ie.button_index = MOUSE_BUTTON_LEFT | ||
ie.position = get_viewport().get_screen_transform() * get_global_transform_with_canvas() * local_pos | ||
Input.parse_input_event(ie) | ||
|
||
.. code-tab:: csharp | ||
|
||
var localPos = new Vector2(10,20); // local to Control/Node2D | ||
var localPos = new Vector2(10,20); // Local to Control/Node2D. | ||
var ie = new InputEventMouseButton() | ||
{ | ||
ButtonIndex = MouseButton.Left, | ||
Position = GetViewportTransform() * (GetGlobalTransform() * localPos), | ||
Position = GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas() * localPos, | ||
}; | ||
GetTree().InputEvent(ie); | ||
Input.ParseInputEvent(ie); |
Binary file not shown.
Binary file not shown.