書名:《Unityで作るリズムゲーム》
作者:長崎大学マルチメディア研究会
計算與邏輯
對於遊戲分數和Combo的計算,不同音遊有不同的計分方法
最大得分
與note數量無關
取決於note數量
Combo對得分的影響
不影響得分
Combo數越高,每個note的得分越高
Long Note分數/Combo處理
起/終點各自撃中增加分數和Combo數
分數和Combo數僅在起點/終點增加
Long Note進入處理狀態後,Combo和分數會持續增加
在本案例中,會使用以下標準
最大得分取決於notes數量
Combo不影響得分
起點和終點被分別撃中時都有加分和Combo
評價管理器(EvaluationManager)
在該管理器中,主要需要處理分數的增加和Combo數的計算,為了進行這兩者的計算,首先要準備一些字段。
//分數上限
public static readonly float theoreticalScore = 1000000;
//評價得分倍率
static readonly Dictionary<JudgementType, float> scoreAddRates = new Dictionary<JudgementType, float>
{
{JudgementType.PERFECT, 1.0f },
{JudgementType.GOOD, 0.5f },
{JudgementType.BAD, 0.2f },
{JudgementType.MISS, 0.0f }
};
public static float score; //當局得分
public static int combo; //當局連撃數
public static int maxCombo; //譜面遊玩歷史最大連撃數
public static int theoreticalCombo; //該譜面的可能最大連撃數
//每種評價的數量
public static Dictionary<JudgementType, int> judgementCounts = new Dictionary<JudgementType, int>();
要知道每一個note佔多少分,首先要計算當前譜面有多少個note。
//值初始化
private void Start()
{
score = 0f;
combo = 0;
maxCombo = 0;
judgementCounts[JudgementType.PERFECT] = 0;
judgementCounts[JudgementType.GOOD] = 0;
judgementCounts[JudgementType.BAD] = 0;
judgementCounts[JudgementType.MISS] = 0;
//最大可能連撃數
theoreticalCombo =
PlayerController.beatMap.noteDatas
.Count(note => note.noteType == NoteType.Single) +
PlayerController.beatMap.noteDatas
.Count(note => note.noteType == NoteType.Long) * 2;
}
public static void OnHit(JudgementType judgementType)
{
combo++;
//每個note的分數
float addValue = theoreticalScore / theoreticalCombo;
//每個note的分數 * 得分倍率
score += addValue * scoreAddRates[judgementType];
judgementCounts[judgementType]++;
}
Combo的判定在Note的撃中和Miss時都要記錄(增加/斷連)
private void Update()
{
//持續更新最大Combo數
maxCombo = Mathf.Max(combo, maxCombo);
}
//斷連
public static void OnMiss()
{
combo = 0;
judgementCounts[JudgementType.MISS]++;
}
總體代碼
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class EvaluationManager : MonoBehaviour
{
//分數上限
public static readonly float theoreticalScore = 1000000;
//評價得分倍率
static readonly Dictionary<JudgementType, float> scoreAddRates = new Dictionary<JudgementType, float>
{
{JudgementType.PERFECT, 1.0f },
{JudgementType.GOOD, 0.5f },
{JudgementType.BAD, 0.2f },
{JudgementType.MISS, 0.0f }
};
public static float score; //當局得分
public static int combo; //當局連撃數
public static int maxCombo; //譜面遊玩歷史最大連撃數
public static int theoreticalCombo; //該譜面的可能最大連撃數
//每種評價的數量
public static Dictionary<JudgementType, int> judgementCounts = new Dictionary<JudgementType, int>
{
{ JudgementType.PERFECT, 0 },
{ JudgementType.GOOD, 0 },
{ JudgementType.BAD, 0 },
{ JudgementType.MISS, 0 },
};
//值初始化
private void Start()
{
score = 0f;
combo = 0;
maxCombo = 0;
judgementCounts[JudgementType.PERFECT] = 0;
judgementCounts[JudgementType.GOOD] = 0;
judgementCounts[JudgementType.BAD] = 0;
judgementCounts[JudgementType.MISS] = 0;
//最大可能連撃數
theoreticalCombo =
PlayerController.beatMap.noteDatas
.Count(note => note.noteType == NoteType.Single) +
PlayerController.beatMap.noteDatas
.Count(note => note.noteType == NoteType.Long) * 2;
}
private void Update()
{
//持續更新最大Combo數
maxCombo = Mathf.Max(combo, maxCombo);
}
public static void OnHit(JudgementType judgementType)
{
combo++;
//每個note的分數
float addValue = theoreticalScore / theoreticalCombo;
//每個note的分數 * 得分倍率
score += addValue * scoreAddRates[judgementType];
judgementCounts[judgementType]++;
}
//斷連
public static void OnMiss()
{
combo = 0;
judgementCounts[JudgementType.MISS]++;
}
}
在SingleNoteController和LongNoteController的判定後方法中調用評價管理器中對應的處理
SingleNoteController
void CheckMiss()
{
if (noteProperty.secBegin - PlayerController.currentSec
< -JudgementManager.judgementWidth[JudgementType.BAD])
{
//斷連
EvaluationManager.OnMiss();
//...
}
}
//只有非Miss的Note才會被被撃中並消除
public override void OnKeyDown(JudgementType judgementType)
{
if(judgementType != JudgementType.MISS)
{
//加分加Combo
EvaluationManager.OnHit(judgementType);
//...
}
}
LongNoteController
void CheckMiss()
{
//沒有進入處理狀態的起點通過判定線且超過了BAD判定範圍(斷頭押)
if(!isProcessed &&
noteProperty.secBegin - PlayerController.currentSec <
-JudgementManager.judgementWidth[JudgementType.BAD])
{
//起點Miss,終點也Miss
EvaluationManager.OnMiss();
EvaluationManager.OnMiss();
//...
}
//進入了處理狀態的終點通過了判定線且超過了BAD判定範圍(Hold太久斷尾押)
if(isProcessed &&
noteProperty.secEnd - PlayerController.currentSec <
-JudgementManager.judgementWidth[JudgementType.BAD])
{
EvaluationManager.OnMiss();
//...
}
}
public override void OnKeyDown(JudgementType judgementType)
{
//按下時,LongNote在BAD判定範圍內
if(judgementType != JudgementType.MISS)
{
EvaluationManager.OnHit(judgementType);
//...
}
}
//抬手
public override void OnKeyUp(JudgementType judgementType)
{
//尾判評價
if(judgementType != JudgementType.MISS)
{
EvaluationManager.OnHit(judgementType);
}
else
{
EvaluationManager.OnMiss();
}
//...
}