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.
|
||||
func (b *Billboard) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||
if b.Hidden {
|
||||
return
|
||||
}
|
||||
var geom ebiten.GeoM
|
||||
geom.Translate(float64(b.Pos.X), float64(b.Pos.Y))
|
||||
geom.Concat(opts.GeoM)
|
||||
|
|
|
@ -29,8 +29,5 @@ type Fill struct {
|
|||
}
|
||||
|
||||
func (f *Fill) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||
if f.Hidden {
|
||||
return
|
||||
}
|
||||
screen.Fill(opts.ColorM.Apply(f.Color))
|
||||
}
|
||||
|
|
145
engine/game.go
145
engine/game.go
|
@ -13,8 +13,6 @@ import (
|
|||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
const gameDoesEverything = true
|
||||
|
||||
var _ interface {
|
||||
Disabler
|
||||
Hider
|
||||
|
@ -40,49 +38,25 @@ type Game struct {
|
|||
Hidden
|
||||
ScreenWidth int
|
||||
ScreenHeight int
|
||||
Root DrawUpdater // typically a *Scene or SceneRef though
|
||||
Root interface{} // typically a *Scene or SceneRef though
|
||||
|
||||
dbmu sync.RWMutex
|
||||
byID map[string]Identifier // Named components by ID
|
||||
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
|
||||
}
|
||||
|
||||
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 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.
|
||||
// Draw draws everything.
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
if g.Hidden {
|
||||
return
|
||||
}
|
||||
|
||||
if gameDoesEverything {
|
||||
// Hiding a parent component should hide the child objects, and the
|
||||
// transform applied to a child should be the cumulative transform of all
|
||||
// parents as well.
|
||||
// accum memoises the results for each component.
|
||||
type state struct {
|
||||
hidden bool
|
||||
opts ebiten.DrawImageOptions
|
||||
|
@ -90,14 +64,14 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
accum := map[interface{}]state{
|
||||
g: {hidden: false},
|
||||
}
|
||||
// draw everything
|
||||
// Draw everything in g.drawList, where not hidden (itself or any parent)
|
||||
for _, d := range g.drawList {
|
||||
// is d directly hidden?
|
||||
// Is d hidden itself?
|
||||
if h, ok := d.(Hider); ok && h.IsHidden() {
|
||||
accum[d] = state{hidden: true}
|
||||
continue // skip drawing
|
||||
}
|
||||
// walk up g.par to find the nearest parent state in accum
|
||||
// Walk up g.par to find the nearest state in accum.
|
||||
var st state
|
||||
stack := []interface{}{d}
|
||||
for p := g.par[d]; ; p = g.par[p] {
|
||||
|
@ -107,7 +81,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
}
|
||||
stack = append(stack, p)
|
||||
}
|
||||
// unwind the stack, accumulating state along the way
|
||||
// Unwind the stack, accumulating state along the way.
|
||||
for len(stack) > 0 {
|
||||
l1 := len(stack) - 1
|
||||
p := stack[l1]
|
||||
|
@ -119,21 +93,19 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
accum[p] = state{hidden: true}
|
||||
continue
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
// now...skip drawing if hidden :P
|
||||
// Skip drawing if hidden.
|
||||
if st.hidden {
|
||||
continue
|
||||
}
|
||||
d.Draw(screen, st.opts)
|
||||
}
|
||||
} else { // !gameDoesEverything
|
||||
g.Root.Draw(screen, ebiten.DrawImageOptions{})
|
||||
}
|
||||
}
|
||||
|
||||
// Layout returns the configured screen width/height.
|
||||
|
@ -141,25 +113,71 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (w, h int) {
|
|||
return g.ScreenWidth, g.ScreenHeight
|
||||
}
|
||||
|
||||
// Update updates the scene.
|
||||
// Update updates everything.
|
||||
func (g *Game) Update() error {
|
||||
if g.Disabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := g.Root.Update(); err != nil {
|
||||
// Need to do a similar trick for Draw: disabling a parent object should
|
||||
// disable the child objects.
|
||||
// accum memoises the disabled state for each component.
|
||||
accum := map[interface{}]bool{
|
||||
g: false,
|
||||
}
|
||||
|
||||
// Update everything that is not disabled.
|
||||
for u := range g.Query(g.Ident(), UpdaterType) {
|
||||
// Skip g (note g satisfies Updater, so this would infinitely recurse)
|
||||
if u == g {
|
||||
continue
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
if gameDoesEverything {
|
||||
// sort the draw list (yes, on every frame)
|
||||
}
|
||||
|
||||
// Sort the draw list (on every frame - this isn't as bad as it sounds)
|
||||
sort.Stable(g.drawList)
|
||||
// slice out any tombstones
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -360,3 +378,36 @@ func (g *Game) unregister(component interface{}) {
|
|||
}
|
||||
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
|
||||
Disabler
|
||||
Drawer
|
||||
Hider
|
||||
Identifier
|
||||
Prepper
|
||||
Scanner
|
||||
Transformer
|
||||
Updater
|
||||
}
|
||||
|
||||
// Saver components can be saved to disk.
|
||||
|
|
|
@ -50,7 +50,7 @@ type ZOrder float64
|
|||
// DrawOrder returns z as a float64.
|
||||
func (z ZOrder) DrawOrder() float64 { return float64(z) }
|
||||
|
||||
// Some math helpers
|
||||
// ---------- Some math helpers ----------
|
||||
|
||||
func mul2(p, q image.Point) image.Point {
|
||||
p.X *= q.X
|
||||
|
|
|
@ -2,8 +2,6 @@ package engine
|
|||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
@ -26,9 +24,10 @@ type Scene struct {
|
|||
ZOrder
|
||||
}
|
||||
|
||||
/*
|
||||
// Draw draws all components in order.
|
||||
func (s *Scene) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||
if s.Hidden || gameDoesEverything {
|
||||
if s.Hidden {
|
||||
return
|
||||
}
|
||||
if s.Camera == nil {
|
||||
|
@ -105,6 +104,7 @@ func (s *Scene) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
|||
d.Draw(screen, opts)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Transform returns the camera transform
|
||||
func (s *Scene) Transform() ebiten.DrawImageOptions {
|
||||
|
@ -114,26 +114,6 @@ func (s *Scene) Transform() ebiten.DrawImageOptions {
|
|||
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).
|
||||
func (s *Scene) Scan() []interface{} {
|
||||
if s.Camera != nil {
|
||||
|
@ -141,34 +121,3 @@ func (s *Scene) Scan() []interface{} {
|
|||
}
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
func (r SceneRef) Scan() []interface{} { return r.scene.Scan() }
|
||||
|
||||
// Transform returns the value of Transform from the scene.
|
||||
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
|
||||
}
|
||||
|
||||
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.
|
||||
type Sprite struct {
|
||||
Actor
|
||||
Actor Actor
|
||||
FrameOffset image.Point
|
||||
Hidden
|
||||
Sheet Sheet
|
||||
|
@ -30,10 +30,7 @@ type Sprite struct {
|
|||
}
|
||||
|
||||
func (s *Sprite) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||
if s.Hidden {
|
||||
return
|
||||
}
|
||||
dp := s.Pos.Add(s.FrameOffset)
|
||||
dp := s.Actor.Pos.Add(s.FrameOffset)
|
||||
var geom ebiten.GeoM
|
||||
geom.Translate(float64(dp.X), float64(dp.Y))
|
||||
geom.Concat(opts.GeoM)
|
||||
|
@ -57,4 +54,5 @@ func (s *Sprite) SetAnim(a *Anim) {
|
|||
s.anim = a
|
||||
}
|
||||
|
||||
// anim isn't returned from Scan so we must update it ourselves
|
||||
func (s *Sprite) Update() error { return s.anim.Update() }
|
||||
|
|
|
@ -14,7 +14,6 @@ var _ interface {
|
|||
Drawer
|
||||
Hider
|
||||
Scanner
|
||||
Updater
|
||||
} = &Tilemap{}
|
||||
|
||||
// Ensure StaticTile and AnimatedTile satisfy Tile.
|
||||
|
@ -67,9 +66,6 @@ func (t *Tilemap) CollidesWith(r image.Rectangle) bool {
|
|||
|
||||
// Draw draws the tilemap.
|
||||
func (t *Tilemap) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||
if t.Hidden {
|
||||
return
|
||||
}
|
||||
og := opts.GeoM
|
||||
var geom ebiten.GeoM
|
||||
for p, tile := range t.Map {
|
||||
|
@ -96,21 +92,6 @@ func (t *Tilemap) Scan() []interface{} {
|
|||
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.
|
||||
func (t *Tilemap) TileAt(wc image.Point) Tile {
|
||||
return t.Map[div2(wc.Sub(t.Offset), t.Sheet.CellSize)]
|
||||
|
|
|
@ -18,7 +18,6 @@ var (
|
|||
Disabler
|
||||
Hider
|
||||
Prepper
|
||||
Updater
|
||||
} = &WallUnit{}
|
||||
)
|
||||
|
||||
|
@ -80,9 +79,6 @@ type WallUnit struct {
|
|||
}
|
||||
|
||||
func (u *WallUnit) Draw(screen *ebiten.Image, opts ebiten.DrawImageOptions) {
|
||||
if u.Hidden {
|
||||
return
|
||||
}
|
||||
var geom ebiten.GeoM
|
||||
geom.Translate(float2(mul2(u.Pos, u.wall.UnitSize).Add(u.wall.UnitOffset).Add(u.wall.Offset)))
|
||||
geom.Concat(opts.GeoM)
|
||||
|
@ -98,12 +94,4 @@ func (u *WallUnit) Prepare(g *Game) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (u *WallUnit) Update() error {
|
||||
if u.Disabled {
|
||||
return nil
|
||||
}
|
||||
if up, ok := u.Tile.(Updater); ok {
|
||||
return up.Update()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (u *WallUnit) Scan() []interface{} { return []interface{}{u.Tile} }
|
||||
|
|
36
game/aw.go
36
game/aw.go
|
@ -12,9 +12,7 @@ import (
|
|||
|
||||
var _ interface {
|
||||
engine.Identifier
|
||||
engine.Drawer // provided by Sprite
|
||||
engine.Disabler
|
||||
engine.Hider // provided by Sprite
|
||||
engine.Prepper
|
||||
engine.Scanner
|
||||
engine.Updater
|
||||
|
@ -24,10 +22,10 @@ func init() {
|
|||
gob.Register(&Awakeman{})
|
||||
}
|
||||
|
||||
// Awakeman is a bit of a god object for now...
|
||||
type Awakeman struct {
|
||||
engine.Disabled
|
||||
engine.Sprite
|
||||
|
||||
Sprite engine.Sprite
|
||||
CameraID string
|
||||
ToastID string
|
||||
|
||||
|
@ -46,10 +44,6 @@ type Awakeman struct {
|
|||
func (aw *Awakeman) Ident() string { return "awakeman" }
|
||||
|
||||
func (aw *Awakeman) Update() error {
|
||||
if aw.Disabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: better cheat for noclip
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
|
||||
aw.noclip = !aw.noclip
|
||||
|
@ -75,22 +69,22 @@ func (aw *Awakeman) Update() error {
|
|||
aw.camera.Zoom = 2
|
||||
}
|
||||
// aw.Pos is top-left corner, so add half size to get centre
|
||||
aw.camera.Centre = aw.Pos.Add(aw.Size.Div(2))
|
||||
return aw.Sprite.Update()
|
||||
aw.camera.Centre = aw.Sprite.Actor.Pos.Add(aw.Sprite.Actor.Size.Div(2))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (aw *Awakeman) noclipUpdate() error {
|
||||
if ebiten.IsKeyPressed(ebiten.KeyUp) {
|
||||
aw.Pos.Y--
|
||||
aw.Sprite.Actor.Pos.Y--
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyDown) {
|
||||
aw.Pos.Y++
|
||||
aw.Sprite.Actor.Pos.Y++
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyLeft) {
|
||||
aw.Pos.X--
|
||||
aw.Sprite.Actor.Pos.X--
|
||||
}
|
||||
if ebiten.IsKeyPressed(ebiten.KeyRight) {
|
||||
aw.Pos.X++
|
||||
aw.Sprite.Actor.Pos.X++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -120,7 +114,7 @@ func (aw *Awakeman) realUpdate() error {
|
|||
ux, uy := aw.vx, aw.vy
|
||||
|
||||
// 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.
|
||||
// Instantly decelerate (AW absorbs all kinetic E in legs, or something)
|
||||
if aw.jumpBuffer > 0 {
|
||||
|
@ -160,25 +154,25 @@ func (aw *Awakeman) realUpdate() error {
|
|||
switch {
|
||||
case ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA):
|
||||
aw.vx = -runVelocity
|
||||
aw.SetAnim(aw.animRunLeft)
|
||||
aw.Sprite.SetAnim(aw.animRunLeft)
|
||||
aw.facingLeft = true
|
||||
case ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD):
|
||||
aw.vx = runVelocity
|
||||
aw.SetAnim(aw.animRunRight)
|
||||
aw.Sprite.SetAnim(aw.animRunRight)
|
||||
aw.facingLeft = false
|
||||
default:
|
||||
aw.vx = 0
|
||||
aw.SetAnim(aw.animIdleRight)
|
||||
aw.Sprite.SetAnim(aw.animIdleRight)
|
||||
if aw.facingLeft {
|
||||
aw.SetAnim(aw.animIdleLeft)
|
||||
aw.Sprite.SetAnim(aw.animIdleLeft)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
if math.Abs(aw.vy) < ε {
|
||||
aw.vy = 0
|
||||
|
|
Loading…
Reference in a new issue