Query now visits everything on the path
This is used fixes some bugs related to parent behaviours not correctly affecting descendants where the parent component does not have the behaviour being queried.
This commit is contained in:
parent
d28762468f
commit
463115efcc
5 changed files with 70 additions and 42 deletions
|
@ -64,7 +64,7 @@ func (a *Actor) CollidesAt(p geom.Int3) bool {
|
|||
return false
|
||||
}
|
||||
return errCollision == a.game.Query(cd, ColliderType, nil, func(c interface{}) error {
|
||||
if c.(Collider).CollidesWith(bounds) {
|
||||
if cl, ok := c.(Collider); ok && cl.CollidesWith(bounds) {
|
||||
return errCollision
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -170,7 +170,9 @@ func (d *DrawDAG) Update() error {
|
|||
// descendants of a different DrawManager.
|
||||
func (d *DrawDAG) Register(component, _ interface{}) error {
|
||||
return d.game.Query(component, DrawBoxerType, func(c interface{}) error {
|
||||
d.registerOne(c.(DrawBoxer))
|
||||
if db, ok := c.(DrawBoxer); ok {
|
||||
d.registerOne(db)
|
||||
}
|
||||
if _, isDM := c.(DrawManager); isDM && c != d {
|
||||
return Skip
|
||||
}
|
||||
|
@ -235,7 +237,9 @@ func (d *DrawDAG) registerOne(x DrawBoxer) {
|
|||
// Unregister unregisters the component and all subcomponents.
|
||||
func (d *DrawDAG) Unregister(component interface{}) {
|
||||
d.game.Query(component, DrawBoxerType, func(c interface{}) error {
|
||||
d.unregisterOne(c.(DrawBoxer))
|
||||
if db, ok := c.(DrawBoxer); ok {
|
||||
d.unregisterOne(db)
|
||||
}
|
||||
if _, isDM := c.(DrawManager); isDM && c != d {
|
||||
return Skip
|
||||
}
|
||||
|
|
|
@ -46,39 +46,43 @@ type DrawDFS struct {
|
|||
}
|
||||
|
||||
func (d *DrawDFS) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
|
||||
d.drawRecursive(screen, *opts, d)
|
||||
stack := []ebiten.DrawImageOptions{*opts}
|
||||
d.game.Query(d, DrawerType,
|
||||
// visitPre
|
||||
func(x interface{}) error {
|
||||
if h, ok := x.(Hider); ok && h.Hidden() {
|
||||
return Skip
|
||||
}
|
||||
opts := stack[len(stack)-1]
|
||||
if tf, ok := x.(Transformer); ok {
|
||||
opts = concatOpts(tf.Transform(), opts)
|
||||
stack = append(stack, opts)
|
||||
}
|
||||
if x == d { // neither draw nor skip d itself
|
||||
return nil
|
||||
}
|
||||
if dr, ok := x.(Drawer); ok {
|
||||
dr.Draw(screen, &opts)
|
||||
}
|
||||
if _, isDM := x.(DrawManager); isDM {
|
||||
return Skip
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// visitPost
|
||||
func(x interface{}) error {
|
||||
if _, ok := x.(Transformer); ok {
|
||||
stack = stack[:len(stack)-1]
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// ManagesDrawingSubcomponents is present so DrawDFS is recognised as a
|
||||
// DrawManager.
|
||||
func (DrawDFS) ManagesDrawingSubcomponents() {}
|
||||
|
||||
func (d *DrawDFS) drawRecursive(screen *ebiten.Image, opts ebiten.DrawImageOptions, component interface{}) {
|
||||
// Hidden? stop drawing
|
||||
if h, ok := component.(Hider); ok && h.Hidden() {
|
||||
return
|
||||
}
|
||||
// Has a transform? apply to opts
|
||||
if tf, ok := component.(Transformer); ok {
|
||||
opts = concatOpts(tf.Transform(), opts)
|
||||
}
|
||||
if component != d {
|
||||
// Does it draw itself? Draw
|
||||
if dr, ok := component.(Drawer); ok {
|
||||
dr.Draw(screen, &opts)
|
||||
}
|
||||
// Is it a DrawManager? It manages drawing all its subcomponents.
|
||||
if _, ok := component.(DrawManager); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Has subcomponents? recurse
|
||||
d.game.Children(component).Scan(func(x interface{}) error {
|
||||
d.drawRecursive(screen, opts, x)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DrawDFS) Prepare(g *Game) error {
|
||||
d.game = g
|
||||
return nil
|
||||
|
|
|
@ -84,12 +84,16 @@ func (g *Game) Update() error {
|
|||
return g.Query(g.Root, UpdaterType,
|
||||
func(c interface{}) error {
|
||||
if d, ok := c.(Disabler); ok && d.Disabled() {
|
||||
// Do not update this component or descendants.
|
||||
return Skip
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(c interface{}) error {
|
||||
return c.(Updater).Update()
|
||||
if u, ok := c.(Updater); ok {
|
||||
return u.Update()
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -167,21 +171,29 @@ func (g *Game) ReversePath(component interface{}) []interface{} {
|
|||
return stack
|
||||
}
|
||||
|
||||
// Query looks for components having both a given ancestor and implementing
|
||||
// a given behaviour (see Behaviors in interface.go). This only returns sensible
|
||||
// values for registered components.
|
||||
// Query recursively searches for components having both a given ancestor and
|
||||
// implementing a given behaviour (see Behaviors in interface.go).
|
||||
// visitPre is called before descendants are visited, while visitPost is called
|
||||
// after descendants are visited. nil visitPre/visitPost are ignored.
|
||||
//
|
||||
// Note that every component is its own ancestor.
|
||||
// It is up to the visitPre and visitPost callbacks to handle components that
|
||||
// do not themselves implement the behaviour - more specifically, every ancestor
|
||||
// (up to the given one) of each component with the behaviour will be visited.
|
||||
// Visiting components in the tree that *don't* implement the behaviour is
|
||||
// important when behaviours of the parent need to influence the behaviours of
|
||||
// the children (e.g. a component can be a Hider and hiding all descendants, but
|
||||
// not necessarily be a Drawer itself).
|
||||
//
|
||||
// visitPre is visited before descendants, while visitPost is visited after
|
||||
// descendants. nil visitors are ignored.
|
||||
// Query only visits components that are registered.
|
||||
//
|
||||
// Note that every component is an ancestor of itself.
|
||||
//
|
||||
// Query returns the first error returned from either visitor callback, except
|
||||
// Skip when it is returned from a recursive call. Returning Skip from visitPre
|
||||
// will cause the descendents of the component to not be visited.
|
||||
// will cause the descendants of the component to be skipped (see the
|
||||
// implementation of Update for an example).
|
||||
func (g *Game) Query(ancestor interface{}, behaviour reflect.Type, visitPre, visitPost VisitFunc) error {
|
||||
pi := reflect.TypeOf(ancestor).Implements(behaviour)
|
||||
if pi && visitPre != nil {
|
||||
if visitPre != nil {
|
||||
if err := visitPre(ancestor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -205,7 +217,7 @@ func (g *Game) Query(ancestor interface{}, behaviour reflect.Type, visitPre, vis
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if pi && visitPost != nil {
|
||||
if visitPost != nil {
|
||||
return visitPost(ancestor)
|
||||
}
|
||||
return nil
|
||||
|
@ -219,6 +231,8 @@ func (g *Game) Scan(visit VisitFunc) error {
|
|||
// Load loads a component and all subcomponents recursively.
|
||||
// Note that this method does not implement Loader itself.
|
||||
func (g *Game) Load(component interface{}, assets fs.FS) error {
|
||||
// Query cannot be used for this method because Load might cause
|
||||
// subcomponents to spring into existence.
|
||||
if l, ok := component.(Loader); ok {
|
||||
if err := l.Load(assets); err != nil {
|
||||
return err
|
||||
|
@ -238,7 +252,10 @@ func (g *Game) Prepare(component interface{}) error {
|
|||
// Postorder traversal, in case ancestors depend on descendants being
|
||||
// ready to answer queries.
|
||||
return g.Query(component, PrepperType, nil, func(c interface{}) error {
|
||||
return c.(Prepper).Prepare(g)
|
||||
if p, ok := c.(Prepper); ok {
|
||||
return p.Prepare(g)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -157,6 +157,9 @@ func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
|||
|
||||
noResults := true
|
||||
g.Query(ancestor, behaviour, func(c interface{}) error {
|
||||
if !reflect.TypeOf(c).Implements(behaviour) {
|
||||
return nil
|
||||
}
|
||||
noResults = false
|
||||
i, ok := c.(Identifier)
|
||||
if ok {
|
||||
|
|
Loading…
Reference in a new issue