From 199d16cb9f377632d253762335c77083e23198ca Mon Sep 17 00:00:00 2001 From: minenice55 Date: Tue, 12 Sep 2023 16:38:28 -0400 Subject: [PATCH] Chart Seek Bugfix (#545) * fix lag spike when starting playback from middle of chart further optimization to GameManager which considerably reduces garbage generation * let dsp offset be calculated on playback start if needed --- Assets/Scripts/Conductor.cs | 12 ++-- Assets/Scripts/EventCaller.cs | 21 ++++++ Assets/Scripts/GameManager.cs | 78 ++++++++++++++--------- Assets/Scripts/InputSystem/PlayerInput.cs | 6 ++ Assets/Scripts/Minigames.cs | 10 +++ 5 files changed, 90 insertions(+), 37 deletions(-) diff --git a/Assets/Scripts/Conductor.cs b/Assets/Scripts/Conductor.cs index fec35f50..6e9e4ac1 100644 --- a/Assets/Scripts/Conductor.cs +++ b/Assets/Scripts/Conductor.cs @@ -170,11 +170,8 @@ namespace HeavenStudio var chart = GameManager.instance.Beatmap; double offset = chart.data.offset; double dspTime = AudioSettings.dspTime; - absTimeAdjust = 0; - dspStart = dspTime; - startTime = DateTime.Now; - GameManager.instance.SortEventsList(); + dspStart = dspTime; startPos = GetSongPosFromBeat(beat); firstBeatOffset = offset; @@ -188,20 +185,21 @@ namespace HeavenStudio { musicScheduledTime = dspTime + musicStartDelay / SongPitch; musicScheduledPitch = SongPitch; - musicSource.PlayScheduled(musicScheduledTime); } else { musicScheduledTime = dspTime; musicScheduledPitch = SongPitch; - - musicSource.Play(); } + musicSource.PlayScheduled(musicScheduledTime); } songPosBeat = GetBeatFromSongPos(time); startBeat = songPosBeat; + absTimeAdjust = 0; + startTime = DateTime.Now; + isPlaying = true; isPaused = false; } diff --git a/Assets/Scripts/EventCaller.cs b/Assets/Scripts/EventCaller.cs index f2a4ee83..ad111faa 100644 --- a/Assets/Scripts/EventCaller.cs +++ b/Assets/Scripts/EventCaller.cs @@ -108,6 +108,27 @@ namespace HeavenStudio } } + static bool StringStartsWith(string a, string b) + { + int aLen = a.Length; + int bLen = b.Length; + + int ap = 0; int bp = 0; + + while (ap < aLen && bp < bLen && a [ap] == b [bp]) + { + ap++; + bp++; + } + + return (bp == bLen); + } + + public static bool IsGameSwitch(RiqEntity entity) + { + return StringStartsWith(entity.datamodel, "gameManager/switchGame"); + } + public static List GetAllInGameManagerList(string gameName, string[] include) { List temp1 = GameManager.instance.Beatmap.Entities.FindAll(c => c.datamodel.Split('/')[0] == gameName); diff --git a/Assets/Scripts/GameManager.cs b/Assets/Scripts/GameManager.cs index 7bfaf1f9..7b57cb7c 100644 --- a/Assets/Scripts/GameManager.cs +++ b/Assets/Scripts/GameManager.cs @@ -55,6 +55,7 @@ namespace HeavenStudio bool ChartLoadError; List eventBeats, tempoBeats, volumeBeats, sectionBeats; + List allGameSwitches; public event Action onBeatChanged; public event Action onSectionChange; @@ -163,8 +164,9 @@ namespace HeavenStudio if (Beatmap.Entities.Count >= 1) { - SetCurrentGame(Beatmap.Entities[0].datamodel.Split(0)); - SetGame(Beatmap.Entities[0].datamodel.Split(0)); + string game = Beatmap.Entities[0].datamodel.Split(0); + SetCurrentGame(game); + SetGame(game); } else { @@ -289,8 +291,9 @@ namespace HeavenStudio if (Beatmap.Entities.Count >= 1) { - SetCurrentGame(Beatmap.Entities[0].datamodel.Split(0)); - SetGame(Beatmap.Entities[0].datamodel.Split(0)); + string game = Beatmap.Entities[0].datamodel.Split(0); + SetCurrentGame(game); + SetGame(game); } else { @@ -330,26 +333,33 @@ namespace HeavenStudio TimingAccuracyDisplay.instance.MakeAccuracyVfx(time, late); } + static bool StringStartsWith(string a, string b) + { + int aLen = a.Length; + int bLen = b.Length; + + int ap = 0; int bp = 0; + + while (ap < aLen && bp < bLen && a [ap] == b [bp]) + { + ap++; + bp++; + } + + return (bp == bLen); + } + public void SeekAheadAndPreload(double start, float seekTime = 8f) { List entitiesAtSameBeat = ListPool.Get(); - List gameSwitchs = ListPool.Get(); Minigames.Minigame inf; //seek ahead to preload games that have assetbundles - //check game switches first - foreach (RiqEntity entity in Beatmap.Entities) + if (currentPreSwitch < allGameSwitches.Count && currentPreSwitch >= 0) { - if (entity.datamodel.Split(1) == "switchGame") + if (start + seekTime >= allGameSwitches[currentPreSwitch].beat) { - gameSwitchs.Add(entity); - } - } - if (currentPreSwitch < gameSwitchs.Count && currentPreSwitch >= 0) - { - if (start + seekTime >= gameSwitchs[currentPreSwitch].beat) - { - string gameName = gameSwitchs[currentPreSwitch].datamodel.Split(2); + string gameName = allGameSwitches[currentPreSwitch].datamodel.Split('/')[2]; inf = GetGameInfo(gameName); if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded) { @@ -389,7 +399,6 @@ namespace HeavenStudio } ListPool.Release(entitiesAtSameBeat); - ListPool.Release(gameSwitchs); } public void SeekAheadAndDoPreEvent(double start) @@ -408,8 +417,10 @@ namespace HeavenStudio } SortEventsByPriority(entitiesAtSameBeat); + string[] seekEntityDatamodel = seekEntity.datamodel.Split('/'); + float seekTime = eventCaller.GetGameAction( - eventCaller.GetMinigame(seekEntity.datamodel.Split(0)), seekEntity.datamodel.Split(1)).preFunctionLength; + eventCaller.GetMinigame(seekEntityDatamodel[0]), seekEntityDatamodel[1]).preFunctionLength; if (start + seekTime >= eventBeats[currentPreSequence]) { @@ -474,8 +485,7 @@ namespace HeavenStudio float seekTime = 8f; //seek ahead to preload games that have assetbundles SeekAheadAndPreload(cond.songPositionInBeatsAsDouble, seekTime); - - SeekAheadAndDoPreEvent(Conductor.instance.songPositionInBeatsAsDouble); + SeekAheadAndDoPreEvent(cond.songPositionInBeatsAsDouble); if (currentEvent < Beatmap.Entities.Count && currentEvent >= 0) { @@ -592,7 +602,6 @@ namespace HeavenStudio yield return new WaitForSeconds(delay); bool paused = Conductor.instance.isPaused; - Conductor.instance.Play(beat); if (paused) { Util.SoundByte.UnpauseOneShots(); @@ -604,11 +613,13 @@ namespace HeavenStudio Conductor.instance.firstBeatOffset = Beatmap.data.offset; SetCurrentEventToClosest(beat); KillAllSounds(); - } - Minigame miniGame = currentGameO.GetComponent(); - if (miniGame != null) - miniGame.OnPlay(beat); + Minigame miniGame = currentGameO?.GetComponent(); + if (miniGame != null) + miniGame.OnPlay(beat); + } + + Conductor.instance.Play(beat); } public void Pause() @@ -700,16 +711,23 @@ namespace HeavenStudio tempoBeats = Beatmap.TempoChanges.Select(c => c.beat).ToList(); volumeBeats = Beatmap.VolumeChanges.Select(c => c.beat).ToList(); sectionBeats = Beatmap.SectionMarkers.Select(c => c.beat).ToList(); + + allGameSwitches = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame" }); } void SortEventsByPriority(List entities) { + string[] xDatamodel; + string[] yDatamodel; entities.Sort((x, y) => { - Minigames.Minigame xGame = eventCaller.GetMinigame(x.datamodel.Split(0)); - Minigames.GameAction xAction = eventCaller.GetGameAction(xGame, x.datamodel.Split(1)); - Minigames.Minigame yGame = eventCaller.GetMinigame(y.datamodel.Split(0)); - Minigames.GameAction yAction = eventCaller.GetGameAction(yGame, y.datamodel.Split(1)); + xDatamodel = x.datamodel.Split('/'); + yDatamodel = y.datamodel.Split('/'); + + Minigames.Minigame xGame = eventCaller.GetMinigame(xDatamodel[0]); + Minigames.GameAction xAction = eventCaller.GetGameAction(xGame, xDatamodel[1]); + Minigames.Minigame yGame = eventCaller.GetMinigame(yDatamodel[0]); + Minigames.GameAction yAction = eventCaller.GetGameAction(yGame, yDatamodel[1]); return yAction.priority.CompareTo(xAction.priority); }); @@ -768,7 +786,7 @@ namespace HeavenStudio currentPreEvent = GetIndexAfter(eventBeats, beat); currentPreSequence = GetIndexAfter(eventBeats, beat); - var gameSwitchs = Beatmap.Entities.FindAll(c => c.datamodel.Split(1) == "switchGame"); + var gameSwitchs = Beatmap.Entities.FindAll(c => c.datamodel.Split("/")[1] == "switchGame"); string newGame = Beatmap.Entities[Math.Min(currentEvent, eventBeats.Count - 1)].datamodel.Split(0); diff --git a/Assets/Scripts/InputSystem/PlayerInput.cs b/Assets/Scripts/InputSystem/PlayerInput.cs index 9d8aeed1..b8471509 100644 --- a/Assets/Scripts/InputSystem/PlayerInput.cs +++ b/Assets/Scripts/InputSystem/PlayerInput.cs @@ -8,6 +8,7 @@ using UnityEngine; using HeavenStudio.InputSystem; using static JSL; +using HeavenStudio.Games; namespace HeavenStudio.InputSystem { @@ -186,6 +187,11 @@ namespace HeavenStudio /*--------------------*/ /* MAIN INPUT METHODS */ /*--------------------*/ + + public static bool GetIsAction(string action) + { + return false; + } // BUTTONS //TODO: refactor for controller and custom binds, currently uses temporary button checks diff --git a/Assets/Scripts/Minigames.cs b/Assets/Scripts/Minigames.cs index b06582cb..03a7ee75 100644 --- a/Assets/Scripts/Minigames.cs +++ b/Assets/Scripts/Minigames.cs @@ -501,6 +501,16 @@ namespace HeavenStudio } } + public class InputAction + { + public string name; + public List mappings; + } + + public class InputActionEntry + { + } + public delegate void EventCallback(); public delegate void ParamChangeCallback(string paramName, object paramValue, RiqEntity entity);