源碼
源碼(Github)
看著目錄來閱讀
第一個(gè)是免費(fèi)視頻
音游制作實(shí)用插件-Koreographer入門教程)
第二個(gè)是siki學(xué)院的收費(fèi)視頻
Unity2D 音游案例-節(jié)奏大師(基于Koreographer)
Demo 音游制作實(shí)用插件-Koreographer入門教程
視頻
視頻演示了,球的彈跳,方塊的縮放,特效的顯示
音游制作實(shí)用插件-Koreographer入門教程
ogg,文件,文件Track
ogg,mp3改下后綴成ogg
文件,紫圖標(biāo)
文件Track,黃圖標(biāo)
Event ID比較重要,代碼通過這一變量來讀取文件
球的腳本
有打點(diǎn)就復(fù)位(快歌打點(diǎn)密,不復(fù)位飛到天上去),然后加速度,
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; //打的點(diǎn)密,不歸零來不及
Vector3 v = rgb.velocity;
v.y = jumpSpeed;
rgb.velocity = v;
}
}
控制節(jié)點(diǎn)
三個(gè)組件,音源、藍(lán)圖標(biāo),紅圖標(biāo)。
紅圖標(biāo)、上面創(chuàng)建的紫圖標(biāo)拖到青圖標(biāo)上
效果
這個(gè)面板就是上面的“進(jìn)去打點(diǎn)”彈出的
Main,Piano(自己命名的,腳本讀的就是EventID)填上去
點(diǎn)擊播放,快捷鍵“E”就進(jìn)行打點(diǎn)(點(diǎn)就是兩條軌道中間的“紅長(zhǎng)矩形”,點(diǎn)擊選中變綠)
打完,Ctrl+S,退出就運(yùn)行
tips:
點(diǎn)擊激活軌道后,Ctrl+A,可以顯示點(diǎn)的命名
Koreographer的理論內(nèi)容---------------------------------------------
一種音頻格式ogg
mp3轉(zhuǎn)mid
需要是伴奏,有人聲打點(diǎn)很亂。
只有伴奏也不好用,打的太密了。
所以請(qǐng)手打
網(wǎng)上推薦的mp3轉(zhuǎn)mid
兩個(gè)插件中的類型文件
Koreography(紫圖標(biāo))
Koreography Tracker(黃圖標(biāo))
一個(gè)編輯面板
關(guān)系
Koreography(紫圖標(biāo))的編輯面板
編輯后的輸出就是Koreography Tracker(黃圖標(biāo))
Zoom或者Ctrl+上下,可以縮放軌道
打點(diǎn)(需要單擊激活軌道UI)
快捷鍵E(常用,接近Pr)
Select下雙擊(覺得費(fèi)勁)
Draw下單擊
Clone還沒用過
OneOff打的點(diǎn)都是短的,一樣長(zhǎng)的
Span打的點(diǎn),按鍵時(shí)間長(zhǎng)或連著,會(huì)有的變長(zhǎng),長(zhǎng)短不一
以上都注意Snap to Beat Divide beat by (1)。
打鉤就是打的點(diǎn)會(huì)對(duì)齊軌道中的豎線。
不打就自由地多不用對(duì)齊。
我是對(duì)應(yīng)人聲(唱一個(gè)字打一下)打點(diǎn),比較密集,所以選后者
有打的點(diǎn)的詳情需要滑輪滾一下
CtrL+A顯示點(diǎn)的名字
一個(gè)總節(jié)點(diǎn)
就是上面說的控制節(jié)點(diǎn)
三個(gè)組件,音源、青圖標(biāo),紅圖標(biāo)。
紅圖標(biāo)和上面創(chuàng)建的紫圖標(biāo),都拖到青圖標(biāo)上
Demo Unity2D 音游案例-節(jié)奏大師(基于Koreographer)----------------------------------
進(jìn)入游戲
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 元素 隱藏不影響
特效
場(chǎng)景中的3個(gè)特效是示例,隱藏不影響
鍵盤按鍵
鍵盤的2組TargetTop(音符生成的位置)和TargetDown(音符結(jié)束的位置)隱藏不影響
腳本 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個(gè))上音符的圖像,和透明、藍(lán)色、綠色三種音符(藍(lán)色到透明=長(zhǎng)按的開始和結(jié)束,綠色=短按)</summary>
public Sprite[] noteSprites;
KoreographyEvent trackedEvent;
/// <summary>長(zhǎng)按開始</summary>
public bool isLongNote;
/// <summary>長(zhǎng)按結(jié)束</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對(duì)象重置
private void ResetNote()
{
}
/// <summary>返回對(duì)象池</summary>
void ReturnToPool()
{
gameController.ReturnNoteObjectToPool(this);
ResetNote();
}
/// <summary>擊中音符對(duì)象</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>當(dāng)前音符是否已經(jīng)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>音符的命中等級(jí)</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
}
音符的生成
六個(gè)軌道,
0-5,綠色音符
6-11,長(zhǎng)按的開始,藍(lán)色音符
12-18,長(zhǎng)按的結(jié)束,白色音符
音符 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
//檢測(cè)是否生成下一個(gè)新音符
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++;
}
}
音符圖片的索引對(duì)應(yīng)著文件的卡點(diǎn)
以下代碼將“紫圖標(biāo)”中的所有點(diǎn)裝入rawEvents。
0-5,6-11,12-17皮隊(duì)ID,相應(yīng)Add入六個(gè)軌道 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 基礎(chǔ)Koreography事件定義。 每個(gè)事件實(shí)例都可以攜帶一個(gè)
//有效載荷 事件可以跨越一系列樣本,也可以綁定到一個(gè)樣本。 樣品
//值(開始/結(jié)束)在“采樣時(shí)間”范圍內(nèi),* NOT *絕對(duì)采樣位置。
//確保查詢/比較在TIME而不是DATA空間中發(fā)生。
KoreographyEvent evt = rawEvents[i];
int noteID = evt.GetIntValue();//獲取每個(gè)事件對(duì)應(yīng)的字符串
// 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))
{
//事件對(duì)應(yīng)的字符串與某個(gè)音軌對(duì)應(yīng)字符串匹配,則把該事件添加到該音軌
// Add the object for input tracking.
lane.AddEventToLane(evt);
// Break out of the lane searching loop.
break;
}
}
}
//SampleRate采樣率,在音頻資源里有。
//命中窗口寬度,采樣率*0.001*命中時(shí)長(zhǎng)
hitWindowRangeInSamples = (int)(0.001f * hitWindowRangeInMS * SampleRate);
}
按鍵(LaneController)
腳本是掛在 Taget[1-6] 上的LaneController
按SDF JKL六個(gè)鍵
不同評(píng)價(jià)圖片的判定邏輯
判定
特效
01 ClickDownSprite激活(鍵盤變藍(lán))
02 特效HitLongEffectGo實(shí)例(下圖大的一直轉(zhuǎn)圈圈的特效)
03 特效DownEffectGo實(shí)例(下圖的綠點(diǎn),音符沒來前按鍵最明顯能看到小綠點(diǎn))
按下后按空,看到 01,02,03 都執(zhí)行了
按下后按中,看到 01,02 都執(zhí)行了
這里有DestrotEffect的腳本佐證hitEffectObjectPool對(duì)應(yīng)02,downEffectObjectPool對(duì)應(yīng)03。所以下圖第二個(gè)特效還沒見其用到
void ReturnToPool()
{
if (isHitted)
{
gameController.ReturnEffectGoToPool(gameObject,gameController.hitEffectObjectPool);
gameObject.SetActive(false);
}
else
{
gameController.ReturnEffectGoToPool(gameObject, gameController.downEffectObjectPool);
gameObject.SetActive(false);
}
}
音樂的播放
誰(shuí)控制
SimpleMusicPlayer會(huì)加載AudioSource,調(diào)用SimpleMusicPlayer的方法就控制
開局以第一個(gè)音符經(jīng)過按鍵才開始播放Bgm
SimpleMusicPlayer的AutoPlayAwake打鉤的話,AudioSource的PalyAwake不管打不打,都會(huì)播放。
但是這個(gè)Demo是第一個(gè)音符經(jīng)過按鍵才開始播放Bgm。
。。。。。
詳解Unity中Time類的用法與深入探究
。。。。。
按腳本是計(jì)時(shí)8s播放Bgm。Bgm是一上來就有音符產(chǎn)生
。。。。。
代碼來自于 RhythmGameController : MonoBehaviour
[Tooltip("開始播放音頻之前提供的時(shí)間量(以秒為單位),也就是提前調(diào)用時(shí)間。相當(dāng)于time計(jì)時(shí)不跑的變量")]//其他
public float leadInTime;
float leadInTimeLeft;//音頻播放之前的剩余時(shí)間量,(盲猜是區(qū)別開局與暫停播放,Bgm)
/// <summary>音樂開始之前的倒計(jì)時(shí)器。相當(dāng)于timer計(jì)時(shí)在跑的變量</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)//倒數(shù)音樂開始
{
timeLeftToPlay -= Time.unscaledDeltaTime;
if (timeLeftToPlay<=0)
{
audioCom.Play();
gameStart = true;
timeLeftToPlay = 0;
}
}
if (leadInTimeLeft>0)//倒數(shù)我們的引導(dǎo)時(shí)間
{
leadInTimeLeft = Mathf.Max(leadInTimeLeft - Time.unscaledDeltaTime, 0);
}
......
游戲中的暫停繼續(xù)會(huì)影響B(tài)gm
RhythmGameController中一下。simpleMusicPlayer是音游插件的腳本
//游戲的開始與暫停
public void PauseMusic()
{
if (!gameStart)
{
return;
}
simpleMusicPlayer.Pause();
}
public void PlayMusic()
{
if (!gameStart)
{
return;
}
simpleMusicPlayer.Play();
}
每次按鍵的評(píng)價(jià)圖標(biāo)
如圖看到,Great,Perfectdt都是擊中,只是音符案件的偏移量。所以連續(xù)擊中下面都有分?jǐn)?shù)遞增顯示
#region 戰(zhàn)斗中的三個(gè)
//顯示命中等級(jí)對(duì)應(yīng)的圖片
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次開始顯示軌道上的分?jǐn)?shù)
{
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()//軌道上的分?jǐn)?shù)。也就是在Great Perfect Miss下的分?jǐn)?shù)
{
comboText.gameObject.SetActive(false);
}
#endregion
扣血與分?jǐn)?shù)
可以看到口5次分,就結(jié)束了文章來源:http://www.zghlxwxcb.cn/news/detail-545733.html
#region UI上的3個(gè)
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
通關(guān) 失敗判定
結(jié)束界面
文章來源地址http://www.zghlxwxcb.cn/news/detail-545733.html
#region 結(jié)束后的兩個(gè)
public void Replay()
{
SceneManager.LoadScene(1);
}
public void ReturnToMain()
{
SceneManager.LoadScene(0);
}
#endregion
到了這里,關(guān)于“音游制作實(shí)用插件-Koreographer入門教程”,“Unity2D 音游案例-節(jié)奏大師(基于Koreographer)”的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!