summaryrefslogtreecommitdiff
path: root/composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt
diff options
context:
space:
mode:
Diffstat (limited to 'composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt')
-rw-r--r--composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt77
1 files changed, 77 insertions, 0 deletions
diff --git a/composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt b/composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt
new file mode 100644
index 0000000..7ae405e
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt
@@ -0,0 +1,77 @@
+package coffee.liz.ecs.animation
+
+import coffee.liz.ecs.System
+import coffee.liz.ecs.World
+
+/**
+ * System that updates animation playback state for all entities with an Animator component.
+ *
+ * This system:
+ * - Updates elapsed time for playing animations
+ * - Advances frame indices based on frame duration
+ * - Handles loop modes (ONCE, LOOP, PING_PONG)
+ * - Stops animations when they complete (for ONCE mode)
+ */
+class AnimationSystem : System {
+ override fun update(world: World, deltaTime: Float) {
+ // Query all entities that have both Animator and SpriteSheet
+ world.query(Animator::class, SpriteSheet::class).forEach { entity ->
+ val animator = entity.get(Animator::class) ?: return@forEach
+
+ // Skip if animation is not playing
+ if (!animator.playing) return@forEach
+
+ // Get the current animation clip
+ val clip = animator.clips[animator.currentClip] ?: return@forEach
+
+ // Accumulate elapsed time
+ animator.elapsed += deltaTime
+
+ // Advance frames while we have enough elapsed time
+ while (animator.elapsed >= clip.frameDuration) {
+ animator.elapsed -= clip.frameDuration
+ advanceFrame(animator, clip)
+ }
+ }
+ }
+
+ /**
+ * Advances the animation to the next frame based on loop mode.
+ */
+ private fun advanceFrame(animator: Animator, clip: AnimationClip) {
+ animator.frameIndex += animator.direction
+
+ when (clip.loopMode) {
+ LoopMode.ONCE -> {
+ // Play once and stop on last frame
+ if (animator.frameIndex >= clip.frameNames.size) {
+ animator.frameIndex = clip.frameNames.size - 1
+ animator.playing = false
+ }
+ }
+
+ LoopMode.LOOP -> {
+ // Loop back to beginning
+ if (animator.frameIndex >= clip.frameNames.size) {
+ animator.frameIndex = 0
+ } else if (animator.frameIndex < 0) {
+ // Handle negative wrap (shouldn't happen in LOOP but be safe)
+ animator.frameIndex = clip.frameNames.size - 1
+ }
+ }
+
+ LoopMode.PING_PONG -> {
+ // Bounce back and forth
+ if (animator.frameIndex >= clip.frameNames.size) {
+ // Hit the end, reverse direction
+ animator.frameIndex = (clip.frameNames.size - 2).coerceAtLeast(0)
+ animator.direction = -1
+ } else if (animator.frameIndex < 0) {
+ // Hit the beginning, reverse direction
+ animator.frameIndex = 1.coerceAtMost(clip.frameNames.size - 1)
+ animator.direction = 1
+ }
+ }
+ }
+ }
+}