@@ -172,10 +172,14 @@ class PlayField extends FlxTypedGroup<FlxBasic>
172172
173173 // Performance optimization fields
174174 private var sustainUpdateCounter : Int = 0 ;
175- private static inline var SUSTAIN_UPDATE_INTERVAL : Int = 2 ; // Update sustains every 2 frames
175+ private var dynamicSustainInterval : Int = 2 ; // Adaptive sustain update interval
176176 private var heldNotes : Array <Note > = []; // Cache of currently held notes
177177 private var receptorAnimStates : Array <String > = []; // Track receptor animation states
178178 private var lastSustainUpdate : Float = 0 ;
179+ private var frameTimeAccumulator : Float = 0 ;
180+ private var lastFrameTime : Float = 0 ;
181+ private var avgFrameTime : Float = 0.0167 ; // Start assuming 60 FPS
182+ private var frameCounter : Int = 0 ;
179183
180184 public var keysPressed : Array <Bool > = [false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ]; // what keys are pressed rn
181185 public var isHolding : Array <Bool > = [false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ,false ];
@@ -1002,8 +1006,13 @@ class PlayField extends FlxTypedGroup<FlxBasic>
10021006 }
10031007 }
10041008
1005- while (column .length > 0 && column [0 ].strumTime - Conductor .songPosition < time )
1009+ // Limit note spawning per frame at high framerates to prevent spikes
1010+ var maxSpawnsPerFrame = dynamicSustainInterval == 4 ? 2 : (dynamicSustainInterval == 3 ? 3 : 5 );
1011+ var spawned = 0 ;
1012+ while (column .length > 0 && column [0 ].strumTime - Conductor .songPosition < time && spawned < maxSpawnsPerFrame ) {
10061013 ((column [0 ].spawned ) ? column .remove (column [0 ]) : spawnNote (column [0 ]));
1014+ spawned ++ ;
1015+ }
10071016 }
10081017 }
10091018
@@ -1012,9 +1021,31 @@ class PlayField extends FlxTypedGroup<FlxBasic>
10121021 for (obj in strumNotes )
10131022 modManager .updateObject (curDecBeat , obj , modNumber );
10141023
1015- // Performance optimization: Update sustains less frequently
1024+ // Adaptive performance optimization based on framerate
1025+ var currentTime = haxe. Timer .stamp ();
1026+ if (lastFrameTime > 0 ) {
1027+ var frameTime = currentTime - lastFrameTime ;
1028+ frameTimeAccumulator + = frameTime ;
1029+ frameCounter ++ ;
1030+
1031+ // Update average every 30 frames for stability
1032+ if (frameCounter >= 30 ) {
1033+ avgFrameTime = frameTimeAccumulator / frameCounter ;
1034+ var fps = 1.0 / avgFrameTime ;
1035+
1036+ // Adaptive intervals: Higher FPS = less frequent heavy updates
1037+ if (fps > 100 ) dynamicSustainInterval = 4 ;
1038+ else if (fps > 80 ) dynamicSustainInterval = 3 ;
1039+ else dynamicSustainInterval = 2 ;
1040+
1041+ frameTimeAccumulator = 0 ;
1042+ frameCounter = 0 ;
1043+ }
1044+ }
1045+ lastFrameTime = currentTime ;
1046+
10161047 sustainUpdateCounter ++ ;
1017- var shouldUpdateSustains = sustainUpdateCounter >= SUSTAIN_UPDATE_INTERVAL ;
1048+ var shouldUpdateSustains = sustainUpdateCounter >= dynamicSustainInterval ;
10181049 if (shouldUpdateSustains ) {
10191050 sustainUpdateCounter = 0 ;
10201051 lastSustainUpdate = Conductor .songPosition ;
@@ -1073,9 +1104,9 @@ class PlayField extends FlxTypedGroup<FlxBasic>
10731104 receptorAnimStates [daNote .column ] = " static" ;
10741105 }
10751106
1076- // Only update heavy sustain logic periodically
1107+ // Only update heavy sustain logic periodically with adaptive timing
10771108 if (shouldUpdateSustains ) {
1078- updateHeldNoteLogic (daNote , elapsed * SUSTAIN_UPDATE_INTERVAL );
1109+ updateHeldNoteLogic (daNote , elapsed * dynamicSustainInterval );
10791110 }
10801111 } else {
10811112 // Note finished or was never held
@@ -1477,9 +1508,10 @@ class PlayField extends FlxTypedGroup<FlxBasic>
14771508 {
14781509 if (daNote .unhitTail .length == 0 ) return ;
14791510
1480- // Process tails with batching to avoid frame spikes
1511+ // Adaptive tail processing based on framerate
14811512 var processedCount = 0 ;
1482- var maxProcessPerFrame = 3 ; // Limit processing to avoid frame spikes
1513+ // Reduce batch size at higher framerates to spread work across more frames
1514+ var maxProcessPerFrame = dynamicSustainInterval == 4 ? 1 : (dynamicSustainInterval == 3 ? 2 : 3 );
14831515
14841516 // Process from beginning each time (simpler but still efficient for small arrays)
14851517 for (i in 0 ... daNote .unhitTail .length ) {
0 commit comments