WIP: Scan now uses visitor pattern

This commit is contained in:
Josh Deprez 2021-09-22 15:48:02 +10:00
parent 83c3182be1
commit fe2558b2c1
16 changed files with 143 additions and 28 deletions

View file

@ -52,7 +52,10 @@ func (b *Billboard) Prepare(g *Game) error {
} }
// Scan returns a slice containing Src. // Scan returns a slice containing Src.
func (b *Billboard) Scan() []interface{} { return []interface{}{&b.Src} } //func (b *Billboard) Scan() []interface{} { return []interface{}{&b.Src} }
func (b *Billboard) Scan(visit func(interface{}) error) error {
return visit(&b.Src)
}
func (b *Billboard) String() string { func (b *Billboard) String() string {
return fmt.Sprintf("Billboard@%v", b.Pos) return fmt.Sprintf("Billboard@%v", b.Pos)

View file

@ -86,7 +86,10 @@ func (c *Camera) Prepare(game *Game) error {
} }
// Scan returns s.Child. // Scan returns s.Child.
func (c *Camera) Scan() []interface{} { return []interface{}{c.Child} } //func (c *Camera) Scan() []interface{} { return []interface{}{c.Child} }
func (c *Camera) Scan(visit func(interface{}) error) error {
return visit(c.Child)
}
// Transform returns the camera transform. // Transform returns the camera transform.
func (c *Camera) Transform() (opts ebiten.DrawImageOptions) { func (c *Camera) Transform() (opts ebiten.DrawImageOptions) {

View file

@ -1,10 +1,23 @@
package engine package engine
var _ interface {
Registrar
Scanner
} = &Container{}
// Container contains many components. // Container contains many components.
type Container []interface{} type Container []interface{}
// Scan returns c. // Scan returns c.
func (c Container) Scan() []interface{} { return c } //func (c Container) Scan() []interface{} { return c }
func (c Container) Scan(visit func(interface{}) error) error {
for _, x := range c {
if err := visit(x); err != nil {
return err
}
}
return nil
}
// Register records component in the slice, if parent is the container. // Register records component in the slice, if parent is the container.
func (c *Container) Register(component, parent interface{}) error { func (c *Container) Register(component, parent interface{}) error {

View file

@ -114,7 +114,10 @@ func (d *DrawDAG) Prepare(game *Game) error {
return d.Register(d, nil) return d.Register(d, nil)
} }
func (d *DrawDAG) Scan() []interface{} { return []interface{}{d.Child} } //func (d *DrawDAG) Scan() []interface{} { return []interface{}{d.Child} }
func (d *DrawDAG) Scan(visit func(interface{}) error) error {
return visit(d.Child)
}
// Update checks for any changes to descendants, and updates its internal // Update checks for any changes to descendants, and updates its internal
// data structures accordingly. // data structures accordingly.
@ -160,11 +163,15 @@ func (d *DrawDAG) Register(component, _ interface{}) error {
return nil return nil
} }
if sc, ok := component.(Scanner); ok { if sc, ok := component.(Scanner); ok {
for _, x := range sc.Scan() { /*for _, x := range sc.Scan() {
if err := d.Register(x, nil); err != nil { if err := d.Register(x, nil); err != nil {
return err return err
} }
}*/
scv := func(x interface{}) error {
return d.Register(x, nil)
} }
return sc.Scan(scv)
} }
return nil return nil
} }
@ -232,9 +239,14 @@ func (d *DrawDAG) Unregister(component interface{}) {
return return
} }
if sc, ok := component.(Scanner); ok { if sc, ok := component.(Scanner); ok {
for _, x := range sc.Scan() { /*for _, x := range sc.Scan() {
d.Unregister(x) d.Unregister(x)
}*/
scv := func(x interface{}) error {
d.Unregister(x)
return nil
} }
sc.Scan(scv)
} }
} }

View file

@ -46,10 +46,18 @@ func (d *DrawDFS) draw(component interface{}, screen *ebiten.Image, opts ebiten.
} }
// Has subcomponents? recurse // Has subcomponents? recurse
if sc, ok := component.(Scanner); ok { if sc, ok := component.(Scanner); ok {
for _, ch := range sc.Scan() { /*for _, ch := range sc.Scan() {
d.draw(ch, screen, opts) d.draw(ch, screen, opts)
}*/
scv := func(x interface{}) error {
d.draw(x, screen, opts)
return nil
} }
sc.Scan(scv)
} }
} }
func (d *DrawDFS) Scan() []interface{} { return []interface{}{d.Child} } //func (d *DrawDFS) Scan() []interface{} { return []interface{}{d.Child} }
func (d *DrawDFS) Scan(visit func(interface{}) error) error {
return visit(d.Child)
}

View file

