extends Node2D const SETTLE_TIME := 8.0 var _texture: Texture2D var _fragments: Array[Dictionary] = [] var _dust: Array[Dictionary] = [] var _elapsed := 0.0 var _rng := RandomNumberGenerator.new() var _dust_color := Color("b4a99a") func configure(sprite: Sprite2D) -> void: _texture = sprite.texture transform = sprite.global_transform var sprite_scale := global_scale scale = Vector2.ONE _rng.randomize() var dimensions := _texture.get_size() var grid: Array[Vector2] = [] for y in 5: for x in 5: var vertex := Vector2(x, y) * dimensions / 4.0 if x > 0 and x < 4: vertex.x += _rng.randf_range(-0.3, 0.3) * dimensions.x / 4.0 if y > 0 and y < 4: vertex.y += _rng.randf_range(-0.3, 0.3) * dimensions.y / 4.0 grid.append(vertex) # Shared jagged cracks keep neighbouring pieces fitted together. var horizontal: Array[PackedVector2Array] = [] var vertical: Array[PackedVector2Array] = [] for y in 5: for x in 4: horizontal.append(_fracture_edge(grid[y * 5 + x], grid[y * 5 + x + 1], y > 0 and y < 4)) for y in 4: for x in 5: vertical.append(_fracture_edge(grid[y * 5 + x], grid[(y + 1) * 5 + x], x > 0 and x < 4)) for y in 4: for x in 4: var outline := PackedVector2Array() outline.append_array(_edge_vertices(horizontal[y * 4 + x], false)) outline.append_array(_edge_vertices(vertical[y * 5 + x + 1], false)) outline.append_array(_edge_vertices(horizontal[(y + 1) * 4 + x], true)) outline.append_array(_edge_vertices(vertical[y * 5 + x], true)) var center := Vector2.ZERO for vertex in outline: center += vertex center /= outline.size() var origin := (center - dimensions * 0.5) * sprite_scale var points := PackedVector2Array() var uv := PackedVector2Array() for vertex in outline: points.append((vertex - center) * sprite_scale) uv.append(vertex / dimensions) var direction := origin.normalized() if origin.length_squared() > 0.1 else Vector2.RIGHT _fragments.append({"points": points, "uv": uv, "position": origin, "velocity": direction.rotated(_rng.randf_range(-0.35, 0.35)) * _rng.randf_range(40.0, 95.0), "angle": 0.0, "spin": _rng.randf_range(-4.0, 4.0)}) _dust_color = Color("a1c7df") if "ice" in _texture.resource_path else Color("b4a99a") for index in 28: _dust.append({"direction": Vector2.RIGHT.rotated(_rng.randf_range(0.0, TAU)), "speed": _rng.randf_range(45.0, 210.0), "size": _rng.randf_range(1.0, 3.0)}) z_index = 35 light_mask = 4 func _fracture_edge(start: Vector2, end: Vector2, jagged: bool) -> PackedVector2Array: var edge := PackedVector2Array([start]) if jagged: var normal := (end - start).orthogonal().normalized() var roughness := start.distance_to(end) * 0.13 var side := 1.0 if _rng.randf() > 0.5 else -1.0 for index in 3: var along := (index + 1.0) / 4.0 + _rng.randf_range(-0.05, 0.05) edge.append(start.lerp(end, along) + normal * roughness * side * _rng.randf_range(0.5, 1.0)) side *= -1.0 edge.append(end) return edge func _edge_vertices(edge: PackedVector2Array, backwards: bool) -> PackedVector2Array: var vertices := PackedVector2Array() # The next edge supplies the end vertex, avoiding duplicate polygon points. for index in edge.size() - 1: vertices.append(edge[edge.size() - 1 - index] if backwards else edge[index]) return vertices func _process(delta: float) -> void: _elapsed += delta for fragment in _fragments: fragment.position += fragment.velocity * delta fragment.velocity *= exp(-1.2 * delta) fragment.angle += fragment.spin * delta fragment.spin *= exp(-0.7 * delta) queue_redraw() # Leave the settled debris visible without updating it every frame. if _elapsed >= SETTLE_TIME: _dust.clear() set_process(false) func _draw() -> void: for fragment in _fragments: draw_set_transform(fragment.position, fragment.angle) draw_polygon(fragment.points, PackedColorArray([Color.WHITE]), fragment.uv, _texture) draw_set_transform(Vector2.ZERO) var dust_fade := maxf(0.0, 1.0 - _elapsed / 0.8) for mote in _dust: var travel: Vector2 = mote.direction * mote.speed * (1.0 - exp(-2.0 * _elapsed)) / 2.0 draw_circle(travel, mote.size * (1.0 + _elapsed), Color(_dust_color, dust_fade * 0.65)) if _elapsed < 0.18: draw_circle(Vector2.ZERO, 16.0 * (1.0 - _elapsed / 0.18), Color(0.85, 0.9, 1.0, (1.0 - _elapsed / 0.18) * 0.55))