Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | Sylvain22 |
Hi,
I’m new to Godot. Using v3.2.1
I’m working on game which is a Qix engine / Gals Panic like clone.
It is a 2D games with a cursor drawing rectilinear shape revealing an hidden background.
So reducing the area where a monster is moving.
I need to create a trail for my player (yellow on the screenshot). I’m thinking of using a Line2D with an associated StaticBody2D with a CollisionShape2D. So I added the CollisionPolygon2D at runtime matching the points of the line.
And the gdscript, that will set the shape at runtime:
func _ready():
# copy line points to a new polygon
var pol : PoolVector2Array
pol.append_array(points)
# recopy all points back to origin
var n = points.size()
for i in range(n-1, 0, -1):
pol.append(points[i])
$StaticBody2D/CollisionPolygon2D.polygon = pol
Is it the way to go or should I use other object?
So I’m answering to myself.
Such “flat” polygon collision shape generate error’s message in the debugger. But the collision was working as it seems.
But I finally needed a more fine grained control over the collision of this shape (The tail). So I redesigned the tail as follow:
The tail is basically a Line2D with an empty StaticBoby2D
The collision shape is a now a list of Segment2D managed by the following code. To avoid collision with the player, I added some code to reduce the size of the last two segment. Because I still use collision detection with the player as we are not allowed cross the tail.
The code is not yet optimized, and update all segment each time, but in the gameplay only the last two only need to be updated.
func update_collision_shape():
var n = points.size()
if n < 2:
# waiting for a 2nd point to create a segment
return
# replace all segments (for now, TODO: optimize the update)
segments.clear()
for i in range(n-1):
segments.append(create_segment(points[i], points[i+1]))
reduce_last_segment()
# re-add all segments: delete old childs and recreate all
clear_all_shape(true)
for s in segments:
$col.add_child(s)
And the helpers functions (a lot of variables are exposed for debugger easy access):
func clear_all_shape(keep_segments = false):
for c in $col.get_children():
c.queue_free()
if not keep_segments:
segments.clear()
func create_segment(p1 : Vector2, p2 : Vector2, shorten : int = 0) -> CollisionShape2D:
var collision = CollisionShape2D.new()
collision.shape = SegmentShape2D.new()
collision.shape.a = p1
collision.shape.b = p2
if shorten > 0:
reduce_segment(collision, shorten)
return collision
func reduce_segment(seg : CollisionShape2D, shorten : int):
var v = (seg.shape.b - seg.shape.a).normalized()
# we calculated the length of the segment, reducing at max of its length
var d = seg.shape.a.distance_to(seg.shape.b)
var delta = min(d, shorten)
var b = seg.shape.b
b -= v * delta
seg.shape.b = b
func reduce_last_segment(shorten : int = -1) -> int:
if points.size() < 2 or segments.size() == 0:
return 0
if shorten < 0:
# default value
shorten = last_segment_reduction
# r count the number of segment modified
var r = 0
# last point is the player, to avoid collision we shorten
# this last segment, or even the previous one on corner
var d = points[-2].distance_to(points[-1])
if d < shorten:
# skip last segment too short
segments.pop_back()
r += 1
# we also reduce previous segment, as are still to near
if segments.size() > 0:
reduce_segment(segments[-1], shorten - d)
r += 1
else:
# we shorten the last segment only
reduce_segment(segments[-1], shorten)
r += 1
return r
And the runtime the node tree result is as follow:
And it looks like:
So now the player can collide with the tail and is stopped. The player’s collision in the same layer as the tail is the small 5 px circle shape. Collision between player and tail are disabled while rewinding on the tail.
I will evolve as I discover more on Godot and as I need to handle more use case.
Suggestions are welcome.
Regards,
Sylvain.
Sylvain22 | 2020-05-08 04:52
sorry, just want to know what is the last segment reduction here.
func reduce_last_segment(shorten : int = -1) -> int:
if points.size() < 2 or segments.size() == 0:
return 0
if shorten < 0:
# default value
shorten = last_segment_reduction
EXSPIRAVIT_1104 | 2022-04-07 00:24
Hello,
Such a long time ago… I did not finish the project, nor worked on it since 2020.
OK. So its just a variable of the module I defined in the script
# will be initilized by the play in set_players_info()
var last_segment_reduction : int = 0
# [...]
# it is also assigned with this small setter
func set_players_info(player_distance):
last_segment_reduction = player_distance
Only those 3 matches in the code.
I don’t really remember how it works without looking at the code more deeply.
I hope it helped you at some point.
EDIT:
oh, this value is for auto-reducing the last segment size so it doesn’t self collide to with the player itself.
You see on the screenshot, in yellow the tail, the ruby is the player, I squared in white the space I made, and you see in green the collision shape that are used.
So there’s a small gap based on the player size, to avoid some self collision to be detected. But if the trace / tail loop, the central player’s dot (circle) will collide in the tail’s segment.
Regards,
Sylvain.
Sylvain22 | 2022-05-06 06:01