game does everything: updates
This commit is contained in:
parent
46c6a72fdd
commit
3a9109ae20
13 changed files with 163 additions and 220 deletions
BIN
cpuprofile.pprof
BIN
cpuprofile.pprof
Binary file not shown.
|
@ -31,9 +31,6 @@ type Billboard struct {
|
||||||
|
|
||||||
// Draw draws the image.
|
// Draw draws the image.
|
||||||
func (b *Billboard) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
func (b *Billboard) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
if b.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var geom ebiten.GeoM
|
var geom ebiten.GeoM
|
||||||
geom.Translate(float64(b.Pos.X), float64(b.Pos.Y))
|
geom.Translate(float64(b.Pos.X), float64(b.Pos.Y))
|
||||||
geom.Concat(opts.GeoM)
|
geom.Concat(opts.GeoM)
|
||||||
|
|
|
@ -29,8 +29,5 @@ type Fill struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Fill) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
func (f *Fill) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
if f.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
screen.Fill(opts.ColorM.Apply(f.Color))
|
screen.Fill(opts.ColorM.Apply(f.Color))
|
||||||
}
|
}
|
||||||
|
|
223
engine/game.go
223
engine/game.go
|
@ -13,8 +13,6 @@ import (
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const gameDoesEverything = true
|
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
Disabler
|
Disabler
|
||||||
Hider
|
Hider
|
||||||
|
@ -40,99 +38,73 @@ type Game struct {
|
||||||
Hidden
|
Hidden
|
||||||
ScreenWidth int
|
ScreenWidth int
|
||||||
ScreenHeight int
|
ScreenHeight int
|
||||||
Root DrawUpdater // typically a *Scene or SceneRef though
|
Root interface{} // typically a *Scene or SceneRef though
|
||||||
|
|
||||||
dbmu sync.RWMutex
|
dbmu sync.RWMutex
|
||||||
byID map[string]Identifier // Named components by ID
|
byID map[string]Identifier // Named components by ID
|
||||||
byAB map[abKey]map[interface{}]struct{} // Ancestor/behaviour index
|
byAB map[abKey]map[interface{}]struct{} // Ancestor/behaviour index
|
||||||
drawList drawers // draw list :|
|
drawList drawList // draw list :|
|
||||||
par map[interface{}]interface{} // par[x] is parent of x
|
par map[interface{}]interface{} // par[x] is parent of x
|
||||||
}
|
}
|
||||||
|
|
||||||
type abKey struct {
|
// Draw draws everything.
|
||||||
ancestor string
|
|
||||||
behaviour reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Drawer = tombstone{}
|
|
||||||
|
|
||||||
type tombstone struct{}
|
|
||||||
|
|
||||||
func (tombstone) Draw(*ebiten.Image, ebiten.DrawImageOptions) {}
|
|
||||||
|
|
||||||
func (tombstone) DrawOrder() float64 { return math.Inf(1) }
|
|
||||||
|
|
||||||
type drawers []Drawer
|
|
||||||
|
|
||||||
func (d drawers) Less(i, j int) bool { return d[i].DrawOrder() < d[j].DrawOrder() }
|
|
||||||
func (d drawers) Len() int { return len(d) }
|
|
||||||
func (d drawers) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
|
||||||
|
|
||||||
func concatOpts(a, b ebiten.DrawImageOptions) ebiten.DrawImageOptions {
|
|
||||||
a.ColorM.Concat(b.ColorM)
|
|
||||||
a.GeoM.Concat(b.GeoM)
|
|
||||||
a.CompositeMode = b.CompositeMode
|
|
||||||
a.Filter = b.Filter
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw draws the entire thing, with default draw options.
|
|
||||||
func (g *Game) Draw(screen *ebiten.Image) {
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
if g.Hidden {
|
if g.Hidden {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if gameDoesEverything {
|
// Hiding a parent component should hide the child objects, and the
|
||||||
type state struct {
|
// transform applied to a child should be the cumulative transform of all
|
||||||
hidden bool
|
// parents as well.
|
||||||
opts ebiten.DrawImageOptions
|
// accum memoises the results for each component.
|
||||||
|
type state struct {
|
||||||
|
hidden bool
|
||||||
|
opts ebiten.DrawImageOptions
|
||||||
|
}
|
||||||
|
accum := map[interface{}]state{
|
||||||
|
g: {hidden: false},
|
||||||
|
}
|
||||||
|
// Draw everything in g.drawList, where not hidden (itself or any parent)
|
||||||
|
for _, d := range g.drawList {
|
||||||
|
// Is d hidden itself?
|
||||||
|
if h, ok := d.(Hider); ok && h.IsHidden() {
|
||||||
|
accum[d] = state{hidden: true}
|
||||||
|
continue // skip drawing
|
||||||
}
|
}
|
||||||
accum := map[interface{}]state{
|
// Walk up g.par to find the nearest state in accum.
|
||||||
g: {hidden: false},
|
var st state
|
||||||
|
stack := []interface{}{d}
|
||||||
|
for p := g.par[d]; ; p = g.par[p] {
|
||||||
|
if s, found := accum[p]; found {
|
||||||
|
st = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
stack = append(stack, p)
|
||||||
}
|
}
|
||||||
// draw everything
|
// Unwind the stack, accumulating state along the way.
|
||||||
for _, d := range g.drawList {
|
for len(stack) > 0 {
|
||||||
// is d directly hidden?
|
l1 := len(stack) - 1
|
||||||
if h, ok := d.(Hider); ok && h.IsHidden() {
|
p := stack[l1]
|
||||||
accum[d] = state{hidden: true}
|
stack = stack[:l1]
|
||||||
continue // skip drawing
|
if h, ok := p.(Hider); ok {
|
||||||
|
st.hidden = st.hidden || h.IsHidden()
|
||||||
}
|
}
|
||||||
// walk up g.par to find the nearest parent state in accum
|
|
||||||
var st state
|
|
||||||
stack := []interface{}{d}
|
|
||||||
for p := g.par[d]; ; p = g.par[p] {
|
|
||||||
if s, found := accum[p]; found {
|
|
||||||
st = s
|
|
||||||
break
|
|
||||||
}
|
|
||||||
stack = append(stack, p)
|
|
||||||
}
|
|
||||||
// unwind the stack, accumulating state along the way
|
|
||||||
for len(stack) > 0 {
|
|
||||||
l1 := len(stack) - 1
|
|
||||||
p := stack[l1]
|
|
||||||
stack = stack[:l1]
|
|
||||||
if h, ok := p.(Hider); ok {
|
|
||||||
st.hidden = st.hidden || h.IsHidden()
|
|
||||||
}
|
|
||||||
if st.hidden {
|
|
||||||
accum[p] = state{hidden: true}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if t, ok := p.(Transformer); ok {
|
|
||||||
st.opts = concatOpts(t.Transform(), st.opts)
|
|
||||||
}
|
|
||||||
accum[p] = st
|
|
||||||
}
|
|
||||||
|
|
||||||
// now...skip drawing if hidden :P
|
|
||||||
if st.hidden {
|
if st.hidden {
|
||||||
|
accum[p] = state{hidden: true}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
d.Draw(screen, st.opts)
|
// p is not hidden, so compute its cumulative transform.
|
||||||
|
if t, ok := p.(Transformer); ok {
|
||||||
|
st.opts = concatOpts(t.Transform(), st.opts)
|
||||||
|
}
|
||||||
|
accum[p] = st
|
||||||
}
|
}
|
||||||
} else { // !gameDoesEverything
|
|
||||||
g.Root.Draw(screen, ebiten.DrawImageOptions{})
|
// Skip drawing if hidden.
|
||||||
|
if st.hidden {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
d.Draw(screen, st.opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,23 +113,69 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
|
||||||
return g.ScreenWidth, g.ScreenHeight
|
return g.ScreenWidth, g.ScreenHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates the scene.
|
// Update updates everything.
|
||||||
func (g *Game) Update() error {
|
func (g *Game) Update() error {
|
||||||
if g.Disabled {
|
if g.Disabled {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := g.Root.Update(); err != nil {
|
// Need to do a similar trick for Draw: disabling a parent object should
|
||||||
return err
|
// disable the child objects.
|
||||||
|
// accum memoises the disabled state for each component.
|
||||||
|
accum := map[interface{}]bool{
|
||||||
|
g: false,
|
||||||
}
|
}
|
||||||
if gameDoesEverything {
|
|
||||||
// sort the draw list (yes, on every frame)
|
// Update everything that is not disabled.
|
||||||
sort.Stable(g.drawList)
|
for u := range g.Query(g.Ident(), UpdaterType) {
|
||||||
// slice out any tombstones
|
// Skip g (note g satisfies Updater, so this would infinitely recurse)
|
||||||
for i := len(g.drawList) - 1; i >= 0; i-- {
|
if u == g {
|
||||||
if g.drawList[i] == (tombstone{}) {
|
continue
|
||||||
g.drawList = g.drawList[:i]
|
}
|
||||||
|
|
||||||
|
// Is u disabled itself?
|
||||||
|
if d, ok := u.(Disabler); ok && d.IsDisabled() {
|
||||||
|
accum[u] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk up g.par to find the nearest state in accum.
|
||||||
|
var st bool
|
||||||
|
stack := []interface{}{u}
|
||||||
|
for p := g.par[u]; ; p = g.par[p] {
|
||||||
|
if s, found := accum[p]; found {
|
||||||
|
st = s
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
stack = append(stack, p)
|
||||||
|
}
|
||||||
|
// Unwind the stack, accumulating state along the way.
|
||||||
|
for len(stack) > 0 {
|
||||||
|
l1 := len(stack) - 1
|
||||||
|
p := stack[l1]
|
||||||
|
stack = stack[:l1]
|
||||||
|
if d, ok := p.(Disabler); ok {
|
||||||
|
st = st || d.IsDisabled()
|
||||||
|
}
|
||||||
|
accum[p] = st
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip updating if disabled.
|
||||||
|
if st {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.(Updater).Update(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the draw list (on every frame - this isn't as bad as it sounds)
|
||||||
|
sort.Stable(g.drawList)
|
||||||
|
// Truncate tombstones from the end.
|
||||||
|
for i := len(g.drawList) - 1; i >= 0; i-- {
|
||||||
|
if g.drawList[i] == (tombstone{}) {
|
||||||
|
g.drawList = g.drawList[:i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -360,3 +378,36 @@ func (g *Game) unregister(component interface{}) {
|
||||||
}
|
}
|
||||||
delete(g.byID, i.Ident())
|
delete(g.byID, i.Ident())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------- Helper types ---------
|
||||||
|
|
||||||
|
type abKey struct {
|
||||||
|
ancestor string
|
||||||
|
behaviour reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Drawer = tombstone{}
|
||||||
|
|
||||||
|
type tombstone struct{}
|
||||||
|
|
||||||
|
func (tombstone) Draw(*ebiten.Image, ebiten.DrawImageOptions) {}
|
||||||
|
|
||||||
|
func (tombstone) DrawOrder() float64 { return math.Inf(1) }
|
||||||
|
|
||||||
|
type drawList []Drawer
|
||||||
|
|
||||||
|
func (d drawList) Less(i, j int) bool { return d[i].DrawOrder() < d[j].DrawOrder() }
|
||||||
|
func (d drawList) Len() int { return len(d) }
|
||||||
|
func (d drawList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||||
|
|
||||||
|
func concatOpts(a, b ebiten.DrawImageOptions) ebiten.DrawImageOptions {
|
||||||
|
a.ColorM.Concat(b.ColorM)
|
||||||
|
a.GeoM.Concat(b.GeoM)
|
||||||
|
if b.CompositeMode != 0 {
|
||||||
|
a.CompositeMode = b.CompositeMode
|
||||||
|
}
|
||||||
|
if b.Filter != 0 {
|
||||||
|
a.Filter = b.Filter
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
|
@ -151,13 +151,10 @@ type Scener interface {
|
||||||
|
|
||||||
Bounder
|
Bounder
|
||||||
Disabler
|
Disabler
|
||||||
Drawer
|
|
||||||
Hider
|
Hider
|
||||||
Identifier
|
Identifier
|
||||||
Prepper
|
|
||||||
Scanner
|
Scanner
|
||||||
Transformer
|
Transformer
|
||||||
Updater
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Saver components can be saved to disk.
|
// Saver components can be saved to disk.
|
||||||
|
|
|
@ -50,7 +50,7 @@ type ZOrder float64
|
||||||
// DrawOrder returns z as a float64.
|
// DrawOrder returns z as a float64.
|
||||||
func (z ZOrder) DrawOrder() float64 { return float64(z) }
|
func (z ZOrder) DrawOrder() float64 { return float64(z) }
|
||||||
|
|
||||||
// Some math helpers
|
// ---------- Some math helpers ----------
|
||||||
|
|
||||||
func mul2(p, q image.Point) image.Point {
|
func mul2(p, q image.Point) image.Point {
|
||||||
p.X *= q.X
|
p.X *= q.X
|
||||||
|
|
|
@ -2,8 +2,6 @@ package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"math"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
)
|
)
|
||||||
|
@ -26,9 +24,10 @@ type Scene struct {
|
||||||
ZOrder
|
ZOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Draw draws all components in order.
|
// Draw draws all components in order.
|
||||||
func (s *Scene) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
func (s *Scene) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
if s.Hidden || gameDoesEverything {
|
if s.Hidden {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.Camera == nil {
|
if s.Camera == nil {
|
||||||
|
@ -105,6 +104,7 @@ func (s *Scene) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
d.Draw(screen, opts)
|
d.Draw(screen, opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Transform returns the camera transform
|
// Transform returns the camera transform
|
||||||
func (s *Scene) Transform() ebiten.DrawImageOptions {
|
func (s *Scene) Transform() ebiten.DrawImageOptions {
|
||||||
|
@ -114,26 +114,6 @@ func (s *Scene) Transform() ebiten.DrawImageOptions {
|
||||||
return s.Camera.Transform()
|
return s.Camera.Transform()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare does an initial Z-order sort.
|
|
||||||
func (s *Scene) Prepare(game *Game) error {
|
|
||||||
s.sortByDrawOrder()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sortByDrawOrder sorts the components by Z position.
|
|
||||||
// Everything without a Z sorts first. Stable sort is used to avoid Z-fighting
|
|
||||||
// (among layers without a Z, or those with equal Z).
|
|
||||||
func (s *Scene) sortByDrawOrder() {
|
|
||||||
sort.SliceStable(s.Components, func(i, j int) bool {
|
|
||||||
a, aok := s.Components[i].(Drawer)
|
|
||||||
b, bok := s.Components[j].(Drawer)
|
|
||||||
if aok && bok {
|
|
||||||
return a.DrawOrder() < b.DrawOrder()
|
|
||||||
}
|
|
||||||
return !aok && bok
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan returns all immediate subcomponents (including the camera, if not nil).
|
// Scan returns all immediate subcomponents (including the camera, if not nil).
|
||||||
func (s *Scene) Scan() []interface{} {
|
func (s *Scene) Scan() []interface{} {
|
||||||
if s.Camera != nil {
|
if s.Camera != nil {
|
||||||
|
@ -141,34 +121,3 @@ func (s *Scene) Scan() []interface{} {
|
||||||
}
|
}
|
||||||
return s.Components
|
return s.Components
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update calls Update on all Updater components.
|
|
||||||
func (s *Scene) Update() error {
|
|
||||||
if s.Disabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range s.Components {
|
|
||||||
// Update each updater in turn
|
|
||||||
if u, ok := c.(Updater); ok {
|
|
||||||
if err := u.Update(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check if the updates put the components out of order; if so, sort
|
|
||||||
cz := -math.MaxFloat64 // fun fact: this is min float64
|
|
||||||
for _, c := range s.Components {
|
|
||||||
z, ok := c.(Drawer)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if t := z.DrawOrder(); t >= cz {
|
|
||||||
cz = t
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.sortByDrawOrder()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -62,11 +62,6 @@ func (r SceneRef) Disable() { r.scene.Disable() }
|
||||||
// Enable calls Enable on the scene.
|
// Enable calls Enable on the scene.
|
||||||
func (r SceneRef) Enable() { r.scene.Enable() }
|
func (r SceneRef) Enable() { r.scene.Enable() }
|
||||||
|
|
||||||
// Draw draws the scene.
|
|
||||||
func (r SceneRef) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
|
||||||
r.scene.Draw(screen, opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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() }
|
||||||
|
|
||||||
|
@ -82,14 +77,8 @@ 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() }
|
||||||
|
|
||||||
// Prepare prepares the scene.
|
|
||||||
func (r SceneRef) Prepare(g *Game) error { return r.scene.Prepare(g) }
|
|
||||||
|
|
||||||
// Scan returns the components in the scene.
|
// Scan returns the components in the scene.
|
||||||
func (r SceneRef) Scan() []interface{} { return r.scene.Scan() }
|
func (r SceneRef) Scan() []interface{} { return r.scene.Scan() }
|
||||||
|
|
||||||
// Transform returns the value of Transform from the scene.
|
// Transform returns the value of Transform from the scene.
|
||||||
func (r SceneRef) Transform() ebiten.DrawImageOptions { return r.scene.Transform() }
|
func (r SceneRef) Transform() ebiten.DrawImageOptions { return r.scene.Transform() }
|
||||||
|
|
||||||
// Update updates the scene.
|
|
||||||
func (r SceneRef) Update() error { return r.scene.Update() }
|
|
||||||
|
|
|
@ -16,4 +16,6 @@ type SolidRect struct {
|
||||||
Bounds
|
Bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SolidRect) CollidesWith(r image.Rectangle) bool { return s.BoundingRect().Overlaps(r) }
|
func (s SolidRect) CollidesWith(r image.Rectangle) bool {
|
||||||
|
return s.BoundingRect().Overlaps(r)
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func init() {
|
||||||
|
|
||||||
// Sprite combines an Actor with the ability to Draw from a single spritesheet.
|
// Sprite combines an Actor with the ability to Draw from a single spritesheet.
|
||||||
type Sprite struct {
|
type Sprite struct {
|
||||||
Actor
|
Actor Actor
|
||||||
FrameOffset image.Point
|
FrameOffset image.Point
|
||||||
Hidden
|
Hidden
|
||||||
Sheet Sheet
|
Sheet Sheet
|
||||||
|
@ -30,10 +30,7 @@ type Sprite struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sprite) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
func (s *Sprite) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
if s.Hidden {
|
dp := s.Actor.Pos.Add(s.FrameOffset)
|
||||||
return
|
|
||||||
}
|
|
||||||
dp := s.Pos.Add(s.FrameOffset)
|
|
||||||
var geom ebiten.GeoM
|
var geom ebiten.GeoM
|
||||||
geom.Translate(float64(dp.X), float64(dp.Y))
|
geom.Translate(float64(dp.X), float64(dp.Y))
|
||||||
geom.Concat(opts.GeoM)
|
geom.Concat(opts.GeoM)
|
||||||
|
@ -57,4 +54,5 @@ func (s *Sprite) SetAnim(a *Anim) {
|
||||||
s.anim = a
|
s.anim = a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// anim isn't returned from Scan so we must update it ourselves
|
||||||
func (s *Sprite) Update() error { return s.anim.Update() }
|
func (s *Sprite) Update() error { return s.anim.Update() }
|
||||||
|
|
|
@ -14,7 +14,6 @@ var _ interface {
|
||||||
Drawer
|
Drawer
|
||||||
Hider
|
Hider
|
||||||
Scanner
|
Scanner
|
||||||
Updater
|
|
||||||
} = &Tilemap{}
|
} = &Tilemap{}
|
||||||
|
|
||||||
// Ensure StaticTile and AnimatedTile satisfy Tile.
|
// Ensure StaticTile and AnimatedTile satisfy Tile.
|
||||||
|
@ -67,9 +66,6 @@ func (t *Tilemap) CollidesWith(r image.Rectangle) bool {
|
||||||
|
|
||||||
// Draw draws the tilemap.
|
// Draw draws the tilemap.
|
||||||
func (t *Tilemap) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
func (t *Tilemap) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
if t.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
og := opts.GeoM
|
og := opts.GeoM
|
||||||
var geom ebiten.GeoM
|
var geom ebiten.GeoM
|
||||||
for p, tile := range t.Map {
|
for p, tile := range t.Map {
|
||||||
|
@ -96,21 +92,6 @@ func (t *Tilemap) Scan() []interface{} {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update calls Update on any tiles that are Updaters, e.g. AnimatedTile.
|
|
||||||
func (t *Tilemap) Update() error {
|
|
||||||
if t.Disabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, tile := range t.Map {
|
|
||||||
if u, ok := tile.(Updater); ok {
|
|
||||||
if err := u.Update(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TileAt returns the tile present at the given world coordinate.
|
// TileAt returns the tile present at the given world coordinate.
|
||||||
func (t *Tilemap) TileAt(wc image.Point) Tile {
|
func (t *Tilemap) TileAt(wc image.Point) Tile {
|
||||||
return t.Map[div2(wc.Sub(t.Offset), t.Sheet.CellSize)]
|
return t.Map[div2(wc.Sub(t.Offset), t.Sheet.CellSize)]
|
||||||
|
|
|
@ -18,7 +18,6 @@ var (
|
||||||
Disabler
|
Disabler
|
||||||
Hider
|
Hider
|
||||||
Prepper
|
Prepper
|
||||||
Updater
|
|
||||||
} = &WallUnit{}
|
} = &WallUnit{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,9 +79,6 @@ type WallUnit struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *WallUnit) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
func (u *WallUnit) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||||
if u.Hidden {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var geom ebiten.GeoM
|
var geom ebiten.GeoM
|
||||||
geom.Translate(float2(mul2(u.Pos, u.wall.UnitSize).Add(u.wall.UnitOffset).Add(u.wall.Offset)))
|
geom.Translate(float2(mul2(u.Pos, u.wall.UnitSize).Add(u.wall.UnitOffset).Add(u.wall.Offset)))
|
||||||
geom.Concat(opts.GeoM)
|
geom.Concat(opts.GeoM)
|
||||||
|
@ -98,12 +94,4 @@ func (u *WallUnit) Prepare(g *Game) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *WallUnit) Update() error {
|
func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} }
|
||||||
if u.Disabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if up, ok := u.Tile.(Updater); ok {
|
|
||||||
return up.Update()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
36
game/aw.go
36
game/aw.go
|
@ -12,9 +12,7 @@ import (
|
||||||
|
|
||||||
var _ interface {
|
var _ interface {
|
||||||
engine.Identifier
|
engine.Identifier
|
||||||
engine.Drawer // provided by Sprite
|
|
||||||
engine.Disabler
|
engine.Disabler
|
||||||
engine.Hider // provided by Sprite
|
|
||||||
engine.Prepper
|
engine.Prepper
|
||||||
engine.Scanner
|
engine.Scanner
|
||||||
engine.Updater
|
engine.Updater
|
||||||
|
@ -24,10 +22,10 @@ func init() {
|
||||||
gob.Register(&Awakeman{})
|
gob.Register(&Awakeman{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Awakeman is a bit of a god object for now...
|
||||||
type Awakeman struct {
|
type Awakeman struct {
|
||||||
engine.Disabled
|
engine.Disabled
|
||||||
engine.Sprite
|
Sprite engine.Sprite
|
||||||
|
|
||||||
CameraID string
|
CameraID string
|
||||||
ToastID string
|
ToastID string
|
||||||
|
|
||||||
|
@ -46,10 +44,6 @@ type Awakeman struct {
|
||||||
func (aw *Awakeman) Ident() string { return "awakeman" }
|
func (aw *Awakeman) Ident() string { return "awakeman" }
|
||||||
|
|
||||||
func (aw *Awakeman) Update() error {
|
func (aw *Awakeman) Update() error {
|
||||||
if aw.Disabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: better cheat for noclip
|
// TODO: better cheat for noclip
|
||||||
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
||||||
aw.noclip = !aw.noclip
|
aw.noclip = !aw.noclip
|
||||||
|
@ -75,22 +69,22 @@ func (aw *Awakeman) Update() error {
|
||||||
aw.camera.Zoom = 2
|
aw.camera.Zoom = 2
|
||||||
}
|
}
|
||||||
// aw.Pos is top-left corner, so add half size to get centre
|
// aw.Pos is top-left corner, so add half size to get centre
|
||||||
aw.camera.Centre = aw.Pos.Add(aw.Size.Div(2))
|
aw.camera.Centre = aw.Sprite.Actor.Pos.Add(aw.Sprite.Actor.Size.Div(2))
|
||||||
return aw.Sprite.Update()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (aw *Awakeman) noclipUpdate() error {
|
func (aw *Awakeman) noclipUpdate() error {
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyUp) {
|
if ebiten.IsKeyPressed(ebiten.KeyUp) {
|
||||||
aw.Pos.Y--
|
aw.Sprite.Actor.Pos.Y--
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyDown) {
|
if ebiten.IsKeyPressed(ebiten.KeyDown) {
|
||||||
aw.Pos.Y++
|
aw.Sprite.Actor.Pos.Y++
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
|
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
|
||||||
aw.Pos.X--
|
aw.Sprite.Actor.Pos.X--
|
||||||
}
|
}
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyRight) {
|
if ebiten.IsKeyPressed(ebiten.KeyRight) {
|
||||||
aw.Pos.X++
|
aw.Sprite.Actor.Pos.X++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -120,7 +114,7 @@ func (aw *Awakeman) realUpdate() error {
|
||||||
ux, uy := aw.vx, aw.vy
|
ux, uy := aw.vx, aw.vy
|
||||||
|
|
||||||
// Has traction?
|
// Has traction?
|
||||||
if aw.CollidesAt(aw.Pos.Add(image.Pt(0, 1))) {
|
if aw.Sprite.Actor.CollidesAt(aw.Sprite.Actor.Pos.Add(image.Pt(0, 1))) {
|
||||||
// Not falling.
|
// Not falling.
|
||||||
// Instantly decelerate (AW absorbs all kinetic E in legs, or something)
|
// Instantly decelerate (AW absorbs all kinetic E in legs, or something)
|
||||||
if aw.jumpBuffer > 0 {
|
if aw.jumpBuffer > 0 {
|
||||||
|
@ -160,25 +154,25 @@ func (aw *Awakeman) realUpdate() error {
|
||||||
switch {
|
switch {
|
||||||
case ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA):
|
case ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA):
|
||||||
aw.vx = -runVelocity
|
aw.vx = -runVelocity
|
||||||
aw.SetAnim(aw.animRunLeft)
|
aw.Sprite.SetAnim(aw.animRunLeft)
|
||||||
aw.facingLeft = true
|
aw.facingLeft = true
|
||||||
case ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD):
|
case ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD):
|
||||||
aw.vx = runVelocity
|
aw.vx = runVelocity
|
||||||
aw.SetAnim(aw.animRunRight)
|
aw.Sprite.SetAnim(aw.animRunRight)
|
||||||
aw.facingLeft = false
|
aw.facingLeft = false
|
||||||
default:
|
default:
|
||||||
aw.vx = 0
|
aw.vx = 0
|
||||||
aw.SetAnim(aw.animIdleRight)
|
aw.Sprite.SetAnim(aw.animIdleRight)
|
||||||
if aw.facingLeft {
|
if aw.facingLeft {
|
||||||
aw.SetAnim(aw.animIdleLeft)
|
aw.Sprite.SetAnim(aw.animIdleLeft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// s = (v_0 + v) / 2.
|
// s = (v_0 + v) / 2.
|
||||||
aw.MoveX((ux+aw.vx)/2, nil)
|
aw.Sprite.Actor.MoveX((ux+aw.vx)/2, nil)
|
||||||
// For Y, on collision, bounce a little bit.
|
// For Y, on collision, bounce a little bit.
|
||||||
// Does not apply to X because controls override it anyway.
|
// Does not apply to X because controls override it anyway.
|
||||||
aw.MoveY((uy+aw.vy)/2, func() {
|
aw.Sprite.Actor.MoveY((uy+aw.vy)/2, func() {
|
||||||
aw.vy *= restitution
|
aw.vy *= restitution
|
||||||
if math.Abs(aw.vy) < ε {
|
if math.Abs(aw.vy) < ε {
|
||||||
aw.vy = 0
|
aw.vy = 0
|
||||||
|
|
Loading…
Reference in a new issue