Reputation: 3647
I'm totally new to godot engine. Wanted to create a game that allows player to draw lines on the screen. All the lines drawn on screen will have physics interaction with other collisionShape
. I found the tutorial and was able to draw lines using the code below. However, I still can't get the collision to work with existing collisionShape
@onready var _lines := $Lines
var _pressed := false
var _current_line: Line2D
func _input(event):
if event is InputEventMouseButton:
_pressed = event.pressed
if _pressed:
_current_line = Line2D.new()
_lines.add_child(_current_line)
if event is InputEventMouseMotion && _pressed:
_current_line.add_point(event.position)
Upvotes: 1
Views: 2058
Reputation: 40305
The following is the code I made for a prior question (here) adapted to Godot 4:
extends Node2D
var _line:Line2D
var _segments:Node2D
var _loops:Node2D
func _ready() -> void:
_line = Line2D.new()
_line.name = "_line"
add_child(_line)
_segments = Node2D.new()
_segments.name = "_segments"
add_child(_segments)
_loops = Node2D.new()
_loops.name = "_loops"
add_child(_loops)
func _physics_process(_delta:float) -> void:
if Input.is_action_pressed("left_click"):
add_position(get_global_mouse_position())
if Input.is_action_just_released("left_click"):
total_purge()
var _points:PackedVector2Array = PackedVector2Array()
var _max_points := 30
var _max_distance := 20
func add_position(pos:Vector2) -> void:
var point_count := _points.size()
if point_count == 0:
_points.append(pos)
_points.append(pos)
add_segment(pos, pos)
elif point_count == 1:
_points.append(pos)
add_segment(_points[-2], pos)
elif point_count > _max_points:
purge(point_count - _max_points)
else:
if _points[-2].distance_to(pos) > _max_distance:
_points.append(pos)
add_segment(_points[-2], pos)
else:
_points[-1] = pos
change_segment(_points[-2], pos)
_line.points = _points
process_loop()
var _width := 5
func add_segment(start:Vector2, end:Vector2) -> void:
var points := rotated_rectangle_points(start, end, _width)
var segment := Area2D.new()
var collision := create_collision_polygon(points)
segment.add_child(collision)
_segments.add_child(segment)
func change_segment(start:Vector2, end:Vector2) -> void:
var points := rotated_rectangle_points(start, end, _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)
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]
func create_collision_polygon(points:Array) -> CollisionPolygon2D:
var result := CollisionPolygon2D.new()
result.set_polygon(points)
return result
func total_purge() -> void:
purge(_points.size())
purge_loops()
func purge(index:int) -> void:
var segments := _segments.get_children()
for _index in range(0, index):
if _points.size() > 0:
_points.remove_at(0)
if segments.size() > 0:
_segments.remove_child(segments[0])
segments[0].queue_free()
segments.remove_at(0)
_line.points = _points
func purge_loops() -> void:
for loop in _loops.get_children():
if is_instance_valid(loop):
loop.queue_free()
func process_loop() -> void:
var segments := _segments.get_children()
for index in range(segments.size() - 1, 0, -1):
var segment:Area2D = 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) > 2:
push_loop(candidate_index, index)
purge(index)
return
func push_loop(first_index:int, second_index:int) -> void:
purge_loops()
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)
_loops.add_child(loop)
Since you are adding points with the mouse, this should be a good fit for your question.
The code maintains a series of Area2D
under _segments
, created to match a Line2D
that is being drawn by dragging the mouse (which results in calls to add_position
which updates both the Line2D
and the Area2D
).
Since, as I said, it works by dragging, it uses a variable _max_distance
to decide when to create a new segment of the Line2D
.
The part you are likely to be interested in are the methods add_segment
and change_segment
. The reason why change_segment
exists is because the last segment is updated as the pointer moves until it exceeds _max_distance
, at which point a new segment is added.
As you can see the Area2D
have each a CollisionPolygon2D
which is made to be a rotated rectangle (created by the method rotated_rectangle_points
based on the two points that define the segment and the variable _width
).
It should be possible to create a single Area2D
and make a connected polygon to represent the Line2D
(for which you need a solution to make the segment connections)...
... However this solution was made to also make it easier to remove segments at the end of the line. In fact, the lines length is limited by the value of _max_points
.
Upvotes: 0