Files
ichni_Official/Assets/Scripts/Game/GameElements/Notes/NoteObjects/NoteBase.cs
SoulliesOfficial bae0bfbc20 perf
2025-07-21 05:42:20 -04:00

413 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Dreamteck.Splines;
using Sirenix.OdinInspector;
using UniRx;
using UnityEngine;
using UnityEngine.Serialization;
namespace Ichni.RhythmGame
{
public abstract partial class NoteBase : GameElement, IHaveTimeDurationSubmodule, IComparable<NoteBase>
{
[Title("Basic Info")]
public float exactJudgeTime;
public NoteJudgeIntervals judgeIntervals;
[Title("Track Info")]
public bool isOnTrack;
public Track track;
public SplinePositioner trackPositioner;
[Title("Audio")]
public AudioContainer audioContainer;
[Title("NoteVisual")]
public NoteVisualBase noteVisual;
[Title("Submodules")]
public TimeDurationSubmodule timeDurationSubmodule { get; set; }
public NoteJudgeSubmodule noteJudgeSubmodule { get; set; }
public NoteAudioSubmodule noteAudioSubmodule { get; set; }
[Title("In-Game Info")]
public bool isDuringJudging;
public Vector2 noteScreenPosition;
public bool isFirstJudged;
public bool isFinalJudged;
public override int HierarchyPriority => -10;
/// <summary>
/// 在MovableTrack上更新Note的位置注意HoldNote需要重写这个方法
/// </summary>
public virtual void UpdateNoteInMovableTrack()
{
TrackTimeSubmoduleMovable trackTimeSubmoduleMovable = track.trackTimeSubmodule as TrackTimeSubmoduleMovable;
trackPositioner.SetPercent(trackTimeSubmoduleMovable.GetTrackPercent(exactJudgeTime));
}
/// <summary>
/// 在StaticTrack上更新Note的位置注意HoldNote需要重写这个方法
/// </summary>
public virtual void UpdateNoteInStaticTrack()
{
float songTime = GameManager.instance.songTime;
TrackTimeSubmoduleStatic trackTimeSubmoduleStatic = track.trackTimeSubmodule as TrackTimeSubmoduleStatic;
float startMove = exactJudgeTime - trackTimeSubmoduleStatic.trackTotalTime;
float percent = AnimationCurveEvaluator.Evaluate(trackTimeSubmoduleStatic.animationCurveType, (songTime - startMove) / trackTimeSubmoduleStatic.trackTotalTime);
percent = Mathf.Max(percent, 0);
percent = Mathf.Min(percent, 1);
trackPositioner.SetPercent(1 - percent);
}
public override void SetDefaultSubmodules()
{
timeDurationSubmodule = new TimeDurationSubmodule(this);
noteJudgeSubmodule = new NoteJudgeSubmodule(this);
}
public override void AfterInitialize()
{
float beyondTime = 0f;
foreach (NoteGenerateEffect ge in noteVisual.effectSubmodule.effectCollection["Generate"].Cast<NoteGenerateEffect>())
{
ge.Recover();
beyondTime = Mathf.Max(beyondTime, ge.generateTime);
}
if (exactJudgeTime - beyondTime - 0.5f > -GameManager.instance.songInformation.delay)
{
gameObject.SetActive(false);
GameManager.instance.noteManager.RegisterNote(this, exactJudgeTime - beyondTime - 0.5f);
}
}
protected virtual void Update()
{
if (!GameManager.instance.audioManager.isUpdating || isFinalJudged)
{
return;
}
if (isOnTrack)
{
if (track.trackTimeSubmodule is TrackTimeSubmoduleStatic)
{
UpdateNoteInStaticTrack();
}
}
if (isDuringJudging)
{
noteScreenPosition = GameManager.instance.cameraManager.gameCamera.gameCamera.WorldToScreenPoint(noteVisual.transform.position);
}
foreach (EffectBase e in noteVisual.effectSubmodule.effectCollection["Generate"])
{
e.UpdateEffect(exactJudgeTime);
}
if (!isFirstJudged && GameManager.instance.songTime > exactJudgeTime + judgeIntervals.afterMiss)
{
Miss(exactJudgeTime + judgeIntervals.afterMiss);
isFirstJudged = true;
isFinalJudged = true;
RemoveFromCheckingList();
}
}
protected virtual NoteJudgeType GetStartJudgeType(float timeDifference)
{
return judgeIntervals.GetNoteJudgeType(timeDifference);
}
protected virtual void RemoveFromCheckingList()
{
throw new NotImplementedException();
}
public virtual void ExecuteStartJudge()
{
float triggerTime = GameManager.instance.songTime;
float timeDifference = triggerTime - exactJudgeTime;
NoteJudgeType startJudgeType = GetStartJudgeType(timeDifference);
if (startJudgeType == NoteJudgeType.Perfect)
{
Perfect(triggerTime);
}
else if (startJudgeType == NoteJudgeType.Good)
{
Good(triggerTime);
}
else if (startJudgeType == NoteJudgeType.Bad)
{
Bad(triggerTime);
}
else if (startJudgeType == NoteJudgeType.Miss)
{
Miss(triggerTime);
}
if (startJudgeType != NoteJudgeType.Miss)
{
noteAudioSubmodule.PlayGeneralJudgeAudios();
}
isFirstJudged = true;
}
public void UpdateNoteInTrack()
{
if (isOnTrack && track.trackTimeSubmodule != null)
{
if (track.trackTimeSubmodule is TrackTimeSubmoduleMovable)
{
UpdateNoteInMovableTrack();
}
else if (track.trackTimeSubmodule is TrackTimeSubmoduleStatic)
{
UpdateNoteInStaticTrack();
}
}
}
public virtual void Perfect(float triggerTime)
{
isDuringJudging = false;
GameManager.instance.playingRecorder.AddPerfect();
noteAudioSubmodule.PlayNoteJudgeAudios(NoteJudgeType.Perfect);
Observable.EveryUpdate().Subscribe(_ =>
{
noteVisual.effectSubmodule.effectCollection["GeneralJudge"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["Perfect"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["AfterJudge"].ForEach(e => e.UpdateEffect(exactJudgeTime));
}).AddTo(gameObject);
if (isOnTrack) track.childElementList.Remove(this);
Destroy(gameObject, 1.2f); //注意所有特效时间不得超过1.2秒
}
public virtual void Good(float triggerTime)
{
isDuringJudging = false;
GameManager.instance.playingRecorder.AddGood();
noteAudioSubmodule.PlayNoteJudgeAudios(NoteJudgeType.Good);
Observable.EveryUpdate().Subscribe(_ =>
{
noteVisual.effectSubmodule.effectCollection["GeneralJudge"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["Good"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["AfterJudge"].ForEach(e => e.UpdateEffect(exactJudgeTime));
}).AddTo(gameObject);
if (isOnTrack) track.childElementList.Remove(this);
Destroy(gameObject, 1.2f);
}
public virtual void Bad(float triggerTime){
{
isDuringJudging = false;
GameManager.instance.playingRecorder.AddBad();
noteAudioSubmodule.PlayNoteJudgeAudios(NoteJudgeType.Bad);
Observable.EveryUpdate().Subscribe(_ =>
{
noteVisual.effectSubmodule.effectCollection["GeneralJudge"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["Bad"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["AfterJudge"].ForEach(e => e.UpdateEffect(exactJudgeTime));
}).AddTo(gameObject);
if (isOnTrack) track.childElementList.Remove(this);
Destroy(gameObject, 1.2f);
}}
public virtual void Miss(float triggerTime)
{
isDuringJudging = false;
GameManager.instance.playingRecorder.AddMiss();
noteAudioSubmodule.PlayNoteJudgeAudios(NoteJudgeType.Miss);
Observable.EveryUpdate().Subscribe(_ =>
{
noteVisual.effectSubmodule.effectCollection["Miss"].ForEach(e => e.UpdateEffect(triggerTime));
noteVisual.effectSubmodule.effectCollection["AfterJudge"].ForEach(e => e.UpdateEffect(exactJudgeTime));
}).AddTo(gameObject);
if (isOnTrack) track.childElementList.Remove(this);
Destroy(gameObject, 1.2f);
}
}
public abstract partial class NoteBase
{
protected void SetJudgeArea()
{
if (noteJudgeSubmodule != null)
{
if (GameManager.instance.songTime > exactJudgeTime - 0.25f && !isFirstJudged)
{
foreach (NoteJudgeUnit unit in noteJudgeSubmodule.judgeUnitList.Where(unit => !unit.isShowingJudge))
{
unit.SetShowingJudge(true);
}
}
foreach (NoteJudgeUnit unit in noteJudgeSubmodule.judgeUnitList.Where(unit => unit.isShowingJudge))
{
unit.UpdateJudge();
}
if (GameManager.instance.songTime > exactJudgeTime + 0.25f)
{
foreach (NoteJudgeUnit unit in noteJudgeSubmodule.judgeUnitList.Where(unit => unit.isShowingJudge))
{
unit.SetShowingJudge(false);
}
}
}
}
public int CompareTo(NoteBase other)
{
return exactJudgeTime.CompareTo(other.exactJudgeTime);
}
}
public abstract partial class NoteBase
{
public enum NoteJudgeType
{
Perfect = 0,
Good = 1,
Bad = 2,
Miss = 3,
NotJudged = -999
}
public static NoteJudgeType GetLowerType(NoteJudgeType typeA, NoteJudgeType typeB)
{
if (typeA == NoteJudgeType.NotJudged) return typeB;
if (typeB == NoteJudgeType.NotJudged) return typeA;
return typeA > typeB ? typeA : typeB;
}
public class NoteJudgeIntervals
{
public TimeInterval beforeMiss;
public TimeInterval beforeBad;
public TimeInterval beforeGood;
public TimeInterval perfect;
public TimeInterval afterGood;
public TimeInterval afterBad;
public float afterMiss;
public NoteJudgeIntervals(TimeInterval beforeMiss, TimeInterval beforeBad, TimeInterval beforeGood,
TimeInterval perfect, TimeInterval afterGood, TimeInterval afterBad, float afterMiss)
{
this.beforeMiss = beforeMiss;
this.beforeBad = beforeBad;
this.beforeGood = beforeGood;
this.perfect = perfect;
this.afterGood = afterGood;
this.afterBad = afterBad;
this.afterMiss = afterMiss;
}
public NoteJudgeType GetNoteJudgeType(float timeDifference)
{
if (beforeMiss.IsInInterval(timeDifference))
{
return NoteJudgeType.Miss;
}
else if (beforeBad.IsInInterval(timeDifference))
{
return NoteJudgeType.Bad;
}
else if (beforeGood.IsInInterval(timeDifference))
{
return NoteJudgeType.Good;
}
else if (perfect.IsInInterval(timeDifference))
{
return NoteJudgeType.Perfect;
}
else if (afterGood.IsInInterval(timeDifference))
{
return NoteJudgeType.Good;
}
else if (afterBad.IsInInterval(timeDifference))
{
return NoteJudgeType.Bad;
}
return NoteJudgeType.Miss; // Default to Miss if no other condition is met
}
}
public class TimeInterval
{
public float intervalStart;
public float intervalEnd;
public TimeInterval(float start, float end)
{
intervalStart = start;
intervalEnd = end;
}
public bool IsInInterval(float time)
{
if (Mathf.Approximately(intervalStart, intervalEnd)) return false;
return time >= intervalStart && time <= intervalEnd;
}
}
public static string GetNoteTypeName(NoteBase note)
{
return note switch
{
Tap => "Tap",
Stay => "Stay",
Hold => "Hold",
Flick => "Flick",
_ => throw new NotImplementedException("Note type not recognized")
};
}
}
namespace Beatmap
{
public abstract class NoteBase_BM : GameElement_BM
{
public float exactJudgeTime;
public NoteBase_BM()
{
}
public NoteBase_BM(string elementName, Guid elementGuid, List<string> tags, GameElement_BM attachedElement, float exactJudgeTime)
: base(elementName, elementGuid, tags, attachedElement)
{
this.exactJudgeTime = exactJudgeTime;
}
public override void ExecuteBM()
{
throw new NotImplementedException();
}
public override GameElement DuplicateBM(GameElement parent)
{
throw new NotImplementedException();
}
}
}
}