A visual debugging suite for Playdate.
Acetate is a visual debugging utility for use with the Playdate Simulator,
specifically optimized for use with the playdate.graphics.sprite
class (and subclasses). With Acetate, you can easily
enter a visual debugging mode at any time, cycle through debug visualizations for each sprite, and optionally
customize the visuals and information shown for each from directly within your sprite classes.
Acetate wraps the built-in functionality for debug drawing, and adds:
- Out-of-the-box visualizations for common properties: bounding boxes, center points, collision rects, rotation, etc.
- The ability to cycle through debug info for each sprite, one by one
- Rich debug strings displayed in a bespoke monospaced font
- The ability to do custom debug drawing from directly within your sprite classes
- An option to pause your game while performing visual debugging
- Customizable keyboard shortcuts for toggling debug mode and various visualization options
- Settings that let you decide how it looks and behaves
Playdate is a registered trademark of Panic.
-
Clone this repo into your project folder (e.g. inside
source
). -
Move the
Acetate-Mono-*.fnt
files into yoursource/fonts/
folder. -
Import it into your project within your
main.lua
file.import 'Acetate/acetate'
-
Initialize it for use.
acetate.init()
Using toybox.py
-
If you haven't already, download and install
toybox.py
. -
Navigate to your project folder in a Terminal window.
cd "/path/to/myProject"
-
Add Acetate to your project
toybox add acetate toybox update
-
Then, if your code is in the
source
directory, import it as follows:import '../toyboxes/toyboxes.lua'
-
Lastly, be sure to initialize it.
acetate.init()
Once you've imported Acetate in your project, you don't need to do anything else to start taking advantage of its features.
- Build and run your app in the Playdate Simulator.
- Press the
D
key on your keyboard to enter debug mode. - Use
,
(<
) and.
(>
) to cycle through sprites (holdSHIFT
to cycle through sprites of the same class as the currently focused sprite). - Refer to the list of keyboard shortcuts for additional options.
Out of the box, you can see the following information for each sprite:
- class name
- size and position
- bounding box
- center point
- collision rect
- orientation orb
You can also easily display additional information and visualizations unique to your sprites. Read on to learn how to implement custom debug drawing for your sprite classes and customize the debug string displayed as you cycle through them in debug mode.
NOTE: If your game adjusts the draw offset, you may need to cache it so that debug drawing appears in the correct position relative to your sprites. See the #Troubleshooting section for additional details.
Acetate provides several debug visualizations out-of-the-box, which are suitable for showing basic properties common to most sprites. However, you may want to visualize custom properties unique to your sprite as well. Acetate makes this easy!
You can implement the debugDraw
function within your playdate.graphics.sprite
subclasses and
Acetate will ensure it gets called automatically:
function MySprite:debugDraw()
-- perform custom debug drawing here
end
Acetate prepares the graphics context for you automatically:
- The color will be set to
kColorWhite
(the color used for all debug drawing). - The line width will be set to
1
. - The drawing offset will be set according to the position of your sprite, so you can do all
drawing relative to your sprite (just like in your
draw
function).
Anything you draw within this function will appear in debug mode. You can toggle your custom
debug drawing on/off using the M
key, or set acetate.customDebugDrawing
to true
or false
from within your code.
Because fonts are rendered as images and tend to be black-on-white, regular use of drawText
variants will likely not appear. To draw text in debugDraw
, change the image drawing mode so
that your text will render in kColorWhite
as follows:
gfx.setImageDrawMode(gfx.kDrawModeFillWhite)
acetate.debugFont:drawText("This text will render in the debug layer!", x, y)
Acetate provides a handful of extensions to the sprite class specifically designed for drawing debug info for common sprite properties. You can toggle these on/off globally using the keyboard shortcuts or Acetate settings, but occasionally you'll want certain features for particular types of sprites and not others. For example, you might want to show orientation orbs only for sprites which rotate within your game.
You can call any of Acetate's debug draw functions from your own sprite's debugDraw
function so
that they appear even when they are turned off globally.
function MySprite:debugDraw()
self:drawOrientation()
-- perform additional debug drawing here
end
The following built-in debug drawing functions are supported:
drawBounds
: Draw the sprite's bounding boxdrawCenter
: Draw the sprite's center pointdrawOrientation
: Draw an indicator of the sprite's current rotationdrawCollideRect
: Draw the sprite's collision rect, if set. (This option is also provided by the simulator itself. You can use the simulator version to overlay collision rects in a contrasting color.)
If you have a small bit of custom identifying information you'd like to display — say, the
number of a pin in a bowling pin rack, or the name of a particular character — you can set
the sprite's debugName
property. When set, the debug name will be shown instead of the className
of the sprite when cycling through sprites in debug mode. For instance:
function Pin:init(number)
Pin.super.init(self)
self.number = number
self.debugName = "Pin " .. number
-- more initialization
end
Acetate displays a debug string for the focused sprite while debug mode is active. By default, this string indicates the size and position of the sprite. You can modify the debug string format to include the most useful information for your use case in two ways:
-
Change the default. Modify the
acetate.defaultDebugStringFormat
to change the debug string shown for all of your sprites. -
Set custom strings. Implement the
debugString()
function on your sprite. You can provide a fully formatted string, or include substitution patterns as shown in the table below, passingtrue
as a second return value to indicate that substitutions are needed.function MySprite:debugString() local s -- construct `s` using any properties belonging to your sprite return s, true -- true indicates that substitutions are needed end
All substitution patterns begin with a dollar sign ($
) followed by either one or two alphabetical
characters. They are case sensitive.
Pattern | Substitution |
---|---|
$n |
Class name, or debugName if provided |
$p |
Position coordinate in the form (x, y) |
$x |
X position |
$y |
Y position |
$w |
Width |
$h |
Height |
$rx |
Local relative horizontal center |
$ry |
Local relative vertical center |
$rc |
Local relative center point, e.g. (0.5, 0.5) |
$o |
Origin coordinate (top left) in local space |
$ox |
Local origin X position |
$oy |
Local origin Y position |
$O |
Origin coordinate in world space |
$Ox |
Local origin X position |
$Oy |
Local origin Y position |
$c |
Center coordinate in local space |
$cx |
Local center X position |
$cy |
Local center Y position |
$C |
Center coordinate in world space |
$Cx |
World center X position |
$Cy |
World center Y position |
$r |
Rotation (radians) |
$d |
Rotation (degrees) |
$s |
Scale |
$t |
Tag number |
$q |
Opaqueness as "OPAQUE" or "TRANSPARENT" |
$u |
Update status as "UPDATING" or "DISABLED" |
$v |
Visibility as "VISIBLE" or "INVISIBLE" |
$z |
Z-index |
Acetate allows you to cycle through sprites in the display list using the ,
and .
keys. However,
you can also focus sprites programmatically, for example in response to a particular game event or
condition. This makes it easy to initiate visual debugging at the right time and for the right sprite.
acetate.setFocus(mySprite)
If Acetate's debug mode isn't active when you call this function, it will be enabled automatically.
Optionally, you can enable the auto-pause behavior by setting acetate.autoPause
to true
(in init,
or at runtime), in order to pause for inspection when focusing your sprite.
You can also lock the focus to a specific class, so only sprites of that class get focused when cycling with the keyboard shortcuts:
acetate.setClassFocusLock(MyClass)
Acetate provides a number of keyboard shortcuts. You're welcome to change any of these shortcuts
to fit your preference, or avoid conflict with other keyPress
handlers defined elsewhere. Edit
the settings object or override the defaults in your project e.g. acetate.toggleDebugKey = "0"
.
Key | Function |
---|---|
D | Toggle Acetate's visual [D]ebugging mode on/off |
C | Toggle drawing of sprite [C]enters while in debug mode |
B | Toggle drawing of sprite [B]ounds while in debug mode |
V | Toggle drawing of sprite orientation [V]ectors while in debug mode |
X | Toggle drawing of sprite colli[X]ion rects while in debug mode |
Z | Toggle debug drawing of invi[Z]ible sprites while in debug mode |
M | Toggle the use of custo[M] debugDraw functions defined in your own sprites |
F | Toggle the [F]PS display on/off |
N | Toggle the total sprite count on/off |
/ | [?] Toggle display of the debug string while focused on an individual sprite |
, | [<] Cycle forward through sprites to focus them one by one |
. | [>] Cycle backward through sprites |
< | [SHIFT <] Cycle forward through sprites of the same class |
> | [SHIFT >] Cycle backward through sprites of the same class |
L | [L]ock focus cycling to the focused sprite class |
P | [P]ause/unpause the game for/while debugging |
Q | [Q]uick-capture a screenshot of either the full screen or the focused sprite |
Acetate also provides a shortcut for capturing instantaneous screenshots from the simulator. While
not strictly a debug feature, it's certainly a useful tool to have in your workflow. Capture
a screenshot by pressing the Q
key at any time (even outside debug mode), or from within your
code:
acetate.captureFullScreenshot([path, filename])
NOTE: Acetate's debug layer will not appear in screenshots.
You can provide a destination path and filename, or let Acetate name it with a timestamp and save
it to the currently configured acetate.defaultScreenshotPath
. This is ~/Desktop
by default, but
may be changed in settings.lua
or from within your app.
If you are focused on an individual sprite while in debug mode when you activate the capture shortcut, Acetate will capture an image of just that sprite, rather than the full screen. You can also capture a screenshot of an individual sprite with:
acetate.captureSpriteScreenshot(sprite, [path, filename])
Acetate's settings object allows you to change a wide array of options to configure the debugging experience. You can change the configuration in one of several ways:
-
Override at
init
. You can override any defaults by passing named arguments to init:acetate.init { autoPause = true, debugColor = {0, 255, 0, 0.8}, -- as many as you like }
-
Create a custom config. If you intend to change many settings, or just want to keep your initialization code to a minimum, you can duplicate the
settings.lua
file, give the settings object therein a unique name (e.g.myAcetateSettings = { … }
, import that file inmain.lua
, and then pass the named config object toinit
:acetate.init(myAcetateSettings)
-
Set individual values. You can also override individual settings from within your app at runtime following initialization, e.g.
acetate.color = {0, 255, 0, 0.8}
and so on.
The following settings are available:
Setting | Type | Default | Description |
---|---|---|---|
enabled |
boolean | false |
Indicates when Acetate debug mode is active. Do not set this directly; call acetate.enable() or acetate.disable() instead. |
paused |
boolean | false |
Indicates when the app is paused during debug mode. Do not set this directly; call acetate.pause() or acetate.unpause() instead. |
autoPause |
boolean | false |
Indicates whether the app should pause automatically when entering debug mode. |
Setting | Type | Default | Description |
---|---|---|---|
drawCenters |
boolean | true |
Center points are shown for all sprites in debug mode when true. |
drawBounds |
boolean | true |
Bounding rects are shown for all sprites in debug mode when true. |
drawOrientations |
boolean | true |
Orientation orbs are shown for all sprites in debug mode when true. |
drawCollideRects |
boolean | false |
Collision rects are shown for all sprites while debug mode is enabled. |
customDebugDrawing |
boolean | true |
Custom debug drawing (implemtented in sprite debugDraw functions) is shown when true. |
customOverridesDefaults |
boolean | false |
Built-in debug drawing is hidden for sprites with custom debug drawing when true. |
Setting | Type | Default | Description |
---|---|---|---|
color |
{r,g,b,a} | cyan 75% | A table containing RGB values ([0,255]) and an alpha value ([0,1]) describing the color used for debug drawing. |
lineWidth |
number | 1 |
The default line width set for the debug drawing graphics context. |
centerRadius |
number | 2 |
The radius of the dot drawn when drawCenters is true. |
orientationOrbScale |
number | 0.5 |
Orientation orbs are drawn in proportion to the sprite they belong to. This setting describes their diameter with respect to the sprite's shortest dimension. |
minOrientationOrbRadius |
number | 10 |
The minimum radius at which the orbs are drawn for smaller sprites, to aid clarity. |
onlyDrawRotatedOrbs |
boolean | true |
Draw orientation orbs only for sprites which have a non-zero rotation. This helps keeps the view uncluttered when sprites aren't being rotated. |
Setting | Type | Default | Description |
---|---|---|---|
showFPS |
boolean | true |
Whether to show the current FPS (frames per second). |
FPSPersists |
boolean | false |
Whether to show the FPS (frames per second) even while debug mode isn't enabled. |
showSpriteCount |
boolean | true |
Whether to show the total number of sprites. |
spriteCountPersists |
boolean | false |
Whether to show the total number of sprites even while debug mode isn't enabled. |
showDebugString |
boolean | true |
Whether the debug string is shown while focused on a single sprite in debug mode. |
defaultDebugFormatString |
string | "$n\nX: $x\nY: $y\nW: $w\nH: $h" |
The format used for the debug string for any sprites which don't define their own. See Debug String Formats for details. |
alwaysShowSpriteNames |
boolean | true |
Whether to display the highlighted sprite's name even while the debug string is hidden. |
debugStringPosition |
{x,y} | {2, 2} |
A table containing the x and y position at which the debug string is drawn. By default, it draws just beneath the FPS counter at the top left corner of the screen. |
debugFontPath |
string | "fonts/Acetate-Mono-Bold-Condensed" |
The path to the font to use for displaying the debug string. |
Setting | Type | Default | Description |
---|---|---|---|
retainFocusOnDisable |
boolean | true |
When true, the focused sprite will remain focused the next time debug mode is entered. |
focusInvisibleSprites |
boolean | false |
Whether to perform debug drawing for and allow focusing of sprites which are made invisible via setVisible(false) . |
animateBoundsForFocus |
boolean | true . |
When true, the bounds of the focused sprite will appear as an animated "marching ants" dotted line |
Setting | Type | Default | Description |
---|---|---|---|
defaultScreenshotPath |
string | "~/Desktop" |
The default location that all screenshots are saved unless otherwise specified. |
spriteScreenshotsEnabled |
boolean | true | Whether the capture will contain only the sprite image, not the full screen, if taken while in debug mode and focused on a single sprite. |
Setting | Type | Default | Description |
---|---|---|---|
toggleDebugModeKey |
character | "d" |
Key used to toggle Acetate's visual [D]ebugging mode on/off. |
toggleCentersKey |
character | "c" |
Key used to toggle drawing of sprite [C]enters while in debug mode. |
toggleBoundsKey |
character | "b" |
Key used to toggle drawing of sprite [B]ounds while in debug mode. |
toggleOrientationsKey |
character | "v" |
Key used to toggle drawing of sprite orientation [V]ectors while in debug mode. |
toggleCollideRectsKey |
character | "x" |
Key used to toggle drawing of sprite colli[X]ion rects while in debug mode. |
toggleInvisiblesKey |
character | "z" |
Key used to toggle debug drawing of invi[Z]ible sprites while in debug mode. |
toggleCustomDrawKey |
character | "m" |
Key used to toggle use of custo[M] sprite debugDraw functions. |
toggleFPSKey |
character | "f" |
Key used to toggle [F]PS display on/off. |
toggleSpriteCountKey |
character | "n" |
Key used to toggle display of the total sprite count. |
toggleDebugString |
character | "?" |
Key used to toggle debug string display while focused a single sprite. |
cycleForwardKey |
character | "." |
Key used to cycle forward through sprites, one by one. |
cycleBackwardKey |
character | "," |
Key used to cycle backward through sprites, one by one. |
cycleForwardInClassKey |
character | ">" |
Key used to cycle forward to the next sprite of the same class as the focused sprite. |
cycleBackwardInClassKey |
character | "<" |
Key used to cycle backward through sprites of the same class as the focused sprite. |
toggleFocusLockKey |
character | "l" |
Key used to [L]ock focus cycling to sprites of the same class as the focused sprite. |
togglePauseKey |
character | "p" |
Key used to [P]ause/unpause the game while in debug mode. |
captureScreenshotKey |
character | "q" |
Key used to [Q]uick-capture a screenshot. |
If you can't activate Acetate debug mode for your app in the simulator, check the following:
-
Installation. Be sure you've followed the installation instructions properly, that all the Acetate files are included in the directory, and that you've imported Acetate via the correct path relative to your source file.
-
Keyboard handler. Acetate implements the
playdate.keyPressed
function, which provides shortcuts for, among other things, toggling its debug overlay. If you implement thekeyPressed
handler yourself, it will override Acetate's. In this case, you can call Acetate's from your own:function playdate.keyPressed(key) -- let Acetate handle any debug key presses acetate.keyPressed(key) -- perform your own key handling here end
If acetate keyboard handling interferes with your own, you can modify the keyboard shortcuts with custom settings, including the key used to enable/disble the acetate debug layer.
-
Debug draw. Acetate implements the
playdate.debugDraw
function in order to render into the debug layer of the simulator. If you implement thedebugDraw
function yourself, it will override Acetate's. In this case, you can call Acetate's from your own:function playdate.debugDraw() -- let Acetate do its own debug drawing acetate.debugDraw() -- perform additional debug drawing here end
Note that you may not need to implement
playdate.debugDraw
yourself if you leverage Acetate's support for implementingdebugDraw
within your individual sprite classes. (You will, however, need to do your own debug drawing for anything not associated with sprites.)
Acetate attempts to adjust the draw offset so that debug drawing aligns properly with your sprites.
However, if your project adjusts the draw offset itself (via playdate.graphics.setDrawOffset()
),
Acetate may not have sufficient knowledge to do so correctly. In this case, provide Acetate with
the appropriate draw offset by calling cacheDrawOffset
from within your sprite's draw
function:
function MySprite:draw()
self:cacheDrawOffset()
-- draw...
end
Acetate is not initialized on-device. Attempting to access its members or call its functions outside
the simulator will cause Playdate to crash. You can do so safely from within any functions you write
(e.g. debugString()
, debugDraw()
, etc.) or within key handlers, as these will only be called inside
the simulator. Any access outside these contexts should be wrapped within a check to ensure acetate
has been initialized:
if acetate.initialized then
-- safe to access acetate members here, for example to call `acetate.setFocus(mySprite)`
end
Acetate is distributed under the terms of the MIT License.