|
|
@@ -0,0 +1,119 @@
|
|
|
+# ZenTap Performance Optimizations
|
|
|
+
|
|
|
+## Problem Identified
|
|
|
+The application was experiencing frame drops with the error "Skipped 31 frames! The application may be doing too much work on its main thread."
|
|
|
+
|
|
|
+## Root Causes and Solutions
|
|
|
+
|
|
|
+### 1. Excessive Particle Effects on Bubble Pop
|
|
|
+**Problem**: Each bubble pop created 8 particles with complex animations (scale, move, fade, gravity) lasting 500ms.
|
|
|
+
|
|
|
+**Solution**:
|
|
|
+- Reduced particles from 8 to 4
|
|
|
+- Simplified effects by combining movement and gravity into single effect
|
|
|
+- Reduced animation duration from 500ms to 300ms
|
|
|
+- Removed scale effects to reduce computational load
|
|
|
+- **File**: [`lib/game/components/bubble.dart`](lib/game/components/bubble.dart:324)
|
|
|
+
|
|
|
+### 2. High-Frequency Tilt Detection Updates
|
|
|
+**Problem**: Accelerometer data was being processed at 10Hz (every 100ms) and immediately applied to all active bubbles.
|
|
|
+
|
|
|
+**Solutions**:
|
|
|
+- Reduced accelerometer sampling rate from 10Hz to 5Hz (200ms intervals)
|
|
|
+- Added throttling to tilt force application (max 10 updates per second)
|
|
|
+- Implemented batched processing of bubble tilt forces to prevent frame blocking
|
|
|
+- Added async processing with `Future.delayed(Duration.zero)` between batches
|
|
|
+- **Files**:
|
|
|
+ - [`lib/utils/tilt_detector.dart`](lib/utils/tilt_detector.dart:33)
|
|
|
+ - [`lib/game/zentap_game.dart`](lib/game/zentap_game.dart:102)
|
|
|
+
|
|
|
+### 3. Blocking Audio Operations
|
|
|
+**Problem**: Audio playback was synchronous and could block the main thread.
|
|
|
+
|
|
|
+**Solution**:
|
|
|
+- Made audio playback completely asynchronous
|
|
|
+- Split audio operations into separate async methods
|
|
|
+- Removed `await` calls from main playback method to prevent blocking
|
|
|
+- **File**: [`lib/game/audio/audio_manager.dart`](lib/game/audio/audio_manager.dart:95)
|
|
|
+
|
|
|
+### 4. Frequent Bubble Cleanup Operations
|
|
|
+**Problem**: Bubble cleanup was running every frame in the update loop.
|
|
|
+
|
|
|
+**Solution**:
|
|
|
+- Changed cleanup frequency from every frame to every 0.5 seconds
|
|
|
+- Added cleanup timer to track cleanup intervals
|
|
|
+- **File**: [`lib/game/components/bubble_spawner.dart`](lib/game/components/bubble_spawner.dart:23)
|
|
|
+
|
|
|
+## Performance Improvements Summary
|
|
|
+
|
|
|
+| 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 |
|
|
|
+
|
|
|
+## Expected Results
|
|
|
+
|
|
|
+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.
|
|
|
+
|
|
|
+## Technical Details
|
|
|
+
|
|
|
+### Async Processing Pattern
|
|
|
+```dart
|
|
|
+// Before: Blocking
|
|
|
+await heavyOperation();
|
|
|
+
|
|
|
+// After: Non-blocking
|
|
|
+_heavyOperationAsync();
|
|
|
+
|
|
|
+void _heavyOperationAsync() async {
|
|
|
+ // Heavy work here
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Batched Processing Pattern
|
|
|
+```dart
|
|
|
+// 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
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Throttling Pattern
|
|
|
+```dart
|
|
|
+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
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Flutter Flame Integration
|
|
|
+
|
|
|
+The implementation successfully uses Flutter Flame game engine with:
|
|
|
+- **Flame Components**: Custom bubble and spawner components
|
|
|
+- **Flame Effects**: Optimized scale, opacity, and movement effects
|
|
|
+- **Flame Audio**: Audio pools for efficient sound playback
|
|
|
+- **Flame Collision Detection**: Circle hitboxes for bubble interactions
|
|
|
+- **Async Support**: Proper async/await patterns that don't block the main thread
|
|
|
+
|
|
|
+## Monitoring
|
|
|
+
|
|
|
+To monitor performance improvements:
|
|
|
+1. Check Flutter Inspector for frame rendering times
|
|
|
+2. Monitor console for frame skip warnings
|
|
|
+3. Use performance overlay: `flutter run --profile`
|
|
|
+4. Watch CPU usage during bubble interactions and tilt movements
|