-
Notifications
You must be signed in to change notification settings - Fork 84
/
onCollision.go
91 lines (76 loc) · 2.31 KB
/
onCollision.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package collision
import (
"errors"
"github.com/oakmound/oak/v3/event"
)
// A Phase is a struct that other structs who want to use PhaseCollision
// should be composed of
type Phase struct {
OnCollisionS *Space
tree *Tree
bus event.Handler
// If allocating maps becomes an issue
// we can have two constant maps that we
// switch between on alternating frames
Touching map[Label]bool
}
func (cp *Phase) getCollisionPhase() *Phase {
return cp
}
func (cp *Phase) CID() event.CallerID {
return cp.OnCollisionS.CID
}
type collisionPhase interface {
getCollisionPhase() *Phase
}
// PhaseCollision binds to the entity behind the space's CID so that it will
// receive CollisionStart and CollisionStop events, appropriately when
// entities begin to collide or stop colliding with the space.
// If tree is nil, it uses DefTree
func PhaseCollision(s *Space, tree *Tree) error {
return PhaseCollisionWithBus(s, tree, event.DefaultBus)
}
// PhaseCollisionWithBus allows for a non-default bus in a phase collision binding.
func PhaseCollisionWithBus(s *Space, tree *Tree, bus event.Handler) error {
en := bus.GetCallerMap().GetEntity(s.CID)
if cp, ok := en.(collisionPhase); ok {
oc := cp.getCollisionPhase()
oc.OnCollisionS = s
oc.tree = tree
oc.bus = bus
if oc.tree == nil {
oc.tree = DefaultTree
}
bus.UnsafeBind(event.Enter.UnsafeEventID, s.CID, phaseCollisionEnter)
return nil
}
return errors.New("This space's entity does not implement collisionPhase")
}
// CollisionStart/Stop: when a PhaseCollision entity starts/stops touching some label.
var (
Start = event.RegisterEvent[Label]()
Stop = event.RegisterEvent[Label]()
)
func phaseCollisionEnter(id event.CallerID, handler event.Handler, _ interface{}) event.Response {
e := handler.GetCallerMap().GetEntity(id).(collisionPhase)
oc := e.getCollisionPhase()
// check hits
hits := oc.tree.Hits(oc.OnCollisionS)
newTouching := map[Label]bool{}
// if any are new, trigger on collision
for _, h := range hits {
l := h.Label
if _, ok := oc.Touching[l]; !ok {
event.TriggerForCallerOn(oc.bus, id, Start, l)
}
newTouching[l] = true
}
// if we lost any, trigger off collision
for l := range oc.Touching {
if _, ok := newTouching[l]; !ok {
event.TriggerForCallerOn(handler, id, Stop, l)
}
}
oc.Touching = newTouching
return 0
}