当前位置:   article > 正文

【游戏开发创新】手把手教你使用Unity制作一个高仿酷狗音乐播放器,滨崎步,旋律起,爷青回(声音可视化 | 频谱 | Audio)_unity音乐播放器制作

unity音乐播放器制作

一、前言

嗨,大家好,我是新发。
事情是这样的,我在听歌的时候听到了滨崎步的MY ALL,热泪盈眶,旋律起,爷青回,

【4K收藏级画质】滨崎步神曲《My All》唱哭全场!!!

我点开酷狗,端详了一下,冒出个想法,我能不能用Unity做一个高仿酷狗音乐播放器,只播放滨崎步的这首歌。
于是,我就做了一个,最终效果如下:
请添加图片描述
运行效果:
请添加图片描述
请添加图片描述
发布成exe,运行效果:
请添加图片描述
下面,我来讲讲我的制作过程~

二、获取UI素材

下面是真酷狗界面,
在这里插入图片描述
根据界面,去阿里图标库搜索需要的素材,
阿里图标库地址:https://www.iconfont.cn/
比如我们要找这一块的图标素材,
在这里插入图片描述
我的电台为例,
在这里插入图片描述
你就搜电台,找到一个形状相似的图标,如下
在这里插入图片描述
点击图标,可以先设置图标的颜色为白色,然后再下载,
在这里插入图片描述
以此类推,我找的素材如下:
在这里插入图片描述
所有的UI素材图片格式都设置为Sprite (2D and UI)
在这里插入图片描述
有一部分UI需要设置一下九宫格,不然可能会变形,比如这个椭圆按钮,
在这里插入图片描述

三、使用UGUI制作界面

最终界面效果如下,
在这里插入图片描述
下面我会挑一下重点讲一下。

1、界面布局

在开始做UI界面之前,我们要先分析一下界面布局,首先从大布局看,是上、中、下,
在这里插入图片描述
那我们做界面时,就按上、中、下三块来做,
在这里插入图片描述
如下:
请添加图片描述
中间布局,又可以继续拆分成顶部标签栏和底部详细面板,以我的音乐为例,底部面板可以分为左右布局,如下:
在这里插入图片描述
这样子,我们中间就可以分成tabsleftright三块,
在这里插入图片描述
如下:
请添加图片描述
以此类推,大布局里嵌小布局,小布局里继续嵌小小布局,根据界面进行合理设计。

2、账号圆形头像

头像图片是正正方方的,如下:
请添加图片描述
我们需要显示圆形头像,可以使用Mask组件,如下head_frame节点,
在这里插入图片描述
使用一张圆形的图片显示,并挂上Mask组件,
在这里插入图片描述
子节点head_img就是头像图片了,
在这里插入图片描述
使用RawImage组件来显示头像,
在这里插入图片描述
效果如下:
在这里插入图片描述

3、搜索框

搜索框使用InputField组件,
在这里插入图片描述
替换框的图片,设置颜色,如下,
在这里插入图片描述
搜索框中再放一个Button来显示放大镜图片,
在这里插入图片描述
在这里插入图片描述
效果如下:
请添加图片描述

4、调节UI层

我的音乐这个按钮,
在这里插入图片描述
正确是要显示在最前面,
在这里插入图片描述
它之所以被挡住,是因为UGUI默认是按节点的顺序来渲染的,后面的按钮排在它下面,所以后面的按钮会盖住它,
在这里插入图片描述
如果我们不想调节节点的顺序,但又想让我的音乐按钮显示在前面,我们可以给它单独挂一个Canvas组件,并设置Sort Order为一个大于上一级CanvasSort Order的值,
在这里插入图片描述
这样它就可以正常显示在前面了,
在这里插入图片描述

5、黑色按钮悬浮高亮效果

鼠标悬停在按钮上时,按钮呈高亮状态,如下,
请添加图片描述
如果你把按钮Image图片设置为黑色,那么是没有这个高亮效果的,因为一个按钮是由ImageButton配合工作的,Button组件可以设置各个状态的颜色,Button组件的颜色和Image的颜色相乘就是最终的按钮状态颜色,如果Image颜色你设置成黑色,那么相乘得出的结果都是黑色,你也就看不到高亮效果了,
在这里插入图片描述
正确做法是,Image为白色,ButtonNormal Color为黑色,其他状态的颜色根据具体需求而定,比如高亮Hightlighted Color为灰色,最终颜色设置如下:
在这里插入图片描述

