前回は、キーを押したときにタイミングが最も近いノーツを調べることで、判定対象となるノーツを得られるようにしました。
今回は、そのノーツについてベストタイミングとの差を求め、PerfectやMissなどの判定評価を出す機能を作っていきます。
判定評価について #
リズムゲームでは、ノーツを叩いたタイミングとそのノーツのベストタイミングを比較して、PerfectやGood、Missなどの判定評価を出します。
処理の内容としては、前回書いたFindNearestNoteメソッドの処理と同じく、ゲーム開始からの時刻とノーツのベストタイミングの差を求めるといったものになります。
float elapsedTime = Time.time - notesMover.startTime;
float diffTime = Mathf.Abs(elapsedTime - noteTime);そして、求めたdiffTimeを場合分けして評価すれば良いです。
判定評価を求めるメソッドを作る #
JudgementManagerクラスに判定評価を求める処理を追加します。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class JudgementManager : MonoBehaviour
{
// ...(省略)
NoteInfo FindNearestNote(int lane)
{
// リズムゲーム開始からの経過時間を求める
float elapsedTime = Time.time - notesMover.startTime;
NoteInfo nearestNote = null;
float minDiff = float.MaxValue;
// 1. リストに入っているすべてのノーツに対して処理
foreach (NoteInfo noteInfo in noteInfoList)
{
// 2. レーンが一致していなければ無視
if (noteInfo.lane != lane)
{
continue;
}
// 3. 時間のズレを計算
float currentDiffTime = Mathf.Abs(elapsedTime - noteInfo.time);
// 4. 今までの最小のズレより小さければ更新
if (currentDiffTime < minDiff)
{
minDiff = currentDiffTime;
nearestNote = noteInfo;
}
}
return nearestNote;
}
string GetNoteEvaluation(float noteTime)
{
// 時間のズレを計算
float elapsedTime = Time.time - notesMover.startTime;
float diffTime = Mathf.Abs(elapsedTime - noteTime);
// 判定評価を返す
if (diffTime < 0.05f)
{
return "Perfect";
}
else if (diffTime < 0.1f)
{
return "Good";
}
else if (diffTime < 0.15f)
{
return "Miss";
}
else
{
return "NonTarget";
}
}
}ノーツのベストタイミングと今の時刻の差を計算し、それをif文で場合分けすることで判定評価ができるようになりました。
今回は、判定幅は以下のようにしました。(1秒 = 1000msです)
| 判定評価 | 判定幅 | (秒換算) |
|---|---|---|
| Perfect | 50ms | 0.05秒 |
| Good | 100ms | 0.1秒 |
| Miss | 150ms | 0.15秒 |
150msより離れていた場合は、判定評価の対象外としています。これは、ノーツが近くにないときに適当にキーを押した場合に、関係ない遠くのノーツまで判定に巻き込まれてしまうのを防ぐためです。
判定評価の動作確認 #
前回と同様に、Judgeメソッドを書き換えて判定評価をコンソールに表示して動作を確認してみましょう。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class JudgementManager : MonoBehaviour
{
// ...(省略)
void Judge(int lane)
{
// 最も近いノーツを探す
NoteInfo nearestNote = FindNearestNote(lane);
// もしノーツが見つからなかったら何もしない
if (nearestNote == null)
{
return;
}
// ノーツのベストタイミングを渡して、判定評価を取得する
string evaluation = GetNoteEvaluation(nearestNote.time);
// 判定対象外(NonTarget)でなければ、判定結果をコンソールに表示する
if (evaluation != "NonTarget")
{
Debug.Log($"レーン{lane}: {evaluation}!");
}
}
// ...(省略)
}解説 #
前回はnearestNote.timeをそのまま表示するだけでしたが、今回はGetNoteEvaluation(nearestNote.time)とすることで、ノーツのベストタイミングを渡し、その結果(“Perfect"などの文字列)をevaluationという変数が受け取っています。
そして、GetNoteEvaluationは、ノーツと叩いた時間が150ms以上離れていた場合に対象外として"NonTarget"を返すように記述しました。
そのため、if (evaluation != "NonTarget")として、結果が"NonTarget"ではない(=150ms以内で叩けた)ときだけ画面に判定を表示するようにしています。
動作確認をしてみよう #
ここまで書けたら、Unityエディタに戻ってゲームを再生してみましょう。
ノーツが落ちてくるタイミングに合わせて対応するキー(D, F, J, K)を押してみてください。Consoleウィンドウに「レーン1: Perfect!」や「レーン2: Miss!」のように、タイミングに応じた判定結果が表示されれば成功です!
現在は判定したノーツを消す処理を入れていないため、タイミング良くキーを連打すると1つのノーツに対して何度も判定されてしまいます。この問題は次回の「判定したノーツを消す」処理を入れることで解決しますので、今は複数回判定されてしまっても問題ありません!
まとめ #
今回はノーツのタイミングと叩いたタイミングの差を使ってノーツの判定評価をして、コンソール上に表示することができました。
内部の処理はほとんどできてきましたね。まだ実装していない処理は、スルーしたノーツをMissにする処理と、判定されたノーツを消す処理です。
次回はこれらの実装をしていきます!