Skip to content

Commit

Permalink
Update 2D transform tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
Sauermann committed Oct 22, 2022
1 parent 4024e8f commit 7208253
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 56 deletions.
198 changes: 142 additions & 56 deletions tutorials/2d/2d_transforms.rst
Original file line number Diff line number Diff line change
@@ -1,84 +1,171 @@
.. _doc_viewport_and_canvas_transforms:
.. _doc_2d_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.
the screen. This overview discusses very low level details of the engine
and also describes how to use them.

Canvas transform
----------------
: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.

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>`.
Godot 2D coordinate systems
---------------------------

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 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.

Global canvas transform
-----------------------
The graphic is based on a node tree of the following form: ``root Window (embed Windows)`` ⇒
``Window`` ⇒ ``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.

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.
.. image:: img/transforms_overview.png
:target: ../../_images/transforms_overview.png

Stretch transform
-----------------
Click graphic to enlarge.

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.
- **Item Coordinates**
This is the local coordinate system of a :ref:`CanvasItem <class_CanvasItem>`.

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.
- **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>`.

Transform order
---------------
- **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.

- **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.

For a coordinate in CanvasItem local properties to become an actual
screen coordinate, the following chain of transforms must be applied:
- **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 *Viewport* and its scale is the one of the embedder or the Window Manager.

.. image:: img/viewport_transforms2.png
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.

If the embedder is the OS Window Manager, then they are also called Absolute Screen
Coordinates.

Node transforms
---------------

Each of the mentioned nodes have one or more :ref:`Transform2D <class_Transform2D>` 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 :ref:`Control <class_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 :ref:`Node2D <class_Node2D>` nodes :ref:`transform <class_Node2D_property_transform>` is a
:ref:`Transform2D <class_Transform2D>`.

- **CanvasLayer transform**
The *CanvasLayer's* :ref:`transform <class_CanvasLayer_property_transform>` affects all
*CanvasItems* within the *CanvasLayer*.

- **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 activated, to achieve a pseudo 3D effect.

- **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.

- **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* transforms. This is
primarily used in the CanvasItem Editor in Godot's editor.

- **Viewport stretch transform**
Finally, *Viewports* have a *stretch transform*, which is used when resizing or stretching the
screen. 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>`.

- **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`
can have a fixed aspect ratio.

- **Window position**
Every *Window* also has a :ref:`position <class_Window_property_position>` to describe its
position within its embedder.

- **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 (exposed to GDScript and also
internal). 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

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>` |
+----------------------------------+---------------------------------------------------------------------------------------------+
canvasPos = GetGlobalTransform().Xform(localPos)
localPos = GetGlobalTransform().AffineInverse().Xform(canvasPos)

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()).Xform(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
Expand All @@ -88,23 +175,22 @@ 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, it must be done the following way:

.. tabs::
.. code-tab:: gdscript GDScript

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 ie = new InputEventMouseButton();
ie.ButtonIndex = (int)ButtonList.Left;
ie.Position = (GetViewportTransform() * GetGlobalTransform()).Xform(localPos);
GetTree().InputEvent(ie);
ie.Position = (GetViewport().GetScreenTransform() * GetGlobalTransformWithCanvas()).Xform(localPos);
Input.ParseInputEvent(ie);
Binary file added tutorials/2d/img/transforms_overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed tutorials/2d/img/viewport_transforms2.png
Binary file not shown.

0 comments on commit 7208253

Please sign in to comment.