Improvements to the Debug Overlay Feature

* Scenes can insert custom key/value labels to the debug overlay and
  track string variables in real time
* Added ability to unthrottle FPS in main loop
This commit is contained in:
Noah 2019-04-09 18:28:08 -07:00
parent bd3dd41cea
commit 8fc4f39da0
8 changed files with 139 additions and 19 deletions

7
docs/Shell.md Normal file
View File

@ -0,0 +1,7 @@
# Developer Console
## Cheats
| Cheat Code | Effect |
|-------------------|--------------------------------|
| unleash the beast | Disable frame rate throttling. |

View File

@ -9,6 +9,16 @@ var (
// Speed to scroll a canvas with arrow keys in Edit Mode. // Speed to scroll a canvas with arrow keys in Edit Mode.
CanvasScrollSpeed int32 = 8 CanvasScrollSpeed int32 = 8
// Window scrolling behavior in Play Mode.
ScrollboxHoz = 64 // horizontal px from window border to start scrol
ScrollboxVert = 128
ScrollMaxVelocity = 24
// Player speeds
PlayerMaxVelocity = 12
PlayerAcceleration = 2
Gravity = 2
// Default chunk size for canvases. // Default chunk size for canvases.
ChunkSize = 128 ChunkSize = 128

View File

@ -22,6 +22,17 @@ func (c Command) Run(d *Doodle) error {
return nil return nil
} }
// Cheat codes
if c.Raw == "unleash the beast" {
if fpsDoNotCap {
d.Flash("Reset frame rate throttle to factory default FPS")
} else {
d.Flash("Unleashing as many frames as we can render!")
}
fpsDoNotCap = !fpsDoNotCap
return nil
}
switch c.Command { switch c.Command {
case "echo": case "echo":
d.Flash(c.ArgsLiteral) d.Flash(c.ArgsLiteral)

View File

@ -148,13 +148,15 @@ func (d *Doodle) Run() error {
} }
// Delay to maintain the target frames per second. // Delay to maintain the target frames per second.
var delay uint32
if !fpsDoNotCap {
elapsed := time.Now().Sub(start) elapsed := time.Now().Sub(start)
tmp := elapsed / time.Millisecond tmp := elapsed / time.Millisecond
var delay uint32
if TargetFPS-int(tmp) > 0 { // make sure it won't roll under if TargetFPS-int(tmp) > 0 { // make sure it won't roll under
delay = uint32(TargetFPS - int(tmp)) delay = uint32(TargetFPS - int(tmp))
} }
d.Engine.Delay(delay) d.Engine.Delay(delay)
}
// Track how long this frame took to measure FPS over time. // Track how long this frame took to measure FPS over time.
d.TrackFPS(delay) d.TrackFPS(delay)

View File

