最近在自己的项目开发过程中,Unity3D场景中开发了一个完整的音乐播放器,涉及到歌曲的切换、UI的变化等,虽然看起来都是些简单的代码,但整体的实现逻辑较为复杂,我自己从零开始独自开发,经过了好几次的优化和完善,才得到了比较好的播放器版本。下面我依次介绍一下我的音乐播放器搭建过程。
1 界面GameObject和UI组成介绍
在搭建过程中,涉及到比较多的还是UI,在UI上,我是通过PS进行一个一个分离开来的,然后再在Unity上进行组装拼接。因为涉及到每个按钮的变化与控制,因此将它们割离开来制作成单独的GameObject进行挂载会使得控制起来方便很多。
2 开发搭建思路
2.1 面板上的歌曲选择
2.1.1 GameObject的设置
开发时,我最开始实现的便是手柄指到对应的歌曲区域便播放对应的歌曲。这个实现起来也很容易,只要在每个文字区域挂载到Button,然后是Button的大小刚好覆盖到文字区域即可。而因为要显示歌曲文字,因此Button的Image要直接设置为None。具体如下。
2.1.2 Button按钮功能的开发
在添加好每个Button之后,接下来要实现的便是每个按钮所执行的功能,再将其挂载到Button组件下的On Click下,从而实现手柄点击控制。以下为按钮的控制代码和Unity中显示的面板。
using UnityEngine;
using UnityEngine.UI;
/*
* 此代码块用于设计音乐室面板块中音乐的选择功能,同时包括音乐的名字读取、描述展现功能
* 此代码块中建立了音乐的输出是在unity面板中拉取挂载输入,因此相关的音频文件需提前下载
* 音乐的描述需手动填写
* 当前页面一页只呈现8首歌,因此只有8个按钮
* 后续歌曲超过8首时可以通过控制musicIndex的变化来控制页面选择的音乐
*/
/// <summary>
/// 此代码块用于设计音乐室面板块中音乐的选择功能
/// </summary>
public class MusicContoller : MonoBehaviour
{
public AudioClip[] musicList; //建立一个数组,用于储存需要播放的音乐
public GameObject[] SongTitle; //建立一个数组,用于储存歌曲名称的文本物体
public int musicAmount; //记录音乐的数量
[SerializeField] GameObject pauseButton; //左下角的暂停按钮
[SerializeField] GameObject playButton; //左下角的播放按钮
public int musicIndex; //定位当前播放的音乐,用于后面的上一首和下一首按钮是使用
public bool isStartMusicPlay = false; //判断是否已经开始播放了音乐,点击任意一首歌后开始变为true
// Start is called before the first frame update
void Start()
{
AudioSource music = this.GetComponent<AudioSource>();
musicAmount = musicList.Length; //获取歌曲的数目
//Debug.Log(musicList[0].name);
//设置面板的名字输入
for(int i = 0; i < musicAmount; i++)
{
Text songTitle = SongTitle[i].GetComponent<Text>();
songTitle.text = musicList[i].name;
}
}
/// <summary>
/// 以下为定义点击每个按钮所播放的音乐已经处理的功能
///目前找不到可以识别点击按钮的功能,所以只能一个按钮设置一个功能
/// </summary>
public void Button_1_MP()//点击按钮1时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[0];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 0;
isStartMusicPlay = true;
}
public void Button_2_MP()//点击按钮2时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[1];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 1;
isStartMusicPlay = true;
}
public void Button_3_MP()//点击按钮3时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[2];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 2;
isStartMusicPlay = true;
}
public void Button_4_MP()//点击按钮4时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[3];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 3;
isStartMusicPlay = true;
}
public void Button_5_MP()//点击按钮5时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[4];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 4;
isStartMusicPlay = true;
}
public void Button_6_MP()//点击按钮6时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[5];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 5;
isStartMusicPlay = true;
}
public void Button_7_MP()//点击按钮7时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[6];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 6;
isStartMusicPlay = true;
}
public void Button_8_MP()//点击按钮8时播放的音乐
{
//设置音乐的播放
AudioSource music = this.GetComponent<AudioSource>();
//Debug.Log(0);
music.clip = musicList[7];
music.time = 0;
music.Play();
//设置选择音乐后左下角按钮的变化
pauseButton.SetActive(false);
playButton.SetActive(true);
musicIndex = 7;
isStartMusicPlay = true;
}
}
在开发完每个按钮的功能之后,接下来的便是把他们依次的挂载到对应的Button组件上。如下如所示。
2.2 面板上的按钮控制
面板上的按钮控制包括上一首、下一首、播放和暂停、音量控制几个按钮。
2.2.1 GameObject的设置
这些GameObject对应播放器下面的控制按钮,它们都挂载有Button组件,从而实现手柄的点击控制。
2.2.2 控制按钮的开发代码
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 此代码块用于设计音乐室面板块左下角的上一首、下一首、播放和暂停四个按键的功能
/// </summary>
public class ControllerButton : MonoBehaviour
{
[SerializeField] private GameObject pauseButton;
[SerializeField] private GameObject playButton;
[SerializeField] private GameObject audioController;
[SerializeField] private GameObject volumeSlider;
public bool isdisplayVolumeSlider = true; //用于判断是否点击了音量键
public bool isMusicPause = false; //用于判断音乐是否处于被用户暂停状态,默认为真
/// <summary>
/// 定义点击播放按钮的功能
/// </summary>
public void PlayButtonController()
{
//播放按钮点击后是为暂停音乐播放
pauseButton.SetActive(true);
playButton.SetActive(false);
AudioSource music = audioController.GetComponent<AudioSource>();
music.Pause();
isMusicPause = true; //判断值变为真
}
/// <summary>
/// 定义点击暂停按钮功能
/// </summary>
public void PauseButtonController()
{
//暂停按钮点击后是为继续播放音乐功能
pauseButton.SetActive(false);
playButton.SetActive(true);
AudioSource music = audioController.GetComponent<AudioSource>();
music.Play();
isMusicPause = false; //点击暂停后判断值变为假
}
/// <summary>
/// 定义下一首按钮的功能
/// </summary>
public void ForWardButtonController()
{
MusicContoller ac_mc = audioController.GetComponent<MusicContoller>();
if(ac_mc.musicIndex < ac_mc.musicAmount-1)
{
ac_mc.musicIndex++; //目前设置无随机播放, 点击即按顺序播放下一首
AudioSource music = audioController.GetComponent<AudioSource>();
music.clip = ac_mc.musicList[ac_mc.musicIndex];
music.time = 0;
music.Play();
//Debug.Log("ac_mc.musicIndex: " + ac_mc.musicIndex + "ac_mc.musicAmount: " + ac_mc.musicAmount);
}
else if(ac_mc.musicIndex == ac_mc.musicAmount -1) //如果是最后一首音乐时,则跳回重头播放
{
//Debug.Log("下一个判断");
ac_mc.musicIndex = 0;
AudioSource music = audioController.GetComponent<AudioSource>();
music.clip = ac_mc.musicList[ac_mc.musicIndex];
music.time = 0;
music.Play();
}
else
{
Debug.Log("运行出错");
}
}
/// <summary>
/// 定义上一首按钮功能
/// </summary>
public void BackButtonController()
{
MusicContoller ac_mc = audioController.GetComponent<MusicContoller>();
if (ac_mc.musicIndex > 0)
{
ac_mc.musicIndex--; //目前设置无随机播放, 点击即按顺序播放上一首
AudioSource music = audioController.GetComponent<AudioSource>();
music.clip = ac_mc.musicList[ac_mc.musicIndex];
music.time = 0;
music.Play();
}
else if (ac_mc.musicIndex == 0) //如果是最后一首音乐时,则跳回重头播放
{
ac_mc.musicIndex = ac_mc.musicAmount -1;
AudioSource music = audioController.GetComponent<AudioSource>();
music.clip = ac_mc.musicList[ac_mc.musicIndex];
music.time = 0;
music.Play();
}
}
/// <summary>
/// 定义音量键的功能,除此之外,在代码VolumeController中也有对音量按钮的控制,控制isdisplayVolumeSlider
/// </summary>
public void VolumeButtonController()
{
//当第一次点击音量按钮时,弹出音量条控制,再次点击则取消显示
if (isdisplayVolumeSlider)
{
volumeSlider.SetActive(true);
isdisplayVolumeSlider = false;
}
else
{
volumeSlider.SetActive(false);
isdisplayVolumeSlider = true;
}
}
}
2.2.2 音量的调整控制
为实现音乐播放的音量变化,在Volume物体上还有slider子物体,在这里会实现音量的拉动功能。对应的物体挂载如下所示。
具体代码如下:
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 音量滑动条控制脚本
/// </summary>
public class VolumeController : MonoBehaviour
{
[SerializeField] AudioSource audioController;
[SerializeField] Slider volumeSlider;
[SerializeField] Text text; //显示音量数字的文本
[SerializeField] GameObject ControllerButton;
private float lastDragTime;
private bool isHidden = false;
public void Start()
{
volumeSlider.value = 1;
}
// Update is called once per frame
void Update()
{
VolumeControl();
//2s内没拉动音量滑动条是隐藏滑动条
ControllerButton Con_button = ControllerButton.GetComponent<ControllerButton>();
if (Time.time - lastDragTime > 2f && !isHidden)
{
// 隐藏Slider
gameObject.SetActive(false);
isHidden = true;
Con_button.isdisplayVolumeSlider = true; //自动隐藏也算是隐藏了,所以下一次点击就要是显示
}
else
{
// 显示Slider
gameObject.SetActive(true);
}
}
public void VolumeControl()
{
audioController.volume = volumeSlider.value;
text.text = ((int)(volumeSlider.value * 100)).ToString(); //音量大小数字显示
}
public void OnSliderValueChanged()
{
if (isHidden)
{
isHidden = false;
this.gameObject.SetActive(true);
}
lastDragTime = Time.time;
}
}
2.3 音乐的播放进度条显示与控制
2.3.1 功能说明
该部分功能包括了音乐的进度条的显示以及自动滑动、时间显示、拖动进度条等功能,同时在进度条播放结束后会自动播放下一首歌曲;还有拖动进度条是歌曲自动暂停,松开后自动播放。
具体物体挂载如下所示。
2.3.2 音乐进度条控制代码
using UnityEngine;
using UnityEngine.UI;
using System;
using UnityEngine.EventSystems;
/// <summary>
/// 设计进度条功能:进度条变化、歌曲名称、时间的变化、拖拽进度条功能
/// </summary>
public class MusciProgressBar : MonoBehaviour, IDragHandler, IEndDragHandler
{
public Slider progressBar;
public AudioSource audioController;
[SerializeField] private GameObject SongName;
[SerializeField] private GameObject CoverSongName; //封面图片下方的歌曲名
[SerializeField] private GameObject TotalTime;
[SerializeField] private GameObject CurrentTime;
[SerializeField] private GameObject ControllerButton;
[SerializeField] private GameObject AudioController;
[SerializeField] private GameObject PlayButton;
[SerializeField] private GameObject PauseButton;
private float progress;
private bool isDraggingSlider = false; //定义一个bool来判断是否在拖拽进度条
public void Start()
{
progressBar.onValueChanged.AddListener(OnSliderValueChanged); //监听进度条是否被拖动
}
public void Update()
{
//设置进度条的自动滑动
AudioSource audio_controller = audioController.GetComponent<AudioSource>();
if (audio_controller.isPlaying)
{
progress = audio_controller.time / audio_controller.clip.length;
Slider progress_Bar = progressBar.GetComponent<Slider>();
progress_Bar.value = progress;
//设置进度条上面显示的歌曲名称
MusicContoller ac_mc = audioController.GetComponent<MusicContoller>();
Text songname = SongName.GetComponent<Text>();
//将输出显示格式定义为:歌曲名(1 of 8)
songname.text = ac_mc.musicList[ac_mc.musicIndex].name + "(" + (ac_mc.musicIndex + 1) + " of " + ac_mc.musicAmount + ")";
//设置封面图片下面的歌曲名
Text coversongname = CoverSongName.GetComponent<Text>();
coversongname.text = ac_mc.musicList[ac_mc.musicIndex].name;
//设置时间的显示
Text totalTime = TotalTime.GetComponent<Text>();
TimeSpan total_timeSpan = TimeSpan.FromSeconds(audio_controller.clip.length); //将时间从秒数转换为为"00:00"的显示格式
totalTime.text = string.Format("{0:D2}:{1:D2}", total_timeSpan.Minutes, total_timeSpan.Seconds);
Text currentTime = CurrentTime.GetComponent<Text>();
TimeSpan current_timeSpan = TimeSpan.FromSeconds(audio_controller.time);
currentTime.text = string.Format("{0:D2}:{1:D2}", current_timeSpan.Minutes, current_timeSpan.Seconds);
}
//判断是否音乐已经播放结束,如果自动播放结束,则自动播放下一首
ControllerButton con_button = ControllerButton.GetComponent<ControllerButton>();
MusicContoller Audio_conl = AudioController.GetComponent<MusicContoller>();
if (!audio_controller.isPlaying && !con_button.isMusicPause && Audio_conl.isStartMusicPlay) //当音乐没有播放 + 音乐不是手动暂停的 + 音乐已经进入播放进程
{
//Debug.Log(con_button.isMusicPause + "和" + Audio_conl.isStartMusicPlay);
OnMusicFinished();
//Debug.Log("结束时执行了");
}
}
public void OnSliderValueChanged(float value) //定义进度条被拖动的方法
{
if (isDraggingSlider)
{
AudioSource audio_controller = audioController.GetComponent<AudioSource>();
//Slider progress_Bar = progressBar.GetComponent<Slider>();
audio_controller.time = (long)(value * audio_controller.clip.length); //设置视频被拖到的时间
}
}
public void OnDrag(PointerEventData eventData)
{
//Debug.Log("进度条被拖拽中");
isDraggingSlider = true; //进度条在拖拽时设置判断变量为真
AudioSource au_col = audioController.GetComponent<AudioSource>();
au_col.Pause();
ControllerButton con_button = ControllerButton.GetComponent<ControllerButton>();
con_button.isMusicPause = true;
PauseButton.SetActive(true);
PlayButton.SetActive(false);
}
public void OnEndDrag(PointerEventData eventData)
{
isDraggingSlider = false; //进度条没有在拖拽时设置判断变量为假
//Debug.Log("进度条拖拽释放");
AudioSource au_col = audioController.GetComponent<AudioSource>();
au_col.Play();
ControllerButton con_button = ControllerButton.GetComponent<ControllerButton>();
con_button.isMusicPause = false;
PauseButton.SetActive(false);
PlayButton.SetActive(true);
}
/// <summary>
/// 定义一个音乐播放完后自动播放下一首
/// </summary>
public void OnMusicFinished()
{
MusicContoller ac_mc = audioController.GetComponent<MusicContoller>();
ac_mc.musicIndex++;
if (ac_mc.musicIndex >= ac_mc.musicList.Length)
{
ac_mc.musicIndex = 0;
}
AudioSource music = audioController.GetComponent<AudioSource>();
music.clip = ac_mc.musicList[ac_mc.musicIndex];
music.time = 0;
music.Play();
}
}
3 结语
以上的开发过程搭建思路写的可能比较简单,具体的逻辑思路都包含在了代码里了,如果要细写真的很长很长。同时这也是我初次开发音乐播放器,但是已具备完整的音乐播放功能。当然上面的代码可能仍有不完善的地方,欢迎各位大佬一同交流。