Skip to content

Commit 0c45840

Browse files
committed
Support property anim, based on timeline
1 parent 845f2c0 commit 0c45840

File tree

7 files changed

+901
-10
lines changed

7 files changed

+901
-10
lines changed

game.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,24 @@ const (
5454
DbgFlagInstr
5555
DbgFlagEvent
5656
DbgFlagPerf
57-
DbgFlagAll = DbgFlagLoad | DbgFlagInstr | DbgFlagEvent | DbgFlagPerf
57+
DbgFlagTimeline
58+
DbgFlagAll = DbgFlagLoad | DbgFlagInstr | DbgFlagEvent | DbgFlagPerf | DbgFlagTimeline
5859
)
5960

6061
var (
61-
debugInstr bool
62-
debugLoad bool
63-
debugEvent bool
64-
debugPerf bool
62+
debugInstr bool
63+
debugLoad bool
64+
debugEvent bool
65+
debugPerf bool
66+
debugTimeline bool
6567
)
6668

6769
func SetDebug(flags dbgFlags) {
6870
debugLoad = (flags & DbgFlagLoad) != 0
6971
debugInstr = (flags & DbgFlagInstr) != 0
7072
debugEvent = (flags & DbgFlagEvent) != 0
7173
debugPerf = (flags & DbgFlagPerf) != 0
74+
debugTimeline = (flags & DbgFlagTimeline) != 0
7275
}
7376

