/
animation.go
144 lines (122 loc) · 4.31 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package render
import (
"log"
"engo.io/ecs"
)
// Animation represents properties of an animation.
type Animation struct {
Name string
Frames []int
Loop bool
}
// AnimationComponent tracks animations of an entity it is part of.
// This component should be created using NewAnimationComponent.
type AnimationComponent struct {
Drawables []Drawable // Renderables
Animations map[string]*Animation // All possible animations
CurrentAnimation *Animation // The current animation
Rate float32 // How often frames should increment, in seconds.
index int // What frame in the is being used
change float32 // The time since the last incrementation
def *Animation // The default animation to play when nothing else is playing
}
// NewAnimationComponent creates an AnimationComponent containing all given
// drawables. Animations will be played using the given rate.
func NewAnimationComponent(drawables []Drawable, rate float32) AnimationComponent {
return AnimationComponent{
Animations: make(map[string]*Animation),
Drawables: drawables,
Rate: rate,
}
}
// SelectAnimationByName sets the current animation. The name must be
// registered.
func (ac *AnimationComponent) SelectAnimationByName(name string) {
ac.CurrentAnimation = ac.Animations[name]
ac.index = 0
}
// SelectAnimationByAction sets the current animation.
// An nil action value selects the default animation.
func (ac *AnimationComponent) SelectAnimationByAction(action *Animation) {
ac.CurrentAnimation = action
ac.index = 0
}
// AddDefaultAnimation adds an animation which is used when no other animation is playing.
func (ac *AnimationComponent) AddDefaultAnimation(action *Animation) {
ac.AddAnimation(action)
ac.def = action
}
// AddAnimation registers an animation under its name, making it available
// through SelectAnimationByName.
func (ac *AnimationComponent) AddAnimation(action *Animation) {
ac.Animations[action.Name] = action
}
// AddAnimations registers all given animations.
func (ac *AnimationComponent) AddAnimations(actions []*Animation) {
for _, action := range actions {
ac.AddAnimation(action)
}
}
// Cell returns the drawable for the current frame.
func (ac *AnimationComponent) Cell() Drawable {
idx := ac.CurrentAnimation.Frames[ac.index]
return ac.Drawables[idx]
}
// NextFrame advances the current animation by one frame.
func (ac *AnimationComponent) NextFrame() {
if len(ac.CurrentAnimation.Frames) == 0 {
log.Println("No data for this animation")
return
}
ac.index++
ac.change = 0
if ac.index >= len(ac.CurrentAnimation.Frames) {
ac.index = 0
if !ac.CurrentAnimation.Loop {
ac.CurrentAnimation = nil
return
}
}
}
// AnimationSystem tracks AnimationComponents, advancing their current animation.
type AnimationSystem struct {
entities map[ecs.BasicEntity]animationEntity
}
type animationEntity struct {
*AnimationComponent
*RenderComponent
}
// Add starts tracking the given entity.
func (a *AnimationSystem) Add(basic *ecs.BasicEntity, anim *AnimationComponent, render *RenderComponent) {
if a.entities == nil {
a.entities = make(map[ecs.BasicEntity]animationEntity)
}
a.entities[*basic] = animationEntity{anim, render}
}
// AddByInterface Allows an Entity to be added directly using the Animtionable interface. which every entity containing the BasicEntity,AnimationComponent,and RenderComponent anonymously, automatically satisfies.
func (a *AnimationSystem) AddByInterface(i ecs.Identifier) {
o, _ := i.(Animationable)
a.Add(o.GetBasicEntity(), o.GetAnimationComponent(), o.GetRenderComponent())
}
// Remove stops tracking the given entity.
func (a *AnimationSystem) Remove(basic ecs.BasicEntity) {
if a.entities != nil {
delete(a.entities, basic)
}
}
// Update advances the animations of all tracked entities.
func (a *AnimationSystem) Update(dt float32) {
for _, e := range a.entities {
if e.AnimationComponent.CurrentAnimation == nil {
if e.AnimationComponent.def == nil {
return
}
e.AnimationComponent.SelectAnimationByAction(e.AnimationComponent.def)
}
e.AnimationComponent.change += dt
if e.AnimationComponent.change >= e.AnimationComponent.Rate {
e.RenderComponent.Drawable = e.AnimationComponent.Cell()
e.AnimationComponent.NextFrame()
}
}
}