90 lines
3.2 KiB
GDScript
90 lines
3.2 KiB
GDScript
class_name BlinkerEffect
|
||
extends BaseEffect
|
||
|
||
## How many blink cycles per second.
|
||
@export_range(0.0, 60.0) var blink_frequency: float = 8.0
|
||
|
||
## Fraction of each cycle the sprite is visible (0.0–1.0).
|
||
@export_range(0.0, 1.0) var blink_length: float = 0.5
|
||
|
||
## Minimum alpha when "off" (0.0 = fully invisible, 1.0 = fully visible).
|
||
@export_range(0.0, 1.0) var min_alpha: float = 0.0
|
||
|
||
## Maximum alpha when "on" (0.0–1.0).
|
||
@export_range(0.0, 1.0) var max_alpha: float = 1.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_modulate: Color
|
||
|
||
|
||
func start(target: Node) -> void:
|
||
super.start(target)
|
||
if target is CanvasItem:
|
||
_original_modulate = target.modulate
|
||
|
||
|
||
func process(delta: float, target: Node) -> void:
|
||
super.process(delta, target)
|
||
if target is CanvasItem:
|
||
var alpha: float = _compute_alpha(_elapsed)
|
||
target.modulate = Color(_original_modulate.r, _original_modulate.g, _original_modulate.b, alpha)
|
||
|
||
|
||
func stop(target: Node) -> void:
|
||
if target is CanvasItem:
|
||
target.modulate = _original_modulate
|
||
|
||
|
||
## Compute the combined eased alpha for a given elapsed time.
|
||
func _compute_alpha(elapsed: float) -> float:
|
||
if duration <= 0.0:
|
||
return 1.0
|
||
|
||
var t: float = min(elapsed, duration)
|
||
|
||
# Blink pulse: on/off cycling at the given frequency and duty cycle
|
||
var period := 1.0 / blink_frequency if blink_frequency > 0.0 else 1.0
|
||
var phase := fmod(t, period) / period
|
||
|
||
var pulse: float = min_alpha
|
||
if phase < blink_length:
|
||
var pulse_progress = phase / blink_length if blink_length > 0.0 else 1.0
|
||
if pulse_progress < 0.5:
|
||
pulse = min_alpha + (max_alpha - min_alpha) * _ease_value(pulse_progress * 2.0, 2)
|
||
else:
|
||
pulse = min_alpha + (max_alpha - min_alpha) * _ease_value((1.0 - pulse_progress) * 2.0, 3)
|
||
|
||
# Start ease: fade from fully visible into the blink pattern
|
||
if t < start_ease and start_ease > 0.0:
|
||
var fade_in = _ease_value(t / start_ease, start_ease_mode)
|
||
return lerp(max_alpha, pulse, fade_in)
|
||
|
||
# End ease: fade from blink pattern back to fully visible
|
||
if t > duration - end_ease and end_ease > 0.0:
|
||
var fade_progress = (t - (duration - end_ease)) / end_ease
|
||
var fade_out = _ease_value(fade_progress, end_ease_mode)
|
||
return lerp(pulse, max_alpha, fade_out)
|
||
|
||
return pulse
|
||
|
||
|
||
## Apply easing to a 0.0–1.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
|