7477
// -------------------------------------------------------------------------------------
@@ -81,11 +84,12 @@ type Game struct {
8184
fs spxfs.Dir
8285
shared *sharedImages
8386

84-
sounds soundMgr
85-
turtle turtleCanvas
86-
typs map[string]reflect.Type // map: name => sprite type, for all sprites
87-
sprs map[string]Sprite // map: name => sprite prototype, for loaded sprites
88-
items []Shape // shapes on stage (in Zorder), not only sprites
87+
sounds soundMgr
88+
turtle turtleCanvas
89+
typs map[string]reflect.Type // map: name => sprite type, for all sprites
90+
sprs map[string]Sprite // map: name => sprite prototype, for loaded sprites
91+
items []Shape // shapes on stage (in Zorder), not only sprites
92+
timelines TimelineMgr
8993

9094
tickMgr tickMgr
9195
input inputMgr
@@ -625,16 +629,23 @@ func (p *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeigh
625629
return p.windowSize_()
626630
}
627631

632+
var ts int64
633+
628634
func (p *Game) Update() error {
629635
if !p.isLoaded {
630636
return nil
631637
}
632638

639+
old := ts
640+
ts = time.Now().UnixMilli()
633641
p.updateColliders()
634642
p.input.update()
635643
p.updateMousePos()
636644
p.sounds.update()
637645
p.tickMgr.update()
646+
if old != 0 {
647+
p.timelines.Update((float64(ts-old) / 1000.0))
648+
}
638649
return nil
639650
}
640651

internal/timeline/interval.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package timeline
2+
3+
import "fmt"
4+
5+
type Interval struct {
6+
Offset float64
7+
Duration float64
8+
}
9+
10+
func (i Interval) End() float64 {
11+
return i.Offset + i.Duration
12+
}
13+
14+
func (i *Interval) Step(time float64) {
15+
i.Offset -= time
16+
}
17+
18+
func (i Interval) Scale(scale float32) Interval {
19+
return Interval{
20+
Offset: float64(float64(i.Offset) * float64(scale)),
21+
Duration: float64(float64(i.Duration) * float64(scale)),
22+
}
23+
}
24+
25+
func (i Interval) Contains(time float64) bool {
26+
return i.Offset <= time && time <= i.End()
27+
}
28+
29+
func (i Interval) String() string {
30+
return fmt.Sprintf("Interval{offset:%.3f, duration:%.3f}", i.Offset, i.Duration)
31+
}

internal/timeline/timeline.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package timeline
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"sync"
7+
)
8+
9+
const EPSILON float64 = 0.0001
10+
const DEFAULT_TRANSITION float64 = 0.5
11+
12+
var debugTimeline bool = false
13+
var idSeed int64
14+
var idSeedM sync.Mutex
15+
16+
type ITimeline interface {
17+
GetTimeline() *Timeline
18+
Step(time *float64) ITimeline
19+
SetActive(bool)
20+
}
21+
22+
type Timeline struct {
23+
ID int64
24+
active bool
25+
offset float64
26+
speed float64
27+
fadeIn *Interval
28+
fadeOut *Interval
29+
freezingTime float64
30+
next ITimeline
31+
group *TimelineGroup
32+
onStep func(*float64) ITimeline
33+
onActive func(bool)
34+
}
35+
36+
func (t *Timeline) Init() *Timeline {
37+
idSeedM.Lock()
38+
defer idSeedM.Unlock()
39+
t.ID = idSeed
40+
idSeed++
41+
if debugTimeline {
42+
log.Printf("Timeline %X", t.ID)
43+
}
44+
t.speed = 1.0
45+
return t
46+
}
47+
48+
func (t *Timeline) GetTimeline() *Timeline {
49+
return t
50+
}
51+
52+
func (t *Timeline) SetActive(on bool) {
53+
if t.active == on {
54+
return
55+
}
56+
if debugTimeline {
57+
log.Println("SetActive", on, t.ID)
58+
}
59+
t.active = on
60+
if t.onActive != nil {
61+
t.onActive(on)
62+
}
63+
}
64+
65+
func (t *Timeline) Step(time *float64) ITimeline {
66+
if *time < 0.0 {
67+
*time = 0.0
68+
}
69+
70+
var running ITimeline = t
71+
var loopLimit int = 10000
72+
for running != nil && *time > EPSILON && loopLimit > 0 {
73+
loopLimit--
74+
var realStep, scaledTime, oldScaledTime float64
75+
var oldRunning ITimeline
76+
r := running.GetTimeline()
77+
78+
// step until time <= 0 || offset <= 0
79+
var min float64 = *time // minimum step in consideration of time, offset, fadeout.end, freezingTime
80+
if r.offset > EPSILON {
81+
if min > r.offset {
82+
min = r.offset
83+
}
84+
*time -= min
85+
r.offset -= min
86+
continue
87+
}
88+
89+
r.SetActive(true)
90+
91+
if r.fadeOut != nil {
92+
end := r.fadeOut.End()
93+
if end < 0.0 {
94+
end = 0.0
95+
}
96+
if min > end {
97+
min = end
98+
}
99+
}
100+
101+
// step until time <= 0 || fadeOut.end <= 0 || freezingTime <= 0
102+
if r.freezingTime > EPSILON {
103+
if min > r.freezingTime {
104+
min = r.freezingTime
105+
}
106+
*time -= min
107+
r.freezingTime -= min
108+
if r.fadeIn != nil {
109+
r.fadeIn.Step(min)
110+
}
111+
if r.fadeOut != nil {
112+
r.fadeOut.Step(min)
113+
}
114+
115+
goto CHECK_FADE_OUT
116+
}
117+
118+
// step until time <= 0 || fadeOut.end <= 0 || runOut.end <= 0
119+
scaledTime = min * r.speed
120+
oldScaledTime = scaledTime
121+
oldRunning = running
122+
running = r.onStep(&scaledTime)
123+
if r.speed > EPSILON {
124+
realStep = (oldScaledTime - scaledTime) / r.speed
125+
} else {
126+
realStep = min
127+
}
128+
*time -= realStep
129+
if r.fadeIn != nil {
130+
r.fadeIn.Step(realStep)
131+
}
132+
if r.fadeOut != nil {
133+
r.fadeOut.Step(realStep)
134+
}
135+
136+
if running != oldRunning {
137+
if running != nil {
138+
continue
139+
}
140+
141+
// running == null, means the timeline has run out, should check whether fade out
142+
if r.fadeOut == nil || r.fadeOut.End() <= EPSILON {
143+
oldRunning.SetActive(false)
144+
running = r.next
145+
continue
146+
}
147+
148+
// step until time <= 0 || fadeOut.end <= 0
149+
end2 := r.fadeOut.End()
150+
if end2 < 0.0 {
151+
end2 = 0.0
152+
}
153+
min2 := *time
154+
if min2 > end2 {
155+
min2 = end2
156+
}
157+
*time -= min2
158+
if r.fadeIn != nil {
159+
r.fadeIn.Step(min2)
160+
}
161+
if r.fadeOut != nil {
162+
r.fadeOut.Step(min2)
163+
}
164+
running = oldRunning
165+
}
166+
167+
CHECK_FADE_OUT:
168+
t2 := running.GetTimeline()
169+
if t2.fadeOut != nil && t2.fadeOut.End() <= EPSILON {
170+
t2.SetActive(false)
171+
running = t2.next
172+
}
173+
}
174+
175+
if loopLimit <= 0 {
176+
panic("LOOP LIMIT")
177+
}
178+
return running
179+
}
180+
181+
func (t *Timeline) String() string {
182+
var nID int64 = 0
183+
if t.next != nil {
184+
nID = t.next.GetTimeline().ID
185+
}
186+
var gID int64 = 0
187+
if t.group != nil {
188+
gID = t.group.ID
189+
}
190+
return fmt.Sprintf("{id:%x,active:%t,offset:%.3f,speed:%.3f,in:%s,out:%s,frz:%.3f,next:%x,group:%x}",
191+
t.ID, t.active, t.offset, t.speed, t.fadeIn, t.fadeOut, t.freezingTime, nID, gID)
192+
}

internal/timeline/timeline_group.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package timeline
2+
3+
type TimelineGroup struct {
4+
Timeline
5+
Tracks []ITimeline
6+
}
7+
8+
func (tg *TimelineGroup) Step(time *float64) ITimeline {
9+
tg.Timeline.Step(time)
10+
return nil
11+
}
12+
13+
// AddTrack 方法
14+
func (tg *TimelineGroup) AddTrack(track ITimeline) {
15+
tg.Tracks = append(tg.Tracks, track)
16+
}

0 commit comments

Comments
 (0)