Эх сурвалжийг харах

[flutter] Clean-up asset bundle constructors, add Flame integration examples.

Mario Zechner 2 жил өмнө
parent
commit
1c0a860139

+ 6 - 1
spine-flutter/CHANGELOG.md

@@ -1,5 +1,10 @@
+# 0.0.4
+* Clean-up `fromAsset()` factory methods so the atlas comes before skeleton data file name.
+* Rename `Vector2` to `Vec2`.
+* Make the bundle configurable in `SpineWidget.asset()`.
+
 # 0.0.3
-* Lower macOS deployment target to 10.11
+* Lower macOS deployment target to 10.11.
 
 # 0.0.2
 * Fix package name in build system `spine_flutter` > `esotericsoftware_spine_flutter`.

+ 1 - 1
spine-flutter/example/lib/animation_state_events.dart

@@ -36,7 +36,7 @@ class AnimationStateEvents extends StatelessWidget {
         children: [
           const Text("See output in console!"),
           Expanded(
-              child: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller)
+              child: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
           )
         ]
       )

+ 2 - 1
spine-flutter/example/lib/dress_up.dart

@@ -4,6 +4,7 @@ import 'dart:ui' as ui;
 import 'package:flutter/material.dart';
 import 'package:flutter/painting.dart' as painting;
 import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
