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)
// TODO: handle descendant DrawLayers
d.dag.topIterate(func(x Drawer) {
d.dag.topWalk(func(x Drawer) {
// Is d hidden itself?
if h, ok := x.(Hider); ok && h.Hidden() {
cache[x] = state{hidden: true}
@ -351,10 +351,10 @@ func (d *dag) removeVertex(v Drawer) {
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.
func (d *dag) topIterate(visit func(Drawer)) {
// Count indegrees - indegree(v) = len(d.in[v]) for each v.
func (d *dag) topWalk(visit func(Drawer)) {
// Count indegrees - indegree(v) = len(d.in[v]) for each vertex v.
// If indegree(v) = 0, enqueue. Total: O(|V|).
queue := make([]Drawer, 0, len(d.in))
indegree := make(map[Drawer]int)

View file

@ -145,6 +145,32 @@ func (g *Game) Parent(c interface{}) interface{} {
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
// a given behaviour (see Behaviors in interface.go). This only returns sensible
// values after LoadAndPrepare. Note that every component is its own ancestor.

View file

@ -126,12 +126,13 @@ func (aw *Awakeman) realUpdate() error {
return err
}
par := aw.game.Parent(aw)
for p := interface{}(aw); p != nil; p = aw.game.Parent(p) {
if r, ok := p.(engine.Registrar); ok {
if err := r.Register(bubble, par); err != nil {
return err
}
if err := aw.game.WalkDown(par, func(c interface{}) error {
if r, ok := c.(engine.Registrar); ok {
return r.Register(bubble, par)
}
return nil
}); err != nil {
return err
}
if err := engine.PreorderWalk(bubble, func(c, _ interface{}) error {
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 {
b.Life--
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 -
// this is an effect particle, if it overlaps a solid, who cares