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

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之间的数值,就可以用,最终是要表现效果,并不是一定要准确的表示出音频的数据,只要该高的时候高,该低的时候低,那就没问题,至于高到多少,低到多少,都是相对的,并不重要。

下面是示例代码:

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