diff --git a/packages/flame/lib/src/game/game.dart b/packages/flame/lib/src/game/game.dart index 388c8048f24..3dfca89f291 100644 --- a/packages/flame/lib/src/game/game.dart +++ b/packages/flame/lib/src/game/game.dart @@ -35,14 +35,6 @@ abstract mixin class Game { late final GestureDetectorBuilder gestureDetectors = GestureDetectorBuilder(refreshWidget)..initializeGestures(this); - /// The game's lifecycle state. - /// - /// This is used to discard the first [update] call after the app was in the - /// background to avoid a massive `dt` value. - /// - /// See [GameLifecycleState] for more details. - GameLifecycleState gameLifecycleState = GameLifecycleState.foregrounded; - /// This should update the state of the game. void update(double dt); @@ -145,27 +137,9 @@ abstract mixin class Game { /// This is the lifecycle state change hook; every time the game is resumed, /// paused or suspended, this is called. /// - /// You may wish to use [gameLifecycleState] instead of [state] - /// to use more game-specific lifecycle states. - /// - /// Check [AppLifecycleState] and [GameLifecycleState] - /// for details about the events received. - @mustCallSuper + /// The default implementation does nothing; override to use the hook. + /// Check [AppLifecycleState] for details about the events received. void lifecycleStateChange(AppLifecycleState state) { - switch (state) { - case (AppLifecycleState.resumed || AppLifecycleState.inactive) - when gameLifecycleState == GameLifecycleState.backgrounded: - // We are coming back from background. - gameLifecycleState = GameLifecycleState.foregrounding; - break; - case (AppLifecycleState.paused || AppLifecycleState.detached) - when gameLifecycleState != GameLifecycleState.backgrounded: - // We are going to background. - gameLifecycleState = GameLifecycleState.backgrounded; - break; - default: - break; - } } /// Method to perform late initialization of the [Game] class. @@ -390,19 +364,3 @@ abstract mixin class Game { gameStateListeners.forEach((callback) => callback()); } } - -enum GameLifecycleState { - /// The game is backgrounded. - /// - /// This corresponds to [AppLifecycleState.paused] and [AppLifecycleState.detached]. - backgrounded, - - /// The game has been foregrounded but the first update should be - /// disregarded if `pauseWhenBackgrounded` is true. - foregrounding, - - /// The game is foregrounded. - /// - /// This corresponds to [AppLifecycleState.resumed] and [AppLifecycleState.inactive]. - foregrounded, -} diff --git a/packages/flame_forge2d/lib/forge2d_game.dart b/packages/flame_forge2d/lib/forge2d_game.dart index 7ea8a4a9430..13072da5c2d 100644 --- a/packages/flame_forge2d/lib/forge2d_game.dart +++ b/packages/flame_forge2d/lib/forge2d_game.dart @@ -29,13 +29,10 @@ class Forge2DGame extends FlameGame { /// /// Defaults to false. bool pauseWhenBackgrounded; + bool _pausedBecauseBackgrounded = false; @override void update(double dt) { - if (!shouldUpdate()) { - return; - } - super.update(dt); world.stepDt(dt); } @@ -52,19 +49,32 @@ class Forge2DGame extends FlameGame { return screenToWorld(position)..y *= -1; } - @protected - bool shouldUpdate() { - switch (gameLifecycleState) { - case GameLifecycleState.backgrounded: - // Don't update when backgrounded - return !pauseWhenBackgrounded; - case GameLifecycleState.foregrounding: - // Discard the first `update` call after the app was in the background - // to avoid a massive [dt] value. - gameLifecycleState = GameLifecycleState.foregrounded; - return !pauseWhenBackgrounded; - case GameLifecycleState.foregrounded: - return true; + @override + @mustCallSuper + void lifecycleStateChange(AppLifecycleState state) { + switch (state) { + case AppLifecycleState.resumed: + case AppLifecycleState.inactive: + if (_pausedBecauseBackgrounded) { + resumeEngine(); + } + case AppLifecycleState.paused: + case AppLifecycleState.detached: + case AppLifecycleState.hidden: + if (pauseWhenBackgrounded) { + pauseEngine(); + _pausedBecauseBackgrounded = true; + } } } + @override + void pauseEngine() { + _pausedBecauseBackgrounded = false; + super.pauseEngine(); + } + @override + void resumeEngine() { + _pausedBecauseBackgrounded = false; + super.resumeEngine(); + } } diff --git a/packages/flame_forge2d/test/forge2d_game_test.dart b/packages/flame_forge2d/test/forge2d_game_test.dart index ea59b8fd5c9..8b0d6858404 100644 --- a/packages/flame_forge2d/test/forge2d_game_test.dart +++ b/packages/flame_forge2d/test/forge2d_game_test.dart @@ -1,5 +1,5 @@ -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; +import 'dart:ui' show AppLifecycleState; + import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:test/test.dart'; @@ -10,17 +10,6 @@ class _TestForge2dGame extends Forge2DGame { } } -class _TestComponent extends Component { - _TestComponent({ - required this.onUpdate, - }); - - final void Function() onUpdate; - - @override - void update(double dt) => onUpdate.call(); -} - void main() { group( 'Test corresponding position on screen and in the Forge2D world', @@ -69,54 +58,13 @@ void main() { group('Game lifecycle state:', () { test('update should only be called if foregrounded', () async { - var updateCalled = false; - void onUpdate() => updateCalled = true; - final game = _TestForge2dGame(); - final component = _TestComponent(onUpdate: onUpdate); - await game.add(component); - const dt = 1 / 60; - - await game.onLoad(); - - game.gameLifecycleState = GameLifecycleState.backgrounded; - game.update(dt); - expect( - updateCalled, - false, - reason: 'Update should not run when app is backgrounded', - ); - expect( - game.gameLifecycleState, - GameLifecycleState.backgrounded, - reason: 'Game should still be backgrounded', - ); - - game.gameLifecycleState = GameLifecycleState.foregrounding; - game.update(dt); - expect( - updateCalled, - false, - reason: 'Update should not run on first foregrounded update', - ); - expect( - game.gameLifecycleState, - GameLifecycleState.foregrounded, - reason: 'Game should be foregrounded', - ); - game.gameLifecycleState = GameLifecycleState.foregrounded; - game.update(dt); - expect( - updateCalled, - true, - reason: 'Update should run when app is foregrounded', - ); - expect( - game.gameLifecycleState, - GameLifecycleState.foregrounded, - reason: 'Game should be foregrounded', - ); + game.lifecycleStateChange(AppLifecycleState.paused); + expect(game.paused, true); + + game.lifecycleStateChange(AppLifecycleState.resumed); + expect(game.paused, false); }); }); }