xiandie/scene/entity/audio/sfx.gd
2025-07-20 20:05:59 +08:00

157 lines
3.9 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.

@tool
class_name Sfx extends AudioStreamPlayer
const META_ORIGINAL_STREAM = &"original_stream"
@export_enum("交互与效果音", "BGM", "场景背景音") var mode := "交互与效果音":
set(value):
_set_up_process_mode_by_mode()
mode = value
notify_property_list_changed()
# 当前播放状态注册;节点销毁前卸载
# TODO BGM 过程抑制场景音效;场景音效随玩家运动呼吸 (结合 Sfx2D)
# 感应玩家移动:装饰音
var default_db := 0.0
# 只有 场景背景音 生效
var scene_loop := true
var scene_sense_player_mov := false
func _ready() -> void:
bus = &"game_sfx"
default_db = volume_db
if Engine.is_editor_hint():
return
# 记录原 stream
if stream:
set_meta(META_ORIGINAL_STREAM, stream)
else:
set_meta(META_ORIGINAL_STREAM, null)
_set_up_process_mode_by_mode()
finished.connect(_on_finished)
# ground 退出时process mode 切换为 alwaysease out
SceneManager.ground_transition_pre_paused.connect(_on_ground_transition_pre_paused)
SceneManager.pause_counter_updated.connect(_on_pause_counter_updated)
var playing_on_debugging_paused = false
func _on_pause_counter_updated() -> void:
_set_up_process_mode_by_mode()
func _set_up_process_mode_by_mode():
# 如果是 debug panel则 pause
if mode == "场景背景音" and not SceneManager.pause_counter_arr.has("debugging"):
process_mode = Node.PROCESS_MODE_ALWAYS
else:
if SceneManager.pause_counter_arr.has("debugging"):
playing_on_debugging_paused = playing
process_mode = Node.PROCESS_MODE_PAUSABLE
func _on_ground_transition_pre_paused():
if not playing:
return
print("[GroundTransition] Sfx %s ease killing..." % name)
easing_kill(1.0)
func _on_finished() -> void:
if mode == "场景背景音" and scene_loop:
play()
func reset_original_stream(ignore_null := true):
var original_stream = get_meta(META_ORIGINAL_STREAM)
if original_stream != null:
replace_stream(original_stream)
elif not ignore_null:
stream = null
func replace_stream(new_stream: AudioStream) -> void:
stream = new_stream
if (playing_on_debugging_paused or autoplay) and is_node_ready():
play()
func stream_was_replaced() -> bool:
return get_meta(META_ORIGINAL_STREAM) != stream
func resart(ease_duration := 1.0):
easing_kill(ease_duration).tween_callback(play)
# queue free 导致 sfx 无法播放,使用全局声源
func global_play() -> void:
if stream:
AudioManager.play_sfx(stream)
func easing_kill(duration: float = 2.0) -> Tween:
# stop with easing
var tween = create_tween()
tween.bind_node(self)
if playing:
tween.tween_property(self, "volume_linear", 0.0, duration)
tween.tween_callback(stop)
# set volume_db back to default
tween.tween_callback(func(): volume_db = default_db)
return tween
func _get_property_list() -> Array[Dictionary]:
if mode != "场景背景音":
return []
# # 只有 场景背景音 生效
# var scene_loop := true
# var scene_autostart := true
# var scene_sense_player_mov := false
return [
{
"name": "场景背景音配置项",
"type": TYPE_NIL,
"usage": PROPERTY_USAGE_GROUP,
},
{"name": "循环播放", "type": TYPE_BOOL},
{"name": "感应玩家操作", "type": TYPE_BOOL}
]
func _property_can_revert(property: StringName) -> bool:
return property == "循环播放" or property == "感应玩家操作"
func _property_get_revert(property: StringName) -> Variant:
if property == "循环播放":
return true
elif property == "感应玩家操作":
return false
return null
func _set(property: StringName, value: Variant) -> bool:
if mode != "场景背景音":
return false
elif property == "循环播放":
scene_loop = value
return true
elif property == "感应玩家操作":
scene_sense_player_mov = value
return true
return false
func _get(property: StringName) -> Variant:
if mode != "场景背景音":
return null
elif property == "循环播放":
return scene_loop
elif property == "感应玩家操作":
return scene_sense_player_mov
return null