-
-
Notifications
You must be signed in to change notification settings - Fork 932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: Pause game when backgrounded #2642
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be done like this directly in the engine (it's also a breaking change), the user should instead handle pausing the engine on events when the app is backgrounded. Not sure if there are any such events to react to when switching browser tabs though.
Should this not be the default out-of-the-box behaviour though? |
That depends on the rest of your settings though, and with a zoom it should very rarely need this except when backgrounded or having massive lags. We should follow how box2d handles it. The better solution that solves most of these problems is: #2613 |
But an app being backgrounded is not rare at all. The flutter package should handle a common event in a flutter app.
I would argue that this doesn't solve this problem with backgrounding. Our previous discussion in flame-engine/forge2d#84 was about hitting the speed limit with fast bodies, but this PR is only concerned with backgrounding. Even if we raise the speed limit with some unit conversion like zoom or a meter-to-pixels constant, the app being backgrounded for a few seconds will still hit any speed limit and break expected behaviour. |
Perhaps we could use a getter for class MyGame extends Forge2DGame {
@override
double get maxDt => 0.5;
} or even like this: class MyGame extends Forge2DGame {
MyGame(): super(
pauseWhenBackgrounded: true,
);
} |
This covers a lot more cases than just backgrounding an app though. |
Something like this should be used: https://api.flutter.dev/flutter/widgets/WidgetsBindingObserver/didChangeAppLifecycleState.html I'm not sure if that would work for web though. |
I'm unsure if the AppLifecycleState would work in this scenario, since I think the |
I'm not sure if I understand you correctly here, but you are not the one setting the EDIT: Sorry, missed that you already pushed an update! |
Nope :(
tells me that It runs as expected on Android though, so I'm guessing this is a Flutter bug since it's easy to detect tab focus with js.
Edit: Just realised that this means enum ForegroundState {
background,
foregroundFirstFrame,
foreground,
} |
I didn't follow, in which case is the app in the background but with the boolean set to true? |
Yes. What happened was:
|
a6652a5
to
b58c81d
Compare
Testing this on Android yields:
which means I need to add |
Probably better to modify how pause/resume works instead, that behaviour doesn't look like it would be intentional |
Did you have any look at this @adil192? |
Added a test in #2731 that makes sure that |
No sorry I've been quite busy recently |
5fc5e4c
to
5f672df
Compare
With some extra logging (and rebasing the branch), it seems everything is in order.
|
@adil192 did you try this on iOS? |
I haven't, no. I don't have access to a macOS/iOS device currently |
Alright, we'll have a test on those then. Which platforms have you tried? |
Just Android at the moment. I can test other platforms if you want me to |
Please do, I guess you at least have access to one desktop platform and web too? |
Alright we had a chat about this, let's make it a breaking change and set it to |
This doesn't work on Windows when you minimise an application, it still updates as if it was foregounded. I assume that's another implementation that Flutter needs. The only lifecycleStateChanges are from when you dispose of the
The web implementation is similarly missing, see flutter/flutter#53107. I'm guessing that AppLifecycleState is only supported on Android and iOS. Do you still want to make |
Yeah, we should mention it in the docs though. |
Some small issues that needs to be fixed:
|
I will do this shortly! |
It seems to somehow break the lifecycle according to one of the lifecycle tests 🤔 |
a3bfd07
to
61998e3
Compare
fix: Pause forge2d when backgrounded chore: temporarily add print statements Revert "chore: temporarily add print statements" This reverts commit 5f672df. fix: prevent duplicate `pauseEngine` calls ref: move `pauseWhenBackgrounded` to `FlameGame` fix: remove outdated comment ref: move test to flame package chore: dart format . chore: remove unused import ref: move variables closer to usage ref: remove `pauseWhenBackgrounded` from constructor feat: make pauseWhenBackgrounded true by default docs: mention only working on mobile docs: add doc for pauseWhenBackgrounded flag
61998e3
to
c0c900f
Compare
With some added events: Expected: [
'onGameResize',
'onLoad',
'onMount',
'update',
'render',
'update',
'onRemove',
'onDispose',
'onGameResize',
'onMount',
'update', // <-- this is missing
'render'
]
Actual: [
'onGameResize',
'onLoad',
'onMount',
'update',
// 'lifecycleStateChange(AppLifecycleState.resumed)',
'render',
'update',
// 'lifecycleStateChange(AppLifecycleState.paused)',
'onRemove',
'onDispose',
'onGameResize',
'onMount',
// 'lifecycleStateChange(AppLifecycleState.resumed)',
'render'
] It looks like the first update is discarded because of the use of |
Unfortunately not, we need to be calling |
@@ -254,6 +254,9 @@ class GameWidgetState<T extends Game> extends State<GameWidget<T>> { | |||
currentGame = widget.game!; | |||
} | |||
currentGame.addGameStateListener(_onGameStateChange); | |||
currentGame.lifecycleStateChange( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, it's pretty weird to call these manually since these state changes should be coming from the framework.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes more sense for the game to act identically to if the app is backgrounded when really it was just unmounted.
This makes the link between "backgrounding the app" and "backgrounding the game (widget)"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plus as an added bonus, it makes the behaviour more consistent on non-mobile platforms since the game will be paused when the widget is disposed of
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean, it feels like the state machine could become inconsistent with the one on Flutter's side though, which might confuse developers and be bug prone.
@@ -698,6 +698,28 @@ void main() { | |||
}); | |||
}); | |||
}); | |||
|
|||
group('pauseWhenBackgrounded:', () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some tests where it is using a widget and not calling lifecycleStateChange
manually? There should be some other widget tests that you can take inspiration from.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add some tests, so that we can get this PR into the release.
Description
When the game is backgrounded and then resumed, the
update
function is called with a very largedt
time step.This might cause the physics engine to hit the maximum translation per timestep, causing moving bodies to slow down to almost-zero velocities.
This PR makes the game pause if the game is backgrounded. If you'd like to change this behaviour, set the
pauseWhenBackgrounded
flag totrue
(default) orfalse
.This is currently not working on the web, see flutter/flutter#53107.
You can view previous discussion about this here: flame-engine/forge2d#84.
Checklist
docs
and added dartdoc comments with///
.examples
ordocs
.Breaking Change?
Migration instructions
If you override the
lifecycleStateChange
method in yourGame
class, you must now callsuper.lifecycleStateChange
.If you'd like the game not to pause when backgrounded, set the
pauseWhenBackgrounded
flag in yourGame
class tofalse
.Related Issues
Closes flame-engine/forge2d#84