Demo Running Level as Title Screen Wallpaper
* Load SDL2 fonts from go-bindata storage so we don't have to ship external font files on disk. * Dedupe names of doodads so we don't show double on the front-end (go-bindata bundled doodads + those on local filesystem) * Use go-bindata for accessing wallpaper images. * Better flashed messages walking you through the Link Tool. * Stylize the title screen (MainScene) by rendering a live example level as the background wallpaper, with mobile doodads in motion.
This commit is contained in:
parent
54776ec9e1
commit
3d08291bc5
6
Makefile
6
Makefile
|
@ -42,12 +42,12 @@ build-debug:
|
|||
# `make bindata` generates the embedded binary assets package.
|
||||
.PHONY: bindata
|
||||
bindata:
|
||||
go-bindata -pkg bindata -o pkg/bindata/bindata.go assets/... fonts/
|
||||
go-bindata -pkg bindata -o pkg/bindata/bindata.go assets/...
|
||||
|
||||
# `make bindata-dev` generates the debug version of bindata package.
|
||||
.PHONY: bindata-dev
|
||||
bindata-dev:
|
||||
go-bindata -debug -pkg bindata -o pkg/bindata/bindata.go assets/... fonts/
|
||||
go-bindata -debug -pkg bindata -o pkg/bindata/bindata.go assets/...
|
||||
|
||||
# `make wasm` builds the WebAssembly port.
|
||||
.PHONY: wasm
|
||||
|
@ -102,7 +102,7 @@ test:
|
|||
dist: doodads bindata build
|
||||
mkdir -p dist/doodle-$(VERSION)
|
||||
cp bin/* dist/doodle-$(VERSION)/
|
||||
cp -r assets fonts README.md dist/doodle-$(VERSION)/
|
||||
cp -r README.md dist/doodle-$(VERSION)/
|
||||
cd dist && tar -czvf doodle-$(VERSION).tar.gz doodle-$(VERSION)
|
||||
cd dist && zip -r doodle-$(VERSION).zip doodle-$(VERSION)
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/lib/render/sdl"
|
||||
doodle "git.kirsle.net/apps/doodle/pkg"
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/bindata"
|
||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||
"github.com/urfave/cli"
|
||||
|
||||
|
@ -80,6 +81,20 @@ func main() {
|
|||
balance.Height,
|
||||
)
|
||||
|
||||
// Load the SDL fonts in from bindata storage.
|
||||
if fonts, err := bindata.AssetDir("assets/fonts"); err == nil {
|
||||
for _, file := range fonts {
|
||||
data, err := bindata.Asset("assets/fonts/" + file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sdl.InstallFont(file, data)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
game := doodle.New(c.Bool("debug"), engine)
|
||||
game.SetupEngine()
|
||||
if c.Bool("guitest") {
|
||||
|
|
|
@ -13,7 +13,7 @@ var (
|
|||
DebugWindowEvents = false
|
||||
DebugMouseEvents = false
|
||||
DebugClickEvents = false
|
||||
DebugKeyEvents = true
|
||||
DebugKeyEvents = false
|
||||
)
|
||||
|
||||
// Poll for events.
|
||||
|
|
|
@ -3,6 +3,7 @@ package sdl
|
|||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.kirsle.net/apps/doodle/lib/events"
|
||||
"git.kirsle.net/apps/doodle/lib/render"
|
||||
|
@ -11,9 +12,29 @@ import (
|
|||
)
|
||||
|
||||
// TODO: font filenames
|
||||
var defaultFontFilename = "./fonts/DejaVuSans.ttf"
|
||||
var defaultFontFilename = "DejaVuSans.ttf"
|
||||
|
||||
var fonts = map[string]*ttf.Font{}
|
||||
// Font holds cached SDL_TTF structures for loaded fonts. They are created
|
||||
// automatically when fonts are either preinstalled (InstallFont) or loaded for
|
||||
// the first time as demanded by the DrawText method.
|
||||
type Font struct {
|
||||
Filename string
|
||||
data []byte // raw binary data of font
|
||||
ttf *ttf.Font
|
||||
}
|
||||
|
||||
var (
|
||||
fonts = map[string]*ttf.Font{} // keys like "DejaVuSans@14" by font size
|
||||
installedFont = map[string][]byte{} // installed font files' binary handles
|
||||
fontsMu sync.RWMutex
|
||||
)
|
||||
|
||||
// InstallFont preloads the font cache using TTF binary data in memory.
|
||||
func InstallFont(filename string, binary []byte) {
|
||||
fontsMu.Lock()
|
||||
installedFont[filename] = binary
|
||||
fontsMu.Unlock()
|
||||
}
|
||||
|
||||
// LoadFont loads and caches the font at a given size.
|
||||
func LoadFont(filename string, size int) (*ttf.Font, error) {
|
||||
|
@ -27,10 +48,30 @@ func LoadFont(filename string, size int) (*ttf.Font, error) {
|
|||
return font, nil
|
||||
}
|
||||
|
||||
font, err := ttf.OpenFont(filename, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Do we have this font in memory?
|
||||
var (
|
||||
font *ttf.Font
|
||||
err error
|
||||
)
|
||||
|
||||
if binary, ok := installedFont[filename]; ok {
|
||||
var RWops *sdl.RWops
|
||||
RWops, err = sdl.RWFromMem(binary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LoadFont(%s): RWFromMem: %s", filename, err)
|
||||
}
|
||||
|
||||
font, err = ttf.OpenFontRW(RWops, 0, size)
|
||||
} else {
|
||||
font, err = ttf.OpenFont(filename, size)
|
||||
}
|
||||
|
||||
// Error opening the font?
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("LoadFont(%s): %s", filename, err)
|
||||
}
|
||||
|
||||
// Cache this font name and size.
|
||||
fonts[keyName] = font
|
||||
|
||||
return font, nil
|
||||
|
|
|
@ -16,7 +16,7 @@ var (
|
|||
***************/
|
||||
|
||||
// Debug overlay (FPS etc.) settings.
|
||||
DebugFontFilename = "./fonts/DejaVuSans-Bold.ttf"
|
||||
DebugFontFilename = "DejaVuSans-Bold.ttf"
|
||||
DebugFontSize = 16
|
||||
DebugLabelColor = render.MustHexColor("#FF9900")
|
||||
DebugValueColor = render.MustHexColor("#00CCFF")
|
||||
|
|
|
@ -4,7 +4,7 @@ import "git.kirsle.net/apps/doodle/lib/render"
|
|||
|
||||
// Shell related variables.
|
||||
var (
|
||||
ShellFontFilename = "./fonts/DejaVuSansMono.ttf"
|
||||
ShellFontFilename = "DejaVuSansMono.ttf"
|
||||
ShellBackgroundColor = render.RGBA(0, 20, 40, 200)
|
||||
ShellForegroundColor = render.RGBA(0, 153, 255, 255)
|
||||
ShellPromptColor = render.White
|
||||
|
|
|
@ -14,7 +14,7 @@ var (
|
|||
OutlineColor: render.Black,
|
||||
}
|
||||
TitleFont = render.Text{
|
||||
FontFilename: "./fonts/DejaVuSans-Bold.ttf",
|
||||
FontFilename: "DejaVuSans-Bold.ttf",
|
||||
Size: 12,
|
||||
Padding: 4,
|
||||
Color: render.White,
|
||||
|
@ -47,7 +47,7 @@ var (
|
|||
// LabelFont is the font for strong labels in UI.
|
||||
LabelFont = render.Text{
|
||||
Size: 12,
|
||||
FontFilename: "./fonts/DejaVuSans-Bold.ttf",
|
||||
FontFilename: "DejaVuSans-Bold.ttf",
|
||||
Padding: 4,
|
||||
Color: render.Black,
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ var (
|
|||
DragColor = render.MustHexColor("#0099FF")
|
||||
|
||||
PlayButtonFont = render.Text{
|
||||
FontFilename: "./fonts/DejaVuSans-Bold.ttf",
|
||||
FontFilename: "DejaVuSans-Bold.ttf",
|
||||
Size: 16,
|
||||
Padding: 4,
|
||||
Color: render.RGBA(255, 255, 0, 255),
|
||||
|
|
|
@ -46,7 +46,18 @@ func ListDoodads() ([]string, error) {
|
|||
// Append user doodads.
|
||||
userFiles, err := userdir.ListDoodads()
|
||||
names = append(names, userFiles...)
|
||||
return names, err
|
||||
|
||||
// Deduplicate names.
|
||||
var uniq = map[string]interface{}{}
|
||||
var result []string
|
||||
for _, name := range names {
|
||||
if _, ok := uniq[name]; !ok {
|
||||
uniq[name] = nil
|
||||
result = append(result, name)
|
||||
}
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// LoadFile reads a doodad file from disk, checking a few locations.
|
||||
|
|
|
@ -302,14 +302,16 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
|
|||
}
|
||||
|
||||
// A link event to connect two actors together.
|
||||
drawing.OnLinkActors = func(a, b *level.Actor) {
|
||||
d.Flash("Link %s and %s", a.Filename, b.Filename)
|
||||
idA, idB := a.ID(), b.ID()
|
||||
a.AddLink(idB)
|
||||
b.AddLink(idA)
|
||||
drawing.OnLinkActors = func(a, b *uix.Actor) {
|
||||
// The actors are a uix.Actor which houses a level.Actor which we
|
||||
// want to update to map each other's IDs.
|
||||
idA, idB := a.Actor.ID(), b.Actor.ID()
|
||||
a.Actor.AddLink(idB)
|
||||
b.Actor.AddLink(idA)
|
||||
|
||||
// Reset the Link tool.
|
||||
drawing.Tool = uix.ActorTool
|
||||
d.Flash("Linked '%s' and '%s' together", a.Doodad.Title, b.Doodad.Title)
|
||||
}
|
||||
|
||||
// Set up the drop handler for draggable doodads.
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
"git.kirsle.net/apps/doodle/lib/ui"
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/branding"
|
||||
"git.kirsle.net/apps/doodle/pkg/filesystem"
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/apps/doodle/pkg/scripting"
|
||||
"git.kirsle.net/apps/doodle/pkg/uix"
|
||||
)
|
||||
|
||||
|
@ -17,7 +19,8 @@ type MainScene struct {
|
|||
frame *ui.Frame
|
||||
|
||||
// Background wallpaper canvas.
|
||||
canvas *uix.Canvas
|
||||
scripting *scripting.Supervisor
|
||||
canvas *uix.Canvas
|
||||
}
|
||||
|
||||
// Name of the scene.
|
||||
|
@ -29,18 +32,9 @@ func (s *MainScene) Name() string {
|
|||
func (s *MainScene) Setup(d *Doodle) error {
|
||||
s.Supervisor = ui.NewSupervisor()
|
||||
|
||||
// Set up the background wallpaper canvas.
|
||||
s.canvas = uix.NewCanvas(100, false)
|
||||
s.canvas.Resize(render.Rect{
|
||||
W: int32(d.width),
|
||||
H: int32(d.height),
|
||||
})
|
||||
s.canvas.LoadLevel(d.Engine, &level.Level{
|
||||
Chunker: level.NewChunker(100),
|
||||
Palette: level.NewPalette(),
|
||||
PageType: level.Bounded,
|
||||
Wallpaper: "notebook.png",
|
||||
})
|
||||
if err := s.SetupDemoLevel(d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Main UI button frame.
|
||||
frame := ui.NewFrame("frame")
|
||||
|
@ -78,10 +72,52 @@ func (s *MainScene) Setup(d *Doodle) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetupDemoLevel configures the wallpaper behind the New screen,
|
||||
// which demos a title screen demo level.
|
||||
func (s *MainScene) SetupDemoLevel(d *Doodle) error {
|
||||
// Set up the background wallpaper canvas.
|
||||
s.canvas = uix.NewCanvas(100, false)
|
||||
s.canvas.Scrollable = true
|
||||
s.canvas.Resize(render.Rect{
|
||||
W: int32(d.width),
|
||||
H: int32(d.height),
|
||||
})
|
||||
|
||||
// Title screen level to load.
|
||||
lvlName, _ := filesystem.FindFile("example1.level")
|
||||
lvl, err := level.LoadJSON(lvlName)
|
||||
if err != nil {
|
||||
log.Error("Error loading title-screen.level: %s", err)
|
||||
}
|
||||
|
||||
s.canvas.LoadLevel(d.Engine, lvl)
|
||||
s.canvas.InstallActors(lvl.Actors)
|
||||
|
||||
// Load all actor scripts.
|
||||
s.scripting = scripting.NewSupervisor()
|
||||
s.canvas.SetScriptSupervisor(s.scripting)
|
||||
if err := s.scripting.InstallScripts(lvl); err != nil {
|
||||
log.Error("Error with title screen level scripts: %s", err)
|
||||
}
|
||||
|
||||
// Run all actors scripts main function to start them off.
|
||||
if err := s.canvas.InstallScripts(); err != nil {
|
||||
log.Error("Error running actor main() functions: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loop the editor scene.
|
||||
func (s *MainScene) Loop(d *Doodle, ev *events.State) error {
|
||||
s.Supervisor.Loop(ev)
|
||||
|
||||
if err := s.scripting.Loop(); err != nil {
|
||||
log.Error("MainScene.Loop: scripting.Loop: %s", err)
|
||||
}
|
||||
|
||||
s.canvas.Loop(ev)
|
||||
|
||||
if resized := ev.Resized.Read(); resized {
|
||||
w, h := d.Engine.WindowSize()
|
||||
d.width = w
|
||||
|
@ -103,6 +139,14 @@ func (s *MainScene) Draw(d *Doodle) error {
|
|||
|
||||
s.canvas.Present(d.Engine, render.Origin)
|
||||
|
||||
// Draw a sheen over the level for clarity.
|
||||
d.Engine.DrawBox(render.RGBA(255, 255, 254, 128), render.Rect{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
W: int32(d.width),
|
||||
H: int32(d.height),
|
||||
})
|
||||
|
||||
label := ui.NewLabel(ui.Label{
|
||||
Text: branding.AppName,
|
||||
Font: render.Text{
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/lib/render"
|
||||
"git.kirsle.net/apps/doodle/lib/ui"
|
||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||
"git.kirsle.net/apps/doodle/pkg/bindata"
|
||||
"git.kirsle.net/apps/doodle/pkg/doodads"
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
|
@ -69,7 +70,7 @@ type Canvas struct {
|
|||
// -- WHEN Canvas.Tool is "Link" --
|
||||
// When the Canvas wants to link two actors together. Arguments are the IDs
|
||||
// of the two actors.
|
||||
OnLinkActors func(a, b *level.Actor)
|
||||
OnLinkActors func(a, b *Actor)
|
||||
linkFirst *Actor
|
||||
|
||||
// Tracking pixels while editing. TODO: get rid of pixelHistory?
|
||||
|
@ -129,9 +130,12 @@ func (w *Canvas) LoadLevel(e render.Engine, level *level.Level) {
|
|||
// TODO: wallpaper paths
|
||||
filename := "assets/wallpapers/" + level.Wallpaper
|
||||
if runtime.GOOS != "js" {
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
log.Error("LoadLevel: %s", err)
|
||||
filename = "assets/wallpapers/notebook.png" // XXX TODO
|
||||
// Check if the wallpaper wasn't found. Check bindata and file system.
|
||||
if _, err := bindata.Asset(filename); err != nil {
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
log.Error("LoadLevel: wallpaper %s did not appear to exist, default to notebook.png", filename)
|
||||
filename = "assets/wallpapers/notebook.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
package uix
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.kirsle.net/apps/doodle/pkg/shmem"
|
||||
)
|
||||
|
||||
// LinkStart initializes the Link tool.
|
||||
func (w *Canvas) LinkStart() {
|
||||
|
@ -13,10 +17,13 @@ func (w *Canvas) LinkAdd(a *Actor) error {
|
|||
if w.linkFirst == nil {
|
||||
// First click, hold onto this actor.
|
||||
w.linkFirst = a
|
||||
shmem.Flash("Doodad '%s' selected, click the next Doodad to link it to",
|
||||
a.Doodad.Title,
|
||||
)
|
||||
} else {
|
||||
// Second click, call the OnLinkActors handler with the two actors.
|
||||
if w.OnLinkActors != nil {
|
||||
w.OnLinkActors(w.linkFirst.Actor, a.Actor)
|
||||
w.OnLinkActors(w.linkFirst, a)
|
||||
} else {
|
||||
return errors.New("Canvas.LinkAdd: no OnLinkActors handler is ready")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package wallpaper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/draw"
|
||||
"os"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.kirsle.net/apps/doodle/lib/render"
|
||||
"git.kirsle.net/apps/doodle/pkg/bindata"
|
||||
)
|
||||
|
||||
// Wallpaper is a repeatable background image to go behind levels.
|
||||
|
@ -64,14 +66,28 @@ func FromFile(e render.Engine, filename string) (*Wallpaper, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Try and get an image object by any means.
|
||||
var (
|
||||
img image.Image
|
||||
format string
|
||||
imgErr error
|
||||
)
|
||||
|
||||
// Try the bindata store.
|
||||
if data, err := bindata.Asset(filename); err == nil {
|
||||
fh := bytes.NewBuffer(data)
|
||||
img, format, imgErr = image.Decode(fh)
|
||||
} else {
|
||||
fh, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, format, imgErr = image.Decode(fh)
|
||||
}
|
||||
|
||||
img, format, err := image.Decode(fh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Image loading error?
|
||||
if imgErr != nil {
|
||||
return nil, imgErr
|
||||
}
|
||||
|
||||
// Ugly hack: make it an image.RGBA because the thing we get tends to be
|
||||
|
|
|
@ -20,7 +20,6 @@ func main() {
|
|||
go watchChanges()
|
||||
|
||||
http.Handle("/", http.FileServer(http.Dir(".")))
|
||||
http.Handle("/fonts", http.FileServer(http.Dir("../fonts/")))
|
||||
http.HandleFunc(wasm, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/wasm")
|
||||
http.ServeFile(w, r, "."+wasm)
|
||||
|
|
Loading…
Reference in New Issue
Block a user