bubbles working yay!!!!!!!!! :)

This commit is contained in:
Josh Deprez 2021-09-21 16:53:04 +10:00
parent 9a9dcd0d40
commit 661a6bbbc0
6 changed files with 71 additions and 84 deletions

View file

@ -65,7 +65,7 @@ func (d *DrawDAG) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
// Walk up game tree to find the nearest state in cache. // Walk up game tree to find the nearest state in cache.
var st state var st state
stack := []interface{}{x} stack := []interface{}{x}
for p := d.game.Parent(x); ; p = d.game.Parent(p) { for p := d.game.Parent(x); p != nil; p = d.game.Parent(p) {
if s, found := cache[p]; found { if s, found := cache[p]; found {
st = s st = s
break break
@ -236,6 +236,8 @@ func (d *DrawDAG) unregisterOne(x DrawBoxer) {
} }
// Remove from reverse chunk map // Remove from reverse chunk map
delete(d.chunksRev, x) delete(d.chunksRev, x)
// Remove from box cache
delete(d.boxCache, x)
// Remove from DAG // Remove from DAG
d.dag.removeVertex(x) d.dag.removeVertex(x)
} }

View file

@ -65,63 +65,29 @@ 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 {
if g.Disabled() { return g.updateRecursive(g)
}
// updateRecursive updates everything in a post-order traversal. It terminates recursion
// early if the component reports it is Disabled.
func (g *Game) updateRecursive(c interface{}) error {
if d, ok := c.(Disabler); ok && d.Disabled() {
return nil return nil
} }
if sc, ok := c.(Scanner); ok {
// Need to do a similar trick for Draw: disabling a parent object should for _, x := range sc.Scan() {
// disable the child objects. if err := g.updateRecursive(x); err != nil {
// cache memoises the disabled state for each component. return err
cache := map[interface{}]bool{ }
g: false, }
} }
// Update everything that is not disabled.
return PostorderWalk(g, func(c, _ interface{}) error {
// Skip g (note g satisfies Updater, so this would infinitely recurse)
if c == g { if c == g {
return nil return nil
} }
u, ok := c.(Updater) if u, ok := c.(Updater); ok {
if !ok {
return nil
}
// Is u disabled itself?
if d, ok := u.(Disabler); ok && d.Disabled() {
cache[u] = true
return nil
}
// Walk up g.par to find the nearest state in accum.
var st bool
stack := []interface{}{u}
for p := g.par[u]; ; p = g.par[p] {
if s, found := cache[p]; found {
st = s
break
}
stack = append(stack, p)
}
// Unwind the stack, accumulating state along the way.
for len(stack) > 0 {
l1 := len(stack) - 1
p := stack[l1]
stack = stack[:l1]
if d, ok := p.(Disabler); ok {
st = st || d.Disabled()
}
cache[p] = st
}
// Skip updating if disabled.
if st {
return nil
}
// Update
return u.Update() return u.Update()
}) }
return nil
} }
// Ident returns "__GAME__". // Ident returns "__GAME__".
@ -145,30 +111,31 @@ 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 reverse(s []interface{}) {
func (g *Game) WalkUp(component interface{}, visit func(interface{}) error) error { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
for p := component; p != nil; p = g.Parent(p) { s[i], s[j] = s[j], s[i]
if err := visit(p); err != nil {
return err
} }
} }
return nil
}
// WalkDown visits g, the subcomponent of g, ..., and then the component. func (g *Game) Path(component interface{}) []interface{} {
func (g *Game) WalkDown(component interface{}, visit func(interface{}) error) error {
var stack []interface{} var stack []interface{}
g.dbmu.RLock() g.dbmu.RLock()
for p := component; p != nil; p = g.Parent(p) { for p := component; p != nil; p = g.Parent(p) {
stack = append(stack, p) stack = append(stack, p)
} }
g.dbmu.RUnlock() g.dbmu.RUnlock()
for _, p := range stack { reverse(stack)
if err := visit(p); err != nil { return stack
return err
} }
func (g *Game) ReversePath(component interface{}) []interface{} {
var stack []interface{}
g.dbmu.RLock()
for p := component; p != nil; p = g.Parent(p) {
stack = append(stack, p)
} }
return nil g.dbmu.RUnlock()
return stack
} }
// Query looks for components having both a given ancestor and implementing // Query looks for components having both a given ancestor and implementing

