This commit is contained in:
Josh Deprez 2021-09-20 15:52:44 +10:00
parent 951b38271f
commit 938fd70921
6 changed files with 42 additions and 53 deletions

View file

@ -38,7 +38,8 @@ func (a *Actor) BoundingBox() geom.Box {
// given position (not necessarily a.Pos). // given position (not necessarily a.Pos).
func (a *Actor) CollidesAt(p geom.Int3) bool { func (a *Actor) CollidesAt(p geom.Int3) bool {
bounds := a.Bounds.Add(p) bounds := a.Bounds.Add(p)
for c := range a.game.Query(a.CollisionDomain, ColliderType) { cd := a.game.Component(a.CollisionDomain)
for c := range a.game.Query(cd, ColliderType) {
if c.(Collider).CollidesWith(bounds) { if c.(Collider).CollidesWith(bounds) {
return true return true
} }

View file

@ -95,15 +95,13 @@ func (d *DrawDAG) Prepare(game *Game) error {
d.chunksRev = make(map[DrawBoxer]image.Rectangle) d.chunksRev = make(map[DrawBoxer]image.Rectangle)
d.game = game d.game = game
// Descendants might not be prepared yet, so fill the cache with zero boxes // Because Game.LoadAndPrepare calls Prepare in a post-order walk, all the
// and fill remaining data structures during update // descendants should be prepared, meaning BoundingBox (and Add) is likely
// TODO: work out a how to prepare the descendants first // to be a safe call.
return PreorderWalk(d, func(c, _ interface{}) error { for db := range game.Query(d, DrawBoxerType) {
if db, ok := c.(DrawBoxer); ok { d.Add(db.(DrawBoxer))
d.boxCache[db] = geom.Box{}
} }
return nil return nil
})
} }
func (d *DrawDAG) Scan() []interface{} { return d.Components } func (d *DrawDAG) Scan() []interface{} { return d.Components }
@ -284,13 +282,11 @@ func (d *dag) addEdge(u, v Drawer) {
d.out[u][v] = struct{}{} d.out[u][v] = struct{}{}
} }
/*
// removeEdge removes the edge u-v in O(1). // removeEdge removes the edge u-v in O(1).
func (d *dag) removeEdge(u, v Drawer) { func (d *dag) removeEdge(u, v Drawer) {
delete(d.in[v], u) delete(d.in[v], u)
delete(d.out[u], v) delete(d.out[u], v)
} }
*/
// addVertex ensures the vertex is present, even if there are no edges. // addVertex ensures the vertex is present, even if there are no edges.
func (d *dag) addVertex(v Drawer) { func (d *dag) addVertex(v Drawer) {
@ -300,15 +296,11 @@ func (d *dag) addVertex(v Drawer) {
// removeVertex removes all in and out edges associated with v in O(degree(v)). // removeVertex removes all in and out edges associated with v in O(degree(v)).
func (d *dag) removeVertex(v Drawer) { func (d *dag) removeVertex(v Drawer) {
for u := range d.in[v] { for u := range d.in[v] {
// u-v is no longer an edge d.removeEdge(u, v)
delete(d.out[u], v)
} }
for w := range d.out[v] { for w := range d.out[v] {
// v-w is no longer an edge d.removeEdge(v, w)
delete(d.in[w], v)
} }
delete(d.in, v)
delete(d.out, v)
delete(d.all, v) delete(d.all, v)
} }

View file

@ -82,7 +82,7 @@ func (g *Game) Update() error {
// Update everything that is not disabled. // Update everything that is not disabled.
// TODO: do it in a fixed order? map essentially randomises iteration order // TODO: do it in a fixed order? map essentially randomises iteration order
for u := range g.Query(g.Ident(), UpdaterType) { for u := range g.Query(g, UpdaterType) {
// Skip g (note g satisfies Updater, so this would infinitely recurse) // Skip g (note g satisfies Updater, so this would infinitely recurse)
if u == g { if u == g {
continue continue
@ -154,10 +154,10 @@ func (g *Game) Parent(c interface{}) interface{} {
// Query looks for components having both a given ancestor and implementing // Query looks for components having both a given ancestor and implementing
// a given behaviour (see Behaviors in interface.go). This only returns sensible // a given behaviour (see Behaviors in interface.go). This only returns sensible
// values after LoadAndPrepare. Note that every component is its own ancestor. // values after LoadAndPrepare. Note that every component is its own ancestor.
func (g *Game) Query(ancestorID string, behaviour reflect.Type) map[interface{}]struct{} { func (g *Game) Query(ancestor interface{}, behaviour reflect.Type) map[interface{}]struct{} {
g.dbmu.RLock() g.dbmu.RLock()
defer g.dbmu.RUnlock() defer g.dbmu.RUnlock()
return g.byAB[abKey{ancestorID, behaviour}] return g.byAB[abKey{ancestor, behaviour}]
} }
// Scan implements Scanner. // Scan implements Scanner.
@ -198,7 +198,7 @@ func preorderWalk(component, parent interface{}, visit func(component, parent in
// value passed to visit when visiting component will be nil. The children will // value passed to visit when visiting component will be nil. The children will
// be visited before the parent. // be visited before the parent.
func PostorderWalk(component interface{}, visit func(component, parent interface{}) error) error { func PostorderWalk(component interface{}, visit func(component, parent interface{}) error) error {
return preorderWalk(component, nil, visit) return postorderWalk(component, nil, visit)
} }
func postorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error { func postorderWalk(component, parent interface{}, visit func(component, parent interface{}) error) error {
@ -223,11 +223,10 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
// Load all the Loaders. // Load all the Loaders.
startLoad := time.Now() startLoad := time.Now()
if err := PreorderWalk(g, func(c, _ interface{}) error { if err := PreorderWalk(g, func(c, _ interface{}) error {
l, ok := c.(Loader) if l, ok := c.(Loader); ok {
if !ok {
return nil
}
return l.Load(assets) return l.Load(assets)
}
return nil
}); err != nil { }); err != nil {
return err return err
} }
@ -247,11 +246,15 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
// Prepare all the Preppers // Prepare all the Preppers
startPrep := time.Now() startPrep := time.Now()
for p := range g.Query(g.Ident(), PrepperType) { if err := PostorderWalk(g, func(c, _ interface{}) error {
if err := p.(Prepper).Prepare(g); err != nil { if p, ok := c.(Prepper); ok {
return p.Prepare(g)
}
return nil
}); err != nil {
return err return err
} }
}
log.Printf("finished preparing in %v", time.Since(startPrep)) log.Printf("finished preparing in %v", time.Since(startPrep))
return nil return nil
} }
@ -270,7 +273,7 @@ func (g *Game) Register(component, parent interface{}) error {
} }
g.dbmu.Lock() g.dbmu.Lock()
defer g.dbmu.Unlock() defer g.dbmu.Unlock()
// walk 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.register)
} }
@ -298,11 +301,7 @@ func (g *Game) register(component, parent interface{}) error {
} }
// TODO: better than O(len(path)^2) time and memory? // TODO: better than O(len(path)^2) time and memory?
for p := component; p != nil; p = g.par[p] { for p := component; p != nil; p = g.par[p] {
id, ok := p.(Identifier) k := abKey{p, b}
if !ok || id.Ident() == "" {
continue
}
k := abKey{id.Ident(), b}
if g.byAB[k] == nil { if g.byAB[k] == nil {
g.byAB[k] = make(map[interface{}]struct{}) g.byAB[k] = make(map[interface{}]struct{})
} }
@ -320,14 +319,12 @@ func (g *Game) Unregister(component interface{}) {
return return
} }
g.dbmu.Lock() g.dbmu.Lock()
postorderWalk(component, nil, func(c, _ interface{}) error { // postorderWalk goes in the right order for unregistering.
g.unregister(c) postorderWalk(component, nil, g.unregister)
return nil
})
g.dbmu.Unlock() g.dbmu.Unlock()
} }
func (g *Game) unregister(component interface{}) { func (g *Game) unregister(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 {
@ -335,17 +332,11 @@ func (g *Game) unregister(component interface{}) {
continue continue
} }
for p := component; p != nil; p = g.par[p] { for p := component; p != nil; p = g.par[p] {
id, ok := p.(Identifier) if k := (abKey{p, b}); g.byAB[k] != nil {
if !ok || id.Ident() == "" {
continue
}
k := abKey{id.Ident(), b}
if g.byAB[k] == nil {
continue
}
delete(g.byAB[k], component) delete(g.byAB[k], component)
} }
} }
}
// unregister from g.par // unregister from g.par
delete(g.par, component) delete(g.par, component)
@ -354,12 +345,13 @@ func (g *Game) unregister(component interface{}) {
if id, ok := component.(Identifier); ok && id.Ident() != "" { if id, ok := component.(Identifier); ok && id.Ident() != "" {
delete(g.byID, id.Ident()) delete(g.byID, id.Ident())
} }
return nil
} }
// --------- Helper stuff --------- // --------- Helper stuff ---------
type abKey struct { type abKey struct {
ancestor string ancestor interface{}
behaviour reflect.Type behaviour reflect.Type
} }

View file

@ -31,7 +31,7 @@ func init() {
gob.Register(&Prism{}) gob.Register(&Prism{})
} }
// PrismMap is a generalised 3D tilemap/wallmap/etc. // PrismMap is a generalised 3D tilemap/wallmap/voxelmap etc.
type PrismMap struct { type PrismMap struct {
ID ID
Disables Disables

View file

@ -127,9 +127,13 @@ func (g *Game) cmdQuery(dst io.Writer, argv []string) {
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1]) fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
} }
ancestor := g.Ident() var ancestor interface{} = g
if len(argv) == 3 { if len(argv) == 3 {
ancestor = argv[2] c := g.Component(argv[2])
if c == nil {
fmt.Fprintf(dst, "Component %q not found\n", argv[2])
}
ancestor = c
} }
x := g.Query(ancestor, behaviour) x := g.Query(ancestor, behaviour)

View file

@ -17,7 +17,7 @@ func Level1() *engine.Scene {
CameraID: "game_camera", CameraID: "game_camera",
Child: &engine.Billboard{ Child: &engine.Billboard{
ID: "bg_image", ID: "bg_image",
Pos: geom.Pt3(-160, -120, -1), Pos: geom.Pt3(-160, -20, -100),
Src: engine.ImageRef{Path: "assets/space.png"}, Src: engine.ImageRef{Path: "assets/space.png"},
}, },
Factor: 0.5, Factor: 0.5,