| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- import 'dart:math';
- import 'package:flame/components.dart';
- import 'bubble.dart';
- class BubbleSpawner extends Component with HasGameReference {
- static const double baseSpawnInterval = 1.5; // base seconds
- static const double spawnVariation = 1.0; // +/- variation in seconds
- static const int maxBubbles = 8;
-
- double _timeSinceLastSpawn = 0;
- double _nextSpawnTime = 0;
- final List<Bubble> _activeBubbles = [];
- final Random _random = Random();
-
- Function(Bubble, bool)? onBubblePopped;
- bool isActive = true;
- BubbleSpawner({this.onBubblePopped}) {
- _calculateNextSpawnTime();
- }
- @override
- void update(double dt) {
- super.update(dt);
-
- if (!isActive) return;
-
- _timeSinceLastSpawn += dt;
-
- // Spawn new bubble if conditions are met
- if (_timeSinceLastSpawn >= _nextSpawnTime && _activeBubbles.length < maxBubbles) {
- _spawnBubble();
- _timeSinceLastSpawn = 0;
- _calculateNextSpawnTime();
- }
-
- // Clean up popped bubbles
- _activeBubbles.removeWhere((bubble) => !bubble.isMounted);
- }
- void _spawnBubble() {
- if (game.size.x == 0 || game.size.y == 0) return;
-
- final spawnPosition = _getValidSpawnPosition();
- if (spawnPosition == null) return;
-
- final bubble = Bubble(
- position: spawnPosition,
- onPop: _onBubblePopped,
- );
-
- _activeBubbles.add(bubble);
- game.add(bubble);
- }
- Vector2? _getValidSpawnPosition() {
- // Calculate safe spawn area (avoiding edges and UI elements)
- // Increased margin to account for larger bubble sprites
- const margin = 120.0;
- final minX = margin;
- final maxX = game.size.x - margin;
- final minY = margin + 100; // Extra margin for score display
- final maxY = game.size.y - margin;
-
- if (maxX <= minX || maxY <= minY) return null;
-
- return Vector2(
- minX + _random.nextDouble() * (maxX - minX),
- minY + _random.nextDouble() * (maxY - minY),
- );
- }
- void _onBubblePopped(Bubble bubble, bool userTriggered) {
- _activeBubbles.remove(bubble);
- onBubblePopped?.call(bubble, userTriggered);
- }
- void spawnBubbleAt(Vector2 position) {
- // Ensure the position is within valid bounds
- final validPosition = _clampPositionToBounds(position);
-
- final bubble = Bubble(
- position: validPosition,
- onPop: _onBubblePopped,
- );
-
- _activeBubbles.add(bubble);
- game.add(bubble);
- }
- Vector2 _clampPositionToBounds(Vector2 position) {
- const margin = 120.0;
- final minX = margin;
- final maxX = game.size.x - margin;
- final minY = margin + 100;
- final maxY = game.size.y - margin;
-
- if (maxX <= minX || maxY <= minY) return position;
-
- return Vector2(
- position.x.clamp(minX, maxX),
- position.y.clamp(minY, maxY),
- );
- }
- void clearAllBubbles() {
- for (final bubble in _activeBubbles) {
- if (bubble.isMounted) {
- bubble.removeFromParent();
- }
- }
- _activeBubbles.clear();
- }
- void setActive(bool active) {
- isActive = active;
- }
- void _calculateNextSpawnTime() {
- // Random spawn time between baseSpawnInterval - spawnVariation and baseSpawnInterval + spawnVariation
- _nextSpawnTime = baseSpawnInterval + (_random.nextDouble() - 0.5) * 2 * spawnVariation;
- // Ensure minimum spawn time of 0.5 seconds
- _nextSpawnTime = _nextSpawnTime.clamp(0.5, double.infinity);
- }
- int get activeBubbleCount => _activeBubbles.length;
-
- /// Get list of active bubbles for performance optimization
- List<Bubble> getActiveBubbles() => List.unmodifiable(_activeBubbles);
- }
|