Skip to content

Commit

Permalink
feat: Add HasPerformanceTracker mixin on Game (#3043)
Browse files Browse the repository at this point in the history
This PR adds a `HasPerformanceTracker` mixin on `Game`. This mixin will
allow for tracking the update and render time of the game.
  • Loading branch information
ufrshubham authored Feb 18, 2024
1 parent d409193 commit 6270353
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 22 deletions.
15 changes: 15 additions & 0 deletions doc/flame/game.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,18 @@ class MyGame extends FlameGame {

On the current Flutter stable (3.13), this flag is effectively ignored on
non-mobile platforms including the web.


## HasPerformanceTracker mixin

While optimizing a game, it can be useful to track the time it took for the game to update and render
each frame. This data can help in detecting areas of the code that are running hot. It can also help
in detecting visual areas of the game that are taking the most time to render.

To get the update and render times, just add the `HasPerformanceTracker` mixin to the game class.

```dart
class MyGame extends FlameGame with HasPerformanceTracker {
// access `updateTime` and `renderTime` getters.
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class BulletComponent extends SpriteAnimationComponent
final Vector2 deltaPosition = Vector2.zero();

BulletComponent({required super.position, super.angle})
: super(size: Vector2(10, 20));
: super(size: Vector2(10, 20), anchor: Anchor.center);

@override
Future<void> onLoad() async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,11 @@ class PlayerComponent extends SpriteAnimationComponent
with HasGameRef, CollisionCallbacks {
late TimerComponent bulletCreator;

PlayerComponent()
: super(
size: Vector2(50, 75),
position: Vector2(100, 500),
anchor: Anchor.center,
);
PlayerComponent() : super(size: Vector2(50, 75), anchor: Anchor.center);

@override
Future<void> onLoad() async {
position = game.size / 2;
add(CircleHitbox());
add(
bulletCreator = TimerComponent(
Expand Down
46 changes: 31 additions & 15 deletions examples/games/rogue_shooter/lib/rogue_shooter_game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,44 @@ import 'package:rogue_shooter/components/player_component.dart';
import 'package:rogue_shooter/components/star_background_creator.dart';

class RogueShooterGame extends FlameGame
with PanDetector, HasCollisionDetection {
with PanDetector, HasCollisionDetection, HasPerformanceTracker {
static const String description = '''
A simple space shooter game used for testing performance of the collision
detection system in Flame.
''';

late final PlayerComponent player;
late final TextComponent componentCounter;
late final TextComponent scoreText;
late final PlayerComponent _player;
late final TextComponent _componentCounter;
late final TextComponent _scoreText;

int score = 0;
final _updateTime = TextComponent(
text: 'Update time: 0ms',
position: Vector2(0, 0),
priority: 1,
);

final TextComponent _renderTime = TextComponent(
text: 'Render time: 0ms',
position: Vector2(0, 25),
priority: 1,
);

int _score = 0;

@override
Future<void> onLoad() async {
add(player = PlayerComponent());
add(_player = PlayerComponent());
addAll([
FpsTextComponent(
position: size - Vector2(0, 50),
anchor: Anchor.bottomRight,
),
scoreText = TextComponent(
_scoreText = TextComponent(
position: size - Vector2(0, 25),
anchor: Anchor.bottomRight,
priority: 1,
),
componentCounter = TextComponent(
_componentCounter = TextComponent(
position: size,
anchor: Anchor.bottomRight,
priority: 1,
Expand All @@ -40,36 +52,40 @@ class RogueShooterGame extends FlameGame

add(EnemyCreator());
add(StarBackGroundCreator());

addAll([_updateTime, _renderTime]);
}

@override
void update(double dt) {
super.update(dt);
scoreText.text = 'Score: $score';
componentCounter.text = 'Components: ${children.length}';
_scoreText.text = 'Score: $_score';
_componentCounter.text = 'Components: ${children.length}';
_updateTime.text = 'Update time: $updateTime ms';
_renderTime.text = 'Render time: $renderTime ms';
}

@override
void onPanStart(_) {
player.beginFire();
_player.beginFire();
}

@override
void onPanEnd(_) {
player.stopFire();
_player.stopFire();
}

@override
void onPanCancel() {
player.stopFire();
_player.stopFire();
}

@override
void onPanUpdate(DragUpdateInfo info) {
player.position += info.delta.global;
_player.position += info.delta.global;
}

void increaseScore() {
score++;
_score++;
}
}
1 change: 1 addition & 0 deletions packages/flame/lib/game.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export 'src/extensions/vector2.dart';
export 'src/game/flame_game.dart';
export 'src/game/game.dart';
export 'src/game/game_widget/game_widget.dart';
export 'src/game/mixins/has_performance_tracker.dart';
export 'src/game/mixins/single_game_instance.dart';
export 'src/game/notifying_vector2.dart';
export 'src/game/transform2d.dart';
Expand Down
34 changes: 34 additions & 0 deletions packages/flame/lib/src/game/mixins/has_performance_tracker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:ui';

import 'package:flame/game.dart';

/// A mixin that adds performance tracking to a game.
mixin HasPerformanceTracker on Game {
int _updateTime = 0;
int _renderTime = 0;
final _stopwatch = Stopwatch();

/// The time it took to update the game in milliseconds.
int get updateTime => _updateTime;

/// The time it took to render the game in milliseconds.
int get renderTime => _renderTime;

@override
void update(double dt) {
_stopwatch.reset();
_stopwatch.start();
super.update(dt);
_stopwatch.stop();
_updateTime = _stopwatch.elapsedMilliseconds;
}

@override
void render(Canvas canvas) {
_stopwatch.reset();
_stopwatch.start();
super.render(canvas);
_stopwatch.stop();
_renderTime = _stopwatch.elapsedMilliseconds;
}
}
46 changes: 46 additions & 0 deletions packages/flame/test/game/mixins/has_performance_tracker_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import 'dart:io';
import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets(
'tracks update and render times.',
(widgetTester) async {
final game = _GameWithPerformanceTracker(
children: [_SlowComponent()],
);

expect(game.updateTime, 0);
expect(game.renderTime, 0);

await widgetTester.pumpFrames(
GameWidget(game: game),
const Duration(seconds: 1),
);

expect(
game.updateTime,
greaterThanOrEqualTo(_SlowComponent.duration.inMilliseconds),
);
expect(
game.renderTime,
greaterThanOrEqualTo(_SlowComponent.duration.inMilliseconds),
);
},
);
}

class _GameWithPerformanceTracker extends FlameGame with HasPerformanceTracker {
_GameWithPerformanceTracker({super.children});
}

class _SlowComponent extends Component {
static const duration = Duration(milliseconds: 8);
@override
void update(double dt) => sleep(duration);
@override
void render(Canvas canvas) => sleep(duration);
}

0 comments on commit 6270353

Please sign in to comment.