lints, loading fix

This commit is contained in:
Josh Deprez 2021-10-11 17:33:06 +11:00
parent f2f9b9dd6f
commit 1ff0694049
17 changed files with 92 additions and 46 deletions

View file

@ -52,6 +52,7 @@ type DebugToast struct {
Text string Text string
} }
// Draw uses DebugPrintAt to draw d.Text at the position d.Pos.
func (d *DebugToast) Draw(screen *ebiten.Image, _ *ebiten.DrawImageOptions) { func (d *DebugToast) Draw(screen *ebiten.Image, _ *ebiten.DrawImageOptions) {
ebitenutil.DebugPrintAt(screen, d.Text, d.Pos.X, d.Pos.Y) ebitenutil.DebugPrintAt(screen, d.Text, d.Pos.X, d.Pos.Y)
} }
@ -60,12 +61,14 @@ func (d *DebugToast) String() string {
return fmt.Sprintf("DebugToast@%v", d.Pos) return fmt.Sprintf("DebugToast@%v", d.Pos)
} }
// Toast sets the text to appear for 2 seconds.
func (d *DebugToast) Toast(text string) { func (d *DebugToast) Toast(text string) {
d.Text = text d.Text = text
d.Timer = 120 d.Timer = 120
d.Hides = false d.Hides = false
} }
// Update hides the toast text once the timer is exhausted.
func (d *DebugToast) Update() error { func (d *DebugToast) Update() error {
if d.Hides = d.Timer <= 0; !d.Hides { if d.Hides = d.Timer <= 0; !d.Hides {
d.Timer-- d.Timer--
@ -78,6 +81,7 @@ type PerfDisplay struct {
Hides Hides
} }
// Draw uses DebugPrint to print the TPS and FPS in the top-left.
func (p PerfDisplay) Draw(screen *ebiten.Image, _ *ebiten.DrawImageOptions) { func (p PerfDisplay) Draw(screen *ebiten.Image, _ *ebiten.DrawImageOptions) {
ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f FPS: %0.2f", ebiten.CurrentTPS(), ebiten.CurrentFPS())) ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f FPS: %0.2f", ebiten.CurrentTPS(), ebiten.CurrentFPS()))
} }

View file

@ -43,6 +43,8 @@ type DrawDFS struct {
game *Game game *Game
} }
// Draw draws all descendant components (that are not managed by some other
// DrawManager) in a pre-order traversal.
func (d *DrawDFS) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) { func (d *DrawDFS) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
stack := []ebiten.DrawImageOptions{*opts} stack := []ebiten.DrawImageOptions{*opts}
d.game.Query(d, DrawerType, d.game.Query(d, DrawerType,
@ -81,6 +83,7 @@ func (d *DrawDFS) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
// DrawManager. // DrawManager.
func (DrawDFS) ManagesDrawingSubcomponents() {} func (DrawDFS) ManagesDrawingSubcomponents() {}
// Prepare saves a reference to g.
func (d *DrawDFS) Prepare(g *Game) error { func (d *DrawDFS) Prepare(g *Game) error {
d.game = g d.game = g
return nil return nil

View file

@ -39,12 +39,13 @@ func init() {
// Fill fills the screen with a colour. // Fill fills the screen with a colour.
type Fill struct { type Fill struct {
ID ID
Color color.Color Colour color.Color
Hides Hides
} }
// Draw fills the screen with the colour.
func (f *Fill) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) { func (f *Fill) Draw(screen *ebiten.Image, opts *ebiten.DrawImageOptions) {
screen.Fill(opts.ColorM.Apply(f.Color)) screen.Fill(opts.ColorM.Apply(f.Colour))
} }
func (f *Fill) String() string { return "Fill" } func (f *Fill) String() string { return "Fill" }

View file

@ -22,19 +22,25 @@ import (
"time" "time"
) )
// LoadingSwitch switches between two subcomponents. While After is being
// loaded asynchronously, During is shown. Once loading is complete, During
// is hidden and After is shown.
type LoadingSwitch struct { type LoadingSwitch struct {
During Hider During, After interface {
After Hider Disabler
Hider
}
assets fs.FS assets fs.FS
} }
// Scan only scans s.During, thus, only s.During is loaded by Game directly. // Scan only scans s.During. Only s.During is loaded automatically - s.After is
// loaded asynchronously from Prepare below.
func (s *LoadingSwitch) Scan(visit VisitFunc) error { func (s *LoadingSwitch) Scan(visit VisitFunc) error {
return visit(s.During) return visit(s.During)
} }
// Load stores a copy of assets to use later. // Load stores a copy of assets to pass to s.After.Load later.
func (s *LoadingSwitch) Load(assets fs.FS) error { func (s *LoadingSwitch) Load(assets fs.FS) error {
s.assets = assets s.assets = assets
return nil return nil
@ -43,30 +49,37 @@ func (s *LoadingSwitch) Load(assets fs.FS) error {
// Prepare loads, registers, and prepares.After in a separate goroutine. Once // Prepare loads, registers, and prepares.After in a separate goroutine. Once
// ready, LoadingSwitch hides s.During and shows s.After. // ready, LoadingSwitch hides s.During and shows s.After.
func (s *LoadingSwitch) Prepare(game *Game) error { func (s *LoadingSwitch) Prepare(game *Game) error {
go func() { go s.loadAfter(game)
startLoad := time.Now()
if err := game.Load(s.After, s.assets); err != nil {
log.Printf("Couldn't load: %v", err)
return
}
log.Printf("LoadingSwitch: finished loading in %v", time.Since(startLoad))
startBuild := time.Now()
if err := game.Register(s.After, s); err != nil {
log.Printf("Couldn't register: %v", err)
return
}
log.Printf("LoadingSwitch: finished registering in %v", time.Since(startBuild))
startPrep := time.Now()
if err := game.Prepare(s.After); err != nil {
log.Printf("Couldn't prepare: %v", err)
return
}
log.Printf("LoadingSwitch: finished preparing in %v", time.Since(startPrep))
// TODO: better scene transitions
s.During.Hide()
s.After.Show()
}()
return nil return nil
} }
func (s *LoadingSwitch) loadAfter(game *Game) {
startLoad := time.Now()
if err := game.Load(s.After, s.assets); err != nil {
log.Printf("Couldn't load: %v", err)
return
}
log.Printf("LoadingSwitch: finished loading in %v", time.Since(startLoad))
s.After.Disable()
s.After.Hide()
startBuild := time.Now()
if err := game.Register(s.After, s); err != nil {
log.Printf("Couldn't register: %v", err)
return
}
log.Printf("LoadingSwitch: finished registering in %v", time.Since(startBuild))
startPrep := time.Now()
if err := game.Prepare(s.After); err != nil {
log.Printf("Couldn't prepare: %v", err)
return
}
log.Printf("LoadingSwitch: finished preparing in %v", time.Since(startPrep))
// TODO: better scene transitions
s.During.Disable()
s.During.Hide()
s.After.Enable()
s.After.Show()
}

View file

@ -28,11 +28,13 @@ func init() {
gob.Register(&SolidRect{}) gob.Register(&SolidRect{})
} }
// SolidRect is a minimal implementation of a Collider defined by a single Box.
type SolidRect struct { type SolidRect struct {
ID ID
geom.Box geom.Box
} }
// CollidesWith reports if r overlaps with s.Box.
func (s SolidRect) CollidesWith(r geom.Box) bool { func (s SolidRect) CollidesWith(r geom.Box) bool {
return s.Box.Overlaps(r) return s.Box.Overlaps(r)
} }

View file

@ -159,6 +159,7 @@ type Tile interface {
// StaticTile returns a fixed tile index. // StaticTile returns a fixed tile index.
type StaticTile int type StaticTile int
// Cell returns s as an int.
func (s StaticTile) Cell() int { return int(s) } func (s StaticTile) Cell() int { return int(s) }
// AnimatedTile uses an Anim to choose a tile index. // AnimatedTile uses an Anim to choose a tile index.
@ -168,6 +169,7 @@ type AnimatedTile struct {
anim *Anim anim *Anim
} }
// Cell returns the value of Cell provided by the animation.
func (a *AnimatedTile) Cell() int { return a.anim.Cell() } func (a *AnimatedTile) Cell() int { return a.anim.Cell() }
// Scan visits a.anim. // Scan visits a.anim.

View file

@ -126,6 +126,7 @@ func (u *WallUnit) Scan(visit VisitFunc) error {
return visit(u.Tile) return visit(u.Tile)
} }
// Transform returns a translation by the position and offset.
func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) { func (u *WallUnit) Transform() (opts ebiten.DrawImageOptions) {
opts.GeoM.Translate(geom.CFloat( opts.GeoM.Translate(geom.CFloat(
geom.CMul(u.pos, u.wall.UnitSize).Add(u.wall.UnitOffset), geom.CMul(u.pos, u.wall.UnitSize).Add(u.wall.UnitOffset),

View file

@ -81,20 +81,20 @@ func main() {
Projection: geom.SimpleProjection{}, Projection: geom.SimpleProjection{},
VoxelScale: geom.Float3{ VoxelScale: geom.Float3{
// Each voxel counts for this much (Euclidean) space. // Each voxel counts for this much (Euclidean) space.
X: 1, X: 1, Y: 1, Z: math.Sqrt(3),
Y: 1,
Z: math.Sqrt(3),
}, },
Root: &engine.DrawDFS{ Root: &engine.DrawDFS{
Child: engine.MakeContainer( Child: engine.MakeContainer(
&engine.Fill{ &engine.Fill{
ID: "bg_fill", ID: "bg_fill",
Color: color.Gray{100}, Colour: color.Gray{100},
}, },
&engine.LoadingSwitch{ &engine.LoadingSwitch{
During: &engine.Billboard{ During: &engine.Scene{
ID: "loading_screen", ID: "loading_scene",
Src: engine.ImageRef{Path: "assets/loading.png"}, Child: &engine.Billboard{
Src: engine.ImageRef{Path: "assets/loading.png"},
},
}, },
After: &engine.Camera{ After: &engine.Camera{
ID: "game_camera", ID: "game_camera",

View file

@ -20,5 +20,6 @@ package example
import "embed" import "embed"
// Assets is the embedded assets FS.
//go:embed assets //go:embed assets
var Assets embed.FS var Assets embed.FS

View file

@ -65,6 +65,8 @@ type Awakeman struct {
// Ident returns "awakeman". There should be only one! // Ident returns "awakeman". There should be only one!
func (aw *Awakeman) Ident() string { return "awakeman" } func (aw *Awakeman) Ident() string { return "awakeman" }
// Update updates Awakeman, including capturing input, applying gravity and
// movement, and repositioning the camera.
func (aw *Awakeman) Update() error { func (aw *Awakeman) Update() error {
// TODO: better cheat for noclip // TODO: better cheat for noclip
if inpututil.IsKeyJustPressed(ebiten.KeyN) { if inpututil.IsKeyJustPressed(ebiten.KeyN) {
@ -257,6 +259,7 @@ func (aw *Awakeman) realUpdate() error {
return nil return nil
} }
// Prepare captures necessary references to other game components.
func (aw *Awakeman) Prepare(game *engine.Game) error { func (aw *Awakeman) Prepare(game *engine.Game) error {
aw.game = game aw.game = game
cam, ok := game.Component(aw.CameraID).(*engine.Camera) cam, ok := game.Component(aw.CameraID).(*engine.Camera)
@ -275,6 +278,7 @@ func (aw *Awakeman) Prepare(game *engine.Game) error {
return nil return nil
} }
// Scan visits &aw.Sprite.
func (aw *Awakeman) Scan(visit engine.VisitFunc) error { func (aw *Awakeman) Scan(visit engine.VisitFunc) error {
return visit(&aw.Sprite) return visit(&aw.Sprite)
} }

View file

@ -31,6 +31,7 @@ var _ interface {
engine.Updater engine.Updater
} = &Bubble{} } = &Bubble{}
// Bubble implements a single bubble within a simple particle system.
type Bubble struct { type Bubble struct {
Life int Life int
Sprite engine.Sprite Sprite engine.Sprite
@ -38,6 +39,8 @@ type Bubble struct {
game *engine.Game game *engine.Game
} }
// NewBubble creates a bubble. Before it can be used, the return value needs to
// be loaded, registered, and prepared.
func NewBubble(pos geom.Int3) *Bubble { func NewBubble(pos geom.Int3) *Bubble {
return &Bubble{ return &Bubble{
Life: 60, Life: 60,
@ -72,6 +75,7 @@ func NewBubble(pos geom.Int3) *Bubble {
} }
} }
// Scan visits &b.sprite.
func (b *Bubble) Scan(visit engine.VisitFunc) error { func (b *Bubble) Scan(visit engine.VisitFunc) error {
return visit(&b.Sprite) return visit(&b.Sprite)
} }
@ -80,11 +84,14 @@ func (b *Bubble) String() string {
return fmt.Sprintf("Bubble@%v", b.Sprite.Actor.Pos) return fmt.Sprintf("Bubble@%v", b.Sprite.Actor.Pos)
} }
// Prepare saves a reference to g.
func (b *Bubble) Prepare(g *engine.Game) error { func (b *Bubble) Prepare(g *engine.Game) error {
b.game = g b.game = g
return nil return nil
} }
// Update moves the bubble randomly, and handles unregistering the bubble when
// it has "popped".
func (b *Bubble) Update() error { func (b *Bubble) Update() error {
b.Life-- b.Life--
if b.Life <= 0 { if b.Life <= 0 {

View file

@ -26,7 +26,7 @@ type Int3 struct {
X, Y, Z int X, Y, Z int
} }
// Pt3(x, y, z) is shorthand for Int3{x, y, z}. // Pt3 returns Int3{x, y, z}.
func Pt3(x, y, z int) Int3 { func Pt3(x, y, z int) Int3 {
return Int3{x, y, z} return Int3{x, y, z}
} }

View file

@ -98,7 +98,7 @@ func (a IntMatrix2x3) Apply(v Int3) image.Point {
// RatMatrix3 implements a 3x3 matrix with rational number entries. // RatMatrix3 implements a 3x3 matrix with rational number entries.
type RatMatrix3 [3][3]Rat type RatMatrix3 [3][3]Rat
// IdentityRatMatrix3x4 is the identity matrix for RatMatrix3x4. // IdentityRatMatrix3 is the identity matrix for RatMatrix3.
var IdentityRatMatrix3 = RatMatrix3{ var IdentityRatMatrix3 = RatMatrix3{
0: [3]Rat{0: {1, 1}}, 0: [3]Rat{0: {1, 1}},
1: [3]Rat{1: {1, 1}}, 1: [3]Rat{1: {1, 1}},

View file

@ -40,6 +40,11 @@ func Dot(p, q image.Point) int {
return p.X*q.X + p.Y*q.Y return p.X*q.X + p.Y*q.Y
} }
// CSign applies Sign componentwise to the Point.
func CSign(p image.Point) image.Point {
return image.Point{X: Sign(p.X), Y: Sign(p.Y)}
}
// ---------- Some other helpers ---------- // ---------- Some other helpers ----------
// FSign returns the sign of the float64 (-1, 0, or 1). // FSign returns the sign of the float64 (-1, 0, or 1).

View file

@ -21,6 +21,7 @@ import (
"math" "math"
) )
// Cardinal directions, as used in the return from PolygonExtrema.
const ( const (
East = iota East = iota
North North

View file

@ -54,6 +54,7 @@ func (SimpleProjection) Project(z int) image.Point { return image.Pt(0, z) }
// Projection uses two floats to define a custom projection. // Projection uses two floats to define a custom projection.
type Projection struct{ X, Y float64 } type Projection struct{ X, Y float64 }
// Sign returns the componentwise sign of π.
func (π Projection) Sign() image.Point { func (π Projection) Sign() image.Point {
return image.Pt(int(FSign(π.X)), int(FSign(π.Y))) return image.Pt(int(FSign(π.X)), int(FSign(π.Y)))
} }
@ -71,7 +72,8 @@ func (π Projection) Project(z int) image.Point {
// be used in e.g. a diametric projection (IntProjection{X:0, Y:2}). // be used in e.g. a diametric projection (IntProjection{X:0, Y:2}).
type IntProjection image.Point type IntProjection image.Point
func (π IntProjection) Sign() image.Point { return image.Point(π) } // Sign returns CSign(π).
func (π IntProjection) Sign() image.Point { return CSign(image.Point(π)) }
// Project returns (z/π.X, z/π.Y), unless π.X or π.Y are 0, in which case that // Project returns (z/π.X, z/π.Y), unless π.X or π.Y are 0, in which case that
// component is zero // component is zero

View file

@ -51,7 +51,7 @@ func (s *LinearSpline) Prepare() error {
return nil return nil
} }
// Interpolate, given x, returns y where (x,y) is a point on the spline. // Interpolate returns y where (x,y) is a point on the spline.
// If x is outside the spline, it extrapolates from either the first or // If x is outside the spline, it extrapolates from either the first or
// last segments of the spline. // last segments of the spline.
func (s *LinearSpline) Interpolate(x float64) float64 { func (s *LinearSpline) Interpolate(x float64) float64 {
@ -236,9 +236,9 @@ func (s *CubicSpline) Prepare() error {
return nil return nil
} }
// Interpolate, given x, returns y where (x,y) is a point on the spline. // Interpolate returns y where (x,y) is a point on the spline.
// If x is outside the spline, it extrapolates from either the first or // If x is outside the spline, it extrapolates from the first or last point
// last segments of the spline. // together with s.Preslope or s.Postslope.
func (s *CubicSpline) Interpolate(x float64) float64 { func (s *CubicSpline) Interpolate(x float64) float64 {
N := len(s.Points) N := len(s.Points)
if x < s.Points[0].X { if x < s.Points[0].X {