some shake
This commit is contained in:
91
addons/shaker/src/Vector3/ShakerBase3D.gd
Normal file
91
addons/shaker/src/Vector3/ShakerBase3D.gd
Normal file
@@ -0,0 +1,91 @@
|
||||
@tool
|
||||
#class_name ShakerBase3D
|
||||
extends Node3D
|
||||
|
||||
# Shake intensity
|
||||
@export_range(0.0, 1.0, 0.001, "or_greater") var intensity: float = 1.0:
|
||||
set = set_intensity,
|
||||
get = get_intensity
|
||||
|
||||
# Shake duration
|
||||
@export var duration: float = 0.00:
|
||||
set = set_duration,
|
||||
get = get_duration
|
||||
|
||||
# Shake speed
|
||||
@export_range(0.0, 1.0, 0.001, "or_greater") var shake_speed: float = 1.0:
|
||||
set = set_shake_speed,
|
||||
get = get_shake_speed
|
||||
|
||||
# Fade-in easing
|
||||
@export_exp_easing var fade_in: float = 0.25:
|
||||
set = set_fade_in,
|
||||
get = get_fade_in
|
||||
|
||||
# Fade-out easing
|
||||
@export_exp_easing("attenuation") var fade_out: float = 0.25:
|
||||
set = set_fade_out,
|
||||
get = get_fade_out
|
||||
|
||||
# Shaker preset
|
||||
@export var shakerPreset:ShakerPreset3D:
|
||||
set = set_shaker_preset,
|
||||
get = get_shaker_preset
|
||||
|
||||
# Timer for shake progress
|
||||
var timer: float = 0.0:
|
||||
set = _on_timeline_progress
|
||||
|
||||
# SIGNALS
|
||||
signal timeline_progress(progress: float)
|
||||
signal shake_started
|
||||
signal shake_finished
|
||||
signal shake_fading_out
|
||||
|
||||
func set_intensity(value: float) -> void:
|
||||
intensity = max(value, 0.0)
|
||||
|
||||
func get_intensity() -> float:
|
||||
return intensity
|
||||
|
||||
func set_duration(value: float) -> void:
|
||||
duration = max(value, 0.0)
|
||||
if shakerPreset != null:
|
||||
shakerPreset.component_duration = duration
|
||||
notify_property_list_changed()
|
||||
|
||||
func get_duration() -> float:
|
||||
return duration
|
||||
|
||||
func set_shake_speed(value: float) -> void:
|
||||
shake_speed = max(value, 0.001)
|
||||
notify_property_list_changed()
|
||||
|
||||
func get_shake_speed() -> float:
|
||||
return shake_speed
|
||||
|
||||
func set_fade_in(value: float) -> void:
|
||||
fade_in = value
|
||||
|
||||
func get_fade_in() -> float:
|
||||
return fade_in
|
||||
|
||||
func set_fade_out(value: float) -> void:
|
||||
fade_out = value
|
||||
|
||||
func get_fade_out() -> float:
|
||||
return fade_out
|
||||
|
||||
func set_shaker_preset(value: ShakerPreset3D) -> void:
|
||||
shakerPreset = value
|
||||
if shakerPreset != null:
|
||||
shakerPreset.parent = self
|
||||
shakerPreset.component_duration = duration
|
||||
|
||||
func get_shaker_preset() -> ShakerPreset3D:
|
||||
return shakerPreset
|
||||
|
||||
# Handles timeline progress
|
||||
func _on_timeline_progress(value: float) -> void:
|
||||
timer = value
|
||||
timeline_progress.emit(timer)
|
||||
1
addons/shaker/src/Vector3/ShakerBase3D.gd.uid
Normal file
1
addons/shaker/src/Vector3/ShakerBase3D.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cna76yluotcdw
|
||||
183
addons/shaker/src/Vector3/ShakerEmitter3D.gd
Normal file
183
addons/shaker/src/Vector3/ShakerEmitter3D.gd
Normal file
@@ -0,0 +1,183 @@
|
||||
@icon("res://addons/shaker/assets/ShakerEmitter3D.svg")
|
||||
@tool
|
||||
class_name ShakerEmitter3D
|
||||
extends "res://addons/shaker/src/Vector3/ShakerBase3D.gd"
|
||||
|
||||
## It emits shake values and is received by ShakeEmitter3D.
|
||||
|
||||
# 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: Vector3 = Vector3.ZERO
|
||||
var shake_offset_rotation: Vector3 = Vector3.ZERO
|
||||
var shake_offset_scale: Vector3 = Vector3.ZERO
|
||||
var area3d: Area3D
|
||||
var collision:CollisionShape3D
|
||||
|
||||
# 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 Area3D:
|
||||
area3d = child
|
||||
|
||||
if not area3d:
|
||||
_create_area3d()
|
||||
|
||||
set_emit(emit)
|
||||
|
||||
# Creates an Area3D child node if one doesn't exist
|
||||
func _create_area3d() -> void:
|
||||
area3d = Area3D.new()
|
||||
collision = CollisionShape3D.new()
|
||||
add_child(area3d)
|
||||
area3d.add_child(collision)
|
||||
area3d.set_owner(get_tree().edited_scene_root)
|
||||
collision.set_owner(get_tree().edited_scene_root)
|
||||
area3d.name = "Area3D"
|
||||
collision.name = "CollisionShape3D"
|
||||
|
||||
area3d.collision_layer = 1 << 9
|
||||
area3d.collision_mask = 0
|
||||
|
||||
# Called every frame
|
||||
func _process(delta: float) -> void:
|
||||
if !Engine.is_editor_hint():
|
||||
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:
|
||||
if !Engine.is_editor_hint():
|
||||
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: Vector3 = Vector3.ZERO
|
||||
var _shake_rotation: Vector3 = Vector3.ZERO
|
||||
var _shake_scale: Vector3 = Vector3.ZERO
|
||||
|
||||
if shakerPreset != null:
|
||||
var _value: float = timer
|
||||
var _strength: float = intensity * _ease_in * _ease_out
|
||||
|
||||
_shake_position += (shakerPreset.get_value(_value, ShakerPreset3D.Categories.POSITION) * _strength)
|
||||
_shake_rotation += (shakerPreset.get_value(_value, ShakerPreset3D.Categories.ROTATION) * _strength * (PI/2.0))
|
||||
_shake_scale += (shakerPreset.get_value(_value, ShakerPreset3D.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 !Engine.is_editor_hint():
|
||||
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
|
||||
|
||||
# Updates the gizmo
|
||||
#func update_gizmo() -> void:
|
||||
#if _gizmo:
|
||||
#_gizmo._redraw()
|
||||
|
||||
# Stops the shake effect with a fade-out
|
||||
func stop_shake() -> void:
|
||||
if !Engine.is_editor_hint():
|
||||
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 Area3D):
|
||||
return ["First child must be Area3D"]
|
||||
return []
|
||||
|
||||
# Sets the shake progress
|
||||
func set_progress(value: float) -> void:
|
||||
if !Engine.is_editor_hint():
|
||||
timer = value
|
||||
_progress_shake()
|
||||
|
||||
# Setter for emit property
|
||||
func set_emit(value: bool) -> void:
|
||||
emit = value
|
||||
if !Engine.is_editor_hint():
|
||||
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
|
||||
1
addons/shaker/src/Vector3/ShakerEmitter3D.gd.uid
Normal file
1
addons/shaker/src/Vector3/ShakerEmitter3D.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vo46i2pfq0uf
|
||||
159
addons/shaker/src/Vector3/ShakerReceiver3D.gd
Normal file
159
addons/shaker/src/Vector3/ShakerReceiver3D.gd
Normal file
@@ -0,0 +1,159 @@
|
||||
@icon("res://addons/shaker/assets/ShakerReceiver3D.svg")
|
||||
@tool
|
||||
class_name ShakerReceiver3D
|
||||
extends Node3D
|
||||
|
||||
## Transmits values from ShakerEmitter3D to ShakerComponent3D
|
||||
|
||||
# Fade-in easing
|
||||
@export_exp_easing var enter_fade_in: float = 0.1:
|
||||
set = set_fade_in,
|
||||
get = get_fade_in
|
||||
|
||||
# Fade-out easing
|
||||
@export_exp_easing("attenuation") var exit_fade_out: float = 3.0:
|
||||
set = set_fade_out,
|
||||
get = get_fade_out
|
||||
|
||||
# Private variables
|
||||
var area3d: Area3D
|
||||
var position_offset: Vector3 = Vector3.ZERO
|
||||
var rotation_offset: Vector3 = Vector3.ZERO
|
||||
var scale_offset: Vector3 = Vector3.ZERO
|
||||
var emitter_list: Array[EmitterData]
|
||||
|
||||
# Called when the node enters the scene tree for the first time
|
||||
func _ready() -> void:
|
||||
_setup_area3d()
|
||||
if !Engine.is_editor_hint():
|
||||
add_to_group("ShakerReceiver")
|
||||
_connect_signals()
|
||||
|
||||
# Sets up the Area3D node
|
||||
func _setup_area3d() -> void:
|
||||
for child in get_children():
|
||||
if child is Area3D:
|
||||
area3d = child
|
||||
return
|
||||
|
||||
area3d = Area3D.new()
|
||||
var collision = CollisionShape3D.new()
|
||||
add_child(area3d)
|
||||
area3d.add_child(collision)
|
||||
area3d.set_owner(get_tree().edited_scene_root)
|
||||
collision.set_owner(get_tree().edited_scene_root)
|
||||
area3d.name = "Area3D"
|
||||
collision.name = "CollisionShape3D"
|
||||
|
||||
area3d.collision_layer = 0
|
||||
area3d.collision_mask = 1 << 9
|
||||
|
||||
# Connects signals
|
||||
func _connect_signals() -> void:
|
||||
area3d.area_entered.connect(on_area_entered)
|
||||
area3d.area_exited.connect(on_area_exited)
|
||||
|
||||
# Called every frame
|
||||
func _process(delta: float) -> void:
|
||||
if !Engine.is_editor_hint():
|
||||
position_offset = Vector3.ZERO
|
||||
rotation_offset = Vector3.ZERO
|
||||
scale_offset = Vector3.ZERO
|
||||
|
||||
if emitter_list.size() > 0:
|
||||
for emitter_data in emitter_list:
|
||||
_process_emitter(emitter_data, delta)
|
||||
|
||||
# Processes each emitter
|
||||
func _process_emitter(emitter_data: EmitterData, delta: float) -> void:
|
||||
if !Engine.is_editor_hint():
|
||||
if is_instance_valid(emitter_data.emitter):
|
||||
var ease_in: float = ease(emitter_data.timer, enter_fade_in)
|
||||
var ease_out: float = ease(1.0 - (emitter_data.timer - emitter_data.fade_out_timer), exit_fade_out) if emitter_data.fade_out_timer != 0.0 else 1.0
|
||||
emitter_data.ease_out_intensity = move_toward(emitter_data.ease_out_intensity, ease_out, delta)
|
||||
ease_out = emitter_data.ease_out_intensity
|
||||
var max_distance: float = emitter_data.emitter.max_distance
|
||||
var distance: float = min(emitter_data.emitter.global_position.distance_to(global_position), max_distance) / max(max_distance, 0.001)
|
||||
|
||||
var attenuation: float = ease(1.0 - distance, emitter_data.emitter.distance_attenuation)
|
||||
position_offset += emitter_data.emitter.shake_offset_position * ease_in * ease_out * attenuation
|
||||
rotation_offset += emitter_data.emitter.shake_offset_rotation * ease_in * ease_out * attenuation
|
||||
scale_offset += emitter_data.emitter.shake_offset_scale * ease_in * ease_out * attenuation
|
||||
emitter_data.timer += delta
|
||||
if ease_out <= delta:
|
||||
emitter_list.erase(emitter_data)
|
||||
else:
|
||||
emitter_list.erase(emitter_data)
|
||||
|
||||
|
||||
# Returns the current shake values
|
||||
func get_value() -> Array[Vector3]:
|
||||
return [position_offset, rotation_offset, scale_offset]
|
||||
|
||||
# Returns configuration warnings
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
if not get_parent() is ShakerComponent3D:
|
||||
return ["Parent must be ShakerComponent3D"]
|
||||
var _ex:bool = false
|
||||
for i in get_children():
|
||||
if i is Area3D:
|
||||
_ex = true
|
||||
break
|
||||
if !_ex:
|
||||
return ["ShakerReceiver3D needs Area3D to work"]
|
||||
return []
|
||||
|
||||
# Called when an area enters
|
||||
func on_area_entered(area: Area3D) -> void:
|
||||
var node = area.get_parent()
|
||||
if node is ShakerEmitter3D:
|
||||
var _exists = null
|
||||
for index in emitter_list.size():
|
||||
var data = emitter_list[index]
|
||||
if data.emitter == node: _exists = data
|
||||
if !_exists:
|
||||
var data = EmitterData.new(node)
|
||||
emitter_list.append(data)
|
||||
else:
|
||||
_exists.fade_out_timer = 0.0
|
||||
|
||||
# Called when an area exits
|
||||
func on_area_exited(area: Area3D) -> void:
|
||||
var node = area.get_parent()
|
||||
if node is ShakerEmitter3D:
|
||||
for index in emitter_list.size():
|
||||
var data = emitter_list[index]
|
||||
if data.emitter == node:
|
||||
data.fade_out_timer = data.timer
|
||||
break
|
||||
|
||||
# Setter for enter_fade_in
|
||||
func set_fade_in(value: float) -> void:
|
||||
enter_fade_in = value
|
||||
|
||||
# Getter for enter_fade_in
|
||||
func get_fade_in() -> float:
|
||||
return enter_fade_in
|
||||
|
||||
# Setter for exit_fade_out
|
||||
func set_fade_out(value: float) -> void:
|
||||
exit_fade_out = value
|
||||
|
||||
# Getter for exit_fade_out
|
||||
func get_fade_out() -> float:
|
||||
return exit_fade_out
|
||||
|
||||
# EmitterData inner class
|
||||
class EmitterData:
|
||||
var emitter: ShakerEmitter3D
|
||||
var timer: float = 0.0
|
||||
var fade_out_timer: float = 0.0
|
||||
var ease_out_intensity:float = 1.0
|
||||
|
||||
func _init(_emitter: ShakerEmitter3D) -> void:
|
||||
self.emitter = _emitter
|
||||
|
||||
func is_playing() -> bool:
|
||||
for i:EmitterData in emitter_list:
|
||||
return i.emitter.emitting
|
||||
return false
|
||||
1
addons/shaker/src/Vector3/ShakerReceiver3D.gd.uid
Normal file
1
addons/shaker/src/Vector3/ShakerReceiver3D.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://csr4nkcrmm0bq
|
||||
284
addons/shaker/src/Vector3/shaker_component3D.gd
Normal file
284
addons/shaker/src/Vector3/shaker_component3D.gd
Normal file
@@ -0,0 +1,284 @@
|
||||
@tool
|
||||
@icon("res://addons/shaker/assets/Shaker3D.svg")
|
||||
class_name ShakerComponent3D
|
||||
extends "res://addons/shaker/src/Vector3/ShakerBase3D.gd"
|
||||
|
||||
## Allows you to apply shake effect to any 3D node according to position, rotation, scale
|
||||
|
||||
enum ShakeAddMode {
|
||||
add,
|
||||
override
|
||||
}
|
||||
|
||||
# Custom target flag
|
||||
@export var custom_target: bool = false:
|
||||
set = set_custom_target,
|
||||
get = get_custom_target
|
||||
|
||||
# Array of target Node3D objects
|
||||
@export var Targets: Array[Node3D]
|
||||
|
||||
# Randomization flag
|
||||
@export var randomize: bool = false:
|
||||
set = set_randomize,
|
||||
get = get_randomize
|
||||
|
||||
# Playing state
|
||||
@export var is_playing: bool = false
|
||||
|
||||
@export var AutoPlay:bool = false
|
||||
|
||||
# Private variables
|
||||
var _last_position_shake: Array[Vector3] = [Vector3.ZERO]
|
||||
var _last_scale_shake: Array[Vector3] = [Vector3.ZERO]
|
||||
var _last_rotation_shake: Array[Vector3] = [Vector3.ZERO]
|
||||
var _timer_offset: float = 0.0
|
||||
var _fading_out: bool = false
|
||||
var _seed: float = 203445
|
||||
var _external_shakes:Array[ExternalShake]
|
||||
|
||||
# Called when the node enters the scene tree for the first time
|
||||
func _ready() -> void:
|
||||
set_process_input(false)
|
||||
set_process_internal(false)
|
||||
set_process_shortcut_input(false)
|
||||
set_process_unhandled_input(false)
|
||||
set_physics_process(false)
|
||||
set_physics_process_internal(false)
|
||||
add_to_group("ShakerComponent")
|
||||
if !Engine.is_editor_hint():
|
||||
if AutoPlay:
|
||||
play_shake()
|
||||
|
||||
# Resets the shaker to its initial state
|
||||
func _reset() -> void:
|
||||
_last_position_shake = [Vector3.ZERO]
|
||||
_last_scale_shake = [Vector3.ZERO]
|
||||
_last_rotation_shake = [Vector3.ZERO]
|
||||
_external_shakes.clear()
|
||||
_initalize_prev_positions()
|
||||
is_playing = false
|
||||
_initialize_timer_offset()
|
||||
_fading_out = false
|
||||
_initalize_target()
|
||||
|
||||
# Initializes previous positions for randomized shaking
|
||||
func _initalize_prev_positions() -> void:
|
||||
_last_position_shake.resize(Targets.size())
|
||||
_last_position_shake.fill(Vector3.ZERO)
|
||||
|
||||
_last_scale_shake.resize(Targets.size())
|
||||
_last_scale_shake.fill(Vector3.ZERO)
|
||||
|
||||
_last_rotation_shake.resize(Targets.size())
|
||||
_last_rotation_shake.fill(Vector3.ZERO)
|
||||
|
||||
# Called every frame
|
||||
func _process(delta: float) -> void:
|
||||
if is_playing:
|
||||
if shakerPreset != null || _external_shakes.size() > 0 || is_receiving_from_emitters():
|
||||
if timer <= duration || 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 && !_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 (!(duration > 0) || _fading_out) && is_playing:
|
||||
if _ease_out <= get_process_delta_time():
|
||||
force_stop_shake()
|
||||
|
||||
var _shake_position: Array[Vector3] = []
|
||||
var _shake_rotation: Array[Vector3] = []
|
||||
var _shake_scale: Array[Vector3] = []
|
||||
|
||||
var _count:int =(Targets.size() if randomize else 1)
|
||||
|
||||
_shake_position.resize(_count)
|
||||
_shake_position.fill(Vector3.ZERO)
|
||||
_shake_rotation.resize(_count)
|
||||
_shake_rotation.fill(Vector3.ZERO)
|
||||
_shake_scale.resize(_count)
|
||||
_shake_scale.fill(Vector3.ZERO)
|
||||
|
||||
for _index in _count:
|
||||
var _randomized: float = (_seed * (float(_index+1) / Targets.size())) if randomize else 0.0
|
||||
if _last_position_shake.size() != _count: _initalize_prev_positions()
|
||||
|
||||
# Shaker Preset
|
||||
if shakerPreset != null:
|
||||
var _value:float = timer + _randomized
|
||||
var _strength:float = intensity * _ease_in * _ease_out
|
||||
_shake_position[_index] += (shakerPreset.get_value(_value, ShakerPreset3D.Categories.POSITION) * _strength)
|
||||
_shake_rotation[_index] += (shakerPreset.get_value(_value, ShakerPreset3D.Categories.ROTATION) * _strength * (PI/2.0))
|
||||
_shake_scale[_index] += (shakerPreset.get_value(_value, ShakerPreset3D.Categories.SCALE) * _strength)
|
||||
|
||||
# External Shake Addition
|
||||
for external_shake:ExternalShake in _external_shakes:
|
||||
var _real_time:float = min(timer-external_shake.start_time, external_shake.duration)
|
||||
_ease_in = ease(_real_time/external_shake.duration, external_shake.fade_in)
|
||||
_ease_out = ease(1.0-(_real_time/external_shake.duration), external_shake.fade_out)
|
||||
var _value:float = (_real_time*external_shake.speed) + _randomized
|
||||
var _strength:float = external_shake.intensity * intensity * _ease_in * _ease_out
|
||||
var _mode_value:Array[Vector3] = [Vector3.ZERO, Vector3.ZERO, Vector3.ZERO]
|
||||
match external_shake.mode:
|
||||
ShakeAddMode.add:
|
||||
_mode_value = [_shake_position[_index], _shake_rotation[_index], _shake_scale[_index] ]
|
||||
_shake_position[_index] = _mode_value[0] + (external_shake.preset.get_value(_value, ShakerPreset3D.Categories.POSITION) * _strength)
|
||||
_shake_rotation[_index] = _mode_value[1] +(external_shake.preset.get_value(_value, ShakerPreset3D.Categories.ROTATION) * _strength * (PI/2.0))
|
||||
_shake_scale[_index] = _mode_value[2] + (external_shake.preset.get_value(_value, ShakerPreset3D.Categories.SCALE) * _strength)
|
||||
|
||||
if _real_time >= external_shake.duration:
|
||||
_external_shakes.erase(external_shake)
|
||||
|
||||
# Shake Emitter
|
||||
for _child in get_children():
|
||||
if _child is ShakerReceiver3D:
|
||||
_shake_position[_index] += _child.position_offset
|
||||
_shake_rotation[_index] += _child.rotation_offset
|
||||
_shake_scale[_index] += _child.scale_offset
|
||||
|
||||
for index: int in Targets.size():
|
||||
var target: Node3D = Targets[index]
|
||||
if !is_instance_valid(target):
|
||||
Targets.remove_at(index)
|
||||
index-=1
|
||||
if Targets.size() <= 0:
|
||||
shake_finished.emit()
|
||||
break
|
||||
|
||||
var _i: int = fmod(index, _shake_position.size())
|
||||
target.position += -_last_position_shake[_i] + _shake_position[_i]
|
||||
target.rotation += -_last_rotation_shake[_i] + _shake_rotation[_i]
|
||||
target.scale += -_last_scale_shake[_i] + _shake_scale[_i]
|
||||
|
||||
_last_position_shake = _shake_position
|
||||
_last_rotation_shake = _shake_rotation
|
||||
_last_scale_shake = _shake_scale
|
||||
|
||||
# Stops the shake effect with a fade-out
|
||||
func stop_shake() -> void:
|
||||
if !_fading_out:
|
||||
_timer_offset = timer
|
||||
_fading_out = true
|
||||
shake_fading_out.emit()
|
||||
|
||||
# Immediately stops the shake effect
|
||||
func force_stop_shake() -> void:
|
||||
if is_playing || _fading_out:
|
||||
set_progress(0.0)
|
||||
_reset()
|
||||
shake_finished.emit()
|
||||
|
||||
# Starts the shake effect
|
||||
func play_shake() -> void:
|
||||
_initalize_target()
|
||||
randomize_shake()
|
||||
is_playing = !is_playing if Engine.is_editor_hint() else true
|
||||
_fading_out = false
|
||||
_initialize_timer_offset()
|
||||
shake_started.emit()
|
||||
|
||||
func randomize_shake() -> void:
|
||||
_seed = randf_range(10000, 99999)
|
||||
|
||||
func _initialize_timer_offset() -> void:
|
||||
if !(duration > 0): _timer_offset = 0x80000
|
||||
else: _timer_offset = 0.0
|
||||
|
||||
func _initalize_target() -> void:
|
||||
if !custom_target:
|
||||
Targets.clear()
|
||||
if get_parent() is Node3D:
|
||||
Targets.append(get_parent())
|
||||
|
||||
# Placeholder for shake function
|
||||
func shake(shaker_preset:ShakerPreset3D, _mode:ShakeAddMode=ShakeAddMode.add, duration:float=1.0, speed:float=1.0, intensity:float=1.0, fade_in:float=.25, fade_out:float=.25) -> void:
|
||||
var external_shake:ExternalShake = ExternalShake.new()
|
||||
external_shake.preset = shaker_preset
|
||||
external_shake.duration = duration
|
||||
external_shake.speed = speed
|
||||
external_shake.intensity = intensity
|
||||
external_shake.start_time = timer
|
||||
external_shake.fade_in = fade_in
|
||||
external_shake.fade_out = fade_out
|
||||
external_shake.mode = _mode
|
||||
_external_shakes.append(external_shake)
|
||||
if Targets.is_empty():
|
||||
_initalize_target()
|
||||
is_playing = true
|
||||
|
||||
# Validates property visibility
|
||||
func _validate_property(property: Dictionary) -> void:
|
||||
if property.name == "Targets" || property.name == "randomize":
|
||||
if !custom_target:
|
||||
property.usage = PROPERTY_USAGE_NONE
|
||||
if property.name == "fade_time":
|
||||
if duration > 0:
|
||||
property.usage = PROPERTY_USAGE_NONE
|
||||
|
||||
# Get configuration warnings
|
||||
func _get_configuration_warnings() -> PackedStringArray:
|
||||
if !custom_target:
|
||||
if not get_parent() is Node3D:
|
||||
return ["Parent must be Node3D"]
|
||||
|
||||
return []
|
||||
|
||||
# Sets the shake progress
|
||||
func set_progress(value: float) -> void:
|
||||
timer = value
|
||||
_progress_shake()
|
||||
|
||||
# Custom setter and getter functions for @export variables
|
||||
func set_custom_target(value: bool) -> void:
|
||||
custom_target = value
|
||||
notify_property_list_changed()
|
||||
|
||||
func get_custom_target() -> bool:
|
||||
return custom_target
|
||||
|
||||
func set_randomize(value: bool) -> void:
|
||||
if custom_target && Targets.size() > 1:
|
||||
for index: int in Targets.size():
|
||||
var target: Node3D = Targets[index]
|
||||
var i = fmod(index, _last_position_shake.size())
|
||||
target.position += -_last_position_shake[i]
|
||||
target.rotation += -_last_rotation_shake[i]
|
||||
target.scale += -_last_scale_shake[i]
|
||||
_last_position_shake.fill(_last_position_shake[0])
|
||||
_last_rotation_shake.fill(_last_rotation_shake[0])
|
||||
_last_scale_shake.fill(_last_scale_shake[0])
|
||||
randomize = value
|
||||
randomize_shake()
|
||||
|
||||
func get_randomize() -> bool:
|
||||
return randomize
|
||||
|
||||
class ExternalShake:
|
||||
var preset:ShakerPreset3D
|
||||
var duration:float = 1.0
|
||||
var speed:float = 1.0
|
||||
var intensity:float = 1.0
|
||||
var start_time:float = 0.0
|
||||
var fade_in:float = 0.25
|
||||
var fade_out:float = 0.25
|
||||
var mode:ShakeAddMode=ShakeAddMode.add
|
||||
|
||||
func is_receiving_from_emitters() -> bool:
|
||||
for _child in get_children():
|
||||
if _child is ShakerReceiver3D:
|
||||
return _child.is_playing()
|
||||
return false
|
||||
1
addons/shaker/src/Vector3/shaker_component3D.gd.uid
Normal file
1
addons/shaker/src/Vector3/shaker_component3D.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dnlxsrumw6ygp
|
||||
Reference in New Issue
Block a user