6、纯文字按钮

这排按钮是纯文字按钮,
在这里插入图片描述
UGUI创建的默认按钮是带图片的,做下小改动即可,我们通过菜单创建一个Button
在这里插入图片描述
Image组件移除掉,
在这里插入图片描述
然后把子节点的Text赋值给Button组件的Target Graphic即可,
在这里插入图片描述
设置一下按钮的各个状态颜色,
在这里插入图片描述
我们直接调节Button节点的宽高即可调节响应区域啦~
在这里插入图片描述
鼠标靠近文字时,按钮可以检测到,如下,

注:对于有图片的按钮,如果也想扩大按钮的响应区域但又不想扩大图片本身,也可以利用这个技巧,把按钮背景图作为Button组件的子节点显示即可

请添加图片描述

7、滚动列表自适应

滚动列表用的是ScrollView
请添加图片描述
需要处理的是Content的自适应,首先列表是竖向滑动的,我们给Content节点挂上VerticalLayoutGroup组件,
在这里插入图片描述
要让Content的高度随着列表item的数量增多而增大,我们可以使用ContentSizeFitter组件,把Vertical Fit设置为Preferred Size
在这里插入图片描述
然后给item设定一个高度,
在这里插入图片描述
这样子,当item增多的时候,Content节点就会自动扩大高度啦,
请添加图片描述

8、歌名与视频图标混排

在单曲列表中,歌名和视频图标混排在一起,视频图标需要跟在歌名后面,歌名文字增多,视频图标需要自动往后移动,如下:
请添加图片描述
这个要用到锚点,视频图标作为歌名文字的子节点,然后视频图标的锚点设置为middle-right,如下,这样子,视频图标就会以文字框的右边为参考线计算相对位置了,
在这里插入图片描述

9、点击按钮切换图片

点击按钮切换按钮图片,如下
请添加图片描述
这里要写点代码,在代码中替换Button组件的ImageSprite对象,例:

