From bc71ea6b41b04a26a37dd9a6ecc251030d579637 Mon Sep 17 00:00:00 2001 From: jiepengtan Date: Wed, 19 Jun 2024 19:18:46 +0800 Subject: [PATCH 1/4] add Awake event, add default animation binding #builder #603 --- config.go | 1 + event.go | 23 +++++++++++++++++++++++ game.go | 23 +++++++++++++++++++++-- input.go | 3 +++ sprite.go | 35 +++++++++++++++++++++++++---------- 5 files changed, 73 insertions(+), 12 deletions(-) diff --git a/config.go b/config.go index 8972ff55..f18f10c0 100644 --- a/config.go +++ b/config.go @@ -232,6 +232,7 @@ type spriteConfig struct { TAnimations map[string]*aniConfig `json:"tAnimations"` Visible bool `json:"visible"` IsDraggable bool `json:"isDraggable"` + DefaultAnimation string `json:"defaultAnimation"` } func (p *spriteConfig) getCostumeIndex() int { diff --git a/event.go b/event.go index 30ebafbd..d181ed2e 100644 --- a/event.go +++ b/event.go @@ -89,6 +89,7 @@ func (p *eventSink) call(wait bool, data interface{}, doSth func(*eventSink)) { // ------------------------------------------------------------------------------------- type eventSinkMgr struct { + allWhenAwake *eventSink allWhenStart *eventSink allWhenKeyPressed *eventSink allWhenIReceive *eventSink @@ -99,9 +100,11 @@ type eventSinkMgr struct { allWhenMoving *eventSink allWhenTurning *eventSink calledStart bool + calledAwake bool } func (p *eventSinkMgr) reset() { + p.allWhenAwake = nil p.allWhenStart = nil p.allWhenKeyPressed = nil p.allWhenIReceive = nil @@ -112,9 +115,11 @@ func (p *eventSinkMgr) reset() { p.allWhenMoving = nil p.allWhenTurning = nil p.calledStart = false + p.calledAwake = false } func (p *eventSinkMgr) doDeleteClone(this interface{}) { + p.allWhenAwake = p.allWhenAwake.doDeleteClone(this) p.allWhenStart = p.allWhenStart.doDeleteClone(this) p.allWhenKeyPressed = p.allWhenKeyPressed.doDeleteClone(this) p.allWhenIReceive = p.allWhenIReceive.doDeleteClone(this) @@ -125,6 +130,17 @@ func (p *eventSinkMgr) doDeleteClone(this interface{}) { p.allWhenMoving = p.allWhenMoving.doDeleteClone(this) p.allWhenTurning = p.allWhenTurning.doDeleteClone(this) } +func (p *eventSinkMgr) doWhenAwake() { + if !p.calledAwake { + p.calledAwake = true + p.allWhenAwake.asyncCall(false, nil, func(ev *eventSink) { + if debugEvent { + log.Println("==> onAwake", nameOf(ev.pthis)) + } + ev.sink.(func())() + }) + } +} func (p *eventSinkMgr) doWhenStart() { if !p.calledStart { @@ -227,6 +243,13 @@ func (p *eventSinks) doDeleteClone() { } // ------------------------------------------------------------------------------------- +func (p *eventSinks) OnAwake(onStart func()) { + p.allWhenAwake = &eventSink{ + prev: p.allWhenAwake, + pthis: p.pthis, + sink: onStart, + } +} func (p *eventSinks) OnStart(onStart func()) { p.allWhenStart = &eventSink{ diff --git a/game.go b/game.go index 1e50e6c0..e7871ba3 100644 --- a/game.go +++ b/game.go @@ -375,8 +375,19 @@ func (p *Game) loadSprite(sprite Spriter, name string, gamer reflect.Value) erro } func spriteOf(sprite Spriter) *Sprite { - vSpr := reflect.ValueOf(sprite).Elem() - return vSpr.Field(0).Addr().Interface().(*Sprite) + vSpr := reflect.ValueOf(sprite) + if vSpr.Kind() != reflect.Ptr { + return nil + } + vSpr = vSpr.Elem() + if vSpr.Kind() != reflect.Struct || vSpr.NumField() < 1 { + return nil + } + spriteField := vSpr.Field(0) + if spriteField.Type() != reflect.TypeOf(Sprite{}) { + return nil + } + return spriteField.Addr().Interface().(*Sprite) } func (p *Game) loadIndex(g reflect.Value, proj *projConfig) (err error) { @@ -408,6 +419,12 @@ func (p *Game) loadIndex(g reflect.Value, proj *projConfig) (err error) { } } for _, ini := range inits { + spr := spriteOf(ini) + if spr != nil { + spr.OnAwake(func() { + spr.Awake() + }) + } ini.Main() } @@ -628,6 +645,8 @@ func (p *Game) handleEvent(event event) { p.doWhenLeftButtonDown(ev) case *eventKeyDown: p.sinkMgr.doWhenKeyPressed(ev.Key) + case *eventAwake: + p.sinkMgr.doWhenAwake() case *eventStart: p.sinkMgr.doWhenStart() } diff --git a/input.go b/input.go index 545d0386..4669bf22 100644 --- a/input.go +++ b/input.go @@ -132,6 +132,8 @@ const ( type event interface{} +type eventAwake struct{} + type eventStart struct{} type eventKeyDown struct { @@ -194,6 +196,7 @@ const ( func (i *inputMgr) update() { i.touchIDs = ebiten.AppendTouchIDs(i.touchIDs[:0]) i.startFlag.Do(func() { + i.firer.fireEvent(&eventAwake{}) i.firer.fireEvent(&eventStart{}) }) i.updateKeyboard() diff --git a/sprite.go b/sprite.go index 187b20a9..5a3c97dd 100644 --- a/sprite.go +++ b/sprite.go @@ -62,10 +62,11 @@ type Sprite struct { rotationStyle RotationStyle rRect *math32.RotatedRect - sayObj *sayOrThinker - quoteObj *quoter - animations map[string]*aniConfig - greffUniforms map[string]interface{} // graphic effects + sayObj *sayOrThinker + quoteObj *quoter + animations map[string]*aniConfig + greffUniforms map[string]interface{} // graphic effects + defaultAnimation string penColor color.RGBA penShade float64 @@ -82,7 +83,8 @@ type Sprite struct { hasOnCloned bool hasOnTouched bool - gamer reflect.Value + gamer reflect.Value + isAwaked bool } func (p *Sprite) SetDying() { // dying: visible but can't be touched @@ -111,6 +113,7 @@ func (p *Sprite) init( p.isVisible = sprite.Visible + p.defaultAnimation = sprite.DefaultAnimation p.animations = make(map[string]*aniConfig) for key, val := range sprite.FAnimations { @@ -146,7 +149,15 @@ func (p *Sprite) init( p.animations[key] = ani } } - +func (p *Sprite) Awake() { + if p.isAwaked { + return + } + p.isAwaked = true + if p.defaultAnimation != "" { + p.Animate(p.defaultAnimation) + } +} func (p *Sprite) InitFrom(src *Sprite) { p.baseObj.initFrom(&src.baseObj) p.eventSinks.initFrom(&src.eventSinks, p) @@ -235,6 +246,11 @@ func cloneSprite(out reflect.Value, outPtr Spriter, in reflect.Value, v specsp) if v != nil { // in loadSprite applySpriteProps(dest, v) } else { // in sprite.Clone + dest.calledAwake = false + dest.isAwaked = false + dest.OnAwake(func() { + dest.Awake() + }) outPtr.Main() } return dest @@ -254,6 +270,7 @@ func Gopt_Sprite_Clone__1(sprite Spriter, data interface{}) { out, outPtr := v.Elem(), v.Interface().(Spriter) dest := cloneSprite(out, outPtr, in, nil) src.g.addClonedShape(src, dest) + dest.doWhenAwake() if dest.hasOnCloned { dest.doWhenCloned(dest, data) } @@ -524,10 +541,8 @@ func (p *Sprite) goAnimate(name string, ani *aniConfig) { var animwg sync.WaitGroup animwg.Add(1) - if ani.OnStart != nil { - if ani.OnStart.Play != "" { - p.g.Play__3(ani.OnStart.Play) - } + if ani.OnStart != nil && ani.OnStart.Play != "" { + p.g.Play__3(ani.OnStart.Play) } //anim frame From 3265d35d9dcea196b07c9bee10481d0a4430963b Mon Sep 17 00:00:00 2001 From: jiepengtan Date: Fri, 21 Jun 2024 14:30:45 +0800 Subject: [PATCH 2/4] add animation rebinding --- config.go | 1 + sprite.go | 35 +++++++++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/config.go b/config.go index f18f10c0..42517be4 100644 --- a/config.go +++ b/config.go @@ -233,6 +233,7 @@ type spriteConfig struct { Visible bool `json:"visible"` IsDraggable bool `json:"isDraggable"` DefaultAnimation string `json:"defaultAnimation"` + AnimBindings map[string]string `json:"animBindings"` } func (p *spriteConfig) getCostumeIndex() int { diff --git a/sprite.go b/sprite.go index 5a3c97dd..97781dff 100644 --- a/sprite.go +++ b/sprite.go @@ -48,6 +48,11 @@ const ( EdgeRight specialObj = touchingScreenRight EdgeBottom specialObj = touchingScreenBottom ) +const ( + StateDie string = "die" + StateTurn string = "turn" + StateGlide string = "glide" +) type Sprite struct { baseObj @@ -66,6 +71,7 @@ type Sprite struct { quoteObj *quoter animations map[string]*aniConfig greffUniforms map[string]interface{} // graphic effects + animBindings map[string]string defaultAnimation string penColor color.RGBA @@ -113,9 +119,13 @@ func (p *Sprite) init( p.isVisible = sprite.Visible + p.animBindings = make(map[string]string) + for key, val := range sprite.AnimBindings { + p.animBindings[key] = val + } + p.defaultAnimation = sprite.DefaultAnimation p.animations = make(map[string]*aniConfig) - for key, val := range sprite.FAnimations { var ani = val ani.AniType = aniTypeFrame @@ -418,7 +428,7 @@ func (p *Sprite) OnTurning__1(onTurning func()) { } func (p *Sprite) Die() { // prototype sprite can't be destroyed, but can die - const aniName = "die" + aniName := p.getStateAnimName(StateDie) p.SetDying() if ani, ok := p.animations[aniName]; ok { p.goAnimate(aniName, ani) @@ -537,6 +547,13 @@ func (p *Sprite) getFromAnToForAni(anitype aniTypeEnum, from interface{}, to int } +func (p *Sprite) getStateAnimName(stateName string) string { + if bindingName, ok := p.animBindings[stateName]; ok { + return bindingName + } + return stateName +} + func (p *Sprite) goAnimate(name string, ani *aniConfig) { var animwg sync.WaitGroup animwg.Add(1) @@ -790,7 +807,8 @@ func (p *Sprite) Glide__0(x, y float64, secs float64) { To: math32.NewVector2(x, y), AniType: aniTypeGlide, } - p.goAnimate("glide", ani) + animName := p.getStateAnimName(StateGlide) + p.goAnimate(animName, ani) } func (p *Sprite) Glide__1(obj interface{}, secs float64) { @@ -885,13 +903,13 @@ func (p *Sprite) Turn(val interface{}) { default: panic("Turn: unexpected input") } - - if ani, ok := p.animations["turn"]; ok { + animName := p.getStateAnimName(StateTurn) + if ani, ok := p.animations[animName]; ok { anicopy := *ani anicopy.From = p.direction anicopy.To = p.direction + delta anicopy.Duration = ani.Duration / 360.0 * math.Abs(delta) - p.goAnimate("turn", &anicopy) + p.goAnimate(animName, &anicopy) return } if p.setDirection(delta, true) && debugInstr { @@ -925,7 +943,8 @@ func (p *Sprite) TurnTo(obj interface{}) { angle = 90 - math.Atan2(dy, dx)*180/math.Pi } - if ani, ok := p.animations["turn"]; ok { + animName := p.getStateAnimName(StateTurn) + if ani, ok := p.animations[animName]; ok { fromangle := math.Mod(p.direction+360.0, 360.0) toangle := math.Mod(angle+360.0, 360.0) if toangle-fromangle > 180.0 { @@ -939,7 +958,7 @@ func (p *Sprite) TurnTo(obj interface{}) { anicopy.From = fromangle anicopy.To = toangle anicopy.Duration = ani.Duration / 360.0 * math.Abs(delta) - p.goAnimate("turn", &anicopy) + p.goAnimate(animName, &anicopy) return } if p.setDirection(angle, false) && debugInstr { From 7f078507ea8ef8116aba09f1e71405b61badc0c4 Mon Sep 17 00:00:00 2001 From: jiepengtan Date: Fri, 21 Jun 2024 17:14:47 +0800 Subject: [PATCH 3/4] remove sprite event Awake --- event.go | 23 ----------------------- game.go | 4 +--- input.go | 3 --- sprite.go | 14 ++++---------- 4 files changed, 5 insertions(+), 39 deletions(-) diff --git a/event.go b/event.go index d181ed2e..30ebafbd 100644 --- a/event.go +++ b/event.go @@ -89,7 +89,6 @@ func (p *eventSink) call(wait bool, data interface{}, doSth func(*eventSink)) { // ------------------------------------------------------------------------------------- type eventSinkMgr struct { - allWhenAwake *eventSink allWhenStart *eventSink allWhenKeyPressed *eventSink allWhenIReceive *eventSink @@ -100,11 +99,9 @@ type eventSinkMgr struct { allWhenMoving *eventSink allWhenTurning *eventSink calledStart bool - calledAwake bool } func (p *eventSinkMgr) reset() { - p.allWhenAwake = nil p.allWhenStart = nil p.allWhenKeyPressed = nil p.allWhenIReceive = nil @@ -115,11 +112,9 @@ func (p *eventSinkMgr) reset() { p.allWhenMoving = nil p.allWhenTurning = nil p.calledStart = false - p.calledAwake = false } func (p *eventSinkMgr) doDeleteClone(this interface{}) { - p.allWhenAwake = p.allWhenAwake.doDeleteClone(this) p.allWhenStart = p.allWhenStart.doDeleteClone(this) p.allWhenKeyPressed = p.allWhenKeyPressed.doDeleteClone(this) p.allWhenIReceive = p.allWhenIReceive.doDeleteClone(this) @@ -130,17 +125,6 @@ func (p *eventSinkMgr) doDeleteClone(this interface{}) { p.allWhenMoving = p.allWhenMoving.doDeleteClone(this) p.allWhenTurning = p.allWhenTurning.doDeleteClone(this) } -func (p *eventSinkMgr) doWhenAwake() { - if !p.calledAwake { - p.calledAwake = true - p.allWhenAwake.asyncCall(false, nil, func(ev *eventSink) { - if debugEvent { - log.Println("==> onAwake", nameOf(ev.pthis)) - } - ev.sink.(func())() - }) - } -} func (p *eventSinkMgr) doWhenStart() { if !p.calledStart { @@ -243,13 +227,6 @@ func (p *eventSinks) doDeleteClone() { } // ------------------------------------------------------------------------------------- -func (p *eventSinks) OnAwake(onStart func()) { - p.allWhenAwake = &eventSink{ - prev: p.allWhenAwake, - pthis: p.pthis, - sink: onStart, - } -} func (p *eventSinks) OnStart(onStart func()) { p.allWhenStart = &eventSink{ diff --git a/game.go b/game.go index e7871ba3..9edc6db4 100644 --- a/game.go +++ b/game.go @@ -421,7 +421,7 @@ func (p *Game) loadIndex(g reflect.Value, proj *projConfig) (err error) { for _, ini := range inits { spr := spriteOf(ini) if spr != nil { - spr.OnAwake(func() { + spr.OnStart(func() { spr.Awake() }) } @@ -645,8 +645,6 @@ func (p *Game) handleEvent(event event) { p.doWhenLeftButtonDown(ev) case *eventKeyDown: p.sinkMgr.doWhenKeyPressed(ev.Key) - case *eventAwake: - p.sinkMgr.doWhenAwake() case *eventStart: p.sinkMgr.doWhenStart() } diff --git a/input.go b/input.go index 4669bf22..545d0386 100644 --- a/input.go +++ b/input.go @@ -132,8 +132,6 @@ const ( type event interface{} -type eventAwake struct{} - type eventStart struct{} type eventKeyDown struct { @@ -196,7 +194,6 @@ const ( func (i *inputMgr) update() { i.touchIDs = ebiten.AppendTouchIDs(i.touchIDs[:0]) i.startFlag.Do(func() { - i.firer.fireEvent(&eventAwake{}) i.firer.fireEvent(&eventStart{}) }) i.updateKeyboard() diff --git a/sprite.go b/sprite.go index 7efab7ed..f1a2077c 100644 --- a/sprite.go +++ b/sprite.go @@ -91,7 +91,6 @@ type Sprite struct { hasOnTouched bool gamer reflect.Value - isAwaked bool lastAnim *anim.Anim } @@ -162,12 +161,10 @@ func (p *Sprite) init( } } func (p *Sprite) Awake() { - if p.isAwaked { - return - } - p.isAwaked = true if p.defaultAnimation != "" { - p.Animate(p.defaultAnimation) + if p.isVisible { + p.Animate(p.defaultAnimation) + } } } func (p *Sprite) InitFrom(src *Sprite) { @@ -258,9 +255,7 @@ func cloneSprite(out reflect.Value, outPtr Spriter, in reflect.Value, v specsp) if v != nil { // in loadSprite applySpriteProps(dest, v) } else { // in sprite.Clone - dest.calledAwake = false - dest.isAwaked = false - dest.OnAwake(func() { + dest.OnCloned__1(func() { dest.Awake() }) outPtr.Main() @@ -282,7 +277,6 @@ func Gopt_Sprite_Clone__1(sprite Spriter, data interface{}) { out, outPtr := v.Elem(), v.Interface().(Spriter) dest := cloneSprite(out, outPtr, in, nil) src.g.addClonedShape(src, dest) - dest.doWhenAwake() if dest.hasOnCloned { dest.doWhenCloned(dest, data) } From 5e4b01dcf2c534e0027997e8be8d6b57059da229 Mon Sep 17 00:00:00 2001 From: jiepengtan Date: Fri, 21 Jun 2024 17:19:25 +0800 Subject: [PATCH 4/4] make the "Awake" method private --- game.go | 2 +- sprite.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/game.go b/game.go index 9edc6db4..6e33982c 100644 --- a/game.go +++ b/game.go @@ -422,7 +422,7 @@ func (p *Game) loadIndex(g reflect.Value, proj *projConfig) (err error) { spr := spriteOf(ini) if spr != nil { spr.OnStart(func() { - spr.Awake() + spr.awake() }) } ini.Main() diff --git a/sprite.go b/sprite.go index f1a2077c..2a2b13ee 100644 --- a/sprite.go +++ b/sprite.go @@ -160,7 +160,7 @@ func (p *Sprite) init( p.animations[key] = ani } } -func (p *Sprite) Awake() { +func (p *Sprite) awake() { if p.defaultAnimation != "" { if p.isVisible { p.Animate(p.defaultAnimation) @@ -256,7 +256,7 @@ func cloneSprite(out reflect.Value, outPtr Spriter, in reflect.Value, v specsp) applySpriteProps(dest, v) } else { // in sprite.Clone dest.OnCloned__1(func() { - dest.Awake() + dest.awake() }) outPtr.Main() }