View file

@ -40,7 +40,7 @@ func (h *Hides) Hide() { *h = true }
// Show sets h to false. // Show sets h to false.
func (h *Hides) Show() { *h = false } func (h *Hides) Show() { *h = false }
// Components implements Scan directly (as a slice of interface{}). // Components implements Scan directly (as itself!)
type Components []interface{} type Components []interface{}
// Scan returns c. // Scan returns c.

View file

@ -21,6 +21,7 @@ type scener interface {
Disabler Disabler
Hider Hider
Identifier Identifier
Registrar
Scanner Scanner
} }
@ -39,6 +40,21 @@ type Scene struct {
Hides Hides
} }
func (s *Scene) Register(component, parent interface{}) error {
if parent == s {
s.Components = append(s.Components, component)
}
return nil
}
func (s *Scene) Unregister(component interface{}) {
for i, c := range s.Components {
if c == component {
s.Components[i] = nil
}
}
}
// SceneRef loads a gzipped, gob-encoded Scene from the asset FS. // SceneRef loads a gzipped, gob-encoded Scene from the asset FS.
// After Load, Scene is usable. // After Load, Scene is usable.
// This is mostly useful for scenes that refer to other scenes, e.g. // This is mostly useful for scenes that refer to other scenes, e.g.

View file

@ -125,16 +125,16 @@ func (aw *Awakeman) realUpdate() error {
}); err != nil { }); err != nil {
return err return err
} }
// Add bubble to same parent as aw
par := aw.game.Parent(aw) par := aw.game.Parent(aw)
if err := aw.game.WalkDown(par, func(c interface{}) error { for _, c := range aw.game.Path(par) {
if r, ok := c.(engine.Registrar); ok { if r, ok := c.(engine.Registrar); ok {
return r.Register(bubble, par) if err := r.Register(bubble, par); err != nil {
}
return nil
}); err != nil {
return err return err
} }
if err := engine.PreorderWalk(bubble, func(c, _ interface{}) error { }
}
if err := engine.PostorderWalk(bubble, func(c, _ interface{}) error {
if p, ok := c.(engine.Prepper); ok { if p, ok := c.(engine.Prepper); ok {
return p.Prepare(aw.game) return p.Prepare(aw.game)
} }

View file

@ -71,22 +71,24 @@ 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 {
if err := b.game.WalkUp(b, func(c interface{}) error { for _, c := range b.game.ReversePath(b) {
b.game.Unregister(b) if r, ok := c.(engine.Registrar); ok {
return nil r.Unregister(b)
}); 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 if false {
// not using MoveX/MoveY/... because collisions are unnecessary -
// this is an effect particle; if it overlaps a solid, who cares
b.Sprite.Actor.Pos = b.Sprite.Actor.Pos.Add(geom.Pt3( b.Sprite.Actor.Pos = b.Sprite.Actor.Pos.Add(geom.Pt3(
// --lint:ignore SA4000 one random minus another is not always zero... //lint:ignore SA4000 one random minus another is not always zero...
rand.Intn(3)-1, rand.Intn(2)-1, 0, // rand.Intn(2)-rand.Intn(2), rand.Intn(3)-1, rand.Intn(2)-1, rand.Intn(2)-rand.Intn(2),
)) ))
} else { } else {
b.Sprite.Actor.MoveX(float64(rand.Intn(3)-1), nil) b.Sprite.Actor.MoveX(float64(rand.Intn(3)-1), nil)
b.Sprite.Actor.MoveY(float64(rand.Intn(2)-1), nil) b.Sprite.Actor.MoveY(float64(rand.Intn(2)-1), nil)
//lint:ignore SA4000 one random minus another is not always zero...
b.Sprite.Actor.MoveZ(float64(rand.Intn(2)-rand.Intn(2)), nil)
} }
return nil return nil
} }