@ -75,11 +75,15 @@ func (g *Game) updateRecursive(c interface{}) error {
return nil return nil
} }
if sc, ok := c.(Scanner); ok { if sc, ok := c.(Scanner); ok {
for _, x := range sc.Scan() { /*for _, x := range sc.Scan() {
if err := g.updateRecursive(x); err != nil { if err := g.updateRecursive(x); err != nil {
return err return err
} }
} }
*/
if err := sc.Scan(g.updateRecursive); err != nil {
return err
}
} }
if c == g { // prevent infinite recursion if c == g { // prevent infinite recursion
return nil return nil
@ -167,7 +171,10 @@ func (g *Game) Query(ancestor interface{}, behaviour reflect.Type) map[interface
} }
// Scan returns g.Root. // Scan returns g.Root.
func (g *Game) Scan() []interface{} { return []interface{}{g.Root} } //func (g *Game) Scan() []interface{} { return []interface{}{g.Root} }
func (g *Game) Scan(visit func(interface{}) error) error {
return visit(g.Root)
}
// PreorderWalk calls visit with every component and its parent, reachable from // PreorderWalk calls visit with every component and its parent, reachable from
// the given component via Scan, for as long as visit returns nil. The parent // the given component via Scan, for as long as visit returns nil. The parent
@ -185,12 +192,15 @@ func preorderWalk(component, parent interface{}, visit func(component, parent in
if !ok { if !ok {
return nil return nil
} }
for _, c := range sc.Scan() { /*for _, c := range sc.Scan() {
if err := preorderWalk(c, component, visit); err != nil { if err := preorderWalk(c, component, visit); err != nil {
return err return err
} }
}*/
scv := func(c interface{}) error {
return preorderWalk(c, component, visit)
} }
return nil return sc.Scan(scv)
} }
// PostorderWalk calls visit with every component and its parent, reachable from // PostorderWalk calls visit with every component and its parent, reachable from
@ -203,10 +213,16 @@ func PostorderWalk(component interface{}, visit func(component, parent interface
func postorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error { func postorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error {
if sc, ok := component.(Scanner); ok { if sc, ok := component.(Scanner); ok {
for _, c := range sc.Scan() { scv := func(c interface{}) error {
return postorderWalk(c, component, visit)
}
/*for _, c := range sc.Scan() {
if err := postorderWalk(c, component, visit); err != nil { if err := postorderWalk(c, component, visit); err != nil {
return err return err
} }
}*/
if err := sc.Scan(scv); err != nil {
return err
} }
} }
return visit(component, parent) return visit(component, parent)

View file

@ -144,10 +144,12 @@ type Saver interface {
} }
// Scanner components can be scanned. It is called when the game tree is walked // Scanner components can be scanned. It is called when the game tree is walked
// (such as when the game component database is constructed). // (such as when the game component database is constructed) and various times
// Scan should return a slice containing all immediate subcomponents. // afterward (such as during Update).
// Scan should visit each immediate subcomponent, aborting early if an error is
// returned from the visitor.
type Scanner interface { type Scanner interface {
Scan() []interface{} Scan(visit func(interface{}) error) error
} }
// Transformer components can provide draw options to apply to themselves and // Transformer components can provide draw options to apply to themselves and

View file

@ -39,7 +39,10 @@ func (p *Parallax) Prepare(game *Game) error {
} }
// Scan returns the child component. // Scan returns the child component.
func (p *Parallax) Scan() []interface{} { return []interface{}{p.Child} } //func (p *Parallax) Scan() []interface{} { return []interface{}{p.Child} }
func (p *Parallax) Scan(visit func(interface{}) error) error {
return visit(p.Child)
}
// Transform returns a GeoM translation of Factor * camera.Centre. // Transform returns a GeoM translation of Factor * camera.Centre.
func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) { func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) {

View file

@ -113,13 +113,24 @@ func (m *PrismMap) Prepare(g *Game) error {
} }
// Scan returns the Sheet and all the Prisms. // Scan returns the Sheet and all the Prisms.
func (m *PrismMap) Scan() []interface{} { /*func (m *PrismMap) Scan() []interface{} {
c := make([]interface{}, 1, len(m.Map)+1) c := make([]interface{}, 1, len(m.Map)+1)
c[0] = &m.Sheet c[0] = &m.Sheet
for _, prism := range m.Map { for _, prism := range m.Map {
c = append(c, prism) c = append(c, prism)
} }
return c return c
}*/
func (m *PrismMap) Scan(visit func(interface{}) error) error {
if err := visit(&m.Sheet); err != nil {
return err
}
for _, prism := range m.Map {
if err := visit(prism); err != nil {
return err
}
}
return nil
} }
// Transform retrurns a translation by the draw offset. // Transform retrurns a translation by the draw offset.

View file

@ -38,7 +38,10 @@ type Scene struct {
Hides Hides
} }
func (s *Scene) Scan() []interface{} { return []interface{}{s.Child} } //func (s *Scene) Scan() []interface{} { return []interface{}{s.Child} }
func (s *Scene) Scan(visit func(interface{}) error) error {
return visit(s.Child)
}
// 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.

View file

@ -47,7 +47,10 @@ func (s *Sheet) Prepare(*Game) error {
} }
// Scan returns the Src. // Scan returns the Src.
func (s *Sheet) Scan() []interface{} { return []interface{}{&s.Src} } //func (s *Sheet) Scan() []interface{} { return []interface{}{&s.Src} }
func (s *Sheet) Scan(visit func(interface{}) error) error {
return visit(&s.Src)
}
// SubImage returns an *ebiten.Image corresponding to the given cell index. // SubImage returns an *ebiten.Image corresponding to the given cell index.
func (s *Sheet) SubImage(i int) *ebiten.Image { func (s *Sheet) SubImage(i int) *ebiten.Image {

View file

@ -41,11 +41,17 @@ func (s *Sprite) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
} }
// Scan returns the Actor and the Sheet. // Scan returns the Actor and the Sheet.
func (s *Sprite) Scan() []interface{} { /*func (s *Sprite) Scan() []interface{} {
return []interface{}{ return []interface{}{
&s.Actor, &s.Actor,
&s.Sheet, &s.Sheet,
} }
}*/
func (s *Sprite) Scan(visit func(interface{}) error) error {
if err := visit(&s.Actor); err != nil {
return err
}
return visit(&s.Sheet)
} }
// SetAnim sets the Anim to use for the sprite. If it is not the same as the // SetAnim sets the Anim to use for the sprite. If it is not the same as the

