赞
踩
嗨,大家好,我是新发。
事情是这样的,我在听歌的时候听到了滨崎步的MY ALL
,热泪盈眶,旋律起,爷青回,
【4K收藏级画质】滨崎步神曲《My All》唱哭全场!!!
我点开酷狗,端详了一下,冒出个想法,我能不能用Unity
做一个高仿酷狗音乐播放器,只播放滨崎步的这首歌。
于是,我就做了一个,最终效果如下:
运行效果:
发布成exe
,运行效果:
下面,我来讲讲我的制作过程~
下面是真酷狗界面,
根据界面,去阿里图标库搜索需要的素材,
阿里图标库地址:https://www.iconfont.cn/
比如我们要找这一块的图标素材,
以我的电台
为例,
你就搜电台
,找到一个形状相似的图标,如下
点击图标,可以先设置图标的颜色为白色,然后再下载,
以此类推,我找的素材如下:
所有的UI
素材图片格式都设置为Sprite (2D and UI)
,
有一部分UI
需要设置一下九宫格,不然可能会变形,比如这个椭圆按钮,
最终界面效果如下,
下面我会挑一下重点讲一下。
在开始做UI
界面之前,我们要先分析一下界面布局,首先从大布局看,是上、中、下,
那我们做界面时,就按上、中、下三块来做,
如下:
中间布局,又可以继续拆分成顶部标签栏和底部详细面板,以我的音乐
为例,底部面板可以分为左右布局,如下:
这样子,我们中间就可以分成tabs
、left
、right
三块,
如下:
以此类推,大布局里嵌小布局,小布局里继续嵌小小布局,根据界面进行合理设计。
头像图片是正正方方的,如下:
我们需要显示圆形头像,可以使用Mask
组件,如下head_frame
节点,
使用一张圆形
的图片显示,并挂上Mask
组件,
子节点head_img
就是头像图片了,
使用RawImage
组件来显示头像,
效果如下:
搜索框使用InputField
组件,
替换框的图片,设置颜色,如下,
搜索框中再放一个Button
来显示放大镜图片,
效果如下:
我的音乐
这个按钮,
正确是要显示在最前面,
它之所以被挡住,是因为UGUI
默认是按节点的顺序来渲染的,后面的按钮排在它下面,所以后面的按钮会盖住它,
如果我们不想调节节点的顺序,但又想让我的音乐
按钮显示在前面,我们可以给它单独挂一个Canvas
组件,并设置Sort Order
为一个大于上一级Canvas
的Sort Order
的值,
这样它就可以正常显示在前面了,
鼠标悬停在按钮上时,按钮呈高亮状态,如下,
如果你把按钮Image
图片设置为黑色,那么是没有这个高亮效果的,因为一个按钮是由Image
和Button
配合工作的,Button
组件可以设置各个状态的颜色,Button
组件的颜色和Image
的颜色相乘就是最终的按钮状态颜色,如果Image
颜色你设置成黑色,那么相乘得出的结果都是黑色,你也就看不到高亮效果了,
正确做法是,Image
为白色,Button
的Normal Color
为黑色,其他状态的颜色根据具体需求而定,比如高亮Hightlighted Color
为灰色,最终颜色设置如下:
这排按钮是纯文字按钮,
UGUI
创建的默认按钮是带图片的,做下小改动即可,我们通过菜单创建一个Button
,
把Image
组件移除掉,
然后把子节点的Text
赋值给Button
组件的Target Graphic
即可,
设置一下按钮的各个状态颜色,
我们直接调节Button
节点的宽高即可调节响应区域啦~
鼠标靠近文字时,按钮可以检测到,如下,
注:对于有图片的按钮,如果也想扩大按钮的响应区域但又不想扩大图片本身,也可以利用这个
技巧
,把按钮背景图作为Button
组件的子节点显示即可
滚动列表用的是ScrollView
,
需要处理的是Content
的自适应,首先列表是竖向滑动的,我们给Content
节点挂上VerticalLayoutGroup
组件,
要让Content
的高度随着列表item
的数量增多而增大,我们可以使用ContentSizeFitter
组件,把Vertical Fit
设置为Preferred Size
,
然后给item
设定一个高度,
这样子,当item
增多的时候,Content
节点就会自动扩大高度啦,
在单曲列表中,歌名和视频图标混排在一起,视频图标需要跟在歌名后面,歌名文字增多,视频图标需要自动往后移动,如下:
这个要用到锚点,视频图标作为歌名文字的子节点,然后视频图标的锚点设置为middle-right
,如下,这样子,视频图标就会以文字框的右边为参考线计算相对位置了,
点击按钮切换按钮图片,如下
这里要写点代码,在代码中替换Button
组件的Image
的Sprite
对象,例:
playBtn.onClick.AddListener(() =>
{
if (!audioSource.isPlaying)
{
audioSource.Play();
playBtn.image.sprite = pauseSprite;
}
else
{
audioSource.Pause();
playBtn.image.sprite = playSprite;
}
});
进度条使用Slider
组件,
需要解决的是进度条很细的情况下,扩大进度条的操作区域,解决办法是给Slider
添加一个Image
组件,并设置透明度为0
,
调整Slider
的高为一个稍微大一点的值,
调整Background
的Top
和Bottom
,使其变细,
同理,Fill Area
也调整一下Top
和Bottom
,使其变细,
这样子做出来的进度条,就可以很细,但是操作区域又得到保证,
在Unity
中要播放一个声音并听到,需要两个组件配合,一个是AudioSource
(相当于喇叭),另一个AudioListener
(相当于耳朵),
其中,AudioListener
默认挂在摄像机上,如果你有多个摄像机,要确保场景中只有一个激活状态的AudioListener
,否则会报错。
AudioSource
需要赋值AudioClip
成员,即设置具体的声音文件,我们可以直接拖拽一个声音文件到AudioSource
的AudioClip
成员上,如下:
我们也可以在代码中动态设置:
// AudioClip audioClip = TODO 动态加载AudioClip文件;
audioSource.clip = audioClip;
动态加载资源文件我之前的文章有讲过三种主要方式,大家感兴趣的可以看下,
《Unity游戏开发——新发教你做游戏(三):3种资源加载方式》
然后播放声音和暂停播放,调用AudioSource
的Play
和Pause
方法即可:
// (继续)播放
audioSource.Play();
// 暂停
audioSource.Pause();
声音播放过程中,可以实时显示频谱特效,
这里要用到AudioSource
的GetSpectrumData
接口,这个接口可以采样声谱数据块,
public static void GetSpectrumData(float[] samples,
int channel,
FFTWindow window);
参数说明:
samples
: 函数返回值。将音频样本数据传送至samples
数组,数组大小必须为2的n次方
,最小64
,最大8192
。
channel
: 声道,一般设置为0
。
window
: 转换信号所用的窗函数,算法越复杂,声音越柔和,但速度更慢。
用法 :
先声明一个浮点数组:
public float[] samples = new float[512];
然后在Update
方法里面使用方法:
audiosource.GetSpectrumData(samples, 0, FFTWindow.BlackmanHarris);
我这里封装了一个脚本,如下:
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); } } }
其中cubeObj
是一个用于显示的小方块,我们做一下方块预设,如下:
材质球用的shader
为Unlit/Texture
,贴图是上面白下面黑的渐变色,
因为这个特效要显示在UI
界面中,并且是穿插在UI
层中,我这里使用RenderTexture
来解决。
先创建一张RenderTexture
,
然后创建一个独立的摄像机EffectCamera
(记得把AudioListener
组件去掉),Culling Mask
只勾选Water
层,
主摄像机记得把Water
去掉,
把特效的Layer
设置为Water
,
这样子,特效就只会在EffectCamera
摄像机中渲染了,
我们把刚刚的RenderTexture
拖给EffectCamera
摄像机的Target Texture
,如下,
接着,给audioEffect
节点挂AudioEffect
脚本,并赋值成员对象,如下,
这样特效就会渲染到RenderTexture
上了,如下:
我们再使用一张RawImage
来显示它,
我们可以调节RawImage
的颜色来调整特效的颜色,如下:
var len = audioSource.clip.length;
根据进度条进度设置当前时间戳
slider.onValueChanged.AddListener((v) =>
{
if (v < 1)
audioSource.time = v * audioSource.clip.length;
});
发布成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);
隐藏标题栏:
#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
另外,我们需要实现这三个按钮的逻辑,
最小化窗口:
#if UNITY_STANDALONE && !UNITY_EDITOR
/// <summary>
/// 最小化
/// </summary>
const int SW_SHOWMINIMIZED = 2;
// 获得窗口句柄
var hwd = GetForegroundWindow();
// 设置窗口最小化
ShowWindow(hwd, SW_SHOWMINIMIZED);
#endif
最大化窗口:
#if UNITY_STANDALONE && !UNITY_EDITOR
/// <summary>
/// 最大化
/// </summary>
const int SW_SHOWMAXIMIZED = 3;
// 获得窗口句柄
var hwd = GetForegroundWindow();
// 设置窗口最小化
ShowWindow(hwd, SW_SHOWMAXIMIZED);
#endif
关闭程序:
Application.Quit();
本文的工程源码我以上传到CODE CHINA
,感兴趣的同学可自行下载学习,
地址:https://codechina.csdn.net/linxinfa/UnityMusicPlayer
注:我使用的Unity
版本为Unity 2020.1.14f1c1 (64-bit)
,
温馨提示:本工程仅供学习使用,禁止用于商业用途,否则后果自负
最后,附上滨崎步MY ALL
的歌词,初识不知曲中意,再听已是曲中人!
多少时光
我们一同经历
多少路程
我们一起走过
至今我们所留下的
虽然不够完美却也灿烂过
如今在这里 那些结晶
正闪耀著骄傲的光辉
一直都那麽开心和快乐
坦白说并不是那麽回事
然而我们永远
都不会是孤身一人
想让你看见梦想的所在
没有终结 没有消亡
真的很想看见那样的梦想
那正是我的愿望
想要一直守护在你身旁
不管即将发生什么
我将用我的全部
一直将你守护
从不曾有丝毫后悔
知道现在我都可以这样断言
我们一直都在竭尽全力地
奋战到底
在那些铭心的夜晚
事实上也会常常想起你
然而我们永远
都不是孤身一人
看到你的笑颜
令人爱恋 令人目眩
多想再看到那样的笑颜
所以我仍活到今天
我能感觉到你的爱
有力而温暖
那样无偿的爱情
我尽全力地感受著
想让你看见梦想的所在
没有终结 没有消亡
真的很想让你看见那样的梦想
那正是我的愿望
我想守护在你的身旁
不管即将发生什麽
我将用我的全部
一直将你守护
好了,就到这里吧,我要再去单曲循环听亿编了~
我是林新发:https://blog.csdn.net/linxinfa
原创不易,若转载请注明出处,感谢大家~
喜欢我的可以点赞、关注、收藏,如果有什么技术上的疑问,欢迎留言或私信,我们下期见~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。