Noah Petherbridge
ba97c81b55
* pkg/loadscreen implements a global Loading Screen for loading heavy levels for playing or editing. * All chunks in a level are pre-rendered to bitmap before gameplay begins, which reduces stutter as chunks were being lazily rendered on first appearance before. * The loading screen can be played with in the developer console: $ loadscreen.Show() $ loadscreen.Hide() Along with ShowWithProgress(), SetProgress(float64) and IsActive() * Chunker: separate the concerns between Bitmaps an (SDL2) Textures. * Chunker.Prerender() converts a chunk to a bitmap (a Go image.Image) and caches it, only re-rendering if marked as dirty. * Chunker.Texture() will use the pre-cached bitmap if available to immediately produce the SDL2 texture. Other miscellaneous changes: * Added to the Colored Pencil palette: Sandstone * Added "perlin noise" brush pattern
232 lines
5.3 KiB
Go
232 lines
5.3 KiB
Go
// Package loadscreen implements a modal "Loading" screen for the game, which
|
|
// can be shown or hidden by gameplay scenes as needed.
|
|
package loadscreen
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
|
"git.kirsle.net/apps/doodle/pkg/level"
|
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
|
"git.kirsle.net/apps/doodle/pkg/uix"
|
|
"git.kirsle.net/go/render"
|
|
"git.kirsle.net/go/ui"
|
|
)
|
|
|
|
// Configuration values.
|
|
const (
|
|
ProgressWidth = 300
|
|
ProgressHeight = 34
|
|
)
|
|
|
|
// State variables for the loading screen.
|
|
var (
|
|
visible bool
|
|
withProgress bool
|
|
subtitle string // custom subtitle text, SetSubtitle().
|
|
|
|
// Animated title bar
|
|
titleBase = "Loading"
|
|
animState = 0
|
|
animation = []string{
|
|
". ",
|
|
".. ",
|
|
"...",
|
|
" ..",
|
|
" .",
|
|
" ",
|
|
}
|
|
animSpeed uint64 = 32
|
|
titleVar string
|
|
|
|
// UI widgets.
|
|
window *ui.Frame
|
|
canvas *uix.Canvas
|
|
secondary *ui.Label // subtitle text
|
|
progressTrough *ui.Frame
|
|
progressBar *ui.Frame
|
|
progressText *ui.Label
|
|
)
|
|
|
|
// Show the basic loading screen without a progress bar.
|
|
func Show() {
|
|
setup()
|
|
visible = true
|
|
withProgress = false
|
|
subtitle = ""
|
|
}
|
|
|
|
// ShowWithProgress initializes the loading screen with a progress bar starting at zero.
|
|
func ShowWithProgress() {
|
|
setup()
|
|
visible = true
|
|
withProgress = true
|
|
subtitle = ""
|
|
SetProgress(0)
|
|
}
|
|
|
|
// SetSubtitle specifies secondary text beneath the Loading banner.
|
|
// The subtitle is blanked on Show() and ShowWithProgress() and must
|
|
// be specified by the caller if desired. Pass multiple values for
|
|
// multiple lines of text.
|
|
func SetSubtitle(value ...string) {
|
|
subtitle = strings.Join(value, "\n")
|
|
}
|
|
|
|
// IsActive returns whether the loading screen is currently visible.
|
|
func IsActive() bool {
|
|
return visible
|
|
}
|
|
|
|
// Hide the loading screen.
|
|
func Hide() {
|
|
visible = false
|
|
}
|
|
|
|
// SetProgress sets the current progress value for loading screens having a progress bar.
|
|
func SetProgress(v float64) {
|
|
// Resize the progress bar in the trough.
|
|
if progressTrough != nil {
|
|
var (
|
|
troughSize = progressTrough.Size()
|
|
height = progressBar.Size().H
|
|
)
|
|
progressBar.Resize(render.Rect{
|
|
W: int(float64(troughSize.W-4) * v),
|
|
H: height,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Common function to initialize the loading screen.
|
|
func setup() {
|
|
if window != nil {
|
|
return
|
|
}
|
|
|
|
titleVar = titleBase + animation[animState]
|
|
|
|
// Create the parent container that will stretch full screen.
|
|
window = ui.NewFrame("Loadscreen Window")
|
|
window.SetBackground(render.RGBA(0, 0, 1, 40))
|
|
|
|
// "Loading" text.
|
|
label := ui.NewLabel(ui.Label{
|
|
TextVariable: &titleVar,
|
|
Font: balance.LoadScreenFont,
|
|
})
|
|
label.Compute(shmem.CurrentRenderEngine)
|
|
window.Place(label, ui.Place{
|
|
Top: 128,
|
|
Center: true,
|
|
})
|
|
|
|
// Subtitle text.
|
|
secondary = ui.NewLabel(ui.Label{
|
|
TextVariable: &subtitle,
|
|
Font: balance.LoadScreenSecondaryFont,
|
|
})
|
|
window.Place(secondary, ui.Place{
|
|
Top: 128 + label.Size().H + 64,
|
|
Center: true,
|
|
})
|
|
|
|
// Progress bar.
|
|
progressTrough = ui.NewFrame("Progress Trough")
|
|
progressTrough.Configure(ui.Config{
|
|
Width: ProgressWidth,
|
|
Height: ProgressHeight,
|
|
BorderSize: 2,
|
|
BorderStyle: ui.BorderSunken,
|
|
Background: render.DarkGrey,
|
|
})
|
|
window.Place(progressTrough, ui.Place{
|
|
Center: true,
|
|
Middle: true,
|
|
})
|
|
|
|
progressBar = ui.NewFrame("Progress Bar")
|
|
progressBar.Configure(ui.Config{
|
|
Width: 0,
|
|
Height: ProgressHeight - 4,
|
|
Background: render.Green,
|
|
})
|
|
progressTrough.Pack(progressBar, ui.Pack{
|
|
Side: ui.W,
|
|
})
|
|
}
|
|
|
|
// Loop is called on every game loop. If the loadscreen is not active, nothing happens.
|
|
// Otherwise the loading screen UI is drawn to screen.
|
|
func Loop(windowSize render.Rect, e render.Engine) {
|
|
if !visible {
|
|
return
|
|
}
|
|
|
|
if window != nil {
|
|
// Initialize the wallpaper canvas?
|
|
if canvas == nil {
|
|
canvas = uix.NewCanvas(128, false)
|
|
canvas.LoadLevel(e, &level.Level{
|
|
Chunker: level.NewChunker(100),
|
|
Palette: level.NewPalette(),
|
|
PageType: level.Bounded,
|
|
Wallpaper: "blueprint.png",
|
|
})
|
|
}
|
|
canvas.Resize(windowSize)
|
|
canvas.Compute(e)
|
|
canvas.Present(e, render.Origin)
|
|
|
|
window.Resize(windowSize)
|
|
window.Compute(e)
|
|
window.Present(e, render.Origin)
|
|
|
|
// Show/hide the progress bar.
|
|
progressTrough.Compute(e)
|
|
if withProgress && progressTrough.Hidden() {
|
|
progressTrough.Show()
|
|
} else if !withProgress && !progressTrough.Hidden() {
|
|
progressTrough.Hide()
|
|
}
|
|
|
|
// Show/hide the subtitle text.
|
|
if len(subtitle) > 0 && secondary.Hidden() {
|
|
secondary.Show()
|
|
} else if subtitle == "" && !secondary.Hidden() {
|
|
secondary.Hide()
|
|
}
|
|
|
|
// Animate the ellipses.
|
|
if shmem.Tick%animSpeed == 0 {
|
|
titleVar = titleBase + animation[animState]
|
|
animState++
|
|
if animState >= len(animation) {
|
|
animState = 0
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// PreloadAllChunkBitmaps is a helper function to eager cache all bitmap
|
|
// images of the chunks in a level drawing. It is designed to work with the
|
|
// loading screen and will set the Progress percent based on the total number
|
|
// of chunks vs. chunks remaining to pre-cache bitmaps from.
|
|
func PreloadAllChunkBitmaps(chunker *level.Chunker) {
|
|
loadChunksTarget := len(chunker.Chunks)
|
|
for {
|
|
remaining := chunker.PrerenderN(10)
|
|
|
|
// Set the load screen progress % based on number of chunks to render.
|
|
if loadChunksTarget > 0 {
|
|
percent := float64(loadChunksTarget-remaining) / float64(loadChunksTarget)
|
|
SetProgress(percent)
|
|
}
|
|
|
|
if remaining == 0 {
|
|
break
|
|
}
|
|
}
|
|
}
|