How to Label the points defined in Path2D through Code

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

Hi,
Please help me in finding out how to access and label the points described in Path2D when we our Sprite goes from one point to another. I want to say that the Sprite has reached Point A. At _ready all the Labels are Label.hide() status. When the Sprite reaches a Label, we say Label.show(). Here is my Node Tree:
Path2D
PathFollow2D
Animation Player
Sprite

:bust_in_silhouette: Reply From: njamster

Here you go (commented version further down below):

onready var curve  = $Path2D.curve
onready var follow = $Path2D/PathFollow2D
onready var sprite = $Sprite

var point_offsets = []

func _ready():
	var points = [Vector2(100, 100), Vector2(400, 400), Vector2(800, 100), Vector2(1000, 100)]
	for point in points:
		curve.add_point(point)

	for i in curve.get_point_count():
		var point = curve.get_point_position(i)
		var offset = curve.get_closest_offset(point)
		point_offsets.push_back(offset)

		var point_label = Label.new()
		point_label.name = "Label" + str(i)
		point_label.rect_position = point
		point_label.text = str(point)
		point_label.visible = false
		add_child(point_label)

func _physics_process(delta: float) -> void:
	follow.offset += 5
	sprite.position = follow.position

	for i in point_offsets.size():
		var point_offset = point_offsets[i]
		if follow.offset > point_offset:
			get_node("Label" + str(i)).show()
		else:
			get_node("Label" + str(i)).hide()

Version with comments:

# some node paths that we use repeatedly in this script - _yours may differ_!
onready var curve  = $Path2D.curve
onready var follow = $Path2D/PathFollow2D
onready var sprite = $Sprite

# an array to store the offset of each point on the path, populated in _ready
var point_offsets = []

func _ready():
	# plug-in your point coordinates here - or remove the next three lines and create the path in the editor
	var points = [Vector2(100, 100), Vector2(400, 400), Vector2(800, 100), Vector2(1000, 100)]
	for point in points:
		curve.add_point(point)

	# for each point of the curve...
	for i in curve.get_point_count():
		# ... get the point's position...
		var point = curve.get_point_position(i)
		# ... and it's offset from the start of the curve...
		var offset = curve.get_closest_offset(point)
		# ... then store this information for later use
		point_offsets.push_back(offset)

		# creates a label for each point...
		var point_label = Label.new()
		# ... named "Label1" for the 1st point, "Label2" for the 2nd and so on...
		point_label.name = "Label" + str(i)
		# ... places the label at the points position...
		point_label.rect_position = point
		# ... and makes it display the point coordinates...
		point_label.text = str(point)
		# ... but hides it by default...
		point_label.visible = false
		# ... then adds the label to the tree
		add_child(point_label)

		# if you prefer to add the labels in the editor, simply remove the last block!
		# however, make sure that the labels are named properly (Label1, Label2, ...)

# each physics-frame ...
func _physics_process(delta: float) -> void:
	# ... increase the offset from the start of the curve by 5...
	# (if this value becomes bigger than the curve's length, it will reset to 0)
	follow.offset += 5
	# ... and move the sprite's position along with it
	sprite.position = follow.position

	# for each point of the curve...
	for i in point_offsets.size():
		# ... get its stored offset...
		var point_offset = point_offsets[i]

		# ... and compare it to the current offset:
		if follow.offset > point_offset:
			# if the current offset is bigger, show the label
			get_node("Label" + str(i)).show()
		else:
			# if the current offset is smaller, hide the label
			get_node("Label" + str(i)).hide()

Thanks for the huge effort and the detailed answer.
However, I am getting this error at runtime:
Invalid get index ‘curve’ (on base: ‘null instance’).

The organisation of my node is :

Path2D
   PathFollow2D
   AnimationPlayer
   Sprite

My code starts as:

extends Path2D

onready var curve = $Path2D.curve

(Rest same as illustrated by you)

I also get a warning :
errors(3,1): The member ‘curve’ already exists in parent class.

(I notice that your code does not extend Path2D. Does it matter? )

Line 3 is onready var curve = $Path2D.curve

ashish | 2020-09-21 19:03

I notice that your code does not extend Path2D. Does it matter?

Yes, it does. Sorry if that caused any confusion!

Invalid get index ‘curve’ (on base: ‘null instance’).

This error is telling you that you’re trying to access a property called index on a non-existent node. In your case that’s because the node the script is attached to already is the Path2D, thus that node doesn’t have a child-node $Path2D on its own.

The member ‘curve’ already exists in parent class.

This error is telling you that the class the script is extending (Path2D) already has a property called curve - so you’re not allowed to define it again!

So the solution is simple: just remove the third line. :slight_smile:

njamster | 2020-09-21 20:06

Thanks. It works now.
Except that:
follow = $Path2D/$PathFollow2D was giving a null instance.
Changed it to
follow = $PathFollow2D
and it worked.

Thanks again.

ashish | 2020-09-22 20:31

Hello NJMaster,
In your code, how do I change the font size of the programmatically generated Labels?
I inserted the following code, having seen it in some other thread:

var font = point_label.get_font(“string_name”, “”)
font.size = 30
point_label.add_font_override(“string_name”, font)

But when I run it, the following error is thrown:

Invalid set index ‘size’ (on base: ‘BitmapFont’) with value of type ‘int’.

Will be grateful if you could help me with the manipulation of the font size.

Thanks a lot.

ashish | 2020-09-27 14:19

That’s because the default font used by Labels is a BitmapFont, which does not support changing the font-size. What you want to use instead is a DynamicFont. The documentation includes an example:

var dynamic_font = DynamicFont.new()
dynamic_font.font_data = load("<PathToFont")
dynamic_font.size = 30
point_label.set("custom_fonts/font", dynamic_font)

Btw: It’s njamster, not njmaster :wink:

njamster | 2020-09-28 13:56

Thanks NJamster,
Works very well.
Is there a reason why we have _physics_process(delta) as opposed to _process(delta)?
Thanks again.

ashish | 2020-09-28 15:22

Is there a reason why we have _physics_process(delta) as opposed to _process(delta)?

Not really. The former will run at 60 FPS (unless you change the physics_fps in the Project Settings or you hardware isn’t good enough to render at 60 FPS), whereas _process will run as often as possible given the hardware you’re using, i.e. on a performant system you’ll travel the path much faster as on a weaker system.

However, the proper way to deal this would be using delta either way:

var UNITS_PER_SECONDS = 300

func _physics_process(delta: float) -> void:
    follow.offset += UNITS_PER_SECOND * delta

This guarantees the traveled path per second is always the same regardless of the number of frames the current machine is able to render which is what you’ll usually want in a game. But you could do the same using _process as well.

njamster | 2020-09-28 15:58

Thank you very much.
This helps a lot.

ashish | 2020-10-01 13:43