谱面替换
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ichni.RhythmGame;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
|
||||
namespace Ichni
|
||||
{
|
||||
@@ -18,224 +18,99 @@ namespace Ichni
|
||||
public List<InputUnitTouch> inputUnitTouchList;
|
||||
public List<InputUnitSwipe> inputUnitSwipeList;
|
||||
|
||||
private List<Tap> _availableTapsBuffer = new List<Tap>(30);
|
||||
private readonly List<NoteBase> _startCandidatesBuffer = new List<NoteBase>(40);
|
||||
private readonly NoteCandidateComparer _candidateComparer = new NoteCandidateComparer();
|
||||
private List<Hold> _availableHoldsBuffer = new List<Hold>(10);
|
||||
private List<Flick> _availableFlicksBuffer = new List<Flick>(10);
|
||||
private List<Stay> _availableStaysBuffer = new List<Stay>(30);
|
||||
private readonly Stack<InputUnitTap> _tapInputPool = new Stack<InputUnitTap>(10);
|
||||
private readonly Stack<InputUnitTouch> _touchInputPool = new Stack<InputUnitTouch>(10);
|
||||
private readonly Stack<InputUnitSwipe> _swipeInputPool = new Stack<InputUnitSwipe>(10);
|
||||
private readonly InputUnitSwipe _assumedSwipeInput = new InputUnitSwipe();
|
||||
|
||||
private List<Tap> _minTapsBuffer = new List<Tap>(5);
|
||||
private List<Hold> _minHoldsBuffer = new List<Hold>(5);
|
||||
private List<Flick> _minFlicksBuffer = new List<Flick>(5);
|
||||
|
||||
private InputUnitSwipe _cachedAssumedSwipe = new InputUnitSwipe(0, Vector2.zero, true, true, Vector2.zero);
|
||||
|
||||
private void Start()
|
||||
{
|
||||
checkingTapList = new List<Tap>();
|
||||
checkingStayList = new List<Stay>();
|
||||
checkingHoldList = new List<Hold>();
|
||||
checkingFlickList = new List<Flick>();
|
||||
inputUnitTapList = new List<InputUnitTap>();
|
||||
inputUnitTouchList = new List<InputUnitTouch>();
|
||||
inputUnitSwipeList = new List<InputUnitSwipe>();
|
||||
inputUnitTapList = new List<InputUnitTap>(10);
|
||||
inputUnitTouchList = new List<InputUnitTouch>(10);
|
||||
inputUnitSwipeList = new List<InputUnitSwipe>(10);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public void ProcessPendingInputs(float songTime)
|
||||
{
|
||||
if (!GameManager.Instance.songPlayer.isPlaying)
|
||||
{
|
||||
ClearInputUnits();
|
||||
return;
|
||||
}
|
||||
|
||||
float songTime = CoreServices.TimeProvider.SongTime;
|
||||
|
||||
|
||||
double inputTime = InputState.currentTime;
|
||||
|
||||
// GameInputManager 在 Update 中只入队;这里位于 LateUpdate 的 Note 空间准备之后,
|
||||
// 使用同一帧已经更新好的判定区域消费 Swipe。
|
||||
foreach (var swipe in inputUnitSwipeList)
|
||||
{
|
||||
_availableFlicksBuffer.Clear();
|
||||
InputJudgeContext inputContext = CreateInputJudgeContext(swipe, songTime, inputTime);
|
||||
_startCandidatesBuffer.Clear();
|
||||
|
||||
foreach (Flick flick in checkingFlickList)
|
||||
{
|
||||
if (flick.CheckJudgeAvailability(swipe))
|
||||
if (flick.CheckJudgeAvailability(inputContext))
|
||||
{
|
||||
_availableFlicksBuffer.Add(flick);
|
||||
}
|
||||
}
|
||||
|
||||
if (_availableFlicksBuffer.Count > 0)
|
||||
{
|
||||
if (swipe.isFirst) // 原 tapSwipeList 逻辑
|
||||
{
|
||||
GetAllMinNotes(_availableFlicksBuffer, _minFlicksBuffer); // 提醒:内部也需防 new List
|
||||
if (_minFlicksBuffer.Count == 1) _minFlicksBuffer[0].ExecuteStartJudge(songTime);
|
||||
else if (_minFlicksBuffer.Count > 1) GetNearestNote(_minFlicksBuffer, swipe.inputPosition).ExecuteTapJudge(songTime);
|
||||
}
|
||||
else // 原 holdSwipeList 逻辑
|
||||
{
|
||||
_availableFlicksBuffer.Sort();
|
||||
_availableFlicksBuffer[0].ExecuteStartJudge(songTime);
|
||||
_startCandidatesBuffer.Add(flick);
|
||||
}
|
||||
}
|
||||
|
||||
ExecuteSwipeCandidates(swipe, inputContext);
|
||||
}
|
||||
|
||||
/*foreach (InputUnitSwipe tapSwipe in tapSwipeList)
|
||||
{
|
||||
List<Flick> availableFlicks = new List<Flick>();
|
||||
|
||||
foreach (Flick flick in checkingFlickList)
|
||||
{
|
||||
if (flick.CheckJudgeAvailability(tapSwipe))
|
||||
{
|
||||
availableFlicks.Add(flick);
|
||||
}
|
||||
}
|
||||
|
||||
List<Flick> minFlicks = GetAllMinNotes(availableFlicks);
|
||||
|
||||
if (minFlicks.Count == 1)
|
||||
{
|
||||
minFlicks[0].ExecuteStartJudge();
|
||||
}
|
||||
else if (minFlicks.Count > 1)
|
||||
{
|
||||
GetNearestNote(minFlicks, tapSwipe.inputPosition).ExecuteTapJudge();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (InputUnitSwipe inputUnitSwipe in holdSwipeList)
|
||||
{
|
||||
List<Flick> availableFlicks = new List<Flick>();
|
||||
|
||||
foreach (Flick flick in checkingFlickList)
|
||||
{
|
||||
if (flick.CheckJudgeAvailability(inputUnitSwipe))
|
||||
{
|
||||
availableFlicks.Add(flick);
|
||||
}
|
||||
}
|
||||
|
||||
if (availableFlicks.Count > 0)
|
||||
{
|
||||
availableFlicks.Sort();
|
||||
Flick closestFlick = availableFlicks[0];
|
||||
closestFlick.ExecuteStartJudge();
|
||||
}
|
||||
}*/
|
||||
|
||||
foreach (InputUnitTap inputUnitTap in inputUnitTapList)
|
||||
{
|
||||
_availableTapsBuffer.Clear();
|
||||
_availableHoldsBuffer.Clear();
|
||||
InputJudgeContext inputContext = CreateInputJudgeContext(inputUnitTap, songTime, inputTime);
|
||||
_startCandidatesBuffer.Clear();
|
||||
|
||||
foreach (Tap tap in checkingTapList)
|
||||
{
|
||||
if (tap.CheckJudgeAvailability(inputUnitTap)) _availableTapsBuffer.Add(tap);
|
||||
if (tap.CheckJudgeAvailability(inputContext)) _startCandidatesBuffer.Add(tap);
|
||||
}
|
||||
foreach (Hold hold in checkingHoldList)
|
||||
{
|
||||
if (hold.CheckJudgeAvailability(inputUnitTap)) _availableHoldsBuffer.Add(hold);
|
||||
if (hold.CheckJudgeAvailability(inputContext)) _startCandidatesBuffer.Add(hold);
|
||||
}
|
||||
|
||||
bool haveTap = _availableTapsBuffer.Count > 0;
|
||||
bool haveHold = _availableHoldsBuffer.Count > 0;
|
||||
NoteBase selectedCandidate = SelectStartCandidate(_startCandidatesBuffer, inputContext,
|
||||
fallbackToFirst: true);
|
||||
if (selectedCandidate == null) continue;
|
||||
|
||||
Flick closestFlick = null;
|
||||
InputUnitSwipe assumedSwipe = new InputUnitSwipe(inputUnitTap.fingerId, inputUnitTap.inputPosition, true, true, Vector2.zero);
|
||||
_assumedSwipeInput.Set(inputUnitTap.fingerId, inputUnitTap.inputPosition,
|
||||
true, true, Vector2.zero, inputUnitTap.inputEventTime);
|
||||
InputJudgeContext assumedSwipeContext = inputContext.WithInputUnit(_assumedSwipeInput);
|
||||
_startCandidatesBuffer.Clear();
|
||||
foreach (Flick flick in checkingFlickList)
|
||||
{
|
||||
if (flick.CheckJudgeAvailability(assumedSwipe))
|
||||
if (flick.CheckJudgeAvailability(assumedSwipeContext))
|
||||
{
|
||||
if (closestFlick == null || flick.exactJudgeTime < closestFlick.exactJudgeTime)
|
||||
{
|
||||
closestFlick = flick;
|
||||
}
|
||||
_startCandidatesBuffer.Add(flick);
|
||||
}
|
||||
}
|
||||
|
||||
if (haveHold && haveTap)
|
||||
{
|
||||
GetAllMinNotes(_availableHoldsBuffer, _minHoldsBuffer);
|
||||
GetAllMinNotes(_availableTapsBuffer, _minTapsBuffer);
|
||||
Hold closestHold = _minHoldsBuffer[0];
|
||||
Tap closestTap = _minTapsBuffer[0];
|
||||
bool holdBlockedByFlick = false;
|
||||
bool tapBlockedByFlick = false;
|
||||
Flick blockingFlick = SelectStartCandidate(_startCandidatesBuffer, assumedSwipeContext,
|
||||
fallbackToFirst: false) as Flick;
|
||||
if (blockingFlick != null &&
|
||||
blockingFlick.exactJudgeTime < selectedCandidate.exactJudgeTime - 0.05f) continue;
|
||||
|
||||
if (closestFlick != null)
|
||||
{
|
||||
if (closestFlick.exactJudgeTime < closestHold.exactJudgeTime - 0.05f)
|
||||
{
|
||||
holdBlockedByFlick = true;
|
||||
}
|
||||
|
||||
if (closestFlick.exactJudgeTime < closestTap.exactJudgeTime - 0.05f)
|
||||
{
|
||||
tapBlockedByFlick = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!holdBlockedByFlick && closestHold.exactJudgeTime < closestTap.exactJudgeTime - 0.05f)
|
||||
{
|
||||
if (_minHoldsBuffer.Count == 1)
|
||||
{
|
||||
_minHoldsBuffer[0].ExecuteStartJudge(songTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNearestNote(_minHoldsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime);
|
||||
}
|
||||
}
|
||||
else if (!tapBlockedByFlick)
|
||||
{
|
||||
if (_minTapsBuffer.Count == 1)
|
||||
{
|
||||
_minTapsBuffer[0].ExecuteStartJudge(songTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNearestNote(_minTapsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (haveHold)
|
||||
{
|
||||
GetAllMinNotes(_availableHoldsBuffer, _minHoldsBuffer);
|
||||
bool holdBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minHoldsBuffer[0].exactJudgeTime - 0.05f;
|
||||
if (!holdBlockedByFlick)
|
||||
{
|
||||
if (_minHoldsBuffer.Count == 1)
|
||||
{
|
||||
_minHoldsBuffer[0].ExecuteStartJudge(songTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNearestNote(_minHoldsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (haveTap)
|
||||
{
|
||||
GetAllMinNotes(_availableTapsBuffer, _minTapsBuffer);
|
||||
bool tapBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minTapsBuffer[0].exactJudgeTime - 0.05f;
|
||||
if (!tapBlockedByFlick)
|
||||
{
|
||||
if (_minTapsBuffer.Count == 1)
|
||||
{
|
||||
_minTapsBuffer[0].ExecuteStartJudge(songTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNearestNote(_minTapsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
selectedCandidate.ExecuteStartJudge(inputContext.InputSongTime);
|
||||
}
|
||||
|
||||
foreach (InputUnitTouch inputUnitTouch in inputUnitTouchList)
|
||||
{
|
||||
InputJudgeContext inputContext = CreateInputJudgeContext(inputUnitTouch, songTime, inputTime);
|
||||
_availableStaysBuffer.Clear();
|
||||
foreach (Stay stay in checkingStayList)
|
||||
{
|
||||
if (stay.CheckJudgeAvailability(inputUnitTouch))
|
||||
if (stay.CheckJudgeAvailability(inputContext))
|
||||
{
|
||||
_availableStaysBuffer.Add(stay);
|
||||
}
|
||||
@@ -244,7 +119,7 @@ namespace Ichni
|
||||
_availableHoldsBuffer.Clear();
|
||||
foreach (Hold hold in checkingHoldList)
|
||||
{
|
||||
if (hold.CheckJudgeAvailability(inputUnitTouch))
|
||||
if (hold.CheckJudgeAvailability(inputContext))
|
||||
{
|
||||
_availableHoldsBuffer.Add(hold);
|
||||
}
|
||||
@@ -252,7 +127,7 @@ namespace Ichni
|
||||
|
||||
foreach (Stay stay in _availableStaysBuffer)
|
||||
{
|
||||
stay.ExecuteStartJudge(songTime);
|
||||
stay.ExecuteStartJudge(inputContext.InputSongTime);
|
||||
}
|
||||
|
||||
foreach (Hold hold in _availableHoldsBuffer)
|
||||
@@ -261,100 +136,290 @@ namespace Ichni
|
||||
}
|
||||
}
|
||||
|
||||
inputUnitTapList.Clear();
|
||||
inputUnitTouchList.Clear();
|
||||
inputUnitSwipeList.Clear();
|
||||
ClearInputUnits();
|
||||
}
|
||||
|
||||
|
||||
public void DiscardPendingInputs()
|
||||
{
|
||||
ClearInputUnits();
|
||||
}
|
||||
|
||||
private void ClearInputUnits()
|
||||
{
|
||||
if (inputUnitTapList != null)
|
||||
{
|
||||
for (int i = 0; i < inputUnitTapList.Count; i++) _tapInputPool.Push(inputUnitTapList[i]);
|
||||
inputUnitTapList.Clear();
|
||||
}
|
||||
|
||||
if (inputUnitTouchList != null)
|
||||
{
|
||||
for (int i = 0; i < inputUnitTouchList.Count; i++) _touchInputPool.Push(inputUnitTouchList[i]);
|
||||
inputUnitTouchList.Clear();
|
||||
}
|
||||
|
||||
if (inputUnitSwipeList != null)
|
||||
{
|
||||
for (int i = 0; i < inputUnitSwipeList.Count; i++) _swipeInputPool.Push(inputUnitSwipeList[i]);
|
||||
inputUnitSwipeList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static float ResolveInputSongTime(InputUnit inputUnit, float currentSongTime, double currentInputTime)
|
||||
{
|
||||
// Input System 时间戳与 currentTime 使用同一时钟。用队列年龄回推 SongTime,
|
||||
// 避免 LateUpdate 消费时把输入时刻错误替换为更晚的当前歌曲时间。
|
||||
double inputAge = currentInputTime - inputUnit.inputEventTime;
|
||||
if (double.IsNaN(inputAge) || double.IsInfinity(inputAge) || inputAge < 0d)
|
||||
{
|
||||
inputAge = 0d;
|
||||
}
|
||||
|
||||
return currentSongTime - (float)inputAge;
|
||||
}
|
||||
|
||||
private static InputJudgeContext CreateInputJudgeContext(InputUnit inputUnit, float currentSongTime,
|
||||
double currentInputTime)
|
||||
{
|
||||
return new InputJudgeContext(inputUnit,
|
||||
ResolveInputSongTime(inputUnit, currentSongTime, currentInputTime));
|
||||
}
|
||||
|
||||
public void SetNewInputUnitTap(int fingerId, Vector2 inputPosition)
|
||||
{
|
||||
InputUnitTap inputUnitTap = new InputUnitTap(fingerId, inputPosition);
|
||||
if(!inputUnitTapList.Exists(x => x.fingerId == fingerId))
|
||||
{
|
||||
inputUnitTapList.Add(inputUnitTap);
|
||||
}
|
||||
SetNewInputUnitTap(fingerId, inputPosition, InputState.currentTime);
|
||||
}
|
||||
|
||||
|
||||
public void SetNewInputUnitTap(int fingerId, Vector2 inputPosition, double inputEventTime)
|
||||
{
|
||||
if (ContainsFinger(inputUnitTapList, fingerId)) return;
|
||||
|
||||
InputUnitTap inputUnitTap = _tapInputPool.Count > 0 ? _tapInputPool.Pop() : new InputUnitTap();
|
||||
inputUnitTap.Set(fingerId, inputPosition, inputEventTime);
|
||||
inputUnitTapList.Add(inputUnitTap);
|
||||
}
|
||||
|
||||
public void SetNewInputUnitTouch(int fingerId, Vector2 inputPosition)
|
||||
{
|
||||
InputUnitTouch inputUnitTouch = new InputUnitTouch(fingerId, inputPosition);
|
||||
if(!inputUnitTouchList.Exists(x => x.fingerId == fingerId))
|
||||
{
|
||||
inputUnitTouchList.Add(inputUnitTouch);
|
||||
}
|
||||
SetNewInputUnitTouch(fingerId, inputPosition, InputState.currentTime);
|
||||
}
|
||||
|
||||
public void SetNewInputUnitTouch(int fingerId, Vector2 inputPosition, double inputEventTime)
|
||||
{
|
||||
if (ContainsFinger(inputUnitTouchList, fingerId)) return;
|
||||
|
||||
InputUnitTouch inputUnitTouch = _touchInputPool.Count > 0 ? _touchInputPool.Pop() : new InputUnitTouch();
|
||||
inputUnitTouch.Set(fingerId, inputPosition, inputEventTime);
|
||||
inputUnitTouchList.Add(inputUnitTouch);
|
||||
}
|
||||
|
||||
public void SetNewInputUnitSwipe(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 delta)
|
||||
{
|
||||
InputUnitSwipe inputUnitSwipe = new InputUnitSwipe(fingerId, inputPosition, isGeneric, isFirst, delta);
|
||||
if(!inputUnitSwipeList.Exists(x => x.fingerId == fingerId))
|
||||
SetNewInputUnitSwipe(fingerId, inputPosition, isGeneric, isFirst, delta, InputState.currentTime);
|
||||
}
|
||||
|
||||
public void SetNewInputUnitSwipe(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 delta, double inputEventTime)
|
||||
{
|
||||
// 每根手指每帧只保留一个 Swipe 样本;下一帧队列清空后,同一条长划动仍可继续触发 Flick。
|
||||
if (ContainsFinger(inputUnitSwipeList, fingerId)) return;
|
||||
|
||||
InputUnitSwipe inputUnitSwipe = _swipeInputPool.Count > 0 ? _swipeInputPool.Pop() : new InputUnitSwipe();
|
||||
inputUnitSwipe.Set(fingerId, inputPosition, isGeneric, isFirst, delta, inputEventTime);
|
||||
inputUnitSwipeList.Add(inputUnitSwipe);
|
||||
}
|
||||
|
||||
private static bool ContainsFinger<TInput>(List<TInput> inputs, int fingerId) where TInput : InputUnit
|
||||
{
|
||||
for (int i = 0; i < inputs.Count; i++)
|
||||
{
|
||||
inputUnitSwipeList.Add(inputUnitSwipe);
|
||||
if (inputs[i].fingerId == fingerId) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class NoteJudgeManager
|
||||
{
|
||||
private void GetAllMinNotes<T>(List<T> availableFlicks, List<T> destination) where T : NoteBase
|
||||
private void ExecuteSwipeCandidates(InputUnitSwipe swipe, InputJudgeContext context)
|
||||
{
|
||||
destination.Clear();
|
||||
float minTime = float.MaxValue;
|
||||
foreach (T note in availableFlicks)
|
||||
// 单个 Swipe 样本只选择一个候选;长划动会在后续帧继续产生样本,
|
||||
// 因而可以依次触发后续 Flick,而不会让同一输入广播命中全部重叠 Note。
|
||||
Flick selectedFlick = SelectStartCandidate(_startCandidatesBuffer, context,
|
||||
fallbackToFirst: true) as Flick;
|
||||
if (selectedFlick == null) return;
|
||||
|
||||
ExecuteSwipeJudge(selectedFlick, swipe, context.InputSongTime);
|
||||
}
|
||||
|
||||
private void ExecuteSwipeJudge(Flick flick, InputUnitSwipe swipe, float inputSongTime)
|
||||
{
|
||||
if (swipe.isFirst && CountCandidatesAtJudgeTime(flick.exactJudgeTime) > 1)
|
||||
{
|
||||
if (note.exactJudgeTime < minTime)
|
||||
{
|
||||
minTime = note.exactJudgeTime;
|
||||
destination.Clear();
|
||||
destination.Add(note);
|
||||
}
|
||||
else if (Mathf.Approximately(note.exactJudgeTime, minTime))
|
||||
{
|
||||
destination.Add(note);
|
||||
}
|
||||
flick.ExecuteTapJudge(inputSongTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
flick.ExecuteStartJudge(inputSongTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private T GetNearestNote<T>(List<T> notes, Vector2 inputPosition) where T : NoteBase
|
||||
|
||||
private NoteBase SelectStartCandidate(List<NoteBase> candidates, InputJudgeContext context,
|
||||
bool fallbackToFirst)
|
||||
{
|
||||
float minDistance = float.MaxValue;
|
||||
T closestNote = null;
|
||||
foreach (T note in notes)
|
||||
if (candidates.Count == 0) return null;
|
||||
|
||||
_candidateComparer.SetContext(context);
|
||||
candidates.Sort(_candidateComparer);
|
||||
|
||||
for (int i = 0; i < candidates.Count; i++)
|
||||
{
|
||||
float distance = Vector2.Distance(inputPosition, note.noteScreenPosition);
|
||||
if (distance < minDistance)
|
||||
// 这里只预览判定等级,不修改 Combo、音频、特效或 Note 生命周期。
|
||||
NoteBase.NoteJudgeType previewType = candidates[i].PreviewStartJudgeType(context.InputSongTime);
|
||||
if (previewType is NoteBase.NoteJudgeType.Perfect or NoteBase.NoteJudgeType.Good)
|
||||
{
|
||||
minDistance = distance;
|
||||
closestNote = note;
|
||||
return candidates[i];
|
||||
}
|
||||
}
|
||||
|
||||
return closestNote;
|
||||
return fallbackToFirst ? candidates[0] : null;
|
||||
}
|
||||
|
||||
private int CountCandidatesAtJudgeTime(float exactJudgeTime)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < _startCandidatesBuffer.Count; i++)
|
||||
{
|
||||
if (Mathf.Approximately(_startCandidatesBuffer[i].exactJudgeTime, exactJudgeTime))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private sealed class NoteCandidateComparer : IComparer<NoteBase>
|
||||
{
|
||||
private Vector2 _inputPosition;
|
||||
private float _inputSongTime;
|
||||
|
||||
public void SetContext(InputJudgeContext context)
|
||||
{
|
||||
_inputPosition = context.InputPosition;
|
||||
_inputSongTime = context.InputSongTime;
|
||||
}
|
||||
|
||||
public int Compare(NoteBase left, NoteBase right)
|
||||
{
|
||||
if (ReferenceEquals(left, right)) return 0;
|
||||
if (left == null) return 1;
|
||||
if (right == null) return -1;
|
||||
|
||||
int timeComparison = left.exactJudgeTime.CompareTo(right.exactJudgeTime);
|
||||
if (timeComparison != 0) return timeComparison;
|
||||
|
||||
float leftDistance = (GetCandidateScreenPosition(left, _inputSongTime) - _inputPosition).sqrMagnitude;
|
||||
float rightDistance = (GetCandidateScreenPosition(right, _inputSongTime) - _inputPosition).sqrMagnitude;
|
||||
int distanceComparison = leftDistance.CompareTo(rightDistance);
|
||||
if (distanceComparison != 0) return distanceComparison;
|
||||
|
||||
return left.elementGuid.CompareTo(right.elementGuid);
|
||||
}
|
||||
|
||||
private static Vector2 GetCandidateScreenPosition(NoteBase note, float inputSongTime)
|
||||
{
|
||||
if (note.NoteJudgeSubmodule?.judgeUnitList != null)
|
||||
{
|
||||
for (int i = 0; i < note.NoteJudgeSubmodule.judgeUnitList.Count; i++)
|
||||
{
|
||||
if (note.NoteJudgeSubmodule.judgeUnitList[i] is TouchAreaJudgeUnit touchArea)
|
||||
{
|
||||
return touchArea.GetJudgeCenter(inputSongTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return note.noteScreenPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct InputJudgeContext
|
||||
{
|
||||
// InputUnit 保存设备事件;InputSongTime 保存该事件映射到谱面时间轴后的发生时刻。
|
||||
// JudgeUnit 必须使用本上下文,不能在判定途中重新读取当前 SongTime。
|
||||
public InputUnit InputUnit { get; }
|
||||
public float InputSongTime { get; }
|
||||
|
||||
public int FingerId => InputUnit.fingerId;
|
||||
public Vector2 InputPosition => InputUnit.inputPosition;
|
||||
public double InputEventTime => InputUnit.inputEventTime;
|
||||
|
||||
public InputJudgeContext(InputUnit inputUnit, float inputSongTime)
|
||||
{
|
||||
InputUnit = inputUnit ?? throw new System.ArgumentNullException(nameof(inputUnit));
|
||||
InputSongTime = inputSongTime;
|
||||
}
|
||||
|
||||
public InputJudgeContext WithInputUnit(InputUnit inputUnit)
|
||||
{
|
||||
return new InputJudgeContext(inputUnit, InputSongTime);
|
||||
}
|
||||
}
|
||||
|
||||
public class InputUnit
|
||||
{
|
||||
public int fingerId;
|
||||
public Vector2 inputPosition;
|
||||
public double inputEventTime;
|
||||
}
|
||||
|
||||
public class InputUnitTap : InputUnit
|
||||
{
|
||||
public InputUnitTap()
|
||||
{
|
||||
}
|
||||
|
||||
public InputUnitTap(int fingerId, Vector2 inputPosition)
|
||||
: this(fingerId, inputPosition, InputState.currentTime)
|
||||
{
|
||||
}
|
||||
|
||||
public InputUnitTap(int fingerId, Vector2 inputPosition, double inputEventTime)
|
||||
{
|
||||
Set(fingerId, inputPosition, inputEventTime);
|
||||
}
|
||||
|
||||
public void Set(int fingerId, Vector2 inputPosition, double inputEventTime)
|
||||
{
|
||||
this.fingerId = fingerId;
|
||||
this.inputPosition = inputPosition;
|
||||
this.inputEventTime = inputEventTime;
|
||||
}
|
||||
}
|
||||
|
||||
public class InputUnitTouch : InputUnit
|
||||
{
|
||||
public InputUnitTouch()
|
||||
{
|
||||
}
|
||||
|
||||
public InputUnitTouch(int fingerId, Vector2 inputPosition)
|
||||
: this(fingerId, inputPosition, InputState.currentTime)
|
||||
{
|
||||
}
|
||||
|
||||
public InputUnitTouch(int fingerId, Vector2 inputPosition, double inputEventTime)
|
||||
{
|
||||
Set(fingerId, inputPosition, inputEventTime);
|
||||
}
|
||||
|
||||
public void Set(int fingerId, Vector2 inputPosition, double inputEventTime)
|
||||
{
|
||||
this.fingerId = fingerId;
|
||||
this.inputPosition = inputPosition;
|
||||
this.inputEventTime = inputEventTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,13 +428,30 @@ namespace Ichni
|
||||
public Vector2 swipeDirection;
|
||||
public bool isGeneric;
|
||||
public bool isFirst;
|
||||
public InputUnitSwipe()
|
||||
{
|
||||
}
|
||||
|
||||
public InputUnitSwipe(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 swipeDirection)
|
||||
: this(fingerId, inputPosition, isGeneric, isFirst, swipeDirection, InputState.currentTime)
|
||||
{
|
||||
}
|
||||
|
||||
public InputUnitSwipe(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 swipeDirection,
|
||||
double inputEventTime)
|
||||
{
|
||||
Set(fingerId, inputPosition, isGeneric, isFirst, swipeDirection, inputEventTime);
|
||||
}
|
||||
|
||||
public void Set(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 swipeDirection,
|
||||
double inputEventTime)
|
||||
{
|
||||
this.fingerId = fingerId;
|
||||
this.inputPosition = inputPosition;
|
||||
this.inputEventTime = inputEventTime;
|
||||
this.isGeneric = isGeneric;
|
||||
this.isFirst = isFirst;
|
||||
this.swipeDirection = swipeDirection.normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user