From e7a075eeb6bea1292e72a4692e2fdb3e2d5e1965 Mon Sep 17 00:00:00 2001 From: Denis Andrasec Date: Thu, 4 Jul 2024 15:39:42 +0200 Subject: [PATCH] Add `Animation State Events` sample --- .../spine/AnimationStateEvents.kt | 148 ++++++++++++++++++ .../esotericsoftware/spine/MainActivity.kt | 8 + .../src/com/esotericsoftware/spine/Slot.java | 6 +- 3 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 spine-android/app/src/main/java/com/esotericsoftware/spine/AnimationStateEvents.kt diff --git a/spine-android/app/src/main/java/com/esotericsoftware/spine/AnimationStateEvents.kt b/spine-android/app/src/main/java/com/esotericsoftware/spine/AnimationStateEvents.kt new file mode 100644 index 000000000..472a8546b --- /dev/null +++ b/spine-android/app/src/main/java/com/esotericsoftware/spine/AnimationStateEvents.kt @@ -0,0 +1,148 @@ +package com.esotericsoftware.spine + +import android.util.Log +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import androidx.navigation.NavHostController +import com.badlogic.gdx.graphics.Color +import com.esotericsoftware.spine.android.SpineController +import com.esotericsoftware.spine.android.SpineView + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AnimationState(nav: NavHostController) { + + val TAG = "AnimationState" + + val controller = remember { + SpineController.Builder() + .setOnInitialized { controller -> + controller.skeleton.setScaleX(0.5f) + controller.skeleton.setScaleY(0.5f) + + controller.skeleton.findSlot("gun")?.color = Color(1f, 0f, 0f, 1f) + + controller.animationStateData.setDefaultMix(0.2f) + controller.animationState.setAnimation(0, "walk", true).setListener(object : AnimationState.AnimationStateListener { + override fun start(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Walk animation event start") + } + + override fun interrupt(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Walk animation event interrupt") + } + + override fun end(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Walk animation event end") + } + + override fun dispose(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Walk animation event dispose") + } + + override fun complete(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Walk animation event complete") + } + + override fun event(entry: AnimationState.TrackEntry?, event: Event?) { + Log.d(TAG, "Walk animation event event") + } + }) + controller.animationState.addAnimation(0, "jump", false, 2f) + controller.animationState.addAnimation(0, "run", true, 0f).setListener(object : AnimationState.AnimationStateListener { + override fun start(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Run animation event start") + } + + override fun interrupt(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Run animation event interrupt") + } + + override fun end(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Run animation event end") + } + + override fun dispose(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Run animation event dispose") + } + + override fun complete(entry: AnimationState.TrackEntry?) { + Log.d(TAG, "Run animation event complete") + } + + override fun event(entry: AnimationState.TrackEntry?, event: Event?) { + Log.d(TAG, "Run animation event event") + } + }) + + controller.animationState.addListener(object : AnimationState.AnimationStateListener { + override fun start(entry: AnimationState.TrackEntry?) {} + + override fun interrupt(entry: AnimationState.TrackEntry?) {} + + override fun end(entry: AnimationState.TrackEntry?) {} + + override fun dispose(entry: AnimationState.TrackEntry?) {} + + override fun complete(entry: AnimationState.TrackEntry?) {} + + override fun event(entry: AnimationState.TrackEntry?, event: Event?) { + if (event != null) { + Log.d(TAG, "User event: { name: ${event.data.name}, intValue: ${event.int}, floatValue: ${event.float}, stringValue: ${event.string} }") + } + } + }) + Log.d(TAG, "Current: ${controller.animationState.getCurrent(0)?.getAnimation()?.getName()}"); + } + .build() + } + + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = Destination.SimpleAnimation.title) }, + navigationIcon = { + IconButton({ nav.navigateUp() }) { + Icon( + Icons.Rounded.ArrowBack, + null, + ) + } + } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier.padding(paddingValues), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text("See output in console!") + AndroidView( + factory = { ctx -> + SpineView(ctx).apply { + loadFromAsset( + "spineboy.atlas", + "spineboy-pro.json", + controller + ) + } + } + ) + } + } +} diff --git a/spine-android/app/src/main/java/com/esotericsoftware/spine/MainActivity.kt b/spine-android/app/src/main/java/com/esotericsoftware/spine/MainActivity.kt index 2ed7ee709..fd3830ec8 100644 --- a/spine-android/app/src/main/java/com/esotericsoftware/spine/MainActivity.kt +++ b/spine-android/app/src/main/java/com/esotericsoftware/spine/MainActivity.kt @@ -60,6 +60,7 @@ fun AppContent() { listOf( Destination.SimpleAnimation, Destination.PlayPause, + Destination.AnimationStateEvents, Destination.IKFollowing ), paddingValues @@ -79,6 +80,12 @@ fun AppContent() { PlayPause(navController) } + composable( + Destination.AnimationStateEvents.route + ) { + AnimationState(navController) + } + composable( Destination.IKFollowing.route ) { @@ -120,5 +127,6 @@ sealed class Destination(val route: String, val title: String) { data object Samples: Destination("samples", "Spine Android Examples") data object SimpleAnimation : Destination("simpleAnimation", "Simple Animation") data object PlayPause : Destination("playPause", "Play/Pause") + data object AnimationStateEvents : Destination("animationStateEvents", "Animation State Listener") data object IKFollowing : Destination("ikFollowing", "IK Following") } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java index afc1d159e..3d2cce195 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Slot.java @@ -44,7 +44,7 @@ import com.esotericsoftware.spine.attachments.VertexAttachment; public class Slot { final SlotData data; final Bone bone; - final Color color = new Color(); + Color color = new Color(); @Null final Color darkColor; @Null Attachment attachment; int sequenceIndex; @@ -95,6 +95,10 @@ public class Slot { return color; } + public void setColor(Color color) { + this.color = color; + } + /** The dark color used to tint the slot's attachment for two color tinting, or null if two color tinting is not used. The dark * color's alpha is not used. */ public @Null Color getDarkColor () {