a nicer solution :)

This commit is contained in:
Josh Deprez 2021-09-01 13:28:07 +10:00
parent 228f8b9b9a
commit 91785fb6c0
4 changed files with 35 additions and 80 deletions

View file

@ -17,13 +17,11 @@ var (
ColliderType = reflect.TypeOf((*Collider)(nil)).Elem()
DisablerType = reflect.TypeOf((*Disabler)(nil)).Elem()
DrawerType = reflect.TypeOf((*Drawer)(nil)).Elem()
DrawUpdaterType = reflect.TypeOf((*DrawUpdater)(nil)).Elem()
HiderType = reflect.TypeOf((*Hider)(nil)).Elem()
IdentifierType = reflect.TypeOf((*Identifier)(nil)).Elem()
LoaderType = reflect.TypeOf((*Loader)(nil)).Elem()
PrepperType = reflect.TypeOf((*Prepper)(nil)).Elem()
ScannerType = reflect.TypeOf((*Scanner)(nil)).Elem()
ScenerType = reflect.TypeOf((*Scener)(nil)).Elem()
SaverType = reflect.TypeOf((*Saver)(nil)).Elem()
TransformerType = reflect.TypeOf((*Transformer)(nil)).Elem()
UpdaterType = reflect.TypeOf((*Updater)(nil)).Elem()
@ -35,13 +33,11 @@ var (
ColliderType,
DisablerType,
DrawerType,
DrawUpdaterType,
HiderType,
IdentifierType,
LoaderType,
PrepperType,
ScannerType,
ScenerType,
SaverType,
TransformerType,
UpdaterType,
@ -81,13 +77,6 @@ type Drawer interface {
DrawOrder() float64
}
// DrawUpdater components can be both drawn and updated.
// Same comments as for Drawer and Updater.
type DrawUpdater interface {
Drawer
Updater
}
// Hider components can be hidden.
type Hider interface {
IsHidden() bool
@ -121,33 +110,6 @@ type Scanner interface {
Scan() []interface{}
}
// Scener components are a scene (Scene or SceneRef).
type Scener interface {
// Q: Why not make Scene able to load itself?
// A: Having separate types makes it easier to reason about what is loading
// what. There is less ambiguity about what "save" means (the contents of
// the scene, or the path to the file?) Additionally, the gob decoder
// decodes over existing fields, which could lead to some fun bugs.
//
// Q: Why not make Scener a small interface, e.g. with just Scene() ?
// A: Everything in the engine would then need to type switch for Scener or
// SceneRef, i.e.
// switch x := i.(type) {
// case Drawer:
// i.Draw(screen, opts)
// case Scener:
// i.Scene().Draw(screen, opts)
// }
// It seems cleaner to let the engine assert only for the interface it
// needs at that moment.
Bounder
Disabler
Hider
Identifier
Scanner
}
// Saver components can be saved to disk.
type Saver interface {
Save() error

View file

@ -2,29 +2,34 @@ package engine
import (
"encoding/gob"
"image"
"io/fs"
"path/filepath"
)
var (
// Ensure Scene satisfies Scener.
_ Scener = &Scene{}
_ scener = &Scene{}
// Ensure SceneRef satisfies interfaces.
_ interface {
Loader
Scener
Saver
scener
} = &SceneRef{}
)
type scener interface {
Bounder
Disabler
Hider
Identifier
Scanner
}
func init() {
gob.Register(&Scene{})
gob.Register(&SceneRef{})
}
// Scene just contains a bunch of components.
// Scene contains a bunch of components.
type Scene struct {
ID
Bounds // world coordinates
@ -48,7 +53,18 @@ func (s *Scene) Scan() []interface{} { return s.Components }
type SceneRef struct {
Path string
scene *Scene // not exported for gob reasons
*Scene // not gob encoded
}
// GobDecode saves the byte slice as Path.
func (r *SceneRef) GobDecode(b []byte) error {
r.Path = string(b)
return nil
}
// GobEncode returns Path as a byte slice.
func (r *SceneRef) GobEncode() ([]byte, error) {
return []byte(r.Path), nil
}
// Load loads the scene from the file.
@ -57,39 +73,11 @@ func (r *SceneRef) Load(assets fs.FS) error {
if err := LoadGobz(sc, assets, r.Path); err != nil {
return err
}
r.scene = sc
r.Scene = sc
return nil
}
// Save saves the scene to a file in the current directory.
func (r *SceneRef) Save() error { return SaveGobz(r.scene, filepath.Base(r.Path)) }
// The rest of the methods forward to r.scene, as such they will
// panic if the scene isn't loaded.
// BoundingRect returns the Bounds from the scene.
func (r SceneRef) BoundingRect() image.Rectangle { return r.scene.BoundingRect() }
// IsDisabled returns the value of IsDisabled from the scene.
func (r SceneRef) IsDisabled() bool { return r.scene.IsDisabled() }
// Disable calls Disable on the scene.
func (r SceneRef) Disable() { r.scene.Disable() }
// Enable calls Enable on the scene.
func (r SceneRef) Enable() { r.scene.Enable() }
// IsHidden returns the value of IsHidden from the scene.
func (r SceneRef) IsHidden() bool { return r.scene.IsHidden() }
// Hide calls Hide on the scene.
func (r SceneRef) Hide() { r.scene.Hide() }
// Show calls Show on the scene.
func (r SceneRef) Show() { r.scene.Show() }
// Ident returns the value of Ident from the scene.
func (r SceneRef) Ident() string { return r.scene.Ident() }
// Scan returns the components in the scene.
func (r SceneRef) Scan() []interface{} { return r.scene.Scan() }
func (r *SceneRef) Save() error {
return SaveGobz(r.Scene, filepath.Base(r.Path))
}

Binary file not shown.

View file

@ -14,9 +14,14 @@ import (
"github.com/hajimehoshi/ebiten/v2"
)
const (
enableCPUProfile = false
rewriteLevel1 = false
)
func main() {
// Change to true to enable cpu profile
if false && runtime.GOOS != "js" {
if enableCPUProfile && runtime.GOOS != "js" {
f, err := os.Create("cpuprofile.pprof")
if err != nil {
log.Fatal("could not create CPU profile: ", err)
@ -33,7 +38,7 @@ func main() {
ebiten.SetWindowTitle("TODO")
// Change to true to rewrite level1.gobz
if false && runtime.GOOS != "js" {
if rewriteLevel1 && runtime.GOOS != "js" {
writeLevel1()
}