Debug and live
Debug drawing and the debug overlay, and the live link with the editor.
Files: debug.odin, live.odin, live_native.odin (#+build !js), live_web.odin (#+build js).
File overviews
Section titled “File overviews”debug.odin
Section titled “debug.odin”Debug drawing and the debug overlay, for finding out what a game is doing while it runs.
Debug shapes are lines, rectangles, circles and text in the world, queued from anywhere (an
update, a script, a collision handler) and drawn over the scene through its camera, after its
nodes and physics shapes. They last one frame unless given seconds, so drawing one every
frame shows something live:
Example
// Where the enemy is looking, and what it sawe.Debug_Line(scene, enemy.position, enemy.position + facing * 200)if hit.hit do e.Debug_Circle(scene, hit.point, 6, {255, 60, 60, 255}, seconds = 0.5)The overlay (Game.debugOverlay, or F10 in development runs: DEV_KEY_DEBUG_OVERLAY) shows
the frame rate and frame times over the last second or two, the scene, its node, body and tween
counts, and the time scale, in the top-left corner over everything.
live.odin
Section titled “live.odin”The live link: a game run by an editor (the player’s --live) answers it while it plays: what’s
in the playing scene, a node’s properties, how long each part of a frame takes. The editor can
change properties, and pause the game and step it a frame at a time.
The channel is the game’s standard input and output, one line per message:
- Commands (the editor writes them to the game’s stdin; a thread reads them and
LiveLink_Processruns them at the start of each update):watch: send stats four times a second and the scene’s tree twice a secondselect <id>: also send that node’s properties with each tree (selectalone: none)set <id> <property> <value>: sets a property (Node_WriteProperty)pause,resume,step(one frame of 1/60 s while paused)
- Replies (lines on stdout starting
@@live, then JSON):{"type":"stats","fps":..,"frame":..,"update":..,"physics":..,"draw":..,"nodes":..,"drawn":..,"paused":..}(milliseconds, averaged over the last frames){"type":"tree","scene":"..","nodes":[{"id":"..","name":"..","type":"..","parent":"..","visible":true}, ...]}{"type":"node","id":"..","properties":[{"name":"..","value":".."}, ...]}{"type":"set","ok":true,"id":"..","property":".."}
Ids are the nodes’ EntityIds as text. Exports never run with --live; on the web there’s no
stdin, so LiveLink_Start does nothing there.
DebugShapeKind
Section titled “DebugShapeKind”DebugShapeKind :: enum u8 { // A line from `a` to `b` Line, // A rectangle: `a` the top-left corner, `b` the size Rect, // A circle: `a` the center, `b.x` the radius Circle, // Text with its top-left corner at `a` Text,}What a debug shape is
enum. Source: debug.odin:33
DebugShape
Section titled “DebugShape”DebugShape :: struct { // What it is kind: DebugShapeKind, // Rect, Circle: filled, or only the outline filled: bool, // Its color color: sdl.Color, // Line: from `a` to `b`; Rect: `a` the top-left corner, `b` the size; Circle: `a` the center, // `b.x` the radius; Text: `a` the top-left corner a, b: [2]f32, // Text: the text (owned, on the heap) text: string, // Seconds it has left; it goes at the first update after this reaches 0 remaining: f32,}One queued debug shape (Debug_Line…), in world pixels
struct. Source: debug.odin:45
FrameStats
Section titled “FrameStats”FrameStats :: struct { // Seconds per frame, a ring of the last DEBUG_FRAME_HISTORY times: [DEBUG_FRAME_HISTORY]f32, // Where the next one goes, and how many there are (up to DEBUG_FRAME_HISTORY) next, count: int,}The frame times the debug overlay shows (Game_Update records them, in real time)
struct. Source: debug.odin:62
LiveLink
Section titled “LiveLink”LiveLink :: struct { // Lines read from stdin, waiting for the next update (guarded by `lock`) lock: sync.Mutex, incoming: [dynamic]string, // The game's updates are held (it still draws) paused: bool, // One update to run while paused step: bool, // Stats and the tree are being sent watching: bool, // The node whose properties are sent ("": none) selected: string, // Seconds to the next stats and tree statsTimer, treeTimer: f32,}A game’s live link (Game.live, made by LiveLink_Start)
struct. Source: live.odin:34
GameProfile
Section titled “GameProfile”GameProfile :: struct { // Milliseconds: globals and the scene's update (physics included), physics alone, drawing update, physics, draw: f32, // Nodes in the playing scene, and drawn last frame nodes, drawn: int,}How long the parts of the last frames took (Game.profile): measured every frame, averaged over
PROFILE_FRAMES.
struct. Source: live.odin:54
Procedures
Section titled “Procedures”Debug_Line
Section titled “Debug_Line”Debug_Line :: proc(scene: ^Scene, from, to: [2]f32, color := DEBUG_COLOR, seconds: f32 = 0)Queues a line in the world, drawn over the scene.
Inputs
- scene: The scene to draw it over
- from: Start, in world pixels
- to: End, in world pixels
- color: Its color
- seconds: How long it stays (0: this frame)
Example
e.Debug_Line(scene, {0, 0}, {200, 100}, sdl.Color{255, 60, 60, 255}, seconds = 1)procedure. Source: debug.odin:82
Debug_Rect
Section titled “Debug_Rect”Debug_Rect :: proc(scene: ^Scene, rect: sdl.FRect, color := DEBUG_COLOR, filled := false, seconds: f32 = 0)Queues a rectangle in the world, drawn over the scene.
Inputs
- scene: The scene to draw it over
- rect: World pixels (x, y the top-left corner)
- color: Its color
- filled: Filled, or only its outline
- seconds: How long it stays (0: this frame)
procedure. Source: debug.odin:96
Debug_Circle
Section titled “Debug_Circle”Debug_Circle :: proc(scene: ^Scene, center: [2]f32, radius: f32, color := DEBUG_COLOR, filled := false, seconds: f32 = 0)Queues a circle in the world, drawn over the scene.
Inputs
- scene: The scene to draw it over
- center: World pixels
- radius: World pixels
- color: Its color
- filled: Filled, or only its outline
- seconds: How long it stays (0: this frame)
procedure. Source: debug.odin:111
Debug_Text
Section titled “Debug_Text”Debug_Text :: proc(scene: ^Scene, position: [2]f32, text: string, color := DEBUG_COLOR, seconds: f32 = 0)Queues text in the world (the default font, 14 px at zoom 1), drawn over the scene.
Inputs
- scene: The scene to draw it over
- position: The text’s top-left corner, world pixels
- text: What it says (copied)
- color: Its color
- seconds: How long it stays (0: this frame)
Example
// Given: enemy: ^e.Node, health: inte.Debug_Text(scene, enemy.position + {0, -40}, fmt.tprintf("hp %d", health))procedure. Source: debug.odin:129
Debug_Clear
Section titled “Debug_Clear”Debug_Clear :: proc(scene: ^Scene)Removes every debug shape a scene has queued, lasting ones too.
Inputs
- scene: The scene
procedure. Source: debug.odin:139
Game_GetFrameStats
Section titled “Game_GetFrameStats”Game_GetFrameStats :: proc(game: ^Game) -> (fps, average, worst: f32)The frame rate and frame times over the frames the game recorded (up to DEBUG_FRAME_HISTORY), in real time (not scaled by the time scale).
Inputs
- game: The game
Returns
- fps: Frames per second, from the average frame time (0 before the first frame)
- average: The average frame time, milliseconds
- worst: The longest frame, milliseconds
Example
fps, average, worst := e.Game_GetFrameStats(game)log.infof("%.0f fps, %.1f ms (worst %.1f)", fps, average, worst)procedure. Source: debug.odin:160
LiveLink_Process
Section titled “LiveLink_Process”LiveLink_Process :: proc(game: ^Game, deltaTime: f64) -> boolRuns the commands that came in since the last update, and sends what the editor watches. Game_Update calls it first.
Inputs
- game: The game
- deltaTime: Seconds since the last frame
Returns
- false while the game is paused (no update this frame); true to update (a step included)
procedure. Source: live.odin:78
LiveLink_Push
Section titled “LiveLink_Push”LiveLink_Push :: proc(game: ^Game, line: string)Hands the live link a line, as if read from stdin (tests; the reader thread does it in games).
Inputs
- game: The game (with a live link)
- line: A command
procedure. Source: live.odin:118
LiveLink_Destroy
Section titled “LiveLink_Destroy”LiveLink_Destroy :: proc(game: ^Game)Frees a game’s live link (Game_Destroy).
procedure. Source: live.odin:129
Scene_GetNodeByText
Section titled “Scene_GetNodeByText”Scene_GetNodeByText :: proc(scene: ^Scene, id: string) -> ^NodeA node by its id as text (as the live link sends them).
Inputs
- scene: The scene
- id: The id
Returns
- The node, or nil
procedure. Source: live.odin:151
LiveLink_Start
Section titled “LiveLink_Start”LiveLink_Start :: proc(game: ^Game)Starts a game’s live link (live.odin): a thread reads commands from stdin, one per line, for
LiveLink_Process to run. The player starts it with --live (the editor’s Play).
Inputs
- game: The game
procedure, only in builds with #+build !js. Source: live_native.odin:16
LiveLink_Start
Section titled “LiveLink_Start”LiveLink_Start :: proc(game: ^Game)The web has no stdin: no live link (live.odin)
procedure, only in builds with #+build js. Source: live_web.odin:5
Constants and variables
Section titled “Constants and variables”DEBUG_COLOR
Section titled “DEBUG_COLOR”DEBUG_COLOR :: sdl.Color{80, 255, 120, 255}The color debug shapes get when none is given
constant. Source: debug.odin:27
DEBUG_FRAME_HISTORY
Section titled “DEBUG_FRAME_HISTORY”DEBUG_FRAME_HISTORY :: 120How many frame times the overlay’s numbers cover
constant. Source: debug.odin:30
PROFILE_FRAMES
Section titled “PROFILE_FRAMES”PROFILE_FRAMES :: 30How many frames the profile averages
constant. Source: live.odin:62
LIVE_PREFIX
Section titled “LIVE_PREFIX”LIVE_PREFIX :: "@@live "The prefix of the live link’s replies on stdout
constant. Source: live.odin:65
