diff --git a/examples/standard_platformer/lib/main.dart b/examples/standard_platformer/lib/main.dart index 81e0a55..49323cf 100644 --- a/examples/standard_platformer/lib/main.dart +++ b/examples/standard_platformer/lib/main.dart @@ -93,7 +93,7 @@ class ExamplePlatformerLeapGame extends LeapGame ); player = Player(); - world.add(player = Player()); + world.add(player!); camera.follow(player!); if (!FlameAudio.bgm.isPlaying) { diff --git a/examples/standard_platformer/lib/player.dart b/examples/standard_platformer/lib/player.dart index 3da714e..482e60a 100644 --- a/examples/standard_platformer/lib/player.dart +++ b/examples/standard_platformer/lib/player.dart @@ -8,7 +8,7 @@ import 'package:leap_standard_platformer/info_text.dart'; import 'package:leap_standard_platformer/main.dart'; class Player extends JumperCharacter { - Player({super.health = initialHealth}) { + Player({super.health = initialHealth}) : super(removeOnDeath: false) { solidTags.add(CommonTags.ground); } @@ -113,7 +113,7 @@ class Player extends JumperCharacter { } final ladderCollision = - collisionInfo.allCollisions?.whereType().firstOrNull; + collisionInfo.allCollisions.whereType().firstOrNull; final onLadderStatus = getStatus(); if (_input.justPressed && _input.isPressedCenter && @@ -240,7 +240,7 @@ class Player extends JumperCharacter { velocity.y = -minJumpImpulse; } - for (final other in collisionInfo.allCollisions ?? const []) { + for (final other in collisionInfo.allCollisions) { if (other is Coin) { other.removeFromParent(); coins++; diff --git a/examples/standard_platformer/pubspec.lock b/examples/standard_platformer/pubspec.lock index 9499c47..2e0ffbd 100644 --- a/examples/standard_platformer/pubspec.lock +++ b/examples/standard_platformer/pubspec.lock @@ -201,7 +201,7 @@ packages: path: "../../packages/leap" relative: true source: path - version: "0.3.1" + version: "0.4.0" material_color_utilities: dependency: transitive description: diff --git a/packages/leap/lib/src/characters/jumper_character.dart b/packages/leap/lib/src/characters/jumper_character.dart index fa259ca..9eeb32a 100644 --- a/packages/leap/lib/src/characters/jumper_character.dart +++ b/packages/leap/lib/src/characters/jumper_character.dart @@ -1,11 +1,12 @@ import 'package:flame/components.dart'; import 'package:leap/src/characters/jumper_behavior.dart'; -import 'package:leap/src/entities/entities.dart'; +import 'package:leap/src/entities/character.dart'; import 'package:leap/src/leap_game.dart'; -class JumperCharacter extends PhysicalEntity { +class JumperCharacter extends Character { JumperCharacter({ super.health = 10, + super.removeOnDeath, }) : super(behaviors: [JumperBehavior()]); /// When true the character is facing left, otherwise right. diff --git a/packages/leap/lib/src/entities/character.dart b/packages/leap/lib/src/entities/character.dart new file mode 100644 index 0000000..b2fb9ff --- /dev/null +++ b/packages/leap/lib/src/entities/character.dart @@ -0,0 +1,61 @@ +import 'package:flutter/foundation.dart'; +import 'package:leap/leap.dart'; + +/// A type of [PhysicalEntity] which is typically used as the base class +/// for players, enemies, etc. +/// Characters have health, and usually a removed on death. +/// +// TODO(kurtome): add sprite animation and state helpers. +class Character extends PhysicalEntity { + Character({ + this.health = 1, + this.removeOnDeath = true, + super.static, + super.behaviors, + super.priority, + super.position, + super.size, + }) : super() { + _wasAlive = isAlive; + } + + /// The health of this character, when positive this is "alive". + int health; + + /// Indicates if this should [remove] itself on death. + bool removeOnDeath; + + /// Whether or not this is "alive" (or not destroyed) in the game + bool get isAlive => health > 0; + + /// Whether or not this is "dead" (or destroyed) in the game. + bool get isDead => !isAlive; + + bool _wasAlive = true; + + /// Indicates that this was alive on the previous [update] loop + bool get wasAlive => _wasAlive; + + /// Called when this entity dies, typically due to health dropping below one. + /// + /// This will be invoked after this has been marked for removal + /// (if [removeOnDeath] is true) but before [onRemove]. + /// Entities can be removed without dying. + @mustCallSuper + void onDeath() {} + + @override + @mustCallSuper + void update(double dt) { + super.update(dt); + + if (isDead && wasAlive) { + if (removeOnDeath) { + removeFromParent(); + } + onDeath(); + } + + _wasAlive = isAlive; + } +} diff --git a/packages/leap/lib/src/entities/ladder.dart b/packages/leap/lib/src/entities/ladder.dart index 0e1e2b9..f6cd2ff 100644 --- a/packages/leap/lib/src/entities/ladder.dart +++ b/packages/leap/lib/src/entities/ladder.dart @@ -65,8 +65,7 @@ class OnLadderStatus extends StatusComponent final parentEntity = parent! as PhysicalEntity; - if (!(parentEntity.collisionInfo.allCollisions?.contains(ladder) ?? - false)) { + if (!parentEntity.collisionInfo.allCollisions.contains(ladder)) { // No longer on the ladder removeFromParent(); parentEntity.velocity.y = 0; diff --git a/packages/leap/lib/src/entities/physical_entity.dart b/packages/leap/lib/src/entities/physical_entity.dart index 2b509f1..d3b9b51 100644 --- a/packages/leap/lib/src/entities/physical_entity.dart +++ b/packages/leap/lib/src/entities/physical_entity.dart @@ -51,12 +51,7 @@ abstract class PhysicalEntity extends PositionedEntity /// Multiplier on standard gravity, see [LeapWorld]. double gravityRate = 1; - /// When health reaches 0, [isDead] will be true. - /// This needs to be used by child classes to have any effect. - int health; - PhysicalEntity({ - this.health = 10, this.static = false, Iterable>? behaviors, super.priority, @@ -121,12 +116,6 @@ abstract class PhysicalEntity extends PositionedEntity /// Tile size (width and height) in pixels double get tileSize => game.tileSize; - /// Whether or not this is "alive" (or not destroyed) in the game - bool get isAlive => health > 0; - - /// Whether or not this is "dead" (or destroyed) in the game. - bool get isDead => !isAlive; - /// Leftmost point. double get left { return x; diff --git a/packages/leap/lib/src/physical_behaviors/collision/collision_detection_behavior.dart b/packages/leap/lib/src/physical_behaviors/collision/collision_detection_behavior.dart index 97d4170..1e3a254 100644 --- a/packages/leap/lib/src/physical_behaviors/collision/collision_detection_behavior.dart +++ b/packages/leap/lib/src/physical_behaviors/collision/collision_detection_behavior.dart @@ -127,7 +127,7 @@ class CollisionDetectionBehavior extends PhysicalBehavior { !collisionInfo.onSlope) { // Moving down. _calculateSolidHits((c) { - return c.bottom >= bottom && + return c.bottom > bottom && c.isSolidFromTop && // For one-way platforms from underneath, make sure this is // currently above it so this doesn't pop up on top of it @@ -292,7 +292,7 @@ class CollisionDetectionBehavior extends PhysicalBehavior { _proxyHitboxForPotentialHits(dt); for (final other in world.physicals) { - if (intersectsOther(_hitboxProxy, other)) { + if (!other.isRemoving && intersectsOther(_hitboxProxy, other)) { _potentialHits.add(other); } } diff --git a/packages/leap/lib/src/physical_behaviors/collision/collision_info.dart b/packages/leap/lib/src/physical_behaviors/collision/collision_info.dart index 42be988..aab07a1 100644 --- a/packages/leap/lib/src/physical_behaviors/collision/collision_info.dart +++ b/packages/leap/lib/src/physical_behaviors/collision/collision_info.dart @@ -32,7 +32,7 @@ class CollisionInfo { set upCollision(PhysicalEntity? c) { if (_upCollision != null) { - allCollisions?.remove(c); + _allCollisions?.remove(c); } if (c != null) { addCollision(c); @@ -45,7 +45,7 @@ class CollisionInfo { set downCollision(PhysicalEntity? c) { if (_downCollision != null) { - allCollisions?.remove(c); + _allCollisions?.remove(c); } if (c != null) { addCollision(c); @@ -58,7 +58,7 @@ class CollisionInfo { set leftCollision(PhysicalEntity? c) { if (_leftCollision != null) { - allCollisions?.remove(c); + _allCollisions?.remove(c); } if (c != null) { addCollision(c); @@ -71,7 +71,7 @@ class CollisionInfo { set rightCollision(PhysicalEntity? c) { if (_rightCollision != null) { - allCollisions?.remove(c); + _allCollisions?.remove(c); } if (c != null) { addCollision(c); @@ -80,11 +80,13 @@ class CollisionInfo { } /// Non-solid collisions. - List? allCollisions; + List? _allCollisions; + + List get allCollisions => _allCollisions ?? const []; void addCollision(PhysicalEntity collision) { - allCollisions ??= []; - allCollisions!.add(collision); + _allCollisions ??= []; + _allCollisions!.add(collision); } /// Is currently colliding on top @@ -115,7 +117,8 @@ class CollisionInfo { downCollision = null; leftCollision = null; rightCollision = null; - allCollisions = null; + + _allCollisions = null; } void copyFrom(CollisionInfo other) { @@ -124,10 +127,10 @@ class CollisionInfo { _leftCollision = other.leftCollision; _rightCollision = other.rightCollision; - allCollisions?.clear(); - if (other.allCollisions != null) { - allCollisions ??= []; - allCollisions!.addAll(other.allCollisions!); + _allCollisions?.clear(); + if (other.allCollisions.isNotEmpty) { + _allCollisions ??= []; + _allCollisions!.addAll(other.allCollisions); } } }