Split Scan and Build

This commit is contained in:
Josh Deprez 2021-08-02 12:16:10 +10:00 committed by Josh Deprez
parent 1c8d054aff
commit 3d75693cc2
4 changed files with 29 additions and 13 deletions

View file

@ -1,6 +1,7 @@
package engine
import (
"compress/gzip"
"encoding/gob"
"errors"
"fmt"
@ -42,11 +43,10 @@ type GobDumper struct {
game *Game
}
func (d *GobDumper) Scan(g *Game) []interface{} {
d.game = g
return nil
}
// Build simply stores the reference to the Game.
func (d *GobDumper) Build(g *Game) { d.game = g }
// Update waits for the key combo, then dumps the game state into a gzipped gob.
func (d *GobDumper) Update() error {
for _, key := range d.KeyCombo {
if !ebiten.IsKeyPressed(key) {
@ -56,12 +56,17 @@ func (d *GobDumper) Update() error {
if d.game == nil {
return errors.New("nil d.game in GobDumper.Update")
}
f, err := os.Create(time.Now().Format("20060102030405.gob"))
f, err := os.Create(time.Now().Format("20060102030405.gob.gz"))
if err != nil {
return err
}
defer f.Close()
if err := gob.NewEncoder(f).Encode(d.game); err != nil {
gz := gzip.NewWriter(f)
defer gz.Close()
if err := gob.NewEncoder(gz).Encode(d.game); err != nil {
return err
}
if err := gz.Close(); err != nil {
return err
}
return f.Close()

View file

@ -16,11 +16,18 @@ type Identifier interface {
Ident() string
}
// Scanner components can be scanned. It is called when the game
// Builder components can be built. It is called when the game
// component database is being constructed. It should store the Game reference
// (if needed later on), and return a slice of all subcomponents.
// (if needed later on).
type Builder interface {
Build(game *Game)
}
// Scanner components can be scanned. It is called when the game tree is walked
// (such as when the game component database is constructed).
// Scan should return a slice containing all subcomponents.
type Scanner interface {
Scan(game *Game) []interface{}
Scan() []interface{}
}
// Game implements the ebiten methods using a collection of components.
@ -77,7 +84,8 @@ func (g *Game) UnregisterComponent(c interface{}) {
// Component returns the component with a given ID, or nil if there is none.
func (g *Game) Component(id string) interface{} { return g.componentsByID[id] }
// Walk calls v with every component, for as long as visit returns true.
// Walk calls v with every component reachable via Scan, for as long as visit
// returns true.
func (g *Game) Walk(v func(interface{}) bool) {
g.walk(g.Scene, v)
}
@ -87,7 +95,7 @@ func (g *Game) walk(c interface{}, v func(interface{}) bool) {
return
}
if sc, ok := c.(Scanner); ok {
for _, c := range sc.Scan(g) {
for _, c := range sc.Scan() {
if !v(c) {
return
}
@ -100,6 +108,9 @@ func (g *Game) walk(c interface{}, v func(interface{}) bool) {
func (g *Game) Build() {
g.componentsByID = make(map[string]interface{})
g.Walk(func(c interface{}) bool {
if b, ok := c.(Builder); ok {
b.Build(g)
}
g.RegisterComponent(c)
return true
})

View file

@ -65,7 +65,7 @@ func (s *Scene) sortByZ() {
}
// Scan returns all subcomponents.
func (s *Scene) Scan(g *Game) []interface{} { return s.Components }
func (s *Scene) Scan() []interface{} { return s.Components }
// Update calls Update on all Updater components.
func (s *Scene) Update() error {

View file

@ -74,7 +74,7 @@ func main() {
ScreenHeight: screenHeight,
ScreenWidth: screenWidth,
Scene: &engine.Scene{
ID: "root_scene",
ID: "root",
Components: []interface{}{
&engine.GobDumper{
KeyCombo: []ebiten.Key{ebiten.KeyControl, ebiten.KeyD},