Offset idle animation so player/enemy Digimon aren't in sync.

This commit is contained in:
lightheel 2025-08-06 18:38:11 -04:00
parent d833a89c17
commit 371a850d45
3 changed files with 36 additions and 12 deletions

View File

@ -7,6 +7,7 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay
@Composable
fun AnimatedSpriteImage(
@ -14,11 +15,22 @@ fun AnimatedSpriteImage(
animationType: DigimonAnimationType = DigimonAnimationType.IDLE,
modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Fit,
reloadMappings: Boolean = false
reloadMappings: Boolean = false,
animationOffset: Long = 0L // New parameter for offsetting animation timing
) {
val context = LocalContext.current
val spriteManager = remember { IndividualSpriteManager(context) }
val animationStateMachine = remember { DigimonAnimationStateMachine(characterId, context) }
// Calculate frame offset based on animation offset
// 750ms is the idle animation duration, so we calculate how many frames to offset
val frameOffset = if (animationOffset > 0L) {
// Convert time offset to frame offset (2 frames per cycle, 750ms per frame)
((animationOffset / 750L) * 2).toInt()
} else {
0
}
val animationStateMachine = remember { DigimonAnimationStateMachine(characterId, context, frameOffset, animationOffset) }
val coroutineScope = rememberCoroutineScope()
var bitmap by remember { mutableStateOf<android.graphics.Bitmap?>(null) }

View File

@ -31,7 +31,9 @@ data class AnimationState(
class DigimonAnimationStateMachine(
private val characterId: String,
private val context: Context
private val context: Context,
private val initialFrameOffset: Int = 0, // New parameter for offsetting the starting frame
private val timingOffset: Long = 0L // New parameter for offsetting the timing
) {
var currentAnimation by mutableStateOf<DigimonAnimationType>(DigimonAnimationType.IDLE)
private set
@ -79,7 +81,7 @@ class DigimonAnimationStateMachine(
)
init {
println("Initialized DigimonAnimationStateMachine for character: $characterId")
println("Initialized DigimonAnimationStateMachine for character: $characterId with frame offset: $initialFrameOffset, timing offset: $timingOffset")
println("Available animation types: ${animationTypeToFrames.keys}")
}
@ -128,12 +130,17 @@ class DigimonAnimationStateMachine(
// Combine frames for cycling idle animation
val combinedFrames = (idleFrames + idle2Frames).distinct()
println("Playing idle animation with frames: $combinedFrames")
println("Playing idle animation with frames: $combinedFrames, starting at offset: $initialFrameOffset, timing offset: $timingOffset")
val duration = animationDurations[DigimonAnimationType.IDLE] ?: 500L
// Cycle through idle frames
var frameIndex = 0
// Apply initial timing offset
if (timingOffset > 0L) {
delay(timingOffset)
}
// Cycle through idle frames, starting from the offset
var frameIndex = initialFrameOffset
while (isPlaying && currentAnimation == DigimonAnimationType.IDLE) {
val frameNumber = combinedFrames[frameIndex % combinedFrames.size]
currentFrameNumber = frameNumber

View File

@ -730,7 +730,8 @@ fun MiddleBattleView(
y = enemyVerticalOffset + 40.dp
),
contentScale = ContentScale.Fit,
reloadMappings = false
reloadMappings = false,
animationOffset = 375L // Offset enemy animation by half the idle duration
)
// Enemy attack sprite (Phase 1 only)
@ -811,7 +812,8 @@ fun MiddleBattleView(
y = playerVerticalOffset - 40.dp
),
contentScale = ContentScale.Fit,
reloadMappings = false
reloadMappings = false,
animationOffset = 0L // Player animation starts immediately
)
// Player attack sprite (Phase 1 only)
@ -1149,7 +1151,8 @@ fun PlayerBattleView(
y = verticalOffset
),
contentScale = ContentScale.Fit,
reloadMappings = false
reloadMappings = false,
animationOffset = 0L // Player animation starts immediately
)
// Attack sprite visibility and positioning based on attack phase
@ -1456,7 +1459,8 @@ fun EnemyBattleView(
y = verticalOffset
),
contentScale = ContentScale.Fit,
reloadMappings = false
reloadMappings = false,
animationOffset = 375L // Offset enemy animation by half the idle duration
)
// Attack sprite visibility and positioning based on attack phase
@ -1797,7 +1801,8 @@ fun BattlesScreen() {
animationType = currentTestAnimation,
modifier = Modifier.size(120.dp),
contentScale = ContentScale.Fit,
reloadMappings = false
reloadMappings = false,
animationOffset = 0L // No offset for sprite tester
)
Spacer(modifier = Modifier.height(16.dp))