summaryrefslogtreecommitdiff
path: root/composeApp/src/commonMain/kotlin/coffee/liz/ecs/animation/AnimationSystem.kt
blob: 7968250811bfdeb99cf7ff07ab6e076811ceb00b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package coffee.liz.ecs.animation

import coffee.liz.ecs.TickedSystem
import coffee.liz.ecs.World
import kotlin.time.Duration

/**
 * Updates animation playback state for all [Animator] components in [World].
 */
class AnimationSystem(animationTickRate: Duration) : TickedSystem(animationTickRate) {
    override fun update(world: World, ticks: Int) {
        world.query(Animator::class).forEach { entity ->
            val animator = entity.get(Animator::class)
            if (!animator.playing) return@forEach

            // Get the current animation clip
            val clip = animator.clips[animator.currentClip] ?: return@forEach

            // Accumulate elapsed time
            animator.elapsedTicks += ticks

            // Advance frames while we have enough elapsed time
            while (animator.elapsedTicks >= clip.frameTicks) {
                animator.elapsedTicks -= clip.frameTicks
                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.step

        when (clip.loopMode) {
            LoopMode.ONCE -> {
                if (animator.frameIndex >= clip.frameNames.size) {
                    animator.frameIndex = clip.frameNames.size - 1
                    animator.playing = false
                }
            }

            LoopMode.LOOP -> {
                if (animator.frameIndex >= clip.frameNames.size) {
                    animator.frameIndex = 0
                } else if (animator.frameIndex < 0) {
                    animator.frameIndex = clip.frameNames.size - 1
                }
            }

            LoopMode.PING_PONG -> {
                // Go to (endFrame - 1) or (beginFrame + 1) ping ponging
                if (animator.frameIndex >= clip.frameNames.size) {
                    animator.frameIndex = (clip.frameNames.size - 2).coerceAtLeast(0)
                    animator.direction = AnimationDirection.BACKWARD
                } else if (animator.frameIndex < 0) {
                    animator.frameIndex = 1.coerceAtMost(clip.frameNames.size - 1)
                    animator.direction = AnimationDirection.FORWARD
                }
            }
        }
    }
}