Support TexturePacker image atlases

This commit is contained in:
gem
2026-06-09 12:49:01 +08:00
parent e2a584d4dc
commit 38f6e0c0c9
16 changed files with 343 additions and 26 deletions

View File

@@ -1,6 +1,7 @@
import 'dart:async' as async;
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' show Rect;
import 'package:flame_lua_runtime/runtime/diagnostics/runtime_diagnostics.dart';
import 'package:flame_lua_runtime/runtime/packages/game_package.dart';
@@ -61,6 +62,19 @@ void main() {
},
);
test('loads TexturePacker atlas frames for image resources', () async {
final resources = GameResourceManager();
final package = await _createTextureAtlasPackage('texture_atlas');
await resources.mount(package);
final idle = resources.textureFrame('ui', 'button_idle.png');
final pressed = resources.textureFrame('ui', 'button_pressed.png');
expect(idle?.rect, Rect.fromLTWH(2, 3, 40, 20));
expect(pressed?.rect, Rect.fromLTWH(44, 3, 40, 20));
expect(resources.textureFrame('ui', 'missing.png'), isNull);
});
test('exports image debug json and evicts failed records', () async {
final resources = GameResourceManager();
final package = await _createPackage('debug_json');
@@ -332,6 +346,83 @@ Future<GamePackage> _createPackage(
);
}
Future<GamePackage> _createTextureAtlasPackage(String name) async {
final root = await Directory.systemTemp.createTemp('resource_${name}_');
Directory('${root.path}/assets').createSync(recursive: true);
File('${root.path}/assets/ui.png').writeAsBytesSync(_pngBytes);
File('${root.path}/assets/ui.json').writeAsStringSync('''
{
"frames": {
"button_idle.png": {
"frame": { "x": 2, "y": 3, "w": 40, "h": 20 },
"rotated": false,
"trimmed": false
}
}
}
''');
addTearDown(() {
if (root.existsSync()) {
root.deleteSync(recursive: true);
}
});
final hashAtlas = RuntimeTextureAtlas.fromJsonString(
File('${root.path}/assets/ui.json').readAsStringSync(),
);
expect(hashAtlas.frames['button_idle.png']?.rect, Rect.fromLTWH(2, 3, 40, 20));
final arrayAtlas = RuntimeTextureAtlas.fromJsonString('''
{
"frames": [
{
"filename": "button_pressed.png",
"frame": { "x": 44, "y": 3, "w": 40, "h": 20 },
"rotated": false,
"trimmed": false
}
]
}
''');
final mergedAtlas = '''
{
"frames": {
"button_idle.png": {
"frame": { "x": 2, "y": 3, "w": 40, "h": 20 },
"rotated": false,
"trimmed": false
},
"button_pressed.png": {
"frame": { "x": ${arrayAtlas.frames['button_pressed.png']!.x}, "y": 3, "w": 40, "h": 20 },
"rotated": false,
"trimmed": false
}
}
}
''';
File('${root.path}/assets/ui.json').writeAsStringSync(mergedAtlas);
return GamePackage.file(
rootPath: root.path,
manifest: GamePackageManifest(
gameId: 'test',
name: 'Test',
version: '0.1.0',
runtimeApiVersion: 1,
entry: 'scripts/main.lua',
assetsBase: 'assets',
resources: const {
'ui': GameResource(
type: GameResourceType.image,
path: 'assets/ui.png',
atlas: 'assets/ui.json',
preload: GameResourcePreload.lazy,
),
},
),
);
}
Future<GamePackage> _createMultiImagePackage(String name) async {
final root = await Directory.systemTemp.createTemp('resource_${name}_');
Directory('${root.path}/assets').createSync(recursive: true);