bunch of changes

This commit is contained in:
Josh Deprez 2021-09-21 14:42:18 +10:00
parent af70dcaf04
commit 787a05e493
18 changed files with 104 additions and 62 deletions

View file

@ -52,7 +52,7 @@ func (b *Billboard) Prepare(g *Game) error {
} }
// Scan returns a slice containing Src. // Scan returns a slice containing Src.
func (b *Billboard) Scan() []interface{} { return []interface{}{&b.Src} } func (b *Billboard) Scan() Components { return Components{&b.Src} }
func (b *Billboard) String() string { func (b *Billboard) String() string {
return fmt.Sprintf("Billboard@%v", b.Pos) return fmt.Sprintf("Billboard@%v", b.Pos)

View file

@ -86,7 +86,7 @@ func (c *Camera) Prepare(game *Game) error {
} }
// Scan returns s.Child. // Scan returns s.Child.
func (c *Camera) Scan() []interface{} { return []interface{}{c.Child} } func (c *Camera) Scan() Components { return Components{c.Child} }
// Transform returns the camera transform. // Transform returns the camera transform.
func (c *Camera) Transform() (opts ebiten.DrawImageOptions) { func (c *Camera) Transform() (opts ebiten.DrawImageOptions) {

View file

@ -14,6 +14,7 @@ var _ interface {
DrawManager DrawManager
Hider Hider
Prepper Prepper
Registrar
Scanner Scanner
Updater Updater
} = &DrawDAG{} } = &DrawDAG{}
@ -23,8 +24,8 @@ var _ interface {
// It combines a DAG with a spatial index used when updating vertices to reduce // It combines a DAG with a spatial index used when updating vertices to reduce
// the number of tests between components. // the number of tests between components.
type DrawDAG struct { type DrawDAG struct {
ChunkSize int ChunkSize int
Components []interface{} Components
Hides Hides
*dag *dag
@ -110,15 +111,11 @@ func (d *DrawDAG) Prepare(game *Game) error {
d.game = game d.game = game
// Because Game.LoadAndPrepare calls Prepare in a post-order walk, all the // Because Game.LoadAndPrepare calls Prepare in a post-order walk, all the
// descendants should be prepared, meaning BoundingBox (hence Add) is likely // descendants should be prepared, meaning BoundingBox (hence Register) is
// to be a safe call. // likely to be a safe call.
d.addRecursive(d) return d.Register(d, nil)
return nil
} }
// Scan returns d.Components.
func (d *DrawDAG) Scan() []interface{} { return d.Components }
// Update checks for any changes to descendants, and updates its internal // Update checks for any changes to descendants, and updates its internal
// data structures accordingly. // data structures accordingly.
func (d *DrawDAG) Update() error { func (d *DrawDAG) Update() error {
@ -130,20 +127,38 @@ func (d *DrawDAG) Update() error {
for db, bb := range d.boxCache { for db, bb := range d.boxCache {
nbb := db.BoundingBox() nbb := db.BoundingBox()
if bb != nbb { if bb != nbb {
d.Remove(db) d.Unregister(db)
readd = append(readd, db) readd = append(readd, db)
} }
} }
for _, db := range readd { for _, db := range readd {
d.Add(db) d.Register(db, nil)
} }
return nil return nil
} }
// Add adds a Drawer and any needed edges to the DAG and chunk map. If x is // Register recursively registers compponent and all descendants that are
// intended to be a direct subcomponent of d, then the caller must append to // DrawBoxers into internal data structures (the DAG, etc) unless they are
// d.Components as well. // descendants of a different DrawManager.
func (d *DrawDAG) Add(x DrawBoxer) { func (d *DrawDAG) Register(component, _ interface{}) error {
if db, ok := component.(DrawBoxer); ok {
d.registerOne(db)
}
if _, ok := component.(DrawManager); ok && component != d {
return nil
}
if sc, ok := component.(Scanner); ok {
for _, x := range sc.Scan() {
if err := d.Register(x, nil); err != nil {
return err
}
}
}
return nil
}
// registerOne adds component and any needed edges to the DAG and chunk map.
func (d *DrawDAG) registerOne(x DrawBoxer) {
// Ensure vertex is present // Ensure vertex is present
d.dag.addVertex(x) d.dag.addVertex(x)
@ -196,24 +211,22 @@ func (d *DrawDAG) Add(x DrawBoxer) {
} }
} }
// addRecursive adds x and all descendants that are DrawBoxers unless they are // Unregister unregisters the component and all subcomponents.
// descendants of a different DrawManager. func (d *DrawDAG) Unregister(component interface{}) {
func (d *DrawDAG) addRecursive(x interface{}) { if db, ok := component.(DrawBoxer); ok {
if db, ok := x.(DrawBoxer); ok { d.unregisterOne(db)
d.Add(db)
} }
if _, ok := x.(DrawManager); ok && x != d { if _, ok := component.(DrawManager); ok && component != d {
return return
} }
if sc, ok := x.(Scanner); ok { if sc, ok := component.(Scanner); ok {
for _, x := range sc.Scan() { for _, x := range sc.Scan() {
d.addRecursive(x) d.Unregister(x)
} }
} }
} }
// Remove removes a Drawer and all associated edges and metadata. func (d *DrawDAG) unregisterOne(x DrawBoxer) {
func (d *DrawDAG) Remove(x DrawBoxer) {
// Remove from chunk map // Remove from chunk map
revr := d.chunksRev[x] revr := d.chunksRev[x]
for j := revr.Min.Y; j <= revr.Max.Y; j++ { for j := revr.Min.Y; j <= revr.Max.Y; j++ {

View file

@ -13,7 +13,7 @@ var _ interface {
// through the game tree, without any extra sorting based on Z values or // through the game tree, without any extra sorting based on Z values or
// consideration for DrawOrderer). // consideration for DrawOrderer).
type DrawDFS struct { type DrawDFS struct {
Components []interface{} Components
Hides Hides
} }
@ -53,5 +53,3 @@ func (d *DrawDFS) draw(component interface{}, screen *ebiten.Image, opts ebiten.
} }
} }
} }
func (d *DrawDFS) Scan() []interface{} { return d.Components }

View file

@ -20,6 +20,7 @@ var _ interface {
Hider Hider
Identifier Identifier
Updater Updater
Registrar
Scanner Scanner
} = &Game{} } = &Game{}
@ -154,7 +155,7 @@ func (g *Game) Query(ancestor interface{}, behaviour reflect.Type) map[interface
} }
// Scan returns g.Root. // Scan returns g.Root.
func (g *Game) Scan() []interface{} { return []interface{}{g.Root} } func (g *Game) Scan() Components { return Components{g.Root} }
// PreorderWalk calls visit with every component and its parent, reachable from // PreorderWalk calls visit with every component and its parent, reachable from
// the given component via Scan, for as long as visit returns nil. The parent // the given component via Scan, for as long as visit returns nil. The parent
@ -225,7 +226,7 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
g.byID = make(map[string]Identifier) g.byID = make(map[string]Identifier)
g.byAB = make(map[abKey]map[interface{}]struct{}) g.byAB = make(map[abKey]map[interface{}]struct{})
g.par = make(map[interface{}]interface{}) g.par = make(map[interface{}]interface{})
if err := PreorderWalk(g, g.register); err != nil { if err := PreorderWalk(g, g.registerOne); err != nil {
return err return err
} }
g.dbmu.Unlock() g.dbmu.Unlock()
@ -261,10 +262,10 @@ func (g *Game) Register(component, parent interface{}) error {
g.dbmu.Lock() g.dbmu.Lock()
defer g.dbmu.Unlock() defer g.dbmu.Unlock()
// preorderWalk goes in the right order for registering. // preorderWalk goes in the right order for registering.
return preorderWalk(component, parent, g.register) return preorderWalk(component, parent, g.registerOne)
} }
func (g *Game) register(component, parent interface{}) error { func (g *Game) registerOne(component, parent interface{}) error {
// register in g.byID if needed // register in g.byID if needed
if i, ok := component.(Identifier); ok { if i, ok := component.(Identifier); ok {
if id := i.Ident(); id != "" { if id := i.Ident(); id != "" {
@ -307,11 +308,11 @@ func (g *Game) Unregister(component interface{}) {
} }
g.dbmu.Lock() g.dbmu.Lock()
// postorderWalk goes in the right order for unregistering. // postorderWalk goes in the right order for unregistering.
postorderWalk(component, nil, g.unregister) postorderWalk(component, nil, g.unregisterOne)
g.dbmu.Unlock() g.dbmu.Unlock()
} }
func (g *Game) unregister(component, _ interface{}) error { func (g *Game) unregisterOne(component, _ interface{}) error {
// unregister from g.byAB, using g.par to trace the path // unregister from g.byAB, using g.par to trace the path
ct := reflect.TypeOf(component) ct := reflect.TypeOf(component)
for _, b := range Behaviours { for _, b := range Behaviours {

View file

@ -25,6 +25,7 @@ var (
IdentifierType = reflect.TypeOf((*Identifier)(nil)).Elem() IdentifierType = reflect.TypeOf((*Identifier)(nil)).Elem()
LoaderType = reflect.TypeOf((*Loader)(nil)).Elem() LoaderType = reflect.TypeOf((*Loader)(nil)).Elem()
PrepperType = reflect.TypeOf((*Prepper)(nil)).Elem() PrepperType = reflect.TypeOf((*Prepper)(nil)).Elem()
RegistrarType = reflect.TypeOf((*Registrar)(nil)).Elem()
SaverType = reflect.TypeOf((*Saver)(nil)).Elem() SaverType = reflect.TypeOf((*Saver)(nil)).Elem()
ScannerType = reflect.TypeOf((*Scanner)(nil)).Elem() ScannerType = reflect.TypeOf((*Scanner)(nil)).Elem()
TransformerType = reflect.TypeOf((*Transformer)(nil)).Elem() TransformerType = reflect.TypeOf((*Transformer)(nil)).Elem()
@ -44,6 +45,7 @@ var (
IdentifierType, IdentifierType,
LoaderType, LoaderType,
PrepperType, PrepperType,
RegistrarType,
SaverType, SaverType,
ScannerType, ScannerType,
TransformerType, TransformerType,
@ -128,6 +130,14 @@ type Prepper interface {
Prepare(game *Game) error Prepare(game *Game) error
} }
// Registrar components can register and unregister other components (usually
// into internal data structures). Registrars are expected to automatically
// register/unregister subcomponents of components (usually recursively).
type Registrar interface {
Register(component, parent interface{}) error
Unregister(component interface{})
}
// Saver components can be saved to disk. // Saver components can be saved to disk.
type Saver interface { type Saver interface {
Save() error Save() error
@ -137,7 +147,7 @@ type Saver interface {
// (such as when the game component database is constructed). // (such as when the game component database is constructed).
// Scan should return a slice containing all immediate subcomponents. // Scan should return a slice containing all immediate subcomponents.
type Scanner interface { type Scanner interface {
Scan() []interface{} Scan() Components
} }
// Transformer components can provide draw options to apply to themselves and // Transformer components can provide draw options to apply to themselves and

View file

@ -39,3 +39,9 @@ func (h *Hides) Hide() { *h = true }
// Show sets h to false. // Show sets h to false.
func (h *Hides) Show() { *h = false } func (h *Hides) Show() { *h = false }
// Components implements Scan directly (as a slice of interface{}).
type Components []interface{}
// Scan returns c.
func (c Components) Scan() Components { return c }

View file

@ -39,7 +39,7 @@ func (p *Parallax) Prepare(game *Game) error {
} }
// Scan returns the child component. // Scan returns the child component.
func (p *Parallax) Scan() []interface{} { return []interface{}{p.Child} } func (p *Parallax) Scan() Components { return Components{p.Child} }
// Transform returns a GeoM translation of Factor * camera.Centre. // Transform returns a GeoM translation of Factor * camera.Centre.
func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) { func (p *Parallax) Transform() (opts ebiten.DrawImageOptions) {

View file

@ -16,6 +16,7 @@ var (
Disabler Disabler
Hider Hider
Prepper Prepper
Scanner
Transformer Transformer
} = &PrismMap{} } = &PrismMap{}
@ -112,8 +113,8 @@ func (m *PrismMap) Prepare(g *Game) error {
} }
// Scan returns the Sheet and all the Prisms. // Scan returns the Sheet and all the Prisms.
func (m *PrismMap) Scan() []interface{} { func (m *PrismMap) Scan() Components {
c := make([]interface{}, 1, len(m.Map)+1) c := make(Components, 1, len(m.Map)+1)
c[0] = &m.Sheet c[0] = &m.Sheet
for _, prism := range m.Map { for _, prism := range m.Map {
c = append(c, prism) c = append(c, prism)

View file

@ -29,18 +29,16 @@ func init() {
gob.Register(&SceneRef{}) gob.Register(&SceneRef{})
} }
// Scene contains a bunch of components. // Scene gives an identity, bounds, and other properties to a collection of
// components.
type Scene struct { type Scene struct {
ID ID
Bounds // world coordinates Bounds // world coordinates
Components []interface{} Components
Disables Disables
Hides Hides
} }
// Scan returns all immediate subcomponents (including the camera, if not nil).
func (s *Scene) Scan() []interface{} { return s.Components }
// SceneRef loads a gzipped, gob-encoded Scene from the asset FS. // SceneRef loads a gzipped, gob-encoded Scene from the asset FS.
// After Load, Scene is usable. // After Load, Scene is usable.
// This is mostly useful for scenes that refer to other scenes, e.g. // This is mostly useful for scenes that refer to other scenes, e.g.

View file

@ -47,7 +47,7 @@ func (s *Sheet) Prepare(*Game) error {
} }
// Scan returns the Src. // Scan returns the Src.
func (s *Sheet) Scan() []interface{} { return []interface{}{&s.Src} } func (s *Sheet) Scan() Components { return Components{&s.Src} }
// SubImage returns an *ebiten.Image corresponding to the given cell index. // SubImage returns an *ebiten.Image corresponding to the given cell index.
func (s *Sheet) SubImage(i int) *ebiten.Image { func (s *Sheet) SubImage(i int) *ebiten.Image {

View file

@ -41,8 +41,8 @@ func (s *Sprite) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
} }
// Scan returns the Actor and the Sheet. // Scan returns the Actor and the Sheet.
func (s *Sprite) Scan() []interface{} { func (s *Sprite) Scan() Components {
return []interface{}{ return Components{
&s.Actor, &s.Actor,
&s.Sheet, &s.Sheet,
} }

View file

@ -100,8 +100,8 @@ func (t *Tilemap) Load(fs.FS) error {
} }
// Scan returns a slice containing Src and all non-nil tiles. // Scan returns a slice containing Src and all non-nil tiles.
func (t *Tilemap) Scan() []interface{} { func (t *Tilemap) Scan() Components {
c := make([]interface{}, 1, len(t.Map)+1) c := make(Components, 1, len(t.Map)+1)
c[0] = &t.Sheet c[0] = &t.Sheet
for _, tile := range t.Map { for _, tile := range t.Map {
c = append(c, tile) c = append(c, tile)
@ -152,4 +152,4 @@ type AnimatedTile struct {
func (a *AnimatedTile) Cell() int { return a.anim.Cell() } func (a *AnimatedTile) Cell() int { return a.anim.Cell() }
// Scan returns a.anim. // Scan returns a.anim.
func (a *AnimatedTile) Scan() []interface{} { return []interface{}{a.anim} } func (a *AnimatedTile) Scan() Components { return Components{a.anim} }

View file

@ -61,8 +61,8 @@ func (w *Wall) CollidesWith(b geom.Box) bool {
} }
// Scan returns the Sheet and all WallUnits. // Scan returns the Sheet and all WallUnits.
func (w *Wall) Scan() []interface{} { func (w *Wall) Scan() Components {
c := make([]interface{}, 1, len(w.Units)+1) c := make(Components, 1, len(w.Units)+1)
c[0] = &w.Sheet c[0] = &w.Sheet
for _, unit := range w.Units { for _, unit := range w.Units {
c = append(c, unit) c = append(c, unit)
@ -103,7 +103,7 @@ func (u *WallUnit) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
} }
// Scan returns the Tile. // Scan returns the Tile.
func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} } func (u *WallUnit) Scan() Components { return Components{u.Tile} }
func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) { func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat( opts.GeoM.Translate(geom.CFloat(

View file

@ -125,7 +125,14 @@ func (aw *Awakeman) realUpdate() error {
}); err != nil { }); err != nil {
return err return err
} }
aw.game.Register(bubble, aw.game.Parent(aw)) par := aw.game.Parent(aw)
for p := interface{}(aw); p != nil; p = aw.game.Parent(p) {
if r, ok := p.(engine.Registrar); ok {
if err := r.Register(bubble, par); err != nil {
return err
}
}
}
if err := engine.PreorderWalk(bubble, func(c, _ interface{}) error { if err := engine.PreorderWalk(bubble, func(c, _ interface{}) error {
if p, ok := c.(engine.Prepper); ok { if p, ok := c.(engine.Prepper); ok {
return p.Prepare(aw.game) return p.Prepare(aw.game)
@ -268,7 +275,9 @@ func (aw *Awakeman) Prepare(game *engine.Game) error {
return nil return nil
} }
func (aw *Awakeman) Scan() []interface{} { return []interface{}{&aw.Sprite} } func (aw *Awakeman) Scan() engine.Components {
return engine.Components{&aw.Sprite}
}
func (aw *Awakeman) String() string { func (aw *Awakeman) String() string {
return fmt.Sprintf("Awakeman@%v", aw.Sprite.Actor.Pos) return fmt.Sprintf("Awakeman@%v", aw.Sprite.Actor.Pos)

View file

@ -9,6 +9,12 @@ import (
"drjosh.dev/gurgle/geom" "drjosh.dev/gurgle/geom"
) )
var _ interface {
engine.Scanner
engine.Prepper
engine.Updater
} = &Bubble{}
type Bubble struct { type Bubble struct {
Life int Life int
Sprite engine.Sprite Sprite engine.Sprite
@ -49,8 +55,8 @@ func NewBubble(pos geom.Int3) *Bubble {
} }
} }
func (b *Bubble) Scan() []interface{} { func (b *Bubble) Scan() engine.Components {
return []interface{}{&b.Sprite} return engine.Components{&b.Sprite}
} }
func (b *Bubble) String() string { func (b *Bubble) String() string {

View file

@ -12,7 +12,7 @@ func Level1() *engine.Scene {
return &engine.Scene{ return &engine.Scene{
ID: "level_1", ID: "level_1",
Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)), Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)),
Components: []interface{}{ Components: engine.Components{
&engine.Parallax{ &engine.Parallax{
CameraID: "game_camera", CameraID: "game_camera",
Child: &engine.Billboard{ Child: &engine.Billboard{

View file

@ -68,14 +68,14 @@ func main() {
Z: math.Sqrt(3), Z: math.Sqrt(3),
}, },
Root: &engine.DrawDFS{ Root: &engine.DrawDFS{
Components: []interface{}{ Components: engine.Components{
&engine.Fill{ &engine.Fill{
ID: "bg_fill", ID: "bg_fill",
Color: color.Gray{100}, Color: color.Gray{100},
}, },
&engine.DrawDAG{ &engine.DrawDAG{
ChunkSize: 16, ChunkSize: 16,
Components: []interface{}{ Components: engine.Components{
&engine.Camera{ &engine.Camera{
ID: "game_camera", ID: "game_camera",
Child: lev1, Child: lev1,