Unity技术分享之使用Unity实现卡拉OK歌词过渡效果

本文分享了一个使用Unity创建卡拉OK歌词过渡效果的项目,包括动态调整歌词、读取KSC歌词文件、歌曲控制及颜色变换等功能。通过Mask实现过渡效果,而非使用Shader。附带工程文件下载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好长时间之前做过的一个项目 , 其中设计到用Unity模拟卡拉OK歌词过渡的效果 , 如下图所示 ↓ , 这里简单把原理部分分享一下.

演示效果

  • 实现歌词动态调整功能
  • 实现动态读取歌词文件功能
  • 实现歌曲快进快退功能
  • 实现歌曲单字时间匹配功能
  • 实现可动态更换歌词前景色背景色功能

注:
这里为实现精准过渡效果使用的是KSC歌词文件, 并不是LRC文件哦 .


这其中我认为就是如何实现歌词部分的前景色向后景色过渡的效果了, 开始的时候我想的也是很复杂 , 使用Shader的形式实现 ,网上找了一些相关代码 , 发现不是特别理想 , 最终还是自己尝试着用Mask来实现的, 发现效果还不错 !
因为今天下班就过年回家啦! 其他细节之后会完善的 , 今天把工程文件先上传了 .

歌词效果类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using DG.Tweening;
using DG.Tweening.Core;

/// <summary>
/// 用于显示歌词过渡的效果
/// 1. 获得路径加载并解析歌词文件信息
/// 2. 判断当前歌曲是否播放( 歌曲暂停的时候歌词效果也暂停 , 歌曲停止的时候歌词效果消失 )
/// 3. 判断歌曲快进或快退事件
/// </summary>
public class LayricPanelEffect : MonoSingleton<LayricPanelEffect>
{
   
	#region *********************************************************************字段

	//由外部传入的声音资源
	[HideInInspector,SerializeField]
	public AudioSource audioSource;
	//歌词前景颜色;歌词后景颜色
	[SerializeField]
	public Color32 frontTextColor = Color.white, backTextColor = Color.black, outlineColor = Color.white;
	//歌词面板的前景部分和后景部分
	public RectTransform rectFrontLyricText, rectBackLyricMask;
	public Slider slider;
	//歌词文件路径
	[HideInInspector,SerializeField]
	public string lyricFilePath;

	//是否开始播放当前行歌词内容
	public bool isStartLyricEffectTransition = true;
	//歌词调整进度 ( 纠错 )
	//	[HideInInspector]
	public float lyricAdjust = -5f;

	//歌词文本信息
	//	[HideInInspector]
	[SerializeField,HideInInspector]
	public Text _lyricText;
	public Text _textContentLyric, _textLogMessage;

	private Vector2 tempFrontSizeDelta, tempBackSizeDelta;

	//用于访问歌词正文部分的内容在KscWord类中
	private KSC.KscWord kscword = new KSC.KscWord ();
	private KSC.KscWord curKscword = new KSC.KscWord ();

	//内部定时器( 由外部传入参数来控制 , 用来记录歌曲播放的当前时间轴 )
	private float _timer = 0.00f;

	#endregion

	/// <summary>
	/// 初始化一些变量
	/// </summary>
	void InitSomething ()
	{
   
		//坚持对歌词文件进行赋值操作
		if (_lyricText == null || rectFrontLyricText.GetComponent <ContentSizeFitter> () == null) {
   
			if (rectFrontLyricText.GetComponent <Text> () == null) {
   
				_lyricText = rectFrontLyricText.gameObject.AddComponent <Text> ();
			}
			_lyricText = rectFrontLyricText.GetComponent <Text> ();

			//保持歌词实现自适应
			rectFrontLyricText.GetComponent <ContentSizeFitter> ().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
			rectFrontLyricText.GetComponent <ContentSizeFitter> ().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
		}
		rectBackLyricMask.GetComponentInChildren <Text> ().text = _lyricText.text;

		//歌词颜色的更改初始化
		rectBackLyricMask.GetComponentInChildren <Text> ().color = backTextColor;
		rectBackLyricMask.GetComponentInChildren <Outline> ().effectColor = outlineColor;
		rectFrontLyricText.GetComponent  <Text> ().color = frontTextColor;

		//歌词过渡的前景部分 ( 用于判断过度遮罩的长度范围 )
		tempFrontSizeDelta = rectFrontLyricText.sizeDelta;
		tempBackSizeDelta = rectBackLyricMask.sizeDelta;

		//是否开始当前歌词行播放标志位
		isStartLyricEffectTransition = true;
	}

	void Awake ()
	{
   		
		//初始化
		InitSomething ();
	}

	/// <summary>
	/// 控制歌词面板的显示
	///  1. 仅仅显示歌词面板信息 , 没有过渡效果!
	/// </summary>
	/// <param name="row">歌词正文部分行号.</param>
	/// <param name="isPanelView">If set to <c>true</c> 显示面板歌词</param>
	public void LyricPanelControllerView (KSC.KscWord curRowInfo, bool isPanelView)
	{
   
//		Debug.Log ("当前行是否开始=====>" + isPanelView.ToString ());
		_textLogMessage.text = isStartLyricEffectTransition.ToString ();

		rectBackLyricMask.sizeDelta = new Vector2 (0f, rectFrontLyricText.sizeDelta.y);

		rectBackLyricMask.GetComponentInChildren  <Text> ().text = _lyricText.text = "";
		if (isPanelView) {
   
			//根据时间得到当前播放的是第i行的歌词
			//处理歌词面板信息 , 显示歌词
			foreach (var item in curRowInfo.PerLineLyrics) {
   
				_lyricText.text += item;
				rectBackLyricMask.GetComponentInChildren<Text> ().text = _lyricText.text;
			}
			StartCoroutine (LyricPanelControllerEffect (curRowInfo, isPanelView));
		} else {
   
			StopAllCoroutines ();
			rectBackLyricMask.sizeDelta = new Vector2 (0f, rectFrontLyricText.sizeDelta.y);
//			StartCoroutine (LyricPanelControllerEffect (curRowInfo, isPanelView));
			//当前歌词结束以后将歌词框初始化成0
			rectBackLyricMask.GetComponentInChildren  <Text> ().text = _lyricText.text = string.Empty;
		}
	}

	/// <summary>
	/// 开始实现歌此过渡效果, 仅仅效果实现
	/// 1. 使用Dotween的doSizedata实现
	/// 2. 动态调整蒙板的sizedata宽度
	/// 3. 根据歌曲当前播放的时间进度进行调整
	/// </summary>
	/// <returns>The panel controller effect.</returns>
	/// <param name="isPanelEffect">If set to <c>true</c> is panel effect.</param>
	public IEnumerator LyricPanelControllerEffect (KSC.KscWord curRowInfo, bool isPanelEffect)
	{
   
		//当前时间歌词播放进度的百分比比例
		int curWordIndex = 0;
		if (isPanelEffect) {
   
			rectBackLyricMask.DORewind ();
			yield return null;
			rectBackLyricMask.sizeDelta = new Vector2 (0f, rectFrontLyricText.sizeDelta.y);
			//开始效果过渡
			if (audioSource.isPlaying) {
   
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值