Unity 3D音乐播放器
操作系统 | 云服务/平台 | 技术难度 | 关注领域 |
---|---|---|---|
Android | Intermediate | 3D Audio Gaming |
任务目标
音乐使人心情愉悦,带有3D音效的音乐则更能给人带来一种身临其境的感觉,为了实现这种美妙的体验,所以尝试使用高通的3D音频插件基于unity实现一款供自己娱乐的音乐播放器,相信这将会很有趣。
这是组成天空盒1的6张背景图。
这是组成天空盒2的6张背景图。
4张图,用作音乐播放器按钮的贴图。
它是音响模型。
所需材料/所需清单/工具
源码/示例/可执行的应用程序
附加资料
以下展示了在这个项目中使用到的部分。
1. 在带有骁龙845处理器的安卓设备上安装了unity编译的apk,主要用来运行apk并查看3D音乐播放器的效果。
2.windows 7
3.type-c 数据线
4.Unity 2018.3.9f1个人版,所有的开发都是基于Unity。
5.骁龙Profiler,一个用来查看apk实时性能指标的工具。
6.3D音频插件,需要导入到Unity中。
部署项目
1.从链接处下载,安装PC。
2. 2. Download Snapdragon Profiler from https://developer.qualcomm.com/software/snapdragon-profiler, and install it to PC.
3. 3. Download 3D Audio Plugin for Unity from https://developer.qualcomm.com/software/3d-audio-plugin-unity, and import into Unity.
4. 寻找按键贴图、音响模型、天空盒资源等,放到Asset子目录下。
5.下载你喜欢的音乐,放到Assets目录下。
6.一步一步实现需要的功能。
7.根据高通提供的步骤试着添加音效。
8.选择android平台,连接设备,然后编译,如果没有问题,将会生成apk并自动安装。
9.打开apk,欣赏它。
10.如果你关注性能指标,你可以使用骁龙Profiler 来查看。
11.如果没有问题,上传代码到github.
工作流程
一、开始应该做些什么?
首先,我尝试实现一些基本功能,比如播放、暂停、上一首、下一首、显示正在播放的歌曲信息,因此创建了一个名为MusicPlayer的脚本。
/Assets/Scripts/MusicPlayer.cs
using UnityEngine;
using UnityEngine.UI;
public class MusicPlayer : MonoBehaviour
{
private AudioSource audioSource;
private bool isPlay = false;
private int audioClipIndex = 0;
public AudioClip[] audioClips;
public Button previousButton; //上一首的按键
public Button nextButton; //下一首的按键
public Button playOrPauseButton; // 播放和暂停按键
public Text playOrPauseText;
public Text MusicNameText; //歌曲名称
public Text NowTimeText; //歌曲当前时间(实际上获取的是 AudioSource的时间)
public Text FullTimeText; //歌曲总时间
public Slider MusicTimeSlider; //控制快进或者后退的滑块
public Sprite PausePicture;
public Sprite PlayPicture;
private int cliphour;
private int clipminute;
private int clipsecond;
private int curhour;
private int curminute;
private int cursecond;
void Start()
{
audioSource = GetComponent<AudioSource>();
audioSource.clip = audioClips[audioClipIndex];
MusicNameText.text = audioClips[audioClipIndex].name;
/* 按下previousButton按键,会给PreviousAudio函数发消息。*/
previousButton.onClick.AddListener(PreviousAudio);
nextButton.onClick.AddListener(NextAudio);
playOrPauseButton.onClick.AddListener(PlayOrPauseAudio);
MusicTimeSlider.onValueChanged.AddListener(
delegate{
setAudioTimeValueChange();
}
);
}
/*更新歌曲信息*/
private void updateMusicInfo()
{
MusicNameText.text = audioClips[audioClipIndex].name;
cliphour = (int) audioSource.clip.length / 3600;
clipminute = (int) (audioSource.clip.length - cliphour * 3600)/ 60;
clipsecond = (int) (audioSource.clip.length - cliphour * 3600 - clipminute * 60);
FullTimeText.text = string.Format("{0:D02}:{1:D2}:{2:D2}", cliphour, clipminute, clipsecond);
}
/*拖动滑块时,UI界面更新滑块值*/
private void setAudioTimeValueChange() //改变滑块值;
{
//audioSource.time = MusicTimeSlider.value * audioSource.clip.length;
audioSource.time = MusicTimeSlider.value * audioSource.clip.length;
}
/*按下播放或者暂停按键时的处理逻辑*/
private void PlayOrPauseAudio()
{
if (isPlay)
{
isPlay = false;
audioSource.Pause();
//playOrPauseText.text = "Play";
playOrPauseButton.GetComponent<Image>().sprite = PausePicture;
}
else
{
isPlay = true;
audioSource.Play();
//playOrPauseText.text = "Pause";
playOrPauseButton.GetComponent<Image>().sprite = PlayPicture;
}
}
/*按下下一首按键时的处理逻辑*/
private void NextAudio()
{
audioClipIndex++;
if (audioClipIndex > audioClips.Length - 1)
{
audioClipIndex = 0;
}
if (audioSource.isPlaying == true)
{
audioSource.Stop();
}
audioSource.clip = audioClips[audioClipIndex];
updateMusicInfo(); //更新歌曲信息
ShowAudioTIme();
audioSource.Play();
playOrPauseButton.GetComponent<Image>().sprite = PlayPicture;
}
/*按上一首按键时的处理逻辑*/
private void PreviousAudio()
{
audioClipIndex--;
if (audioClipIndex < 0)
{
audioClipIndex = audioClips.Length - 1;
}
if (audioSource.isPlaying == true)
{
audioSource.Stop();
}
audioSource.clip = audioClips[audioClipIndex];
updateMusicInfo(); //更新歌曲信息
ShowAudioTIme();
audioSource.Play();
playOrPauseButton.GetComponent<Image>().sprite = PlayPicture;
}
/*显示当前时间*/
private void ShowAudioTIme()
{
curhour = (int)audioSource.time / 3600;
curminute = (int)(audioSource.time - curhour * 3600) / 60;
cursecond = (int)(audioSource.time - curhour * 3600 - curminute * 60);
NowTimeText.text = string.Format("{0:D2}:{1:D2}:{2:D2}", curhour, curminute, cursecond);
MusicTimeSlider.value = audioSource.time / audioSource.clip.length;
}
/*在每一帧中时间,歌曲信息等*/
void Update()
{
ShowAudioTIme();
updateMusicInfo();
}
}
二、如何显示音频数据?
为了实现音频数据可视化,我创建了两个脚本,一个是MakeFreqBands .cs,另外一个是DrawFreqBands.cs
/*平均一定范围内的频谱数据,并赋值给freqBand 数组*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MakeFreqBands : MonoBehaviour
{
public AudioSource audioSource;
public static float[] samples = new float[512];
public static float[] freqBand = new float[8];
public GameObject prefab;
GameObject[] MusicCube = new GameObject[512];
// Start is called before the first frame update
void Start()
{
audioSource = GetComponent<AudioSource> ();
}
// Update is called once per frame
void Update()
{
GetAudioSourceSpectrum();
MakefreqBand();
}
void GetAudioSourceSpectrum()
{
audioSource.GetSpectrumData(samples, 0, FFTWindow.Blackman);
}
void MakefreqBand()
{
int count = 0;
for (int i = 0; i < 8; i++)
{
float average = 0;
int sampleCount = (int)Mathf.Pow(2, i) * 2; //2^n
if (i == 7)
{
sampleCount += 2;
}
for (int j = 0; j < sampleCount; j++)
{
average += samples[count] * (count + 1); //累加2^n以内的频谱数据
count++;
}
average /= count; //求均值
freqBand[i] = average * 10; //均值赋给freqBand
}
}
}
/*根据freqBand值,修改cube的y值以更新cube大小*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawFreqBands : MonoBehaviour
{
public int bandNum;
public float multiplier, startScale;
// Start is called before the first frame update
void Start()
{
//
}
// Update is called once per frame
void Update()
{
transform.localScale = new Vector3 (transform.localScale.x, (MakeFreqBands.freqBand[bandNum] * multiplier + startScale), transform.localScale.z);
}
}
三、如何实现手机触控?
为了实现移动音响模型,我添加了一个名为MoveSpeaker.cs的脚本。
using UnityEngine;
using System.Collections;
public class MoveSpeaker : MonoBehaviour {
Vector2 m_screenPos = new Vector2(); //记录手指触碰的位置
void Start()
{
//Input.multiTouchEnabled = true;//开启多点触碰
}
void Update()
{
if (Input.touchCount <= 0)
return;
if (Input.touchCount == 1) //单点触碰移动摄像机
{
if (Input.touches[0].phase == TouchPhase.Began)
m_screenPos = Input.touches[0].position; //记录手指刚触碰的位置
if (Input.touches[0].phase == TouchPhase.Moved) //手指在屏幕上移动,移动摄像机
{
transform.Translate(new Vector3( Input.touches[0].deltaPosition.x * 50 * Time.deltaTime, Input.touches[0].deltaPosition.y * 50 * Time.deltaTime, 0));
}
}
}
}
四、如何更改和旋转天空盒?
为使得背景看起来更加漂亮,添加了两个脚本,一个是ChangeSkyBox.cs,另外一个是RotateSkyBox.cs
/”设置每过60s更新一个SkyBox”/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ChangeSkyBox : MonoBehaviour
{
/*天空盒数组mats*/
public Material[] mats;
private int index;
// Start is called before the first frame update
void Start()
{
InvokeRepeating("DoChangeSkyBox", 0, 60f);
}
void DoChangeSkyBox()
{
RenderSettings.skybox = mats[index];
index++;
index %= mats.Length;
}
void Update()
{
//
}
}
/*旋转天空盒*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateSkyBox : MonoBehaviour
{
void Start()
{
}
void Update()
{
float num = RenderSettings.skybox.GetFloat("_Rotation");
RenderSettings.skybox.SetFloat("_Rotation", num + 0.05f);
}
}
1、https://github.com/ThunderSoft-XA/Solar-System-With-3D-Sound.git从此链接处下载源码
2、安装unity,unity版本低于5的不可用。(主要是3D音频插件要求unity版本高于5。)
3、双击.unity文件打开demo.
4、编译成apk并安装到安卓设备。
5、打开apk,欣赏它。
6、如果关注性能指标,可以使用骁龙Profiler查看。
贡献者信息
姓名 | 公司 |
---|---|
Zhen sunzhen@thundersoft.com |
Thundersoft |
Rong yangrong0925@thundersoft.com |
Thundersoft |
Jie wangjie0508@thundersoft.com |
Thundersoft |
Kou kouzw0723@thundersoft.com |
Thundersoft |
Eric yansh0810@thundersoft.com |
Thundersoft |
>>浏览更多Qualcomm硬件案例:http://qualcomm.csdn.net/m/zone/qualcomm2016/project
Qualcomm 开发者专区是 Qualcomm 联合CSDN 共同打造的面向中国开发者的技术专区。致力于通过提供全球最新资讯和最多元的技术资源及支持,为开发者们打造全面一流的开发环境。本专区将以嵌入式、物联网、游戏开发、Qualcomm® 骁龙™处理器的软件优化等技术为核心,打造全面的开发者技术服务社区,为下一代高性能体验和设计带来更多的想法和灵感。
加入 Qualcomm 开发者专区