View file

@ -100,13 +100,24 @@ func (t *Tilemap) Load(fs.FS) error {
} }
// Scan returns a slice containing Src and all non-nil tiles. // Scan returns a slice containing Src and all non-nil tiles.
func (t *Tilemap) Scan() []interface{} { /*func (t *Tilemap) Scan() []interface{} {
c := make([]interface{}, 1, len(t.Map)+1) c := make([]interface{}, 1, len(t.Map)+1)
c[0] = &t.Sheet c[0] = &t.Sheet
for _, tile := range t.Map { for _, tile := range t.Map {
c = append(c, tile) c = append(c, tile)
} }
return c return c
}*/
func (t *Tilemap) Scan(visit func(interface{}) error) error {
if err := visit(&t.Sheet); err != nil {
return err
}
for _, tile := range t.Map {
if err := visit(tile); err != nil {
return err
}
}
return nil
} }
// Transform returns a translation by t.Offset. // Transform returns a translation by t.Offset.
@ -152,4 +163,7 @@ type AnimatedTile struct {
func (a *AnimatedTile) Cell() int { return a.anim.Cell() } func (a *AnimatedTile) Cell() int { return a.anim.Cell() }
// Scan returns a.anim. // Scan returns a.anim.
func (a *AnimatedTile) Scan() []interface{} { return []interface{}{a.anim} } //func (a *AnimatedTile) Scan() []interface{} { return []interface{}{a.anim} }
func (a *AnimatedTile) Scan(visit func(interface{}) error) error {
return visit(a.anim)
}

View file

@ -61,6 +61,7 @@ func (w *Wall) CollidesWith(b geom.Box) bool {
} }
// Scan returns the Sheet and all WallUnits. // Scan returns the Sheet and all WallUnits.
/*
func (w *Wall) Scan() []interface{} { func (w *Wall) Scan() []interface{} {
c := make([]interface{}, 1, len(w.Units)+1) c := make([]interface{}, 1, len(w.Units)+1)
c[0] = &w.Sheet c[0] = &w.Sheet
@ -69,6 +70,18 @@ func (w *Wall) Scan() []interface{} {
} }
return c return c
} }
*/
func (w *Wall) Scan(visit func(interface{}) error) error {
if err := visit(&w.Sheet); err != nil {
return err
}
for _, unit := range w.Units {
if err := visit(unit); err != nil {
return err
}
}
return nil
}
// Prepare makes sure all WallUnits know about Wall and where they are, for // Prepare makes sure all WallUnits know about Wall and where they are, for
// drawing. // drawing.
@ -103,7 +116,10 @@ func (u *WallUnit) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
} }
// Scan returns the Tile. // Scan returns the Tile.
func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} } //func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} }
func (u *WallUnit) Scan(visit func(interface{}) error) error {
return visit(u.Tile)
}
func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) { func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat( opts.GeoM.Translate(geom.CFloat(

View file

@ -270,8 +270,9 @@ func (aw *Awakeman) Prepare(game *engine.Game) error {
return nil return nil
} }
func (aw *Awakeman) Scan() []interface{} { //func (aw *Awakeman) Scan() []interface{} { return []interface{}{&aw.Sprite} }
return []interface{}{&aw.Sprite} func (aw *Awakeman) Scan(visit func(interface{}) error) error {
return visit(&aw.Sprite)
} }
func (aw *Awakeman) String() string { func (aw *Awakeman) String() string {

View file

@ -55,8 +55,9 @@ func NewBubble(pos geom.Int3) *Bubble {
} }
} }
func (b *Bubble) Scan() []interface{} { //func (b *Bubble) Scan() []interface{} { return []interface{}{&b.Sprite} }
return []interface{}{&b.Sprite} func (b *Bubble) Scan(visit func(interface{}) error) error {
return visit(&b.Sprite)
} }
func (b *Bubble) String() string { func (b *Bubble) String() string {