0 votes

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.

Screenshot game in progress

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.

scene nodes

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?

in Engine by (33 points)

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:

tail object desing

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:
tail attached to playground at runtime

And it looks like:
qixall game screenshot with tail

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.

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

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.

spacer between player collide shape and tails segment

Regards,
Sylvain.

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.