goplus / spx

spx - A Scratch Compatible Go/Go+ 2D Game Engine for STEM education
https://builder.goplus.org
Apache License 2.0
97 stars 24 forks source link

Strange behavior of `OnTouched` #298

Open nighca opened 2 weeks ago

nighca commented 2 weeks ago

For sprite A with code:

// sprite A
onTouched => {
    println "onTouched"
}

The callback will not be called when A collides with another sprite (referred as B).

The callback is called when A collides with B, and method Touching of B is called with A, for example:

// sprite B
onMoving => {
    if touching("A") {
        // do whatever here
    }
}

It's strange, but may be intended. @xushiwei can you offer any hint?

Related PR: https://github.com/goplus/spx/pull/68

P.S. There is no similar API in Scratch

xushiwei commented 2 weeks ago

The background of onTouched event is that the two sprites A and B need to have the same understanding of the world: for example, A is a bullet and B is a monster. When A touches B, A will disappear and B will lose health. We want to avoid the situation where A disappears but B does not lose health, or B loses a lot of health (repeatedly checking that it was hit by a bullet). To this end, we let B receive the onTouched event when A detects that it is touching B.

nighca commented 2 weeks ago

Consider details about the process of "touching" between A & B, it can be devided into several stages:

  1. The first time the engine found that there is an overlap between A & B, referred as touchstart
  2. There is a period, in which A & B keep the overlap, refered as touching
  3. The first time, afterwards, the engine found that the overlap disappears, referred as touchend

If we define event touching, which is expected to be fired repeatedly in the 2nd stage, it causes problems like

B loses a lot of health (repeatedly checking that it was hit by a bullet)

But if we provide event touchstart for such case, the problems can be avoided.

Actually in my opinion, in most cases developers care about touchstart more than touching or touchend, because most of the effect of touching / collision should take place when the touching / collision starts.

For the other problem:

A disappears but B does not lose health

It should be considered an implementation bug of the engine. Whatever the event is (touchstart, touching, touchend, etc.), when the event is detected by the engine, it should be the engine's responsibility to ensure that the event is broadcasted to all attendees. When A touches B, B should always be notified of that, even when A is notifed first and destroyed itself.