源码
源码(Github)
看着目录来阅读
第一个是免费视频
音游制作实用插件-Koreographer入门教程)
第二个是siki学院的收费视频
Unity2D 音游案例-节奏大师(基于Koreographer)
Demo 音游制作实用插件-Koreographer入门教程
视频
视频演示了,球的弹跳,方块的缩放,特效的显示
音游制作实用插件-Koreographer入门教程
ogg,文件,文件Track
ogg,mp3改下后缀成ogg
文件,紫图标
文件Track,黄图标
Event ID比较重要,代码通过这一变量来读取文件
球的脚本
有打点就复位(快歌打点密,不复位飞到天上去),然后加速度,
using SonicBloom.Koreo;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour {
Rigidbody rgb;
string eventID;
float jumpSpeed = 3f;
// Use this for initialization
void Start () {
jumpSpeed = 5f;
eventID = "Piano";
rgb=GetComponent<Rigidbody>();
Koreographer.Instance.RegisterForEvents(eventID, BallJump);
}
void BallJump(KoreographyEvent evt)
{
transform.position = Vector3.zero; //打的点密,不归零来不及
Vector3 v = rgb.velocity;
v.y = jumpSpeed;
rgb.velocity = v;
}
}
控制节点
三个组件,音源、蓝图标,红图标。
红图标、上面创建的紫图标拖到青图标上
效果
这个面板就是上面的“进去打点”弹出的
Main,Piano(自己命名的,脚本读的就是EventID)填上去
点击播放,快捷键“E”就进行打点(点就是两条轨道中间的“红长矩形”,点击选中变绿)
打完,Ctrl+S,退出就运行
tips:
点击激活轨道后,Ctrl+A,可以显示点的命名
Koreographer的理论内容---------------------------------------------
一种音频格式ogg
mp3转mid
需要是伴奏,有人声打点很乱。
只有伴奏也不好用,打的太密了。
所以请手打
网上推荐的mp3转mid
两个插件中的类型文件
Koreography(紫图标)
Koreography Tracker(黄图标)
一个编辑面板
关系
Koreography(紫图标)的编辑面板
编辑后的输出就是Koreography Tracker(黄图标)
Zoom或者Ctrl+上下,可以缩放轨道
打点(需要单击激活轨道UI)
快捷键E(常用,接近Pr)
Select下双击(觉得费劲)
Draw下单击
Clone还没用过
OneOff打的点都是短的,一样长的
Span打的点,按键时间长或连着,会有的变长,长短不一
以上都注意Snap to Beat Divide beat by (1)。
打钩就是打的点会对齐轨道中的竖线。
不打就自由地多不用对齐。
我是对应人声(唱一个字打一下)打点,比较密集,所以选后者
有打的点的详情需要滑轮滚一下
CtrL+A显示点的名字
一个总节点
就是上面说的控制节点
三个组件,音源、青图标,红图标。
红图标和上面创建的紫图标,都拖到青图标上
Demo Unity2D 音游案例-节奏大师(基于Koreographer)----------------------------------
进入游戏
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class StartGameButton : MonoBehaviour {
// Use this for initialization
void Start () {
GetComponent<Button>().onClick.AddListener(StartGame);
}
// Update is called once per frame
void Update () {
}
private void StartGame()
{
SceneManager.LoadScene(1);
}
}
选中是带脚本的
UI 元素 隐藏不影响
特效
场景中的3个特效是示例,隐藏不影响
键盘按键
键盘的2组TargetTop(音符生成的位置)和TargetDown(音符结束的位置)隐藏不影响
脚本 NoteObject(音符的生成)
代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SonicBloom.Koreo;
/// <summary>音符,绿色的音符</summary>
public class NoteObject : MonoBehaviour
{
#region 字属
RhythmGameController gameController;
LaneController laneController;
/// <summary>音符身上挂的组件</summary>
public SpriteRenderer visuals;
/// <summary>不同轨道(6个)上音符的图像,和透明、蓝色、绿色三种音符(蓝色到透明=长按的开始和结束,绿色=短按)</summary>
public Sprite[] noteSprites;
KoreographyEvent trackedEvent;
/// <summary>长按开始</summary>
public bool isLongNote;
/// <summary>长按结束</summary>
public bool isLongNoteEnd;
public int hitOffset;
#endregion
#region 生命
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
if (gameController.isPauseState)
{
return;
}
UpdatePosition();
GetHitOffset();
if (transform.position.z<=laneController.targetBottomTrans.position.z)
{
gameController.ReturnNoteObjectToPool(this);
ResetNote();
}
}
#endregion
#region 辅助
//初始化方法
public void Initialize(KoreographyEvent evt,
int noteNum,LaneController laneCont,
RhythmGameController gameCont,
bool isLongStart,
bool isLongEnd)
{
trackedEvent = evt;
laneController = laneCont;
gameController = gameCont;
isLongNote = isLongStart;
isLongNoteEnd = isLongEnd;
int spriteNum = noteNum;
if (isLongNote)
{
spriteNum+=6;
}
else if (isLongNoteEnd)
{
spriteNum += 12;
}
visuals.sprite = noteSprites[spriteNum - 1];
}
//将Note对象重置
private void ResetNote()
{
}
/// <summary>返回对象池</summary>
void ReturnToPool()
{
gameController.ReturnNoteObjectToPool(this);
ResetNote();
}
/// <summary>击中音符对象</summary>
public void OnHit()
{
ReturnToPool();
}
/// <summary>更新位置的方法</summary>
void UpdatePosition()
{
Vector3 pos = laneController.TargetPosition;
pos.z -= (gameController.DelayedSampleTime - trackedEvent.StartSample) / (float)gameController.SampleRate * gameController.noteSpeed;
transform.position = pos;
}
void GetHitOffset()
{
int curTime = gameController.DelayedSampleTime;
int noteTime = trackedEvent.StartSample;
int hitWindow = gameController.HitWindowSampleWidth;
hitOffset = hitWindow - Mathf.Abs(noteTime-curTime);
}
/// <summary>当前音符是否已经Miss</summary>
public bool IsNoteMissed()
{
bool bMissed = true;
if (enabled)
{
int curTime = gameController.DelayedSampleTime;
int noteTime = trackedEvent.StartSample;
int hitWindow = gameController.HitWindowSampleWidth;
bMissed = curTime - noteTime > hitWindow;
}
return bMissed;
}
/// <summary>音符的命中等级</summary>
public int IsNoteHittable()
{
int hitLevel = 0;
if (hitOffset>=0)
{
if (hitOffset>=2000 && hitOffset<=9000)
{
hitLevel = 2;
}
else
{
hitLevel = 1;
}
}
else
{
this.enabled = false;
}
return hitLevel;
}
#endregion
}
音符的生成
六个轨道,
0-5,绿色音符
6-11,长按的开始,蓝色音符
12-18,长按的结束,白色音符
音符 NoteObject
//初始化方法
public void Initialize(KoreographyEvent evt,
int noteNum,LaneController laneCont,
RhythmGameController gameCont,
bool isLongStart,
bool isLongEnd)
{
trackedEvent = evt;
laneController = laneCont;
gameController = gameCont;
isLongNote = isLongStart;
isLongNoteEnd = isLongEnd;
int spriteNum = noteNum;
if (isLongNote)
{
spriteNum+=6;
}
else if (isLongNoteEnd)
{
spriteNum += 12;
}
visuals.sprite = noteSprites[spriteNum - 1];
}
轨道 LaneController
//检测是否生成下一个新音符
void CheckSpawnNext()
{
int samplesToTarget = GetSpawnSampleOffset();
int currentTime = gameController.DelayedSampleTime;
while (pendingEventIdx < laneEvents.Count
&& laneEvents[pendingEventIdx].StartSample < currentTime + samplesToTarget)
{
KoreographyEvent evt = laneEvents[pendingEventIdx];
int noteNum = evt.GetIntValue();
NoteObject newObj = gameController.GetFreshNoteObject();
bool isLongNoteStart = false;
bool isLongNoteEnd = false;
if (noteNum > 6)
{
isLongNoteStart = true;
noteNum = noteNum - 6;
if (noteNum > 6)
{
isLongNoteEnd = true;
isLongNoteStart = false;
noteNum = noteNum - 6;
}
}
newObj.Initialize(evt, noteNum, this, gameController, isLongNoteStart, isLongNoteEnd);
trackedNotes.Enqueue(newObj);
pendingEventIdx++;
}
}
音符图片的索引对应着文件的卡点
以下代码将“紫图标”中的所有点装入rawEvents。
0-5,6-11,12-17皮队ID,相应Add入六个轨道 rawEvents[i];
RhythmGameController
void Start()
{
InitializeLeadIn();
simpleMusicPlayer = simpleMusciPlayerTrans.GetComponent<SimpleMusicPlayer>();
simpleMusicPlayer.LoadSong(kgy, 0, false);
// 初始化所有音轨.
for (int i = 0; i < noteLanes.Count; ++i)
{
noteLanes[i].Initialize(this);
}
playingKoreo = Koreographer.Instance.GetKoreographyAtIndex(0);// 初始化事件。
KoreographyTrackBase rhythmTrack = playingKoreo.GetTrackByID(eventID);//获取事件轨迹// 获取Koreography中的所有事件。
List<KoreographyEvent> rawEvents = rhythmTrack.GetAllEvents();//获取所有事件
//KoreographyEvent rawEvent = rhythmTrack.GetEventAtStartSample(2419200);
//rawEvent.
for (int i = 0; i < rawEvents.Count; ++i)
{
//KoreographyEvent 基础Koreography事件定义。 每个事件实例都可以携带一个
//有效载荷 事件可以跨越一系列样本,也可以绑定到一个样本。 样品
//值(开始/结束)在“采样时间”范围内,* NOT *绝对采样位置。
//确保查询/比较在TIME而不是DATA空间中发生。
KoreographyEvent evt = rawEvents[i];
int noteID = evt.GetIntValue();//获取每个事件对应的字符串
// Find the right lane. 遍历所有音轨
for (int j = 0; j < noteLanes.Count; ++j)
{
LaneController lane = noteLanes[j];
if (noteID > 6)
{
noteID = noteID - 6;
if (noteID > 6)
{
noteID = noteID - 6;
}
}
if (lane.DoesMatch(noteID))
{
//事件对应的字符串与某个音轨对应字符串匹配,则把该事件添加到该音轨
// Add the object for input tracking.
lane.AddEventToLane(evt);
// Break out of the lane searching loop.
break;
}
}
}
//SampleRate采样率,在音频资源里有。
//命中窗口宽度,采样率*0.001*命中时长
hitWindowRangeInSamples = (int)(0.001f * hitWindowRangeInMS * SampleRate);
}
按键(LaneController)
脚本是挂在 Taget[1-6] 上的LaneController
按SDF JKL六个键
不同评价图片的判定逻辑
判定
特效
01 ClickDownSprite激活(键盘变蓝)
02 特效HitLongEffectGo实例(下图大的一直转圈圈的特效)
03 特效DownEffectGo实例(下图的绿点,音符没来前按键最明显能看到小绿点)
按下后按空,看到 01,02,03 都执行了
按下后按中,看到 01,02 都执行了
这里有DestrotEffect的脚本佐证hitEffectObjectPool对应02,downEffectObjectPool对应03。所以下图第二个特效还没见其用到
void ReturnToPool()
{
if (isHitted)
{
gameController.ReturnEffectGoToPool(gameObject,gameController.hitEffectObjectPool);
gameObject.SetActive(false);
}
else
{
gameController.ReturnEffectGoToPool(gameObject, gameController.downEffectObjectPool);
gameObject.SetActive(false);
}
}
音乐的播放
谁控制
SimpleMusicPlayer会加载AudioSource,调用SimpleMusicPlayer的方法就控制
开局以第一个音符经过按键才开始播放Bgm
SimpleMusicPlayer的AutoPlayAwake打钩的话,AudioSource的PalyAwake不管打不打,都会播放。
但是这个Demo是第一个音符经过按键才开始播放Bgm。
。。。。。
详解Unity中Time类的用法与深入探究
。。。。。
按脚本是计时8s播放Bgm。Bgm是一上来就有音符产生
。。。。。
代码来自于 RhythmGameController : MonoBehaviour
[Tooltip("开始播放音频之前提供的时间量(以秒为单位),也就是提前调用时间。相当于time计时不跑的变量")]//其他
public float leadInTime;
float leadInTimeLeft;//音频播放之前的剩余时间量,(盲猜是区别开局与暂停播放,Bgm)
/// <summary>音乐开始之前的倒计时器。相当于timer计时在跑的变量</summary>
float timeLeftToPlay;
void InitializeLeadIn()
{
if (leadInTime > 0)
{
leadInTimeLeft = leadInTime;
timeLeftToPlay = leadInTime;
}
else
{
audioCom.Play();
}
}
// Update is called once per frame
void Update () {
if (isPauseState)
{
return;
}
if (timeLeftToPlay>0)//倒数音乐开始
{
timeLeftToPlay -= Time.unscaledDeltaTime;
if (timeLeftToPlay<=0)
{
audioCom.Play();
gameStart = true;
timeLeftToPlay = 0;
}
}
if (leadInTimeLeft>0)//倒数我们的引导时间
{
leadInTimeLeft = Mathf.Max(leadInTimeLeft - Time.unscaledDeltaTime, 0);
}
......
游戏中的暂停继续会影响Bgm
RhythmGameController中一下。simpleMusicPlayer是音游插件的脚本
//游戏的开始与暂停
public void PauseMusic()
{
if (!gameStart)
{
return;
}
simpleMusicPlayer.Pause();
}
public void PlayMusic()
{
if (!gameStart)
{
return;
}
simpleMusicPlayer.Play();
}
每次按键的评价图标
如图看到,Great,Perfectdt都是击中,只是音符案件的偏移量。所以连续击中下面都有分数递增显示
#region 战斗中的三个
//显示命中等级对应的图片
public void ChangeHitLevelSprite(int hitLevel)//Great Perfect Miss三种
{
hideHitLevelImageTimeVal = 1;
hitLevelImage.sprite = hitLevelSprites[hitLevel];
hitLevelImage.SetNativeSize();
hitLevelImage.gameObject.SetActive(true);
hitLevelImageAnim.SetBool("IsNoteHittable",true);
if (comboNum>=5)//连击5次开始显示轨道上的分数
{
comboText.gameObject.SetActive(true);
comboText.text = comboNum.ToString();
comboTextAnim.SetBool("IsNoteHittable",true);
}
//hitLevelImageAnim.Play("UIAnimation");
}
private void HideHitLevelImage()
{
hitLevelImage.gameObject.SetActive(false);
}
public void HideComboNumText()//轨道上的分数。也就是在Great Perfect Miss下的分数
{
comboText.gameObject.SetActive(false);
}
#endregion
扣血与分数
可以看到口5次分,就结束了
文章来源:https://www.toymoban.com/news/detail-545733.html
#region UI上的3个
public void UpdateScoreText(int addNum)
{
score += addNum;
scoreText.text = score.ToString();
}
public void UpdateHP()
{
hp = hp - 2;
slider.value = (float)hp / 10;
if (hp==0)
{
isPauseState = true;
gameOverUI.SetActive(true);
PauseMusic();
}
}
//游戏的开始与暂停
public void PauseMusic()
{
if (!gameStart)
{
return;
}
simpleMusicPlayer.Pause();
}
public void PlayMusic()
{
if (!gameStart)
{
return;
}
simpleMusicPlayer.Play();
}
#endregion
通关 失败判定
结束界面
文章来源地址https://www.toymoban.com/news/detail-545733.html
#region 结束后的两个
public void Replay()
{
SceneManager.LoadScene(1);
}
public void ReturnToMain()
{
SceneManager.LoadScene(0);
}
#endregion
到了这里,关于“音游制作实用插件-Koreographer入门教程”,“Unity2D 音游案例-节奏大师(基于Koreographer)”的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!