Godot - Simple Trail Effect

How it started

Some months ago I started playing around with Godot engine, I’ve started with a Pong clone just to learn the basics, then - when talking about it to a friend - came the idea of this game where the player reflects projectiles with some sort of magic shield. And here I am, making some good progress (considering the short amount of free time I have) thanks to how high level development Godot provides, its awesome community, great documentation and great learning references, such as GD Quest!

The dash skill and its trail effect

The problem:

I have this modular character composed by head and body sprites, it doesn’t have multiple animation frames, its animations are just position and scale tweens.

My solution:

I’ve created a script and attached it to a child node of my character. This dash script has a owner_path that should point to the character. It assumes the character has some methods:

# Character.gd
func get_speed():
  return _speed

func set_speed(value):
  _speed = value

func set_input_enabled(enabled):
  _input_enabled = enabled

func get_current_sprites():
  var head = get_node("YSort/Node2D/Head")
  var body = get_node("YSort/Node2D/Body")
  return [head, body] 

Then it uses those methods to achieve the dash with trail effect:

# Dash.gd
extends Node

# The owner of this Dash node needs to have the following methods:
# func set_input_enabled(enabled:bool)
# func get_speed()
# func set_speed(speed_scalar)
# func get_current_sprites() -> List[Sprite]

export (NodePath) var owner_path
var owner_body
var owner_speed

# list of list: each list contains all necessary sprites to represent a 
# ghost copy of the original character
var ghosts = [] 
var tween

func _ready():
    owner_body = get_node(owner_path)
    var num_sprites = owner_body.get_current_sprites().size()
    for i in range(5): # 5 ghost trails
        var sprites = []
        for j in range(num_sprites): 
            var s = Sprite.new()
            s.set_visible(false)
            add_child(s) 
            sprites.append(s)
        ghosts.append(sprites)

    tween = Tween.new()
    add_child(tween)

func dash():
    owner_body.set_input_enabled(false)
    owner_speed = owner_body.get_speed()
    owner_body.set_speed(owner_speed * 3)

    var timer = Timer.new()
    timer.connect("timeout", self, "_return_input_to_owner") 
    add_child(timer) 
    _start_ghost_tweens()
    timer.start(0.25)
    timer.set_one_shot(true)

func _start_ghost_tweens():
    for i in range(ghosts.size()): # ghost trails
        yield(get_tree().create_timer(0.05), "timeout")

        for j in range(ghosts[i].size()): # ghost parts (head, body, etc)    
            var owner_sprites = owner_body.get_current_sprites() 
            var ghost_part = ghosts[i][j]

            ghost_part.set_scale(owner_body.global_scale)
            ghost_part.set_position(owner_sprites[j].global_position)
            ghost_part.set_texture(owner_sprites[j].get_texture())
            ghost_part.set_rotation(owner_sprites[j].global_rotation)
            ghost_part.flip_h = owner_sprites[j].flip_h
            ghost_part.set_visible(true)

            tween.interpolate_property(
                ghost_part, 
                "modulate", 
                Color(1, 1, 1, 1), 
                Color(1, 1, 1, 0), 
                0.25, 
                Tween.TRANS_LINEAR, 
                Tween.EASE_IN
            )
            if not tween.is_connected("tween_completed", self, "_on_complete_ghost_tween"):
                tween.connect("tween_completed", self, "_on_complete_ghost_tween") 
            tween.start()

func _on_complete_ghost_tween(object, key):
    object.set_visible(false)

func _return_input_to_owner():
    owner_body.set_speed(owner_speed)
    owner_body.set_input_enabled(true)

Next

You can easily change this script to export the number of ghost trails and tween variables so you can change them directly on the editor. Also, it might be a good idea to detach (by creating another script) the trail from the dash logic, after all you might want to add the trail effect to other things that won’t have a dash skill.

Thanks for reading!

Guilherme Recchi Cardozo

Guilherme Recchi Cardozo
Python software developer who also loves Haxe, game development, heavy metal, books, comics and movies.

Design Patterns pt 1

_Design Patterns: Elements of Reusable Object-Oriented Software_ by Erich Gamma, John Vlissides, Ralph Johnson and Richard Helm is one of...… Continue reading

Git Cheat Sheet

Published on April 30, 2018