在游戏开发中,你是否也想将音乐旋律变为漂亮的图形显示?就像这样的效果。
或者这样的效果。
还有这样的效果。
但是,我们今天不会讨论怎样实现上面的那些效果(可以用插件实现,哈哈哈…),而是聊一下最朴素的效果实现原理。了解了原理,再复杂的效果也可以实现。最朴素的效果,见下图,哈哈哈哈~
简单来说,我们要实现的东西就是几个条状的UI,跟随音乐的旋律,进行上下缩放。而旋律,其实就是声音中的数据,就是一个int值,只要取到这个数据,就可以将数据通过一些计算,映射为UI的大小。
下面整理一下要做的事情(以下事情都是在Update中做的):
每一帧去取音频数据 查看Unity的 AudioSource API,我们发现有一个
GetOutputData
方法,获取音频输出数据,返回类型是一个 float 数组。取到音频数据后,将数据进行标准化处理 就是找出音频数据中最大的值,然后用每一个值,除以最大值,就可以将所有音频数据映射为 0 到 1 之间的数值大小,返回新的 float 数组。
有了音频数据大小,我们就可以映射为UI的条形图的大小 假设我们限定条状UI的大小最小为10,最大为200,再配合上面取到的音频数据,就可以设定UI的大小了。假设有10个条,那只需要使 float 数组中前10个数值就行。
其中第二步还可以加入更多计算处理,最后只要映射到0~1之间的数值,就可以用,最终是要表现效果,并不是一定要准确的表示出音频的数据,只要该高的时候高,该低的时候低,那就没问题,至于高到多少,低到多少,都是相对的,并不重要。
下面是示例代码:
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