소스 검색

Add sample that renders many SpineViews at once

Denis Andrasec 1 년 전
부모
커밋
ff32e3ebb8

+ 9 - 1
spine-android/app/src/main/java/com/esotericsoftware/spine/MainActivity.kt

@@ -66,7 +66,8 @@ fun AppContent() {
                                 Destination.DebugRendering,
                                 Destination.DressUp,
                                 Destination.IKFollowing,
-                                Destination.Physics
+                                Destination.Physics,
+                                Destination.TheBoys
                             ),
                             paddingValues
                         )
@@ -114,6 +115,12 @@ fun AppContent() {
                 ) {
                     Physics(navController)
                 }
+
+                composable(
+                    Destination.TheBoys.route
+                ) {
+                    TheBoys(navController)
+                }
             }
         }
     }
@@ -181,4 +188,5 @@ sealed class Destination(val route: String, val title: String) {
     data object DressUp : Destination("dressUp", "Dress Up")
     data object IKFollowing : Destination("ikFollowing", "IK Following")
     data object Physics: Destination("physics", "Physics (drag anywhere)")
+    data object TheBoys: Destination("theBoys", "100 Spine Boys")
 }

+ 116 - 0
spine-android/app/src/main/java/com/esotericsoftware/spine/TheBoys.kt

@@ -0,0 +1,116 @@
+package com.esotericsoftware.spine
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+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.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.toSize
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.navigation.NavHostController
+import com.esotericsoftware.spine.android.AndroidSkeletonDrawable
+import com.esotericsoftware.spine.android.AndroidTextureAtlas
+import com.esotericsoftware.spine.android.SpineController
+import com.esotericsoftware.spine.android.SpineView
+import com.esotericsoftware.spine.android.utils.SkeletonDataUtils
+import kotlin.random.Random
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun TheBoys(nav: NavHostController) {
+    Scaffold(
+        topBar = {
+            TopAppBar(
+                title = { Text(text = Destination.TheBoys.title) },
+                navigationIcon = {
+                    IconButton({ nav.navigateUp() }) {
+                        Icon(
+                            Icons.Rounded.ArrowBack,
+                            null,
+                        )
+                    }
+                }
+            )
+        }
+    ) { paddingValues ->
+        var viewportSize by remember { mutableStateOf(Size.Zero) }
+
+        Box(
+            modifier = Modifier
+                .padding(paddingValues)
+                .fillMaxSize()
+                .onGloballyPositioned { coordinates ->
+                    viewportSize = coordinates.size.toSize()
+                }
+        ) {
+            if (viewportSize != Size.Zero) {
+                val contentSize = Size(viewportSize.width * 2, viewportSize.height * 2)
+
+                val context = LocalContext.current
+                val cachedAtlas = remember { AndroidTextureAtlas.fromAsset("spineboy.atlas", context) }
+                val cachedSkeletonData = remember { SkeletonDataUtils.fromAsset(cachedAtlas, "spineboy-pro.json", context) }
+
+                val spineboys = remember {
+                    val rng = Random(System.currentTimeMillis())
+                    List(100) {
+                        val scale = 0.1f + rng.nextFloat() * 0.2f
+                        val position = Offset(rng.nextFloat() * contentSize.width, rng.nextFloat() * contentSize.height)
+                        SpineBoyData(scale, position, "walk")
+                    }
+                }
+
+                Box(
+                    modifier = Modifier
+                        .size(contentSize.width.dp, contentSize.height.dp)
+                ) {
+                    spineboys.forEach { spineBoyData ->
+                        AndroidView(
+                            modifier = Modifier
+                                .scale(spineBoyData.scale)
+                                .offset(
+                                    -(contentSize.width / 2).dp + spineBoyData.position.x.dp,
+                                    -(contentSize.height / 2).dp + spineBoyData.position.y.dp
+                                ),
+                            factory = { ctx ->
+                                SpineView.loadFromDrawable(
+                                    AndroidSkeletonDrawable(cachedAtlas, cachedSkeletonData),
+                                    ctx,
+                                    SpineController {
+                                        it.animationState.setAnimation(0, spineBoyData.animation, true)
+                                    }
+                                )
+                            },
+                        )
+                    }
+                }
+            }
+        }
+    }
+}
+
+data class SpineBoyData(
+    val scale: Float,
+    val position: Offset,
+    val animation: String
+)
+