a nicer solution :)
This commit is contained in:
parent
228f8b9b9a
commit
91785fb6c0
4 changed files with 35 additions and 80 deletions
|
@ -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
|
||||
|
|
|
@ -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.
9
main.go
9
main.go
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue