doodle/pkg/story_scene.go
Noah Petherbridge 215ed5c847 Stabilize Load Screen by Deferring SDL2 Calls
* The loading screen for Edit and Play modes is stable and the risk of
  game crash is removed. The root cause was the setupAsync() functions
  running on a background goroutine, and running SDL2 draw functions
  while NOT on the main thread, which causes problems.
* The fix is all SDL2 Texture draws become lazy loaded: when the main
  thread is presenting, any Wallpaper or ui.Image that has no texture
  yet gets one created at that time from the cached image.Image.
* All internal game logic then uses image.Image types, to cache bitmaps
  of Level Chunks, Wallpaper images, Sprite icons, etc. and the game is
  free to prepare these asynchronously; only the main thread ever
  Presents and the SDL2 textures initialize on first appearance.
* Several functions had arguments cleaned up: Canvas.LoadLevel() does
  not need the render.Engine as (e.g. wallpaper) textures don't render
  at that stage.
2021-07-19 17:14:00 -07:00

145 lines
3.2 KiB
Go

package doodle
import (
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/campaign"
"git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/uix"
"git.kirsle.net/go/render"
"git.kirsle.net/go/render/event"
"git.kirsle.net/go/ui"
)
// StoryScene manages the "Story Mode" menu selection screen.
type StoryScene struct {
// Private variables.
d *Doodle
running bool
// Background wallpaper canvas.
canvas *uix.Canvas
// UI widgets.
supervisor *ui.Supervisor
campaignFrame *ui.Frame // Select a Campaign screen
levelSelectFrame *ui.Frame // Select a level in the campaign screen
// Pointer to the currently active frame.
activeFrame *ui.Frame
}
// Name of the scene.
func (s *StoryScene) Name() string {
return "Story"
}
// GotoStoryMenu initializes the story menu scene.
func (d *Doodle) GotoStoryMenu() {
log.Info("Loading Story Scene")
scene := &StoryScene{}
d.Goto(scene)
}
// Setup the play scene.
func (s *StoryScene) Setup(d *Doodle) error {
s.d = d
// Set up the background wallpaper canvas.
s.canvas = uix.NewCanvas(100, false)
s.canvas.Resize(render.NewRect(d.width, d.height))
s.canvas.LoadLevel(&level.Level{
Chunker: level.NewChunker(100),
Palette: level.NewPalette(),
PageType: level.Bounded,
Wallpaper: "notebook.png",
})
s.supervisor = ui.NewSupervisor()
// Set up the sub-screens of this scene.
s.campaignFrame = s.setupCampaignFrame()
s.levelSelectFrame = s.setupLevelSelectFrame()
s.activeFrame = s.campaignFrame
return nil
}
// setupCampaignFrame sets up the Campaign List screen.
func (s *StoryScene) setupCampaignFrame() *ui.Frame {
var frame = ui.NewFrame("List Frame")
frame.SetBackground(render.RGBA(0, 0, 255, 20))
// Title label
labelTitle := ui.NewLabel(ui.Label{
Text: "Select a Story",
Font: balance.TitleScreenFont,
})
labelTitle.Compute(s.d.Engine)
frame.Place(labelTitle, ui.Place{
Top: 120,
Center: true,
})
// Buttons for campaign selection.
{
campaignFiles, err := campaign.List()
if err != nil {
log.Error("campaign.List: %s", err)
}
_ = campaignFiles
// for _, file := range campaignFiles {
//
// }
}
frame.Resize(render.NewRect(s.d.width, s.d.height))
frame.Compute(s.d.Engine)
return frame
}
// setupLevelSelectFrame sets up the Level Select screen.
func (s *StoryScene) setupLevelSelectFrame() *ui.Frame {
var frame = ui.NewFrame("List Frame")
return frame
}
// Loop the story scene.
func (s *StoryScene) Loop(d *Doodle, ev *event.State) error {
s.supervisor.Loop(ev)
// Has the window been resized?
if ev.WindowResized {
w, h := d.Engine.WindowSize()
if w != d.width || h != d.height {
d.width = w
d.height = h
s.canvas.Resize(render.NewRect(d.width, d.height))
s.activeFrame.Resize(render.NewRect(d.width, d.height))
s.activeFrame.Compute(d.Engine)
return nil
}
}
return nil
}
// Draw the pixels on this frame.
func (s *StoryScene) Draw(d *Doodle) error {
// Draw the background canvas.
s.canvas.Present(d.Engine, render.Origin)
// Draw the active screen.
s.activeFrame.Present(d.Engine, render.Origin)
return nil
}
// Destroy the scene.
func (s *StoryScene) Destroy() error {
return nil
}