|
|
|
|
Reply From: |
iron_weasel |
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.
Hey thx!
I didn’t have time to try your scripts yet, but I’ll do it!
gcardozo | 2018-12-06 08:56
Hey, your script helped me to figure out some things and I ended up tweaking my own version. It works for modular characters with multiple sprites, it changes the character speed and calls owner.set_input_enabled(...)
to remove/give back character input.
Here it is:
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
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)
The owner has:
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]
Note:
it might be a good idea to separate the trail effect from the dash logic (the part that removes character input and changes its speed), but for now it does what I need.
gcardozo | 2018-12-15 13:06