15、音频系统

作者:狂人开飞机日期:2026/9/15

第15章 音频系统

15.1 AudioStreamPlayer 基础

1# AudioStreamPlayer:非位置音频(BGM、UI音效)
2# AudioStreamPlayer2D:2D 位置音频(距离衰减)
3# AudioStreamPlayer3D:3D 位置音频
4
5extends AudioStreamPlayer
6
7func _ready() -> void:
8    stream = preload("res://audio/bgm/battle_theme.ogg")
9    volume_db = -10  # 音量(分贝)
10    pitch_scale = 1.0
11    autoplay = false
12    bus = "Music"  # 音频总线
13
14func play_bgm() -> void:
15    if not playing:
16        play()
17
18func stop_bgm() -> void:
19    if playing:
20        # 淡出
21        var tween := create_tween()
22        tween.tween_property(self, "volume_db", -40.0, 1.0)
23        await tween.finished
24        stop()
25        volume_db = -10  # 恢复音量
26

15.2 AudioStreamRandomizer(随机音效变体)

1# AudioStreamRandomizer 自动在多个音效中随机选择
2# 避免重复播放同一音效产生机械感
3
4# 设置:
5# 1. 创建 AudioStreamRandomizer 资源
6# 2. 添加多个 AudioStream(如不同的脚步声)
7# 3. 设置 Playback Mode: Random (No Repeat)
8# 4. 设置 Random Pitch: 0.9 ~ 1.1(音高随机变化)
9
10# 代码使用
11extends AudioStreamPlayer2D
12
13func play_footstep() -> void:
14    stream = preload("res://audio/sfx/footstep_randomizer.tres")
15    pitch_scale = randf_range(0.9, 1.1)  # 额外随机
16    play()
17

15.3 音频总线(Audio Bus)

1# 音频总线用于分组控制音量和效果
2# 设置:底部面板  Audio  Audio Bus
3
4# 推荐总线布局:
5# Bus 0: Master(主音量)
6# Bus 1: Music(背景音乐)
7# Bus 2: SFX(音效)
8# Bus 3: Voice(语音)
9# Bus 4: Ambient(环境音)
10
11# 代码控制
12func set_music_volume(volume_db: float) -> void:
13    var bus_idx := AudioServer.get_bus_index("Music")
14    AudioServer.set_bus_volume_db(bus_idx, volume_db)
15
16func set_sfx_mute(muted: bool) -> void:
17    var bus_idx := AudioServer.get_bus_index("SFX")
18    AudioServer.set_bus_mute(bus_idx, muted)
19
20# 添加音频效果
21func add_reverb_to_sfx() -> void:
22    var bus_idx := AudioServer.get_bus_index("SFX")
23    var reverb := AudioEffectReverb.new()
24    reverb.room_size = 0.8
25    reverb.damping = 0.5
26    AudioServer.add_bus_effect(bus_idx, reverb)
27

15.4 位置音频(AudioStreamPlayer2D)

1extends AudioStreamPlayer2D
2
3func _ready() -> void:
4    # 距离衰减设置
5    max_distance = 500.0  # 超过这个距离听不到
6    # attenuation 属性在 Godot 4.x 中通过衰减曲线控制
7    # 默认使用反距离衰减
8
9# 爆炸音效(在特定位置播放)
10func play_explosion_sound(pos: Vector2) -> void:
11    var sound := AudioStreamPlayer2D.new()
12    sound.stream = preload("res://audio/sfx/explosion.wav")
13    sound.global_position = pos
14    sound.max_distance = 800.0
15    add_child(sound)
16    sound.play()
17    sound.finished.connect(sound.queue_free)
18

15.5 动态音乐系统

