public void Update() { AllowSpinnerOptimisation = false; List <HitObject> activeObjects = ActiveStreamObjects; if (activeObjects == null) { return; } int lowestActiveObject = -1; ProcessTo = activeObjects.Count - 1; //initialise to the last object. if we don't find an earlier one below, this will be used. ActiveObject = null; NextObject = null; for (int i = ProcessFrom; i < activeObjects.Count; i++) { HitObject h = activeObjects[i]; int hitObjectNow = h.ClockingNow; if (h.IsVisible || !h.IsHit) { h.Update(); if (h.StartTime <= hitObjectNow && h.EndTime > hitObjectNow) { ActiveObject = h; } else if (h.StartTime > hitObjectNow) { if (NextObject == null && !h.IsHit) { NextObject = h; } } if (!AllowSpinnerOptimisation) { AllowSpinnerOptimisation |= h is Spinner && h.Sprites[0].Alpha == 1; } if (Player.Autoplay && !h.IsHit && hitObjectNow >= h.StartTime) { TriggerScoreChange(h.Hit(), h); } if (Clock.AudioTimeSource.IsElapsing) { TriggerScoreChange(h.CheckScoring(), h); } if (lowestActiveObject < 0) { lowestActiveObject = i; } } else { if (h is Slider s && s.EndTime < hitObjectNow) { s.DisposePathTexture(); } } if (h.StartTime > hitObjectNow + 4000 && !h.IsVisible) { ProcessTo = i; break; //stop processing after a decent amount of leeway... } } if (lowestActiveObject >= 0) { ProcessFrom = lowestActiveObject; } if (nextStreamChange > 0 && nextStreamChange <= Clock.AudioTime) { if (OnStreamChanged != null) { OnStreamChanged(ActiveStream); } nextStreamChange = 0; } streamSpriteManagers[(int)(ActiveStream)].Update(); spriteManager.Update(); }