// Package game innehåller Ebitengine-spelloopen och scenerna. Detta är det // enda paketet (tillsammans med cmd/) som får känna till Ebitengine; all // spellogik ligger i paket som går att testa utan fönster (internal/sim, // internal/level …). package game import ( "github.com/hajimehoshi/ebiten/v2" ) // Intern upplösning. Allt ritas i denna storlek och skalas sedan upp med // heltalsfaktor av Ebitengine så pixlarna förblir skarpa. const ( ScreenW = 320 ScreenH = 180 TileSize = 16 dt = 1.0 / 60 // Ebitengine kör Update med fast 60 TPS ) // Scene är ett spelläge: meny, bana, resultat … type Scene interface { Update(g *Game) error Draw(g *Game, screen *ebiten.Image) } // Game är Ebitengine-tillståndet för hela programmet. type Game struct { Assets *Assets scene Scene next Scene smokeLeft int // >0: avsluta automatiskt efter så många uppdateringar script *script frame *ebiten.Image // allt ritas hit och blittas sedan till skärmen (gör skärmdumpar möjliga) } // Options styr uppstarten. type Options struct { LevelID string // hoppa direkt in i en bana (tom = meny) SmokeFrames int // >0 avslutar efter N uppdateringar (smoke-test) Script string // skriptade steg för skärmdumpar/tester, se script.go } // New laddar resurser och bygger spelet. func New(opt Options) (*Game, error) { a, err := LoadAssets() if err != nil { return nil, err } g := &Game{Assets: a, smokeLeft: opt.SmokeFrames, frame: ebiten.NewImage(ScreenW, ScreenH)} if opt.Script != "" { if g.script, err = parseScript(opt.Script); err != nil { return nil, err } } if opt.LevelID != "" { s, err := NewLevelScene(a, opt.LevelID) if err != nil { return nil, err } g.scene = s } else { g.scene, err = NewMenuScene(a) if err != nil { return nil, err } } return g, nil } // SetScene byter scen från och med nästa uppdatering. func (g *Game) SetScene(s Scene) { g.next = s } // Update körs 60 ggr/s. func (g *Game) Update() error { if g.smokeLeft > 0 { g.smokeLeft-- if g.smokeLeft == 0 { return ebiten.Termination } } if g.next != nil { g.scene, g.next = g.next, nil } if g.script != nil { if err := g.script.tick(g, g.frame); err != nil { return err } } return g.scene.Update(g) } // Draw ritar aktuell scen till den interna bilden och sedan till skärmen. func (g *Game) Draw(screen *ebiten.Image) { g.scene.Draw(g, g.frame) screen.DrawImage(g.frame, nil) } // Layout talar om för Ebitengine vilken intern upplösning vi ritar i. func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { return ScreenW, ScreenH }