Skip to content

Commit

Permalink
feat: Adding component snapshot to Dev tools (#3261)
Browse files Browse the repository at this point in the history
  • Loading branch information
erickzanardo authored Aug 9, 2024
1 parent 4af00c1 commit 1a57491
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:ui';

import 'package:flame/components.dart';
import 'package:flame/src/devtools/dev_tools_connector.dart';

class ComponentSnapshotConnector extends DevToolsConnector {
@override
void init() {
registerExtension(
'ext.flame_devtools.getComponentSnapshot',
(method, parameters) async {
Image? image;
final id = int.tryParse(parameters['id'] ?? '');
game.propagateToChildren<Component>(
(c) {
if (c.hashCode == id) {
final pictureRecorder = PictureRecorder();

final canvas = Canvas(pictureRecorder);

// I am not sure how we could calculate the size of a component
// that isn't a PositionComponent, so for now we will just use
// an arbitrary size.
var width = 100;
var height = 100;

if (c is PositionComponent) {
width = c.width.toInt();
height = c.height.toInt();

// Translate the canvas so that the component is
// drawn at the 0,0
canvas.translate(-c.x, -c.y);
}

c.renderTree(canvas);

final picture = pictureRecorder.endRecording();

image = picture.toImageSync(width, height);

return false;
}
return true;
},
);

if (image != null) {
final byteData = await image!.toByteData(format: ImageByteFormat.png);
final buffer = byteData!.buffer.asUint8List();
final snapshot = base64Encode(buffer);
return ServiceExtensionResponse.result(
json.encode({
'id': id,
'snapshot': snapshot,
}),
);
}

return ServiceExtensionResponse.result(
json.encode({
'id': id,
'snapshot': '',
}),
);
},
);
}
}
2 changes: 2 additions & 0 deletions packages/flame/lib/src/devtools/dev_tools_service.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flame/game.dart';
import 'package:flame/src/devtools/connectors/component_count_connector.dart';
import 'package:flame/src/devtools/connectors/component_snapshot_connector.dart';
import 'package:flame/src/devtools/connectors/component_tree_connector.dart';
import 'package:flame/src/devtools/connectors/debug_mode_connector.dart';
import 'package:flame/src/devtools/connectors/game_loop_connector.dart';
Expand Down Expand Up @@ -35,6 +36,7 @@ class DevToolsService {
ComponentCountConnector(),
ComponentTreeConnector(),
GameLoopConnector(),
ComponentSnapshotConnector(),
];

/// This method is called every time a new game is set in the service and it
Expand Down
9 changes: 9 additions & 0 deletions packages/flame_devtools/lib/repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ sealed class Repository {
);
return stepResponse.json!['step_time'] as double;
}

static Future<String?> snapshot({String? id}) async {
final snapshotResponse =
await serviceManager.callServiceExtensionOnMainIsolate(
'ext.flame_devtools.getComponentSnapshot',
args: {'id': id},
);
return snapshotResponse.json!['snapshot'] as String?;
}
}
89 changes: 89 additions & 0 deletions packages/flame_devtools/lib/widgets/component_snapshot.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import 'package:flame/flame.dart';
import 'package:flame/widgets.dart';
import 'package:flame_devtools/repository.dart';
import 'package:flutter/material.dart' hide Image;

class ComponentSnapshot extends StatefulWidget {
const ComponentSnapshot({
required this.id,
super.key,
});

final String id;

@override
State<ComponentSnapshot> createState() => _ComponentSnapshotState();
}

class _ComponentSnapshotState extends State<ComponentSnapshot> {
late Future<String?> _snapshot;

@override
void initState() {
super.initState();

_snapshot = Repository.snapshot(id: widget.id);
}

@override
void didUpdateWidget(ComponentSnapshot oldWidget) {
super.didUpdateWidget(oldWidget);

if (oldWidget.id != widget.id) {
_snapshot = Repository.snapshot(id: widget.id);
}
}

@override
Widget build(BuildContext context) {
return FutureBuilder<String?>(
future: _snapshot,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return Base64Image(
base64: snapshot.data!,
imageId: widget.id,
);
}
return const Text('Loading snapshot...');
},
);
}
}

class Base64Image extends StatelessWidget {
const Base64Image({
required this.base64,
required this.imageId,
super.key,
});

final String base64;
final String imageId;

@override
Widget build(BuildContext context) {
final imageFuture = Flame.images.fromBase64(
imageId,
base64,
);
return FutureBuilder(
future: imageFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return SizedBox(
width: 200,
height: 200,
child: SpriteWidget(
sprite: Sprite(
snapshot.data!,
),
),
);
}
return const Text('Loading image...');
},
);
}
}
54 changes: 30 additions & 24 deletions packages/flame_devtools/lib/widgets/component_tree.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:animated_tree_view/animated_tree_view.dart';
import 'package:devtools_app_shared/ui.dart' as devtools_ui;
import 'package:flame_devtools/widgets/component_snapshot.dart';
import 'package:flame_devtools/widgets/component_tree_model.dart';
import 'package:flame_devtools/widgets/debug_mode_button.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -128,33 +129,38 @@ class ComponentSection extends ConsumerWidget {
],
),
),
Padding(
padding: const EdgeInsets.all(20),
child: node == null
? Text(
'Select a component in the tree',
style: textStyle,
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
Expanded(
child: Padding(
padding: const EdgeInsets.all(20),
child: node == null
? Text(
'Select a component in the tree',
style: textStyle,
)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Id: ${node.id}', style: textStyle),
DebugModeButton(id: node.id),
Row(
children: [
Text('Id: ${node.id}', style: textStyle),
DebugModeButton(id: node.id),
].withSpacing(),
),
Text('Type: ${node.name}', style: textStyle),
Text(
'Children: ${node.children.length}',
style: textStyle,
),
Text(
'toString:\n${node.toStringText}',
style: textStyle,
),
ComponentSnapshot(id: node.id.toString()),
].withSpacing(),
),
Text('Type: ${node.name}', style: textStyle),
Text(
'Children: ${node.children.length}',
style: textStyle,
),
Text(
'toString:\n${node.toStringText}',
style: textStyle,
),
].withSpacing(),
),
),
),
),
],
),
Expand Down

0 comments on commit 1a57491

Please sign in to comment.