Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Strange behavior of OnTouched #298 #340

Merged
merged 9 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ type eventSinkMgr struct {
allWhenIReceive *eventSink
allWhenBackdropChanged *eventSink
allWhenCloned *eventSink
allWhenTouched *eventSink
allWhenTouchStart *eventSink
allWhenTouching *eventSink
allWhenTouchEnd *eventSink
allWhenClick *eventSink
allWhenMoving *eventSink
allWhenTurning *eventSink
Expand All @@ -107,7 +109,9 @@ func (p *eventSinkMgr) reset() {
p.allWhenIReceive = nil
p.allWhenBackdropChanged = nil
p.allWhenCloned = nil
p.allWhenTouched = nil
p.allWhenTouchStart = nil
p.allWhenTouching = nil
p.allWhenTouchEnd = nil
p.allWhenClick = nil
p.allWhenMoving = nil
p.allWhenTurning = nil
Expand All @@ -120,7 +124,9 @@ func (p *eventSinkMgr) doDeleteClone(this interface{}) {
p.allWhenIReceive = p.allWhenIReceive.doDeleteClone(this)
p.allWhenBackdropChanged = p.allWhenBackdropChanged.doDeleteClone(this)
p.allWhenCloned = p.allWhenCloned.doDeleteClone(this)
p.allWhenTouched = p.allWhenTouched.doDeleteClone(this)
p.allWhenTouchStart = p.allWhenTouchStart.doDeleteClone(this)
p.allWhenTouching = p.allWhenTouching.doDeleteClone(this)
p.allWhenTouchEnd = p.allWhenTouchEnd.doDeleteClone(this)
p.allWhenClick = p.allWhenClick.doDeleteClone(this)
p.allWhenMoving = p.allWhenMoving.doDeleteClone(this)
p.allWhenTurning = p.allWhenTurning.doDeleteClone(this)
Expand Down Expand Up @@ -153,10 +159,28 @@ func (p *eventSinkMgr) doWhenClick(this threadObj) {
})
}

func (p *eventSinkMgr) doWhenTouched(this threadObj, obj *Sprite) {
p.allWhenTouched.asyncCall(false, this, func(ev *eventSink) {
func (p *eventSinkMgr) doWhenTouchStart(this threadObj, obj *Sprite) {
p.allWhenTouchStart.asyncCall(false, this, func(ev *eventSink) {
if debugEvent {
log.Println("==> onTouched", nameOf(this), obj.name)
log.Println("===> onTouchStart", nameOf(this), obj.name)
}
ev.sink.(func(*Sprite))(obj)
})
}

func (p *eventSinkMgr) doWhenTouching(this threadObj, obj *Sprite) {
p.allWhenTouching.asyncCall(false, this, func(ev *eventSink) {
if debugEvent {
log.Println("==> onTouching", nameOf(this), obj.name)
}
ev.sink.(func(*Sprite))(obj)
})
}

func (p *eventSinkMgr) doWhenTouchEnd(this threadObj, obj *Sprite) {
p.allWhenTouchEnd.asyncCall(false, this, func(ev *eventSink) {
if debugEvent {
log.Println("===> onTouchEnd", nameOf(this), obj.name)
}
ev.sink.(func(*Sprite))(obj)
})
Expand Down
35 changes: 34 additions & 1 deletion game.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,22 @@ const (
DbgFlagLoad dbgFlags = 1 << iota
DbgFlagInstr
DbgFlagEvent
DbgFlagAll = DbgFlagLoad | DbgFlagInstr | DbgFlagEvent
DbgFlagPerf
DbgFlagAll = DbgFlagLoad | DbgFlagInstr | DbgFlagEvent | DbgFlagPerf
)

var (
debugInstr bool
debugLoad bool
debugEvent bool
debugPerf bool
)

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

// -------------------------------------------------------------------------------------
Expand Down Expand Up @@ -617,13 +620,43 @@ func (p *Game) Update() error {
if !p.isLoaded {
return nil
}

p.updateColliders()
p.input.update()
p.updateMousePos()
p.sounds.update()
p.tickMgr.update()
return nil
}

func (p *Game) updateColliders() {
var startTime time.Time
if debugPerf {
startTime = time.Now()
}

items := p.items
n := len(items)
for i := 0; i < n; i++ {
s1, ok1 := items[i].(*Sprite)
if ok1 {
flag := s1.isVisible && !s1.isDying
for j := i + 1; j < n; j++ {
s2, ok2 := items[j].(*Sprite)
if ok2 && s1 != s2 {
flag2 := flag && s2.isVisible && !s2.isDying && s1.touchingSprite(s2)
s1.collider.SetTouching(s2, flag2)
s2.collider.SetTouching(s1, flag2)
}
}
}
}

if debugPerf {
log.Println("updateColliders shapes:", n, " cost:", time.Now().Sub(startTime))
}
}

// startTick creates tickHandler to handle `onTick` event.
// You can call tickHandler.Stop to stop listening `onTick` event.
func (p *Game) startTick(duration int64, onTick func(tick int64)) *tickHandler {
Expand Down
136 changes: 95 additions & 41 deletions sprite.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,51 @@ const (
AnimChannelMove string = "@move"
)

type Collider struct {
sprite *Sprite
others map[*Sprite]bool
othersM sync.Mutex
}

func (c *Collider) SetTouching(other *Sprite, on bool) {
c.othersM.Lock()
defer c.othersM.Unlock()

if other == nil || c.sprite == nil {
return
}

if c.others == nil {
c.others = make(map[*Sprite]bool)
}

_, exist := c.others[other]
if on {
if !exist {
c.others[other] = true
c.sprite.fireTouchStart(other)
} else {
c.sprite.fireTouching(other)
}
} else {
if exist {
delete(c.others, other)
c.sprite.fireTouchEnd(other)
}
}
}

func (c *Collider) Reset() {
copy := make(map[*Sprite]bool, len(c.others))
for k, v := range c.others {
copy[k] = v
}
for other := range copy {
c.SetTouching(other, false)
other.collider.SetTouching(c.sprite, false)
}
}

type Sprite struct {
baseObj
eventSinks
Expand Down Expand Up @@ -92,15 +137,19 @@ type Sprite struct {
isPenDown bool
isDying bool

hasOnTurning bool
hasOnMoving bool
hasOnCloned bool
hasOnTouched bool
hasOnTurning bool
hasOnMoving bool
hasOnCloned bool
hasOnTouchStart bool
hasOnTouching bool
hasOnTouchEnd bool

gamer reflect.Value
lastAnim *anim.Anim
isWaitingStopAnim bool
defaultCostumeIndex int

collider Collider
}

func (p *Sprite) SetDying() { // dying: visible but can't be touched
Expand Down Expand Up @@ -224,7 +273,11 @@ func (p *Sprite) init(
}
p.animations[key] = ani
}

p.collider.others = make(map[*Sprite]bool)
p.collider.sprite = p
}

func (p *Sprite) awake() {
p.playDefaultAnim()
}
Expand Down Expand Up @@ -255,7 +308,12 @@ func (p *Sprite) InitFrom(src *Sprite) {
p.hasOnTurning = false
p.hasOnMoving = false
p.hasOnCloned = false
p.hasOnTouched = false
p.hasOnTouchStart = false
p.hasOnTouching = false
p.hasOnTouchEnd = false

p.collider.others = make(map[*Sprite]bool)
p.collider.sprite = p
}

func cloneMap(v map[string]interface{}) map[string]interface{} {
Expand Down Expand Up @@ -362,59 +420,53 @@ func (p *Sprite) OnCloned__1(onCloned func()) {
})
}

func (p *Sprite) fireTouched(obj *Sprite) {
if p.hasOnTouched {
p.doWhenTouched(p, obj)
func (p *Sprite) fireTouchStart(obj *Sprite) {
if p.hasOnTouchStart {
p.doWhenTouchStart(p, obj)
}
}

func (p *Sprite) fireTouching(obj *Sprite) {
if p.hasOnTouching {
p.doWhenTouching(p, obj)
}
}

func (p *Sprite) fireTouchEnd(obj *Sprite) {
if p.hasOnTouchEnd {
p.doWhenTouchEnd(p, obj)
}
}

func (p *Sprite) OnTouched__0(onTouched func(obj *Sprite)) {
p.hasOnTouched = true
p.allWhenTouched = &eventSink{
prev: p.allWhenTouched,
func (p *Sprite) OnTouchStart__0(onTouchStart func(obj *Sprite)) {
p.hasOnTouchStart = true
p.allWhenTouchStart = &eventSink{
prev: p.allWhenTouchStart,
pthis: p,
sink: onTouched,
sink: onTouchStart,
cond: func(data interface{}) bool {
return data == p
},
}
}

func (p *Sprite) OnTouched__1(onTouched func()) {
p.OnTouched__0(func(*Sprite) {
onTouched()
func (p *Sprite) OnTouchStart__1(onTouchStart func()) {
p.OnTouchStart__0(func(*Sprite) {
onTouchStart()
})
}

func (p *Sprite) OnTouched__2(name string, onTouched func(obj *Sprite)) {
p.OnTouched__0(func(obj *Sprite) {
func (p *Sprite) OnTouchStart__2(name string, onTouchStart func(*Sprite)) {
p.OnTouchStart__0(func(obj *Sprite) {
if obj.name == name {
onTouched(obj)
onTouchStart(obj)
}
})
}

func (p *Sprite) OnTouched__3(name string, onTouched func()) {
p.OnTouched__2(name, func(*Sprite) {
onTouched()
})
}

func (p *Sprite) OnTouched__4(names []string, onTouched func(obj *Sprite)) {
p.OnTouched__0(func(obj *Sprite) {
name := obj.name
for _, v := range names {
if v == name {
onTouched(obj)
return
}
}
})
}

func (p *Sprite) OnTouched__5(names []string, onTouched func()) {
p.OnTouched__4(names, func(*Sprite) {
onTouched()
func (p *Sprite) OnTouchStart__3(name string, onTouchStart func()) {
p.OnTouchStart__2(name, func(*Sprite) {
onTouchStart()
})
}

Expand Down Expand Up @@ -502,6 +554,7 @@ func (p *Sprite) Destroy() { // destroy sprite, whether prototype or cloned
log.Println("Destroy", p.name)
}

p.collider.Reset()
p.Hide()
p.doDeleteClone()
p.g.removeShape(p)
Expand All @@ -525,6 +578,8 @@ func (p *Sprite) Hide() {
if debugInstr {
log.Println("Hide", p.name)
}

p.collider.Reset()
p.doStopSay()
p.isVisible = false
}
Expand Down Expand Up @@ -1267,7 +1322,6 @@ func (p *Sprite) Touching(obj interface{}) bool {
switch v := obj.(type) {
case string:
if o := p.g.touchingSpriteBy(p, v); o != nil {
o.fireTouched(p)
return true
}
return false
Expand Down
2 changes: 1 addition & 1 deletion tutorial/05-Animation/Bullet.spx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
onTouched => {
onTouchStart => {
destroy
}

Expand Down
22 changes: 10 additions & 12 deletions tutorial/05-Animation/SmallEnemy.spx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
var life int
onStart => {
for {
wait 0.3
Expand All @@ -6,6 +7,7 @@ onStart => {
}

onCloned => {
life = 3
setXYpos rand(-131, 131), 237
show
for {
Expand All @@ -17,15 +19,11 @@ onCloned => {
}
}

onCloned => {
life := 3
for {
wait 0.05
if touching("Bullet") {
life--
if life == 0 {
die
}
}
}
}
onTouchStart "Bullet", => {
if life > 0 {
life --
if life <= 0 {
die
}
}
}
Loading