Skip to content

Commit

Permalink
Support property anim, based on timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
qlli committed Oct 30, 2024
1 parent 845f2c0 commit 71f5067
Show file tree
Hide file tree
Showing 7 changed files with 912 additions and 10 deletions.
32 changes: 22 additions & 10 deletions game.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/goplus/spx/internal/coroutine"
"github.com/goplus/spx/internal/gdi"
"github.com/goplus/spx/internal/math32"
"github.com/goplus/spx/internal/timeline"
"github.com/hajimehoshi/ebiten/v2"

spxfs "github.com/goplus/spx/fs"
Expand All @@ -54,21 +55,24 @@ const (
DbgFlagInstr
DbgFlagEvent
DbgFlagPerf
DbgFlagAll = DbgFlagLoad | DbgFlagInstr | DbgFlagEvent | DbgFlagPerf
DbgFlagTimeline
DbgFlagAll = DbgFlagLoad | DbgFlagInstr | DbgFlagEvent | DbgFlagPerf | DbgFlagTimeline
)

var (
debugInstr bool
debugLoad bool
debugEvent bool
debugPerf bool
debugInstr bool
debugLoad bool
debugEvent bool
debugPerf bool
debugTimeline bool
)

func SetDebug(flags dbgFlags) {
debugLoad = (flags & DbgFlagLoad) != 0
debugInstr = (flags & DbgFlagInstr) != 0
debugEvent = (flags & DbgFlagEvent) != 0
debugPerf = (flags & DbgFlagPerf) != 0
debugTimeline = (flags & DbgFlagTimeline) != 0
}

