OK man. It took me some work but I think I have an answer. First things first, based on my understanding of Godot you don't really need (or want) to create a texture for this. What you want is use custom drawing. Here is my solution.
extends Node2D
var visual_length = 4
var time_gap = 0.2
var sprite_nodes = []
var trail_elements = []
func _ready():
var open_nodes = [$'..']
while open_nodes:
var n = open_nodes.pop_front()
open_nodes = open_nodes + n.get_children()
if n is AnimatedSprite:
sprite_nodes.append(n)
_add_to_trail()
func _process(delta):
self.global_position = Vector2(0,0)
func _draw():
for te in trail_elements:
for s in te:
self.draw_set_transform(s[0][0], s[0][1], s[0][2])
self.draw_texture(s[1], s[2] + s[1].get_size() * -0.5)
func _add_to_trail():
while true:
var te = []
for n in sprite_nodes:
var n_pos = n.global_position
var n_rot = n.global_rotation
var n_scale = n.global_scale
n_scale.x = n_scale.x * (-1 if n.flip_h else 1)
var s_texture = n.frames.get_frame(n.animation, n.frame)
var s_offset = n.offset
te.append([[n_pos, n_rot, n_scale], s_texture, s_offset])
trail_elements.append(te)
if trail_elements.size() > visual_length:
trail_elements.pop_front()
update()
yield(get_tree().create_timer(time_gap), "timeout")
I will try and explain what is going on.
func _ready():
var open_nodes = [$'..']
while open_nodes:
var n = open_nodes.pop_front()
open_nodes = open_nodes + n.get_children()
if n is AnimatedSprite:
sprite_nodes.append(n)
_add_to_trail()
In the ready function I scan the parent for all the nodes that are AnimatedSprites and make the list of them for later. I also start a coroutine for adding to the trail.
func _process(delta):
self.global_position = Vector2(0,0)
The process function is real simple, it just keeps the trail node at the world origin so the trail doesn't move.
var te = []
for n in sprite_nodes:
var n_pos = n.global_position
var n_rot = n.global_rotation
var n_scale = n.global_scale
n_scale.x = n_scale.x * (-1 if n.flip_h else 1)
var s_texture = n.frames.get_frame(n.animation, n.frame)
var s_offset = n.offset
te.append([[n_pos, n_rot, n_scale], s_texture, s_offset])
trail_elements.append(te)
The important part here is that each trail_element is a list of textures and their positions, rotations and scales.
update()
Every time I update the trail I need to call update so the draw function gets called by the engine.
func _draw():
for te in trail_elements:
for s in te:
self.draw_set_transform(s[0][0], s[0][1], s[0][2])
self.draw_texture(s[1], s[2] + s[1].get_size() * -0.5)
I think this is pretty self explanatory. It loops through all the textures in each trail element. Sets a draw position then draws the texture. There a trail!
Hopefully this gets you started, but it might not cover all your needs. A few things to note:
- I am only supporting animated sprite, but you can expand.
- I am not doing anything fancy with drawing such as transparency.
- I am only supporting centered sprites.
- I am only supporting horizontal flipping.
- I am not being at all smart with draw order. This might be a big problem for you so you'll need to figure out how you want to handle that.