mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 01:36:02 +08:00
Load Atlas/Skeleton from assets + Add basic classes (#2570)
- Add load methods to load atlas and skeleton from assets. - Add basic classes for next steps (SpineController etc)
This commit is contained in:
parent
603f181c79
commit
ceb9ae13f4
@ -59,7 +59,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(project(":spine-android"))
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
@ -67,4 +67,10 @@ dependencies {
|
|||||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||||
debugImplementation(libs.androidx.ui.tooling)
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
|
|
||||||
|
implementation(project(":spine-android"))
|
||||||
|
|
||||||
|
// TODO Check if we really need to import `spine-libgdx` in addition to `spine-android`?
|
||||||
|
implementation("com.badlogicgames.gdx:gdx:1.12.2-SNAPSHOT")
|
||||||
|
implementation("com.esotericsoftware.spine:spine-libgdx:4.2.0")
|
||||||
}
|
}
|
||||||
@ -11,11 +11,10 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.SpineAndroidExamples"
|
android:theme="@style/Theme.SpineAndroidExamples"
|
||||||
tools:targetApi="31">
|
tools:targetApi="34">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name="MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.SpineAndroidExamples">
|
android:theme="@style/Theme.SpineAndroidExamples">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|||||||
8723
spine-android/app/src/main/assets/spineboy-pro.json
Normal file
8723
spine-android/app/src/main/assets/spineboy-pro.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,19 +3,16 @@ package com.esotericsoftware.spine
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import com.esotericsoftware.spine.ui.theme.SpineAndroidExamplesTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import com.esotericsoftware.spine.android.SpineController
|
||||||
import com.esotericsoftware.spine.android.SpineView
|
import com.esotericsoftware.spine.android.SpineView
|
||||||
|
import com.esotericsoftware.spine.ui.theme.SpineAndroidExamplesTheme
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -42,10 +39,20 @@ fun AppContent() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SpineViewComposable(modifier: Modifier = Modifier.fillMaxSize()) {
|
fun SpineViewComposable(modifier: Modifier = Modifier.fillMaxSize()) {
|
||||||
val context = LocalContext.current
|
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { ctx ->
|
factory = { ctx ->
|
||||||
SpineView(ctx).apply {
|
SpineView(ctx).apply {
|
||||||
|
loadFromAsset(
|
||||||
|
"spineboy.atlas",
|
||||||
|
"spineboy-pro.json",
|
||||||
|
SpineController {
|
||||||
|
it.skeleton.scaleY = -1f
|
||||||
|
it.skeleton.setToSetupPose()
|
||||||
|
|
||||||
|
it.animationStateData.defaultMix = 0.2f
|
||||||
|
it.animationState.setAnimation(0, "hoverboard", true)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|||||||
@ -16,6 +16,9 @@ dependencyResolutionManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,10 +28,10 @@ includeBuild("../spine-libgdx") {
|
|||||||
substitute(module("com.esotericsoftware.spine:spine-libgdx")).using(project(":spine-libgdx"))
|
substitute(module("com.esotericsoftware.spine:spine-libgdx")).using(project(":spine-libgdx"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
includeBuild("../../libgdx") {
|
//includeBuild("../../libgdx") {
|
||||||
dependencySubstitution {
|
// dependencySubstitution {
|
||||||
substitute(module("com.badlogicgames.gdx:gdx")).using(project(":gdx"))
|
// substitute(module("com.badlogicgames.gdx:gdx")).using(project(":gdx"))
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
include(":app")
|
include(":app")
|
||||||
include(":spine-android")
|
include(":spine-android")
|
||||||
|
|||||||
@ -32,7 +32,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
implementation("com.badlogicgames.gdx:gdx:1.12.1")
|
implementation("com.badlogicgames.gdx:gdx:1.12.2-SNAPSHOT")
|
||||||
implementation("com.esotericsoftware.spine:spine-libgdx:4.2.0")
|
implementation("com.esotericsoftware.spine:spine-libgdx:4.2.0")
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.AnimationState;
|
||||||
|
import com.esotericsoftware.spine.AnimationStateData;
|
||||||
|
import com.esotericsoftware.spine.Skeleton;
|
||||||
|
import com.esotericsoftware.spine.SkeletonData;
|
||||||
|
import com.esotericsoftware.spine.android.utils.SkeletonDataUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import kotlin.NotImplementedError;
|
||||||
|
|
||||||
|
public class AndroidSkeletonDrawable {
|
||||||
|
|
||||||
|
private final AndroidTextureAtlas atlas;
|
||||||
|
|
||||||
|
private final SkeletonData skeletonData;
|
||||||
|
|
||||||
|
private final Skeleton skeleton;
|
||||||
|
|
||||||
|
private final AnimationStateData animationStateData;
|
||||||
|
|
||||||
|
private final AnimationState animationState;
|
||||||
|
|
||||||
|
public AndroidSkeletonDrawable(AndroidTextureAtlas atlas, SkeletonData skeletonData) {
|
||||||
|
this.atlas = atlas;
|
||||||
|
this.skeletonData = skeletonData;
|
||||||
|
|
||||||
|
skeleton = new Skeleton(skeletonData);
|
||||||
|
animationStateData = new AnimationStateData(skeletonData);
|
||||||
|
animationState = new AnimationState(animationStateData);
|
||||||
|
|
||||||
|
skeleton.updateWorldTransform(Skeleton.Physics.none);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(float delta) {
|
||||||
|
animationState.update(delta);
|
||||||
|
animationState.apply(skeleton);
|
||||||
|
|
||||||
|
skeleton.update(delta);
|
||||||
|
skeleton.updateWorldTransform(Skeleton.Physics.update);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndroidTextureAtlas getAtlas() {
|
||||||
|
return atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Skeleton getSkeleton() {
|
||||||
|
return skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonData getSkeletonData() {
|
||||||
|
return skeletonData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimationStateData getAnimationStateData() {
|
||||||
|
return animationStateData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimationState getAnimationState() {
|
||||||
|
return animationState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AndroidSkeletonDrawable fromAsset (String atlasFileName, String skeletonFileName, Context context) {
|
||||||
|
AndroidTextureAtlas atlas = AndroidTextureAtlas.fromAsset(atlasFileName, context);
|
||||||
|
SkeletonData skeletonData = SkeletonDataUtils.fromAsset(atlas, skeletonFileName, context);
|
||||||
|
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AndroidSkeletonDrawable fromFile (File atlasFile, File skeletonFile) {
|
||||||
|
AndroidTextureAtlas atlas = AndroidTextureAtlas.fromFile(atlasFile);
|
||||||
|
SkeletonData skeletonData = SkeletonDataUtils.fromFile(atlas, skeletonFile);
|
||||||
|
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AndroidSkeletonDrawable fromHttp (URL atlasUrl, URL skeletonUrl) {
|
||||||
|
AndroidTextureAtlas atlas = AndroidTextureAtlas.fromHttp(atlasUrl);
|
||||||
|
SkeletonData skeletonData = SkeletonDataUtils.fromHttp(atlas, skeletonUrl);
|
||||||
|
return new AndroidSkeletonDrawable(atlas, skeletonData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -30,8 +30,10 @@
|
|||||||
package com.esotericsoftware.spine.android;
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
import com.badlogic.gdx.files.FileHandle;
|
import com.badlogic.gdx.files.FileHandle;
|
||||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
|
||||||
@ -39,10 +41,13 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData;
|
|||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.Null;
|
import com.badlogic.gdx.utils.Null;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
|
||||||
|
import kotlin.NotImplementedError;
|
||||||
|
|
||||||
public class AndroidTextureAtlas {
|
public class AndroidTextureAtlas {
|
||||||
private static interface BitmapLoader {
|
private static interface BitmapLoader {
|
||||||
Bitmap load (String path);
|
Bitmap load (String path);
|
||||||
@ -92,35 +97,41 @@ public class AndroidTextureAtlas {
|
|||||||
return regions;
|
return regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public AndroidTextureAtlas loadFromAssets (String atlasFile, AssetManager assetManager) {
|
static public AndroidTextureAtlas fromAsset(String atlasFileName, Context context) {
|
||||||
TextureAtlasData data = new TextureAtlasData();
|
TextureAtlasData data = new TextureAtlasData();
|
||||||
|
AssetManager assetManager = context.getAssets();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileHandle inputFile = new FileHandle() {
|
FileHandle inputFile = new FileHandle() {
|
||||||
@Override
|
@Override
|
||||||
public InputStream read () {
|
public InputStream read () {
|
||||||
try {
|
try {
|
||||||
return assetManager.open(atlasFile);
|
return assetManager.open(atlasFileName);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
data.load(inputFile, new FileHandle(atlasFile).parent(), false);
|
data.load(inputFile, new FileHandle(atlasFileName).parent(), false);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throw new RuntimeException(t);
|
throw new RuntimeException(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AndroidTextureAtlas(data, new BitmapLoader() {
|
return new AndroidTextureAtlas(data, path -> {
|
||||||
@Override
|
path = path.startsWith("/") ? path.substring(1) : path;
|
||||||
public Bitmap load (String path) {
|
try (InputStream in = new BufferedInputStream(assetManager.open(path))) {
|
||||||
path = path.startsWith("/") ? path.substring(1) : path;
|
return BitmapFactory.decodeStream(in);
|
||||||
try (InputStream in = new BufferedInputStream(assetManager.open(path))) {
|
} catch (Throwable t) {
|
||||||
return BitmapFactory.decodeStream(in);
|
throw new RuntimeException(t);
|
||||||
} catch (Throwable t) {
|
}
|
||||||
throw new RuntimeException(t);
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
static public AndroidTextureAtlas fromFile(File atlasFile) {
|
||||||
|
throw new NotImplementedError("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
static public AndroidTextureAtlas fromHttp(URL atlasUrl) {
|
||||||
|
throw new NotImplementedError("TODO");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.AnimationState;
|
||||||
|
import com.esotericsoftware.spine.AnimationStateData;
|
||||||
|
import com.esotericsoftware.spine.Skeleton;
|
||||||
|
import com.esotericsoftware.spine.SkeletonData;
|
||||||
|
import com.esotericsoftware.spine.android.utils.SpineControllerCallback;
|
||||||
|
|
||||||
|
public class SpineController {
|
||||||
|
private final SpineControllerCallback onInitialized;
|
||||||
|
private AndroidSkeletonDrawable drawable;
|
||||||
|
|
||||||
|
private boolean playing = true;
|
||||||
|
|
||||||
|
public SpineController(SpineControllerCallback onInitialized) {
|
||||||
|
this.onInitialized = onInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init(AndroidSkeletonDrawable drawable) {
|
||||||
|
this.drawable = drawable;
|
||||||
|
onInitialized.execute(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AndroidTextureAtlas getAtlas() {
|
||||||
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
|
return drawable.getAtlas();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkeletonData getSkeletonDate() {
|
||||||
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
|
return drawable.getSkeletonData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Skeleton getSkeleton() {
|
||||||
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
|
return drawable.getSkeleton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimationStateData getAnimationStateData() {
|
||||||
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
|
return drawable.getAnimationStateData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AnimationState getAnimationState() {
|
||||||
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
|
return drawable.getAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidSkeletonDrawable getDrawable() {
|
||||||
|
if (drawable == null) throw new RuntimeException("Controller is not initialized yet.");
|
||||||
|
return drawable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return drawable != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean isPlaying() {
|
||||||
|
return playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resume() {
|
||||||
|
playing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,116 +29,82 @@
|
|||||||
|
|
||||||
package com.esotericsoftware.spine.android;
|
package com.esotericsoftware.spine.android;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import com.esotericsoftware.spine.android.utils.AndroidSkeletonDrawableLoader;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.math.MathUtils;
|
|
||||||
import com.badlogic.gdx.math.Vector2;
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
|
||||||
import com.esotericsoftware.spine.AnimationState;
|
|
||||||
import com.esotericsoftware.spine.AnimationStateData;
|
|
||||||
import com.esotericsoftware.spine.Skeleton;
|
|
||||||
import com.esotericsoftware.spine.SkeletonBinary;
|
|
||||||
import com.esotericsoftware.spine.SkeletonData;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.os.Handler;
|
||||||
import android.graphics.Paint;
|
import android.os.Looper;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
public class SpineView extends View implements Choreographer.FrameCallback {
|
public class SpineView extends View implements Choreographer.FrameCallback {
|
||||||
private long lastTime = 0;
|
private long lastTime = 0;
|
||||||
private float delta = 0;
|
private float delta = 0;
|
||||||
private Paint textPaint;
|
|
||||||
int instances = 1;
|
|
||||||
Vector2[] coords = new Vector2[instances];
|
|
||||||
AndroidTextureAtlas atlas;
|
|
||||||
SkeletonData data;
|
|
||||||
Array<Skeleton> skeletons = new Array<>();
|
|
||||||
Array<AnimationState> states = new Array<>();
|
|
||||||
SkeletonRenderer renderer = new SkeletonRenderer();
|
SkeletonRenderer renderer = new SkeletonRenderer();
|
||||||
|
SpineController controller;
|
||||||
|
|
||||||
public SpineView (Context context) {
|
public SpineView (Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpineView (Context context, AttributeSet attrs) {
|
public SpineView (Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpineView (Context context, AttributeSet attrs, int defStyle) {
|
public SpineView (Context context, AttributeSet attrs, int defStyle) {
|
||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSkeleton () {
|
public void loadFromAsset(String atlasFileName, String skeletonFileName, SpineController controller) {
|
||||||
String skel = "spineboy-pro.skel";
|
this.controller = controller;
|
||||||
String atlasFile = "spineboy.atlas";
|
loadFrom(() -> AndroidSkeletonDrawable.fromAsset(atlasFileName, skeletonFileName, getContext()));
|
||||||
|
|
||||||
AssetManager assetManager = this.getContext().getAssets();
|
|
||||||
atlas = AndroidTextureAtlas.loadFromAssets(atlasFile, assetManager);
|
|
||||||
AndroidAtlasAttachmentLoader attachmentLoader = new AndroidAtlasAttachmentLoader(atlas);
|
|
||||||
SkeletonBinary binary = new SkeletonBinary(attachmentLoader);
|
|
||||||
try (InputStream in = new BufferedInputStream(assetManager.open(skel))) {
|
|
||||||
data = binary.readSkeletonData(in);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init () {
|
public void loadFromFile(File atlasFile, File skeletonFile, SpineController controller) {
|
||||||
textPaint = new Paint();
|
this.controller = controller;
|
||||||
textPaint.setColor(Color.WHITE); // Set the color of the paint
|
loadFrom(() -> AndroidSkeletonDrawable.fromFile(atlasFile, skeletonFile));
|
||||||
textPaint.setTextSize(48);
|
}
|
||||||
Choreographer.getInstance().postFrameCallback(this);
|
|
||||||
|
|
||||||
loadSkeleton();
|
public void loadFromHttp(URL atlasUrl, URL skeletonUrl, SpineController controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
loadFrom(() -> AndroidSkeletonDrawable.fromHttp(atlasUrl, skeletonUrl));
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < instances; i++) {
|
private void loadFrom(AndroidSkeletonDrawableLoader loader) {
|
||||||
Skeleton skeleton = new Skeleton(data);
|
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
skeleton.setScaleY(-1);
|
Thread backgroundThread = new Thread(() -> {
|
||||||
skeleton.setToSetupPose();
|
final AndroidSkeletonDrawable skeletonDrawable = loader.load();
|
||||||
skeletons.add(skeleton);
|
mainHandler.post(() -> {
|
||||||
|
controller.init(skeletonDrawable);
|
||||||
AnimationStateData stateData = new AnimationStateData(data);
|
Choreographer.getInstance().postFrameCallback(SpineView.this);
|
||||||
stateData.setDefaultMix(0.2f);
|
});
|
||||||
AnimationState state = new AnimationState(stateData);
|
});
|
||||||
state.setAnimation(0, "hoverboard", true);
|
backgroundThread.start();
|
||||||
states.add(state);
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
coords[i] = new Vector2(500, 1000);
|
|
||||||
} else {
|
|
||||||
coords[i] = new Vector2(MathUtils.random(1000), MathUtils.random(3000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDraw (Canvas canvas) {
|
public void onDraw (@NonNull Canvas canvas) {
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
|
if (!controller.isInitialized()) {
|
||||||
for (int i = 0; i < instances; i++) {
|
return;
|
||||||
AnimationState state = states.get(i);
|
|
||||||
Skeleton skeleton = skeletons.get(i);
|
|
||||||
state.update(delta);
|
|
||||||
state.apply(skeleton);
|
|
||||||
skeleton.update(delta);
|
|
||||||
skeleton.updateWorldTransform(Skeleton.Physics.update);
|
|
||||||
renderer.render(canvas, skeleton, coords[i].x, coords[i].y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.drawText(delta * 1000 + " ms", 100, 100, textPaint);
|
controller.getDrawable().update(delta);
|
||||||
canvas.drawText(instances + " instances", 100, 150, textPaint);
|
|
||||||
|
// TODO: Calculate scaling + position
|
||||||
|
|
||||||
|
renderer.render(canvas, controller.getSkeleton(), 500f, 1000f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Choreographer.FrameCallback
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doFrame (long frameTimeNanos) {
|
public void doFrame (long frameTimeNanos) {
|
||||||
if (lastTime != 0) delta = (frameTimeNanos - lastTime) / 1e9f;
|
if (lastTime != 0) delta = (frameTimeNanos - lastTime) / 1e9f;
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.esotericsoftware.spine.android.utils;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.android.AndroidSkeletonDrawable;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface AndroidSkeletonDrawableLoader {
|
||||||
|
AndroidSkeletonDrawable load();
|
||||||
|
}
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
package com.esotericsoftware.spine.android.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.SkeletonBinary;
|
||||||
|
import com.esotericsoftware.spine.SkeletonData;
|
||||||
|
import com.esotericsoftware.spine.SkeletonJson;
|
||||||
|
import com.esotericsoftware.spine.SkeletonLoader;
|
||||||
|
import com.esotericsoftware.spine.android.AndroidAtlasAttachmentLoader;
|
||||||
|
import com.esotericsoftware.spine.android.AndroidTextureAtlas;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import kotlin.NotImplementedError;
|
||||||
|
|
||||||
|
public class SkeletonDataUtils {
|
||||||
|
|
||||||
|
public static SkeletonData fromAsset(AndroidTextureAtlas atlas, String skeletonFileName, Context context) {
|
||||||
|
|
||||||
|
AndroidAtlasAttachmentLoader attachmentLoader = new AndroidAtlasAttachmentLoader(atlas);
|
||||||
|
|
||||||
|
SkeletonLoader skeletonLoader;
|
||||||
|
if (skeletonFileName.endsWith(".json")) {
|
||||||
|
skeletonLoader = new SkeletonJson(attachmentLoader);
|
||||||
|
} else {
|
||||||
|
skeletonLoader = new SkeletonBinary(attachmentLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
SkeletonData skeletonData;
|
||||||
|
|
||||||
|
AssetManager assetManager = context.getAssets();
|
||||||
|
try (InputStream in = new BufferedInputStream(assetManager.open(skeletonFileName))) {
|
||||||
|
skeletonData = skeletonLoader.readSkeletonData(in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return skeletonData;
|
||||||
|
}
|
||||||
|
public static SkeletonData fromFile(AndroidTextureAtlas atlas, File skeletonFile) {
|
||||||
|
throw new NotImplementedError("TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkeletonData fromHttp(AndroidTextureAtlas atlas, URL skeletonUrl) {
|
||||||
|
throw new NotImplementedError("TODO");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
package com.esotericsoftware.spine.android.utils;
|
||||||
|
|
||||||
|
import com.esotericsoftware.spine.android.SpineController;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface SpineControllerCallback {
|
||||||
|
void execute (SpineController controller);
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ group = "com.esotericsoftware.spine"
|
|||||||
version = "4.2.0"
|
version = "4.2.0"
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
libgdxVersion = "1.12.1"
|
libgdxVersion = "1.12.2-SNAPSHOT"
|
||||||
javaVersion = 8
|
javaVersion = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user