The application was experiencing frame drops with the error "Skipped 31 frames! The application may be doing too much work on its main thread."
Problem: Each bubble pop created 8 particles with complex animations (scale, move, fade, gravity) lasting 500ms.
Solution:
lib/game/components/bubble.dartProblem: Accelerometer data was being processed at 10Hz (every 100ms) and immediately applied to all active bubbles.
Solutions:
Future.delayed(Duration.zero) between batchesProblem: Audio playback was synchronous and could block the main thread.
Solution:
await calls from main playback method to prevent blockinglib/game/audio/audio_manager.dartProblem: Bubble cleanup was running every frame in the update loop.
Solution:
lib/game/components/bubble_spawner.dart| Optimization | Before | After | Impact |
|---|---|---|---|
| Particle Count | 8 per pop | 4 per pop | 50% reduction |
| Particle Duration | 500ms | 300ms | 40% faster cleanup |
| Tilt Sampling Rate | 10Hz | 5Hz | 50% fewer sensor events |
| Tilt Force Application | Immediate | Throttled to 10Hz + batched | Prevents frame blocking |
| Audio Operations | Synchronous | Asynchronous | No main thread blocking |
| Bubble Cleanup | Every frame (~60Hz) | Every 0.5s (2Hz) | 97% reduction in cleanup frequency |
These optimizations should significantly reduce main thread blocking and eliminate the frame skipping issues. The game should now maintain a smooth 60 FPS even with multiple bubbles and simultaneous interactions.
// Before: Blocking
await heavyOperation();
// After: Non-blocking
_heavyOperationAsync();
void _heavyOperationAsync() async {
// Heavy work here
}
// Process items in small batches with yielding
const batchSize = 3;
for (int i = 0; i < items.length; i += batchSize) {
final batch = items.sublist(i, endIndex);
// Process batch
if (endIndex < items.length) {
await Future.delayed(Duration.zero); // Yield control
}
}
double _lastUpdateTime = 0.0;
static const double updateInterval = 0.1;
void onEvent() {
final currentTime = DateTime.now().millisecondsSinceEpoch / 1000.0;
if (currentTime - _lastUpdateTime >= updateInterval) {
_lastUpdateTime = currentTime;
// Process event
}
}
The implementation successfully uses Flutter Flame game engine with:
To monitor performance improvements:
flutter run --profile