173 lines
4.6 KiB
GDScript
173 lines
4.6 KiB
GDScript
@icon("res://addons/shaker/assets/ShakerEmitter2D.svg")
|
|
@tool
|
|
class_name ShakerEmitter2D
|
|
extends "res://addons/shaker/src/Vector2/ShakerBase2D.gd"
|
|
|
|
## It emits shake values and is received by ShakeEmitter2D.
|
|
|
|
# Exported variables
|
|
@export var emit: bool:
|
|
set = set_emit,
|
|
get = get_emit
|
|
|
|
@export var max_distance: float = 0.0:
|
|
set = set_max_distance,
|
|
get = get_max_distance
|
|
|
|
@export_exp_easing("attenuation") var distance_attenuation: float = 0.5:
|
|
set = set_distance_attenuation,
|
|
get = get_distance_attenuation
|
|
|
|
# Private variables
|
|
var emitting: bool = false
|
|
var _timer_offset: float = 0.0
|
|
var _fading_out: bool = false
|
|
var shake_offset_position: Vector2 = Vector2.ZERO
|
|
var shake_offset_rotation:float = 0.0
|
|
var shake_offset_scale: Vector2 = Vector2.ZERO
|
|
var area2d: Area2D
|
|
var collision:CollisionShape2D
|
|
|
|
# Called when the node enters the scene tree for the first time
|
|
func _ready() -> void:
|
|
add_to_group("ShakerEmitter")
|
|
|
|
for child in get_children():
|
|
if child is Area2D:
|
|
area2d = child
|
|
|
|
if not area2d:
|
|
_create_Area2D()
|
|
|
|
set_emit(emit)
|
|
|
|
# Creates an Area2D child node if one doesn't exist
|
|
func _create_Area2D() -> void:
|
|
area2d = Area2D.new()
|
|
collision = CollisionShape2D.new()
|
|
add_child(area2d)
|
|
area2d.add_child(collision)
|
|
area2d.set_owner(get_tree().edited_scene_root)
|
|
collision.set_owner(get_tree().edited_scene_root)
|
|
area2d.name = "Area2D"
|
|
collision.name = "CollisionShape2D"
|
|
|
|
area2d.collision_layer = 1 << 9
|
|
area2d.collision_mask = 0
|
|
|
|
# Called every frame
|
|
func _process(delta: float) -> void:
|
|
if emitting:
|
|
if shakerPreset != null:
|
|
if timer <= duration or duration == 0.0:
|
|
_progress_shake()
|
|
timer += delta * shake_speed
|
|
else:
|
|
force_stop_shake()
|
|
else:
|
|
if timer > 0:
|
|
force_stop_shake()
|
|
|
|
# Progresses the shake effect
|
|
func _progress_shake() -> void:
|
|
var _ease_in: float = 1.0
|
|
var _ease_out: float = 1.0
|
|
var _final_duration: float = duration if (duration > 0 and not _fading_out) else 1.0
|
|
|
|
_ease_in = ease(timer/_final_duration, fade_in)
|
|
_ease_out = ease(1.0 - (max((timer - _timer_offset), 0.0))/_final_duration, fade_out)
|
|
|
|
if not (duration > 0) or _fading_out:
|
|
if _ease_out <= get_process_delta_time():
|
|
force_stop_shake()
|
|
|
|
var _shake_position: Vector2 = Vector2.ZERO
|
|
var _shake_rotation:float = 0.0
|
|
var _shake_scale: Vector2 = Vector2.ZERO
|
|
|
|
if shakerPreset != null:
|
|
var _value: float = timer
|
|
var _strength: float = intensity * _ease_in * _ease_out
|
|
|
|
_shake_position += (shakerPreset.get_value(_value, ShakerPreset2D.Categories.POSITION) * _strength)
|
|
_shake_rotation += (shakerPreset.get_value(_value, ShakerPreset2D.Categories.ROTATION) * _strength * (PI/2.0))
|
|
_shake_scale += (shakerPreset.get_value(_value, ShakerPreset2D.Categories.SCALE) * _strength)
|
|
|
|
shake_offset_position = _shake_position
|
|
shake_offset_rotation = _shake_rotation
|
|
shake_offset_scale = _shake_scale
|
|
|
|
# Starts the shake effect
|
|
func play_shake() -> void:
|
|
if shakerPreset != null:
|
|
emitting = true
|
|
_fading_out = false
|
|
_initialize_timer_offset()
|
|
shake_started.emit()
|
|
|
|
func _initialize_timer_offset() -> void:
|
|
if !(duration > 0): _timer_offset = 0x80000
|
|
else: _timer_offset = 0.0
|
|
|
|
# Stops the shake effect with a fade-out
|
|
func stop_shake() -> void:
|
|
if not _fading_out:
|
|
_timer_offset = timer
|
|
_fading_out = true
|
|
shake_fading_out.emit()
|
|
|
|
# Immediately stops the shake effect
|
|
func force_stop_shake() -> void:
|
|
if emitting:
|
|
if emit: emit = false
|
|
_fading_out = false
|
|
emitting = false
|
|
set_progress(0.0)
|
|
shake_finished.emit()
|
|
|
|
# Returns configuration warnings
|
|
func _get_configuration_warnings() -> PackedStringArray:
|
|
if not get_children().any(func(child): return child is Area2D):
|
|
return ["First child must be Area2D"]
|
|
return []
|
|
|
|
# Sets the shake progress
|
|
func set_progress(value: float) -> void:
|
|
timer = value
|
|
_progress_shake()
|
|
|
|
# Setter for emit property
|
|
func set_emit(value: bool) -> void:
|
|
emit = value
|
|
if value:
|
|
play_shake()
|
|
elif timer > 0:
|
|
force_stop_shake()
|
|
|
|
# Getter for emit property
|
|
func get_emit() -> bool:
|
|
return emit
|
|
|
|
# Setter for max_distance property
|
|
func set_max_distance(value: float) -> void:
|
|
max_distance = value
|
|
notify_property_list_changed()
|
|
|
|
# Getter for max_distance property
|
|
func get_max_distance() -> float:
|
|
return max_distance
|
|
|
|
# Setter for distance_attenuation property
|
|
func set_distance_attenuation(value: float) -> void:
|
|
distance_attenuation = value
|
|
|
|
# Getter for distance_attenuation property
|
|
func get_distance_attenuation() -> float:
|
|
return distance_attenuation
|
|
|
|
# Validates properties
|
|
func _validate_property(property: Dictionary) -> void:
|
|
if property.name == "distance_attenuation":
|
|
if not (max_distance > 0):
|
|
property.usage = PROPERTY_USAGE_NONE
|