|
@@ -7,11 +7,11 @@ Why?
|
|
----
|
|
----
|
|
|
|
|
|
Godot has nodes to draw sprites, polygons, particles, and all sorts of
|
|
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,
|
|
|
|
|
|
+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...
|
|
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
|
|
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>`
|
|
:ref:`Control <class_Control>` or :ref:`Node2D <class_Node2D>`
|
|
-based) draw custom commands. It is *really* easy to do it too.
|
|
|
|
|
|
+based) draw custom commands. It is *really* easy to do it, too.
|
|
|
|
|
|
But...
|
|
But...
|
|
------
|
|
------
|
|
@@ -27,8 +27,8 @@ examples why:
|
|
the blocks.
|
|
the blocks.
|
|
- Managing drawing logic of a large amount of simple objects (in the
|
|
- Managing drawing logic of a large amount of simple objects (in the
|
|
hundreds of thousands). Using a thousand nodes is probably not nearly
|
|
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.
|
|
|
|
|
|
+ as efficient as drawing, but a thousand draw calls are cheap;
|
|
|
|
+ take the "Shower of Bullets" demo as an example.
|
|
- Making a custom UI control. There are plenty of controls available,
|
|
- 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.
|
|
but it's easy to run into the need to make a new, custom one.
|
|
|
|
|
|
@@ -68,7 +68,7 @@ If re-drawing is required because a state or something else changed,
|
|
simply call :ref:`CanvasItem.update() <class_CanvasItem_update>`
|
|
simply call :ref:`CanvasItem.update() <class_CanvasItem_update>`
|
|
in that same node and a new _draw() call will happen.
|
|
in that same node and a new _draw() call will happen.
|
|
|
|
|
|
-Here is a little more complex example. A texture variable that will be
|
|
|
|
|
|
+Here is a little more complex example, a texture variable that will be
|
|
redrawn if modified:
|
|
redrawn if modified:
|
|
|
|
|
|
.. tabs::
|
|
.. tabs::
|
|
@@ -76,10 +76,10 @@ redrawn if modified:
|
|
|
|
|
|
extends Node2D
|
|
extends Node2D
|
|
|
|
|
|
- export var texture setget _set_texture
|
|
|
|
|
|
+ export (Texture) var texture setget _set_texture
|
|
|
|
|
|
func _set_texture(value):
|
|
func _set_texture(value):
|
|
- # if the texture variable is modified externally,
|
|
|
|
|
|
+ # If the texture variable is modified externally,
|
|
# this callback is called.
|
|
# this callback is called.
|
|
texture = value #texture was changed
|
|
texture = value #texture was changed
|
|
update() # update the node
|
|
update() # update the node
|
|
@@ -154,17 +154,17 @@ portion of a circle? You will have to code a function to perform this and draw i
|
|
Arc function
|
|
Arc function
|
|
^^^^^^^^^^^^
|
|
^^^^^^^^^^^^
|
|
|
|
|
|
-An arc is defined by its support circle parameters. That is: the center position
|
|
|
|
|
|
+An arc is defined by its support circle parameters, that is, the center position
|
|
and the radius. The arc itself is then defined by the angle it starts from
|
|
and the radius. The arc itself is then defined by the angle it starts from
|
|
-and the angle at which it stops. These are the 4 parameters that we have to provide to our drawing.
|
|
|
|
|
|
+and the angle at which it stops. These are the 4 arguments that we have to provide to our drawing function.
|
|
We'll also provide the color value, so we can draw the arc in different colors if we wish.
|
|
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 from one to the following one. As you can imagine, the more points your shape is made of,
|
|
|
|
|
|
+Basically, drawing a shape on the screen requires it to be decomposed into a certain number of points
|
|
|
|
+linked from one to the next. As you can imagine, the more points your shape is made of,
|
|
the smoother it will appear, but the heavier it will also be in terms of processing cost. In general,
|
|
the smoother it will appear, but the heavier it will also 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
|
|
if your shape is huge (or in 3D, close to the camera), it will require more points to be drawn without
|
|
it being angular-looking. On the contrary, if your shape is small (or in 3D, far from the camera),
|
|
it being angular-looking. On the contrary, if your 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)*.
|
|
|
|
|
|
+you may decrease its number of points to save processing costs; this is known as *Level of Detail (LoD)*.
|
|
In our example, we will simply use a fixed number of points, no matter the radius.
|
|
In our example, we will simply use a fixed number of points, no matter the radius.
|
|
|
|
|
|
.. tabs::
|
|
.. tabs::
|
|
@@ -174,7 +174,7 @@ In our example, we will simply use a fixed number of points, no matter the radiu
|
|
var nb_points = 32
|
|
var nb_points = 32
|
|
var points_arc = PoolVector2Array()
|
|
var points_arc = PoolVector2Array()
|
|
|
|
|
|
- for i in range(nb_points+1):
|
|
|
|
|
|
+ for i in range(nb_points + 1):
|
|
var angle_point = deg2rad(angle_from + i * (angle_to-angle_from) / nb_points - 90)
|
|
var angle_point = deg2rad(angle_from + i * (angle_to-angle_from) / nb_points - 90)
|
|
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
|
|
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
|
|
|
|
|
|
@@ -200,18 +200,18 @@ In our example, we will simply use a fixed number of points, no matter the radiu
|
|
|
|
|
|
|
|
|
|
Remember the number of points our shape has to be decomposed into? We fixed this
|
|
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
|
|
|
|
-PoolVector2Array, which is simply an array of Vector2.
|
|
|
|
|
|
+number in the ``nb_points`` variable to a value of ``32``. Then, we initialize an empty
|
|
|
|
+``PoolVector2Array``, which is simply an array of ``Vector2``s.
|
|
|
|
|
|
The next step consists of computing the actual positions of these 32 points that
|
|
The next step consists of computing the actual positions of these 32 points that
|
|
compose an arc. This is done in the first for-loop: we iterate over the number of
|
|
compose an arc. This is done in the first for-loop: we iterate over the number of
|
|
points for which we want to compute the positions, plus one to include the last point.
|
|
points for which 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.
|
|
We first determine the angle of each point, between the starting and ending angles.
|
|
|
|
|
|
-The reason why each angle is reduced by 90° is that we will compute 2D positions
|
|
|
|
|
|
+The reason why each angle is decreased by 90° is that we will compute 2D positions
|
|
out of each angle using trigonometry (you know, cosine and sine stuff...). However,
|
|
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)
|
|
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 12 o'clock. So we reduce
|
|
|
|
|
|
+starts at 3 o'clock, although we want to start counting at 12 o'clock. So we decrease
|
|
each angle by 90° in order to start counting from 12 o'clock.
|
|
each angle by 90° in order to start counting from 12 o'clock.
|
|
|
|
|
|
The actual position of a point located on a circle at angle 'angle' (in radians)
|
|
The actual position of a point located on a circle at angle 'angle' (in radians)
|
|
@@ -225,17 +225,17 @@ Finally, we insert the point in the PoolVector2Array which was previously define
|
|
Now, we need to actually draw our points. As you can imagine, we will not simply
|
|
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.
|
|
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 drew
|
|
We could have computed every point ourselves using the previous method, and drew
|
|
-it one by one. But this is too complicated and inefficient (except if explicitly needed).
|
|
|
|
-So, we simply draw lines between each pair of points. Unless the radius of our
|
|
|
|
|
|
+it one by one. But this is 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 big, the length of each line between a pair of points will
|
|
support circle is 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
|
|
|
|
|
|
+never be long enough to see them. If that were to happen, we would simply need to
|
|
increase the number of points.
|
|
increase the number of points.
|
|
|
|
|
|
-Draw the arc on screen
|
|
|
|
-^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
+Draw the arc on the screen
|
|
|
|
+^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
-We now have a function that draws stuff on the screen:
|
|
|
|
-It is time to call in the _draw() function.
|
|
|
|
|
|
+We now have a function that draws stuff on the screen;
|
|
|
|
+it is time to call it inside the _draw() function:
|
|
|
|
|
|
.. tabs::
|
|
.. tabs::
|
|
|
|
|
|
@@ -270,7 +270,7 @@ Arc polygon function
|
|
|
|
|
|
We can take this a step further and not only write a function that draws the plain
|
|
We can take this a step further and not only write a function that draws the plain
|
|
portion of the disc defined by the arc, but also its shape. The method is exactly
|
|
portion of the disc defined by the arc, but also its shape. The method is exactly
|
|
-the same as previously, except that we draw a polygon instead of lines:
|
|
|
|
|
|
+the same as before, except that we draw a polygon instead of lines:
|
|
|
|
|
|
.. tabs::
|
|
.. tabs::
|
|
.. code-tab:: gdscript GDScript
|
|
.. code-tab:: gdscript GDScript
|
|
@@ -281,7 +281,7 @@ the same as previously, except that we draw a polygon instead of lines:
|
|
points_arc.push_back(center)
|
|
points_arc.push_back(center)
|
|
var colors = PoolColorArray([color])
|
|
var colors = PoolColorArray([color])
|
|
|
|
|
|
- for i in range(nb_points+1):
|
|
|
|
|
|
+ for i in range(nb_points + 1):
|
|
var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90)
|
|
var angle_point = deg2rad(angle_from + i * (angle_to - angle_from) / nb_points - 90)
|
|
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
|
|
points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
|
|
draw_polygon(points_arc, colors)
|
|
draw_polygon(points_arc, colors)
|
|
@@ -310,8 +310,8 @@ the same as previously, except that we draw a polygon instead of lines:
|
|
Dynamic custom drawing
|
|
Dynamic custom drawing
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
|
-Alright, we are now able to draw custom stuff on screen. However, it is static:
|
|
|
|
-Let's make this shape turn around the center. The solution to do this is simply
|
|
|
|
|
|
+All right, we are now able to draw custom stuff on the screen. However, it is 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,
|
|
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
|
|
we will simply increment them by 50. This increment value has to remain
|
|
constant or else the rotation speed will change accordingly.
|
|
constant or else the rotation speed will change accordingly.
|
|
@@ -364,7 +364,7 @@ calls _draw(). This way, you can control when you want to refresh the frame.
|
|
angle_from += rotation_angle
|
|
angle_from += rotation_angle
|
|
angle_to += rotation_angle
|
|
angle_to += rotation_angle
|
|
|
|
|
|
- # We only wrap angles if both of them are bigger than 360
|
|
|
|
|
|
+ # We only wrap angles when both of them are bigger than 360.
|
|
if angle_from > 360 and angle_to > 360:
|
|
if angle_from > 360 and angle_to > 360:
|
|
angle_from = wrap(angle_from, 0, 360)
|
|
angle_from = wrap(angle_from, 0, 360)
|
|
angle_to = wrap(angle_to, 0, 360)
|
|
angle_to = wrap(angle_to, 0, 360)
|
|
@@ -384,7 +384,7 @@ calls _draw(). This way, you can control when you want to refresh the frame.
|
|
_angleFrom += _rotationAngle;
|
|
_angleFrom += _rotationAngle;
|
|
_angleTo += _rotationAngle;
|
|
_angleTo += _rotationAngle;
|
|
|
|
|
|
- // We only wrap angles if both of them are bigger than 360
|
|
|
|
|
|
+ // We only wrap angles when both of them are bigger than 360.
|
|
if (_angleFrom > 360 && _angleTo > 360)
|
|
if (_angleFrom > 360 && _angleTo > 360)
|
|
{
|
|
{
|
|
_angleFrom = Wrap(_angleFrom, 0, 360);
|
|
_angleFrom = Wrap(_angleFrom, 0, 360);
|
|
@@ -422,10 +422,10 @@ Let's run!
|
|
It works, but the arc is rotating insanely fast! What's wrong?
|
|
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 it can.
|
|
The reason is that your GPU is actually displaying the frames as fast as it can.
|
|
-We need to "normalize" the drawing by this speed. To achieve, we have to make
|
|
|
|
|
|
+We need to "normalize" the drawing by this speed; to achieve that, we have to make
|
|
use of the 'delta' parameter of the _process() function. 'delta' contains the
|
|
use of the 'delta' parameter of the _process() function. 'delta' contains the
|
|
time elapsed between the two last rendered frames. It is generally small
|
|
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
|
|
|
|
|
|
+(about 0.0003 seconds, but this depends on your hardware), so using 'delta' to
|
|
control your drawing ensures that your program runs at the same speed on
|
|
control your drawing ensures that your program runs at the same speed on
|
|
everybody's hardware.
|
|
everybody's hardware.
|
|
|
|
|
|
@@ -440,7 +440,7 @@ smaller value, which directly depends on the rendering speed.
|
|
angle_from += rotation_angle * delta
|
|
angle_from += rotation_angle * delta
|
|
angle_to += rotation_angle * delta
|
|
angle_to += rotation_angle * delta
|
|
|
|
|
|
- # we only wrap angles if both of them are bigger than 360
|
|
|
|
|
|
+ # We only wrap angles when both of them are bigger than 360.
|
|
if angle_from > 360 and angle_to > 360:
|
|
if angle_from > 360 and angle_to > 360:
|
|
angle_from = wrap(angle_from, 0, 360)
|
|
angle_from = wrap(angle_from, 0, 360)
|
|
angle_to = wrap(angle_to, 0, 360)
|
|
angle_to = wrap(angle_to, 0, 360)
|
|
@@ -453,7 +453,7 @@ smaller value, which directly depends on the rendering speed.
|
|
_angleFrom += _rotationAngle * delta;
|
|
_angleFrom += _rotationAngle * delta;
|
|
_angleTo += _rotationAngle * delta;
|
|
_angleTo += _rotationAngle * delta;
|
|
|
|
|
|
- // We only wrap angles if both of them are bigger than 360
|
|
|
|
|
|
+ // We only wrap angles when both of them are bigger than 360.
|
|
if (_angleFrom > 360 && _angleTo > 360)
|
|
if (_angleFrom > 360 && _angleTo > 360)
|
|
{
|
|
{
|
|
_angleFrom = Wrap(_angleFrom, 0, 360);
|
|
_angleFrom = Wrap(_angleFrom, 0, 360);
|