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?

asked Apr 15 in Engine by Sylvain22 (27 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.

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.