Skip to content

Commit

Permalink
add multiFactory to SpawnComponent to be able to create a list of com…
Browse files Browse the repository at this point in the history
…ponents for each spawn tick. For backwards compatibility the single component factory is still supported and wrapped internally.
  • Loading branch information
Patrick Cornelißen committed Jan 9, 2025
1 parent f4a75d5 commit f4a66a9
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 10 deletions.
59 changes: 49 additions & 10 deletions packages/flame/lib/src/components/spawn_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ import 'package:flame/math.dart';
/// [SpawnComponent.periodRange] constructor.
/// If you want to set the position of the spawned components yourself inside of
/// the [factory], set [selfPositioning] to true.
/// You can either provide a factory that returns one component or a multiFactory

Check notice on line 19 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze

The line length exceeds the 80-character limit.

Try breaking the line across multiple lines. See https://dart.dev/lints/lines_longer_than_80_chars to learn more about this problem.

Check notice on line 19 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

The line length exceeds the 80-character limit.

Try breaking the line across multiple lines. See https://dart.dev/lints/lines_longer_than_80_chars to learn more about this problem.
/// which returns a list of components. In this case the amount parameter will be

Check notice on line 20 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze

The line length exceeds the 80-character limit.

Try breaking the line across multiple lines. See https://dart.dev/lints/lines_longer_than_80_chars to learn more about this problem.

Check notice on line 20 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

The line length exceeds the 80-character limit.

