Refactor Layers into separate type so that Layers can nest
This commit is contained in:
parent
b976b849b7
commit
24d47ba639
3 changed files with 86 additions and 64 deletions
|
@ -1,55 +1,17 @@
|
||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import "github.com/hajimehoshi/ebiten/v2"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Updater is a component that can update. Update is called repeatedly.
|
|
||||||
type Updater interface {
|
|
||||||
Update() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drawer is a component that can draw itself. Draw is called often.
|
|
||||||
// Z is used to reorder components.
|
|
||||||
type Drawer interface {
|
|
||||||
Draw(screen *ebiten.Image, geom ebiten.GeoM)
|
|
||||||
Z() float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Game implements the ebiten methods using a collection of components.
|
// Game implements the ebiten methods using a collection of components.
|
||||||
type Game struct {
|
type Game struct {
|
||||||
ScreenWidth int
|
ScreenWidth int
|
||||||
ScreenHeight int
|
ScreenHeight int
|
||||||
Components []interface{}
|
Layers *Layers
|
||||||
|
|
||||||
needsSort bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update calls Update on all Updater components.
|
// Draw draws the entire thing.
|
||||||
func (g *Game) Update() error {
|
|
||||||
for _, c := range g.Components {
|
|
||||||
if u, ok := c.(Updater); ok {
|
|
||||||
if err := u.Update(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if g.needsSort {
|
|
||||||
g.needsSort = false
|
|
||||||
g.sortDrawers()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw calls Draw on all Drawer components.
|
|
||||||
func (g *Game) Draw(screen *ebiten.Image) {
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
for _, c := range g.Components {
|
g.Layers.Draw(screen, ebiten.GeoM{})
|
||||||
if d, ok := c.(Drawer); ok {
|
|
||||||
d.Draw(screen, ebiten.GeoM{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layout returns the configured screen width/height.
|
// Layout returns the configured screen width/height.
|
||||||
|
@ -57,22 +19,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
|
||||||
return g.ScreenWidth, g.ScreenHeight
|
return g.ScreenWidth, g.ScreenHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNeedsSort tells the game that the Drawers need sorting.
|
// Update just passes the call onto Layers.
|
||||||
// This will be done in the current update.
|
func (g *Game) Update() error {
|
||||||
func (g *Game) SetNeedsSort() {
|
return g.Layers.Update()
|
||||||
g.needsSort = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// sortDrawers sorts the components by Z position.
|
|
||||||
// Non-Drawers are sorted before all Drawers.
|
|
||||||
func (g *Game) sortDrawers() {
|
|
||||||
// Stable sort to avoid z-fighting (among Non-Drawers and equal Drawers)
|
|
||||||
sort.SliceStable(g.Components, func(i, j int) bool {
|
|
||||||
a, aok := g.Components[i].(Drawer)
|
|
||||||
b, bok := g.Components[j].(Drawer)
|
|
||||||
if aok && bok {
|
|
||||||
return a.Z() < b.Z()
|
|
||||||
}
|
|
||||||
return !aok && bok
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
71
engine/layers.go
Normal file
71
engine/layers.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Drawer is a component that can draw itself. Draw is called often.
|
||||||
|
type Drawer interface {
|
||||||
|
Draw(screen *ebiten.Image, geom ebiten.GeoM)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updater is a component that can update. Update is called repeatedly.
|
||||||
|
type Updater interface {
|
||||||
|
Update() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZPositioner is used to reorder layers.
|
||||||
|
type ZPositioner interface {
|
||||||
|
Z() float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layers
|
||||||
|
type Layers struct {
|
||||||
|
Components []interface{}
|
||||||
|
needsSort bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws all layers in order.
|
||||||
|
func (l *Layers) Draw(screen *ebiten.Image, geom ebiten.GeoM) {
|
||||||
|
for _, i := range l.Components {
|
||||||
|
if d, ok := i.(Drawer); ok {
|
||||||
|
d.Draw(screen, geom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNeedsSort informs l that its layers may be out of order.
|
||||||
|
func (l *Layers) SetNeedsSort() {
|
||||||
|
l.needsSort = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortByZ sorts the components by Z position.
|
||||||
|
// Stable sort is used to avoid z-fighting among layers without a Z.
|
||||||
|
func (l *Layers) sortByZ() {
|
||||||
|
sort.SliceStable(l.Components, func(i, j int) bool {
|
||||||
|
a, aok := l.Components[i].(ZPositioner)
|
||||||
|
b, bok := l.Components[j].(ZPositioner)
|
||||||
|
if aok && bok {
|
||||||
|
return a.Z() < b.Z()
|
||||||
|
}
|
||||||
|
return !aok && bok
|
||||||
|
})
|
||||||
|
l.needsSort = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update calls Update on all Updater components.
|
||||||
|
func (l *Layers) Update() error {
|
||||||
|
for _, c := range l.Components {
|
||||||
|
if u, ok := c.(Updater); ok {
|
||||||
|
if err := u.Update(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if l.needsSort {
|
||||||
|
l.sortByZ()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
12
main.go
12
main.go
|
@ -76,15 +76,19 @@ func main() {
|
||||||
TileSize: 16,
|
TileSize: 16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
components := []interface{}{
|
||||||
|
tilemap,
|
||||||
|
engine.PerfDisplay{},
|
||||||
|
}
|
||||||
|
|
||||||
game := &engine.Game{
|
game := &engine.Game{
|
||||||
ScreenHeight: screenHeight,
|
ScreenHeight: screenHeight,
|
||||||
ScreenWidth: screenWidth,
|
ScreenWidth: screenWidth,
|
||||||
Components: []interface{}{
|
Layers: &engine.Layers{
|
||||||
tilemap,
|
Components: components,
|
||||||
engine.PerfDisplay{},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
game.SetNeedsSort()
|
game.Layers.SetNeedsSort()
|
||||||
|
|
||||||
if err := ebiten.RunGame(game); err != nil {
|
if err := ebiten.RunGame(game); err != nil {
|
||||||
log.Fatalf("Game error: %v", err)
|
log.Fatalf("Game error: %v", err)
|
||||||
|
|
Loading…
Reference in a new issue