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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
package coffee.liz.ecs.animation
import coffee.liz.ecs.Component
import coffee.liz.ecs.Rect
import kotlin.jvm.JvmInline
/**
* Loop modes for animation playback.
*/
enum class LoopMode {
/** Play once **/
ONCE,
/** Repeat **/
LOOP,
/** Play forward, then backward, repeat **/
PING_PONG,
}
/**
* Name of an animation clip.
*/
@JvmInline
value class AnimationName(
val value: String,
)
/**
* Name of a frame.
*/
@JvmInline
value class FrameName(
val value: String,
)
/**
* Animation Clip details.
*/
data class AnimationClip(
val frameNames: List<FrameName>,
val frameTicks: Int,
val loopMode: LoopMode = LoopMode.LOOP,
)
/**
* Component containing sprite sheet data - the image and frame definitions.
*/
data class SpriteSheet(
val imagePath: String,
val frames: Map<FrameName, Rect>,
) : Component
enum class AnimationDirection(
val step: Int,
) {
/** Play in forward direction **/
FORWARD(step = 1),
/** Play in reverse direction **/
BACKWARD(step = -1),
}
/**
* Current state of an animation.
*/
data class Animator(
val clips: Map<AnimationName, AnimationClip>,
var currentClip: AnimationName,
var frameIndex: Int = 0,
var elapsedTicks: Int = 0,
var playing: Boolean = true,
var direction: AnimationDirection = AnimationDirection.FORWARD,
) : Component {
/**
* Play a specific animation clip by name.
*/
fun play(
clipName: AnimationName,
restart: Boolean = true,
) {
clipName
.takeIf { currentClip != it || restart }
.run {
currentClip = clipName
reset()
}
}
/**
* Pause the current animation.
*/
fun pause() {
playing = false
}
/**
* Resume the current animation.
*/
fun resume() {
playing = true
}
/**
* Get the current frame name being displayed.
*/
fun getCurrentFrameName(): FrameName? {
val clip = clips[currentClip] ?: return null
return clip.frameNames.getOrNull(frameIndex)
}
private fun reset() {
frameIndex = 0
elapsedTicks = 0
direction = AnimationDirection.FORWARD
playing = true
}
}
|