+import 'package:flutter/services.dart';
 import 'package:raw_image_provider/raw_image_provider.dart';
 
 class DressUp extends StatefulWidget {
@@ -25,7 +26,7 @@ class DressUpState extends State<DressUp> {
   void initState() {
     reportLeaks();
     super.initState();
-    SkeletonDrawable.fromAsset("assets/mix-and-match-pro.skel", "assets/mix-and-match.atlas").then((drawable) async {
+    SkeletonDrawable.fromAsset("assets/mix-and-match.atlas", "assets/mix-and-match-pro.skel").then((drawable) async {
       for (var skin in drawable.skeletonData.getSkins()) {
         if (skin.getName() == "default") continue;
 

+ 145 - 0
spine-flutter/example/lib/flame_example.dart

@@ -0,0 +1,145 @@
+import 'dart:math';
+
+import 'package:flame/components.dart';
+import 'package:flame/game.dart';
+import 'package:flutter/material.dart';
+import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
+
+class SpineComponent extends PositionComponent {
+  final BoundsProvider _boundsProvider;
+  final SkeletonDrawable _drawable;
+  late final Bounds _bounds;
+  final bool _ownsDrawable;
+
+  SpineComponent(this._drawable, {
+        bool ownsDrawable = false,
+        BoundsProvider boundsProvider = const SetupPoseBounds(),
+        super.position,
+        super.scale,
+        double super.angle = 0.0,
+        Anchor super.anchor = Anchor.topLeft,
+        super.children,
+        super.priority,
+      }) :
+        _ownsDrawable = ownsDrawable,
+        _boundsProvider = boundsProvider {
+    _drawable.update(0);
+    _bounds = _boundsProvider.computeBounds(_drawable);
+    size = Vector2(_bounds.width, _bounds.height);
+  }
+
+  static Future<SpineComponent> fromAssets(String atlasFile, String skeletonFile, {
+        AssetBundle? bundle, BoundsProvider boundsProvider = const SetupPoseBounds(),
+        Vector2? position,
+        Vector2? scale,
+        double angle = 0.0,
+        Anchor anchor = Anchor.topLeft,
+        Iterable<Component>? children,
+        int? priority,
+      }) async {
+    final drawable = await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle);
+    return SpineComponent(
+        drawable,
+        ownsDrawable: true,
+        boundsProvider: boundsProvider,
+        position: position,
+        scale: scale,
+        angle: angle,
+        anchor: anchor,
+        children: children,
+        priority: priority);
+  }
+
+  @override
+  void onRemove() {
+    if (_ownsDrawable) _drawable.dispose();
+  }
+  
+  @override
+  void update(double dt) {
+    _drawable.update(dt);
+  }
+  
+  @override
+  void render(Canvas canvas) {
+    canvas.save();
+    canvas.translate(-_bounds.x, -_bounds.y);
+    _drawable.renderToCanvas(canvas);
+    canvas.restore();
+  }
+
+  get animationState => _drawable.animationState;
+  get animationStateData => _drawable.animationStateData;
+  get skeleton => _drawable.skeleton;
+}
+
+class SimpleFlameExample extends FlameGame {
+  late final SpineComponent spineboy;
+
+  @override
+  Future<void> onLoad() async {
+    // Load the Spineboy atlas and skeleton data from asset files
+    // and create a SpineComponent from them, scaled down and
+    // centered on the screen
+    spineboy = await SpineComponent.fromAssets(
+        "assets/spineboy.atlas", "assets/spineboy-pro.skel",
+        scale: Vector2(0.4, 0.4),
+        anchor: Anchor.center,
+        position: Vector2(size.x / 2, size.y / 2)
+    );
+
+    // Set the "walk" animation on track 0 in looping mode
+    spineboy.animationState.setAnimationByName(0, "walk", true);
+    await add(spineboy);
+  }
+}
+
+class PreloadAndShareSpineDataExample extends FlameGame {
+  late final SkeletonData cachedSkeletonData;
+  late final Atlas cachedAtlas;
+  late final List<SpineComponent> spineboys;
+
+  @override
+  Future<void> onLoad() async {
+    // Pre-load the atlas and skeleton data once.
+    cachedAtlas = await Atlas.fromAsset("assets/spineboy.atlas");
+    cachedSkeletonData = await SkeletonData.fromAsset(cachedAtlas, "assets/spineboy-pro.skel");
+
+    // Instantiate many spineboys from the pre-loaded data. Each SpineComponent
+    // gets their own SkeletonDrawable copy derived from the cached data. The
+    // SkeletonDrawable copies do not own the underlying skeleton data and atlas.
+    final rng = Random();
+    for (int i = 0; i < 100; i++) {
+      final drawable = SkeletonDrawable(cachedAtlas, cachedSkeletonData, false);
+      final scale = 0.1 + rng.nextDouble() * 0.2;
+      final position = Vector2(rng.nextDouble() * size.x, rng.nextDouble() * size.y);
+      final spineboy = SpineComponent(
+          drawable,
+          scale: Vector2(scale, scale),
+          position: position
+      );
+      spineboy.animationState.setAnimationByName(0, "walk", true);
+      await add(spineboy);
+    }
+  }
+
+  @override
+  void onRemove() {
+    // Dispose the pre-loaded atlas and skeleton data when the game/scene is removed
+    cachedAtlas.dispose();
+    cachedSkeletonData.dispose();
+  }
+}
+
+class SpineFlameGameWidget extends StatelessWidget {
+  final FlameGame game;
+  const SpineFlameGameWidget(this.game, {super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(title: const Text('Flame Integration')),
+      body: GameWidget(game: game)
+    );
+  }
+}

+ 2 - 2
spine-flutter/example/lib/ik_following.dart

@@ -25,7 +25,7 @@ class IkFollowingState extends State<IkFollowing> {
       if (worldPosition == null) return;
       var bone = controller.skeleton.findBone("crosshair");
       if (bone == null) return;
-      var position = bone.getParent()?.worldToLocal(worldPosition.dx, worldPosition.dy) ?? Vector2(0, 0);
+      var position = bone.getParent()?.worldToLocal(worldPosition.dx, worldPosition.dy) ?? Vec2(0, 0);
       bone.setX(position.x);
       bone.setY(position.y);
     });
@@ -44,7 +44,7 @@ class IkFollowingState extends State<IkFollowing> {
         body: GestureDetector(
           onPanDown: (drag) => _updateBonePosition(drag.localPosition),
           onPanUpdate: (drag) => _updateBonePosition(drag.localPosition),
-          child: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
+          child: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
         ));
   }
 }

+ 26 - 1
spine-flutter/example/lib/main.dart

@@ -1,12 +1,13 @@
 import 'package:flutter/material.dart';
+import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
 
+import 'flame_example.dart';
 import 'simple_animation.dart';
 import 'animation_state_events.dart';
 import 'pause_play_animation.dart';
 import 'skins.dart';
 import 'dress_up.dart';
 import 'ik_following.dart';
-import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
 
 class ExampleSelector extends StatelessWidget {
   const ExampleSelector({super.key});
@@ -91,6 +92,30 @@ class ExampleSelector extends StatelessWidget {
                   );
                 },
               ),
+              spacer,
+              ElevatedButton(
+                child: const Text('Flame: Simple Example'),
+                onPressed: () {
+                  Navigator.push(
+                    context,
+                    MaterialPageRoute<void>(
+                      builder: (context) => SpineFlameGameWidget(SimpleFlameExample()),
+                    ),
+                  );
+                },
+              ),
+              spacer,
+              ElevatedButton(
+                child: const Text('Flame: Pre-load and share Spine data'),
+                onPressed: () {
+                  Navigator.push(
+                    context,
+                    MaterialPageRoute<void>(
+                      builder: (context) => SpineFlameGameWidget(PreloadAndShareSpineDataExample()),
+                    ),
+                  );
+                },
+              ),
               spacer
             ]
           )

+ 1 - 1
spine-flutter/example/lib/pause_play_animation.dart

@@ -33,7 +33,7 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
 
     return Scaffold(
       appBar: AppBar(title: const Text('Play/Pause')),
-      body: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
+      body: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
       floatingActionButton: FloatingActionButton(
         onPressed: _togglePlay,
         child: Icon(isPlaying ? Icons.pause : Icons.play_arrow),

+ 3 - 4
spine-flutter/example/lib/simple_animation.dart

@@ -14,10 +14,9 @@ class SimpleAnimation extends StatelessWidget {
 
     return Scaffold(
       appBar: AppBar(title: const Text('Simple Animation')),
-      body: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
-      // body: SpineWidget.file("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", controller),
-      // body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
-      // body: SpineWidget.asset("assets/skeleton.json", "assets/skeleton.atlas", controller, alignment: Alignment.topLeft, fit: BoxFit.cover),
+      body: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller),
+      // body: SpineWidget.file( "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.skel", controller),
+      // body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy.atlas", "https://marioslab.io/dump/spineboy/spineboy-pro.json"),
     );
   }
 }

+ 1 - 1
spine-flutter/example/lib/skins.dart

@@ -17,7 +17,7 @@ class SkinsState extends State<Skins> {
   @override
   void initState() {
     super.initState();
-    SkeletonDrawable.fromAsset("assets/mix-and-match-pro.skel", "assets/mix-and-match.atlas").then((drawable) {
+    SkeletonDrawable.fromAsset("assets/mix-and-match.atlas", "assets/mix-and-match-pro.skel").then((drawable) {
       for (var skin in drawable.skeletonData.getSkins()) {
         _selectedSkins[skin.getName()] = false;
       }

+ 16 - 2
spine-flutter/example/pubspec.lock

@@ -42,7 +42,7 @@ packages:
       path: ".."
       relative: true
     source: path
-    version: "0.0.2"
+    version: "0.0.3"
   ffi:
     dependency: transitive
     description:
@@ -50,6 +50,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.1"
+  flame:
+    dependency: "direct main"
+    description:
+      name: flame
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.4.0"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -111,6 +118,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.8.0"
+  ordered_set:
+    dependency: transitive
+    description:
+      name: ordered_set
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "5.0.0"
   path:
     dependency: transitive
     description:
@@ -174,4 +188,4 @@ packages:
     version: "2.1.2"
 sdks:
   dart: ">=2.17.6 <3.0.0"
-  flutter: ">=2.11.0"
+  flutter: ">=3.3.0"

+ 1 - 0
spine-flutter/example/pubspec.yaml

@@ -14,6 +14,7 @@ dependencies:
     path: ../
   cupertino_icons: ^1.0.2
   raw_image_provider: ^0.2.0
+  flame: ^1.4.0
 
 dev_dependencies:
   flutter_lints: ^2.0.0

+ 22 - 18
spine-flutter/lib/spine_flutter.dart

@@ -50,11 +50,11 @@ class Bounds {
   Bounds(this.x, this.y, this.width, this.height);
 }
 
-class Vector2 {
+class Vec2 {
   double x;
   double y;
 
-  Vector2(this.x, this.y);
+  Vec2(this.x, this.y);
 }
 
 class Atlas {
@@ -99,8 +99,9 @@ class Atlas {
     return Atlas._(atlas, atlasPages, atlasPagePaints);
   }
 
-  static Future<Atlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
-    return _load(atlasFileName, (file) async => (await assetBundle.load(file)).buffer.asUint8List());
+  static Future<Atlas> fromAsset(String atlasFileName, {AssetBundle? bundle}) async {
+    bundle ??= rootBundle;
+    return _load(atlasFileName, (file) async => (await bundle!.load(file)).buffer.asUint8List());
   }
 
   static Future<Atlas> fromFile(String atlasFileName) async {
@@ -160,11 +161,12 @@ class SkeletonData {
     return data;
   }
 
-  static Future<SkeletonData> fromAsset(AssetBundle assetBundle, Atlas atlas, String skeletonFile) async {
+  static Future<SkeletonData> fromAsset(Atlas atlas, String skeletonFile, {AssetBundle? bundle}) async {
+    bundle ??= rootBundle;
     if (skeletonFile.endsWith(".json")) {
-      return fromJson(atlas, await assetBundle.loadString(skeletonFile));
+      return fromJson(atlas, await bundle.loadString(skeletonFile));
     } else {
-      return fromBinary(atlas, (await assetBundle.load(skeletonFile)).buffer.asUint8List());
+      return fromBinary(atlas, (await bundle.load(skeletonFile)).buffer.asUint8List());
     }
   }
 
@@ -652,16 +654,16 @@ class Bone {
     _bindings.spine_bone_set_to_setup_pose(_bone);
   }
 
-  Vector2 worldToLocal(double worldX, double worldY) {
+  Vec2 worldToLocal(double worldX, double worldY) {
     final local = _bindings.spine_bone_world_to_local(_bone, worldX, worldY);
-    final result = Vector2(_bindings.spine_vector_get_x(local), _bindings.spine_vector_get_y(local));
+    final result = Vec2(_bindings.spine_vector_get_x(local), _bindings.spine_vector_get_y(local));
     _allocator.free(local);
     return result;
   }
 
-  Vector2 localToWorld(double localX, double localY) {
+  Vec2 localToWorld(double localX, double localY) {
     final world = _bindings.spine_bone_local_to_world(_bone, localX, localY);
-    final result = Vector2(_bindings.spine_vector_get_x(world), _bindings.spine_vector_get_y(world));
+    final result = Vec2(_bindings.spine_vector_get_x(world), _bindings.spine_vector_get_y(world));
     _allocator.free(world);
     return result;
   }
@@ -1593,9 +1595,9 @@ class PathAttachment extends VertexAttachment<spine_path_attachment> {
 class PointAttachment extends Attachment<spine_point_attachment> {
   PointAttachment._(spine_point_attachment attachment) : super._(attachment);
 
-  Vector2 computeWorldPosition(Bone bone) {
+  Vec2 computeWorldPosition(Bone bone) {
     final position = _bindings.spine_point_attachment_compute_world_position(_attachment, bone._bone);
-    final result = Vector2(_bindings.spine_vector_get_x(position), _bindings.spine_vector_get_y(position));
+    final result = Vec2(_bindings.spine_vector_get_x(position), _bindings.spine_vector_get_y(position));
     _allocator.free(position);
     return result;
   }
@@ -3256,21 +3258,23 @@ class SkeletonDrawable {
     skeleton = Skeleton._(_bindings.spine_skeleton_drawable_get_skeleton(_drawable));
     animationStateData = AnimationStateData._(_bindings.spine_skeleton_drawable_get_animation_state_data(_drawable));
     animationState = AnimationState._(_bindings.spine_skeleton_drawable_get_animation_state(_drawable), _bindings.spine_skeleton_drawable_get_animation_state_events(_drawable));
+    skeleton.updateWorldTransform();
   }
 
-  static Future<SkeletonDrawable> fromAsset(String skeletonFile, String atlasFile) async {
-    var atlas = await Atlas.fromAsset(rootBundle, atlasFile);
-    var skeletonData = await SkeletonData.fromAsset(rootBundle, atlas, skeletonFile);
+  static Future<SkeletonDrawable> fromAsset(String atlasFile, String skeletonFile, {AssetBundle? bundle}) async {
+    bundle ??= rootBundle;
+    var atlas = await Atlas.fromAsset(atlasFile, bundle: bundle);
+    var skeletonData = await SkeletonData.fromAsset(atlas, skeletonFile, bundle: bundle);
     return SkeletonDrawable(atlas, skeletonData, true);
   }
 
-  static Future<SkeletonDrawable> fromFile(String skeletonFile, String atlasFile) async {
+  static Future<SkeletonDrawable> fromFile(String atlasFile, String skeletonFile) async {
     var atlas = await Atlas.fromFile(atlasFile);
     var skeletonData = await SkeletonData.fromFile(atlas, skeletonFile);
     return SkeletonDrawable(atlas, skeletonData, true);
   }
 
-  static Future<SkeletonDrawable> fromHttp(String skeletonFile, String atlasFile) async {
+  static Future<SkeletonDrawable> fromHttp(String atlasFile, String skeletonFile) async {
     var atlas = await Atlas.fromUrl(atlasFile);
     var skeletonData = await SkeletonData.fromHttp(atlas, skeletonFile);
     return SkeletonDrawable(atlas, skeletonData, true);

+ 17 - 11
spine-flutter/lib/spine_widget.dart

@@ -2,6 +2,7 @@ import 'dart:math';
 
 import 'package:flutter/rendering.dart' as rendering;
 import 'package:flutter/scheduler.dart';
+import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
 
 import 'spine_flutter.dart';
@@ -135,6 +136,7 @@ class ComputedBounds extends BoundsProvider {
 
 class SpineWidget extends StatefulWidget {
   final AssetType _assetType;
+  AssetBundle? _bundle;
   final String? _skeletonFile;
   final String? _atlasFile;
   final SkeletonDrawable? _drawable;
@@ -144,32 +146,37 @@ class SpineWidget extends StatefulWidget {
   final BoundsProvider _boundsProvider;
   final bool _sizedByBounds;
 
-  const SpineWidget.asset(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+  SpineWidget.asset(this._skeletonFile, this._atlasFile, this._controller, {AssetBundle? bundle, BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.Asset,
         _fit = fit ?? BoxFit.contain,
         _alignment = alignment ?? Alignment.center,
         _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
         _sizedByBounds = sizedByBounds ?? false,
-        _drawable = null;
+        _drawable = null {
+    _bundle = bundle ?? rootBundle;
+  }
 
-  const SpineWidget.file(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+  SpineWidget.file(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.File,
+        _bundle = null,
         _fit = fit ?? BoxFit.contain,
         _alignment = alignment ?? Alignment.center,
         _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
         _sizedByBounds = sizedByBounds ?? false,
         _drawable = null;
 
-  const SpineWidget.http(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+  SpineWidget.http(this._skeletonFile, this._atlasFile, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.Http,
+        _bundle = null,
         _fit = fit ?? BoxFit.contain,
         _alignment = alignment ?? Alignment.center,
         _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
         _sizedByBounds = sizedByBounds ?? false,
         _drawable = null;
 
-  const SpineWidget.drawable(this._drawable, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
+  SpineWidget.drawable(this._drawable, this._controller, {BoxFit? fit, Alignment? alignment, BoundsProvider? boundsProvider, bool? sizedByBounds, super.key})
       : _assetType = AssetType.Drawable,
+        _bundle = null,
         _fit = fit ?? BoxFit.contain,
         _alignment = alignment ?? Alignment.center,
         _boundsProvider = boundsProvider ?? const SetupPoseBounds(),
@@ -189,26 +196,25 @@ class _SpineWidgetState extends State<SpineWidget> {
     if (widget._assetType == AssetType.Drawable) {
       loadDrawable(widget._drawable!);
     } else {
-      loadFromAsset(widget._skeletonFile!, widget._atlasFile!, widget._assetType);
+      loadFromAsset(widget._bundle, widget._skeletonFile!, widget._atlasFile!, widget._assetType);
     }
   }
 
   void loadDrawable(SkeletonDrawable drawable) {
     widget._controller._initialize(drawable);
-    drawable.update(0);
     setState(() {});
   }
 
-  void loadFromAsset(String skeletonFile, String atlasFile, AssetType assetType) async {
+  void loadFromAsset(AssetBundle? bundle, String atlasFile, String skeletonFile, AssetType assetType) async {
     switch (assetType) {
       case AssetType.Asset:
-        loadDrawable(await SkeletonDrawable.fromAsset(skeletonFile, atlasFile));
+        loadDrawable(await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle));
         break;
       case AssetType.File:
-        loadDrawable(await SkeletonDrawable.fromFile(skeletonFile, atlasFile));
+        loadDrawable(await SkeletonDrawable.fromFile(atlasFile, skeletonFile));
         break;
       case AssetType.Http:
-        loadDrawable(await SkeletonDrawable.fromHttp(skeletonFile, atlasFile));
+        loadDrawable(await SkeletonDrawable.fromHttp(atlasFile, skeletonFile));
         break;
       case AssetType.Drawable:
         throw Exception("Drawable can not be loaded via loadFromAsset().");