Scenerise Camera and i am g.Root

This commit is contained in:
Josh Deprez 2021-08-20 16:46:26 +10:00
parent 45e2b300e8
commit af89c01dbd
4 changed files with 31 additions and 19 deletions

View file

@ -25,7 +25,7 @@ func init() {
// Camera ignores Scene.Draw and calls Scene's children's Draw.
type Camera struct {
ID
Scene *Scene
Scene Scener
// Camera controls
Centre image.Point // world coordinates
@ -37,14 +37,14 @@ type Camera struct {
// Draw applies transformations to opts, then calls c.Scene.Draw with it.
func (c *Camera) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
if c.Scene.Hidden {
if c.Scene.Scene().Hidden {
return
}
// The lower bound on zoom is the larger of
// { (ScreenWidth / BoundsWidth), (ScreenHeight / BoundsHeight) }
zoom := c.Zoom
sz := c.Scene.Bounds.Size()
sz := c.Scene.Scene().Bounds.Size()
if z := float64(c.game.ScreenWidth) / float64(sz.X); zoom < z {
zoom = z
}
@ -57,17 +57,17 @@ func (c *Camera) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
// Camera frame currently Rectangle{ centre ± (screen/(2*zoom)) }.
sw2, sh2 := float64(c.game.ScreenWidth/2), float64(c.game.ScreenHeight/2)
swz, shz := int(sw2/zoom), int(sh2/zoom)
if centre.X-swz < c.Scene.Bounds.Min.X {
centre.X = c.Scene.Bounds.Min.X + swz
if centre.X-swz < c.Scene.Scene().Bounds.Min.X {
centre.X = c.Scene.Scene().Bounds.Min.X + swz
}
if centre.Y-shz < c.Scene.Bounds.Min.Y {
centre.Y = c.Scene.Bounds.Min.Y + shz
if centre.Y-shz < c.Scene.Scene().Bounds.Min.Y {
centre.Y = c.Scene.Scene().Bounds.Min.Y + shz
}
if centre.X+swz > c.Scene.Bounds.Max.X {
centre.X = c.Scene.Bounds.Max.X - swz
if centre.X+swz > c.Scene.Scene().Bounds.Max.X {
centre.X = c.Scene.Scene().Bounds.Max.X - swz
}
if centre.Y+shz > c.Scene.Bounds.Max.Y {
centre.Y = c.Scene.Bounds.Max.Y - shz
if centre.Y+shz > c.Scene.Scene().Bounds.Max.Y {
centre.Y = c.Scene.Scene().Bounds.Max.Y - shz
}
// Apply other options
@ -101,7 +101,7 @@ func (c *Camera) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
}
// Update passes the call to c.Scene.
func (c *Camera) Update() error { return c.Scene.Update() }
func (c *Camera) Update() error { return c.Scene.Scene().Update() }
// Scan returns the only child (c.Scene).
func (c *Camera) Scan() []interface{} { return []interface{}{c.Scene} }

View file

@ -11,17 +11,19 @@ func init() {
}
// Game implements the ebiten methods using a collection of components.
// One component must be the designated root component - usually a
// scene of some kind.
type Game struct {
ScreenWidth int
ScreenHeight int
Scener
Root DrawUpdater // typically a *Scene or SceneRef though
componentsByID map[string]interface{}
}
// Draw draws the entire thing, with default draw options.
func (g *Game) Draw(screen *ebiten.Image) {
g.Scene().Draw(screen, ebiten.DrawImageOptions{})
g.Root.Draw(screen, ebiten.DrawImageOptions{})
}
// Layout returns the configured screen width/height.
@ -29,6 +31,9 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
return g.ScreenWidth, g.ScreenHeight
}
// Update updates the scene.
func (g *Game) Update() error { return g.Root.Update() }
// RegisterComponent tells the game there is a new component. Currently this is
// only necessary for components with IDs.
func (g *Game) RegisterComponent(c interface{}) {
@ -61,7 +66,7 @@ func (g *Game) UnregisterComponent(c interface{}) {
func (g *Game) Component(id string) interface{} { return g.componentsByID[id] }
// Scan implements Scanner.
func (g *Game) Scan() []interface{} { return []interface{}{g.Scener} }
func (g *Game) Scan() []interface{} { return []interface{}{g.Root} }
// Walk calls v with every component reachable from c via Scan, recursively,
// for as long as visit returns nil.
@ -84,7 +89,7 @@ func Walk(c interface{}, v func(interface{}) error) error {
// Load calls Load on all Loaders reachable via Scan (using Walk).
// It stops on the first error.
func (g *Game) Load() error {
return Walk(g.Scener, func(c interface{}) error {
return Walk(g.Root, func(c interface{}) error {
l, ok := c.(Loader)
if !ok {
return nil
@ -99,11 +104,11 @@ func (g *Game) Load() error {
// fastidiously calling RegisterComponent/UnregisterComponent).
func (g *Game) Prepare() {
g.componentsByID = make(map[string]interface{})
Walk(g.Scener, func(c interface{}) error {
Walk(g.Root, func(c interface{}) error {
g.RegisterComponent(c)
return nil
})
Walk(g.Scener, func(c interface{}) error {
Walk(g.Root, func(c interface{}) error {
if p, ok := c.(Prepper); ok {
p.Prepare(g)
}

View file

@ -23,6 +23,13 @@ type DrawOrderer interface {
DrawOrder() float64
}
// DrawUpdater components can be both drawn and updated.
// Same comments as for Drawer and Updater.
type DrawUpdater interface {
Drawer
Updater
}
// Identifier components have a sense of self. This makes it easier for
// components to find and interact with one another.
type Identifier interface {

View file

@ -105,7 +105,7 @@ func main() {
game := &engine.Game{
ScreenHeight: 240,
ScreenWidth: 320,
Scener: &engine.Scene{
Root: &engine.Scene{
ID: "root",
Components: []interface{}{
&engine.GobDumper{