她
一、 介绍
QQ炫舞是由腾讯公司开发的一款音乐舞蹈网游,玩家可以在游戏中扮演自己的虚拟角色,跳舞展示自己的舞技,与其他玩家互动。游戏包括多种不同风格的舞曲,玩家可以通过完成任务或购买道具获得更多舞曲和装扮。
本文模拟qq炫舞游戏的核心玩法。
玩家输入上下左右按键、按空格结束,成功后角色播放动画,跳到下一个level,箭头数量+1。
二、 重点知识点和游戏逻辑
1.单例模式
2.使用“队列”数据结构
3.Arrows Holder组件
4.动画混合器
5.协程
三、 箭头脚本
生成单个箭头
绿色箭头是终点
using System.Collections;
using System.Collections.Generic; // 引入命名空间
using UnityEngine;
using UnityEngine.UI;
public class Arrow : MonoBehaviour // 定义了一个名为 Arrow 的类,并继承自 MonoBehaviour 类
{
public Sprite[] arrowSprites; // 公有变量,类型为 Sprite 数组,用于存储箭头的图像资源
Image image; // 私有变量,类型为 Image,用于引用箭头的 Image 组件
[HideInInspector]
public int arrowDir; // 公有整型变量,用于存储箭头的朝向
public Color finishColor; // 公有变量,类型为 Color,定义了箭头到达终点时的颜色
private void Awake()
{
image = GetComponent<Image>(); // 获取当前 GameObject 上的 Image 组件
}
public void Setup(int dir) // 定义了一个公有方法,用于设置箭头的朝向以及显示箭头的图像
{
arrowDir = dir;
image.sprite = arrowSprites[dir]; // 设置 Image 组件的 sprite 属性为 arrowSprites 数组中对应索引的 Sprite 对象
image.SetNativeSize(); // 根据 Sprite 图像的大小调整 Image 组件的大小
}
public void SetFinish() // 定义了一个公有方法,用于将箭头的颜色更改为定义的终点颜色
{
image.color = finishColor; // 更改 Image 组件的 color 属性为定义好的终点颜色
}
}
四、 管理箭头脚本
生成一组箭头
队列加入箭头
箭头方向转换
判断箭头方向
销毁所有箭头
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowManager : MonoBehaviour
{
public static ArrowManager S; // 单例实例
private void Awake()
{
S = this; // 初始化单例实例
}
public GameObject arrowPrefab; // 箭头预制体
public Transform arrowsHolder; // 箭头容器
public static bool isFinish; // 是否完成当前波次
Queue<Arrow> arrows = new Queue<Arrow>(); // 箭头队列
Arrow currentArrow; // 当前需要敲击的箭头
public void CreateWave(int length)
{
arrows = new Queue<Arrow>(); // 初始化箭头队列
isFinish = false; // 重置是否完成当前波次的标志
for (int i = 0; i < length; i++)
{
Arrow arrow = Instantiate(arrowPrefab, arrowsHolder).GetComponent<Arrow>(); // 实例化箭头预制体
int randomDir = Random.Range(0, 4); // 随机生成箭头方向
arrow.Setup(randomDir); // 初始化箭头
arrows.Enqueue(arrow); // 将箭头添加到队列中
}
currentArrow = arrows.Dequeue(); // 取出队列中的第一个箭头作为当前需要敲击的箭头
}
public void TypeArrow(KeyCode inputKey)
{
if (ConvertKeyCodeToInt(inputKey) == currentArrow.arrowDir) // 判断是否敲击正确
{
//Type Correctly
currentArrow.SetFinish(); // 标志当前箭头已被敲击正确
if (arrows.Count > 0) // 如果队列中还有箭头
{
currentArrow = arrows.Dequeue(); // 取出队列中的下一个箭头作为当前需要敲击的箭头
}
else // 如果队列中没有箭头了
{
isFinish = true; // 标志完成当前波次
}
}
else // 如果敲击错误
{
GameManager.S.FailWave(); // 通知 GameManager 波次失败
}
}
public void ClearWave()
{
arrows = new Queue<Arrow>(); // 初始化箭头队列
foreach(Transform arrow in arrowsHolder) // 销毁箭头容器中的所有箭头
{
Destroy(arrow.gameObject);
}
}
int ConvertKeyCodeToInt(KeyCode key)
{
int result = 0;
switch(key) // 将 KeyCode 转换为 int 类型的箭头方向
{
case KeyCode.UpArrow:
{
result = 0;
break;
}
case KeyCode.DownArrow:
{
result = 1;
break;
}
case KeyCode.LeftArrow:
{
result = 2;
break;
}
case KeyCode.RightArrow:
{
result = 3;
break;
}
}
return result; // 返回转换后的箭头方向
}
}
五、 管理全局脚本
管理全局,负责控制节拍、动画控制、播放音乐、下一关生成
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager S; // 单例实例
private void Awake()
{
S = this; // 初始化单例实例
}
int currentLevel = 5; // 当前等级
public static bool isDancing; // 是否正在跳舞
//
public Animator anim; // 动画控制器
public int bpm; // 每分钟节拍数
public float startDelay; // 延迟播放音乐的时间
public AudioSource au; // 音频源
private void Start()
{
NextWave(); // 进入下一波
//
Invoke("PlaySound", startDelay); // 延迟播放音乐
}
void PlaySound()
{
au.Play(); // 播放音乐
}
void NextWave()
{
UIManager.S.ShowOrHidePointer(); // 隐藏箭头指针
UIManager.S.RenewLevelText(currentLevel); // 更新等级显示
ArrowManager.S.CreateWave(currentLevel); // 创建新的箭头波次
isDancing = true; // 进入跳舞状态
}
public void FinishWave()
{
currentLevel++; // 升级
isDancing = false; // 退出跳舞状态
ArrowManager.S.ClearWave(); // 清除箭头波次
UIManager.S.ShowOrHidePointer(); // 显示箭头指针
//
anim.SetFloat("Dance", Random.Range(0.01f, 1f)); // 随机设置动画参数
}
public void FailWave()
{
isDancing = false; // 退出跳舞状态
ArrowManager.S.ClearWave(); // 清除箭头波次
UIManager.S.ShowOrHidePointer(); // 显示箭头指针
//
anim.SetFloat("Dance", -0.01f); // 设置动画参数
}
public void PointerIsReset()
{
if (isDancing)
FailWave(); // 失败
else
NextWave(); // 进入下一波
}
}
六、 管理玩家输入脚本
玩家输入上下左右、空格。
如果输入错误、按得太快、按得太慢,重新加载当前关卡
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InputManager : MonoBehaviour
{
public static InputManager S; // 单例实例
private void Awake()
{
S = this; // 初始化单例实例
}
void Update()
{
if (!GameManager.isDancing) // 如果没有在跳舞,直接返回
return;
if (Input.GetKeyDown(KeyCode.UpArrow)) // 如果按下了上箭头键
ArrowManager.S.TypeArrow(KeyCode.UpArrow); // 处理上箭头
if (Input.GetKeyDown(KeyCode.DownArrow)) // 如果按下了下箭头键
ArrowManager.S.TypeArrow(KeyCode.DownArrow); // 处理下箭头
if (Input.GetKeyDown(KeyCode.LeftArrow)) // 如果按下了左箭头键
ArrowManager.S.TypeArrow(KeyCode.LeftArrow); // 处理左箭头
if (Input.GetKeyDown(KeyCode.RightArrow)) // 如果按下了右箭头键
ArrowManager.S.TypeArrow(KeyCode.RightArrow); // 处理右箭头
if(Input.GetKeyDown(KeyCode.Space)) // 如果按下了空格键
{
if (ArrowManager.isFinish && UIManager.S.CheckPointerIsInSweetSpot()) // 如果当前波次已完成并且箭头指针在甜点区域内
GameManager.S.FinishWave(); // 完成当前波次
else
GameManager.S.FailWave(); // 失败当前波次
}
}
}
七、 ui脚本
制作ui
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIManager : MonoBehaviour
{
public static UIManager S; // 单例实例
private void Awake()
{
S = this; // 初始化单例实例
}
public Text levelText; // 显示等级的文本框
public Transform pointer, sweetSpot; // 箭头指针和甜点区域的位置
public Transform startPos, endPos; // 指针移动的起始位置和终止位置
public float pointerMoveSpeed; // 指针移动的速度
private void Start()
{
// 根据bpm和节拍计算指针移动的速度
pointerMoveSpeed = (endPos.position.x - startPos.position.x) / (60f / GameManager.S.bpm * 8f);
}
private void Update()
{
// 每帧将指针向右移动一定距离
pointer.position += Vector3.right * pointerMoveSpeed * Time.deltaTime;
// 如果指针移动到了终止位置,将指针重置到起始位置
if (pointer.position.x > endPos.position.x)
ResetPointer();
}
// 将指针重置到起始位置
void ResetPointer()
{
pointer.position = startPos.position;
GameManager.S.PointerIsReset();
}
// 显示或隐藏指针
public void ShowOrHidePointer()
{
pointer.gameObject.SetActive(!pointer.gameObject.activeInHierarchy);
}
// 检查指针是否在甜点区域内
public bool CheckPointerIsInSweetSpot()
{
return Mathf.Abs((pointer.position.x - sweetSpot.position.x)) < 25f;
}
// 更新等级文本
public void RenewLevelText(int level)
{
levelText.text = "LEVEL " + level;
}
}
八、 制作动画
下载动画模型
https://www.mixamo.com/#/?page=1&query=
创建混合树,随机生成数值,控制动画播放
九、 添加音乐节奏
添加不同的背景音乐,根据音乐调节节奏
设置滑动条滚动速度
private void Start()
{
pointerMoveSpeed = (endPos.position.x - startPos.position.x) / (60f / GameManager.S.bpm * 8f);
}
十、 工程下载
https://wwez.lanzoul.com/ifDh20t2ef7i