From eb4fc09d83fdfe235d658b30de5e19c110526d4c Mon Sep 17 00:00:00 2001 From: Adil Hanney Date: Tue, 8 Aug 2023 19:50:07 +0100 Subject: [PATCH] fix: Pause forge2d when backgrounded --- .github/.cspell/gamedev_dictionary.txt | 2 + .../flame/lib/src/game/game_render_box.dart | 10 ++++- packages/flame_forge2d/lib/forge2d_game.dart | 41 +++++++++++++++++++ .../flame_forge2d/test/forge2d_game_test.dart | 29 +++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/.github/.cspell/gamedev_dictionary.txt b/.github/.cspell/gamedev_dictionary.txt index 5cd7da96f94..09b55065806 100644 --- a/.github/.cspell/gamedev_dictionary.txt +++ b/.github/.cspell/gamedev_dictionary.txt @@ -22,6 +22,7 @@ arial arities arity autofocus +backgrounded backpressure backquote backtick @@ -51,6 +52,7 @@ drawables easings flippable focusable +foregrounded fullscreen gamepad gamepads diff --git a/packages/flame/lib/src/game/game_render_box.dart b/packages/flame/lib/src/game/game_render_box.dart index 64d4f9401a2..d6f245f43db 100644 --- a/packages/flame/lib/src/game/game_render_box.dart +++ b/packages/flame/lib/src/game/game_render_box.dart @@ -131,11 +131,17 @@ class GameRenderBox extends RenderBox with WidgetsBindingObserver { } void _bindLifecycleListener() { - _ambiguate(WidgetsBinding.instance)!.addObserver(this); + final binding = _ambiguate(WidgetsBinding.instance)!; + binding.addObserver(this); + didChangeAppLifecycleState( + binding.lifecycleState ?? AppLifecycleState.resumed, + ); } void _unbindLifecycleListener() { - _ambiguate(WidgetsBinding.instance)!.removeObserver(this); + final binding = _ambiguate(WidgetsBinding.instance)!; + binding.removeObserver(this); + didChangeAppLifecycleState(AppLifecycleState.paused); } @override diff --git a/packages/flame_forge2d/lib/forge2d_game.dart b/packages/flame_forge2d/lib/forge2d_game.dart index 40eb86bbcb1..45424bd6afb 100644 --- a/packages/flame_forge2d/lib/forge2d_game.dart +++ b/packages/flame_forge2d/lib/forge2d_game.dart @@ -1,5 +1,6 @@ import 'package:flame/game.dart'; import 'package:flame_forge2d/world_contact_listener.dart'; +import 'package:flutter/material.dart'; import 'package:forge2d/forge2d.dart'; class Forge2DGame extends FlameGame { @@ -8,6 +9,7 @@ class Forge2DGame extends FlameGame { double zoom = defaultZoom, Camera? camera, ContactListener? contactListener, + this.pauseWhenBackgrounded = false, }) : world = World(gravity ?? defaultGravity), super(camera: camera ?? Camera()) { // ignore: deprecated_member_use @@ -21,6 +23,14 @@ class Forge2DGame extends FlameGame { final World world; + /// Whether the game should pause when the app is backgrounded. + /// + /// If true, the first update after the app is foregrounded will be skipped. + /// + /// Defaults to false. + bool pauseWhenBackgrounded; + bool _pausedBecauseBackgrounded = false; + @override void update(double dt) { super.update(dt); @@ -38,4 +48,35 @@ class Forge2DGame extends FlameGame { Vector2 screenToFlameWorld(Vector2 position) { return screenToWorld(position)..y *= -1; } + + @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 727986a9095..f302ffd84d9 100644 --- a/packages/flame_forge2d/test/forge2d_game_test.dart +++ b/packages/flame_forge2d/test/forge2d_game_test.dart @@ -1,3 +1,5 @@ +import 'dart:ui' show AppLifecycleState; + import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:test/test.dart'; @@ -50,4 +52,31 @@ void main() { }); }, ); + + group('pauseWhenBackgrounded:', () { + test('true', () async { + final game = Forge2DGame( + pauseWhenBackgrounded: true, + ); + + game.lifecycleStateChange(AppLifecycleState.paused); + expect(game.paused, true); + + game.lifecycleStateChange(AppLifecycleState.resumed); + expect(game.paused, false); + }); + + test('false', () async { + final game = Forge2DGame( + // ignore: avoid_redundant_argument_values + pauseWhenBackgrounded: false, + ); + + game.lifecycleStateChange(AppLifecycleState.paused); + expect(game.paused, false); + + game.lifecycleStateChange(AppLifecycleState.resumed); + expect(game.paused, false); + }); + }); }