Setting the position of ANY node in gdscript

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Timshark

Hi
I fell in love with Godot when I started using it, but now the inconveniences shows.
Since anything is a node in godot, why isnt there an easy way to position any node on the screen in script, whatever kind it is…
why doesnt every node have a global x,y (2d) and and a global x,y,z - or a local x,y and x,y,z

For example :
If I want to change ONLY the x value (position) of a richtextlabel in code - how do I do this?
The same for a label…
It seems that I have to use rect property or something…

Tim

:bust_in_silhouette: Reply From: kidscancode

A “Node” is a generic object that has no positional information at all. Inheriting from Node are a variety of different types that may or may not have anything to do with being positioned on the screen. Tween, AnimationPlayer, etc. - these have no need for such a thing.

RichTextLabel is a Control node, and belongs to the family of nodes that are designed for building UIs. They have a rect_position property, which you may use but typically won’t, since Controls also have Containers for controlling layout and positioning. If you’re manually setting Control coordinates, you’re probably using them wrong.

Node2D serves an entirely different purpose - creating game objects that operate in 2D space.

Ok. Thank you.

My scenario is that I use Multiple labels/richtextlabels as objects that can form sentences.
The content (text) of these labels are procedurally created. And I move them around in code.
I also want to manipulate the labels when the mouse is hovering over them or the mouse clicks them. (change the text color etc.)

So I guess I want to move them as sprites but also have the ability to change the text and the text color in them, and maybe use bbcode on them.

What kind of parent node should I put a richtextlabel inside to achieve this kind of control in code?

TIm

Timshark | 2021-05-04 17:26

:bust_in_silhouette: Reply From: timothybrentwood

Yep nodes that are derived from the Control class are meant to have their positions and sizes more or less determined at run time. They are meant for UI elements. You generally give a parent Control node (typically a container derived node) a rect_position and rect_min_size then add children to it. For the children you really only want to set their rect_min_size either in the editor or in code and they’ll have their positions and sizes determined at run time based on their parent’s Container type, Size Flags and Grow Direction.

:bust_in_silhouette: Reply From: Timshark

I will repeat my scenario i wrote in a comment to kidscancode:

"My scenario is that I use Multiple labels/richtextlabels as objects that can form sentences. The content (text) of these labels are procedurally created. And I move them around in code. I also want to manipulate the labels when the mouse is hovering over them or the mouse clicks them. (change the text color etc.)

So I guess I want to move them as sprites manually but also have the ability to change the text and the text color in them, and maybe use bbcode on them.
What kind of parent node should I put a richtextlabel inside to achieve this kind of control in code?"

my solution right now:
I made a Node2d object with the richtextlabel as a child.
Then I can use position.x or position.y to control the Node2d.
Very simple, but Im new to godot, so I was confused.

Tim

:bust_in_silhouette: Reply From: Wakatta

Came across projects that needed to move all objects UI/3D/Drag-&-Drop 2D~3D and vise-versa so understand the need.

To set the x value on a RichTextLabel you can use either $RichTextLabel.rect_global_position.x = 5 or $RichTextLabel.rect_position.x = 5

The following code is part of a base class that gets extended but you can easily add it to a Singleton and call as needed.
E.g Singleton.set_position($RichTextLabel, Vector2(5, 0))

Pseudo code

func get_position(node):
	if node is Control:
		return node.rect_global_position
	elif node is Spatial:
		return node.global_transform.origin
	elif node is Node2D:
		return node.position
	else:
		assert(false, "%s nodes contain no position" % node.get_class())

func set_position(node, position):
	assert(position is Vector2 or position is Vector3, "Invalid position type")
	if node is Control or node is Node2D:
		if position is Vector3:
			position = Vector2(position.x, position.z)
		if node is Control:
			node.rect_global_position = position
		elif node is Node2D:
			node.position = position
	elif node is Spatial:
		if position is Vector2:
			position = Vector3(position.x, 0, position.y)
		node.global_transform.origin = position
	else:
		assert(false, "%s nodes contain no position to set" % node.get_class())

if you want a 3D node’s position in relation to the viewport screen you’ll have to project or unproject a ray and that node would have to be a CollisionObject

For the Spatial sections of the above code you can add

func raycast_from_mouse(position, mask):
	var camera = get_viewport().get_camera()
	var rayLength = 1000
	var rayStart = camera.project_ray_origin(position)
	var rayEnd = rayStart + camera.project_ray_normal(position) * rayLength
	var spaceState = camera.get_world().direct_space_state
	return spaceState.intersect_ray(rayStart, rayEnd, [], mask)

func get_position(node : Node):
    if node is CollisionObject:
	    var mouse_pos = get_viewport().get_mouse_position()
	    var hit = raycast_from_mouse(mouse_pos, node.collision_layer)
	    if hit.size():
	        if hit.collider == node:
                return hit.position

Thank you so much for this very helpful answer, Wakatta.

I DO blend 2d and 3d so this is very welcome. The solution to moving labels and richtextlabels was much easier than I thought.
As you wrote - there is no need to put them inside another node. You can change their 2d position manually in code with “rect _ position.x” (or y) or “rect _ global_position.x” (or y)
I also discovered that label and richtextlabel has mouse_entered() and mouse_exited() signals. I guess I didnt do my research good enough. (I come from python so Im used to coding everything manually.)

the raycasting will be useful when I put this section into my 3d world later.

The odd thing with the other answers I got, Is that they suggested Im using richtextlabel and labels wrong just because I want to manually manipulate the position of gui objects in code…

well, I cant see anything wrong in this.

Thank you again.

Timshark | 2021-05-05 16:51

Control nodes are mostly used for GUI and their size and position get set automatically by their parents using margins and anchors.

Don’t mean to sound redundant but Containers were made to sort and arrange it’s children nodes in an automated way eliminating the need to manually reposition or keep track of their location. Hence it coming off as odd.

So for a list generation a VBoxContainer would suffice and you’d just have to manipulate the placement index of the children nodes with functions such as raise(), add_child_below(), move_child(), replace_by()

Wakatta | 2021-05-05 20:26

Not redundant at all. I learn more with every answer.

I´ll check out vboxcontainer!

Timshark | 2021-05-05 20:56