diff --git a/engine/actor.go b/engine/actor.go index 84fb999..511541e 100644 --- a/engine/actor.go +++ b/engine/actor.go @@ -12,7 +12,7 @@ var _ Prepper = &Actor{} var errCollision = errors.New("collision detected") func init() { - gob.Register(Actor{}) + gob.Register(&Actor{}) } // Thorson-style movement: diff --git a/engine/anim.go b/engine/anim.go index bc843cd..04ba674 100644 --- a/engine/anim.go +++ b/engine/anim.go @@ -1,8 +1,14 @@ package engine +import "encoding/gob" + // Ensure Anim satisfies Animer. var _ Animer = &Anim{} +func init() { + gob.Register(&Anim{}) +} + // AnimFrame describes a frame in an animation. type AnimFrame struct { Frame int // show this frame diff --git a/engine/animref.go b/engine/animref.go index e1961b2..3ab46d6 100644 --- a/engine/animref.go +++ b/engine/animref.go @@ -13,7 +13,7 @@ var ( ) func init() { - gob.Register(AnimRef{}) + gob.Register(&AnimRef{}) } // AnimRef manages an Anim using a premade AnimDef from the cache. diff --git a/engine/asset.go b/engine/asset.go index 8f547b3..eb071ec 100644 --- a/engine/asset.go +++ b/engine/asset.go @@ -5,6 +5,7 @@ import ( "encoding/gob" "image" "io/fs" + "os" "github.com/hajimehoshi/ebiten/v2" ) @@ -38,6 +39,28 @@ func loadGobz(dst interface{}, assets fs.FS, path string) error { return gob.NewDecoder(gz).Decode(dst) } +// SaveGobz takes an object, gob-encodes it, gzips it, and writes to disk. +func SaveGobz(src interface{}, name string) error { + f, err := os.CreateTemp(".", name) + if err != nil { + return err + } + defer os.Remove(f.Name()) + defer f.Close() + + gz := gzip.NewWriter(f) + if err := gob.NewEncoder(gz).Encode(src); err != nil { + return err + } + if err := gz.Close(); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + return os.Rename(f.Name(), name) +} + // ImageRef loads images from the AssetFS into *ebiten.Image form. // It is your responsibility to import _ "image/..." for whatever // format the files are in, and to load it (either return it as a diff --git a/engine/camera.go b/engine/camera.go index 1fa1c06..be4b2f8 100644 --- a/engine/camera.go +++ b/engine/camera.go @@ -17,7 +17,7 @@ var ( ) func init() { - gob.Register(Camera{}) + gob.Register(&Camera{}) } // Camera models a camera that is viewing a scene. diff --git a/engine/debug.go b/engine/debug.go index 514ec3b..d4c4567 100644 --- a/engine/debug.go +++ b/engine/debug.go @@ -24,8 +24,8 @@ var ( ) func init() { - gob.Register(GobDumper{}) - gob.Register(PerfDisplay{}) + gob.Register(&GobDumper{}) + gob.Register(&PerfDisplay{}) } // DebugToast debugprints a string for a while, then disappears. diff --git a/engine/fill.go b/engine/fill.go index 7864102..3d28fcc 100644 --- a/engine/fill.go +++ b/engine/fill.go @@ -1,6 +1,7 @@ package engine import ( + "encoding/gob" "image/color" "github.com/hajimehoshi/ebiten/v2" @@ -9,6 +10,12 @@ import ( // Ensure Fill satisfies Drawer. var _ Drawer = &Fill{} +func init() { + gob.Register(Fill{}) + gob.Register(color.Gray{}) + gob.Register(color.RGBA{}) +} + // Fill fills the screen with a colour. type Fill struct { ID diff --git a/engine/game.go b/engine/game.go index 2f122cd..9fb77ff 100644 --- a/engine/game.go +++ b/engine/game.go @@ -8,7 +8,7 @@ import ( ) func init() { - gob.Register(Game{}) + gob.Register(&Game{}) } // Game implements the ebiten methods using a collection of components. diff --git a/engine/image.go b/engine/image.go index 29ef64b..e70159a 100644 --- a/engine/image.go +++ b/engine/image.go @@ -17,7 +17,7 @@ var ( ) func init() { - gob.Register(Image{}) + gob.Register(&Image{}) } // Image draws an image at a position. diff --git a/engine/scene.go b/engine/scene.go index 338974f..0ef0e13 100644 --- a/engine/scene.go +++ b/engine/scene.go @@ -12,7 +12,7 @@ import ( var _ Scener = &Scene{} func init() { - gob.Register(Scene{}) + gob.Register(&Scene{}) } // Scene manages drawing and updating a bunch of components. diff --git a/engine/sceneref.go b/engine/sceneref.go index ccfc38c..e887969 100644 --- a/engine/sceneref.go +++ b/engine/sceneref.go @@ -24,7 +24,7 @@ func init() { // // var sc = &Scene{ // Components: []interface{}{ -// SceneRef{Path: "assets/foo.gob.gz"} // inflated at Load time +// &SceneRef{Path: "assets/foo.gob.gz"} // inflated at Load time // }, // } type SceneRef struct { diff --git a/engine/solid.go b/engine/solid.go index 21288fa..a681949 100644 --- a/engine/solid.go +++ b/engine/solid.go @@ -8,7 +8,7 @@ import ( var _ Collider = SolidRect{} func init() { - gob.Register(SolidRect{}) + gob.Register(&SolidRect{}) } type SolidRect struct { diff --git a/engine/sprite.go b/engine/sprite.go index 713dd27..d1d25c5 100644 --- a/engine/sprite.go +++ b/engine/sprite.go @@ -17,7 +17,7 @@ var ( ) func init() { - gob.Register(Sprite{}) + gob.Register(&Sprite{}) } // Sprite combines an Actor with the ability to Draw from a single spritesheet. diff --git a/engine/tiles.go b/engine/tiles.go index 93934ce..acea945 100644 --- a/engine/tiles.go +++ b/engine/tiles.go @@ -19,9 +19,9 @@ var ( ) func init() { - gob.Register(AnimatedTile{}) + gob.Register(&AnimatedTile{}) gob.Register(StaticTile(0)) - gob.Register(Tilemap{}) + gob.Register(&Tilemap{}) } // Tilemap renders a grid of tiles. diff --git a/game/assets/level1.gobz b/game/assets/level1.gobz new file mode 100644 index 0000000..72ccf22 Binary files /dev/null and b/game/assets/level1.gobz differ diff --git a/game/aw.go b/game/aw.go index 4563f74..dba5d33 100644 --- a/game/aw.go +++ b/game/aw.go @@ -11,7 +11,7 @@ import ( ) func init() { - gob.Register(Awakeman{}) + gob.Register(&Awakeman{}) } type Awakeman struct { diff --git a/main.go b/main.go index 8de1fbe..4967abd 100644 --- a/main.go +++ b/main.go @@ -16,95 +16,101 @@ func main() { ebiten.SetWindowSize(640, 480) ebiten.SetWindowTitle("TODO") - redTileAnim := &engine.Anim{Frames: []engine.AnimFrame{ - {Frame: 3, Duration: 12}, - {Frame: 4, Duration: 12}, - {Frame: 5, Duration: 12}, - {Frame: 6, Duration: 12}, - }} - greenTileAnim := &engine.Anim{Frames: []engine.AnimFrame{ - {Frame: 0, Duration: 16}, - {Frame: 1, Duration: 16}, - {Frame: 2, Duration: 16}, - }} - denseTiles := [][]engine.Tile{ - {engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.StaticTile(9)}, - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil}, - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil}, - {nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil}, - {nil, nil, nil, nil, engine.StaticTile(9), engine.StaticTile(9), engine.StaticTile(9), nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil}, - {engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.AnimatedTile{Animer: redTileAnim.Copy()}, engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8)}, - {engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.AnimatedTile{Animer: redTileAnim.Copy()}, engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.AnimatedTile{Animer: greenTileAnim.Copy()}, engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7)}, - {engine.StaticTile(9), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(9)}, - } - tiles := make(map[image.Point]engine.Tile) - for j, row := range denseTiles { - for i, tile := range row { - if tile == nil { - continue - } - tiles[image.Pt(i, j)] = tile + if false { + redTileAnim := &engine.Anim{Frames: []engine.AnimFrame{ + {Frame: 3, Duration: 12}, + {Frame: 4, Duration: 12}, + {Frame: 5, Duration: 12}, + {Frame: 6, Duration: 12}, + }} + greenTileAnim := &engine.Anim{Frames: []engine.AnimFrame{ + {Frame: 0, Duration: 16}, + {Frame: 1, Duration: 16}, + {Frame: 2, Duration: 16}, + }} + denseTiles := [][]engine.Tile{ + {engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.StaticTile(9)}, + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil}, + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.AnimatedTile{Animer: greenTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, engine.AnimatedTile{Animer: redTileAnim.Copy()}, nil, nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, engine.StaticTile(9), nil, nil, nil}, + {nil, nil, nil, nil, engine.StaticTile(9), engine.StaticTile(9), engine.StaticTile(9), nil, nil, nil, engine.StaticTile(9), nil, nil, nil, nil, nil, nil, nil, nil, nil}, + {engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.AnimatedTile{Animer: redTileAnim.Copy()}, engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8), engine.StaticTile(8)}, + {engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.AnimatedTile{Animer: redTileAnim.Copy()}, engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.AnimatedTile{Animer: greenTileAnim.Copy()}, engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7)}, + {engine.StaticTile(9), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(7), engine.StaticTile(9)}, + } + tiles := make(map[image.Point]engine.Tile) + for j, row := range denseTiles { + for i, tile := range row { + if tile == nil { + continue + } + tiles[image.Pt(i, j)] = tile + } } - } - level1 := &engine.Scene{ - ID: "level_1", - Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)), - Components: []interface{}{ - &engine.Fill{ - Color: color.Gray{100}, - ZOrder: 0, - }, - &engine.Image{ - ID: "bg_image", - Parallax: 0.5, - ZOrder: 1, - Pos: image.Pt(-160, -120), - Src: engine.ImageRef{Path: "assets/space.png"}, - }, - &engine.Tilemap{ - ID: "terrain", - ZOrder: 2, - Map: tiles, - Src: engine.ImageRef{Path: "assets/boxes.png"}, - TileSize: 16, - }, - &engine.SolidRect{ - ID: "ceiling", - Bounds: engine.Bounds(image.Rect(0, -1, 320, 0)), - }, - &engine.SolidRect{ - ID: "left_wall", - Bounds: engine.Bounds(image.Rect(-1, 0, 0, 240)), - }, - &engine.SolidRect{ - ID: "right_wall", - Bounds: engine.Bounds(image.Rect(320, 0, 321, 240)), - }, - &game.Awakeman{ - CameraID: "game_camera", - ToastID: "toast", - Sprite: engine.Sprite{ - ID: "awakeman", - Actor: engine.Actor{ - CollisionDomain: "level_1", - Pos: image.Pt(100, 100), - Size: image.Pt(8, 16), + level1 := &engine.Scene{ + ID: "level_1", + Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)), + Components: []interface{}{ + &engine.Fill{ + Color: color.Gray{100}, + ZOrder: 0, + }, + &engine.Image{ + ID: "bg_image", + Parallax: 0.5, + ZOrder: 1, + Pos: image.Pt(-160, -120), + Src: engine.ImageRef{Path: "assets/space.png"}, + }, + &engine.Tilemap{ + ID: "terrain", + ZOrder: 2, + Map: tiles, + Src: engine.ImageRef{Path: "assets/boxes.png"}, + TileSize: 16, + }, + &engine.SolidRect{ + ID: "ceiling", + Bounds: engine.Bounds(image.Rect(0, -1, 320, 0)), + }, + &engine.SolidRect{ + ID: "left_wall", + Bounds: engine.Bounds(image.Rect(-1, 0, 0, 240)), + }, + &engine.SolidRect{ + ID: "right_wall", + Bounds: engine.Bounds(image.Rect(320, 0, 321, 240)), + }, + &game.Awakeman{ + CameraID: "game_camera", + ToastID: "toast", + Sprite: engine.Sprite{ + ID: "awakeman", + Actor: engine.Actor{ + CollisionDomain: "level_1", + Pos: image.Pt(100, 100), + Size: image.Pt(8, 16), + }, + ZOrder: 3, + FrameOffset: image.Pt(-1, 0), + FrameSize: image.Pt(10, 16), + Src: engine.ImageRef{Path: "assets/aw.png"}, }, - ZOrder: 3, - FrameOffset: image.Pt(-1, 0), - FrameSize: image.Pt(10, 16), - Src: engine.ImageRef{Path: "assets/aw.png"}, }, }, - }, + } + + if err := engine.SaveGobz(level1, "level1.gobz"); err != nil { + log.Fatalf("Couldn't save level1.gobz: %v", err) + } } g := &engine.Game{ @@ -118,7 +124,7 @@ func main() { }, &engine.Camera{ ID: "game_camera", - Scene: level1, + Scene: &engine.SceneRef{Path: "assets/level1.gobz"}, }, &engine.DebugToast{ ID: "toast",