Fixed animation mapping.

Was previously using _00, _01, _02, etc. number suffix at end of .json file.

Actual sprite index is stored as the m_Name field inside this file.
This commit is contained in:
lightheel 2025-08-04 13:23:04 -04:00
parent 9f7e452850
commit c973030d9d
2 changed files with 89 additions and 36 deletions

View File

@ -17,7 +17,7 @@ fun AnimatedSpriteImage(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val spriteManager = remember { BattleSpriteManager(context) } val spriteManager = remember { BattleSpriteManager(context) }
val animationStateMachine = remember { DigimonAnimationStateMachine(characterId) } val animationStateMachine = remember { DigimonAnimationStateMachine(characterId, context) }
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) } var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) }

View File

@ -4,6 +4,9 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import android.content.Context
import java.io.File
import com.google.gson.Gson
enum class DigimonAnimationType { enum class DigimonAnimationType {
IDLE, IDLE,
@ -28,7 +31,8 @@ data class AnimationState(
) )
class DigimonAnimationStateMachine( class DigimonAnimationStateMachine(
private val characterId: String private val characterId: String,
private val context: Context
) { ) {
var currentAnimation by mutableStateOf<DigimonAnimationType>(DigimonAnimationType.IDLE) var currentAnimation by mutableStateOf<DigimonAnimationType>(DigimonAnimationType.IDLE)
private set private set
@ -39,38 +43,24 @@ class DigimonAnimationStateMachine(
var isPlaying by mutableStateOf(false) var isPlaying by mutableStateOf(false)
private set private set
// Animation mapping - maps animation types to sprite indices // Animation mapping based on m_Name values
// For now, we'll assume the sprite indices 0-11 correspond to the 12 animation types private val mNameToAnimationType = mapOf(
private val animationMapping = mapOf( "01" to DigimonAnimationType.IDLE,
DigimonAnimationType.IDLE to 0, "02" to DigimonAnimationType.IDLE2,
DigimonAnimationType.IDLE2 to 1, "03" to DigimonAnimationType.WALK,
DigimonAnimationType.WALK to 2, "04" to DigimonAnimationType.WALK2,
DigimonAnimationType.WALK2 to 3, "05" to DigimonAnimationType.RUN,
DigimonAnimationType.RUN to 4, "06" to DigimonAnimationType.RUN2,
DigimonAnimationType.RUN2 to 5, "07" to DigimonAnimationType.WORKOUT,
DigimonAnimationType.WORKOUT to 6, "08" to DigimonAnimationType.WORKOUT2,
DigimonAnimationType.WORKOUT2 to 7, "09" to DigimonAnimationType.HAPPY,
DigimonAnimationType.HAPPY to 8, "10" to DigimonAnimationType.SLEEP,
DigimonAnimationType.SLEEP to 9, "11" to DigimonAnimationType.ATTACK,
DigimonAnimationType.ATTACK to 10, "12" to DigimonAnimationType.FLEE
DigimonAnimationType.FLEE to 11
) )
// Animation frame sequences - defines which frames to cycle through for each animation // Cache for sprite file mappings
private val animationFrameSequences = mapOf( private var spriteFileMappings: Map<DigimonAnimationType, List<String>> = emptyMap()
DigimonAnimationType.IDLE to listOf(0, 1), // Cycle between idle frames
DigimonAnimationType.IDLE2 to listOf(1, 0), // Alternative idle cycle
DigimonAnimationType.WALK to listOf(2, 3), // Walk animation frames
DigimonAnimationType.WALK2 to listOf(3, 2), // Alternative walk cycle
DigimonAnimationType.RUN to listOf(4, 5), // Run animation frames
DigimonAnimationType.RUN2 to listOf(5, 4), // Alternative run cycle
DigimonAnimationType.WORKOUT to listOf(6, 7), // Workout animation frames
DigimonAnimationType.WORKOUT2 to listOf(7, 6), // Alternative workout cycle
DigimonAnimationType.HAPPY to listOf(8), // Single happy frame
DigimonAnimationType.SLEEP to listOf(9), // Single sleep frame
DigimonAnimationType.ATTACK to listOf(10), // Single attack frame
DigimonAnimationType.FLEE to listOf(11) // Single flee frame
)
// Animation durations for each type // Animation durations for each type
private val animationDurations = mapOf( private val animationDurations = mapOf(
@ -84,10 +74,65 @@ class DigimonAnimationStateMachine(
DigimonAnimationType.WORKOUT2 to 300L, DigimonAnimationType.WORKOUT2 to 300L,
DigimonAnimationType.HAPPY to 400L, DigimonAnimationType.HAPPY to 400L,
DigimonAnimationType.SLEEP to 1000L, DigimonAnimationType.SLEEP to 1000L,
DigimonAnimationType.ATTACK to 300L, // Longer for attack animation DigimonAnimationType.ATTACK to 300L,
DigimonAnimationType.FLEE to 150L DigimonAnimationType.FLEE to 150L
) )
init {
loadSpriteFileMappings()
}
private fun loadSpriteFileMappings() {
try {
val spriteBaseDir = File(context.filesDir, "battle_sprites/extracted_assets/sprites")
val gson = Gson()
val mappings = mutableMapOf<DigimonAnimationType, MutableList<String>>()
// Initialize all animation types
DigimonAnimationType.values().forEach { animationType ->
mappings[animationType] = mutableListOf()
}
println("Loading sprite mappings for character: $characterId")
// Scan all sprite files for this character
val spriteFiles = spriteBaseDir.listFiles { file ->
file.name.startsWith("${characterId}_sprite_") && file.name.endsWith(".json")
}
println("Found ${spriteFiles?.size ?: 0} sprite files for $characterId")
spriteFiles?.forEach { spriteFile ->
println("Processing sprite file: ${spriteFile.name}")
val spriteDataJson = spriteFile.readText()
val spriteData = gson.fromJson(spriteDataJson, SpriteData::class.java)
println(" m_Name: ${spriteData.m_Name}")
// Get the animation type from m_Name
val animationType = mNameToAnimationType[spriteData.m_Name]
if (animationType != null) {
// Extract the sprite index from filename (e.g., "dim000_mon01_sprite_00.json" -> "00")
val spriteIndex = spriteFile.name.substringAfter("_sprite_").substringBefore(".json")
mappings[animationType]?.add(spriteIndex)
println(" Mapped to animation type: $animationType with sprite index: $spriteIndex")
} else {
println(" Unknown m_Name: ${spriteData.m_Name}")
}
}
// Convert to immutable map
spriteFileMappings = mappings.mapValues { it.value.sorted() }
println("Final sprite mappings for $characterId: $spriteFileMappings")
} catch (e: Exception) {
println("Error loading sprite file mappings: ${e.message}")
e.printStackTrace()
}
}
suspend fun playAnimation(animationType: DigimonAnimationType) { suspend fun playAnimation(animationType: DigimonAnimationType) {
if (currentAnimation == animationType && isPlaying) { if (currentAnimation == animationType && isPlaying) {
return // Already playing this animation return // Already playing this animation
@ -96,19 +141,27 @@ class DigimonAnimationStateMachine(
currentAnimation = animationType currentAnimation = animationType
isPlaying = true isPlaying = true
val frameSequence = animationFrameSequences[animationType] ?: listOf(0) val frameSequence = spriteFileMappings[animationType] ?: listOf("00")
val duration = animationDurations[animationType] ?: 100L val duration = animationDurations[animationType] ?: 100L
// Ensure we have at least one frame
if (frameSequence.isEmpty()) {
println("Warning: No sprite files found for animation type $animationType")
currentSpriteIndex = 0
return
}
// For non-looping animations like ATTACK, play once and return to IDLE // For non-looping animations like ATTACK, play once and return to IDLE
if (animationType == DigimonAnimationType.ATTACK) { if (animationType == DigimonAnimationType.ATTACK) {
currentSpriteIndex = frameSequence[0] currentSpriteIndex = frameSequence.firstOrNull()?.toIntOrNull() ?: 0
delay(duration) delay(duration)
playAnimation(DigimonAnimationType.IDLE) playAnimation(DigimonAnimationType.IDLE)
} else { } else {
// For looping animations, cycle through frames // For looping animations, cycle through frames
var frameIndex = 0 var frameIndex = 0
while (isPlaying && currentAnimation == animationType) { while (isPlaying && currentAnimation == animationType) {
currentSpriteIndex = frameSequence[frameIndex % frameSequence.size] val spriteIndex = frameSequence[frameIndex % frameSequence.size]
currentSpriteIndex = spriteIndex.toIntOrNull() ?: 0
delay(duration) delay(duration)
frameIndex++ frameIndex++
} }