Update uses Query, Query is fixed
This commit is contained in:
parent
6792db5d7a
commit
3ad1cf8d4d
3 changed files with 52 additions and 36 deletions
|
@ -44,11 +44,10 @@ func (a *Actor) CollidesAt(p geom.Int3) bool {
|
||||||
bounds := a.Bounds.Add(p)
|
bounds := a.Bounds.Add(p)
|
||||||
cd := a.game.Component(a.CollisionDomain)
|
cd := a.game.Component(a.CollisionDomain)
|
||||||
if cd == nil {
|
if cd == nil {
|
||||||
log.Printf("collision domain %q not found in game", a.CollisionDomain)
|
log.Printf("collision domain %q not found", a.CollisionDomain)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return errCollision == a.game.Query(cd, ColliderType, func(c interface{}) error {
|
return errCollision == a.game.Query(cd, ColliderType, nil, func(c interface{}) error {
|
||||||
log.Printf("checking for collision with %v", c)
|
|
||||||
if c.(Collider).CollidesWith(bounds) {
|
if c.(Collider).CollidesWith(bounds) {
|
||||||
return errCollision
|
return errCollision
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,24 +66,18 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
|
||||||
|
|
||||||
// Update updates everything.
|
// Update updates everything.
|
||||||
func (g *Game) Update() error {
|
func (g *Game) Update() error {
|
||||||
return g.updateRecursive(g)
|
//return g.updateRecursive(g)
|
||||||
}
|
return g.Query(g.Root, UpdaterType,
|
||||||
|
func(c interface{}) error {
|
||||||
// updateRecursive updates everything in a post-order traversal. It terminates
|
|
||||||
// the recursion early if the component reports it is Disabled.
|
|
||||||
func (g *Game) updateRecursive(c interface{}) error {
|
|
||||||
if d, ok := c.(Disabler); ok && d.Disabled() {
|
if d, ok := c.(Disabler); ok && d.Disabled() {
|
||||||
return nil
|
return Skip
|
||||||
}
|
|
||||||
if sc, ok := c.(Scanner); ok {
|
|
||||||
if err := sc.Scan(g.updateRecursive); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if u, ok := c.(Updater); ok && c != g {
|
|
||||||
return u.Update()
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
},
|
||||||
|
func(c interface{}) error {
|
||||||
|
return c.(Updater).Update()
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ident returns "__GAME__".
|
// Ident returns "__GAME__".
|
||||||
|
@ -162,23 +156,37 @@ func (g *Game) ReversePath(component interface{}) []interface{} {
|
||||||
|
|
||||||
// Query looks for components having both a given ancestor and implementing
|
// Query looks for components having both a given ancestor and implementing
|
||||||
// a given behaviour (see Behaviors in interface.go). This only returns sensible
|
// a given behaviour (see Behaviors in interface.go). This only returns sensible
|
||||||
// values after LoadAndPrepare. Note that every component is its own ancestor.
|
// values for registered components.
|
||||||
func (g *Game) Query(ancestor interface{}, behaviour reflect.Type, visit func(interface{}) error) error {
|
//
|
||||||
// NB: per the godoc, do not use RLock for recursive read locking.
|
// Note that every component is its own ancestor.
|
||||||
g.dbmu.RLock()
|
//
|
||||||
defer g.dbmu.RUnlock()
|
// visitPre is visited before descendants, while visitPost is visited after
|
||||||
return g.queryRecursive(ancestor, behaviour, visit)
|
// descendants. nil visitors are ignored.
|
||||||
}
|
func (g *Game) Query(ancestor interface{}, behaviour reflect.Type, visitPre, visitPost func(interface{}) error) error {
|
||||||
|
pi := reflect.TypeOf(ancestor).Implements(behaviour)
|
||||||
// Post-order visit p and descendants of p having behaviour b.
|
if pi && visitPre != nil {
|
||||||
func (g *Game) queryRecursive(p interface{}, b reflect.Type, v func(interface{}) error) error {
|
if err := visitPre(ancestor); err != nil {
|
||||||
for x := range g.byAB[abKey{p, b}] {
|
|
||||||
if err := g.queryRecursive(x, b, v); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if reflect.TypeOf(p).Implements(b) {
|
// * Update uses Query.
|
||||||
return v(p)
|
// * Updaters can Register new components.
|
||||||
|
// * Register acquires g.dbmu.Lock.
|
||||||
|
// ==> Wrapping the whole thing in RLock would deadlock.
|
||||||
|
// Make the read lock as tight as possible.
|
||||||
|
g.dbmu.RLock()
|
||||||
|
q := g.byAB[abKey{ancestor, behaviour}]
|
||||||
|
g.dbmu.RUnlock()
|
||||||
|
for x := range q {
|
||||||
|
if err := g.Query(x, behaviour, visitPre, visitPost); err != nil {
|
||||||
|
if errors.Is(err, Skip) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pi && visitPost != nil {
|
||||||
|
return visitPost(ancestor)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -416,3 +424,12 @@ func concatOpts(a, b ebiten.DrawImageOptions) ebiten.DrawImageOptions {
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip is an "error" value that can be returned from visitor callbacks. It
|
||||||
|
// tells recursive methods of Game to skip processing the current item and its
|
||||||
|
// descendants, but will otherwise continue processing.
|
||||||
|
const Skip = skip("skip")
|
||||||
|
|
||||||
|
type skip string
|
||||||
|
|
||||||
|
func (s skip) Error() string { return string(s) }
|
||||||
|
|
|
@ -151,7 +151,7 @@ func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
||||||
fmt.Fprintf(dst, "%T\n", c)
|
fmt.Fprintf(dst, "%T\n", c)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
}, nil)
|
||||||
if noResults {
|
if noResults {
|
||||||
fmt.Fprintln(dst, "No results")
|
fmt.Fprintln(dst, "No results")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue