feat: implement volume step feature
- Add volume step functionality with VolumeStepResolver class - Add VolumeStepThreshold, Volume, and VolumeStep properties to AudioObject - Integrate volume step resolution in SfxSystem for dynamic audio volume control
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cc8ce9203c4a4d03b07e0ae9a3cf219
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f3b3e40c5ec34183af765f15c1ce362
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: true
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e640dc3c900064126804112a521170ea
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 7
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
conversionMode: 0
|
||||
preloadAudioData: 0
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be8f215dba3904ddf9f72fc064c97e71
|
||||
AudioImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 7
|
||||
defaultSettings:
|
||||
serializedVersion: 2
|
||||
loadType: 0
|
||||
sampleRateSetting: 0
|
||||
sampleRateOverride: 44100
|
||||
compressionFormat: 1
|
||||
quality: 1
|
||||
conversionMode: 0
|
||||
preloadAudioData: 0
|
||||
platformSettingOverrides: {}
|
||||
forceToMono: 0
|
||||
normalize: 1
|
||||
loadInBackground: 0
|
||||
ambisonic: 0
|
||||
3D: 1
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1012,7 +1012,7 @@ GameObject:
|
||||
- component: {fileID: 1394234350}
|
||||
- component: {fileID: 1394234349}
|
||||
m_Layer: 5
|
||||
m_Name: Button (Legacy)
|
||||
m_Name: PlaySound
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
@@ -1085,15 +1085,27 @@ MonoBehaviour:
|
||||
- m_Target: {fileID: 2093584670}
|
||||
m_TargetAssemblyTypeName: OCES.Audio.AudioSystem, Assembly-CSharp
|
||||
m_MethodName: Play
|
||||
m_Mode: 5
|
||||
m_Mode: 3
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 11
|
||||
m_IntArgument: 57
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument: rain
|
||||
m_BoolArgument: 0
|
||||
m_CallState: 2
|
||||
- m_Target: {fileID: 2093584670}
|
||||
m_TargetAssemblyTypeName: OCES.Audio.AudioSystem, Assembly-CSharp
|
||||
m_MethodName: Play
|
||||
m_Mode: 3
|
||||
m_Arguments:
|
||||
m_ObjectArgument: {fileID: 0}
|
||||
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
|
||||
m_IntArgument: 58
|
||||
m_FloatArgument: 0
|
||||
m_StringArgument:
|
||||
m_BoolArgument: 0
|
||||
m_CallState: 2
|
||||
--- !u!114 &1394234350
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -130,6 +130,23 @@ public partial class AudioObject : IBinarySerializable
|
||||
/// </summary>
|
||||
public bool RandomType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Volume Step阈值 ms
|
||||
/// </summary>
|
||||
public uint VolumeStepThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始音量
|
||||
/// dB
|
||||
/// </summary>
|
||||
public int Volume { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 音量变化幅度
|
||||
/// dB
|
||||
/// </summary>
|
||||
public int VolumeStep { get; set; }
|
||||
|
||||
|
||||
public void DeSerialize(BinaryReader reader)
|
||||
{
|
||||
@@ -166,6 +183,9 @@ public partial class AudioObject : IBinarySerializable
|
||||
BlendCrossFadeType = (BlendCrossFadeType)reader.ReadByte();
|
||||
LimitRepetition = reader.ReadByte();
|
||||
RandomType = reader.ReadBoolean();
|
||||
VolumeStepThreshold = reader.ReadUInt32();
|
||||
Volume = reader.ReadInt32();
|
||||
VolumeStep = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void Serialize(BinaryWriter writer)
|
||||
@@ -202,6 +222,9 @@ public partial class AudioObject : IBinarySerializable
|
||||
writer.Write((byte)BlendCrossFadeType);
|
||||
writer.Write(LimitRepetition);
|
||||
writer.Write(RandomType);
|
||||
writer.Write(VolumeStepThreshold);
|
||||
writer.Write(Volume);
|
||||
writer.Write(VolumeStep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,13 @@ public static class AudioObjectDefinitions
|
||||
{ "Chinese Number 08", 49 },
|
||||
{ "Chinese Number 09", 49 },
|
||||
{ "Chinese Number 10", 49 },
|
||||
{ "Bar", 52 },
|
||||
{ "Beat", 53 },
|
||||
{ "Grid", 54 },
|
||||
{ "NVDice", 55 },
|
||||
{ "NVHeartbeats", 56 },
|
||||
{ "au_sfx_notice_level_countDown_edge", 57 },
|
||||
{ "au_sfx_notice_level_countDown_time", 58 },
|
||||
{ "sfx_amb_desert", 2000 },
|
||||
{ "sfx_amb_forest", 2001 },
|
||||
{ "sfx_anim_common_item_fly", 3000 },
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OCES.Audio.HandWritten;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
@@ -33,6 +34,7 @@ namespace OCES.Audio
|
||||
AudioSourcePool m_pool;
|
||||
AudioContainerSelector m_containerSelector;
|
||||
PitchStepResolver m_pitchStepResolver;
|
||||
VolumeStepResolver m_volumeStepResolver;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void Update()
|
||||
@@ -74,6 +76,7 @@ namespace OCES.Audio
|
||||
this.m_pool = pool;
|
||||
this.m_containerSelector = new AudioContainerSelector();
|
||||
this.m_pitchStepResolver = new PitchStepResolver();
|
||||
this.m_volumeStepResolver = new VolumeStepResolver();
|
||||
|
||||
AudioMixerGroup[] sfx = Find("Master/Regular/SFX");
|
||||
if (sfx.Length > 0) this.m_sfxGroup = sfx[0];
|
||||
@@ -173,7 +176,8 @@ namespace OCES.Audio
|
||||
|
||||
// 执行播放
|
||||
float pitch = this.m_pitchStepResolver.ResolvePitch(audioObject, now); //算一下用不用变调
|
||||
PlayNewSound(audioObject, pitch);
|
||||
float volume = this.m_volumeStepResolver.ResolveVolume(audioObject, now);
|
||||
PlayNewSound(audioObject, pitch, volume);
|
||||
this.m_lastPlayTime[audioObject.Id] = now;
|
||||
onPlay?.Invoke();
|
||||
}
|
||||
@@ -182,12 +186,13 @@ namespace OCES.Audio
|
||||
// 播放逻辑
|
||||
// ─────────────────────────────────────────────
|
||||
|
||||
void PlayNewSound(AudioObject audioObject, float pitch)
|
||||
void PlayNewSound(AudioObject audioObject, float pitch, float volume)
|
||||
{
|
||||
ActiveSound active = new()
|
||||
{
|
||||
AudioObject = audioObject,
|
||||
Pitch = pitch,
|
||||
Volume = volume,
|
||||
State = ActiveSoundState.Pending,
|
||||
StartTime = Time.realtimeSinceStartupAsDouble,
|
||||
};
|
||||
@@ -207,7 +212,7 @@ namespace OCES.Audio
|
||||
/// <summary>
|
||||
/// 连续容器播放协程(Random / Sequence 持续模式)
|
||||
/// </summary>
|
||||
IEnumerator PlayContainerContinuous(AudioSource source, AudioObject audioObject, ActiveSound chainActive, int startIndex, float pitch)
|
||||
IEnumerator PlayContainerContinuous(AudioSource source, AudioObject audioObject, ActiveSound chainActive, int startIndex, float pitch, float volume)
|
||||
{
|
||||
bool isRandom = audioObject.ContainerType == ContainerType.Random;
|
||||
|
||||
@@ -229,7 +234,7 @@ namespace OCES.Audio
|
||||
limitRepetition);
|
||||
|
||||
// 配置并播放
|
||||
if (!SetupSource(source, audioObject, pitch, index))
|
||||
if (!SetupSource(source, audioObject, pitch, volume,index))
|
||||
{
|
||||
Debug.LogError($"音频文件未找到:{audioObject.Name[index]}");
|
||||
yield break;
|
||||
@@ -334,7 +339,7 @@ namespace OCES.Audio
|
||||
return this.m_sfxGroup;
|
||||
}
|
||||
|
||||
bool SetupSource(AudioSource source, AudioObject audioObject, float pitch, int clipIndex = 0)
|
||||
bool SetupSource(AudioSource source, AudioObject audioObject, float pitch, float volume, int clipIndex = 0)
|
||||
{
|
||||
AudioClip clip = Resources.Load<AudioClip>($"Audios/{audioObject.Name[clipIndex]}"); // TODO 抽象同一资源加载接口
|
||||
if (!clip)
|
||||
@@ -348,6 +353,7 @@ namespace OCES.Audio
|
||||
source.priority = audioObject.Priority;
|
||||
source.outputAudioMixerGroup = GetMixerGroup(audioObject.MixingType);
|
||||
source.pitch = pitch;
|
||||
source.volume = volume;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -371,6 +377,7 @@ namespace OCES.Audio
|
||||
{
|
||||
AudioObject audioObject = active.AudioObject;
|
||||
float pitch = active.Pitch;
|
||||
float volume = active.Volume;
|
||||
|
||||
// =======================
|
||||
// Blend(每个clip一个ActiveSound)
|
||||
@@ -385,10 +392,11 @@ namespace OCES.Audio
|
||||
{
|
||||
AudioObject = audioObject,
|
||||
Pitch = pitch,
|
||||
Volume = volume,
|
||||
State = ActiveSoundState.Playing
|
||||
};
|
||||
|
||||
if (!SetupSource(source, audioObject, pitch, i))
|
||||
if (!SetupSource(source, audioObject, pitch, volume, i))
|
||||
{
|
||||
this.m_pool.ReturnToPool(source.gameObject);
|
||||
continue;
|
||||
@@ -424,7 +432,7 @@ namespace OCES.Audio
|
||||
int start = audioObject.ContainerType == ContainerType.Random ? -1 : 0;
|
||||
|
||||
active.Coroutine = StartCoroutine(
|
||||
PlayContainerContinuous(sourceSingle, audioObject, active, start, pitch)
|
||||
PlayContainerContinuous(sourceSingle, audioObject, active, start, pitch, volume)
|
||||
);
|
||||
|
||||
return;
|
||||
@@ -440,7 +448,7 @@ namespace OCES.Audio
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (!SetupSource(sourceSingle, audioObject, pitch, index))
|
||||
if (!SetupSource(sourceSingle, audioObject, pitch, volume, index))
|
||||
{
|
||||
m_pool.ReturnToPool(sourceSingle.gameObject);
|
||||
return;
|
||||
@@ -506,6 +514,7 @@ namespace OCES.Audio
|
||||
public int CurrentLoopCount;
|
||||
public Coroutine Coroutine;
|
||||
public float Pitch;
|
||||
public float Volume;
|
||||
public ActiveSoundState State;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OCES.Audio.HandWritten
|
||||
{
|
||||
public class VolumeStepResolver
|
||||
{
|
||||
readonly Dictionary<uint, int> m_volumeStepCounts = new();
|
||||
readonly Dictionary<uint, double> m_volumeStepLastTime = new();
|
||||
|
||||
internal float ResolveVolume(AudioObject audioObject, double time)
|
||||
{
|
||||
// 计算一下配置的音量是多少
|
||||
float baseVolume = Mathf.Pow(10,audioObject.Volume / 20f);
|
||||
|
||||
// 优化版。看看表现,要是确实Mathf.Pow造成性能卡点了就用这个。
|
||||
//float baseVolume = audioObject.Volume == 0 ? 1f : Mathf.Pow(10,audioObject.Volume / 20f);
|
||||
|
||||
if (audioObject.VolumeStepThreshold == 0) //没配置VolumeStep
|
||||
{
|
||||
return baseVolume;
|
||||
}
|
||||
|
||||
// 超时了,或者没播过
|
||||
if (!this.m_volumeStepLastTime.TryGetValue(audioObject.Id, out double lastVolumeStepTime)
|
||||
|| time - lastVolumeStepTime > audioObject.VolumeStepThreshold)
|
||||
{
|
||||
this.m_volumeStepCounts[audioObject.Id] = 0;
|
||||
this.m_volumeStepLastTime[audioObject.Id] = time;
|
||||
return baseVolume;
|
||||
}
|
||||
|
||||
//命中了
|
||||
int volumeStepCount = this.m_volumeStepCounts[audioObject.Id];
|
||||
volumeStepCount = this.m_volumeStepCounts[audioObject.Id] = volumeStepCount + 1;
|
||||
this.m_volumeStepLastTime[audioObject.Id] = time;
|
||||
return Mathf.Clamp(Mathf.Pow(10, (audioObject.Volume + audioObject.VolumeStep * volumeStepCount) / 20f), 0f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0059d6fc851c474a97bc05f6385f9a48
|
||||
timeCreated: 1776067889
|
||||
Reference in New Issue
Block a user