Skip to content

Commit

Permalink
feat: make the internal find behavior logic more clear on when it can…
Browse files Browse the repository at this point in the history
… find something (#12)

* feat: make the internal find behavior logic more clear on when it can find something
  • Loading branch information
wolfenrain committed Jun 9, 2022
1 parent 5cbbe3c commit e778b00
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 16 deletions.
2 changes: 1 addition & 1 deletion lib/src/behaviors/propagating_collision_behavior.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ abstract class CollisionBehavior<Collider extends Component,
/// Whether the object is currently colliding with another [Collider] or not.
bool get isColliding {
final propagatingCollisionBehavior =
parent.findBehavior<PropagatingCollisionBehavior>()!;
parent.findBehavior<PropagatingCollisionBehavior>();
final activeCollisions = propagatingCollisionBehavior.activeCollisions;

return activeCollisions
Expand Down
39 changes: 30 additions & 9 deletions lib/src/entity.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,52 @@ abstract class Entity extends PositionComponent {
children?.whereType<Behavior>().isEmpty ?? true,
'Behaviors cannot be added to as a child directly.',
) {
children.register<Behavior>();
_behaviors = children.query<Behavior>();

if (behaviors != null) {
addAll(behaviors);
}

children.register<Behavior>();
_behaviors = children.query<Behavior>();
}

late final List<Behavior> _behaviors;

/// Returns a list of behaviors with the given type, that are attached to
/// this entity.
///
/// This will only return behaviors that have a completed lifecycle, aka they
/// are fully mounted.
Iterable<T> findBehaviors<T extends Behavior>() {
return _behaviors.whereType<T>();
}

/// Returns the first behavior with the given type, that is attached to this
/// entity.
T? findBehavior<T extends Behavior>() {
/// Returns the first found behavior with the given type, that is attached
/// to this entity.
///
/// This will only return a behavior that has a completed lifecycle, aka it
/// is fully mounted. If no behavior is found, it will throw a [StateError].
T findBehavior<T extends Behavior>() {
final it = findBehaviors<T>().iterator;
return it.moveNext() ? it.current : null;
if (!it.moveNext()) {
throw StateError('No behavior of type $T found.');
}
return it.current;
}

/// Checks if this entity has a behavior with the given type.
/// Checks if this entity has at least one behavior with the given type.
///
///
/// This will only return true if the behavior with the type [T] has a
/// completed lifecycle, aka it is fully mounted.
bool hasBehavior<T extends Behavior>() {
return findBehavior<T>() != null;
try {
findBehavior<T>();
return true;
} catch (e) {
if (e is StateError) {
return false;
}
rethrow;
}
}
}
1 change: 1 addition & 0 deletions test/helpers/helpers.dart
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export 'test_game.dart';
export 'throws_behavior_not_found_for.dart';
11 changes: 11 additions & 0 deletions test/helpers/throws_behavior_not_found_for.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:flame_behaviors/flame_behaviors.dart';
import 'package:flutter_test/flutter_test.dart';

/// A matcher for [StateError]s thrown when a certain behavior is not found.
Matcher throwsBehaviorNotFoundFor<T extends Behavior>() => throwsA(
isStateError.having(
(e) => e.message,
'message',
equals('No behavior of type $T found.'),
),
);
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ void main() {

await game.ensureAdd(entityA);
await game.ensureAdd(entityB);

game.update(0);

expect(collisionBehaviorAtoB.isColliding, isTrue);
Expand Down
26 changes: 21 additions & 5 deletions test/src/entity_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void main() {
final flameTester = FlameTester(TestGame.new);

group('Entity', () {
flameTester.test('adds behaviors to itself', (game) async {
flameTester.test('adds behaviors directly to itself', (game) async {
final behavior = TestBehavior();
final entity = TestEntity(behaviors: [behavior]);

Expand All @@ -27,6 +27,16 @@ void main() {
expect(entity.children.contains(behavior), isTrue);
});

flameTester.test('adds behaviors to itself', (game) async {
final behavior = TestBehavior();
final entity = TestEntity();
await entity.add(behavior);

await game.ensureAdd(entity);

expect(entity.children.contains(behavior), isTrue);
});

flameTester.test(
'behavior can be removed from entity and the internal cache',
(game) async {
Expand All @@ -35,13 +45,19 @@ void main() {

await game.ensureAdd(entity);

expect(entity.findBehavior<TestBehavior>(), isNull);
await expectLater(
entity.findBehavior<TestBehavior>,
throwsBehaviorNotFoundFor<TestBehavior>(),
);
await entity.ensureAdd(behavior);
expect(entity.findBehavior<TestBehavior>(), isNotNull);

behavior.removeFromParent();
game.update(0);
expect(entity.findBehavior<TestBehavior>(), isNull);
await game.ready();
await expectLater(
entity.findBehavior<TestBehavior>,
throwsBehaviorNotFoundFor<TestBehavior>(),
);
},
);

Expand All @@ -58,7 +74,7 @@ void main() {
expect(entity.hasBehavior<TestBehavior>(), isTrue);

behavior.removeFromParent();
game.update(0);
await game.ready();
expect(entity.hasBehavior<TestBehavior>(), isFalse);
},
);
Expand Down

0 comments on commit e778b00

Please sign in to comment.