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).
func (a *Actor) CollidesAt(p geom.Int3) bool {
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) {
return true
}

View file

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

View file

@ -82,7 +82,7 @@ func (g *Game) Update() error {
// Update everything that is not disabled.
// 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)
if u == g {
continue
@ -154,10 +154,10 @@ func (g *Game) Parent(c interface{}) interface{} {
// Query looks for components having both a given ancestor and implementing
// a given behaviour (see Behaviors in interface.go). This only returns sensible
// 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()
defer g.dbmu.RUnlock()
return g.byAB[abKey{ancestorID, behaviour}]
return g.byAB[abKey{ancestor, behaviour}]
}
// 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
// be visited before the parent.
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 {
@ -223,11 +223,10 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
// Load all the Loaders.
startLoad := time.Now()
if err := PreorderWalk(g, func(c, _ interface{}) error {
l, ok := c.(Loader)
if !ok {
return nil
if l, ok := c.(Loader); ok {
return l.Load(assets)
}
return l.Load(assets)
return nil
}); err != nil {
return err
}
@ -247,11 +246,15 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
// Prepare all the Preppers
startPrep := time.Now()
for p := range g.Query(g.Ident(), PrepperType) {
if err := p.(Prepper).Prepare(g); err != nil {
return err
if err := PostorderWalk(g, func(c, _ interface{}) error {
if p, ok := c.(Prepper); ok {
return p.Prepare(g)
}
return nil
}); err != nil {
return err
}
log.Printf("finished preparing in %v", time.Since(startPrep))
return nil
}
@ -270,7 +273,7 @@ func (g *Game) Register(component, parent interface{}) error {
}
g.dbmu.Lock()
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)
}
@ -298,11 +301,7 @@ func (g *Game) register(component, parent interface{}) error {
}
// TODO: better than O(len(path)^2) time and memory?
for p := component; p != nil; p = g.par[p] {
id, ok := p.(Identifier)
if !ok || id.Ident() == "" {
continue
}
k := abKey{id.Ident(), b}
k := abKey{p, b}
if g.byAB[k] == nil {
g.byAB[k] = make(map[interface{}]struct{})
}
@ -320,14 +319,12 @@ func (g *Game) Unregister(component interface{}) {
return
}
g.dbmu.Lock()
postorderWalk(component, nil, func(c, _ interface{}) error {
g.unregister(c)
return nil
})
// postorderWalk goes in the right order for unregistering.
postorderWalk(component, nil, g.unregister)
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
ct := reflect.TypeOf(component)
for _, b := range Behaviours {
@ -335,15 +332,9 @@ func (g *Game) unregister(component interface{}) {
continue
}
for p := component; p != nil; p = g.par[p] {
id, ok := p.(Identifier)
if !ok || id.Ident() == "" {
continue
if k := (abKey{p, b}); g.byAB[k] != nil {
delete(g.byAB[k], component)
}
k := abKey{id.Ident(), b}
if g.byAB[k] == nil {
continue
}
delete(g.byAB[k], component)
}
}
@ -354,12 +345,13 @@ func (g *Game) unregister(component interface{}) {
if id, ok := component.(Identifier); ok && id.Ident() != "" {
delete(g.byID, id.Ident())
}
return nil
}
// --------- Helper stuff ---------
type abKey struct {
ancestor string
ancestor interface{}
behaviour reflect.Type
}

View file

@ -31,7 +31,7 @@ func init() {
gob.Register(&Prism{})
}
// PrismMap is a generalised 3D tilemap/wallmap/etc.
// PrismMap is a generalised 3D tilemap/wallmap/voxelmap etc.
type PrismMap struct {
ID
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])
}
ancestor := g.Ident()
var ancestor interface{} = g
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)

View file

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