Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

2D 中的自定义绘图

前言

Godot has nodes to draw sprites, polygons, particles, text, and many other common game development needs. However, if you need something specific not covered with the standard nodes you can make any 2D node (for example, Control or Node2D-based) draw on screen using custom commands.

2D 节点中的自定义绘制非常有用。下面是一些用例:

  • 绘制现有节点类型无法完成的形状或逻辑,例如带有轨迹或特殊动态多边形的图像。

  • Drawing a large number of simple objects, such as a grid or a board for a 2d game. Custom drawing avoids the overhead of using a large number of nodes, possibly lowering memory usage and improving performance.

  • 制作自定义的 UI 控件,以满足很多可用的控件之外的特别需求。

绘制

Add a script to any CanvasItem derived node, like Control or Node2D. Then override the _draw() function.

extends Node2D

func _draw():
    pass  # Your draw commands here.

Draw commands are described in the CanvasItem class reference. There are plenty of them and we will see some of them in the examples below.

更新

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 variable 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, where we have a texture variable that can be modified at any time, and using a setter, it forces a redraw of the texture when modified:

extends Node2D

@export var texture : Texture2D:
    set(value):
        texture = value
        queue_redraw()

func _draw():
    draw_texture(texture, Vector2())

To see it in action, you can set the texture to be the Godot icon on the editor by dragging and dropping the default icon.svg from the FileSystem tab to the Texture property on the Inspector tab. When changing the Texture property value while the previous script is running, the texture will also change automatically.

In some cases, we may need to redraw every frame. For this, call queue_redraw from the _process method, like this:

extends Node2D

func _draw():
    pass  # Your draw commands here.

func _process(_delta):
    queue_redraw()

Coordinates and line width alignment

The drawing API uses the CanvasItem's coordinate system, not necessarily pixel coordinates. This means _draw() 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 of the start and end points should be shifted by 0.5 to keep the line centered, as shown below.

../../_images/draw_line.png
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, 3.0)

The same applies to the draw_rect method with filled = false.

../../_images/draw_rect.png
func _draw():
    draw_rect(Rect2(1.0, 1.0, 3.0, 3.0), Color.GREEN)
    draw_rect(Rect2(5.5, 1.5, 2.0, 2.0), Color.GREEN, false, 1.0)
    draw_rect(Rect2(9.0, 1.0, 5.0, 5.0), Color.GREEN)
    draw_rect(Rect2(16.0, 2.0, 3.0, 3.0), Color.GREEN, false, 2.0)

抗锯齿绘图

Godot 在 draw_line 方法中提供参数来启用抗锯齿功能,但并非所有自定义绘图方法都提供这个 抗锯齿(antialiased) 参数。

对于不提供 antialiased 参数的自定义绘图方法,你可以启用 2D MSAA,这会影响整个视口的渲染。这个功能(2D MSAA)提供了高质量的抗锯齿,但性能成本更高,而且只适用于特定元素。详情见 2D 抗锯齿

Here is a comparison of a line of minimal width (width=-1) drawn with antialiased=false, antialiased=true, and antialiased=false with 2D MSAA 2x, 4x, and 8x enabled.

../../_images/draw_antialiasing_options.webp

工具

Drawing your own nodes might also be desired while running them in the editor. This can be used as a preview or visualization of some feature or behavior.

To do this, you can use the tool annotation on both GDScript and C#. See the example below and 在编辑器中运行代码 for more information.

Example 1: drawing a custom shape

We will now use the custom drawing functionality of the Godot Engine to draw something that Godot doesn't provide functions for. We will recreate the Godot logo but with code- only using drawing functions.

You will have to code a function to perform this and draw it yourself.

备注

The following instructions use a fixed set of coordinates that could be too small for high resolution screens (larger than 1080p). If that is your case, and the drawing is too small consider increasing your window scale in Menu > Project > Project settings > display/window/stretch/scale to adjust the project to a higher resolution (a 2 or 4 scale tends to work well).

Drawing a custom polygon shape

While there is a dedicated node to draw custom polygons ( Polygon2D), we will use in this case exclusively lower level drawing functions to combine them on the same node and be able to create more complex shapes later on.

First, we will define a set of points -or X and Y coordinates- that will form the base of our shape:

extends Node2D

var coords_head : Array = [
    [ 22.952, 83.271 ],  [ 28.385, 98.623 ],
    [ 53.168, 107.647 ], [ 72.998, 107.647 ],
    [ 99.546, 98.623 ],  [ 105.048, 83.271 ],
    [ 105.029, 55.237 ], [ 110.740, 47.082 ],
    [ 102.364, 36.104 ], [ 94.050, 40.940 ],
    [ 85.189, 34.445 ],  [ 85.963, 24.194 ],
    [ 73.507, 19.930 ],  [ 68.883, 28.936 ],
    [ 59.118, 28.936 ],  [ 54.494, 19.930 ],
    [ 42.039, 24.194 ],  [ 42.814, 34.445 ],
    [ 33.951, 40.940 ],  [ 25.637, 36.104 ],
    [ 17.262, 47.082 ],  [ 22.973, 55.237 ]
]

This format, while compact, is not the one that Godot understands to draw a polygon. In a different scenario we could have to load these coordinates from a file or calculate the positions while the appl