mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[flutter] Clean-up asset bundle constructors, add Flame integration examples.
This commit is contained in:
parent
6efb197776
commit
1c0a860139
@ -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
|
# 0.0.3
|
||||||
* Lower macOS deployment target to 10.11
|
* Lower macOS deployment target to 10.11.
|
||||||
|
|
||||||
# 0.0.2
|
# 0.0.2
|
||||||
* Fix package name in build system `spine_flutter` > `esotericsoftware_spine_flutter`.
|
* Fix package name in build system `spine_flutter` > `esotericsoftware_spine_flutter`.
|
||||||
|
|||||||
@ -36,7 +36,7 @@ class AnimationStateEvents extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Text("See output in console!"),
|
const Text("See output in console!"),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller)
|
child: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'dart:ui' as ui;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/painting.dart' as painting;
|
import 'package:flutter/painting.dart' as painting;
|
||||||
import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
|
import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:raw_image_provider/raw_image_provider.dart';
|
import 'package:raw_image_provider/raw_image_provider.dart';
|
||||||
|
|
||||||
class DressUp extends StatefulWidget {
|
class DressUp extends StatefulWidget {
|
||||||
@ -25,7 +26,7 @@ class DressUpState extends State<DressUp> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
reportLeaks();
|
reportLeaks();
|
||||||
super.initState();
|
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()) {
|
for (var skin in drawable.skeletonData.getSkins()) {
|
||||||
if (skin.getName() == "default") continue;
|
if (skin.getName() == "default") continue;
|
||||||
|
|
||||||
|
|||||||
145
spine-flutter/example/lib/flame_example.dart
Normal file
145
spine-flutter/example/lib/flame_example.dart
Normal file
@ -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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,7 +25,7 @@ class IkFollowingState extends State<IkFollowing> {
|
|||||||
if (worldPosition == null) return;
|
if (worldPosition == null) return;
|
||||||
var bone = controller.skeleton.findBone("crosshair");
|
var bone = controller.skeleton.findBone("crosshair");
|
||||||
if (bone == null) return;
|
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.setX(position.x);
|
||||||
bone.setY(position.y);
|
bone.setY(position.y);
|
||||||
});
|
});
|
||||||
@ -44,7 +44,7 @@ class IkFollowingState extends State<IkFollowing> {
|
|||||||
body: GestureDetector(
|
body: GestureDetector(
|
||||||
onPanDown: (drag) => _updateBonePosition(drag.localPosition),
|
onPanDown: (drag) => _updateBonePosition(drag.localPosition),
|
||||||
onPanUpdate: (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),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
|
||||||
|
|
||||||
|
import 'flame_example.dart';
|
||||||
import 'simple_animation.dart';
|
import 'simple_animation.dart';
|
||||||
import 'animation_state_events.dart';
|
import 'animation_state_events.dart';
|
||||||
import 'pause_play_animation.dart';
|
import 'pause_play_animation.dart';
|
||||||
import 'skins.dart';
|
import 'skins.dart';
|
||||||
import 'dress_up.dart';
|
import 'dress_up.dart';
|
||||||
import 'ik_following.dart';
|
import 'ik_following.dart';
|
||||||
import 'package:esotericsoftware_spine_flutter/spine_flutter.dart';
|
|
||||||
|
|
||||||
class ExampleSelector extends StatelessWidget {
|
class ExampleSelector extends StatelessWidget {
|
||||||
const ExampleSelector({super.key});
|
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
|
spacer
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class PlayPauseAnimationState extends State<PlayPauseAnimation> {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Play/Pause')),
|
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(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: _togglePlay,
|
onPressed: _togglePlay,
|
||||||
child: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
|
child: Icon(isPlaying ? Icons.pause : Icons.play_arrow),
|
||||||
|
|||||||
@ -14,10 +14,9 @@ class SimpleAnimation extends StatelessWidget {
|
|||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Simple Animation')),
|
appBar: AppBar(title: const Text('Simple Animation')),
|
||||||
body: SpineWidget.asset("assets/spineboy-pro.skel", "assets/spineboy.atlas", controller),
|
body: SpineWidget.asset("assets/spineboy.atlas", "assets/spineboy-pro.skel", 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: 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-pro.json", "https://marioslab.io/dump/spineboy/spineboy.atlas"),
|
// body: const SpineWidget.http("https://marioslab.io/dump/spineboy/spineboy.atlas", "https://marioslab.io/dump/spineboy/spineboy-pro.json"),
|
||||||
// body: SpineWidget.asset("assets/skeleton.json", "assets/skeleton.atlas", controller, alignment: Alignment.topLeft, fit: BoxFit.cover),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ class SkinsState extends State<Skins> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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()) {
|
for (var skin in drawable.skeletonData.getSkins()) {
|
||||||
_selectedSkins[skin.getName()] = false;
|
_selectedSkins[skin.getName()] = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.2"
|
version: "0.0.3"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -50,6 +50,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flame:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flame
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -111,6 +118,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
|
ordered_set:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ordered_set
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -174,4 +188,4 @@ packages:
|
|||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.17.6 <3.0.0"
|
dart: ">=2.17.6 <3.0.0"
|
||||||
flutter: ">=2.11.0"
|
flutter: ">=3.3.0"
|
||||||
|
|||||||
@ -14,6 +14,7 @@ dependencies:
|
|||||||
path: ../
|
path: ../
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
raw_image_provider: ^0.2.0
|
raw_image_provider: ^0.2.0
|
||||||
|
flame: ^1.4.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.0
|
flutter_lints: ^2.0.0
|
||||||
|
|||||||
@ -50,11 +50,11 @@ class Bounds {
|
|||||||
Bounds(this.x, this.y, this.width, this.height);
|
Bounds(this.x, this.y, this.width, this.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Vector2 {
|
class Vec2 {
|
||||||
double x;
|
double x;
|
||||||
double y;
|
double y;
|
||||||
|
|
||||||
Vector2(this.x, this.y);
|
Vec2(this.x, this.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Atlas {
|
class Atlas {
|
||||||
@ -99,8 +99,9 @@ class Atlas {
|
|||||||
return Atlas._(atlas, atlasPages, atlasPagePaints);
|
return Atlas._(atlas, atlasPages, atlasPagePaints);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Atlas> fromAsset(AssetBundle assetBundle, String atlasFileName) async {
|
static Future<Atlas> fromAsset(String atlasFileName, {AssetBundle? bundle}) async {
|
||||||
return _load(atlasFileName, (file) async => (await assetBundle.load(file)).buffer.asUint8List());
|
bundle ??= rootBundle;
|
||||||
|
return _load(atlasFileName, (file) async => (await bundle!.load(file)).buffer.asUint8List());
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Atlas> fromFile(String atlasFileName) async {
|
static Future<Atlas> fromFile(String atlasFileName) async {
|
||||||
@ -160,11 +161,12 @@ class SkeletonData {
|
|||||||
return data;
|
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")) {
|
if (skeletonFile.endsWith(".json")) {
|
||||||
return fromJson(atlas, await assetBundle.loadString(skeletonFile));
|
return fromJson(atlas, await bundle.loadString(skeletonFile));
|
||||||
} else {
|
} 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);
|
_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 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);
|
_allocator.free(local);
|
||||||
return result;
|
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 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);
|
_allocator.free(world);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1593,9 +1595,9 @@ class PathAttachment extends VertexAttachment<spine_path_attachment> {
|
|||||||
class PointAttachment extends Attachment<spine_point_attachment> {
|
class PointAttachment extends Attachment<spine_point_attachment> {
|
||||||
PointAttachment._(spine_point_attachment attachment) : super._(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 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);
|
_allocator.free(position);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -3256,21 +3258,23 @@ class SkeletonDrawable {
|
|||||||
skeleton = Skeleton._(_bindings.spine_skeleton_drawable_get_skeleton(_drawable));
|
skeleton = Skeleton._(_bindings.spine_skeleton_drawable_get_skeleton(_drawable));
|
||||||
animationStateData = AnimationStateData._(_bindings.spine_skeleton_drawable_get_animation_state_data(_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));
|
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 {
|
static Future<SkeletonDrawable> fromAsset(String atlasFile, String skeletonFile, {AssetBundle? bundle}) async {
|
||||||
var atlas = await Atlas.fromAsset(rootBundle, atlasFile);
|
bundle ??= rootBundle;
|
||||||
var skeletonData = await SkeletonData.fromAsset(rootBundle, atlas, skeletonFile);
|
var atlas = await Atlas.fromAsset(atlasFile, bundle: bundle);
|
||||||
|
var skeletonData = await SkeletonData.fromAsset(atlas, skeletonFile, bundle: bundle);
|
||||||
return SkeletonDrawable(atlas, skeletonData, true);
|
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 atlas = await Atlas.fromFile(atlasFile);
|
||||||
var skeletonData = await SkeletonData.fromFile(atlas, skeletonFile);
|
var skeletonData = await SkeletonData.fromFile(atlas, skeletonFile);
|
||||||
return SkeletonDrawable(atlas, skeletonData, true);
|
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 atlas = await Atlas.fromUrl(atlasFile);
|
||||||
var skeletonData = await SkeletonData.fromHttp(atlas, skeletonFile);
|
var skeletonData = await SkeletonData.fromHttp(atlas, skeletonFile);
|
||||||
return SkeletonDrawable(atlas, skeletonData, true);
|
return SkeletonDrawable(atlas, skeletonData, true);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:flutter/rendering.dart' as rendering;
|
import 'package:flutter/rendering.dart' as rendering;
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'spine_flutter.dart';
|
import 'spine_flutter.dart';
|
||||||
@ -135,6 +136,7 @@ class ComputedBounds extends BoundsProvider {
|
|||||||
|
|
||||||
class SpineWidget extends StatefulWidget {
|
class SpineWidget extends StatefulWidget {
|
||||||
final AssetType _assetType;
|
final AssetType _assetType;
|
||||||
|
AssetBundle? _bundle;
|
||||||
final String? _skeletonFile;
|
final String? _skeletonFile;
|
||||||
final String? _atlasFile;
|
final String? _atlasFile;
|
||||||
final SkeletonDrawable? _drawable;
|
final SkeletonDrawable? _drawable;
|
||||||
@ -144,32 +146,37 @@ class SpineWidget extends StatefulWidget {
|
|||||||
final BoundsProvider _boundsProvider;
|
final BoundsProvider _boundsProvider;
|
||||||
final bool _sizedByBounds;
|
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,
|
: _assetType = AssetType.Asset,
|
||||||
_fit = fit ?? BoxFit.contain,
|
_fit = fit ?? BoxFit.contain,
|
||||||
_alignment = alignment ?? Alignment.center,
|
_alignment = alignment ?? Alignment.center,
|
||||||
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
||||||
_sizedByBounds = sizedByBounds ?? false,
|
_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,
|
: _assetType = AssetType.File,
|
||||||
|
_bundle = null,
|
||||||
_fit = fit ?? BoxFit.contain,
|
_fit = fit ?? BoxFit.contain,
|
||||||
_alignment = alignment ?? Alignment.center,
|
_alignment = alignment ?? Alignment.center,
|
||||||
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
||||||
_sizedByBounds = sizedByBounds ?? false,
|
_sizedByBounds = sizedByBounds ?? false,
|
||||||
_drawable = null;
|
_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,
|
: _assetType = AssetType.Http,
|
||||||
|
_bundle = null,
|
||||||
_fit = fit ?? BoxFit.contain,
|
_fit = fit ?? BoxFit.contain,
|
||||||
_alignment = alignment ?? Alignment.center,
|
_alignment = alignment ?? Alignment.center,
|
||||||
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
||||||
_sizedByBounds = sizedByBounds ?? false,
|
_sizedByBounds = sizedByBounds ?? false,
|
||||||
_drawable = null;
|
_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,
|
: _assetType = AssetType.Drawable,
|
||||||
|
_bundle = null,
|
||||||
_fit = fit ?? BoxFit.contain,
|
_fit = fit ?? BoxFit.contain,
|
||||||
_alignment = alignment ?? Alignment.center,
|
_alignment = alignment ?? Alignment.center,
|
||||||
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
_boundsProvider = boundsProvider ?? const SetupPoseBounds(),
|
||||||
@ -189,26 +196,25 @@ class _SpineWidgetState extends State<SpineWidget> {
|
|||||||
if (widget._assetType == AssetType.Drawable) {
|
if (widget._assetType == AssetType.Drawable) {
|
||||||
loadDrawable(widget._drawable!);
|
loadDrawable(widget._drawable!);
|
||||||
} else {
|
} else {
|
||||||
loadFromAsset(widget._skeletonFile!, widget._atlasFile!, widget._assetType);
|
loadFromAsset(widget._bundle, widget._skeletonFile!, widget._atlasFile!, widget._assetType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadDrawable(SkeletonDrawable drawable) {
|
void loadDrawable(SkeletonDrawable drawable) {
|
||||||
widget._controller._initialize(drawable);
|
widget._controller._initialize(drawable);
|
||||||
drawable.update(0);
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadFromAsset(String skeletonFile, String atlasFile, AssetType assetType) async {
|
void loadFromAsset(AssetBundle? bundle, String atlasFile, String skeletonFile, AssetType assetType) async {
|
||||||
switch (assetType) {
|
switch (assetType) {
|
||||||
case AssetType.Asset:
|
case AssetType.Asset:
|
||||||
loadDrawable(await SkeletonDrawable.fromAsset(skeletonFile, atlasFile));
|
loadDrawable(await SkeletonDrawable.fromAsset(atlasFile, skeletonFile, bundle: bundle));
|
||||||
break;
|
break;
|
||||||
case AssetType.File:
|
case AssetType.File:
|
||||||
loadDrawable(await SkeletonDrawable.fromFile(skeletonFile, atlasFile));
|
loadDrawable(await SkeletonDrawable.fromFile(atlasFile, skeletonFile));
|
||||||
break;
|
break;
|
||||||
case AssetType.Http:
|
case AssetType.Http:
|
||||||
loadDrawable(await SkeletonDrawable.fromHttp(skeletonFile, atlasFile));
|
loadDrawable(await SkeletonDrawable.fromHttp(atlasFile, skeletonFile));
|
||||||
break;
|
break;
|
||||||
case AssetType.Drawable:
|
case AssetType.Drawable:
|
||||||
throw Exception("Drawable can not be loaded via loadFromAsset().");
|
throw Exception("Drawable can not be loaded via loadFromAsset().");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user