This commit is contained in:
Josh Deprez 2021-09-21 15:17:59 +10:00
parent 787a05e493
commit 9a9dcd0d40
4 changed files with 42 additions and 10 deletions

View file

@ -56,7 +56,7 @@ func (d *DrawDAG) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
} }
// Draw everything in d.dag, where not hidden (itself or any parent) // Draw everything in d.dag, where not hidden (itself or any parent)
// TODO: handle descendant DrawLayers // TODO: handle descendant DrawLayers
d.dag.topIterate(func(x Drawer) { d.dag.topWalk(func(x Drawer) {
// Is d hidden itself? // Is d hidden itself?
if h, ok := x.(Hider); ok && h.Hidden() { if h, ok := x.(Hider); ok && h.Hidden() {
cache[x] = state{hidden: true} cache[x] = state{hidden: true}
@ -351,10 +351,10 @@ func (d *dag) removeVertex(v Drawer) {
delete(d.all, v) delete(d.all, v)
} }
// topIterate visits each vertex in topological order, in time O(|V| + |E|) and // topWalk visits each vertex in topological order, in time O(|V| + |E|) and
// O(|V|) temporary memory. // O(|V|) temporary memory.
func (d *dag) topIterate(visit func(Drawer)) { func (d *dag) topWalk(visit func(Drawer)) {
// Count indegrees - indegree(v) = len(d.in[v]) for each v. // Count indegrees - indegree(v) = len(d.in[v]) for each vertex v.
// If indegree(v) = 0, enqueue. Total: O(|V|). // If indegree(v) = 0, enqueue. Total: O(|V|).
queue := make([]Drawer, 0, len(d.in)) queue := make([]Drawer, 0, len(d.in))
indegree := make(map[Drawer]int) indegree := make(map[Drawer]int)

View file

@ -145,6 +145,32 @@ func (g *Game) Parent(c interface{}) interface{} {
return g.par[c] return g.par[c]
} }
// WalkUp visits the component, its parent, its parent, ..., and then g.
func (g *Game) WalkUp(component interface{}, visit func(interface{}) error) error {
for p := component; p != nil; p = g.Parent(p) {
if err := visit(p); err != nil {
return err
}
}
return nil
}
// WalkDown visits g, the subcomponent of g, ..., and then the component.
func (g *Game) WalkDown(component interface{}, visit func(interface{}) error) error {
var stack []interface{}
g.dbmu.RLock()
for p := component; p != nil; p = g.Parent(p) {
stack = append(stack, p)
}
g.dbmu.RUnlock()
for _, p := range stack {
if err := visit(p); err != nil {
return err
}
}
return nil
}
// 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 after LoadAndPrepare. Note that every component is its own ancestor.

View file

@ -126,12 +126,13 @@ func (aw *Awakeman) realUpdate() error {
return err return err
} }
par := aw.game.Parent(aw) par := aw.game.Parent(aw)
for p := interface{}(aw); p != nil; p = aw.game.Parent(p) { if err := aw.game.WalkDown(par, func(c interface{}) error {
if r, ok := p.(engine.Registrar); ok { if r, ok := c.(engine.Registrar); ok {
if err := r.Register(bubble, par); err != nil { return r.Register(bubble, par)
return err
}
} }
return nil
}); err != nil {
return err
} }
if err := engine.PreorderWalk(bubble, func(c, _ interface{}) error { if err := engine.PreorderWalk(bubble, func(c, _ interface{}) error {
if p, ok := c.(engine.Prepper); ok { if p, ok := c.(engine.Prepper); ok {

View file

@ -71,7 +71,12 @@ func (b *Bubble) Prepare(g *engine.Game) error {
func (b *Bubble) Update() error { func (b *Bubble) Update() error {
b.Life-- b.Life--
if b.Life <= 0 { if b.Life <= 0 {
b.game.Unregister(b) if err := b.game.WalkUp(b, func(c interface{}) error {
b.game.Unregister(b)
return nil
}); err != nil {
return err
}
} }
if false { // not using MoveX/MoveY/... because collisions are unnecessary - if false { // not using MoveX/MoveY/... because collisions are unnecessary -
// this is an effect particle, if it overlaps a solid, who cares // this is an effect particle, if it overlaps a solid, who cares