// -------------------------------------------------------------------------------------
Expand All @@ -81,11 +85,12 @@ type Game struct {
fs spxfs.Dir
shared *sharedImages

sounds soundMgr
turtle turtleCanvas
typs map[string]reflect.Type // map: name => sprite type, for all sprites
sprs map[string]Sprite // map: name => sprite prototype, for loaded sprites
items []Shape // shapes on stage (in Zorder), not only sprites
sounds soundMgr
turtle turtleCanvas
typs map[string]reflect.Type // map: name => sprite type, for all sprites
sprs map[string]Sprite // map: name => sprite prototype, for loaded sprites
items []Shape // shapes on stage (in Zorder), not only sprites
timelines TimelineMgr

tickMgr tickMgr
input inputMgr
Expand Down Expand Up @@ -625,16 +630,23 @@ func (p *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeigh
return p.windowSize_()
}

var ts int64

func (p *Game) Update() error {
if !p.isLoaded {
return nil
}

old := ts
ts = time.Now().UnixMilli()
p.updateColliders()
p.input.update()
p.updateMousePos()
p.sounds.update()
p.tickMgr.update()
if old != 0 {
p.timelines.Update(timeline.TIME(float64(ts-old) / 1000.0))
}
return nil
}

Expand Down
33 changes: 33 additions & 0 deletions internal/timeline/interval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package timeline

import "fmt"

// interval.go
type TIME float64
type Interval struct {
Offset TIME
Duration TIME
}

func (this Interval) End() TIME {
return this.Offset + this.Duration
}

func (this *Interval) Step(time TIME) {
this.Offset -= time
}

func (this Interval) Scale(scale float32) Interval {
return Interval{
Offset: TIME(float64(this.Offset) * float64(scale)),
Duration: TIME(float64(this.Duration) * float64(scale)),
}
}

func (this Interval) Contains(time TIME) bool {
return this.Offset <= time && time <= this.End()
}

func (this Interval) String() string {
return fmt.Sprintf("Interval{offset:%.3f, duration:%.3f}", this.Offset, this.Duration)
}
195 changes: 195 additions & 0 deletions internal/timeline/timeline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package timeline

import (
"fmt"
"log"
"sync"
)

const EPSILON TIME = 0.0001
const DEFAULT_TRANSITION TIME = 0.5

var debugTimeline bool = false
var idSeed int64
var idSeedMutex sync.Mutex

type ON_STEP func(*TIME) ITimeline
type ON_ACTIVE func(bool)

type ITimeline interface {
GetTimeline() *Timeline
Step(time *TIME) ITimeline
SetActive(bool)
}

type Timeline struct {
ID int64
active bool
offset TIME
speed float64
fadeIn *Interval
fadeOut *Interval
freezingTime TIME
next ITimeline
group *TimelineGroup
onStep ON_STEP
onActive ON_ACTIVE
}

func (t *Timeline) Init() *Timeline {
idSeedMutex.Lock()
defer idSeedMutex.Unlock()
t.ID = idSeed
idSeed++
if debugTimeline {
log.Printf("Timeline %X", t.ID)
}
t.speed = 1.0
return t
}

func (t *Timeline) GetTimeline() *Timeline {
return t
}

func (t *Timeline) SetActive(on bool) {
if t.active == on {
return
}
if debugTimeline {
log.Println("SetActive", on, t.ID)
}
t.active = on
if t.onActive != nil {
t.onActive(on)
}
}

func (t *Timeline) Step(time *TIME) ITimeline {
if *time < 0.0 {
*time = 0.0
}

var running ITimeline = t
var LOOP_LIMIT int = 10000
for running != nil && *time > EPSILON && LOOP_LIMIT > 0 {
LOOP_LIMIT--
var realStep, scaledTime, oldScaledTime TIME
var oldRunning ITimeline
r := running.GetTimeline()

// step until time <= 0 || offset <= 0
var min TIME = *time // minimum step in consideration of time, offset, fadeout.end, freezingTime
if r.offset > EPSILON {
if min > r.offset {
min = r.offset
}
*time -= min
r.offset -= min
continue
}

r.SetActive(true)

if r.fadeOut != nil {
end := r.fadeOut.End()
if end < 0.0 {
end = 0.0
}
if min > end {
min = end
}
}

// step until time <= 0 || fadeOut.end <= 0 || freezingTime <= 0
if r.freezingTime > EPSILON {
if min > r.freezingTime {
min = r.freezingTime
}
*time -= min
r.freezingTime -= min
if r.fadeIn != nil {
r.fadeIn.Step(min)
}
if r.fadeOut != nil {
r.fadeOut.Step(min)
}

goto CHECK_FADE_OUT
}

// step until time <= 0 || fadeOut.end <= 0 || runOut.end <= 0
scaledTime = TIME(float64(min) * r.speed)
oldScaledTime = scaledTime
oldRunning = running
running = r.onStep(&scaledTime)
if r.speed > float64(EPSILON) {
realStep = (oldScaledTime - scaledTime) / TIME(r.speed)
} else {
realStep = min
}
*time -= realStep
if r.fadeIn != nil {
r.fadeIn.Step(realStep)
}
if r.fadeOut != nil {
r.fadeOut.Step(realStep)
}

if running != oldRunning {
if running != nil {
continue
}

// running == null, means the timeline has run out, should check whether fade out
if r.fadeOut == nil || r.fadeOut.End() <= EPSILON {
oldRunning.SetActive(false)
running = r.next
continue
}

// step until time <= 0 || fadeOut.end <= 0
end2 := r.fadeOut.End()
if end2 < 0.0 {
end2 = 0.0
}
min2 := *time
if min2 > end2 {
min2 = end2
}
*time -= min2
if r.fadeIn != nil {
r.fadeIn.Step(min2)
}
if r.fadeOut != nil {
r.fadeOut.Step(min2)
}
running = oldRunning
}

CHECK_FADE_OUT:
t2 := running.GetTimeline()
if t2.fadeOut != nil && t2.fadeOut.End() <= EPSILON {
t2.SetActive(false)
running = t2.next
}
}

if LOOP_LIMIT <= 0 {
panic("LOOP LIMIT")
}
return running
}

func (this *Timeline) String() string {
var nID int64 = 0
if this.next != nil {
nID = this.next.GetTimeline().ID
}
var gID int64 = 0
if this.group != nil {
gID = this.group.ID
}
return fmt.Sprintf("{id:%x,active:%t,offset:%.3f,speed:%.3f,in:%s,out:%s,frz:%.3f,next:%x,group:%x}",
this.ID, this.active, this.offset, this.speed, this.fadeIn, this.fadeOut, this.freezingTime, nID, gID)
}
19 changes: 19 additions & 0 deletions internal/timeline/timelineGroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package timeline

import "fmt"

type TimelineGroup struct {
Timeline
Tracks []ITimeline
}

func (tg *TimelineGroup) Step(time *TIME) ITimeline {
tg.Timeline.Step(time)
fmt.Println("TimelineGroup::Step", *time)
return nil
}

// AddTrack 方法
func (tg *TimelineGroup) AddTrack(track ITimeline) {
tg.Tracks = append(tg.Tracks, track)
}
Loading

0 comments on commit 71f5067

Please sign in to comment.