/
spawn_component.dart
154 lines (135 loc) · 4.56 KB
/
spawn_component.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import 'dart:async';
import 'dart:math';
import 'package:flame/components.dart';
import 'package:flame/effects.dart';
import 'package:flame/experimental.dart';
import 'package:flame/math.dart';
/// {@template spawn_component}
/// The [SpawnComponent] is a non-visual component which can spawn
/// [PositionComponent]s randomly within a set [area]. If [area] is not set it
/// will use the size of the nearest ancestor that provides a size.
/// [period] will set the static time interval for when it will spawn new
/// components.
/// If you want to use a non static time interval, use the
/// [SpawnComponent.periodRange] constructor.
/// If you want to set the position of the spawned components yourself inside of
/// the [factory], set [selfPositioning] to true.
/// {@endtemplate}
class SpawnComponent extends Component {
/// {@macro spawn_component}
SpawnComponent({
required this.factory,
required double period,
this.area,
this.within = true,
this.selfPositioning = false,
Random? random,
super.key,
}) : assert(
!(selfPositioning && area != null),
"Don't set an area when you are using selfPositioning=true",
),
_period = period,
_random = random ?? randomFallback;
/// Use this constructor if you want your components to spawn within an
/// interval time range.
/// [minPeriod] will be the minimum amount of time before the next component
/// spawns and [maxPeriod] will be the maximum amount of time before it
/// spawns.
SpawnComponent.periodRange({
required this.factory,
required double minPeriod,
required double maxPeriod,
this.area,
this.within = true,
this.selfPositioning = false,
Random? random,
super.key,
}) : assert(
!(selfPositioning && area != null),
"Don't set an area when you are using selfPositioning=true",
),
_period = minPeriod +
(random ?? randomFallback).nextDouble() * (maxPeriod - minPeriod),
_random = random ?? randomFallback;
/// The function used to create new components to spawn.
///
/// [amount] is the amount of components that the [SpawnComponent] has spawned
/// so far.
PositionComponent Function(int amount) factory;
/// The area where the components should be spawned.
Shape? area;
/// Whether the random point should be within the [area] or along its edges.
bool within;
/// Whether the spawned components positions shouldn't be given a position,
/// so that they can continue to have the position that they had after they
/// came out of the [factory].
bool selfPositioning;
/// The timer that is used to control when components are spawned.
late final Timer timer;
/// The time between each component is spawned.
double get period => _period;
set period(double newPeriod) {
_period = newPeriod;
timer.limit = _period;
}
double _period;
/// The minimum amount of time that has to pass until the next component is
/// spawned.
double? minPeriod;
/// The maximum amount of time that has to pass until the next component is
/// spawned.
double? maxPeriod;
/// Whether it is spawning components within a random time frame or at a
/// static rate.
bool get hasRandomPeriod => minPeriod != null;
final Random _random;
/// The amount of spawned components.
int amount = 0;
@override
FutureOr<void> onLoad() async {
if (area == null && !selfPositioning) {
final parentPosition =
ancestors().whereType<PositionProvider>().firstOrNull?.position ??
Vector2.zero();
final parentSize =
ancestors().whereType<ReadOnlySizeProvider>().firstOrNull?.size ??
Vector2.zero();
assert(
!parentSize.isZero(),
'The SpawnComponent needs an ancestor with a size if area is not '
'provided.',
);
area = Rectangle.fromLTWH(
parentPosition.x,
parentPosition.y,
parentSize.x,
parentSize.y,
);
}
void updatePeriod() {
if (hasRandomPeriod) {
period = minPeriod! + _random.nextDouble() * (maxPeriod! - minPeriod!);
}
}
updatePeriod();
final timerComponent = TimerComponent(
period: _period,
repeat: true,
onTick: () {
final component = factory(amount);
if (!selfPositioning) {
component.position = area!.randomPoint(
random: _random,
within: within,
);
}
parent?.add(component);
updatePeriod();
amount++;
},
);
timer = timerComponent.timer;
add(timerComponent);
}
}