query
This commit is contained in:
parent
cf34a7785c
commit
12e3254a34
4 changed files with 142 additions and 12 deletions
|
@ -25,7 +25,7 @@ type Actor struct {
|
|||
Pos image.Point
|
||||
Size image.Point
|
||||
|
||||
collisionDomain interface{}
|
||||
collisionDomain []Collider
|
||||
xRem, yRem float64
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ func (a *Actor) BoundingRect() image.Rectangle { return image.Rectangle{a.Pos, a
|
|||
|
||||
func (a *Actor) CollidesAt(p image.Point) bool {
|
||||
bounds := image.Rectangle{Min: p, Max: p.Add(a.Size)}
|
||||
return nil != Walk(a.collisionDomain, func(c interface{}, _ []interface{}) error {
|
||||
/*return nil != Walk(a.collisionDomain, func(c interface{}, _ []interface{}) error {
|
||||
coll, ok := c.(Collider)
|
||||
if !ok {
|
||||
return nil
|
||||
|
@ -42,7 +42,13 @@ func (a *Actor) CollidesAt(p image.Point) bool {
|
|||
return Collision{With: coll}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})*/
|
||||
for _, c := range a.collisionDomain {
|
||||
if c.CollidesWith(bounds) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Actor) MoveX(dx float64, onCollide func()) {
|
||||
|
@ -86,7 +92,13 @@ func (a *Actor) MoveY(dy float64, onCollide func()) {
|
|||
}
|
||||
|
||||
func (a *Actor) Prepare(g *Game) error {
|
||||
a.collisionDomain = g.Component(a.CollisionDomain)
|
||||
//a.collisionDomain = g.Component(a.CollisionDomain)
|
||||
|
||||
cs := g.Query(g.Component(a.CollisionDomain), ColliderType)
|
||||
a.collisionDomain = make([]Collider, 0, len(cs))
|
||||
for _, c := range cs {
|
||||
a.collisionDomain = append(a.collisionDomain, c.(Collider))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/gob"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
@ -24,7 +25,13 @@ type Game struct {
|
|||
Root DrawUpdater // typically a *Scene or SceneRef though
|
||||
|
||||
dbmu sync.RWMutex
|
||||
db map[string]Identifier // Components by ID
|
||||
db map[string]Identifier // Named components by ID
|
||||
dex map[dexKey][]interface{} // Ancestor/behaviour index
|
||||
}
|
||||
|
||||
type dexKey struct {
|
||||
ancestor interface{}
|
||||
behaviour reflect.Type
|
||||
}
|
||||
|
||||
// Draw draws the entire thing, with default draw options.
|
||||
|
@ -48,7 +55,22 @@ func (g *Game) Update() error {
|
|||
return g.Root.Update()
|
||||
}
|
||||
|
||||
func (g *Game) registerComponent(c interface{}, p []interface{}) error {
|
||||
func (g *Game) registerComponent(c interface{}, path []interface{}) error {
|
||||
// register in g.dex
|
||||
for _, b := range Behaviours {
|
||||
ct := reflect.TypeOf(c)
|
||||
if !ct.Implements(b) {
|
||||
continue
|
||||
}
|
||||
k := dexKey{c, b}
|
||||
g.dex[k] = append(g.dex[k], c)
|
||||
for _, p := range path {
|
||||
k := dexKey{p, b}
|
||||
g.dex[k] = append(g.dex[k], c)
|
||||
}
|
||||
}
|
||||
|
||||
// register in g.db
|
||||
i, ok := c.(Identifier)
|
||||
if !ok {
|
||||
return nil
|
||||
|
@ -65,12 +87,22 @@ func (g *Game) registerComponent(c interface{}, p []interface{}) error {
|
|||
}
|
||||
|
||||
// Component returns the component with a given ID, or nil if there is none.
|
||||
// This only returns sensible values after LoadAndPrepare.
|
||||
func (g *Game) Component(id string) Identifier {
|
||||
g.dbmu.RLock()
|
||||
defer g.dbmu.RUnlock()
|
||||
return g.db[id]
|
||||
}
|
||||
|
||||
// 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(ancestor interface{}, behaviour reflect.Type) []interface{} {
|
||||
g.dbmu.RLock()
|
||||
defer g.dbmu.RUnlock()
|
||||
return g.dex[dexKey{ancestor, behaviour}]
|
||||
}
|
||||
|
||||
// Scan implements Scanner.
|
||||
func (g *Game) Scan() []interface{} { return []interface{}{g.Root} }
|
||||
|
||||
|
@ -97,11 +129,9 @@ func walk(c interface{}, p []interface{}, v func(interface{}, []interface{}) err
|
|||
return nil
|
||||
}
|
||||
|
||||
// LoadAndPrepare first calls Load on all Loaders. Once loading is complete,
|
||||
// it builds the component database and then calls Prepare on every Preparer.
|
||||
// You must call Prepare before any calls
|
||||
// to Component. You may call Prepare again (e.g. as an alternative to
|
||||
// fastidiously calling RegisterComponent/UnregisterComponent).
|
||||
// LoadAndPrepare first calls Load on all Loaders. Once loading is complete, it
|
||||
// builds the component databases and then calls Prepare on every Preparer.
|
||||
// LoadAndPrepare must be called before any calls to Component or Query.
|
||||
func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
||||
// Load all the Loaders
|
||||
if err := Walk(g, func(c interface{}, _ []interface{}) error {
|
||||
|
@ -117,6 +147,7 @@ func (g *Game) LoadAndPrepare(assets fs.FS) error {
|
|||
// Build the component databases
|
||||
g.dbmu.Lock()
|
||||
g.db = make(map[string]Identifier)
|
||||
g.dex = make(map[dexKey][]interface{})
|
||||
if err := Walk(g, g.registerComponent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -3,10 +3,53 @@ package engine
|
|||
import (
|
||||
"image"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
// Reflection types used for queries... Is there a better way?
|
||||
var (
|
||||
// TypeOf(pointer to interface).Elem() is "idiomatic" -
|
||||
// see https://pkg.go.dev/reflect#example-TypeOf
|
||||
AnimerType = reflect.TypeOf((*Animer)(nil)).Elem()
|
||||
BounderType = reflect.TypeOf((*Bounder)(nil)).Elem()
|
||||
ColliderType = reflect.TypeOf((*Collider)(nil)).Elem()
|
||||
DisablerType = reflect.TypeOf((*Disabler)(nil)).Elem()
|
||||
DrawerType = reflect.TypeOf((*Drawer)(nil)).Elem()
|
||||
DrawOrdererType = reflect.TypeOf((*DrawOrderer)(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()
|
||||
ParallaxScalerType = reflect.TypeOf((*ParallaxScaler)(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()
|
||||
UpdaterType = reflect.TypeOf((*Updater)(nil)).Elem()
|
||||
|
||||
// Behaviours lists all the behaviours that can be queried with Game.Query.
|
||||
Behaviours = []reflect.Type{
|
||||
AnimerType,
|
||||
BounderType,
|
||||
ColliderType,
|
||||
DisablerType,
|
||||
DrawerType,
|
||||
DrawOrdererType,
|
||||
DrawUpdaterType,
|
||||
HiderType,
|
||||
IdentifierType,
|
||||
LoaderType,
|
||||
ParallaxScalerType,
|
||||
PrepperType,
|
||||
ScannerType,
|
||||
ScenerType,
|
||||
SaverType,
|
||||
UpdaterType,
|
||||
}
|
||||
)
|
||||
|
||||
// Animer components have a current frame index.
|
||||
type Animer interface {
|
||||
Updater
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -33,6 +34,8 @@ func (g *Game) REPL(src io.Reader, dst io.Writer, assets fs.FS) error {
|
|||
g.cmdReload(dst, assets)
|
||||
case "tree":
|
||||
g.cmdTree(dst, argv)
|
||||
case "query":
|
||||
g.cmdQuery(dst, argv)
|
||||
}
|
||||
fmt.Fprint(dst, prompt)
|
||||
}
|
||||
|
@ -75,7 +78,7 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
|
|||
if len(argv) < 1 || len(argv) > 2 {
|
||||
fmt.Println(dst, "Usage: tree [ID]")
|
||||
}
|
||||
var c interface{} = g
|
||||
c := interface{}(g)
|
||||
if len(argv) == 2 { // subtree
|
||||
id := argv[1]
|
||||
c = g.Component(id)
|
||||
|
@ -98,3 +101,44 @@ func (g *Game) cmdTree(dst io.Writer, argv []string) {
|
|||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (g *Game) cmdQuery(dst io.Writer, argv []string) {
|
||||
if len(argv) < 2 || len(argv) > 3 {
|
||||
fmt.Fprintln(dst, "Usage: query BEHAVIOUR [ANCESTOR_ID]")
|
||||
}
|
||||
|
||||
var behav reflect.Type
|
||||
for _, b := range Behaviours {
|
||||
if b.Name() == argv[1] {
|
||||
behav = b
|
||||
}
|
||||
}
|
||||
if behav == nil {
|
||||
fmt.Fprintf(dst, "Unknown behaviour %q\n", argv[1])
|
||||
}
|
||||
|
||||
ances := interface{}(g)
|
||||
if len(argv) == 3 {
|
||||
id := argv[2]
|
||||
ances = g.Component(id)
|
||||
if ances == nil {
|
||||
fmt.Fprintf(dst, "Component %q not found\n", id)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
x := g.Query(ances, behav)
|
||||
if len(x) == 0 {
|
||||
fmt.Fprintln(dst, "No results")
|
||||
return
|
||||
}
|
||||
|
||||
for _, c := range x {
|
||||
i, ok := c.(Identifier)
|
||||
if ok {
|
||||
fmt.Fprintf(dst, "%T %s\n", c, i.Ident())
|
||||
} else {
|
||||
fmt.Fprintf(dst, "%T\n", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue