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

View file

@ -16,11 +16,18 @@ type Identifier interface {
Ident() string 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 // 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 { type Scanner interface {
Scan(game *Game) []interface{} Scan() []interface{}
} }
// Game implements the ebiten methods using a collection of components. // 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. // 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] } 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) { func (g *Game) Walk(v func(interface{}) bool) {
g.walk(g.Scene, v) g.walk(g.Scene, v)
} }
@ -87,7 +95,7 @@ func (g *Game) walk(c interface{}, v func(interface{}) bool) {
return return
} }
if sc, ok := c.(Scanner); ok { if sc, ok := c.(Scanner); ok {
for _, c := range sc.Scan(g) { for _, c := range sc.Scan() {
if !v(c) { if !v(c) {
return return
} }
@ -100,6 +108,9 @@ func (g *Game) walk(c interface{}, v func(interface{}) bool) {
func (g *Game) Build() { func (g *Game) Build() {
g.componentsByID = make(map[string]interface{}) g.componentsByID = make(map[string]interface{})
g.Walk(func(c interface{}) bool { g.Walk(func(c interface{}) bool {
if b, ok := c.(Builder); ok {
b.Build(g)
}
g.RegisterComponent(c) g.RegisterComponent(c)
return true return true
}) })

View file

@ -65,7 +65,7 @@ func (s *Scene) sortByZ() {
} }
// Scan returns all subcomponents. // 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. // Update calls Update on all Updater components.
func (s *Scene) Update() error { func (s *Scene) Update() error {

View file

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