Poprawiony efekt eksplozji

This commit is contained in:
Tomasz Boruc 2026-10-15 19:29:40 +02:00
parent bcde93eca9
commit 6f9bd71298
2 changed files with 22 additions and 11 deletions

View File

@ -31,7 +31,8 @@ func configure(sprite: Sprite2D, drift: Vector2, camera: Camera2D) -> void:
global_position = sprite.global_position
global_rotation = sprite.global_rotation
_ship_scale = sprite.global_scale
_drift = drift.rotated(-global_rotation) * 0.12
# The whole effect inherits world velocity; fragment velocities are relative.
_drift = drift
_camera = camera
if is_instance_valid(_camera):
_camera_offset = _camera.offset
@ -39,6 +40,9 @@ func configure(sprite: Sprite2D, drift: Vector2, camera: Camera2D) -> void:
func _ready() -> void:
z_index = 45
light_mask = 4
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
# Animation advances in render frames, so don't interpolate its transform again.
physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF
_rng.randomize()
_build_fragments()
_prepare_smoke_texture()
@ -81,13 +85,14 @@ func _build_fragments() -> void:
uv.append(vertex / dimensions)
var wreck := origin.length() < 12.0
var radial := origin.normalized() if origin.length_squared() > 0.1 else Vector2.RIGHT.rotated(_rng.randf_range(0, TAU))
_fragments.append({"points": points, "uv": uv, "position": origin, "velocity": radial.rotated(_rng.randf_range(-0.45, 0.45)) * _rng.randf_range(65, 155) + _drift if not wreck else radial * 5.0, "angle": 0.0, "spin": _rng.randf_range(-5, 5) if not wreck else _rng.randf_range(-0.3, 0.3), "wreck": wreck})
_fragments.append({"points": points, "uv": uv, "position": origin, "velocity": radial.rotated(_rng.randf_range(-0.45, 0.45)) * _rng.randf_range(65, 155) if not wreck else radial * 5.0, "angle": 0.0, "spin": _rng.randf_range(-5, 5) if not wreck else _rng.randf_range(-0.3, 0.3), "wreck": wreck})
func _process(delta: float) -> void:
global_position += _drift * delta
if _done and _elapsed >= DURATION + 1.8:
if not _smoke.is_empty() or not _sparks.is_empty():
_smoke.clear()
_sparks.clear()
set_process(false)
queue_redraw()
return
_elapsed += delta
@ -191,13 +196,14 @@ func _emit_smoke() -> void:
position = _burns[_rng.randi_range(0, _burns.size() - 1)]["position"] if not _burns.is_empty() else _hull_burn_position()
elif not _fragments.is_empty() and _rng.randf() < 0.7:
position = _fragments[_rng.randi_range(0, _fragments.size() - 1)]["position"]
_smoke.append({"position": position, "velocity": Vector2(_rng.randf_range(-12, 12), _rng.randf_range(-22, 8)), "age": 0.0, "life": _rng.randf_range(0.9, 1.7), "radius": _rng.randf_range(5, 12)})
_smoke.append({"position": position, "velocity": Vector2(_rng.randf_range(-12, 12), _rng.randf_range(-22, 8)) - _drift.rotated(-global_rotation) * 0.25, "age": 0.0, "life": _rng.randf_range(0.9, 1.7), "radius": _rng.randf_range(5, 12)})
func _update_camera() -> void:
if not is_instance_valid(_camera):
return
var time := _elapsed - CHARGE_TIME
var strength := 0.8 if time < 0.0 else 7.0 * exp(-5.0 * time)
_camera.global_position = global_position
_camera.offset = _camera_offset + Vector2(sin(_elapsed * 83), cos(_elapsed * 67)) * strength
func _restore_camera() -> void:
@ -238,10 +244,6 @@ func _draw() -> void:
for layer in 3:
draw_arc(Vector2.ZERO, 18 + time * 230 + layer * 2, 0, TAU, 96, Color(0.85, 0.75, 0.6, fade * 0.08), 2, true)
_draw_sparks()
if time > 0.8:
for index in 4:
var flicker := maxf(0, sin(_elapsed * 17 + index * 8)) * clampf((3.3 - time) / 2, 0, 1)
draw_circle(Vector2.RIGHT.rotated(index * 2.3) * (4 + index * 2), 2 + flicker * 2, Color(1, 0.28, 0.04, flicker * 0.75))
func _draw_sparks() -> void:
for spark in _sparks:

View File

@ -22,8 +22,11 @@ func _ready() -> void:
assert(effect.get("_fragments").size() >= 4)
assert(not game.has_node("DefeatOverlay"))
assert(not effect.has_node("ExplosionAudio"))
var start_position: Vector2 = effect.global_position
assert(effect.get("_drift") == Vector2(100, -40))
effect.finished.connect(func() -> void: _finished_count += 1)
effect._process(0.2)
assert(effect.global_position.is_equal_approx(start_position + Vector2(20, -8)))
assert(not effect.get("_detonated") and camera.offset != Vector2(3, 5))
for step in 18:
effect._process(0.1)
@ -39,6 +42,7 @@ func _ready() -> void:
effect._process(0.3)
assert(game.has_node("DefeatOverlay") and _finished_count == 1)
assert(camera.offset == Vector2(3, 5))
assert(effect.global_position.is_equal_approx(start_position + Vector2(100, -40) * 5.35))
effect._process(0.1)
ship.call("_destroy_ship")
assert(_finished_count == 1)
@ -47,6 +51,11 @@ func _ready() -> void:
if child.name == "PlayerDestructionEffect":
deaths += 1
assert(deaths == 1)
# A drifting wreck must retain its velocity even after the visual cleanup.
effect._process(2.0)
var coasting_position: Vector2 = effect.global_position
effect._process(0.5)
assert(effect.global_position.is_equal_approx(coasting_position + Vector2(50, -20)))
for chassis in preload("res://scripts/game/ship_catalog.gd").SHIPS:
var source := Sprite2D.new()
source.texture = load(preload("res://scripts/game/ship_catalog.gd").texture_path(chassis))