1extends Node
2
3@onready var exploration_player: AudioStreamPlayer = $ExplorationMusic
4@onready var combat_player: AudioStreamPlayer = $CombatMusic
5
6var is_in_combat: bool = false
7var crossfade_duration: float = 2.0
8
9func enter_combat() -> void:
10    if is_in_combat:
11        return
12    is_in_combat = true
13    
14    # 交叉渐变
15    combat_player.volume_db = -40
16    combat_player.play()
17    
18    var tween := create_tween().set_parallel(true)
19    tween.tween_property(exploration_player, "volume_db", -40, crossfade_duration)
20    tween.tween_property(combat_player, "volume_db", 0, crossfade_duration)
21
22func exit_combat() -> void:
23    if not is_in_combat:
24        return
25    is_in_combat = false
26    
27    var tween := create_tween().set_parallel(true)
28    tween.tween_property(combat_player, "volume_db", -40, crossfade_duration)
29    tween.tween_property(exploration_player, "volume_db", 0, crossfade_duration)
30    await tween.finished
31    combat_player.stop()
32

15.6 对话语音

1extends RichTextLabel
2
3@export var voice_stream: AudioStreamPlayer
4
5var is_typing: bool = false
6var typing_speed: float = 0.03  # 每个字符的时间
7
8func show_dialogue(text: String, voice: AudioStream = null) -> void:
9    visible_characters = 0
10    self.text = text
11    is_typing = true
12    
13    if voice and voice_stream:
14        voice_stream.stream = voice
15        voice_stream.play()
16    
17    for i in text.length():
18        if not is_typing:
19            break
20        visible_characters = i + 1
21        await get_tree().create_timer(typing_speed).timeout
22    
23    visible_characters = -1  # 显示全部
24    is_typing = false
25
26func skip_typing() -> void:
27    is_typing = false
28    visible_characters = -1
29

15、音频系统》 是转载文章,点击查看原文


相关推荐


Git-SVN 混合开发,从入门到精通!
重生之我来学Python2026/9/7

Git-SVN 混合开发:命令对照、工作流程与 15+ 实战技巧,一篇搞定! 摘要 本文详细介绍 Git-SVN 混合开发的完整方案,涵盖从安装配置到实战操作的全流程。通过 Git 命令与 SVN 命令的对照表格、15+ 个核心命令详解、以及真实场景下的工作流程,帮助你在 SVN 项目中享受 Git 的便利。无论你是被迫使用 SVN 的 Git 用户,还是想尝试 Git 功能的 SVN 团队,这篇文章都能让你快速上手 Git-SVN 混合开发模式。 在 SVN 项目中使用 Git 本地分支、暂


Highcharts 双 Y 轴分组错位柱状图・代码详解
Highcharts.js2026/8/30

