/
animation.go
119 lines (99 loc) · 3.46 KB
/
animation.go
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
119
package animationsystem
import (
"fmt"
"math"
c "github.com/x-hgg-x/goecsengine/components"
m "github.com/x-hgg-x/goecsengine/math"
"github.com/x-hgg-x/goecsengine/utils"
w "github.com/x-hgg-x/goecsengine/world"
"github.com/hajimehoshi/ebiten"
ecs "github.com/x-hgg-x/goecs"
)
// AnimationSystem updates animations
func AnimationSystem(world w.World) {
world.Manager.Join(world.Components.Engine.SpriteRender, world.Components.Engine.AnimationControl).Visit(ecs.Visit(func(entity ecs.Entity) {
sprite := world.Components.Engine.SpriteRender.Get(entity).(*c.SpriteRender)
animationControl := world.Components.Engine.AnimationControl.Get(entity).(*c.AnimationControl)
times := animationControl.Animation.Time
spriteNumbers := animationControl.Animation.SpriteNumber
currentTime := animationControl.GetState().CurrentTime
animationPos := computeAnimationPos(currentTime, times)
// Process command
switch animationControl.Command.Type {
case c.AnimationCommandRestart:
animationControl.SetStateType(c.ControlStateRunning)
currentTime = 0
animationPos = 0
case c.AnimationCommandStart:
animationControl.SetStateType(c.ControlStateRunning)
case c.AnimationCommandStepBackward:
if animationControl.End.Type == c.EndControlLoop && animationPos == 0 {
animationPos = len(spriteNumbers) - 1
} else {
animationPos = m.Max(0, animationPos-1)
}
currentTime = times[animationPos]
case c.AnimationCommandStepForward:
if animationControl.End.Type == c.EndControlLoop && animationPos == len(spriteNumbers)-1 {
animationPos = 0
} else {
animationPos = m.Min(len(spriteNumbers)-1, animationPos+1)
}
currentTime = times[animationPos]
case c.AnimationCommandSetTime:
currentTime = math.Min(math.Max(animationControl.Command.Time, times[0]), times[len(times)-1])
animationPos = computeAnimationPos(currentTime, times)
case c.AnimationCommandPause:
animationControl.SetStateType(c.ControlStatePaused)
case c.AnimationCommandAbort:
entity.RemoveComponent(world.Components.Engine.AnimationControl)
return
case c.AnimationCommandNone:
break
default:
utils.LogError(fmt.Errorf("unknown animation command: %v", animationControl.Command.Type))
}
// Reset command
animationControl.Command = c.AnimationCommand{}
// Run animation
if animationControl.GetState().Type == c.ControlStateRunning {
currentTime += animationControl.RateMultiplier / float64(ebiten.DefaultTPS)
}
// Check animation end
if animationControl.End.Type == c.EndControlLoop {
currentTime = mod(currentTime, times[len(times)-1])
} else if currentTime >= times[len(times)-1] {
switch animationControl.End.Type {
case c.EndControlNormal:
animationControl.SetStateType(c.ControlStateDone)
currentTime = 0
case c.EndControlStay:
animationControl.SetStateType(c.ControlStateDone)
currentTime = times[len(times)-1]
default:
utils.LogError(fmt.Errorf("unknown end control: %v", animationControl.End.Type))
}
}
animationPos = computeAnimationPos(currentTime, times)
// Set animation state
animationControl.SetCurrentTime(currentTime)
sprite.SpriteNumber = spriteNumbers[animationPos]
}))
}
func computeAnimationPos(currentTime float64, times []float64) int {
animationPos := 0
for iTime := range times[1:] {
animationPos = iTime
if times[iTime+1] > currentTime {
break
}
}
return animationPos
}
func mod(a, b float64) float64 {
m := math.Mod(a, b)
if m < 0 {
m += math.Abs(b)
}
return m
}