浅谈Unity3D音频可视化效果原理

在游戏开发中,你是否也想将音乐旋律变为漂亮的图形显示?就像这样的效果。

p000601_1

或者这样的效果。

p000602_2

还有这样的效果。

p000603_3

但是,我们今天不会讨论怎样实现上面的那些效果(可以用插件实现,哈哈哈…),而是聊一下最朴素的效果实现原理。了解了原理,再复杂的效果也可以实现。最朴素的效果,见下图,哈哈哈哈~

p000604_4

简单来说,我们要实现的东西就是几个条状的UI,跟随音乐的旋律,进行上下缩放。而旋律,其实就是声音中的数据,就是一个int值,只要取到这个数据,就可以将数据通过一些计算,映射为UI的大小。

下面整理一下要做的事情(以下事情都是在Update中做的):

  1. 每一帧去取音频数据
    查看Unity的 AudioSource API,我们发现有一个 GetOutputData 方法,获取音频输出数据,返回类型是一个 float 数组。

  2. 取到音频数据后,将数据进行标准化处理
    就是找出音频数据中最大的值,然后用每一个值,除以最大值,就可以将所有音频数据映射为 0 到 1 之间的数值大小,返回新的 float 数组。

  3. 有了音频数据大小,我们就可以映射为UI的条形图的大小
    假设我们限定条状UI的大小最小为10,最大为200,再配合上面取到的音频数据,就可以设定UI的大小了。假设有10个条,那只需要使 float 数组中前10个数值就行。

其中第二步还可以加入更多计算处理,最后只要映射到0~1之间的数值,就可以用,最终是要表现效果,并不是一定要准确的表示出音频的数据,只要该高的时候高,该低的时候低,那就没问题,至于高到多少,低到多少,都是相对的,并不重要。

下面是示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;

public class AudioV : MonoBehaviour
{
public AudioSource audioSource;
public float minSize = 10;
public float maxSize = 200;
public float lerpSpeed = 20.0f; // 用于控制变化速度
private List<RectTransform> barList = new List<RectTransform>();

// 注意,采样数组的大小必须为2的次方
// 另外,采样数据的个数,一定要 >= 条形UI的数量
private float[] sampleData = new float[64];


private void Start()
{
// 查找所有的子物体,就是我们要用来显示音频数据的条形UI
RectTransform[] childs = GetComponentsInChildren<RectTransform>();
for (int i = 1; i < childs.Length; ++i)
{
barList.Add(childs[i]);
}
}

/// <summary>
/// 进行标准化数据,将数据映射为 0 到 1 之间的数值
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private float[] NormalizeData(float[] input)
{
float[] output = new float[input.Length];
float max = 0;
float min = 0;
for (int i = 0; i < input.Length; i++)
{
max = Mathf.Max(max, input[i]);
min = Mathf.Min(min, input[i]);
}

float len = max - min;

for (int i = 0; i < input.Length; i++)
{
if (len <= 0)
{
output[i] = 0;
}
else
{
output[i] = (input[i] - min) / len;
}
}

return output;
}

void Update()
{
float[] normalizedData = null;

// 获取原始采样数据
audioSource.GetOutputData(sampleData, 0);

// 进行标准化处理
normalizedData = NormalizeData(sampleData);

for (int i = 0; i < barList.Count; ++i)
{
float newHeight = minSize + (maxSize - minSize) * normalizedData[i];
float currHeight = Mathf.Lerp(barList[i].sizeDelta.y, newHeight, Time.deltaTime * lerpSpeed);

barList[i].SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, currHeight);
}
}
}

在上面的代码中,因为我们是每帧采样,所以UI变化会很剧烈,所以我们这里用了一个Lerp去控制变化速度,但是这样也会带来一个问题就是变化幅度的减小。大家可以自己试一下,自己尝试改进。

如果你用的不是 AudioSource,用了别的音频插件,只要取到音频的采样数据,用法都是一样的。

基本的原理已经介绍完,更复杂的表现形式,也就是改变音频数据的映射形式而已~

如果你有更好的改进方法,欢迎在下面留言~

上文中用到的图片分别来自Unity插件 3D Visualizer Spectrum Vu Meter 和插件 Sound Reactor - Standard

分享到