Need help with script-created Polygon2D being offset?

Godot Version

v4.1.1.stable.official [bd6af8e0e]

Question

I’m creating a dynamic area of effect system based on collision shapes blocking a Raycast2D and creating a Polygon2D with the returned Vector2D values:

extends Node2D

@onready var ray = $RayCast2D
@onready var raydius = $RayCast2D/defaultWidth
var x = 0

func _ready():
	raydius.set_position(ray.target_position)
	var newPackedPolygon = genCircle(20.0)
	var newPolygon = Polygon2D.new()
	newPolygon.polygon = newPackedPolygon
	self.add_child(newPolygon)
	newPolygon.set_position(self.get_position())
	print(newPolygon.get_position())
	newPolygon.name = "province"

func genCircle(radius: float):
	var polygon: PackedVector2Array
	var vector = Vector2(0,0)
	for i in range(0, 360):
		ray.set_global_rotation_degrees(i)
		ray.force_raycast_update()
		var collide = ray.get_collider()
		if collide != null:
			vector = ray.get_collision_point()
		else:
			vector = raydius.get_global_position()
		polygon.append(vector)
	return polygon

This works as intended when the object the script is connected to is at position (0, 0) but gets weirdly offset whenever it isn’t at that position. I’m imagining it has something to do with translating the raycast vectors into a polygon, but I really have no idea.

Screenshots of results:

The method set_position() is just the setter for position, which is the local position of the object, relative to its parent. So when you set the position of newPolygon to the position of its parent, it actually sets its own local position relative to the parent (the node the script is on) to the local position of the parent, effectively doubling its global position.

If you want to get/set the global position of an object, you can use either the property global_position or the methods get_global_position() and set_global_position().

In this case you can just remove the line
newPolygon.set_position(self.get_position())
because new nodes are automatically placed at the global position of their parent, but if you want to keep it (e.g. for readability) it should be
newPolygon.global_position = self.global_position
or
newPolygon.set_global_position(self.get_global_position)

1 Like

Thank you, I removed that line as it was just doing nothing, however that wasn’t the solution. I’m really confused at what is even wrong tbh, the global position of the polygon is correct, (25, -5), and even the coordinates for each vector are correct.

When checking them using remote view and finding the coordinates in the editor, I found that they are correct coordinates, just for some reason the polygon is still in the wrong place?

Edit:
After more testing I found that the vector coordinates are local, not global, which is why assigning a global position means that it is offset.

Oh my bad, I think the problem is still the global position of the new Polygon2D, but that it should actually be at (0, 0), because the points in its polygon property are relative to the node’s position and the collision point of the RayCast2D, which is directly used as a point, is in the global coordinate system.

If the node the script is on moves around a lot, it’s probably best to just reparent the polygon to a static node (e.g. the root node of the scene), because otherwise its position (or it’s polygon) would constantly need to be adjusted to remain fixed.

1 Like

Ok, so I’ve been working on this and found a solution to keep the polygon at the right position, however it seems the final hurdle is getting the vectors of the collision points to be correct.

Currently, I know that .get_collision_point() is returning the global position of the collision and that the position it returns is correct. Yet, when I try the same tactic of converting it to a local position by subtracting its global position from an origin global position, the vectors are somehow flipped?

extends Node2D

@onready var ray = $RayCast2D
@onready var raydius = $RayCast2D/defaultWidth
@onready var newPolygon = Polygon2D.new()
var x = 0

func _ready():
	print(self.global_position)
	raydius.position = ray.target_position
	var newPackedPolygon = genCircle(20.0)
	newPolygon.polygon = newPackedPolygon
	self.add_child(newPolygon)
	newPolygon.name = "province"

func genCircle(radius: float):
	var polygon: PackedVector2Array
	var vector = Vector2(0,0)
	for i in range(0, 360):
		ray.set_global_rotation_degrees(i)
		ray.force_raycast_update()
		var collide = ray.get_collider()
		if collide != null:
			print("minus ray collide: " + str(self.global_position - ray.get_collision_point())) #supposedly local positions of each collision point
			$MeshInstance2D.position = self.global_position 
 - ray.get_collision_point() #added a meshinstance so i could track coordinates in remote view
			vector = self.global_position - ray.get_collision_point()
		else:
			vector = self.global_position - raydius.global_position
		polygon.append(vector)
		#print(raydius.global_position)
		#print(vector)
	ray.set_global_rotation_degrees(0)
	return polygon

Just solved it, turns out that because of the way I calculated the local vector positions, by subtracting self.global_position from ray.get_collision_point(), I had to append a negative vector to the polygon in order to adjust it (I’m guessing).

Working code (note the polygon.append(-vector)):

extends Node2D

@onready var ray = $RayCast2D
@onready var raydius = $RayCast2D/defaultWidth
@onready var newPolygon = Polygon2D.new()
var x = 0

func _ready():
	raydius.position = ray.target_position
	var newPackedPolygon = genCircle(20.0)
	newPolygon.polygon = newPackedPolygon
	self.add_child(newPolygon)
	newPolygon.name = "province"

func genCircle(radius: float):
	var polygon: PackedVector2Array
	var vector = Vector2(0,0)
	for i in range(0, 360):
		ray.set_global_rotation_degrees(i)
		ray.force_raycast_update()
		var collide = ray.get_collider()
		if collide != null:
			vector = self.global_position - ray.get_collision_point()
		else:
			vector = self.global_position - raydius.global_position
		polygon.append(-vector)
	ray.set_global_rotation_degrees(0)
	return polygon