传送门:(完整工程见第五章篇尾)
音乐播放器的实现(一)—— Audio Listener和Audio Source面板的介绍
音乐播放器的实现(二)—— UI面板的制作
音乐播放器的实现(三)—— 进度条控制、播放、暂停、上(下)一曲、播放时间和总时间显示
音乐播放器的实现(五)—— 音量的加减和静音
前言:
上一篇简单的实现了算是顺序播放的上一曲和下一曲,这篇加上播放模式(顺序播放、单曲循环、随机播放),再优化上/下一曲,同时显示所有歌曲的列表,并能点击播放对应的歌曲音频。
先看下最终效果:
具体操作
一、首先,我们还是先完善UI面板:
1、在上一篇的基础上,新建一个按钮Button命名为pModeImage,Button的图片用来显示播放模式对应的图片(文本可以删掉);
2、准备三张图片放入Project面板中的Resources文件夹下的Sprite文件夹中(可根据自己需求放置,但一定要在Resources文件夹下,当然后台代码对应的路径也要更改),分别命名为:stright、circle和random;
3、新建一个Scroll View,用来显示歌曲的列表,在content下新建一个Button,用来点击播放对应歌曲。在content上添加布局组件,调整相关参数,使Button大小位置合适,最后把该Button拖至Project面板中形成一个预制体,存放在Prefab文件夹中,再删掉或隐藏掉content下的Button按钮。
最终效果如下图所示:
二、核心代码
1、相对上一篇,我把播放音频的方法独立了出来,方便调用。
///
/// 播放索引为index的音频
///
/// 音频索引
void playMusic(int index)
{
aus.clip = auc[index]; //音频播放器的音频为aus数组中索引对应的音频
slid.value = 0; //初始化进度条
aus.time = 0; //初始化音乐的播放时间(从头开始播)
aus.Play(); //播放音频
}
2、音乐列表
实例化每个音频按钮,形成音乐列表,该方法在Start()中调用,即开始运行时就形成歌曲列表。(其中
item为上面制作的Button预制体。)
public GameObject item; //要实例化的列表按钮对象,即Button预制体
private GameObject it; //实例化出的列表音频按钮
void initItem()//音频列表
{
List<GameObject> L = new List<GameObject>();//列表L
for (int i = 0; i < auc.Length; i++)
{
it = Instantiate(item); //实例化音频列表的各个按钮
//设置父节点为Scroll View下的content
it.transform.SetParent(GameObject.Find("Content").transform, false);
//音频名
string[] name = auc[i].name.Split('-');
//连接音频名,序号、名字、作者
it.transform.Find("Text").GetComponent<Text>().text = string.Format("{0} {1}-- " + "{2} ", i + 1, name[0], name[1]);
//把每个按钮放入列表L
L.Add(it);
}
GameObject[] g = new GameObject[L.Count]; //数组g,长度等于列表L 的长度
L.CopyTo(g); //把列表L的内容复制到数组g中
//给每个音频按钮加上监听,点击即播放对应音频
foreach(GameObject gob in g)
{
gob.GetComponent<Button>().onClick.AddListener(delegate
{
index = Array.IndexOf(g, gob); //获取当前点击的按钮在数组g中的索引
playMusic(index); //播放音频
});
}
}
效果如下图所示:
3、播放模式
(1)枚举出顺序播放、单曲循环、随机播放这三个模式:
public enum playMode
{
stright,//列表循环
circle1,//单曲循环
random //随机播放
}
private playMode playM = playMode.stright; //初始是顺序播放
private GameObject pModeImage; //播放模式的图片切换
在Start()中调用:
void initPlayMode()//初始化播放模式
{
//找到名为pModeImage的按钮
pModeImage = GameObject.Find("pModeImage");
//给它添加监听(调用PlayMode方法,来切换播放模式)
pModeImage.GetComponent<Button>().onClick.AddListener(PlayMode);
}
以下为PlayMode()方法:
//变量t,初始值为1
int t = 1;
///
/// 播放模式
///
public void PlayMode()
{
t++;
if (t == 1)
{
//播放模式为顺序播放
playM = playMode.stright;
//上传顺序播放对应的图片
pModeImage.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/stright");
}
if(t==2)
{
//播放模式为单曲循环
playM = playMode.circle1;
//上传单曲循环对应的图片
pModeImage.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/circle");
}
if(t==3)
{
//播放模式为随机播放
playM = playMode.random;
//上传速记播放对应的图片
pModeImage.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/random");
//初始化t
t = 0;
}
}
(2)重新修改了LastMusic()和NextMusic()方法:
上一曲:
private void LastMusic()
{
if (playM == playMode.random) //如果播放模式是随机播放
randomRange(); //随机播放
else
{
index--; //索引减一
//若索引等于-1,即当前播放的音频是aus数组列表的第一首,则上一首应为aus数组列表的最后一首
if (index == -1)
index = auc.Length - 1;
playMusic(index);
}
}
下一曲:
private void NextMusic()
{
if(playM==playMode.random) //如果播放模式是随机播放
randomRange(); //随机播放
else
{
index++; //索引加一
//若索引等于aus音频数组的长度,即当前播放的音频是最后一首,则下一首应为aus数组列表的第一首
if (index == auc.Length)
index = 0;
playMusic(index);//播放音频
}
}
随机播放时调用的代码:
public void randomRange()//随机播放
{
int r = UnityEngine.Random.Range(0, auc.Length);//生成随机数(包含0,不包含auc.Length);
if(index == r) //如果随机数等于当前播放的音频的索引(避免播放同一首)
r++; //索引加一
//若索引等于aus音频数组的长度,即当前播放的音频是最后一首,则下一首应为aus数组列表的第一首
if (r == auc.Length)
r = 0;
playMusic(r);//播放音频
}
4、自动播放下一曲
当进度条走到最后时,播放下一曲,若是单曲循环,则依旧播放当前的音频
void Slider()//当进度条走到最后时,播放下一曲
{
if (currentHour == clipHour && currentMinute == clipMinute && currentSecond == clipSecond)
{
if (playM == playMode.circle1)//如果播放模式是单曲循环,下一曲还是当前这首
playMusic(index);
else
NextMusic();//下一曲
}
}
DragSlider脚本不变。 Player脚本如下:
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Player: MonoBehaviour {
public enum playMode
{
stright,//列表循环
circle1,//单曲循环
random //随机播放
}
public Text musicName; //显示歌曲的名字及歌手
public Text nowTime; //显示歌曲已播放的时间长度
public Text allTime; //显示当前歌曲的总时间长度
public Slider slid; //进度条
public Transform buttons; //按钮所在的集合体
public AudioClip[] auc; //存放歌曲的数组
public AudioSource aus; //音频播放器
private int index; //数组aus的索引
private int currentHour, currentMinute, currentSecond; //已播放的时间(时、分、秒)
private int clipHour, clipMinute, clipSecond; //总时间(时、分、秒)
private playMode playM=playMode.stright; //初始是顺序播放
private GameObject pModeImage; //播放模式的图片切换
private GameObject vioce_butt, voice_text, voice_slid; //音量按钮、音量数字、音量进度条
int clickNum=0,value;//变量
bool mouseIn;//鼠标进入
public GameObject item; //要实例化的列表按钮对象
private GameObject it; //实例化出的列表音频按钮
public static Player _instance;
void initItem()//音频列表
{
List<GameObject> L = new List<GameObject>();//列表L
for (int i = 0; i < auc.Length; i++)
{
it = Instantiate(item); //实例化音频列表的各个按钮
it.transform.SetParent(GameObject.Find("Content").transform, false);
//音频名
string[] name = auc[i].name.Split('-');
//连接音频名,序号、名字、作者
it.transform.Find("Text").GetComponent<Text>().text = string.Format("{0} {1}-- " + "{2} ", i + 1, name[0], name[1]);
//把每个按钮放入列表L
L.Add(it);
}
GameObject[] g = new GameObject[L.Count]; //数组g,长度等于列表L 的长度
L.CopyTo(g); //把列表L的内容复制到数组g中
//给每个音频按钮加上监听,点击即播放对应音频
foreach(GameObject gob in g)
{
gob.GetComponent<Button>().onClick.AddListener(delegate
{
index = Array.IndexOf(g, gob);
playMusic(index); //播放音频
});
}
}
void initVoice()//初始化
{
vioce_butt = GameObject.Find("voice_button");
voice_text = GameObject.Find("voice_text");
voice_slid = GameObject.Find("voice_slider");
//voice_slid.SetActive(false); //开始时声音进度条不显示(当鼠标放在音量按钮上时显示)
voice_slid.GetComponent<Slider>().value = 20;//开始声音为20;
//开始先调用一次,设置初始的音量大小
voice();
//给进度条添加监听,进度条的值改变即调用voice()方法
voice_slid.GetComponent<Slider>().onValueChanged.AddListener(delegate { voice(); });
//给声音按钮添加监听
vioce_butt.GetComponent<Button>().onClick.AddListener(delegate
{
clickNum++;//点击按钮的次数
if(clickNum==1)//1为打开静音,0为关闭静音
{
//打开音频播放器的静音
aus.mute = true;
//上传对应的静音的图片
vioce_butt.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/voiceMute");
//存下点击静音时的进度条的值
value = (int)voice_slid.GetComponent<Slider>().value;
//进度条的value值为0
voice_slid.GetComponent<Slider>().value = 0;
}
else
{
//关闭音频播放器的静音
aus.mute = false;
//上传对应的不静音的图片
vioce_butt.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/voice1");
//进度条的值变为关闭静音前的值
voice_slid.GetComponent<Slider>().value = value;
//初始化点击次数
clickNum = 0;
}
});
}
void initPlayMode()//初始化播放模式
{
//找到名为pModeImage的按钮
pModeImage = GameObject.Find("pModeImage");
//给它添加监听(调用PlayMode方法,来切换播放模式)
pModeImage.GetComponent<Button>().onClick.AddListener(PlayMode);
}
void Start()
{
_instance = this;
aus.Stop(); //开始运行时不播放,点播放键才开始播放
#region 注:该方式虽然可以直接达到拖动进度条就播放到进度条当前的音乐,但会产生滋滋啦啦的杂音,是因为进度条value变化,使之不断调用onValueChanged,从而让音乐的进度发生了变化,音乐前后颠倒,不断重叠
///
/// 所以新建了个DragSlider脚本,挂在进度条上,来实现拖动播放的效果
///
//slid.onValueChanged.AddListener(delegate
//{
// //加上之后,避免拖动进度条到最后不松手时报错
// if (slid.value == 1)
// return;
// //给进度条添加事件监听,每当拖动进度条,歌曲从相应的位置播放
// aus.time = slid.value * aus.clip.length;
//});
#endregion
foreach (Transform go in buttons) //遍历所有的操作按钮
{
go.GetComponent<Button>().onClick.AddListener(delegate //根据按钮名给按钮添加事件监听
{
switch (go.name)
{
case "lastM": //点击“上一曲”按钮
LastMusic();
break;
case "pause": //点击“暂停”按钮
Pause();
break;
case "play": //点击“播放”按钮
Play();
break;
case "nextM": //点击“下一曲”按钮
NextMusic();
break;
}
});
}
initItem(); //音频列表
initVoice(); //音量部分初始化
initPlayMode(); //初始化播放模式
}
void Update () {
//当前播放过的时间
nowtime();
//当前正在播放的音频的总时间
alltime();
//当前正在播放的音乐
nowMusic();
//单机左键,隐藏所有要隐藏的
//setActiveFalse();
}
void nowtime()//当前播放过的时间
{
currentHour = (int)aus.time / 3600; //时
currentMinute = (int)(aus.time - currentHour * 3600) / 60;//分
currentSecond = (int)(aus.time - currentHour * 3600 - currentMinute * 60);//秒
//显示当前播放过的时间
nowTime.text = string.Format("{0:D2}:{1:D2}:{2:D2} ",
currentHour, currentMinute, currentSecond);
//进度条变化
//(之后更新版)如果当前没有拖拽进度条
if(!DragSlider.isDrag)
slid.value = aus.time / aus.clip.length;
}
///
/// 歌曲总时间
///
void alltime()
{
//slid.value = 0;
clipHour = (int)aus.clip.length / 3600;//时
clipMinute = (int)(aus.clip.length - clipHour * 3600) / 60;//分
clipSecond = (int)(aus.clip.length - clipHour * 3600 - clipMinute * 60);//秒
//显示歌曲总时间
allTime.text = string.Format("{0:D2}:{1:D2}:{2:D2}",
clipHour, clipMinute, clipSecond);
}
///
/// 上一曲
///
private void LastMusic()
{
if (playM == playMode.random) //如果播放模式是随机播放
randomRange(); //随机播放
else
{
index--; //索引减一
//若索引等于-1,即当前播放的音频是aus数组列表的第一首,则上一首应为aus数组列表的最后一首
if (index == -1)
index = auc.Length - 1;
playMusic(index);
}
}
private void Pause() //暂停
{
aus.Pause();
}
private void Play() //播放
{
//如果当前正在播放,则返回
if (aus.isPlaying)
return;
aus.Play();
}
///
/// 下一曲
///
public void NextMusic()
{
if(playM==playMode.random) //如果播放模式是随机播放
randomRange(); //随机播放
else
{
index++; //索引加一
//若索引等于aus音频数组的长度,即当前播放的音频是最后一首,则下一首应为aus数组列表的第一首
if (index == auc.Length)
index = 0;
playMusic(index);//播放音频
}
}
void nowMusic() //当前的音频
{
AudioClip clip = aus.clip; //当前播放的音频
string n = aus.clip.name; //当前播放的音频的名字
string[] na = n.Split('-'); //以“-”为分割点,把音频名分为若干部分
//显示当前正在播放的歌曲的名字【歌曲名(默认字体,25号)+歌手名(默认字体,18号,红色)的形式】
musicName.text = string.Format("{0} " + "\n{1} ", na[0], na[1]);
index = Array.IndexOf(auc, clip);//当前播放的音频在aus数组中的索引
Slider(); //当进度条走到最后时,播放下一曲
}
void Slider()//当进度条走到最后时,播放下一曲
{
if (currentHour == clipHour && currentMinute == clipMinute && currentSecond == clipSecond)
{
if (playM == playMode.circle1)//如果播放模式是单曲循环,下一曲还是当前这首
playMusic(index);
else
NextMusic();//下一曲
}
}
///
/// 播放索引为index的音频
///
/// 音频索引
void playMusic(int index)
{
aus.clip = auc[index]; //音频播放器的音频为aus数组中索引对应的音频
slid.value = 0; //初始化进度条
aus.time = 0; //初始化音乐的播放时间(从头开始播)
aus.Play(); //播放音频
}
//变量t
int t = 1;
///
/// 播放模式
///
public void PlayMode()
{
t++;
if (t == 1)
{
//播放模式为顺序播放
playM = playMode.stright;
//上传顺序播放对应的图片
pModeImage.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/stright");
}
if(t==2)
{
//播放模式为单曲循环
playM = playMode.circle1;
//上传单曲循环对应的图片
pModeImage.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/circle");
}
if(t==3)
{
//播放模式为随机播放
playM = playMode.random;
//上传速记播放对应的图片
pModeImage.GetComponent<Image>().sprite = Resources.Load<Sprite>("Sprite/random");
//初始化t
t = 0;
}
}
//设置和显示音量大小
void voice()
{
//把进度条最大值改为100,最小值为0
//音频播放器的音量volume = 进度条的值 * 0.01f。(因为volume的范围为:0~1)
aus.volume = voice_slid.GetComponent<Slider>().value * 0.01f;
//音量的文本显示(为进度条的value值,value值是浮点型,这里显示成整型)
voice_text.GetComponent<Text>().text = ((int)voice_slid.GetComponent<Slider>().value).ToString();
}
public void voice_button(bool on)
{
if(on)
{
voice_slid.SetActive(true);
mouseIn = true;
}
else
{
mouseIn = false;
voice_slid.SetActive(false);
}
}
///
/// 单机左键,隐藏所有要隐藏的
///
void setActiveFalse()
{
if(Input.GetMouseButtonDown(0))
{
if(mouseIn==false)
voice_slid.SetActive(false);
}
}
public void randomRange()//随机播放
{
int r = UnityEngine.Random.Range(0, auc.Length);//生成随机数(包含0,不包含auc.Length);
if(index == r) //如果随机数等于当前播放的音频的索引(避免播放同一首)
r++; //索引加一
//若索引等于aus音频数组的长度,即当前播放的音频是最后一首,则下一首应为aus数组列表的第一首
if (r == auc.Length)
r = 0;
playMusic(r);//播放音频
}
}
代码备注的也比较详细,若文中存在错误或者描述不清的地方也请指正,欢迎评论区留言共同探讨,以上。