Skip to content
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: Add TimeTrackComponent and ChildCounterComponent #2846

Merged
merged 13 commits into from
Nov 21, 2023
1 change: 1 addition & 0 deletions .github/.cspell/dart_dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ endtemplate # Use @endtemplate to close a @template block in dartdoc
pubspec # dependency and configuration file of every Dart project
typedefs # plural of typedef
intelli # JetBrain's IDE
writeln # StringBuffer.writeln
2 changes: 1 addition & 1 deletion doc/flame/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ removing it from the tree. This affects the visibility of the component and all
/// Example that implements HasVisibility
class MyComponent extends PositionComponent with HasVisibility {}

/// Usage of the isVisible property
/// Usage of the isVisible property
final myComponent = MyComponent();
add(myComponent);

Expand Down
42 changes: 42 additions & 0 deletions doc/flame/other/debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,45 @@ commonly want to show the current FPS somewhere when the `FpsComponent` is used.


[TextComponent]: ../rendering/text_rendering.md#textcomponent


### ChildCounterComponent

`ChildCounterComponent` is a component that can be added to a game and will every second render
the number of children of type `T` from a component. So for example:
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved

```dart
add(
ChildCounterComponent<SpriteAnimationComponent>(
target: world,
),
);
```

Will render the number of `SpriteAnimationComponent` that are children of the game `world`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to merge this with "So for example:" and put it above the code, since it sounds like it is continuing that sentence.

So for example, the following will render the number of SpriteAnimationComponent that are children of the game world:



### TimeTrackComponent

This component allows developers to track time spent inside their code. This can be useful for
performance debugging time spent in certain parts of the code.

To use it, add it to your game somewhere (since this is a debug feature, we advise to only add the
component in a debug build/flavor):

```dart
add(TimeTrackComponent());
```

Then in the code section that you want to track time, do the following:

```dart
void update(double dt) {
TimeTrackComponent.start('MyComponent.update');
// ...
TimeTrackComponent.end('MyComponent.update');
}
```

With the calls above, the added `TimeTrackComponent` will render the elapsed time in
microseconds.
2 changes: 2 additions & 0 deletions packages/flame/lib/components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export 'src/camera/camera_component.dart' show CameraComponent;
export 'src/camera/world.dart' show World;
export 'src/collisions/has_collision_detection.dart';
export 'src/collisions/hitboxes/screen_hitbox.dart';
export 'src/components/child_counter_component.dart';
export 'src/components/clip_component.dart';
export 'src/components/components_notifier.dart';
export 'src/components/core/component.dart';
Expand Down Expand Up @@ -48,6 +49,7 @@ export 'src/components/sprite_group_component.dart';
export 'src/components/text_box_component.dart';
export 'src/components/text_component.dart';
export 'src/components/text_element_component.dart';
export 'src/components/time_track_component.dart';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move these to a debug.dart barrel file like we discussed :)

export 'src/components/timer_component.dart';
export 'src/extensions/vector2.dart';
export 'src/geometry/circle_component.dart';
Expand Down
42 changes: 42 additions & 0 deletions packages/flame/lib/src/components/child_counter_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'dart:async';
import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/text.dart';

/// A debug component that shows the number of children of a given type from
/// a target component.
///
/// Add it to the game to start seeing the count.
class ChildCounterComponent<T> extends TextComponent {
ChildCounterComponent({
required this.target,
super.position,
});

final Component target;
late String _label;

@override
FutureOr<void> onLoad() async {
await super.onLoad();
_label = T.toString();

textRenderer = TextPaint(
style: const TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 12,
),
);

add(
TimerComponent(
period: 1,
repeat: true,
onTick: () {
text = '$_label: ${target.children.whereType<T>().length}';
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
},
),
);
}
}
65 changes: 65 additions & 0 deletions packages/flame/lib/src/components/time_track_component.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import 'dart:async';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you create a subdirectory inside component called debug too where these can live? And eventually we can move over other ones too, like the ones tracking fps (probably add those to the debug barrel file too).

import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/text.dart';

/// A component that offers a simple way to track time spent in different parts
/// of your game.
///
/// This component is meant to be used for debugging purposes only and should
/// not be added in production builds.
///
/// To start tracking time, call [TimeTrackComponent.start] with an identifier
/// and [TimeTrackComponent.end] with that same identifier to finish tracking.
///
/// To see the recorded times, simply add this component to your game.
class TimeTrackComponent extends TextComponent {
TimeTrackComponent({super.position});

static final Map<String, int> _startTimes = {};
static final Map<String, int> _endTimes = {};

static void clear() {
_startTimes.clear();
_endTimes.clear();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, do we really want all these to be static?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is also part of the rough implementation. But at the same time, I thought that keeping then static would make the tracking be less impactful and make the tracking more accurate? But anyhow totally open to suggestion, at the time I just did it static because I needed something quick.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if it is an explicit debug component it can probably just be kept as static, since debugging is supposed to be "quick & dirty" anyways.


static void start(String name) {
_startTimes[name] = DateTime.now().microsecondsSinceEpoch;
}

static void end(String name) {
_endTimes[name] = DateTime.now().microsecondsSinceEpoch;
}

@override
FutureOr<void> onLoad() async {
await super.onLoad();

textRenderer = TextPaint(
style: const TextStyle(
color: Color(0xFFFFFFFF),
fontSize: 12,
),
);

add(
TimerComponent(
period: 1,
repeat: true,
onTick: () {
final sb = StringBuffer();
for (final entry in _startTimes.entries) {
final name = entry.key;
final startTime = entry.value;
final endTime = _endTimes[name];
final duration = endTime == null ? 0 : endTime - startTime;
sb.writeln('$name: $duration');
}
text = sb.toString();
},
),
);
}
}