Skip to content

Commit

Permalink
fix!: Update should be called before render in first tick (#2714)
Browse files Browse the repository at this point in the history
Previously render was called before the first update was called, which should be considered as a bug.
  • Loading branch information
spydon committed Sep 14, 2023
1 parent a67c625 commit 51932c0
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 14 deletions.
9 changes: 9 additions & 0 deletions packages/flame/lib/src/game/game_widget/game_widget.dart
Expand Up @@ -196,6 +196,9 @@ class GameWidgetState<T extends Game> extends State<GameWidget<T>> {
await onLoad;
}
game.mount();
if (!game.paused) {
game.update(0);
}
})();

Future<void>? _loaderFuture;
Expand Down Expand Up @@ -362,6 +365,12 @@ class GameWidgetState<T extends Game> extends State<GameWidget<T>> {
Container();
}
currentGame.onGameResize(size);
// This should only be called if the game has already been
// loaded (in the case of resizing for example), since
// update otherwise should be called after onMount.
if (!currentGame.paused && currentGame.isAttached) {
currentGame.update(0);
}
return FutureBuilder(
future: loaderFuture,
builder: (_, snapshot) {
Expand Down
Binary file modified packages/flame/test/_goldens/snapshot_test_3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 11 additions & 5 deletions packages/flame/test/components/mixins/snapshot_test.dart
Expand Up @@ -16,8 +16,8 @@ void main() {
'Snapshot should be created once',
_SnapshotTestGame.new,
(game) async {
final snapshotComponent = game.snapshotComponent;
await game.ready();
final snapshotComponent = game.snapshotComponent;

// Wait a few frames
final canvas = Canvas(PictureRecorder());
Expand All @@ -39,8 +39,8 @@ void main() {
'Should render normally when renderSnapshot is false',
() => _SnapshotTestGame(renderSnapshot: false),
(game) async {
final snapshotComponent = game.snapshotComponent;
await game.ready();
final snapshotComponent = game.snapshotComponent;

// Wait a few frames
const framesToWait = 5;
Expand Down Expand Up @@ -73,14 +73,15 @@ void main() {
'Should generate a snapshot when takeSnapshot is called',
(tester) async {
final game = _SnapshotTestGame(renderSnapshot: false);
final snapshotComponent = game.snapshotComponent;
const framesToWait = 5;
late final _MockSnapshotComponent snapshotComponent;

await tester.runAsync(() async {
final widget = GameWidget(game: game);
await tester.pumpWidget(widget);
await tester.pump();
await game.ready();
snapshotComponent = game.snapshotComponent;

// Wait a few frames
final recorder = PictureRecorder();
Expand Down Expand Up @@ -116,13 +117,14 @@ void main() {
'Should generate a transformed image',
(tester) async {
final game = _SnapshotTestGame(renderSnapshot: false);
final snapshotComponent = game.snapshotComponent;
late final _MockSnapshotComponent snapshotComponent;

await tester.runAsync(() async {
final widget = GameWidget(game: game);
await tester.pumpWidget(widget);
await tester.pump();
await game.ready();
snapshotComponent = game.snapshotComponent;

// Force a frame
final canvas = Canvas(PictureRecorder());
Expand Down Expand Up @@ -173,8 +175,12 @@ void main() {

class _SnapshotTestGame extends FlameGame {
late final _MockSnapshotComponent snapshotComponent;
bool renderSnapshot;

_SnapshotTestGame({bool renderSnapshot = true}) {
_SnapshotTestGame({this.renderSnapshot = true});

@override
Future<void> onLoad() async {
// Add a snapshot-enabled component that has it's own rendered content
snapshotComponent = _MockSnapshotComponent()
..size = Vector2(200, 200)
Expand Down
Expand Up @@ -25,6 +25,18 @@ class _MyGame extends FlameGame {
events.add('onMount');
}

@override
void update(double dt) {
super.update(dt);
events.add('update');
}

@override
void render(Canvas canvas) {
super.render(canvas);
events.add('render');
}

@override
void onRemove() {
super.onRemove();
Expand Down Expand Up @@ -207,12 +219,27 @@ void main() {
state.causeResize();

await tester.pump();
expect(events, ['onGameResize']); // no onRemove
expect(events, ['onGameResize', 'update', 'render']); // no onRemove
final game =
tester.allWidgets.whereType<GameWidget<_MyGame>>().first.game;
expect(game?.children, everyElement((Component c) => c.parent == game));
});

testWidgets('update is not called when game is paused', (tester) async {
final events = <String>[];
await tester.pumpWidget(_MyContainer(events));

events.clear();
tester.allWidgets
.whereType<GameWidget<_MyGame>>()
.first
.game
?.pauseEngine();
await tester.pump();
await tester.pump();
expect(events, ['render']);
});

testWidgets('all events are executed in the correct order', (tester) async {
final events = <String>[];
await tester.pumpWidget(_MyApp(events));
Expand All @@ -239,10 +266,15 @@ void main() {
'onGameResize',
'onLoad',
'onMount',
'update',
'render',
'update',
'onRemove',
'onDispose',
'onGameResize',
'onMount',
'update',
'render',
],
);
});
Expand Down
26 changes: 18 additions & 8 deletions packages/flame/test/game/game_widget/game_widget_pause_test.dart
Expand Up @@ -52,15 +52,21 @@ class _WrapperState extends State<_Wrapper> {
}

class _MyGame extends FlameGame {
int callCount = 0;
int updateCount = 0;
int renderCount = 0;
double timePassed = 0;

@override
void update(double dt) {
super.update(dt);
timePassed += dt;
updateCount++;
}

callCount++;
@override
void render(Canvas canvas) {
super.render(canvas);
renderCount++;
}
}

Expand All @@ -86,7 +92,8 @@ void main() {
// shouldn't run another frame on the game
await tester.pump();

expect(game.callCount, equals(2));
// Remember that there is one initial update(0) called.
expect(game.updateCount, equals(3));
},
);

Expand All @@ -105,7 +112,8 @@ void main() {
game.resumeEngine();
await tester.pump();

expect(game.callCount, equals(3));
// Remember that there is one initial update(0) called.
expect(game.updateCount, equals(4));
},
);

Expand All @@ -121,7 +129,8 @@ void main() {
await tester.tap(find.text('Toggle'));
await tester.pumpAndSettle();

expect(game.callCount, equals(2));
// Remember that there is one initial update(0) called.
expect(game.updateCount, equals(3));
},
);

Expand All @@ -132,7 +141,7 @@ void main() {
await tester.pump();
await tester.pump();

expect(game.callCount, equals(0));
expect(game.updateCount, equals(0));
},
);

Expand All @@ -149,7 +158,7 @@ void main() {
await tester.pump();
await tester.pump();

expect(game.callCount, equals(2));
expect(game.updateCount, equals(2));
},
);

Expand All @@ -173,7 +182,8 @@ void main() {
await tester.pump(const Duration(seconds: 100));
await tester.pump(frameLength);

expect(game.callCount, equals(4));
// Remember that there is one initial update(0) after mount
expect(game.updateCount, equals(5));
expect(game.timePassed, equals(3));
},
);
Expand Down

0 comments on commit 51932c0

Please sign in to comment.