mirror of
https://github.com/RHeavenStudioPlus/HeavenStudioPlus.git
synced 2024-11-26 03:25:22 +00:00
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
This commit is contained in:
parent
63a2814caa
commit
199d16cb9f
5 changed files with 90 additions and 37 deletions
|
@ -170,11 +170,8 @@ namespace HeavenStudio
|
||||||
var chart = GameManager.instance.Beatmap;
|
var chart = GameManager.instance.Beatmap;
|
||||||
double offset = chart.data.offset;
|
double offset = chart.data.offset;
|
||||||
double dspTime = AudioSettings.dspTime;
|
double dspTime = AudioSettings.dspTime;
|
||||||
absTimeAdjust = 0;
|
|
||||||
dspStart = dspTime;
|
|
||||||
startTime = DateTime.Now;
|
|
||||||
|
|
||||||
GameManager.instance.SortEventsList();
|
dspStart = dspTime;
|
||||||
|
|
||||||
startPos = GetSongPosFromBeat(beat);
|
startPos = GetSongPosFromBeat(beat);
|
||||||
firstBeatOffset = offset;
|
firstBeatOffset = offset;
|
||||||
|
@ -188,20 +185,21 @@ namespace HeavenStudio
|
||||||
{
|
{
|
||||||
musicScheduledTime = dspTime + musicStartDelay / SongPitch;
|
musicScheduledTime = dspTime + musicStartDelay / SongPitch;
|
||||||
musicScheduledPitch = SongPitch;
|
musicScheduledPitch = SongPitch;
|
||||||
musicSource.PlayScheduled(musicScheduledTime);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
musicScheduledTime = dspTime;
|
musicScheduledTime = dspTime;
|
||||||
musicScheduledPitch = SongPitch;
|
musicScheduledPitch = SongPitch;
|
||||||
|
|
||||||
musicSource.Play();
|
|
||||||
}
|
}
|
||||||
|
musicSource.PlayScheduled(musicScheduledTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
songPosBeat = GetBeatFromSongPos(time);
|
songPosBeat = GetBeatFromSongPos(time);
|
||||||
startBeat = songPosBeat;
|
startBeat = songPosBeat;
|
||||||
|
|
||||||
|
absTimeAdjust = 0;
|
||||||
|
startTime = DateTime.Now;
|
||||||
|
|
||||||
isPlaying = true;
|
isPlaying = true;
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<RiqEntity> GetAllInGameManagerList(string gameName, string[] include)
|
public static List<RiqEntity> GetAllInGameManagerList(string gameName, string[] include)
|
||||||
{
|
{
|
||||||
List<RiqEntity> temp1 = GameManager.instance.Beatmap.Entities.FindAll(c => c.datamodel.Split('/')[0] == gameName);
|
List<RiqEntity> temp1 = GameManager.instance.Beatmap.Entities.FindAll(c => c.datamodel.Split('/')[0] == gameName);
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace HeavenStudio
|
||||||
bool ChartLoadError;
|
bool ChartLoadError;
|
||||||
|
|
||||||
List<double> eventBeats, tempoBeats, volumeBeats, sectionBeats;
|
List<double> eventBeats, tempoBeats, volumeBeats, sectionBeats;
|
||||||
|
List<RiqEntity> allGameSwitches;
|
||||||
|
|
||||||
public event Action<double> onBeatChanged;
|
public event Action<double> onBeatChanged;
|
||||||
public event Action<RiqEntity> onSectionChange;
|
public event Action<RiqEntity> onSectionChange;
|
||||||
|
@ -163,8 +164,9 @@ namespace HeavenStudio
|
||||||
|
|
||||||
if (Beatmap.Entities.Count >= 1)
|
if (Beatmap.Entities.Count >= 1)
|
||||||
{
|
{
|
||||||
SetCurrentGame(Beatmap.Entities[0].datamodel.Split(0));
|
string game = Beatmap.Entities[0].datamodel.Split(0);
|
||||||
SetGame(Beatmap.Entities[0].datamodel.Split(0));
|
SetCurrentGame(game);
|
||||||
|
SetGame(game);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -289,8 +291,9 @@ namespace HeavenStudio
|
||||||
|
|
||||||
if (Beatmap.Entities.Count >= 1)
|
if (Beatmap.Entities.Count >= 1)
|
||||||
{
|
{
|
||||||
SetCurrentGame(Beatmap.Entities[0].datamodel.Split(0));
|
string game = Beatmap.Entities[0].datamodel.Split(0);
|
||||||
SetGame(Beatmap.Entities[0].datamodel.Split(0));
|
SetCurrentGame(game);
|
||||||
|
SetGame(game);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -330,26 +333,33 @@ namespace HeavenStudio
|
||||||
TimingAccuracyDisplay.instance.MakeAccuracyVfx(time, late);
|
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)
|
public void SeekAheadAndPreload(double start, float seekTime = 8f)
|
||||||
{
|
{
|
||||||
List<RiqEntity> entitiesAtSameBeat = ListPool<RiqEntity>.Get();
|
List<RiqEntity> entitiesAtSameBeat = ListPool<RiqEntity>.Get();
|
||||||
List<RiqEntity> gameSwitchs = ListPool<RiqEntity>.Get();
|
|
||||||
Minigames.Minigame inf;
|
Minigames.Minigame inf;
|
||||||
|
|
||||||
//seek ahead to preload games that have assetbundles
|
//seek ahead to preload games that have assetbundles
|
||||||
//check game switches first
|
if (currentPreSwitch < allGameSwitches.Count && currentPreSwitch >= 0)
|
||||||
foreach (RiqEntity entity in Beatmap.Entities)
|
|
||||||
{
|
{
|
||||||
if (entity.datamodel.Split(1) == "switchGame")
|
if (start + seekTime >= allGameSwitches[currentPreSwitch].beat)
|
||||||
{
|
{
|
||||||
gameSwitchs.Add(entity);
|
string gameName = allGameSwitches[currentPreSwitch].datamodel.Split('/')[2];
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentPreSwitch < gameSwitchs.Count && currentPreSwitch >= 0)
|
|
||||||
{
|
|
||||||
if (start + seekTime >= gameSwitchs[currentPreSwitch].beat)
|
|
||||||
{
|
|
||||||
string gameName = gameSwitchs[currentPreSwitch].datamodel.Split(2);
|
|
||||||
inf = GetGameInfo(gameName);
|
inf = GetGameInfo(gameName);
|
||||||
if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded)
|
if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded)
|
||||||
{
|
{
|
||||||
|
@ -389,7 +399,6 @@ namespace HeavenStudio
|
||||||
}
|
}
|
||||||
|
|
||||||
ListPool<RiqEntity>.Release(entitiesAtSameBeat);
|
ListPool<RiqEntity>.Release(entitiesAtSameBeat);
|
||||||
ListPool<RiqEntity>.Release(gameSwitchs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SeekAheadAndDoPreEvent(double start)
|
public void SeekAheadAndDoPreEvent(double start)
|
||||||
|
@ -408,8 +417,10 @@ namespace HeavenStudio
|
||||||
}
|
}
|
||||||
SortEventsByPriority(entitiesAtSameBeat);
|
SortEventsByPriority(entitiesAtSameBeat);
|
||||||
|
|
||||||
|
string[] seekEntityDatamodel = seekEntity.datamodel.Split('/');
|
||||||
|
|
||||||
float seekTime = eventCaller.GetGameAction(
|
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])
|
if (start + seekTime >= eventBeats[currentPreSequence])
|
||||||
{
|
{
|
||||||
|
@ -474,8 +485,7 @@ namespace HeavenStudio
|
||||||
float seekTime = 8f;
|
float seekTime = 8f;
|
||||||
//seek ahead to preload games that have assetbundles
|
//seek ahead to preload games that have assetbundles
|
||||||
SeekAheadAndPreload(cond.songPositionInBeatsAsDouble, seekTime);
|
SeekAheadAndPreload(cond.songPositionInBeatsAsDouble, seekTime);
|
||||||
|
SeekAheadAndDoPreEvent(cond.songPositionInBeatsAsDouble);
|
||||||
SeekAheadAndDoPreEvent(Conductor.instance.songPositionInBeatsAsDouble);
|
|
||||||
|
|
||||||
if (currentEvent < Beatmap.Entities.Count && currentEvent >= 0)
|
if (currentEvent < Beatmap.Entities.Count && currentEvent >= 0)
|
||||||
{
|
{
|
||||||
|
@ -592,7 +602,6 @@ namespace HeavenStudio
|
||||||
yield return new WaitForSeconds(delay);
|
yield return new WaitForSeconds(delay);
|
||||||
bool paused = Conductor.instance.isPaused;
|
bool paused = Conductor.instance.isPaused;
|
||||||
|
|
||||||
Conductor.instance.Play(beat);
|
|
||||||
if (paused)
|
if (paused)
|
||||||
{
|
{
|
||||||
Util.SoundByte.UnpauseOneShots();
|
Util.SoundByte.UnpauseOneShots();
|
||||||
|
@ -604,13 +613,15 @@ namespace HeavenStudio
|
||||||
Conductor.instance.firstBeatOffset = Beatmap.data.offset;
|
Conductor.instance.firstBeatOffset = Beatmap.data.offset;
|
||||||
SetCurrentEventToClosest(beat);
|
SetCurrentEventToClosest(beat);
|
||||||
KillAllSounds();
|
KillAllSounds();
|
||||||
}
|
|
||||||
|
|
||||||
Minigame miniGame = currentGameO.GetComponent<Minigame>();
|
Minigame miniGame = currentGameO?.GetComponent<Minigame>();
|
||||||
if (miniGame != null)
|
if (miniGame != null)
|
||||||
miniGame.OnPlay(beat);
|
miniGame.OnPlay(beat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Conductor.instance.Play(beat);
|
||||||
|
}
|
||||||
|
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
Conductor.instance.Pause();
|
Conductor.instance.Pause();
|
||||||
|
@ -700,16 +711,23 @@ namespace HeavenStudio
|
||||||
tempoBeats = Beatmap.TempoChanges.Select(c => c.beat).ToList();
|
tempoBeats = Beatmap.TempoChanges.Select(c => c.beat).ToList();
|
||||||
volumeBeats = Beatmap.VolumeChanges.Select(c => c.beat).ToList();
|
volumeBeats = Beatmap.VolumeChanges.Select(c => c.beat).ToList();
|
||||||
sectionBeats = Beatmap.SectionMarkers.Select(c => c.beat).ToList();
|
sectionBeats = Beatmap.SectionMarkers.Select(c => c.beat).ToList();
|
||||||
|
|
||||||
|
allGameSwitches = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame" });
|
||||||
}
|
}
|
||||||
|
|
||||||
void SortEventsByPriority(List<RiqEntity> entities)
|
void SortEventsByPriority(List<RiqEntity> entities)
|
||||||
{
|
{
|
||||||
|
string[] xDatamodel;
|
||||||
|
string[] yDatamodel;
|
||||||
entities.Sort((x, y) =>
|
entities.Sort((x, y) =>
|
||||||
{
|
{
|
||||||
Minigames.Minigame xGame = eventCaller.GetMinigame(x.datamodel.Split(0));
|
xDatamodel = x.datamodel.Split('/');
|
||||||
Minigames.GameAction xAction = eventCaller.GetGameAction(xGame, x.datamodel.Split(1));
|
yDatamodel = y.datamodel.Split('/');
|
||||||
Minigames.Minigame yGame = eventCaller.GetMinigame(y.datamodel.Split(0));
|
|
||||||
Minigames.GameAction yAction = eventCaller.GetGameAction(yGame, y.datamodel.Split(1));
|
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);
|
return yAction.priority.CompareTo(xAction.priority);
|
||||||
});
|
});
|
||||||
|
@ -768,7 +786,7 @@ namespace HeavenStudio
|
||||||
currentPreEvent = GetIndexAfter(eventBeats, beat);
|
currentPreEvent = GetIndexAfter(eventBeats, beat);
|
||||||
currentPreSequence = 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);
|
string newGame = Beatmap.Entities[Math.Min(currentEvent, eventBeats.Count - 1)].datamodel.Split(0);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ using UnityEngine;
|
||||||
using HeavenStudio.InputSystem;
|
using HeavenStudio.InputSystem;
|
||||||
|
|
||||||
using static JSL;
|
using static JSL;
|
||||||
|
using HeavenStudio.Games;
|
||||||
|
|
||||||
namespace HeavenStudio.InputSystem
|
namespace HeavenStudio.InputSystem
|
||||||
{
|
{
|
||||||
|
@ -187,6 +188,11 @@ namespace HeavenStudio
|
||||||
/* MAIN INPUT METHODS */
|
/* MAIN INPUT METHODS */
|
||||||
/*--------------------*/
|
/*--------------------*/
|
||||||
|
|
||||||
|
public static bool GetIsAction(string action)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// BUTTONS
|
// BUTTONS
|
||||||
//TODO: refactor for controller and custom binds, currently uses temporary button checks
|
//TODO: refactor for controller and custom binds, currently uses temporary button checks
|
||||||
|
|
||||||
|
|
|
@ -501,6 +501,16 @@ namespace HeavenStudio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class InputAction
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public List<InputActionEntry> mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InputActionEntry
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public delegate void EventCallback();
|
public delegate void EventCallback();
|
||||||
public delegate void ParamChangeCallback(string paramName, object paramValue, RiqEntity entity);
|
public delegate void ParamChangeCallback(string paramName, object paramValue, RiqEntity entity);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue