facsimile-wing/scripts/effects/flash_effect.gd

105 lines
4.2 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name FlashEffect
extends BaseEffect
## Shader applied to the target sprite during the flash.
@export var flash_shader: Shader = preload("res://shaders/flash.gdshader")
## Color of the flash overlay.
@export var flash_color: Color = Color.WHITE
## How many flash cycles per second.
@export_range(0.0, 60.0) var flash_frequency: float = 10.0
## Fraction of each cycle the flash is visible (0.01.0, i.e. duty cycle).
@export_range(0.0, 1.0) var flash_length: float = 0.5
## How the flash blends with the original sprite.
@export_enum("Mix", "Additive", "Screen") var blend_mode: int = 0
## Additional brightness added to the flash color (0.0 = none, 1.0 = double).
@export_range(0.0, 1.0) var brightness_boost: float = 0.0
## Duration of the fade-in at the start of the effect (seconds).
@export_range(0.0, 5.0) var start_ease: float = 0.0
## Easing curve for the fade-in.
@export_enum("Linear", "Smooth", "Ease-In", "Ease-Out", "Ease-In-Out") var start_ease_mode: int = 1
## Duration of the fade-out at the end of the effect (seconds).
@export_range(0.0, 5.0) var end_ease: float = 0.0
## Easing curve for the fade-out.
@export_enum("Linear", "Smooth", "Ease-In", "Ease-Out", "Ease-In-Out") var end_ease_mode: int = 1
var _original_material: Material
func start(target: Node) -> void:
super.start(target)
if target is CanvasItem:
_original_material = target.material
var flash_material := ShaderMaterial.new()
flash_material.shader = flash_shader
flash_material.set_shader_parameter("flash_color", flash_color)
flash_material.set_shader_parameter("flash_intensity", 0.0)
flash_material.set_shader_parameter("blend_mode", blend_mode)
flash_material.set_shader_parameter("brightness_boost", brightness_boost)
target.material = flash_material
func process(delta: float, target: Node) -> void:
super.process(delta, target)
if target.material is ShaderMaterial:
var intensity := _compute_intensity(_elapsed)
target.material.set_shader_parameter("flash_intensity", intensity)
func stop(target: Node) -> void:
if target is CanvasItem:
target.material = _original_material
## Compute the combined eased flash intensity for a given elapsed time.
## Returns a value 0.01.0 where 1.0 means full flash visibility.
func _compute_intensity(elapsed: float) -> float:
if duration <= 0.0:
return 0.0
# Clamp elapsed to duration to avoid overshoot on the final frame
var t: float = min(elapsed, duration)
# Overall envelope: fade in during start_ease, hold, fade out during end_ease
var envelope: float = 1.0
if t < start_ease and start_ease > 0.0:
envelope = _ease_value(t / start_ease, start_ease_mode)
elif t > duration - end_ease and end_ease > 0.0:
var fade_progress = (t - (duration - end_ease)) / end_ease
envelope = 1.0 - _ease_value(fade_progress, end_ease_mode)
# Flash pattern: on/off cycling at the given frequency and duty cycle
# Each flash pulse also eases in and out for a smoother look
var period := 1.0 / flash_frequency if flash_frequency > 0.0 else 1.0
var phase := fmod(t, period) / period # 0.01.0 position within the current cycle
var pulse: float = 0.0
if phase < flash_length:
# Inside the "on" portion of this cycle — ease within the pulse itself
var pulse_progress = phase / flash_length if flash_length > 0.0 else 1.0
# Ease in for the first half of the pulse (build up), ease out for the second (fade down)
if pulse_progress < 0.5:
pulse = _ease_value(pulse_progress * 2.0, 2) # Ease-In (build up)
else:
pulse = _ease_value((1.0 - pulse_progress) * 2.0, 3) # Ease-Out (fade down)
return envelope * pulse
## Apply easing to a 0.01.0 normalized value based on the selected mode.
func _ease_value(value: float, mode: int) -> float:
match mode:
0: return value # Linear
1: return smoothstep(0.0, 1.0, value) # Smooth
2: return ease(value, -2.0) # Ease-In (accelerating)
3: return ease(value, 2.0) # Ease-Out (decelerating)
4: return ease(value, 3.0) # Ease-In-Out (S-curve)
return value