Noah Petherbridge
6713dd7bfc
Implement scrolling behavior in Play Mode by allowing the Canvas to follow a specific actor and keep it in view. The Canvas has a FollowActor property which holds an ID of the actor to follow (if blank, no actor is being followed). In Play Mode the Player is followed and when they get too close to the left or right edges of the screen, the level will scroll to try and catch them. If the player is moving very fast they can outrun the camera. The bounded levels are enforced in Play Mode and the camera won't scroll to view pixels out-of-bounds and the Doodad actors inside the level aren't allowed to exit its boundaries. This is global, not only for the Player doodad but any Doodad that came with the level as well. Other changes: - Restructured Canvas widget code into many new files. The Canvas widget is shaping up to be where most of the magic happens, which is okay because it's close to the action and pulling the strings from outside would be harder, even tho as a UI element you think it should be lightweight. - Debug Overlay: added room for Scenes to insert their own custom Debug Overlay key/value pairs (the values are string pointers so the Scene can update them freely): - The core labels are FPS, Scene and Mouse. The Pixel (world coordinate under cursor) is removed from the core labels. - Edit Scene provides Pixel, Tool and Swatch - Play Scene provides Pixel, Player, Viewport, Scroll
161 lines
3.7 KiB
Go
161 lines
3.7 KiB
Go
package doodle
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.kirsle.net/apps/doodle/balance"
|
|
"git.kirsle.net/apps/doodle/doodads"
|
|
"git.kirsle.net/apps/doodle/render"
|
|
"git.kirsle.net/apps/doodle/ui"
|
|
)
|
|
|
|
// Frames to cache for FPS calculation.
|
|
const maxSamples = 100
|
|
|
|
// Debug mode options, these can be enabled in the dev console
|
|
// like: boolProp DebugOverlay true
|
|
var (
|
|
DebugOverlay = true
|
|
DebugCollision = true
|
|
|
|
DebugTextPadding int32 = 8
|
|
DebugTextSize = 24
|
|
DebugTextColor = render.SkyBlue
|
|
DebugTextStroke = render.Grey
|
|
DebugTextShadow = render.Black
|
|
)
|
|
|
|
var (
|
|
fpsCurrentTicks uint32 // current time we get sdl.GetTicks()
|
|
fpsLastTime uint32 // last time we printed the fpsCurrentTicks
|
|
fpsCurrent int
|
|
fpsFrames int
|
|
fpsSkipped uint32
|
|
fpsInterval uint32 = 1000
|
|
|
|
// Custom labels for scenes to add to the debug overlay view.
|
|
customDebugLabels []debugLabel
|
|
)
|
|
|
|
type debugLabel struct {
|
|
key string
|
|
variable *string
|
|
}
|
|
|
|
// DrawDebugOverlay draws the debug FPS text on the SDL canvas.
|
|
func (d *Doodle) DrawDebugOverlay() {
|
|
if !DebugOverlay {
|
|
return
|
|
}
|
|
|
|
var (
|
|
darken = balance.DebugStrokeDarken
|
|
Yoffset int32 = 20 // leave room for the menu bar
|
|
Xoffset int32 = 5
|
|
keys = []string{
|
|
"FPS:",
|
|
"Scene:",
|
|
"Mouse:",
|
|
}
|
|
values = []string{
|
|
fmt.Sprintf("%d (skip: %dms)", fpsCurrent, fpsSkipped),
|
|
d.Scene.Name(),
|
|
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.
|
|
var longest int
|
|
for _, key := range keys {
|
|
if len(key) > longest {
|
|
longest = len(key)
|
|
}
|
|
}
|
|
|
|
// Space pad the keys to align them.
|
|
for i, key := range keys {
|
|
if len(key) < longest {
|
|
key = strings.Repeat(" ", longest-len(key)) + key
|
|
keys[i] = key
|
|
}
|
|
}
|
|
|
|
key := ui.NewLabel(ui.Label{
|
|
Text: strings.Join(keys, "\n"),
|
|
Font: render.Text{
|
|
Size: balance.DebugFontSize,
|
|
FontFilename: balance.ShellFontFilename,
|
|
Color: balance.DebugLabelColor,
|
|
Stroke: balance.DebugLabelColor.Darken(darken),
|
|
},
|
|
})
|
|
key.Compute(d.Engine)
|
|
key.Present(d.Engine, render.NewPoint(
|
|
DebugTextPadding+Xoffset,
|
|
DebugTextPadding+Yoffset,
|
|
))
|
|
|
|
value := ui.NewLabel(ui.Label{
|
|
Text: strings.Join(values, "\n"),
|
|
Font: render.Text{
|
|
Size: balance.DebugFontSize,
|
|
FontFilename: balance.DebugFontFilename,
|
|
Color: balance.DebugValueColor,
|
|
Stroke: balance.DebugValueColor.Darken(darken),
|
|
},
|
|
})
|
|
value.Compute(d.Engine)
|
|
value.Present(d.Engine, render.NewPoint(
|
|
DebugTextPadding+Xoffset+key.Size().W+DebugTextPadding,
|
|
DebugTextPadding+Yoffset, // padding to not overlay menu bar
|
|
))
|
|
}
|
|
|
|
// DrawCollisionBox draws the collision box around a Doodad.
|
|
func (d *Doodle) DrawCollisionBox(actor doodads.Actor) {
|
|
if !DebugCollision {
|
|
return
|
|
}
|
|
|
|
var (
|
|
rect = doodads.GetBoundingRect(actor)
|
|
box = doodads.GetCollisionBox(rect)
|
|
)
|
|
|
|
d.Engine.DrawLine(render.DarkGreen, box.Top[0], box.Top[1])
|
|
d.Engine.DrawLine(render.DarkBlue, box.Bottom[0], box.Bottom[1])
|
|
d.Engine.DrawLine(render.DarkYellow, box.Left[0], box.Left[1])
|
|
d.Engine.DrawLine(render.Red, box.Right[0], box.Right[1])
|
|
}
|
|
|
|
// TrackFPS shows the current FPS once per second.
|
|
func (d *Doodle) TrackFPS(skipped uint32) {
|
|
fpsFrames++
|
|
fpsCurrentTicks = d.Engine.GetTicks()
|
|
|
|
// Skip the first second.
|
|
if fpsCurrentTicks < fpsInterval {
|
|
return
|
|
}
|
|
|
|
if fpsLastTime < fpsCurrentTicks-fpsInterval {
|
|
fpsLastTime = fpsCurrentTicks
|
|
fpsCurrent = fpsFrames
|
|
fpsFrames = 0
|
|
fpsSkipped = skipped
|
|
}
|
|
}
|