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.
var st state
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 {
st = s
break
@ -236,6 +236,8 @@ func (d *DrawDAG) unregisterOne(x DrawBoxer) {
}
// Remove from reverse chunk map
delete(d.chunksRev, x)
// Remove from box cache
delete(d.boxCache, x)
// Remove from DAG
d.dag.removeVertex(x)
}

View file

@ -65,63 +65,29 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
// Update updates everything.
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
}
// Need to do a similar trick for Draw: disabling a parent object should
// disable the child objects.
// cache memoises the disabled state for each component.
cache := map[interface{}]bool{
g: false,
if sc, ok := c.(Scanner); ok {
for _, x := range sc.Scan() {
if err := g.updateRecursive(x); err != nil {
return err
}
}
}
// 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 {
return nil
}
u, ok := c.(Updater)
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
if c == g {
return nil
}
if u, ok := c.(Updater); ok {
return u.Update()
})
}
return nil
}
// Ident returns "__GAME__".
@ -145,30 +111,31 @@ 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
}
func reverse(s []interface{}) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return nil
}
// WalkDown visits g, the subcomponent of g, ..., and then the component.
func (g *Game) WalkDown(component interface{}, visit func(interface{}) error) error {
func (g *Game) Path(component interface{}) []interface{} {
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
}
reverse(stack)
return stack
}
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

View file

@ -40,7 +40,7 @@ func (h *Hides) Hide() { *h = true }
// Show sets h to 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{}
// Scan returns c.

View file

@ -21,6 +21,7 @@ type scener interface {
Disabler
Hider
Identifier
Registrar
Scanner
}
@ -39,6 +40,21 @@ type Scene struct {
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.
// After Load, Scene is usable.
// 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 {
return err
}
// Add bubble to same parent as 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 {
return r.Register(bubble, par)
if err := r.Register(bubble, par); err != nil {
return err
}
}
return nil
}); err != nil {
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 {
return p.Prepare(aw.game)
}

View file

@ -71,22 +71,24 @@ func (b *Bubble) Prepare(g *engine.Game) error {
func (b *Bubble) Update() error {
b.Life--
if b.Life <= 0 {
if err := b.game.WalkUp(b, func(c interface{}) error {
b.game.Unregister(b)
return nil
}); err != nil {
return err
for _, c := range b.game.ReversePath(b) {
if r, ok := c.(engine.Registrar); ok {
r.Unregister(b)
}
}
}
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(
// --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),
//lint:ignore SA4000 one random minus another is not always zero...
rand.Intn(3)-1, rand.Intn(2)-1, rand.Intn(2)-rand.Intn(2),
))
} else {
b.Sprite.Actor.MoveX(float64(rand.Intn(3)-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
}