I’m trying to make a compass control that points north when the user turns around. Everything is working, except I noticed that the graphics output by _draw is one step behind.
I have a function that users of the Compass control can use to set the north direction which then forces a redraw with queue_redraw
func _draw() -> void:
var scale_x = size.x / texture.get_width()
var scale_y = size.y / texture.get_height()
print( "Compass: Redraw with north as ", rad_to_deg( north_angle ))
label.text = str( rad_to_deg( north_angle )) + "º"
draw_set_transform( Vector2.ZERO, 0, Vector2( scale_x, scale_y ))
offset_transform_rotation = -north_angle
draw_texture( texture, Vector2.ZERO )
Additional Information after some discussion here
The print and label.text updates are for debugging purposes only. The issue existed before they were added.
The issue happens in a real game prototype where the rest of the game window continues updating normally, including other UI controls, responses to input, etc. This is not an issue that is only noticeable while stepping through the code.
Using offset_transform_rotation makes the compass rotate around its centre, if I put the rotation into the draw_set_transform, the rotation is around the control’s top left corner.
However, when I put the rotation into draw_set_transform the UI updates happen when expected, they just happen to be rotating around the wrong point.
Problem
label.text has the correct angle in it, but the UI shows the previous angle. If I call the set_north after _draw with the same value, the UI updates properly.
in the sample it is called like this from the root Node2D:
func _process( _delta: float ) -> void:
# pressing left rotates anticlockwise
if Input.is_action_just_pressed("ui_left"):
north_angle += PI / 4 # 45º
print( "Left Pressed. Angle now: ", rad_to_deg( north_angle ))
compass.set_north( north_angle )
# pressing right rotates clockwise
if Input.is_action_just_pressed("ui_right"):
north_angle -= PI / 4 # 45º
print( "Right Pressed. Angle now: ", rad_to_deg( north_angle ))
compass.set_north( north_angle )
# pressing up resets the compass to north
if Input.is_action_just_pressed("ui_up"):
north_angle = 0.00
print( "Up Pressed. Angle now: ", rad_to_deg( north_angle ))
compass.set_north( north_angle )
# don't change the angle - just redraw the compass
if Input.is_action_just_pressed("ui_down"):
print( "Down Pressed. Angle STILL: ", rad_to_deg( north_angle ))
compass.set_north( north_angle )
And here’s the logging:
Compass: Redraw with north as 0.0
Left Pressed. Angle now: 45.0
Compass: North Angle now 45.0
Compass: Redraw with north as 45.0
Left Pressed. Angle now: 90.0
Compass: North Angle now 90.0
Compass: Redraw with north as 90.0
Left Pressed. Angle now: 135.0
Compass: North Angle now 135.0
Compass: Redraw with north as 135.0
Up Pressed. Angle now: 0.0
Compass: North Angle now 0.0
Compass: Redraw with north as 0.0
Down Pressed. Angle STILL: 0.0
Compass: North Angle now 0.0
Compass: Redraw with north as 0.0
Note that the label always agrees with the logged value, but the compass itself is wrong. For example, with the above, the compass points:
rendering for the changes in _draw() happens different than rendering for a control node. Also queue_redraw() doesn’t mean it’ll redraw just when the function runs. It’ll run _draw() later in the process frame.
So, just because you added label.text = str( rad_to_deg( north_angle )) + "º" in _draw() function doesn’t mean both draw related rendering and label’s rendering will happen at the same time or in the order you’d thik it would.
As a general rule, you should have only draw related items (what to draw) on _draw(), not any state change/logic for a node.
I’d move label.text = str( rad_to_deg( north_angle )) + "º" from _draw() to set_north() function. Try that and see if it changes anything.
It took me ages to get the compass to rotate around it’s centre point rather than the control’s origin.
However, I did change it to check it, and at least the update works now, but the animation is wrong.
The documentation of what the offset_ stuff does is very light. I assumed it meant something about rotating around the offset position (that’s certainly what happens), but now it seems it also means offset in time?
Edit:
Further reading suggests that offset refers to the offset in the controls offsets and anchors. So that explains why the rotation works when I use offset, because the default offset is the centre.
The question is back to why doing this delays the drawing.
Its should. but the whole point of this is that it doesn’t. I’m not stepping through the code. The code is running at full speed. The game keeps working, my avatar moves when I move it, enemies update, UIs update. Everything goes through thousands of frames, but the compass only changes (to the previous setting) when I move it again.
That’s fine. I added the label as part of the debugging process just to make it clear that the state inside the compass was what I thought it should be, even though the compass pointed incorrectly. As such, moving or removing the Label doesn’t change the compasses behaviour, it just makes the error less obvious because now you have to watch the messages being printed out.
The offset_* properties are useful for applying visual transformations and animations to UI controls without interfering with the parent Container’s layout logic.
Before offset_* introduced, you couldn’t change any transform properties of a control node if it’s a child of a container. Because Container is responsible for the transform of its children.
The control keeps its regular transform and layout properties, while the offset transform is applied on top of them. This lets you animate things like rotation, scale, or position without changing the properties the parent Container uses to lay out its children.
I don’t see it says there anywhere in the link you shared.
To be fair, I really don’t understand why you are using _draw for this.
You can simply create a Control Node with container. And change its transform properties when buttons are pressed, no need to use _draw here.
func set_north( angle : float ) -> void:
north_angle = angle
print( "Compass: North Angle now ", rad_to_deg( north_angle ) )
#define your texture_rect: TextureRect for the compass somewhere on your script..
#and make sure offset transform is enabled on the property panel of the texture_rect node
texture_rect.offset_transform_rotation = -north_angle