mirror of
https://github.com/godotengine/godot-docs.git
synced 2026-01-04 14:11:02 +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.
This commit is contained in:
committed by
Rémi Verschelde
parent
b632022426
commit
b408bdb918
@@ -1,10 +0,0 @@
|
||||
2D tutorials
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:name: toc-2d-tutorials
|
||||
|
||||
_2d_graphics
|
||||
_2d_gui
|
||||
_2d_physics
|
||||
@@ -1,14 +0,0 @@
|
||||
Graphics
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-2d-graphics
|
||||
|
||||
canvas_layers
|
||||
viewport_and_canvas_transforms
|
||||
custom_drawing_in_2d
|
||||
screen-reading_shaders
|
||||
particle_systems_2d
|
||||
cutout_animation
|
||||
using_tilemaps
|
||||
@@ -1,10 +0,0 @@
|
||||
Graphical user interface (GUI)
|
||||
==============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-2d-gui
|
||||
|
||||
size_and_anchors
|
||||
gui_skinning
|
||||
custom_gui_controls
|
||||
@@ -1,10 +0,0 @@
|
||||
Physics
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:name: toc-2d-physics
|
||||
|
||||
physics_introduction
|
||||
kinematic_character_2d
|
||||
../ray-casting
|
||||
@@ -1,76 +0,0 @@
|
||||
.. _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>`).
|
||||
@@ -1,256 +0,0 @@
|
||||
.. _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).
|
||||
@@ -1,143 +0,0 @@
|
||||
.. _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
|
||||
@@ -1,355 +0,0 @@
|
||||
.. _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
|
||||
@@ -1,161 +0,0 @@
|
||||
.. _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!
|
||||
@@ -1,249 +0,0 @@
|
||||
.. _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.
|
||||
@@ -1,260 +0,0 @@
|
||||
.. _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
|
||||
@@ -1,441 +0,0 @@
|
||||
.. _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!
|
||||
@@ -1,109 +0,0 @@
|
||||
.. _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!
|
||||
@@ -1,42 +0,0 @@
|
||||
.. _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.
|
||||
@@ -1,165 +0,0 @@
|
||||
.. _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).
|
||||
@@ -1,99 +0,0 @@
|
||||
.. _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)
|
||||
Reference in New Issue
Block a user