소스 정보
- 저장소
- infometa/workbuddyskills
- 최근 소스 활동
- 2026년 7월 21일 03:35
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 205
- 포크
- 65
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/infometa/workbuddyskills --skill audio-system명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | audio-system |
| description | 音频系统技能。管理游戏音频播放,包括背景音乐、音效、语音等。 支持音频池、淡入淡出、3D 音效等功能。 |
| version | 1.0.0 |
| dependencies | ["godot-core"] |
| triggers | [{"pattern":"音频|音乐|音效|BGM|SFX|AudioManager"}] |
| inputs | [{"name":"audio_type","type":"string","enum":["music","sfx","voice","ambient"],"required":false}] |
| outputs | [{"name":"audio_manager","type":"file","path_pattern":"res://scripts/autoload/audio_manager.gd"}] |
管理游戏中的所有音频播放。
在 Godot 中配置以下音频总线:
Master
├── Music # 背景音乐
├── SFX # 音效
├── Voice # 语音
└── Ambient # 环境音
## 音频管理器
##
## 统一管理所有音频播放
## @author AI Generated
## @version 1.0.0
class_name AudioManager
extends Node
# region 信号
signal music_started(track_name: String)
signal music_stopped
signal music_changed(old_track: String, new_track: String)
# endregion
# region 常量
const MUSIC_BUS = "Music"
const SFX_BUS = "SFX"
const VOICE_BUS = "Voice"
const AMBIENT_BUS = "Ambient"
# endregion
# region 导出变量
@export var sfx_pool_size: int = 16
@export var default_music_fade_time: float = 1.0
# endregion
# region 节点引用
var _music_player: AudioStreamPlayer
var _music_player_2: AudioStreamPlayer # 用于交叉淡化
var _voice_player: AudioStreamPlayer
var _ambient_player: AudioStreamPlayer
var _sfx_pool: Array[AudioStreamPlayer] = []
var _sfx_pool_3d: Array[AudioStreamPlayer3D] = []
# endregion
# region 状态
var current_music: String = ""
var _music_volume: float = 1.0
var _sfx_volume: float = 1.0
var _voice_volume: float = 1.0
var _is_music_fading: bool = false
var _active_player: AudioStreamPlayer = null
# endregion
# region 预加载音频缓存
var _audio_cache: Dictionary = {}
# endregion
# region 生命周期
func _ready() -> void:
_setup_music_players()
_setup_sfx_pool()
_setup_voice_player()
_setup_ambient_player()
func _setup_music_players() -> void:
_music_player = AudioStreamPlayer.new()
_music_player.bus = MUSIC_BUS
add_child(_music_player)
_music_player_2 = AudioStreamPlayer.new()
_music_player_2.bus = MUSIC_BUS
add_child(_music_player_2)
_active_player = _music_player
func _setup_sfx_pool() -> void:
for i in range(sfx_pool_size):
var player = AudioStreamPlayer.new()
player.bus = SFX_BUS
add_child(player)
_sfx_pool.append(player)
var player_3d = AudioStreamPlayer3D.new()
player_3d.bus = SFX_BUS
add_child(player_3d)
_sfx_pool_3d.append(player_3d)
func _setup_voice_player() -> void:
_voice_player = AudioStreamPlayer.new()
_voice_player.bus = VOICE_BUS
add_child(_voice_player)
func _setup_ambient_player() -> void:
_ambient_player = AudioStreamPlayer.new()
_ambient_player.bus = AMBIENT_BUS
add_child(_ambient_player)
# endregion
# region 音乐播放
## 播放背景音乐
func play_music(track_path: String, fade_time: float = -1, loop: bool = true) -> void:
if fade_time < 0:
fade_time = default_music_fade_time
var stream = _get_audio_stream(track_path)
if not stream:
push_error("Music not found: " + track_path)
return
var old_track = current_music
current_music = track_path
if fade_time > 0 and _active_player.playing:
_crossfade_music(stream, fade_time, loop)
else:
_active_player.stream = stream
_active_player.play()
music_started.emit(track_path)
if old_track != track_path:
music_changed.emit(old_track, track_path)
## 停止背景音乐
func stop_music(fade_time: float = -1) -> void:
if fade_time < 0:
fade_time = default_music_fade_time
if fade_time > 0:
_fade_out_music(fade_time)
else:
_active_player.stop()
current_music = ""
music_stopped.emit()
## 暂停背景音乐
func pause_music() -> void:
_active_player.stream_paused = true
## 继续背景音乐
func resume_music() -> void:
_active_player.stream_paused = false
## 交叉淡化
func _crossfade_music(new_stream: AudioStream, duration: float, loop: bool) -> void:
_is_music_fading = true
var old_player = _active_player
var new_player = _music_player if _active_player == _music_player_2 else _music_player_2
new_player.stream = new_stream
new_player.volume_db = -80
new_player.play()
var tween = create_tween()
tween.set_parallel(true)
tween.tween_property(old_player, "volume_db", -80, duration)
tween.tween_property(new_player, "volume_db", linear_to_db(_music_volume), duration)
await tween.finished
old_player.stop()
old_player.volume_db = linear_to_db(_music_volume)
_active_player = new_player
_is_music_fading = false
func _fade_out_music(duration: float) -> void:
var tween = create_tween()
tween.tween_property(_active_player, "volume_db", -80, duration)
await tween.finished
_active_player.stop()
_active_player.volume_db = linear_to_db(_music_volume)
# endregion
# region 音效播放
## 播放 2D 音效
func play_sfx(sound_path: String, volume: float = 1.0, pitch: float = 1.0) -> AudioStreamPlayer:
var stream = _get_audio_stream(sound_path)
if not stream:
push_error("SFX not found: " + sound_path)
return null
var player = _get_available_sfx_player()
if player:
player.stream = stream
player.volume_db = linear_to_db(volume * _sfx_volume)
player.pitch_scale = pitch
player.play()
return player
## 播放带随机音高的音效
func play_sfx_pitched(sound_path: String, pitch_range: Vector2 = Vector2(0.9, 1.1), volume: float = 1.0) -> AudioStreamPlayer:
var pitch = randf_range(pitch_range.x, pitch_range.y)
return play_sfx(sound_path, volume, pitch)
## 播放 3D 音效
func play_sfx_3d(sound_path: String, position: Vector3, volume: float = 1.0, pitch: float = 1.0) -> AudioStreamPlayer3D:
var stream = _get_audio_stream(sound_path)
if not stream:
push_error("SFX not found: " + sound_path)
return null
var player = _get_available_sfx_player_3d()
if player:
player.stream = stream
player.global_position = position
player.volume_db = linear_to_db(volume * _sfx_volume)
player.pitch_scale = pitch
player.play()
return player
## 播放 3D 音效(附加到节点)
func play_sfx_at_node(sound_path: String, node: Node3D, volume: float = 1.0) -> AudioStreamPlayer3D:
return play_sfx_3d(sound_path, node.global_position, volume)
func _get_available_sfx_player() -> AudioStreamPlayer:
for player in _sfx_pool:
if not player.playing:
return player
# 如果所有都在使用,返回第一个(会被覆盖)
return _sfx_pool[0]
func _get_available_sfx_player_3d() -> AudioStreamPlayer3D:
for player in _sfx_pool_3d:
if not player.playing:
return player
return _sfx_pool_3d[0]
# endregion
# region 语音播放
## 播放语音
func play_voice(voice_path: String, on_finished: Callable = Callable()) -> void:
var stream = _get_audio_stream(voice_path)
if not stream:
push_error("Voice not found: " + voice_path)
return
# 停止当前语音
if _voice_player.playing:
_voice_player.stop()
_voice_player.stream = stream
_voice_player.play()
if on_finished.is_valid():
await _voice_player.finished
on_finished.call()
## 停止语音
func stop_voice() -> void:
_voice_player.stop()
## 检查语音是否在播放
func is_voice_playing() -> bool:
return _voice_player.playing
# endregion
# region 环境音
## 播放环境音
func play_ambient(ambient_path: String, fade_time: float = 1.0) -> void:
var stream = _get_audio_stream(ambient_path)
if not stream:
return
_ambient_player.stream = stream
_ambient_player.volume_db = -80
_ambient_player.play()
var tween = create_tween()
tween.tween_property(_ambient_player, "volume_db", 0, fade_time)
## 停止环境音
func stop_ambient(fade_time: float = 1.0) -> void:
var tween = create_tween()
tween.tween_property(_ambient_player, "volume_db", -80, fade_time)
await tween.finished
_ambient_player.stop()
# endregion
# region 音量控制
func set_master_volume(volume: float) -> void:
var bus_index = AudioServer.get_bus_index("Master")
AudioServer.set_bus_volume_db(bus_index, linear_to_db(volume))
func set_music_volume(volume: float) -> void:
_music_volume = volume
var bus_index = AudioServer.get_bus_index(MUSIC_BUS)
AudioServer.set_bus_volume_db(bus_index, linear_to_db(volume))
func set_sfx_volume(volume: float) -> void:
_sfx_volume = volume
var bus_index = AudioServer.get_bus_index(SFX_BUS)
AudioServer.set_bus_volume_db(bus_index, linear_to_db(volume))
func set_voice_volume(volume: float) -> void:
_voice_volume = volume
var bus_index = AudioServer.get_bus_index(VOICE_BUS)
AudioServer.set_bus_volume_db(bus_index, linear_to_db(volume))
func get_music_volume() -> float:
return _music_volume
func get_sfx_volume() -> float:
return _sfx_volume
func get_voice_volume() -> float:
return _voice_volume
## 静音/取消静音
func mute_bus(bus_name: String, muted: bool) -> void:
var bus_index = AudioServer.get_bus_index(bus_name)
AudioServer.set_bus_mute(bus_index, muted)
func is_bus_muted(bus_name: String) -> bool:
var bus_index = AudioServer.get_bus_index(bus_name)
return AudioServer.is_bus_mute(bus_index)
# endregion
# region 音频缓存
func preload_audio(paths: Array) -> void:
for path in paths:
_get_audio_stream(path)
func _get_audio_stream(path: String) -> AudioStream:
if _audio_cache.has(path):
return _audio_cache[path]
if ResourceLoader.exists(path):
var stream = load(path)
_audio_cache[path] = stream
return stream
return null
func clear_audio_cache() -> void:
_audio_cache.clear()
# endregion
## 音效触发器
##
## 在特定事件时播放音效
class_name SFXTrigger
extends Node
@export var sound_path: String
@export var volume: float = 1.0
@export var pitch_variation: Vector2 = Vector2(1.0, 1.0)
@export var trigger_on_ready: bool = false
@export var cooldown: float = 0.0
var _can_play: bool = true
func _ready() -> void:
if trigger_on_ready:
play()
func play() -> void:
if not _can_play:
return
var pitch = randf_range(pitch_variation.x, pitch_variation.y)
AudioManager.play_sfx(sound_path, volume, pitch)
if cooldown > 0:
_can_play = false
await get_tree().create_timer(cooldown).timeout
_can_play = true
## 区域音效触发器
class_name AreaSFXTrigger
extends Area3D
@export var sound_path: String
@export var volume: float = 1.0
@export var one_shot: bool = true
var _triggered: bool = false
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node3D) -> void:
if one_shot and _triggered:
return
if body.is_in_group("player"):
AudioManager.play_sfx_3d(sound_path, global_position, volume)
_triggered = true
## 音乐区域
##
## 进入区域时切换背景音乐
class_name MusicZone
extends Area3D
@export var music_path: String
@export var fade_time: float = 2.0
@export var priority: int = 0
var _previous_music: String = ""
func _ready() -> void:
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
func _on_body_entered(body: Node3D) -> void:
if body.is_in_group("player"):
_previous_music = AudioManager.current_music
AudioManager.play_music(music_path, fade_time)
func _on_body_exited(body: Node3D) -> void:
if body.is_in_group("player"):
if _previous_music:
AudioManager.play_music(_previous_music, fade_time)
## 随机音效播放器
##
## 从列表中随机播放音效
class_name RandomSFXPlayer
extends Node
@export var sounds: Array[String] = []
@export var volume: float = 1.0
@export var pitch_variation: Vector2 = Vector2(0.9, 1.1)
@export var avoid_repeat: bool = true
var _last_played_index: int = -1
func play() -> void:
if sounds.is_empty():
return
var index = randi() % sounds.size()
if avoid_repeat and sounds.size() > 1:
while index == _last_played_index:
index = randi() % sounds.size()
_last_played_index = index
var pitch = randf_range(pitch_variation.x, pitch_variation.y)
AudioManager.play_sfx(sounds[index], volume, pitch)
func play_at_position(position: Vector3) -> void:
if sounds.is_empty():
return
var index = randi() % sounds.size()
if avoid_repeat and sounds.size() > 1:
while index == _last_played_index:
index = randi() % sounds.size()
_last_played_index = index
var pitch = randf_range(pitch_variation.x, pitch_variation.y)
AudioManager.play_sfx_3d(sounds[index], position, volume, pitch)
## 脚步声系统
class_name FootstepSystem
extends Node
@export var character: CharacterBody3D
@export var step_interval: float = 0.5
@export var run_step_interval: float = 0.3
var _step_timer: float = 0.0
var _surface_detector: RayCast3D
# 表面类型 -> 音效列表
var _surface_sounds: Dictionary = {
"default": ["res://assets/audio/sfx/footsteps/concrete_01.ogg"],
"grass": ["res://assets/audio/sfx/footsteps/grass_01.ogg"],
"wood": ["res://assets/audio/sfx/footsteps/wood_01.ogg"],
"metal": ["res://assets/audio/sfx/footsteps/metal_01.ogg"],
"water": ["res://assets/audio/sfx/footsteps/water_01.ogg"]
}
func _ready() -> void:
_setup_surface_detector()
func _setup_surface_detector() -> void:
_surface_detector = RayCast3D.new()
_surface_detector.target_position = Vector3(0, -1, 0)
character.add_child(_surface_detector)
func _physics_process(delta: float) -> void:
if not character.is_on_floor():
return
var velocity = character.velocity
var speed = Vector2(velocity.x, velocity.z).length()
if speed < 0.1:
return
_step_timer += delta
var interval = run_step_interval if speed > 5.0 else step_interval
if _step_timer >= interval:
_step_timer = 0.0
_play_footstep()
func _play_footstep() -> void:
var surface = _detect_surface()
var sounds = _surface_sounds.get(surface, _surface_sounds["default"])
var sound = sounds[randi() % sounds.size()]
AudioManager.play_sfx_pitched(sound, Vector2(0.9, 1.1), 0.7)
func _detect_surface() -> String:
if _surface_detector.is_colliding():
var collider = _surface_detector.get_collider()
if collider.has_meta("surface_type"):
return collider.get_meta("surface_type")
return "default"
{
"music": {
"main_menu": "res://assets/audio/music/main_menu.ogg",
"gameplay": "res://assets/audio/music/gameplay.ogg",
"boss": "res://assets/audio/music/boss.ogg",
"victory": "res://assets/audio/music/victory.ogg",
"game_over": "res://assets/audio/music/game_over.ogg"
},
"sfx": {
"ui": {
"click": "res://assets/audio/sfx/ui/click.ogg",
"hover": "res://assets/audio/sfx/ui/hover.ogg",
"confirm": "res://assets/audio/sfx/ui/confirm.ogg",
"cancel": "res://assets/audio/sfx/ui/cancel.ogg"
},
"player": {
"jump": "res://assets/audio/sfx/player/jump.ogg"
# 播放背景音乐
AudioManager.play_music("res://assets/audio/music/gameplay.ogg")
# 带淡入淡出切换音乐
AudioManager.play_music("res://assets/audio/music/boss.ogg", 2.0)
# 播放音效
AudioManager.play_sfx("res://assets/audio/sfx/explosion.ogg")
# 播放带随机音高的音效
AudioManager.play_sfx_pitched("res://assets/audio/sfx/hit.ogg")
# 播放 3D 音效
AudioManager.play_sfx_3d("res://assets/audio/sfx/explosion.ogg", enemy.global_position)
# 播放语音
AudioManager.play_voice("res://assets/audio/voice/greeting.ogg", func():
print("Voice finished")
)
# 调整音量
AudioManager.set_music_volume(0.5)
AudioManager.set_sfx_volume(0.8)