using System; using System.Collections.Generic; using Dreamteck.Splines; using Ichni.RhythmGame.Beatmap; using Lean.Pool; using Unity.VisualScripting; using UnityEngine; namespace Ichni.RhythmGame { public class Flick : NoteBase { #region [特有属性字段] Special Fields public NoteJudgeType preJudgeType; public List availableFlickDirections; public float flickBuffer = 0.5f; #endregion #region [生成与初始化] Generation & Initialization public static Flick GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, GameElement parentElement, float exactJudgeTime, List directions) { Flick flick = LeanPool.Spawn(GameManager.Instance.basePrefabs.flickNote, parentElement.transform).GetComponent(); flick.Initialize(elementName, id, tags, isFirstGenerated, parentElement); flick.exactJudgeTime = exactJudgeTime; flick.judgeIntervals = NoteJudgeIntervals.FlickDefault; // 注意这里暂未赋值传入的 directions, 请根据情况补充。 if (parentElement.TryGetComponent(out Track track) && track.trackTimeSubmodule != null) { flick.track = track; flick.trackPositioner.enabled = true; flick.trackPositioner.spline = track.trackPathSubmodule.path; flick.isOnTrack = true; flick.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); } else { flick.track = null; flick.isOnTrack = false; } return flick; } public override void SetDefaultSubmodules() { base.SetDefaultSubmodules(); NoteAudioSubmodule = new NoteAudioSubmodule(this, "DefaultStay"); } #endregion #region [主循环阶段] Main Update public override bool ManualUpdate(float currentSongTime) { if (!isFirstJudged && !isDuringJudging && currentSongTime >= exactJudgeTime + judgeIntervals.beforeMiss.intervalStart && !GameManager.Instance.noteJudgeManager.checkingFlickList.Contains(this)) { isDuringJudging = true; GameManager.Instance.noteJudgeManager.checkingFlickList.Add(this); } ExecuteFinalJudge(currentSongTime); return base.ManualUpdate(currentSongTime); } protected override void RemoveFromCheckingList() { if (GameManager.Instance.noteJudgeManager.checkingFlickList.Contains(this)) GameManager.Instance.noteJudgeManager.checkingFlickList.Remove(this); } #endregion #region [核心判定逻辑] Judgement Logic public bool CheckJudgeAvailability(InputUnitSwipe inputUnitSwipe) { foreach (var judgeUnit in NoteJudgeSubmodule.judgeUnitList) { if (!judgeUnit.CheckJudgeAvailability(inputUnitSwipe)) return false; } return true; } public bool CheckSwipeDirection(InputUnitSwipe inputUnitSwipe) { if (inputUnitSwipe.isGeneric) return true; Camera gameCamera = GameManager.Instance.cameraManager.gameCamera.cam; foreach (Vector2 localDir in availableFlickDirections) { Vector3 worldDirection = noteVisual.transform.TransformDirection(localDir.normalized); Vector3 noteOriginWorld = noteVisual.transform.position; Vector3 noteTargetWorld = noteOriginWorld + worldDirection; Vector3 screenOrigin = gameCamera.WorldToScreenPoint(noteOriginWorld); Vector3 screenTarget = gameCamera.WorldToScreenPoint(noteTargetWorld); Vector2 noteScreenDirection = new Vector2(screenTarget.x - screenOrigin.x, screenTarget.y - screenOrigin.y).normalized; if (noteScreenDirection.sqrMagnitude < 0.01f) continue; float dotProduct = Vector2.Dot(inputUnitSwipe.swipeDirection, noteScreenDirection); if (dotProduct >= flickBuffer) return true; } return false; } public override void ExecuteStartJudge(float triggerTime) { float timeDifference = triggerTime - exactJudgeTime; RemoveFromCheckingList(); preJudgeType = GetStartJudgeType(timeDifference); isFirstJudged = true; } public void ExecuteTapJudge(float triggerTime) { RemoveFromCheckingList(); NoteJudgeType startJudgeType = GetStartJudgeType(triggerTime - exactJudgeTime); switch (startJudgeType) { case NoteJudgeType.Perfect: Perfect(triggerTime); GameManager.Instance.playingRecorder.resultData.Add(0); break; case NoteJudgeType.Good: Good(triggerTime); break; case NoteJudgeType.Bad: Bad(triggerTime); break; case NoteJudgeType.Miss: Miss(triggerTime); break; } if (startJudgeType != NoteJudgeType.Miss) NoteAudioSubmodule.PlayGeneralJudgeAudios(); isFirstJudged = true; isFinalJudged = true; } public void ExecuteFinalJudge(float triggerTime) { if (isFirstJudged && !isFinalJudged && preJudgeType != NoteJudgeType.NotJudged && triggerTime >= exactJudgeTime) { switch (preJudgeType) { case NoteJudgeType.Perfect: Perfect(triggerTime); GameManager.Instance.playingRecorder.resultData.Add(0); break; case NoteJudgeType.Good: Good(triggerTime); break; case NoteJudgeType.Bad: Bad(triggerTime); break; case NoteJudgeType.Miss: Miss(triggerTime); break; } if (preJudgeType != NoteJudgeType.Miss) NoteAudioSubmodule.PlayGeneralJudgeAudios(); isFinalJudged = true; } } #endregion #region [谱面功能] Beatmap Method #endregion } }