bubble_spawner.dart 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import 'dart:math';
  2. import 'package:flame/components.dart';
  3. import 'bubble.dart';
  4. class BubbleSpawner extends Component with HasGameReference {
  5. static const double baseSpawnInterval = 1.5; // base seconds
  6. static const double spawnVariation = 1.0; // +/- variation in seconds
  7. static const int maxBubbles = 8;
  8. double _timeSinceLastSpawn = 0;
  9. double _nextSpawnTime = 0;
  10. final List<Bubble> _activeBubbles = [];
  11. final Random _random = Random();
  12. Function(Bubble, bool)? onBubblePopped;
  13. bool isActive = true;
  14. BubbleSpawner({this.onBubblePopped}) {
  15. _calculateNextSpawnTime();
  16. }
  17. double _lastCleanupTime = 0;
  18. static const double cleanupInterval = 0.5; // Clean up bubbles every 0.5 seconds instead of every frame
  19. @override
  20. void update(double dt) {
  21. super.update(dt);
  22. if (!isActive) return;
  23. _timeSinceLastSpawn += dt;
  24. _lastCleanupTime += dt;
  25. // Spawn new bubble if conditions are met
  26. if (_timeSinceLastSpawn >= _nextSpawnTime && _activeBubbles.length < maxBubbles) {
  27. _spawnBubble();
  28. _timeSinceLastSpawn = 0;
  29. _calculateNextSpawnTime();
  30. }
  31. // Clean up popped bubbles less frequently to improve performance
  32. if (_lastCleanupTime >= cleanupInterval) {
  33. _activeBubbles.removeWhere((bubble) => !bubble.isMounted);
  34. _lastCleanupTime = 0;
  35. }
  36. }
  37. void _spawnBubble() {
  38. if (game.size.x == 0 || game.size.y == 0) return;
  39. final spawnPosition = _getValidSpawnPosition();
  40. if (spawnPosition == null) return;
  41. final bubble = Bubble(
  42. position: spawnPosition,
  43. onPop: _onBubblePopped,
  44. );
  45. _activeBubbles.add(bubble);
  46. game.add(bubble);
  47. }
  48. Vector2? _getValidSpawnPosition() {
  49. // Calculate safe spawn area (avoiding edges and UI elements)
  50. // Increased margin to account for larger bubble sprites
  51. const margin = 120.0;
  52. final minX = margin;
  53. final maxX = game.size.x - margin;
  54. final minY = margin + 100; // Extra margin for score display
  55. final maxY = game.size.y - margin;
  56. if (maxX <= minX || maxY <= minY) return null;
  57. return Vector2(
  58. minX + _random.nextDouble() * (maxX - minX),
  59. minY + _random.nextDouble() * (maxY - minY),
  60. );
  61. }
  62. void _onBubblePopped(Bubble bubble, bool userTriggered) {
  63. _activeBubbles.remove(bubble);
  64. onBubblePopped?.call(bubble, userTriggered);
  65. }
  66. void spawnBubbleAt(Vector2 position) {
  67. // Ensure the position is within valid bounds
  68. final validPosition = _clampPositionToBounds(position);
  69. final bubble = Bubble(
  70. position: validPosition,
  71. onPop: _onBubblePopped,
  72. );
  73. _activeBubbles.add(bubble);
  74. game.add(bubble);
  75. }
  76. Vector2 _clampPositionToBounds(Vector2 position) {
  77. const margin = 120.0;
  78. final minX = margin;
  79. final maxX = game.size.x - margin;
  80. final minY = margin + 100;
  81. final maxY = game.size.y - margin;
  82. if (maxX <= minX || maxY <= minY) return position;
  83. return Vector2(
  84. position.x.clamp(minX, maxX),
  85. position.y.clamp(minY, maxY),
  86. );
  87. }
  88. void clearAllBubbles() {
  89. for (final bubble in _activeBubbles) {
  90. if (bubble.isMounted) {
  91. bubble.removeFromParent();
  92. }
  93. }
  94. _activeBubbles.clear();
  95. }
  96. void setActive(bool active) {
  97. isActive = active;
  98. }
  99. void _calculateNextSpawnTime() {
  100. // Random spawn time between baseSpawnInterval - spawnVariation and baseSpawnInterval + spawnVariation
  101. _nextSpawnTime = baseSpawnInterval + (_random.nextDouble() - 0.5) * 2 * spawnVariation;
  102. // Ensure minimum spawn time of 0.5 seconds
  103. _nextSpawnTime = _nextSpawnTime.clamp(0.5, double.infinity);
  104. }
  105. int get activeBubbleCount => _activeBubbles.length;
  106. /// Get list of active bubbles for performance optimization
  107. List<Bubble> getActiveBubbles() => List.unmodifiable(_activeBubbles);
  108. }