F=B*UE%}z&mZg@UMYgQ5tg~#iY_}YQWIBTbnq=EF
zo7uL&=C-Y|ZL}S+9k=z^BDRyZ)3zjgsy)p<&7N;xWM67;wm)LuVc%!(v>&o}p{09h
VZ1pMk4AQivXs0z=Q2w7j{|naEt!DrL
diff --git a/Assets/Plugins/JSL/x86/JoyShockLibrary.dll.meta b/Assets/Plugins/JSL/x86/JoyShockLibrary.dll.meta
deleted file mode 100644
index bfe0997e7..000000000
--- a/Assets/Plugins/JSL/x86/JoyShockLibrary.dll.meta
+++ /dev/null
@@ -1,63 +0,0 @@
-fileFormatVersion: 2
-guid: c603b06f7fc73b9448935991b913d915
-PluginImporter:
- externalObjects: {}
- serializedVersion: 2
- iconMap: {}
- executionOrder: {}
- defineConstraints: []
- isPreloaded: 0
- isOverridable: 0
- isExplicitlyReferenced: 0
- validateReferences: 1
- platformData:
- - first:
- : Any
- second:
- enabled: 0
- settings:
- Exclude Editor: 1
- Exclude Linux64: 0
- Exclude OSXUniversal: 0
- Exclude Win: 0
- Exclude Win64: 1
- - first:
- Any:
- second:
- enabled: 1
- settings: {}
- - first:
- Editor: Editor
- second:
- enabled: 0
- settings:
- CPU: AnyCPU
- DefaultValueInitialized: true
- OS: AnyOS
- - first:
- Standalone: Linux64
- second:
- enabled: 1
- settings:
- CPU: None
- - first:
- Standalone: OSXUniversal
- second:
- enabled: 1
- settings:
- CPU: None
- - first:
- Standalone: Win
- second:
- enabled: 1
- settings:
- CPU: x86
- - first:
- Standalone: Win64
- second:
- enabled: 0
- settings:
- CPU: None
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Plugins/JSL/x86/JoyShockLibrary.exp b/Assets/Plugins/JSL/x86/JoyShockLibrary.exp
deleted file mode 100644
index 99426f698ff3a9d618d818bd191a84a5dde066f2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 9105
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
diff --git a/Assets/Plugins/JSL/x86/JoyShockLibrary.exp.meta b/Assets/Plugins/JSL/x86/JoyShockLibrary.exp.meta
deleted file mode 100644
index d692a168e..000000000
--- a/Assets/Plugins/JSL/x86/JoyShockLibrary.exp.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 47521c369c3ab7849aeb7e479bad21ea
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Plugins/JSL/x86/JoyShockLibrary.lib b/Assets/Plugins/JSL/x86/JoyShockLibrary.lib
deleted file mode 100644
index cb0f0e87ae22386011bd383dc0a4232e6f922d3b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 16156
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 addedPitchChanges = new List();
+
// Song beats per minute
// This is determined by the song you're trying to sync up to
public float songBpm;
@@ -25,18 +34,19 @@ namespace HeavenStudio
// Current song position, in seconds
private double songPos; // for Conductor use only
- public float songPosition => (float) songPos;
+ public float songPosition => (float)songPos;
public double songPositionAsDouble => songPos;
// Current song position, in beats
public double songPosBeat; // for Conductor use only
- public float songPositionInBeats => (float) songPosBeat;
+ public float songPositionInBeats => (float)songPosBeat;
public double songPositionInBeatsAsDouble => songPosBeat;
// Current time of the song
private double time;
- double dspTime, lastDspTime;
- double absTime, lastAbsTime;
+ double dspTime;
+ double absTime, absTimeAdjust;
+ double dspMargin = 128 / 44100.0;
// the dspTime we started at
private double dspStart;
@@ -45,6 +55,7 @@ namespace HeavenStudio
DateTime startTime;
//the beat we started at
+ private double startPos;
private double startBeat;
public double startBeatAsDouble => startBeat;
@@ -59,7 +70,7 @@ namespace HeavenStudio
// Conductor is currently playing song
public bool isPlaying;
-
+
// Conductor is currently paused, but not fully stopped
public bool isPaused;
@@ -79,16 +90,40 @@ namespace HeavenStudio
public void SetTimelinePitch(float pitch)
{
+ if (pitch != 0 && pitch * minigamePitch != SongPitch)
+ {
+ Debug.Log("added pitch change " + pitch * minigamePitch + " at" + absTime);
+ addedPitchChanges.Add(new AddedPitchChange { time = absTime, pitch = pitch * minigamePitch });
+ }
+
timelinePitch = pitch;
musicSource.pitch = SongPitch;
+
}
public void SetMinigamePitch(float pitch)
{
+ if (pitch != 0 && pitch * timelinePitch != SongPitch)
+ {
+ Debug.Log("added pitch change " + pitch * timelinePitch + " at" + absTime);
+ addedPitchChanges.Add(new AddedPitchChange { time = absTime, pitch = pitch * timelinePitch });
+ }
+
minigamePitch = pitch;
musicSource.pitch = SongPitch;
}
+ public void SetMinigamePitch(float pitch, double beat)
+ {
+ BeatAction.New( this,
+ new List {
+ new BeatAction.Action(beat, delegate {
+ SetMinigamePitch(pitch);
+ }),
+ }
+ );
+ }
+
void Awake()
{
instance = this;
@@ -97,13 +132,16 @@ namespace HeavenStudio
void Start()
{
musicSource.priority = 0;
+ AudioConfiguration config = AudioSettings.GetConfiguration();
+ dspMargin = 2 * (config.dspBufferSize / (double)config.sampleRate);
+ addedPitchChanges.Clear();
}
public void SetBeat(double beat)
{
var chart = GameManager.instance.Beatmap;
double offset = chart.data.offset;
- double startPos = GetSongPosFromBeat(beat);
+ startPos = GetSongPosFromBeat(beat);
double dspTime = AudioSettings.dspTime;
@@ -113,19 +151,32 @@ namespace HeavenStudio
SeekMusicToTime(startPos);
songPosBeat = GetBeatFromSongPos(time);
-
+
GameManager.instance.SetCurrentEventToClosest(beat);
}
public void Play(double beat)
{
if (isPlaying) return;
+
+ if (!isPaused)
+ {
+ AudioConfiguration config = AudioSettings.GetConfiguration();
+ dspMargin = 2 * (config.dspBufferSize / (double)config.sampleRate);
+ addedPitchChanges.Clear();
+ addedPitchChanges.Add(new AddedPitchChange { time = 0, pitch = SongPitch });
+ }
+
var chart = GameManager.instance.Beatmap;
double offset = chart.data.offset;
double dspTime = AudioSettings.dspTime;
+ absTimeAdjust = 0;
+ dspStart = dspTime;
+ startTime = DateTime.Now;
+
GameManager.instance.SortEventsList();
- double startPos = GetSongPosFromBeat(beat);
+ startPos = GetSongPosFromBeat(beat);
firstBeatOffset = offset;
time = startPos;
@@ -149,12 +200,8 @@ namespace HeavenStudio
}
songPosBeat = GetBeatFromSongPos(time);
- startTime = DateTime.Now;
- lastAbsTime = 0;
- lastDspTime = AudioSettings.dspTime;
- dspStart = dspTime;
startBeat = songPosBeat;
-
+
isPlaying = true;
isPaused = false;
}
@@ -170,10 +217,15 @@ namespace HeavenStudio
public void Stop(double time)
{
+ if (absTimeAdjust != 0)
+ {
+ Debug.Log($"Last playthrough had a dsp (audio) drift of {absTimeAdjust}.\nConsider increasing audio buffer size if audio distortion was present.");
+ }
this.time = time;
songPos = time;
songPosBeat = 0;
+ absTimeAdjust = 0;
isPlaying = false;
isPaused = false;
@@ -181,13 +233,48 @@ namespace HeavenStudio
musicSource.Stop();
}
- void SeekMusicToTime(double startPos)
+ ///
+ /// stops playback of the audio without stopping beatkeeping
+ ///
+ public void StopOnlyAudio()
+ {
+ musicSource.Stop();
+ }
+
+ ///
+ /// fades out the audio over a duration
+ ///
+ /// duration of the fade
+ public void FadeOutAudio(float duration)
+ {
+ StartCoroutine(FadeOutAudioCoroutine(duration));
+ }
+
+ IEnumerator FadeOutAudioCoroutine(float duration)
+ {
+ float startVolume = musicSource.volume;
+ float endVolume = 0f;
+ float startTime = Time.time;
+ float endTime = startTime + duration;
+
+ while (Time.time < endTime)
+ {
+ float t = (Time.time - startTime) / duration;
+ musicSource.volume = Mathf.Lerp(startVolume, endVolume, t);
+ yield return null;
+ }
+
+ musicSource.volume = endVolume;
+ StopOnlyAudio();
+ }
+
+ void SeekMusicToTime(double fStartPos)
{
double offset = GameManager.instance.Beatmap.data.offset;
- if (musicSource.clip != null && startPos < musicSource.clip.length - offset)
+ if (musicSource.clip != null && fStartPos < musicSource.clip.length - offset)
{
// https://www.desmos.com/calculator/81ywfok6xk
- double musicStartDelay = -offset - startPos;
+ double musicStartDelay = -offset - fStartPos;
if (musicStartDelay > 0)
{
musicSource.timeSamples = 0;
@@ -195,24 +282,13 @@ namespace HeavenStudio
else
{
int freq = musicSource.clip.frequency;
- int samples = (int)(freq * (startPos + offset));
+ int samples = (int)(freq * (fStartPos + offset));
musicSource.timeSamples = samples;
}
}
}
- double deltaTimeReal { get {
- double ret = absTime - lastAbsTime;
- lastAbsTime = absTime;
- return ret;
- }}
- double deltaTimeDsp { get {
- double ret = dspTime - lastDspTime;
- lastDspTime = dspTime;
- return ret;
- }}
-
public void Update()
{
if (isPlaying)
@@ -229,24 +305,54 @@ namespace HeavenStudio
musicSource.UnPause();
musicScheduledPitch = SongPitch;
- musicScheduledTime = (AudioSettings.dspTime + (-GameManager.instance.Beatmap.data.offset - songPositionAsDouble)/(double)SongPitch);
+ musicScheduledTime = (AudioSettings.dspTime + (-GameManager.instance.Beatmap.data.offset - songPositionAsDouble) / (double)SongPitch);
musicSource.SetScheduledStartTime(musicScheduledTime);
}
}
absTime = (DateTime.Now - startTime).TotalSeconds;
+
+ //dspTime to sync with audio thread in case of drift
dspTime = AudioSettings.dspTime - dspStart;
- double dt = deltaTimeReal;
+ if (Math.Abs(absTime + absTimeAdjust - dspTime) > dspMargin)
+ {
+ int i = 0;
+ while (Math.Abs(absTime + absTimeAdjust - dspTime) > dspMargin)
+ {
+ i++;
+ absTimeAdjust = (dspTime - absTime + absTimeAdjust) * 0.5;
+ if (i > 8) break;
+ }
+ }
- //todo: dspTime to sync with audio thread in case of drift
+ time = MapTimeToPitchChanges(absTime + absTimeAdjust);
- time += dt * SongPitch;
-
- songPos = time;
+ songPos = startPos + time;
songPosBeat = GetBeatFromSongPos(songPos);
}
}
+ double MapTimeToPitchChanges(double time)
+ {
+ double counter = 0;
+ double lastChangeTime = 0;
+ float pitch = addedPitchChanges[0].pitch;
+ foreach (var pch in addedPitchChanges)
+ {
+ double changeTime = pch.time;
+ if (changeTime > time)
+ {
+ break;
+ }
+
+ counter += (changeTime - lastChangeTime) * pitch;
+ lastChangeTime = changeTime;
+ pitch = pch.pitch;
+ }
+
+ counter += (time - lastChangeTime) * pitch;
+ return counter;
+ }
public void LateUpdate()
{
@@ -265,7 +371,7 @@ namespace HeavenStudio
{
if (metronomeSound != null)
{
- metronomeSound.Delete();
+ metronomeSound.Stop();
metronomeSound = null;
}
}
@@ -352,53 +458,53 @@ namespace HeavenStudio
break;
}
- counter += (t.beat - lastTempoChangeBeat) * 60/bpm;
+ counter += (t.beat - lastTempoChangeBeat) * 60 / bpm;
bpm = t["tempo"];
lastTempoChangeBeat = t.beat;
}
- counter += (beat - lastTempoChangeBeat) * 60/bpm;
+ counter += (beat - lastTempoChangeBeat) * 60 / bpm;
return counter;
}
//thank you @wooningcharithri#7419 for the psuedo-code
- public double BeatsToSecs(double beats, float bpm)
- {
- return beats / bpm * 60f;
- }
- public double SecsToBeats(double s, float bpm)
- {
- return s / 60f * bpm;
- }
+ public double BeatsToSecs(double beats, float bpm)
+ {
+ return beats / bpm * 60f;
+ }
+ public double SecsToBeats(double s, float bpm)
+ {
+ return s / 60f * bpm;
+ }
- public double GetBeatFromSongPos(double seconds)
- {
- double lastTempoChangeBeat = 0f;
- double counterSeconds = 0;
- float lastBpm = 120f;
-
- foreach (RiqEntity t in GameManager.instance.Beatmap.TempoChanges)
- {
- double beatToNext = t.beat - lastTempoChangeBeat;
- double secToNext = BeatsToSecs(beatToNext, lastBpm);
- double nextSecs = counterSeconds + secToNext;
+ public double GetBeatFromSongPos(double seconds)
+ {
+ double lastTempoChangeBeat = 0f;
+ double counterSeconds = 0;
+ float lastBpm = 120f;
- if (nextSecs >= seconds)
- break;
-
- lastTempoChangeBeat = t.beat;
- lastBpm = t["tempo"];
- counterSeconds = nextSecs;
- }
- return lastTempoChangeBeat + SecsToBeats(seconds - counterSeconds, lastBpm);
+ foreach (RiqEntity t in GameManager.instance.Beatmap.TempoChanges)
+ {
+ double beatToNext = t.beat - lastTempoChangeBeat;
+ double secToNext = BeatsToSecs(beatToNext, lastBpm);
+ double nextSecs = counterSeconds + secToNext;
+
+ if (nextSecs >= seconds)
+ break;
+
+ lastTempoChangeBeat = t.beat;
+ lastBpm = t["tempo"];
+ counterSeconds = nextSecs;
}
+ return lastTempoChangeBeat + SecsToBeats(seconds - counterSeconds, lastBpm);
+ }
//
// convert real seconds to beats
public double GetRestFromRealTime(double seconds)
{
- return seconds/pitchedSecPerBeat;
+ return seconds / pitchedSecPerBeat;
}
public void SetBpm(float bpm)
diff --git a/Assets/Scripts/GameInitializer.cs b/Assets/Scripts/GameInitializer.cs
index ff110493e..4d56edb42 100644
--- a/Assets/Scripts/GameInitializer.cs
+++ b/Assets/Scripts/GameInitializer.cs
@@ -88,6 +88,7 @@ namespace HeavenStudio
AudioSource source = Conductor.AddComponent();
Conductor.AddComponent();
Conductor.GetComponent().musicSource = source;
+ source.priority = 255;
source.outputAudioMixerGroup = Settings.GetMusicMixer();
// Conductor.AddComponent();
diff --git a/Assets/Scripts/GameManager.cs b/Assets/Scripts/GameManager.cs
index 419473c8d..7bfaf1f9d 100644
--- a/Assets/Scripts/GameManager.cs
+++ b/Assets/Scripts/GameManager.cs
@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
+using UnityEngine.Pool;
using Starpelly;
using Jukebox;
@@ -14,10 +15,13 @@ namespace HeavenStudio
{
public class GameManager : MonoBehaviour
{
+ const int SoundPoolSizeMin = 32;
+ const int SoundPoolSizeMax = 32;
+
[Header("Lists")]
[NonSerialized] public RiqBeatmap Beatmap = new();
- private List preloadedGames = new List();
- [NonSerialized] public List SoundObjects = new List();
+ private List preloadedGames = new();
+ [NonSerialized] public ObjectPool SoundObjects;
[Header("Components")]
[NonSerialized] public Camera GameCamera, CursorCam, OverlayCamera, StaticCamera;
@@ -34,7 +38,8 @@ namespace HeavenStudio
[NonSerialized] public string txt = null;
[NonSerialized] public string ext = null;
- [NonSerialized] public int currentEvent, currentTempoEvent, currentVolumeEvent, currentSectionEvent,
+ [NonSerialized]
+ public int currentEvent, currentTempoEvent, currentVolumeEvent, currentSectionEvent,
currentPreEvent, currentPreSwitch, currentPreSequence;
[NonSerialized] public double endBeat;
[NonSerialized] public float startOffset;
@@ -49,6 +54,8 @@ namespace HeavenStudio
bool AudioLoadDone;
bool ChartLoadError;
+ List eventBeats, tempoBeats, volumeBeats, sectionBeats;
+
public event Action onBeatChanged;
public event Action onSectionChange;
@@ -109,7 +116,7 @@ namespace HeavenStudio
{
AudioLoadDone = false;
ChartLoadError = false;
- currentPreEvent= 0;
+ currentPreEvent = 0;
currentPreSwitch = 0;
currentPreSequence = 0;
@@ -138,7 +145,9 @@ namespace HeavenStudio
GoForAPerfect.instance.Disable();
/////
-
+
+ SoundObjects = new ObjectPool(CreatePooledSound, OnTakePooledSound, OnReturnPooledSound, OnDestroyPooledSound, true, SoundPoolSizeMin, SoundPoolSizeMax);
+
if (preLoaded)
{
@@ -168,8 +177,42 @@ namespace HeavenStudio
}
}
+ Sound CreatePooledSound()
+ {
+ GameObject oneShot = new GameObject($"Pooled Scheduled Sound");
+ oneShot.transform.SetParent(transform);
+
+ AudioSource audioSource = oneShot.AddComponent();
+ audioSource.playOnAwake = false;
+
+ Sound snd = oneShot.AddComponent();
+
+ oneShot.SetActive(false);
+
+ return snd;
+ }
+
+ // Called when an item is returned to the pool using Release
+ void OnReturnPooledSound(Sound snd)
+ {
+ snd.Stop();
+ }
+
+ // Called when an item is taken from the pool using Get
+ void OnTakePooledSound(Sound snd)
+ {
+ snd.gameObject.SetActive(true);
+ }
+
+ // If the pool capacity is reached then any items returned will be destroyed.
+ void OnDestroyPooledSound(Sound snd)
+ {
+ snd.Stop();
+ Destroy(snd.gameObject);
+ }
+
public void NewRemix()
- {
+ {
AudioLoadDone = false;
Beatmap = new("1", "HeavenStudio");
Beatmap.data.properties = Minigames.propertiesModel;
@@ -260,7 +303,7 @@ namespace HeavenStudio
if (Beatmap.data.riqOrigin != "HeavenStudio")
{
string origin = Beatmap.data.riqOrigin?.DisplayName() ?? "Unknown Origin";
- GlobalGameManager.ShowErrorMessage("Warning",
+ GlobalGameManager.ShowErrorMessage("Warning",
$"This chart came from\n{origin}\nand uses content not included in Heaven Studio.\n\nYou may be able to edit this chart in Heaven Studio to be used in its original program.");
}
}
@@ -275,7 +318,7 @@ namespace HeavenStudio
{
SkillStarManager.instance.KillStar();
}
-
+
if (SkillStarManager.instance.IsEligible && !skillStarCollected && accuracy >= 1f)
{
if (SkillStarManager.instance.DoStarJust())
@@ -289,16 +332,26 @@ namespace HeavenStudio
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
- var gameSwitchs = Beatmap.Entities.FindAll(c => c.datamodel.Split(1) == "switchGame");
+ foreach (RiqEntity entity in Beatmap.Entities)
+ {
+ if (entity.datamodel.Split(1) == "switchGame")
+ {
+ gameSwitchs.Add(entity);
+ }
+ }
if (currentPreSwitch < gameSwitchs.Count && currentPreSwitch >= 0)
{
if (start + seekTime >= gameSwitchs[currentPreSwitch].beat)
{
string gameName = gameSwitchs[currentPreSwitch].datamodel.Split(2);
- var inf = GetGameInfo(gameName);
- if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded)
+ inf = GetGameInfo(gameName);
+ if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded)
{
Debug.Log($"ASYNC loading assetbundles for game {gameName}");
StartCoroutine(inf.LoadCommonAssetBundleAsync());
@@ -308,18 +361,23 @@ namespace HeavenStudio
}
}
//then check game entities
- List entities = Beatmap.Entities.Select(c => c.beat).ToList();
if (currentPreEvent < Beatmap.Entities.Count && currentPreEvent >= 0)
{
- if (start + seekTime >= entities[currentPreEvent])
+ if (start + seekTime >= eventBeats[currentPreEvent])
{
- var entitiesAtSameBeat = Beatmap.Entities.FindAll(c => c.beat == Beatmap.Entities[currentPreEvent].beat && !EventCaller.FXOnlyGames().Contains(EventCaller.instance.GetMinigame(c.datamodel.Split('/')[0])));
+ foreach (RiqEntity entity in Beatmap.Entities)
+ {
+ if (entity.beat == Beatmap.Entities[currentPreEvent].beat && !EventCaller.FXOnlyGames().Contains(eventCaller.GetMinigame(entity.datamodel.Split('/')[0])))
+ {
+ entitiesAtSameBeat.Add(entity);
+ }
+ }
SortEventsByPriority(entitiesAtSameBeat);
foreach (RiqEntity entity in entitiesAtSameBeat)
{
string gameName = entity.datamodel.Split('/')[0];
- var inf = GetGameInfo(gameName);
- if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded)
+ inf = GetGameInfo(gameName);
+ if (inf != null && inf.usesAssetBundle && !inf.AssetsLoaded)
{
Debug.Log($"ASYNC loading assetbundles for game {gameName}");
StartCoroutine(inf.LoadCommonAssetBundleAsync());
@@ -329,29 +387,38 @@ namespace HeavenStudio
}
}
}
+
+ ListPool.Release(entitiesAtSameBeat);
+ ListPool.Release(gameSwitchs);
}
public void SeekAheadAndDoPreEvent(double start)
{
- List entities = Beatmap.Entities.Select(c => c.beat).ToList();
if (currentPreSequence < Beatmap.Entities.Count && currentPreSequence >= 0)
{
- var seekEntity = Beatmap.Entities[currentPreSequence];
+ List entitiesAtSameBeat = ListPool.Get();
+ RiqEntity seekEntity = Beatmap.Entities[currentPreSequence];
- float seekTime = EventCaller.instance.GetGameAction(
- EventCaller.instance.GetMinigame(seekEntity.datamodel.Split(0)), seekEntity.datamodel.Split(1)).preFunctionLength;
-
- if (start + seekTime >= entities[currentPreSequence])
+ foreach (RiqEntity entity in Beatmap.Entities)
+ {
+ if (entity.beat == seekEntity.beat)
+ {
+ entitiesAtSameBeat.Add(entity);
+ }
+ }
+ SortEventsByPriority(entitiesAtSameBeat);
+
+ float seekTime = eventCaller.GetGameAction(
+ eventCaller.GetMinigame(seekEntity.datamodel.Split(0)), seekEntity.datamodel.Split(1)).preFunctionLength;
+
+ if (start + seekTime >= eventBeats[currentPreSequence])
{
- double beat = seekEntity.beat;
- var entitiesAtSameBeat = Beatmap.Entities.FindAll(c => c.beat == seekEntity.beat);
- SortEventsByPriority(entitiesAtSameBeat);
foreach (RiqEntity entity in entitiesAtSameBeat)
{
currentPreSequence++;
string gameName = entity.datamodel.Split('/')[0];
var inf = GetGameInfo(gameName);
- if (inf != null && inf.usesAssetBundle && inf.AssetsLoaded && !inf.SequencesPreloaded)
+ if (inf != null && inf.usesAssetBundle && inf.AssetsLoaded && !inf.SequencesPreloaded)
{
Debug.Log($"Preloading game {gameName}");
PreloadGameSequences(gameName);
@@ -359,6 +426,7 @@ namespace HeavenStudio
eventCaller.CallPreEvent(entity);
}
}
+ ListPool.Release(entitiesAtSameBeat);
}
}
@@ -369,33 +437,28 @@ namespace HeavenStudio
if (!Conductor.instance.isPlaying)
return;
Conductor cond = Conductor.instance;
-
- List entities = Beatmap.Entities.Select(c => c.beat).ToList();
- List tempoChanges = Beatmap.TempoChanges.Select(c => c.beat).ToList();
if (currentTempoEvent < Beatmap.TempoChanges.Count && currentTempoEvent >= 0)
{
- if (cond.songPositionInBeatsAsDouble >= tempoChanges[currentTempoEvent])
+ if (cond.songPositionInBeatsAsDouble >= tempoBeats[currentTempoEvent])
{
cond.SetBpm(Beatmap.TempoChanges[currentTempoEvent]["tempo"]);
currentTempoEvent++;
}
}
- List volumeChanges = Beatmap.VolumeChanges.Select(c => c.beat).ToList();
if (currentVolumeEvent < Beatmap.VolumeChanges.Count && currentVolumeEvent >= 0)
{
- if (cond.songPositionInBeatsAsDouble >= volumeChanges[currentVolumeEvent])
+ if (cond.songPositionInBeatsAsDouble >= volumeBeats[currentVolumeEvent])
{
cond.SetVolume(Beatmap.VolumeChanges[currentVolumeEvent]["volume"]);
currentVolumeEvent++;
}
}
- List chartSections = Beatmap.SectionMarkers.Select(c => c.beat).ToList();
if (currentSectionEvent < Beatmap.SectionMarkers.Count && currentSectionEvent >= 0)
{
- if (cond.songPositionInBeatsAsDouble >= chartSections[currentSectionEvent])
+ if (cond.songPositionInBeatsAsDouble >= sectionBeats[currentSectionEvent])
{
Debug.Log("Section " + Beatmap.SectionMarkers[currentSectionEvent]["sectionName"] + " started");
currentSection = Beatmap.SectionMarkers[currentSectionEvent];
@@ -416,19 +479,35 @@ namespace HeavenStudio
if (currentEvent < Beatmap.Entities.Count && currentEvent >= 0)
{
- if (cond.songPositionInBeatsAsDouble >= entities[currentEvent])
+ if (cond.songPositionInBeatsAsDouble >= eventBeats[currentEvent])
{
+ List entitiesAtSameBeat = ListPool.Get();
+ List fxEntities = ListPool.Get();
+
// allows for multiple events on the same beat to be executed on the same frame, so no more 1-frame delay
- var entitiesAtSameBeat = Beatmap.Entities.FindAll(c => c.beat == Beatmap.Entities[currentEvent].beat && !EventCaller.FXOnlyGames().Contains(EventCaller.instance.GetMinigame(c.datamodel.Split('/')[0])));
- var fxEntities = Beatmap.Entities.FindAll(c => c.beat == Beatmap.Entities[currentEvent].beat && EventCaller.FXOnlyGames().Contains(EventCaller.instance.GetMinigame(c.datamodel.Split('/')[0])));
+ using (PooledObject