双 Y 轴、非分组错位并列柱状图,同时展示两类指标: 左轴:员工数量(人数,普通数值)右轴:利润(百万美元,金额) 同一分类下 4 根柱子错开摆放,不会互相重叠,用来对比「优化前 / 优化后」两组数据差异。 示例代码 Highcharts.chart('container', { chart: { type: 'column' }, title: { text: 'Efficiency Optimization by Branch


OpenCV实战——透视变换与身份证号码模板识别
clorinda2026/8/22

一、前言》》》》》》》 在现实生活中,发票、身份证和纸张通常不是完全正对着摄像头拍摄的。由于拍摄角度不同,原本的矩形会变成梯形,文字也会发生倾斜。 如果直接对这种图片进行识别,效果往往不理想。因此,通常需要先完成透视变换,把图像矫正成正面视图,再进行二值化、轮廓检测和模板匹配。 本篇主要介绍: 1,透视变换的原理; 2,四个角点的排序; 3,发票图像矫正; 4,形态学闭运算; 5,使用模板匹配识别身份证号码。 二、透视变换的基本原理 透视变换可以把原图中的任意四边形转换成一


Rust Borrow借用详解:不转移所有权访问数据
程序员爱钓鱼2026/8/9

《Rust编程实战》系列第35篇 在上一篇文章中,我们学习了Rust的Copy,了解了哪些轻量数据类型可以在赋值或传参时自动复制。本篇继续进入Rust所有权体系中非常重要的概念: Borrow 中文通常称为“借用”。 前面我们已经知道,String、Vec等非Copy类型传递给另一个变量或函数时,通常会发生所有权移动: fn print_name(name: String) { println!("{}", name); } fn main() { let name = S


Rust图像处理第22节-从RGB到YCbCr: 让亮度和颜色分家
花褪残红青杏小2026/7/31

🦀 Rust + WASM 实战系列 第 22 篇 阅读时间:约 8 分钟 | 实战可运行 📌 写在前面 前面 21 篇,所有图像都是拿 R、G、B 三个数表示的。这一篇换一套坐标系:一个亮度 + 两个色度。 手机里的每一张照片、你看的每一个视频,存进文件之前都会先做这个转换。原因很简单:人眼对亮度极其敏感,对颜色极其迟钝,把两者分开之后,颜色那部分可以随便砍。 数学上,它就是一个 3×3 矩阵(19节的矩阵 × 向量)加它的逆矩阵(21节的 try_inverse) 🚀 TL;DR


🤯 面试被问 AI Workflow 和 Agent 有啥区别?3 张图 + 2 段代码讲清楚!
ReBound2026/7/23

🤯 面试被问 AI Workflow 和 Agent 有啥区别?3 张图 + 2 段代码讲清楚! 摘要:Workflow 和 Agent 到底有啥区别?为什么 Coze 工作流和 ReAct Agent 看起来都在"执行任务",本质却完全不同?本文从面试翻车经历出发,结合 Anthropic 官方定义、ReAct 框架、完整代码示例和实战选型指南,帮你彻底搞懂这对最容易混淆的 AI 概念。 📌 前言 上周面试,面试官笑眯眯地问了一句: "你说说 AI Workflow 和 Agent


大模型入门:从“猜词游戏“到“超级大脑“,一篇读懂 AI 大模型
修己xj2026/7/15

2023 年初,ChatGPT 横空出世,"大模型"三个字一夜之间刷爆了所有人的朋友圈。有人拿它写代码,有人拿它写情书,还有人拿它辅导孩子做数学——而且它居然真的会。 可当你真正想搞懂"大模型到底是什么"时,迎面而来的却是满屏的"Transformer""自注意力""千亿参数",瞬间劝退。 别慌。这篇文章,我们用打比方的方式,把大模型从里到外讲清楚。 一、什么是大模型?先搞懂"大"在哪 今天大家口中的大模型,通常特指大语言模型(LLM,Large Language Model)。ChatGPT


【Java实习面试算法冲刺】双指针
ZenithSourceQuest2026/7/6

第2类题型:双指针 为什么双指针题看起来不难,你一到面试就容易写乱 很多同学第一次刷双指针时,会觉得这类题比哈希表还“直观”。因为代码通常不长,变量也常常只有 left、right、slow、fast 四个名字。但真正到了面试现场,双指针反而很容易暴露出两类问题: 你会套模板,但说不清两个指针各自代表什么。你知道要移动某一边,却解释不出“为什么这样移动不会漏解”。你能把 三数之和 写个大概,却总在去重和边界上翻车。你把“会写代码”当成“理解题型”,结果一换题面就不稳。 如果你


论虚拟线程与 Kotlin IO 协程:资源开销、时长、高并发表现及适用场景与技术选型思考
zimoyin2026/6/28

在现代并发编程中,虚拟线程(由 Java 20+ 引入)和 Kotlin IO 协程(基于 Dispatchers.IO)是两种高效处理异步任务的技术框架。在资源开销、时长(特别是长时间 IO)、高并发场景表现、以及何时选择合适方案等方面各有特点。本文将逐一展开对比分析。 1. 资源开销对比 虚拟线程和协程的核心开销差异源于其底层设计原理: 维度虚拟线程Kotlin IO 协程关键差异内存开销固定栈机制:默认约 1MB1\text{MB}1MB 占⽤[注1]动态内存分配:2KB∼512KB2


当 AI 学会「自己催自己」:对 Loop Engineering 的理解与思考
莫西很trouble2026/6/19

先说一个让我「咯噔」一下的瞬间 前段时间看到 Claude Code 负责人 Boris Cherny 说了句话,大意是:我已经不写 Prompt 了,我只写 Loop 我的第一反应是:啊?Prompt 不是刚学会怎么写好吗?怎么就又过时了? 但仔细想了下这句话背后的意思,突然意识到它不是在讲什么新技术,而是在讲一个我们早就该意识到的问题 ——如果每次用 AI,你都要在它身边喊「继续」「还是报错」「你改了啥」「回滚」,那说明你其实不是在用工具,你是在当监工 而 Loop Engineer

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读