@ -32,6 +32,11 @@ type EditorScene struct {
Level *level.Level Level *level.Level
Doodad *doodads.Doodad Doodad *doodads.Doodad
// Custom debug overlay values.
debTool *string
debSwatch *string
debWorldIndex *string
// Last saved filename by the user. // Last saved filename by the user.
filename string filename string
} }
@ -43,6 +48,16 @@ func (s *EditorScene) Name() string {
// Setup the editor scene. // Setup the editor scene.
func (s *EditorScene) Setup(d *Doodle) error { func (s *EditorScene) Setup(d *Doodle) error {
// Debug overlay values.
s.debTool = new(string)
s.debSwatch = new(string)
s.debWorldIndex = new(string)
customDebugLabels = []debugLabel{
{"Pixel", s.debWorldIndex},
{"Tool", s.debTool},
{"Swatch", s.debSwatch},
}
// Initialize the user interface. It references the palette and such so it // Initialize the user interface. It references the palette and such so it
// must be initialized after those things. // must be initialized after those things.
s.d = d s.d = d
@ -107,6 +122,11 @@ func (s *EditorScene) Setup(d *Doodle) error {
// Loop the editor scene. // Loop the editor scene.
func (s *EditorScene) Loop(d *Doodle, ev *events.State) error { func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
// Update debug overlay values.
*s.debTool = s.UI.Canvas.Tool.String()
*s.debSwatch = s.UI.Canvas.Palette.ActiveSwatch.Name
*s.debWorldIndex = s.UI.Canvas.WorldIndexAt(s.UI.cursor).String()
// Has the window been resized? // Has the window been resized?
if resized := ev.Resized.Read(); resized { if resized := ev.Resized.Read(); resized {
w, h := d.Engine.WindowSize() w, h := d.Engine.WindowSize()
@ -262,6 +282,5 @@ func (s *EditorScene) SaveDoodad(filename string) error {
// Destroy the scene. // Destroy the scene.
func (s *EditorScene) Destroy() error { func (s *EditorScene) Destroy() error {
debugWorldIndex = render.Origin
return nil return nil
} }

View File

@ -164,11 +164,10 @@ func (u *EditorUI) Loop(ev *events.State) error {
// Update status bar labels. // Update status bar labels.
{ {
debugWorldIndex = u.Canvas.WorldIndexAt(u.cursor)
u.StatusMouseText = fmt.Sprintf("Rel:(%d,%d) Abs:(%s)", u.StatusMouseText = fmt.Sprintf("Rel:(%d,%d) Abs:(%s)",
ev.CursorX.Now, ev.CursorX.Now,
ev.CursorY.Now, ev.CursorY.Now,
debugWorldIndex, *u.Scene.debWorldIndex,
) )
u.StatusPaletteText = fmt.Sprintf("%s Tool", u.StatusPaletteText = fmt.Sprintf("%s Tool",
u.Canvas.Tool, u.Canvas.Tool,

View File

@ -33,37 +33,72 @@ var (
fpsFrames int fpsFrames int
fpsSkipped uint32 fpsSkipped uint32
fpsInterval uint32 = 1000 fpsInterval uint32 = 1000
fpsDoNotCap bool // remove the FPS delay cap in main loop
// XXX: some opt-in WorldIndex variables for the debug overlay. // Custom labels for individual Scenes to add debug info.
// This is the world pixel that the mouse cursor is over, customDebugLabels []debugLabel
// the Cursor + Scroll position of the canvas.
debugWorldIndex render.Point
) )
type debugLabel struct {
key string
variable *string
}
// DrawDebugOverlay draws the debug FPS text on the SDL canvas. // DrawDebugOverlay draws the debug FPS text on the SDL canvas.
func (d *Doodle) DrawDebugOverlay() { func (d *Doodle) DrawDebugOverlay() {
if !d.Debug || !DebugOverlay { if !DebugOverlay {
return return
} }
var framesSkipped = fmt.Sprintf("(skip: %dms)", fpsSkipped)
if fpsDoNotCap {
framesSkipped = "uncapped"
}
var ( var (
darken = balance.DebugStrokeDarken darken = balance.DebugStrokeDarken
Yoffset int32 = 20 // leave room for the menu bar Yoffset int32 = 20 // leave room for the menu bar
Xoffset int32 = 5 Xoffset int32 = 20
keys = []string{ keys = []string{
"FPS:", "FPS:",
"Scene:", "Scene:",
"Pixel:",
"Mouse:", "Mouse:",
} }
values = []string{ values = []string{
fmt.Sprintf("%d (skip: %dms)", fpsCurrent, fpsSkipped), fmt.Sprintf("%d %s", fpsCurrent, framesSkipped),
d.Scene.Name(), d.Scene.Name(),
debugWorldIndex.String(),
fmt.Sprintf("%d,%d", d.event.CursorX.Now, d.event.CursorY.Now), fmt.Sprintf("%d,%d", d.event.CursorX.Now, d.event.CursorY.Now),
} }
) )
// Insert custom keys.
for _, custom := range customDebugLabels {
keys = append(keys, custom.key)
if custom.variable == nil {
values = append(values, "<nil>")
} else if len(*custom.variable) == 0 {
values = append(values, `""`)
} else {
values = append(values, *custom.variable)
}
}
// Find the longest key to align the labels up.
var longest int
for _, key := range keys {
if len(key) > longest {
longest = len(key)
}
}
// Space pad the keys for alignment.
for i, key := range keys {
if len(key) < longest {
key = strings.Repeat(" ", longest-len(key)) + key
keys[i] = key
}
}
key := ui.NewLabel(ui.Label{ key := ui.NewLabel(ui.Label{
Text: strings.Join(keys, "\n"), Text: strings.Join(keys, "\n"),
Font: render.Text{ Font: render.Text{
@ -97,7 +132,7 @@ func (d *Doodle) DrawDebugOverlay() {
// DrawCollisionBox draws the collision box around a Doodad. // DrawCollisionBox draws the collision box around a Doodad.
func (d *Doodle) DrawCollisionBox(actor doodads.Actor) { func (d *Doodle) DrawCollisionBox(actor doodads.Actor) {
if !d.Debug || !DebugCollision { if !DebugCollision {
return return
} }

View File

@ -22,6 +22,12 @@ type PlayScene struct {
d *Doodle d *Doodle
drawing *uix.Canvas drawing *uix.Canvas
// Custom debug labels.
debPosition *string
debViewport *string
debScroll *string
debWorldIndex *string
// Player character // Player character
Player doodads.Actor Player doodads.Actor
} }
@ -34,6 +40,20 @@ func (s *PlayScene) Name() string {
// Setup the play scene. // Setup the play scene.
func (s *PlayScene) Setup(d *Doodle) error { func (s *PlayScene) Setup(d *Doodle) error {
s.d = d s.d = d
// Initialize debug overlay values.
s.debPosition = new(string)
s.debViewport = new(string)
s.debScroll = new(string)
s.debWorldIndex = new(string)
customDebugLabels = []debugLabel{
{"Pixel:", s.debWorldIndex},
{"Player:", s.debPosition},
{"Viewport:", s.debViewport},
{"Scroll:", s.debScroll},
}
// Initialize the drawing canvas.
s.drawing = uix.NewCanvas(balance.ChunkSize, false) s.drawing = uix.NewCanvas(balance.ChunkSize, false)
s.drawing.MoveTo(render.Origin) s.drawing.MoveTo(render.Origin)
s.drawing.Resize(render.NewRect(int32(d.width), int32(d.height))) s.drawing.Resize(render.NewRect(int32(d.width), int32(d.height)))
@ -63,6 +83,23 @@ func (s *PlayScene) Setup(d *Doodle) error {
// Loop the editor scene. // Loop the editor scene.
func (s *PlayScene) Loop(d *Doodle, ev *events.State) error { func (s *PlayScene) Loop(d *Doodle, ev *events.State) error {
// Update debug overlay values.
*s.debWorldIndex = s.drawing.WorldIndexAt(render.NewPoint(ev.CursorX.Now, ev.CursorY.Now)).String()
*s.debPosition = s.Player.Position().String()
*s.debViewport = s.drawing.Viewport().String()
*s.debScroll = s.drawing.Scroll.String()
// Has the window been resized?
if resized := ev.Resized.Read(); resized {
w, h := d.Engine.WindowSize()
if w != d.width || h != d.height {
d.width = w
d.height = h
s.drawing.Resize(render.NewRect(int32(d.width), int32(d.height)))
return nil
}
}
// Switching to Edit Mode? // Switching to Edit Mode?
if ev.KeyName.Read() == "e" { if ev.KeyName.Read() == "e" {
log.Info("Edit Mode, Go!") log.Info("Edit Mode, Go!")