Custom drawing in 2D¶
Introduction¶
Godot has nodes to draw sprites, polygons, particles, and all sorts of stuff. For most cases, this is enough. If there's no node to draw something specific you need, you can make any 2D node (for example, Control or Node2D based) draw custom commands.
Custom drawing in a 2D node is really useful. Here are some use cases:
Drawing shapes or logic that existing nodes can't do, such as an image with trails or a special animated polygon.
Visualizations that are not that compatible with nodes, such as a tetris board. (The tetris example uses a custom draw function to draw the blocks.)
Drawing a large number of simple objects. Custom drawing avoids the overhead of using a large number of nodes, possibly lowering memory usage and improving performance.
Making a custom UI control. There are plenty of controls available, but when you have unusual needs, you will likely need a custom control.
Drawing¶
Add a script to any CanvasItem
derived node, like Control or
Node2D. Then override the _draw()
function.
extends Node2D
func _draw():
# Your draw commands here
pass
public override void _Draw()
{
// Your draw commands here
}
Draw commands are described in the 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,
call CanvasItem.queue_redraw()
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 (Texture) 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.
queue_redraw() # Trigger a redraw of the node.
func _draw():
draw_texture(texture, Vector2())
using Godot;
public partial class MyNode2D : Node2D
{
private Texture _texture;
public Texture Texture
{
get
{
return _texture;
}
set
{
_texture = value;
QueueRedraw();
}
}
public override void _Draw()
{
DrawTexture(_texture, new Vector2());
}
}
In some cases, it may be desired to draw every frame. For this,
call queue_redraw()
from the _process()
callback, like this:
extends Node2D
func _draw():
# Your draw commands here
pass
func _process(delta):
queue_redraw()
using Godot;
public partial class CustomNode2D : Node2D
{
public override void _Draw()
{
// Your draw commands here
}
public override void _Process(double delta)
{
QueueRedraw();
}
}
Coordinates¶
The drawing API uses the CanvasItem's coordinate system, not necessarily pixel coordinates. Which means it uses the coordinate space created after applying the CanvasItem's transform. Additionally, you can apply a custom transform on top of it by using draw_set_transform or draw_set_transform_matrix.
When using draw_line
, you should consider the width of the line.
When using a width that is an odd size, the position should be shifted
by 0.5
to keep the line centered as shown below.
func _draw():
draw_line(Vector2(1.5, 1.0), Vector2(1.5, 4.0), Color.GREEN, 1.0)
draw_line(Vector2(4.0, 1.0), Vector2(4.0, 4.0), Color.GREEN, 2.0)
draw_line(Vector2(7.5, 1.0), Vector2(7.5, 4.0), Color.GREEN