playBtn.onClick.AddListener(() =>
{
    if (!audioSource.isPlaying)
    {
        audioSource.Play();
        playBtn.image.sprite = pauseSprite;
    }
    else
    {
        audioSource.Pause();
        playBtn.image.sprite = playSprite;
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
10、进度条

进度条使用Slider组件,
请添加图片描述

需要解决的是进度条很细的情况下,扩大进度条的操作区域,解决办法是给Slider添加一个Image组件,并设置透明度为0
在这里插入图片描述
调整Slider的高为一个稍微大一点的值,
在这里插入图片描述
调整BackgroundTopBottom,使其变细,
在这里插入图片描述
同理,Fill Area也调整一下TopBottom,使其变细,
在这里插入图片描述
这样子做出来的进度条,就可以很细,但是操作区域又得到保证,
在这里插入图片描述

四、写代码

1、歌曲的播放与暂停

Unity中要播放一个声音并听到,需要两个组件配合,一个是AudioSource(相当于喇叭),另一个AudioListener(相当于耳朵),
其中,AudioListener默认挂在摄像机上,如果你有多个摄像机,要确保场景中只有一个激活状态的AudioListener,否则会报错。
AudioSource需要赋值AudioClip成员,即设置具体的声音文件,我们可以直接拖拽一个声音文件到AudioSourceAudioClip成员上,如下:
在这里插入图片描述
我们也可以在代码中动态设置:

// AudioClip audioClip = TODO 动态加载AudioClip文件;
audioSource.clip = audioClip;
  • 1
  • 2

动态加载资源文件我之前的文章有讲过三种主要方式,大家感兴趣的可以看下,
《Unity游戏开发——新发教你做游戏(三):3种资源加载方式》
在这里插入图片描述
然后播放声音和暂停播放,调用AudioSourcePlayPause方法即可:

// (继续)播放
audioSource.Play();
  • 1
  • 2
// 暂停
audioSource.Pause();
  • 1
  • 2
2、声音频谱特效

声音播放过程中,可以实时显示频谱特效,
请添加图片描述
这里要用到AudioSourceGetSpectrumData接口,这个接口可以采样声谱数据块,

public static void GetSpectrumData(float[] samples, 
									int channel, 
									FFTWindow window);
  • 1
  • 2
  • 3

参数说明:
samples: 函数返回值。将音频样本数据传送至samples数组,数组大小必须为2的n次方,最小64,最大8192
channel: 声道,一般设置为0
window: 转换信号所用的窗函数,算法越复杂,声音越柔和,但速度更慢。
用法 :
先声明一个浮点数组:

public float[] samples = new float[512];
  • 1

然后在Update方法里面使用方法:

audiosource.GetSpectrumData(samples, 0, FFTWindow.BlackmanHarris);
  • 1

我这里封装了一个脚本,如下:

using UnityEngine;

/// <summary>
/// 声音特效
/// </summary>
public class AudioEffect : MonoBehaviour
{
    // 用于显示的方块
    public GameObject cubeObj;
    // 方块的其实点
    public Transform startPoint;

    // 采样数据长度
    private const int SPECTRUM_CNT = 512;
    // 采样数据
    private float[] spectrumData = new float[SPECTRUM_CNT];
    // 方块Transform数组
    private Transform[] cubeTransforms = new Transform[SPECTRUM_CNT];

    void Start()
    {
        //cube生成与排列
        Vector3 p = startPoint.position;

        for (int i = 0; i < SPECTRUM_CNT; i++)
        {
            p = new Vector3(p.x + 0.11f, p.y, p.z);
            GameObject cube = Instantiate(cubeObj, p, cubeObj.transform.rotation);
            cube.transform.parent = startPoint;
            cubeTransforms[i] = cube.transform;
        }
    }

    /// <summary>
    /// 当前帧频率波功率,传到对应cube的localScale
    /// </summary>
    public void UpdateEffect(AudioSource audioSource)
    {
        audioSource.GetSpectrumData(spectrumData, 0, FFTWindow.BlackmanHarris);
        float scaleY;
        for (int i = 0; i < SPECTRUM_CNT; i++)
        {
            scaleY = Mathf.Lerp(cubeTransforms[i].localScale.y, spectrumData[i] * 10000f, 0.5f);
            // 限制一下功率
            if (scaleY > 400)
            {
                scaleY -= 400;
            }
            else if (scaleY > 300)
            {
                scaleY -= 300;
            }
            else if (scaleY > 200)
            {
                scaleY -= 200;
            }
            else if (scaleY > 100)
            {
                scaleY -= 100;
            }

            cubeTransforms[i].localScale = new Vector3(0.15f, scaleY, 0.15f);
        }
    }
}
  • 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

其中cubeObj是一个用于显示的小方块,我们做一下方块预设,如下:
在这里插入图片描述
材质球用的shaderUnlit/Texture,贴图是上面白下面黑的渐变色,
在这里插入图片描述
因为这个特效要显示在UI界面中,并且是穿插在UI层中,我这里使用RenderTexture来解决。
先创建一张RenderTexture
在这里插入图片描述
然后创建一个独立的摄像机EffectCamera(记得把AudioListener组件去掉),Culling Mask只勾选Water层,
在这里插入图片描述
主摄像机记得把Water去掉,
在这里插入图片描述
把特效的Layer设置为Water
在这里插入图片描述
这样子,特效就只会在EffectCamera摄像机中渲染了,
我们把刚刚的RenderTexture拖给EffectCamera摄像机的Target Texture,如下,
在这里插入图片描述
接着,给audioEffect节点挂AudioEffect脚本,并赋值成员对象,如下,
在这里插入图片描述
这样特效就会渲染到RenderTexture上了,如下:
请添加图片描述
我们再使用一张RawImage来显示它,
在这里插入图片描述
我们可以调节RawImage的颜色来调整特效的颜色,如下:
请添加图片描述

4、获取声音总时长
var len = audioSource.clip.length;
  • 1
5、设置当前时间戳

根据进度条进度设置当前时间戳

slider.onValueChanged.AddListener((v) =>
    {
        if (v < 1)
            audioSource.time = v * audioSource.clip.length;
    });
  • 1
  • 2
  • 3
  • 4
  • 5
6、发布exe隐藏标题栏

发布成exe,运行时自动隐藏默认的标题栏,
请添加图片描述
隐藏标题栏的方法,我之前有写过相关文章,《Unity发布PC平台exe的窗口花样(WindowsAPI、捕获关闭事件、隐藏窗口标题栏、隐藏最小化最大化关闭按钮等等)》
用到了几个Windows API,如下:

[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
public static extern long GetWindowLong(IntPtr hwd, int nIndex);

[DllImport("user32.dll")]
public static extern void SetWindowLong(IntPtr hwd, int nIndex, long dwNewLong);

[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hwd, int cmdShow);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

隐藏标题栏:

#if UNITY_STANDALONE && !UNITY_EDITOR
/// <summary>
/// 窗口风格
/// </summary>
const int GWL_STYLE = -16;
/// <summary>
/// 标题栏
/// </summary>
const int WS_CAPTION = 0x00c00000;


// 隐藏标题栏
var hwd = GetForegroundWindow();
var wl = GetWindowLong(hwd, GWL_STYLE);
wl &= ~WS_CAPTION;
SetWindowLong(hwd, GWL_STYLE, wl);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

另外,我们需要实现这三个按钮的逻辑,
在这里插入图片描述
最小化窗口:

#if UNITY_STANDALONE && !UNITY_EDITOR
/// <summary>
/// 最小化
/// </summary>
const int SW_SHOWMINIMIZED = 2;

// 获得窗口句柄
var hwd = GetForegroundWindow();
// 设置窗口最小化
ShowWindow(hwd, SW_SHOWMINIMIZED);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

最大化窗口:

#if UNITY_STANDALONE && !UNITY_EDITOR
/// <summary>
/// 最大化
/// </summary>
const int SW_SHOWMAXIMIZED = 3;

// 获得窗口句柄
var hwd = GetForegroundWindow();
// 设置窗口最小化
ShowWindow(hwd, SW_SHOWMAXIMIZED);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

关闭程序:

Application.Quit();
  • 1

五、工程源码

本文的工程源码我以上传到CODE CHINA,感兴趣的同学可自行下载学习,
地址:https://codechina.csdn.net/linxinfa/UnityMusicPlayer
注:我使用的Unity版本为Unity 2020.1.14f1c1 (64-bit)
温馨提示:本工程仅供学习使用,禁止用于商业用途,否则后果自负
在这里插入图片描述

六、完毕

最后,附上滨崎步MY ALL的歌词,初识不知曲中意,再听已是曲中人!

多少时光
我们一同经历
多少路程
我们一起走过
至今我们所留下的
虽然不够完美却也灿烂过
如今在这里 那些结晶
正闪耀著骄傲的光辉
一直都那麽开心和快乐
坦白说并不是那麽回事
然而我们永远
都不会是孤身一人
想让你看见梦想的所在
没有终结 没有消亡
真的很想看见那样的梦想
那正是我的愿望
想要一直守护在你身旁
不管即将发生什么
我将用我的全部
一直将你守护
从不曾有丝毫后悔
知道现在我都可以这样断言
我们一直都在竭尽全力地
奋战到底
在那些铭心的夜晚
事实上也会常常想起你
然而我们永远
都不是孤身一人
看到你的笑颜
令人爱恋 令人目眩
多想再看到那样的笑颜
所以我仍活到今天
我能感觉到你的爱
有力而温暖
那样无偿的爱情
我尽全力地感受著
想让你看见梦想的所在
没有终结 没有消亡
真的很想让你看见那样的梦想
那正是我的愿望
我想守护在你的身旁
不管即将发生什麽
我将用我的全部
一直将你守护

请添加图片描述
好了,就到这里吧,我要再去单曲循环听亿编了~
我是林新发:https://blog.csdn.net/linxinfa
原创不易,若转载请注明出处,感谢大家~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信,我们下期见~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/118400
推荐阅读
相关标签
  

闽ICP备14008679号