Try breaking the line across multiple lines. See https://dart.dev/lints/lines_longer_than_80_chars to learn more about this problem.
/// increased by the number of returned components.
/// {@endtemplate}
class SpawnComponent extends Component {
/// {@macro spawn_component}
SpawnComponent({
required this.factory,
required double period,
PositionComponent Function(int amount)? factory,
List<PositionComponent> Function(int amount)? multiFactory,
this.area,
this.within = true,
this.selfPositioning = false,
Expand All @@ -33,7 +37,16 @@ class SpawnComponent extends Component {
!(selfPositioning && area != null),
"Don't set an area when you are using selfPositioning=true",
),
assert(
factory != null || multiFactory != null,
'You need to provide a factory or multiFactory to create components',
),
assert(
!(factory != null && multiFactory != null),
'You need to provide either a factory or a multiFactory not both',
),
_period = period,
multiFactory = multiFactory ?? _wrapFactory(factory!),
_random = random ?? randomFallback;

/// Use this constructor if you want your components to spawn within an
Expand All @@ -42,9 +55,10 @@ class SpawnComponent extends 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,
PositionComponent Function(int amount)? factory,
List<PositionComponent> Function(int amount)? multiFactory,
this.area,
this.within = true,
this.selfPositioning = false,
Expand All @@ -58,13 +72,37 @@ class SpawnComponent extends Component {
),
_period = minPeriod +
(random ?? randomFallback).nextDouble() * (maxPeriod - minPeriod),
multiFactory = multiFactory ?? _wrapFactory(factory!),
_random = random ?? randomFallback;

/// The function used to create a new component to spawn.
///
/// [amount] is the amount of components that the [SpawnComponent] has spawned
/// so far.
///
/// Be aware: internally the component uses a factory that creates a list of
/// components.
/// If you have set such a factory it was wrapped to create a list. The factory

Check notice on line 85 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze

The line length exceeds the 80-character limit.

Try breaking the line across multiple lines. See https://dart.dev/lints/lines_longer_than_80_chars to learn more about this problem.

Check notice on line 85 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

The line length exceeds the 80-character limit.

Try breaking the line across multiple lines. See https://dart.dev/lints/lines_longer_than_80_chars to learn more about this problem.
/// getter wrapps it again to return the first element of the list and fails
/// when the list is empty!
///
PositionComponent Function(int amount) get factory =>
(int amount) => multiFactory.call(amount).elementAt(0);

set factory(PositionComponent Function(int amount) newFactory) {
multiFactory = _wrapFactory(newFactory);
}

static List<PositionComponent> Function(int amount) _wrapFactory(
PositionComponent Function(int amount) newFactory) {

Check notice on line 97 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze

Missing a required trailing comma.

Try adding a trailing comma. See https://dart.dev/lints/require_trailing_commas to learn more about this problem.

Check notice on line 97 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

Missing a required trailing comma.

Try adding a trailing comma. See https://dart.dev/lints/require_trailing_commas to learn more about this problem.
return (int amount) => [newFactory.call(amount)];
}

/// 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;
List<PositionComponent> Function(int amount) multiFactory;

/// The area where the components should be spawned.
Shape? area;
Expand Down Expand Up @@ -146,16 +184,17 @@ class SpawnComponent extends Component {
period: _period,
repeat: true,
onTick: () {
final component = factory(amount);
final List<PositionComponent> components = multiFactory(amount);

Check notice on line 187 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze

Unnecessary type annotation on a local variable.

Try removing the type annotation. See https://dart.dev/lints/omit_local_variable_types to learn more about this problem.

Check notice on line 187 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

Unnecessary type annotation on a local variable.

Try removing the type annotation. See https://dart.dev/lints/omit_local_variable_types to learn more about this problem.
if (!selfPositioning) {
component.position = area!.randomPoint(
random: _random,
within: within,
);
components
.forEach((component) => component.position = area!.randomPoint(
random: _random,
within: within,
));

Check notice on line 193 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze

Missing a required trailing comma.

Try adding a trailing comma. See https://dart.dev/lints/require_trailing_commas to learn more about this problem.

Check notice on line 193 in packages/flame/lib/src/components/spawn_component.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

Missing a required trailing comma.

Try adding a trailing comma. See https://dart.dev/lints/require_trailing_commas to learn more about this problem.
}
parent?.add(component);
parent?.addAll(components);
updatePeriod();
amount++;
amount += components.length;
},
autoStart: autoStart,
tickWhenLoaded: spawnWhenLoaded,
Expand Down
69 changes: 69 additions & 0 deletions packages/flame/test/components/spawn_component_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,42 @@ void main() {
);
});

testWithFlameGame('Spawns multiple components within rectangle',
(game) async {
final random = Random(0);
final shape = Rectangle.fromCenter(
center: Vector2(100, 200),
size: Vector2.all(200),
);
final spawn = SpawnComponent(
multiFactory: (_) =>
[PositionComponent(), PositionComponent(), PositionComponent()],
period: 1,
area: shape,
random: random,
);
final world = game.world;
await world.ensureAdd(spawn);
game.update(0.5);
expect(world.children.length, 1); //1 being the spawnComponent
game.update(0.5);
game.update(0.0);
expect(world.children.length, 4); //1+3 spawned components
game.update(1.0);
game.update(0.0);
expect(world.children.length, 7); //1+2*3 spawned components

for (var i = 0; i < 1000; i++) {
game.update(random.nextDouble());
}
expect(
world.children
.query<PositionComponent>()
.every((c) => shape.containsPoint(c.position)),
isTrue,
);
});

testWithFlameGame('Spawns components within circle', (game) async {
final random = Random(0);
final shape = Circle(Vector2(100, 200), 100);
Expand Down Expand Up @@ -140,6 +176,39 @@ void main() {
isTrue,
);
});
testWithFlameGame('Can self position multiple components ', (game) async {
final random = Random(0);
final spawn = SpawnComponent(
multiFactory: (_) => [
PositionComponent(position: Vector2.all(1000)),
PositionComponent(position: Vector2.all(1000)),
PositionComponent(position: Vector2.all(1000))
],

Check notice on line 186 in packages/flame/test/components/spawn_component_test.dart

View workflow job for this annotation

GitHub Actions / analyze

Missing a required trailing comma.

Try adding a trailing comma. See https://dart.dev/lints/require_trailing_commas to learn more about this problem.

Check notice on line 186 in packages/flame/test/components/spawn_component_test.dart

View workflow job for this annotation

GitHub Actions / analyze-latest

Missing a required trailing comma.

Try adding a trailing comma. See https://dart.dev/lints/require_trailing_commas to learn more about this problem.
period: 1,
selfPositioning: true,
random: random,
);
final world = game.world;
await world.ensureAdd(spawn);
game.update(0.5);
expect(world.children.length, 1); //1 spawn component
game.update(0.5);
game.update(0.0);
expect(world.children.length, 4); //1+3 spawned components
game.update(1.0);
game.update(0.0);
expect(world.children.length, 7); //1+2*3 spawned components

for (var i = 0; i < 1000; i++) {
game.update(random.nextDouble());
}
expect(
world.children
.query<PositionComponent>()
.every((c) => c.position == Vector2.all(1000)),
isTrue,
);
});

testWithFlameGame('Does not spawns when auto start is false', (game) async {
final random = Random(0);
Expand Down

0 comments on commit f4a66a9

Please sign in to comment.