Quick explanation: This script was previously made by Theraot who graciously created this script for me a few years a go, Today I attempted to update it and improve it! it work-ish apart from these weird issues I am met with.
Issue 1: A issue I had originally with the script was that each segment was not equal length this was because of the mouse speed, the player could move the mouse very fast and it would create huge segments, I have attempted to fix this issue by first calculating how many segments are needed, and I have tried creating a interpolated point for the new segment but it does not work it glitches out like its lagging behind.
var distance = Points[-2].distance_to(Mouse_Position)
# Check if the current distance is greater then segment distance
if distance > Segment_Distance:
var distanceDifference = distance - Segment_Distance
var segmentsToAdd = int(distanceDifference / Segment_Distance)
# Want to add segments for when the mouse is far away from the second to last point
# but I am not sure what to do, I attempted using a interpolated point
# but it glitches out
for i in range(segmentsToAdd):
var newPoint = Points[-2] + (Mouse_Position - Points[-2]).normalized() * Segment_Distance
Points.append(newPoint)
Create_Segment(Points[-2], newPoint)
Points[-1] = Mouse_Position
Update_Segment(Points[-2], Mouse_Position)
else:
# if the distance is not greater then we simply update the last position of the point to mouse
Points[-1] = Mouse_Position
Update_Segment(Points[-2], Mouse_Position)
Issue 2: When a loop is created and a new initial point is created there is left over segments and also the new initial point also seems to either missing or completely not drawing it, not sure, The process_loop function is over my head so I dont really understand it.
Issue 3: Sometimes when a loop is created one segment point? goes to 0,0 and I dont know why, this happens occasionally.
Full script can be copy and pasted into any scene as long as its attached to a Node2D.
extends Node2D
# Variables
var Line : Line2D # Holds the line created
var Line_Width : int = 10 # We use this for collision thickness
var Segment_Distance : int = 50 # The Distance of each segment also how we create a new one
var Segments : Node2D # Creates a segment when enough distance is made also holds area2Ds
var Loop : Node2D # Holds the first created loop until a new loop is created
var Points : PackedVector2Array = PackedVector2Array() # Holds all the points for the line2D
var Max_Points : int = 50 # To stop having multiple points on the screen
func _ready() -> void:
# Create Nodes to hold each part of the line loop detection
Line = Line2D.new()
Line.name = "Line"
Line.width = Line_Width
add_child(Line)
Segments = Node2D.new()
Segments.name = "Segments"
add_child(Segments)
Loop = Node2D.new()
Loop.name = "Loop"
add_child(Loop)
func _process(delta: float) -> void:
if Input.is_action_pressed("Left_Click"):
Process_Line(get_global_mouse_position())
if Input.is_action_just_released("Left_Click"):
Clear_All()
func Process_Line(Mouse_Position: Vector2) -> void:
# Get the size of points
var Point_Count = Points.size()
if Point_Count == 0: # Checks if its 0, we add 2 points for the start and current position
Points.append(Mouse_Position)
Points.append(Mouse_Position)
Create_Segment(Mouse_Position, Mouse_Position) # Create a segment to connect area2D
elif Point_Count == 1:
Points.append(Mouse_Position)
Create_Segment(Points[-2], Mouse_Position)
elif Point_Count > Max_Points:
Clear(Point_Count - Max_Points)
else:
var distance = Points[-2].distance_to(Mouse_Position)
# Check if the current distance is greater then segment distance
if distance > Segment_Distance:
var distanceDifference = distance - Segment_Distance
var segmentsToAdd = int(distanceDifference / Segment_Distance)
# Want to add segments for when the mouse is far away from the second to last point
# but I am not sure what to do, I attempted using a interpolated point
# but it glitches out
for i in range(segmentsToAdd):
var newPoint = Points[-2] + (Mouse_Position - Points[-2]).normalized() * Segment_Distance
Points.append(newPoint)
Create_Segment(Points[-2], newPoint)
Points[-1] = Mouse_Position
Update_Segment(Points[-2], Mouse_Position)
else:
# if the distance is not greater then we simply update the last position of the point to mouse
Points[-1] = Mouse_Position
Update_Segment(Points[-2], Mouse_Position)
# Apply the points array to the line2D points
Line.points = Points
# What does the loop detection currently buggy.
Process_Loop()
func Create_Segment(start: Vector2, end: Vector2) -> void:
# Get the rotation of the points
var points = rotated_rectangle_points(start, end, Line_Width)
var segment = Area2D.new()
segment.add_to_group("Player_Looper") # Add segment to a group to detect stuff
var collision = create_collision_polygon(points)
segment.add_child(collision)
Segments.add_child(segment)
func Update_Segment(start: Vector2, end: Vector2) -> void:
var points = rotated_rectangle_points(start, end, Line_Width)
var segment = Segments.get_child(Segments.get_child_count() - 1) as Area2D
var collision = segment.get_child(0) as CollisionPolygon2D
collision.set_polygon(points)
static func rotated_rectangle_points(start: Vector2, end: Vector2, width: float) -> Array:
var diff = end - start
var normal = diff.rotated(TAU / 4).normalized()
var offset = normal * width * 0.5
return [start + offset, start - offset, end - offset, end + offset]
static func create_collision_polygon(points: Array) -> CollisionPolygon2D:
var result = CollisionPolygon2D.new()
result.set_polygon(points)
return result
func Process_Loop() -> void:
var segments = Segments.get_children()
for index in range(segments.size() - 1, 0, -1):
var segment = segments[index]
var candidates = segment.get_overlapping_areas()
for candidate in candidates:
var candidate_index = segments.find(candidate)
if candidate_index == -1:
continue
if abs(candidate_index - index) > 1:
push_loop(candidate_index, index)
Clear(index)
return
func push_loop(first_index:int, second_index:int) -> void:
Clear_Loop()
var loop = Area2D.new()
var points = Points
points.resize(second_index)
for point_index in first_index + 1:
points.remove_at(0)
var collision = create_collision_polygon(points)
loop.add_child(collision)
Loop.add_child(loop)
func Clear_All() -> void:
# Clears the points and segments and also clear loops
Clear(Points.size())
Clear_Loop()
func Clear(index) -> void:
# Loop through the points array and remove each one based on index
for _clear in range(index):
if Points.size() > 0:
Points.remove_at(0)
# Get the current segments and also remove each segment
var segments = Segments.get_children()
for i in range(index):
if segments.size() > 0:
Segments.remove_child(segments[0])
segments[0].queue_free()
segments.remove_at(0)
Line.points = Points
func Clear_Loop() -> void:
for loop in Loop.get_children():
if is_instance_valid(loop):
loop.queue_free()