• 2D
  • Drawing an ARC is extremely slow

Hello,

I need to draw an arc for a node, so I'm using this code:

func _ready():
	for i in range(100 + 1):
		var angle_point = deg2rad(270 + i * 90 / 100 - 90)
		points_arc.push_back(Vector2(15, 15) + Vector2(cos(angle_point), sin(angle_point)) * 25)

func _draw():
	for index_point in range(100):
		draw_line(points_arc[index_point], points_arc[index_point + 1], Color(1, 1, 0), 5, true)

It's basically the code from https://docs.godotengine.org/en/stable/tutorials/2d/custom_drawing_in_2d.html but instead of calculating the points every time I moved it to ready.

However this code alone (executed on about 10 of those nodes) is enough to render the game a slideshow.

Is there any fast way to draw an arc? I wish there would be nodes to draw circles, arcs and such similar to ColorRect.

Read the whole tutorial through. You'll want to multiply by delta if you are running the update() in say the _process(delta) function so it wouldn't update as fast as possible but rather in sync with your frame rate. Now imagine you have 10 nodes all trying to draw something as fast(in other words as many times) as possible...

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 it can. 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 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 that your program runs at the same speed on everybody's hardware.

In our case, we simply need to multiply our rotation_angle 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_angle * delta
    angle_to += rotation_angle * delta

    # We only wrap angles when both of them are bigger than 360.
    if angle_from > 360 and angle_to > 360:
        angle_from = wrapf(angle_from, 0, 360)
        angle_to = wrapf(angle_to, 0, 360)
    update()

I would probably do what the tutorial does and create a custom function for most of that logic. If you want to update just once I guess you could do so from either a signal from a gui button or perhaps in response to a key press in _input() but I'd first try to just follow the tutorial and get it working as intended by the tutorial via _process(delta).

Thank you for the reply.

Maybe I'm missing some part, but I don't want to update the arc at all - just draw it once and let it stay there. I would not know what parameter to multiply by delta...

If it is just once then it should be fine in ready and draw alone. I wonder whats going on causing the redraws, assuming that really even is what is tanking the performance.

It is indeed caused by the drawing routine - if I comment out draw_line() the game runs at 100 fps again.

I found a different solution now (Line2D with the points from the calculation above) but I'd be curious to know why drawing just an arc slows it down by so much...

I've drawn circles before without issue, but I was only drawing 4 at once.

Here is the code I was using:

func _draw():
	draw_arc(position, radius, 0, 2 * PI, 128, Color(1, 1, 1), 3.0, true)

As an experiment, I made a project to draw a rotating spiral. In _draw(), I use draw_arc() to draw 450 five-degree arcs with increasing radii, and that's rotated continuously by an AnimationPlayer. It runs smoothly on my computer, but lags on Android.

Antialiased line drawing is slow because it can't make use of batching. Disable antialiasing in the draw_arc() call so that it can use batching again.