all in on interfaces

This commit is contained in:
Josh Deprez 2021-08-23 10:34:56 +10:00
parent 7e76032ee2
commit 0f5346bf0e
7 changed files with 71 additions and 29 deletions

View file

@ -126,12 +126,12 @@ func (r *SceneRef) Load(assets fs.FS) error {
return nil return nil
} }
// Scene returns the loaded scene, or nil if not yet loaded.
func (r SceneRef) Scene() *Scene { return r.scene }
// The rest of the methods forward to r.scene, as such they will // The rest of the methods forward to r.scene, as such they will
// panic if the scene isn't loaded. // panic if the scene isn't loaded.
// BoundingRect returns the Bounds from the scene.
func (r SceneRef) BoundingRect() image.Rectangle { return r.scene.BoundingRect() }
// Draw draws the scene. // Draw draws the scene.
func (r SceneRef) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) { func (r SceneRef) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
r.scene.Draw(screen, opts) r.scene.Draw(screen, opts)
@ -140,6 +140,15 @@ func (r SceneRef) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
// DrawOrder returns the value of DrawOrder from the scene. // DrawOrder returns the value of DrawOrder from the scene.
func (r SceneRef) DrawOrder() float64 { return r.scene.DrawOrder() } func (r SceneRef) DrawOrder() float64 { return r.scene.DrawOrder() }
// IsHidden returns the value of IsHidden from the scene.
func (r SceneRef) IsHidden() bool { return r.scene.IsHidden() }
// Hide calls Hide on the scene.
func (r SceneRef) Hide() { r.scene.Hide() }
// Show calls Show on the scene.
func (r SceneRef) Show() { r.scene.Show() }
// Ident returns the value of Ident from the scene. // Ident returns the value of Ident from the scene.
func (r SceneRef) Ident() string { return r.scene.Ident() } func (r SceneRef) Ident() string { return r.scene.Ident() }

View file

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

View file

@ -7,6 +7,11 @@ import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
// Bounder components have a bounding rectangle.
type Bounder interface {
BoundingRect() image.Rectangle
}
// Collider components have tangible form. // Collider components have tangible form.
type Collider interface { type Collider interface {
CollidesWith(image.Rectangle) bool CollidesWith(image.Rectangle) bool
@ -31,6 +36,13 @@ type DrawUpdater interface {
Updater Updater
} }
// Hider components can be hidden.
type Hider interface {
IsHidden() bool
Hide()
Show()
}
// Identifier components have a sense of self. This makes it easier for // Identifier components have a sense of self. This makes it easier for
// components to find and interact with one another. // components to find and interact with one another.
type Identifier interface { type Identifier interface {
@ -75,14 +87,14 @@ type Scener interface {
// } // }
// It seems cleaner to let the engine assert only for the interface it needs at that moment. // It seems cleaner to let the engine assert only for the interface it needs at that moment.
Bounder
Drawer Drawer
DrawOrderer DrawOrderer
Hider
Identifier Identifier
Prepper Prepper
Scanner Scanner
Updater Updater
Scene() *Scene
} }
// Updater components can update themselves. Update is called repeatedly. // Updater components can update themselves. Update is called repeatedly.

View file

@ -1,11 +1,31 @@
package engine package engine
import "image"
// ID implements Identifier directly (as a string value). // ID implements Identifier directly (as a string value).
type ID string type ID string
// Ident returns id as a string. // Ident returns id as a string.
func (id ID) Ident() string { return string(id) } func (id ID) Ident() string { return string(id) }
// Bounds implements Bounder directly (as an image.Rectangle value).
type Bounds image.Rectangle
// BoundingRect returns b as an image.Rectangle.
func (b Bounds) BoundingRect() image.Rectangle { return image.Rectangle(b) }
// Hidden implements Hider directly (as a bool).
type Hidden bool
// IsHidden returns h as a bool.
func (h Hidden) IsHidden() bool { return bool(h) }
// Hide sets h to true.
func (h *Hidden) Hide() { *h = true }
// Show sets h to false.
func (h *Hidden) Show() { *h = false }
// Parallax implements ParallaxScaler directly (as a float64 value). // Parallax implements ParallaxScaler directly (as a float64 value).
type Parallax float64 type Parallax float64

View file

@ -2,7 +2,6 @@ package engine
import ( import (
"encoding/gob" "encoding/gob"
"image"
"math" "math"
"sort" "sort"
@ -18,11 +17,11 @@ func init() {
// Scene manages drawing and updating a bunch of components. // Scene manages drawing and updating a bunch of components.
type Scene struct { type Scene struct {
Bounds image.Rectangle // world coordinates ID
Bounds // world coordinates
Components []interface{} Components []interface{}
Disabled bool Disabled bool
Hidden bool Hidden
ID
ZOrder ZOrder
} }

View file

@ -13,7 +13,7 @@ func init() {
type SolidRect struct { type SolidRect struct {
ID ID
Rect image.Rectangle Bounds
} }
func (s SolidRect) CollidesWith(r image.Rectangle) bool { return s.Rect.Overlaps(r) } func (s SolidRect) CollidesWith(r image.Rectangle) bool { return s.BoundingRect().Overlaps(r) }

14
main.go
View file

@ -51,7 +51,7 @@ func main() {
level1 := &engine.Scene{ level1 := &engine.Scene{
ID: "level_1", ID: "level_1",
Bounds: image.Rect(-32, -32, 320+32, 240+32), Bounds: engine.Bounds(image.Rect(-32, -32, 320+32, 240+32)),
Components: []interface{}{ Components: []interface{}{
&engine.Fill{ &engine.Fill{
Color: color.Gray{100}, Color: color.Gray{100},
@ -72,16 +72,16 @@ func main() {
TileSize: 16, TileSize: 16,
}, },
&engine.SolidRect{ &engine.SolidRect{
ID: "ceiling", ID: "ceiling",
Rect: image.Rect(0, -1, 320, 0), Bounds: engine.Bounds(image.Rect(0, -1, 320, 0)),
}, },
&engine.SolidRect{ &engine.SolidRect{
ID: "left_wall", ID: "left_wall",
Rect: image.Rect(-1, 0, 0, 240), Bounds: engine.Bounds(image.Rect(-1, 0, 0, 240)),
}, },
&engine.SolidRect{ &engine.SolidRect{
ID: "right_wall", ID: "right_wall",
Rect: image.Rect(320, 0, 321, 240), Bounds: engine.Bounds(image.Rect(320, 0, 321, 240)),
}, },
&game.Awakeman{ &game.Awakeman{
CameraID: "game_camera", CameraID: "game_camera",