Defold 支持声音但是不那么强大. 要注意两个概念:
声音组件只能在游戏对象里创建. 创建好游戏对象, 在其上面右键点击选择 Add Component ▸ Sound 然后点击 OK.
声音组件有一系列属性可以设置:
设置好声音属性之后, 可以通过调用 sound.play()
函数播放声音:
sound.play("go#sound", {delay = 1, gain = 0.5, pan = -1.0, speed = 1.25})
即使声音组件所在游戏对象被删除了, 声音也会继续播放. 可以通过调用 sound.stop()
函数停止播放 (见下文).
只要发送消息到声音组件都会造成新声音实例的播放, 直到声音缓存满溢引擎报错. 所以建议自己实现一个控制或者分组机制.
可以通过调用 sound.stop()
函数停止播放声音:
sound.stop("go#sound")
声音系统有 4 级增益:
sound.play()
函数或者直接调用 sound.set_gain()
函数设置的增益.sound.set_group_gain()
函数设置的增益.sound.set_group_gain(hash("master"))
函数修改设置.结果是4级增益的乘积. 默认每个增益都是 1.0 (0 dB).
在声音组件上设置组名就把这个声音归到了那个组里. 如果不设置组名默认归到 “master” 组里. 设置组名为 “master” 效果相同.
有一系列函数用于获得声音组, 声音名, 获得/设置增益, 均方根 (见 http://en.wikipedia.org/wiki/Root_mean_square) 和峰值. 还有一个函数用于检测设备播放器是否正在运行:
-- 如果 iPhone/Android 设备的播放器正在播放引用, 则所有游戏声音静音
if sound.is_music_playing() then
for i, group_hash in ipairs(sound.get_groups()) do
sound.set_group_gain(group_hash, 0)
end
end
组名是个哈希值. 其字符串值可以使用 sound.get_group_name()
获得, 可以用来显示音轨名称.
代码里不要依赖字符串组名因为编译后不保存字符串组名.
所有值都是线性 0 到 1.0 (0 dB) 的范围. 要转换为分贝数, 可使用标准转换公式:
\[db = 20 \times \log \left( gain \right)\]for i, group_hash in ipairs(sound.get_groups()) do
-- 字符串组名只在调试时可用. 发布后会变成 "unknown_*".
local name = sound.get_group_name(group_hash)
local gain = sound.get_group_gain(group_hash)
-- 转换为分贝.
local db = 20 * math.log10(gain)
-- 得到 RMS (增益均方根). 左右声道分开计算.
local left_rms, right_rms = sound.get_rms(group_hash, 2048 / 65536.0)
left_rmsdb = 20 * math.log10(left_rms)
right_rmsdb = 20 * math.log10(right_rms)
-- 得到峰值. 左右声道分开计算.
left_peak, right_peak = sound.get_peak(group_hash, 2048 * 10 / 65536.0)
left_peakdb = 20 * math.log10(left_peak)
right_peakdb = 20 * math.log10(right_peak)
end
-- 设置主声道增益为 +6 dB (math.pow(10, 6/20)).
sound.set_group_gain("master", 1.995)
如果用什么事件来触发声音播放, 就有可能造成同时播放2个或多个重复的声音. 这样的话, 声音会产生 偏移重叠 现象效果非常不好.
最简单的办法是设置一个过滤时间段对最近播放过的声音进行过滤:
-- 在一定 "过滤时间段" 内不允许再次播放同一声音.
local gate_time = 0.3
function init(self)
-- 把计时器保存到一个表里然后每帧计时递减
-- 直到过了 "过滤时间段". 再删除它.
self.sounds = {}
end
function update(self, dt)
-- 计时器递减
for k,_ in pairs(self.sounds) do
self.sounds[k] = self.sounds[k] - dt
if self.sounds[k] < 0 then
self.sounds[k] = nil
end
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("play_gated_sound") then
-- 表里没有才能播放.
if self.sounds[message.soundcomponent] == nil then
-- 保存计时器
self.sounds[message.soundcomponent] = gate_time
-- 播放声音
sound.play(message.soundcomponent, { gain = message.gain })
else
-- 过滤表里有的声音
print("gated " .. message.soundcomponent)
end
end
end
这样, 只要把 play_gated_sound
消息连同增益发送给声音组件. 使用 sound.play()
播放函数前就会对声音进行过滤:
msg.post("/sound_gate#script", "play_gated_sound", { soundcomponent = "/sounds#explosion1", gain = 1.0 })
对于 play_sound
消息没法过滤因为该消息由 Defold 引擎内部保留. 如果使用引擎保留消息名会造成运行不正确.
可以通关一些列属性在运行时控制声音 (用法参见 API). 以下属性可以使用 go.get()
和 go.set()
来进行操作:
gain
number
).pan
number
). 取值从 -1 (向左-45度) 到 1 (向右45度).speed
number
). 取值 1.0 为一般速度, 0.5 半速, 2.0 两倍速.sound
hash
). 可以使用 resource.set_sound(path, buffer)
来变更声音资源. 例如:local boom = sys.load_resource("/sounds/boom.wav")
local path = go.get("#sound", "sound")
resource.set_sound(path, boom)
在 game.project 文件里有些关于声音组件的 设置项目.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB