Noah Petherbridge
d14eaf7df2
* The F4 key to draw collision boxes works reliably again: it draws the player's hitbox in world-space using the canvas.DrawStrokes() function, rather than in screen-space so it follows the player reliably. * The F4 key also draws hitboxes for ALL other actors in the level: buttons, enemies, doors, etc. * The level geometry collision function is updated to respect a doodad's declared Hitbox from their script, which may result in a smaller box than their raw Canvas size. The result is tighter collision between doodads, and Boy's sprite is rather narrow for its square Canvas so collision on rightward geometry is tighter for the player character. * Collision checks between actors also respect the actor's declared hitboxes now, allowing for Boy to get even closer to a locked door before being blocked.
198 lines
4.8 KiB
Go
198 lines
4.8 KiB
Go
package doodle
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
|
"git.kirsle.net/apps/doodle/pkg/collision"
|
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
|
"git.kirsle.net/apps/doodle/pkg/uix"
|
|
"git.kirsle.net/go/render"
|
|
"git.kirsle.net/go/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 = false
|
|
DebugCollision = false
|
|
|
|
DebugTextPadding = 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
|
|
fpsDoNotCap bool // remove the FPS delay cap in main loop
|
|
|
|
// Custom labels for individual Scenes to add debug info.
|
|
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 framesSkipped = fmt.Sprintf("(skip: %dms)", fpsSkipped)
|
|
if fpsDoNotCap {
|
|
framesSkipped = "uncapped"
|
|
}
|
|
|
|
var (
|
|
darken = balance.DebugStrokeDarken
|
|
Yoffset = 20 // leave room for the menu bar
|
|
Xoffset = 20
|
|
keys = []string{
|
|
"FPS:",
|
|
"Scene:",
|
|
"Mouse:",
|
|
}
|
|
values = []string{
|
|
fmt.Sprintf("%d %s", fpsCurrent, framesSkipped),
|
|
d.Scene.Name(),
|
|
fmt.Sprintf("%d,%d", d.event.CursorX, d.event.CursorY),
|
|
}
|
|
)
|
|
|
|
// 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{
|
|
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.
|
|
//
|
|
// The canvas will be the level Canvas, and the collision box is drawn in world
|
|
// space using the canvas.DrawStrokes function.
|
|
func (d *Doodle) DrawCollisionBox(canvas *uix.Canvas, actor *uix.Actor) {
|
|
if !DebugCollision {
|
|
return
|
|
}
|
|
|
|
var (
|
|
rect = collision.GetBoundingRect(actor)
|
|
box = collision.GetCollisionBox(rect)
|
|
hitbox = actor.Hitbox()
|
|
)
|
|
|
|
// Adjust the actor's bounding rect by its stated Hitbox from its script.
|
|
rect = collision.SizePlusHitbox(rect, hitbox)
|
|
|
|
box = collision.GetCollisionBox(rect)
|
|
|
|
// The stroke data for drawing the collision box "inside" the level Canvas,
|
|
// so it scrolls and works in world units not screen units.
|
|
var strokes = []struct{
|
|
Color render.Color
|
|
PointA render.Point
|
|
PointB render.Point
|
|
}{
|
|
{render.DarkGreen, box.Top[0], box.Top[1]},
|
|
{render.DarkBlue, box.Bottom[0], box.Bottom[1]},
|
|
{render.DarkYellow, box.Left[0], box.Left[1]},
|
|
{render.Red, box.Right[0], box.Right[1]},
|
|
}
|
|
for _, cfg := range strokes {
|
|
stroke := drawtool.NewStroke(drawtool.Line, cfg.Color)
|
|
stroke.PointA = cfg.PointA
|
|
stroke.PointB = cfg.PointB
|
|
canvas.DrawStrokes(d.Engine, []*drawtool.Stroke{stroke})
|
|
}
|
|
}
|
|
|
|
// TrackFPS shows the current FPS once per second.
|
|
//
|
|
// In debug mode, changes the window title to include the FPS counter.
|
|
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
|
|
}
|
|
|
|
if d.Debug {
|
|
d.Engine.SetTitle(fmt.Sprintf("%s (%d FPS)", d.Title(), fpsCurrent))
|
|
}
|
|
}
|