Direct Drawing: Difference between revisions
No edit summary |
No edit summary |
||
| (3 intermediate revisions by the same user not shown) | |||
| Line 437: | Line 437: | ||
Each queued dot occupies one sprite-queue entry, but the 5,000-entry queue provides generous headroom. The 80-step curve above submits only 81 dots, and ordinary curves, outlines, particles, or object-heavy gameplay remain comfortably within practical capacity. | Each queued dot occupies one sprite-queue entry, but the 5,000-entry queue provides generous headroom. The 80-step curve above submits only 81 dots, and ordinary curves, outlines, particles, or object-heavy gameplay remain comfortably within practical capacity. | ||
Subscreens are still valuable for persistent drawings or deliberately dense raster work. Use <code>drawdottoscreen()</code> to generate static procedural artwork once, retain it on a subscreen, then place the completed surface with <code>drawscreen()</code>. | Subscreens are still valuable for persistent drawings or deliberately dense raster work. Use <code>drawdottoscreen()</code> to generate static procedural artwork once, retain it on a subscreen, then place the completed surface with <code>drawscreen()</code>. | ||
[[File:World Hereos Timeless Sine.png|none|frame|An example of creating shapes with dots. Calculating a sine wave and drawing as rows of single pixels with color graduation produces the smoothly curved line shown here.]] | |||
=== drawline === | === drawline === | ||
| Line 724: | Line 725: | ||
Sprite loading and screen allocation are setup operations, not frame-by-frame drawing operations. Reusing allocations avoids file access, decoding work, heap churn, and leak risk. | Sprite loading and screen allocation are setup operations, not frame-by-frame drawing operations. Reusing allocations avoids file access, decoding work, heap churn, and leak risk. | ||
Large subscreens cost memory and fill bandwidth, but a completed surface consumes only one sprite-queue entry when submitted with <code>drawscreen()</code>. Keep off-screen surfaces near the size of the content being composed, use widths divisible by four, and redraw only when their contents need to change. | Large subscreens cost memory and fill bandwidth, but a completed surface consumes only one sprite-queue entry when submitted with <code>drawscreen()</code>. In effect, subscreens act as sprite-queue multipliers by consolidating many separately drawn elements into a single queued item. Keep off-screen surfaces near the size of the content being composed, use widths divisible by four, and redraw only when their contents need to change. | ||
== Practical guidance == | == Practical guidance == | ||
| Line 745: | Line 746: | ||
* [[Openborvariant]] | * [[Openborvariant]] | ||
* [[Sprites]] | * [[Sprites]] | ||
[[Category:Openbor]] | |||
[[Category:Graphics]] | |||
[[Category:Script]] | |||
Latest revision as of 00:35, 16 August 2026
OpenBOR direct drawing lets scripts place text, sprites, dots, lines, boxes, and complete off-screen surfaces into the video output. Two related paths are available: queued drawing, which joins the engine's normal depth-sorted render queue, and target-screen drawing, which changes a selected screen immediately.
This article describes the direct-draw API, resource ownership, ordering, color and blend handling, drawmethod control, and off-screen composition.
Parts of the system
Direct drawing uses four main objects.
| Object | Purpose |
|---|---|
| Sprite | Image data loaded by loadsprite() or supplied by the engine.
|
| Sprite queue | Depth-sorted list used by drawsprite(), drawline(), drawbox(), and the other queued functions.
|
| Screen | Pixel surface used as a drawing target. The engine's main surface is vscreen; script-created surfaces are commonly called subscreens.
|
| Drawmethod | Shared transform and color state used for scaling, flipping, rotation, clipping, tinting, transparency, and other effects. |
Queued drawing and target-screen drawing
Queued functions record a command for later rendering. The engine sorts those commands with its other render entries, then draws them during the normal video pass. Calls must be submitted again on every frame where the result should remain visible.
Target-screen functions write pixels immediately. Their results remain on a script-created screen until another operation overwrites the pixels, clearscreen() clears them, or free() releases the screen.
| Queued function | Immediate target-screen equivalent | Key difference |
|---|---|---|
drawstring()
|
drawstringtoscreen()
|
Queued text accepts a Z value; target-screen text follows call order. |
drawsprite()
|
drawspritetoscreen()
|
Queued sprites accept Z and sort ID values; target-screen sprites do not. |
drawdot()
|
drawdottoscreen()
|
Queued dots accept Z; target-screen dots are written immediately. |
drawline()
|
drawlinetoscreen()
|
Queued lines accept Z; target-screen lines follow call order. |
drawbox()
|
drawboxtoscreen()
|
Queued boxes accept Z; target-screen boxes follow call order. |
drawscreen()
|
Not applicable | Places an already-drawn screen into the queue for composition. |
Coordinates and order
Screen coordinates begin at the upper-left corner. X increases toward the right, while Y increases toward the bottom.
Queued entries sort by Z first and sort ID second. Larger values render later and therefore appear above entries with smaller values. Sort ID is available only to drawsprite(); it provides a stable secondary order when several sprites share the same Z value.
Immediate target-screen operations have no Z value. Later calls overwrite earlier pixels where the results overlap.
Colors and blend modes
Use rgbcolor() to create packed colors in the format expected by the current platform.
int red = rgbcolor(255, 0, 0);
int green = rgbcolor(0, 255, 0);
int blue = rgbcolor(0, 0, 255);
int charcoal = rgbcolor(32, 36, 40);
Each component normally ranges from 0 to 255. Raw hexadecimal RGB literals are less portable because the packed channel order can differ by platform.
Shape functions and drawscreen() accept an optional blend mode.
| Constant | Effect |
|---|---|
BLEND_MODE_MODEL
|
Uses the applicable model or drawmethod behavior. |
BLEND_MODE_NONE
|
Copies the source without blending. |
BLEND_MODE_ALPHA
|
Screen-style lightening blend. |
BLEND_MODE_ALPHA_NEGATIVE
|
Multiply-style darkening blend. |
BLEND_MODE_OVERLAY
|
Overlay blend. |
BLEND_MODE_HARDLIGHT
|
Hard-light blend. |
BLEND_MODE_DODGE
|
Color-dodge blend. |
BLEND_MODE_AVERAGE
|
Equal average of source and destination. |
int blend = openborconstant("BLEND_MODE_ALPHA");
drawline(20, 40, 180, 40, 1000, rgbcolor(255, 192, 64), blend);
Loading and releasing sprites
loadsprite
void sprite = loadsprite("data/sprites/interface/marker.gif");
void masked = loadsprite("data/sprites/interface/glow.gif",
"data/sprites/interface/glow_mask.gif");
loadsprite() places the requested image under OpenBOR's resource management and returns a script-owned handle. Load reusable artwork once during setup, retain the returned pointer, and reuse it for every frame that needs the artwork. Repeatedly loading and freeing the same file abuses the intended resource lifecycle and should be avoided.
free() tells OpenBOR that the script no longer needs the handle. The engine may release active sprite and mask data while retaining internal resource records and previously allocated cache capacity. This retention is deliberate and beneficial: it lets OpenBOR recognize and intelligently reuse sprite resources instead of blindly repeating every lookup, allocation, and load operation.
Memory reserved for those records and caches does not necessarily disappear immediately after free(). Rapid load/free cycles can expand the retained high-water capacity and make misuse look like a memory leak. That appearance does not indicate a defect in the resource manager - it indicates that short-lived loading is defeating a system designed around reuse.
Treat loaded sprites as retained resources rather than disposable draw commands. Load each sprite once, reuse it as needed, then call free() when its useful lifetime truly ends.
Transparent borders are trimmed while the sprite is prepared. Internal offsets preserve the visible artwork's position relative to the X and Y values passed to a draw function. Mask artwork should match the source image's dimensions and transparent layout.
Lifetime example
Store loaded pointers when multiple scripts need the same resource. Release every script-owned sprite with free() when its useful lifetime ends.
level.c
void main()
{
void marker = loadsprite("data/sprites/interface/marker.gif");
setglobalvar("direct_draw_marker", marker);
}
updated.c
void main()
{
void marker = getglobalvar("direct_draw_marker");
if(!isempty(marker)) {
drawsprite(marker, 160, 100, 1000, 0);
}
}
endlevel.c
void main()
{
void marker = getglobalvar("direct_draw_marker");
if(!isempty(marker)) {
free(marker);
setglobalvar("direct_draw_marker", NULL());
}
}
Never pass an engine-owned sprite or screen to free(). Only objects returned to the script by allocation or loading functions belong to the script.
Inspecting graphics objects
getgfxproperty() is a type-aware inspection function for sprites, screens, and bitmaps. Scripts can use the same interface to discover dimensions, drawing anchors, pixel format, palette data, and individual pixel values at runtime.
This capability makes graphics code independent of hard-coded asset dimensions. Interface layouts can size themselves around supplied artwork, effects can adapt to different screen formats, scripts can inspect generated subscreens, and tools can analyze image content without maintaining a duplicate description of the resource.
Syntax
Most properties use a graphics handle and property name.
int value = getgfxproperty(handle, property);
The pixel property also requires X and Y coordinates.
int value = getgfxproperty(handle, "pixel", x, y);
Properties
| Property | Valid objects | Return | Description |
|---|---|---|---|
width
|
Sprite, screen, bitmap | Integer | Drawable width in pixels. Sprite width describes the encoded image after transparent-edge trimming. |
height
|
Sprite, screen, bitmap | Integer | Drawable height in pixels. Sprite height describes the encoded image after transparent-edge trimming. |
srcwidth
|
Sprite, screen, bitmap | Integer | Source-width metadata stored with a sprite. Screens and bitmaps return their ordinary width. Sprite loaders may preserve a distinct source measurement when the resource representation requires one. |
srcheight
|
Sprite, screen, bitmap | Integer | Source-height metadata stored with a sprite. Screens and bitmaps return their ordinary height. |
centerx
|
Sprite, screen, bitmap | Integer | Horizontal drawing anchor stored by a sprite. The X coordinate passed to a sprite draw call positions this anchor rather than assuming the visible left edge. Screens and bitmaps return 0. |
centery
|
Sprite, screen, bitmap | Integer | Vertical drawing anchor stored by a sprite. The Y coordinate passed to a sprite draw call positions this anchor rather than assuming the visible top edge. Screens and bitmaps return 0. |
palette
|
Sprite, screen, bitmap | Pointer | Pointer to the palette owned by the graphics object. The result may be empty when the object has no attached palette. Never release this borrowed pointer with free().
|
pixelformat
|
Sprite, screen, bitmap | Integer | Native storage format of the graphics object. The result corresponds to PIXEL_8, PIXEL_x8, PIXEL_16, or PIXEL_32.
|
pixel
|
Sprite, screen, bitmap | Integer | Native pixel value at the supplied X and Y coordinates. Indexed surfaces return a palette index; 16-bit and 32-bit surfaces return their packed pixel value. Transparent, unavailable, unsupported, or out-of-bounds pixels return 0. |
Dimensions and anchors
int width = getgfxproperty(sprite, "width");
int height = getgfxproperty(sprite, "height");
int source_w = getgfxproperty(sprite, "srcwidth");
int source_h = getgfxproperty(sprite, "srcheight");
int center_x = getgfxproperty(sprite, "centerx");
int center_y = getgfxproperty(sprite, "centery");
Width and height describe the pixels OpenBOR can draw from the encoded object. Source dimensions and center offsets expose the metadata needed to preserve alignment after transparent borders are trimmed. Reading these values lets reusable scripts accept artwork with different dimensions or anchor points without embedding asset-specific constants.
The next example builds a backing box around an arbitrary sprite.
int x = 40;
int y = 24;
int padding = 4;
int width = getgfxproperty(sprite, "width");
int height = getgfxproperty(sprite, "height");
int center_x = getgfxproperty(sprite, "centerx");
int center_y = getgfxproperty(sprite, "centery");
int left = x - center_x;
int top = y - center_y;
int back = rgbcolor(24, 28, 40);
drawbox(left - padding,
top - padding,
width + padding * 2,
height + padding * 2,
990,
back);
drawsprite(sprite, x, y, 1000, 0);
Sprite anchor values can shift the visible image relative to x and y. Subtracting centerx and centery from the draw coordinates produces the visible image's upper-left position.

Pixel format
int format = getgfxproperty(screen, "pixelformat");
if(format == openborconstant("PIXEL_32"))
{
// The screen stores packed 32-bit pixels.
}
Checking the format before interpreting a pixel value is essential. Value 12 means palette entry 12 on an indexed surface, while the same integer represents a packed color value on a true-color surface.
Palette access
The palette property returns the live palette pointer attached to the graphics object. Palette functions can inspect or edit its color entries directly.
void palette = getgfxproperty(sprite, "palette");
if(!isempty(palette))
{
int component = openborconstant("COLOR_COMPONENT_RED");
int red = get_palette_property(palette, 12, component);
}
Palette pointers are borrowed views into their owning graphics objects. Keep the owner alive for as long as the pointer is used, and never pass the returned palette pointer to free(). Editing a live palette changes how its owner interprets indexed pixels, which enables runtime recoloring and diagnostic tools without duplicating the underlying image.
Pixel inspection
int sample = getgfxproperty(sprite, "pixel", 4, 6);
Pixel coordinates begin at the object's upper-left corner. Negative coordinates and coordinates at or beyond the reported width or height return 0. Since 0 can also represent transparency, palette index 0, or a packed black pixel, the return value alone cannot distinguish an out-of-bounds lookup from a valid zero-valued pixel. Check coordinates against width and height when that distinction matters.
Selective pixel inspection supports procedural masks, content-aware effects, palette analysis, runtime graphics diagnostics, and scripts that derive behavior from generated subscreens.
Tip: Large full-surface scans can be expensive, but you can usually cache them and avoid needless churn by sampling only the needed area. Used correctly this is an extremely powerful feature, such as seen here with a rain splatter effect on bodies. The underlying script profiles the upper surface of objects, and draws alternating offset dots to create a convincing rain effect tailored to the current sprite frame. Careful optimization allows the script to scan dozens of on screen objects and draw the effect with no frame loss.

https://www.youtube.com/watch?v=aAADyl40x-4
Queued drawing functions
drawsprite
drawsprite(sprite, x, y, z, sortid);
Queues a sprite at x, y, and z. The optional sortid defaults to 0. Current common drawmethod settings are copied into the queue entry when the call is made.
drawsprite(marker, 160, 100, 1000, 0);
drawstring
drawstring(x, y, font, value, z);
Queues text using the selected font index. The value is converted to text, and the optional Z value defaults to 0.
drawstring(12, 12, 0, "READY", 1000);
drawstring(12, 24, 0, getglobalvar("score"), 1000);
drawdot
drawdot(x, y, z, color, blend);
Queues one pixel at x, y, and z. The optional blend argument may be omitted.
One pixel sounds trivial, but it is the fundamental procedural drawing primitive. Every raster shape is ultimately a collection of pixels. Scripts can calculate coordinates from an equation, a data set, random input, entity positions, or any other runtime state, then submit a dot at each result. This makes drawdot() the foundation for graphics that have no dedicated built-in drawing command.
| Technique | How dots provide it |
|---|---|
| Curves | Evaluate a parametric or polynomial curve at successive intervals and plot each resulting coordinate. |
| Circles and ellipses | Convert successive angles into X and Y offsets around a center point. |
| Irregular outlines | Generate coordinates from custom rules, sampled paths, or gameplay data. |
| Filled shapes | Plot every pixel selected by a scan-line, distance, winding, or other inclusion test. |
| Graphs and waveforms | Convert measured values into screen coordinates and plot the samples. |
| Particles and fields | Plot independently calculated points for sparks, stars, dust, noise, gradients, or procedural textures. |
| Masks and diagnostic overlays | Mark selected pixels, collision samples, anchors, paths, or regions for inspection. |
The following example plots a quadratic Bézier curve. Three control points define the start, bend, and end. Increasing steps produces a smoother curve by plotting more samples.
int i;
int steps = 80;
int z = 1000;
int color = rgbcolor(255, 192, 64);
int blend = openborconstant("BLEND_MODE_NONE");
float t;
float inverse;
int x;
int y;
// Start point: 24, 120
// Control point: 160, 16
// End point: 296, 120
for(i = 0; i <= steps; i++)
{
t = i * 1.0 / steps;
inverse = 1.0 - t;
x = inverse * inverse * 24
+ 2.0 * inverse * t * 160
+ t * t * 296;
y = inverse * inverse * 120
+ 2.0 * inverse * t * 16
+ t * t * 120;
drawdot(x, y, z, color, blend);
}
Closely spaced samples appear continuous at normal display resolutions. Curves with rapid changes or long spans need more samples. Connecting successive calculated coordinates with drawline() is another option when a guaranteed unbroken outline matters.
Each queued dot occupies one sprite-queue entry, but the 5,000-entry queue provides generous headroom. The 80-step curve above submits only 81 dots, and ordinary curves, outlines, particles, or object-heavy gameplay remain comfortably within practical capacity.
Subscreens are still valuable for persistent drawings or deliberately dense raster work. Use drawdottoscreen() to generate static procedural artwork once, retain it on a subscreen, then place the completed surface with drawscreen().

drawline
drawline(x1, y1, x2, y2, z, color, blend);
Queues a line between two screen coordinates. The optional blend argument may be omitted.
int white = rgbcolor(255, 255, 255);
drawline(20, 30, 140, 30, 1000, white);
drawbox
drawbox(x, y, width, height, z, color, blend);
Queues a filled rectangle. The optional blend argument may be omitted.
int back = rgbcolor(16, 20, 28);
int blend = openborconstant("BLEND_MODE_ALPHA");
drawbox(8, 8, 144, 36, 990, back, blend);
drawscreen
drawscreen(screen, x, y, z, blend);
Queues a complete screen at the requested position and depth. The optional blend argument changes more than the blend mode: supplying it also makes zero-valued black pixels transparent. Omitting it preserves the current common drawmethod behavior, so black pixels remain opaque unless background transparency is already enabled there.
// Opaque zero-valued background unless the common drawmethod says otherwise.
drawscreen(panel, 0, 160, 1000);
// Zero-valued background becomes transparent.
int blend = openborconstant("BLEND_MODE_ALPHA");
drawscreen(panel, 0, 160, 1000, blend);
Screens and off-screen composition
allocscreen
void screen = allocscreen(width, height);
Creates a script-owned 32-bit screen and clears its pixels to zero. Internal allocation rounds the requested width down to a multiple of four, so widths divisible by four are the safest choice.
void panel = allocscreen(320, 80);
Use openborvariant("vscreen") to obtain the engine-owned main video surface. Never release vscreen with free().
clearscreen
clearscreen(screen);
Fills the target's pixel buffer with zero. Script-created screens retain previous drawing until cleared or overwritten.
Target-screen functions
drawstringtoscreen(screen, x, y, font, value);
drawspritetoscreen(sprite, screen, x, y);
drawdottoscreen(screen, x, y, color, blend);
drawlinetoscreen(screen, x1, y1, x2, y2, color, blend);
drawboxtoscreen(screen, x, y, width, height, color, blend);
The target screen is the first argument for text and shapes. drawspritetoscreen() is the exception: its sprite comes first and its target screen comes second. Blend arguments are required by the target-screen dot, line, and box functions; use BLEND_MODE_NONE when no blending is wanted.
drawspritetoscreen() and the target-screen shape functions use the current common drawmethod. drawstringtoscreen() prints directly with the selected font and does not use that drawmethod.
Composition example
The following setup creates a retained panel, redraws its contents, then places the result into the normal Z-sorted queue.
level.c
void main()
{
void panel = allocscreen(320, 80);
void icon = loadsprite("data/sprites/interface/icon.gif");
setglobalvar("direct_draw_panel", panel);
setglobalvar("direct_draw_icon", icon);
}
updated.c
void main()
{
void panel = getglobalvar("direct_draw_panel");
void icon = getglobalvar("direct_draw_icon");
int none = openborconstant("BLEND_MODE_NONE");
int alpha = openborconstant("BLEND_MODE_ALPHA");
clearscreen(panel);
drawboxtoscreen(panel, 4, 4, 312, 72, rgbcolor(24, 28, 40), none);
drawlinetoscreen(panel, 4, 4, 315, 4, rgbcolor(224, 224, 255), none);
drawspritetoscreen(icon, panel, 12, 12);
drawstringtoscreen(panel, 52, 16, 0, "MISSION STATUS");
// Supplying a blend also treats the cleared black area as transparent.
drawscreen(panel, 0, 160, 1000, alpha);
}
endlevel.c
void main()
{
void panel = getglobalvar("direct_draw_panel");
void icon = getglobalvar("direct_draw_icon");
if(!isempty(panel)) free(panel);
if(!isempty(icon)) free(icon);
setglobalvar("direct_draw_panel", NULL());
setglobalvar("direct_draw_icon", NULL());
}
Common drawmethod
The common drawmethod is global rendering state shared by compatible direct-draw calls. Obtain it and the read-only default state through system variants.
void common = openborvariant("drawmethod_common");
void defaults = openborvariant("drawmethod_default");
Use set_drawmethod_property() with named property constants. Useful properties include the following groups.
| Purpose | Property constants |
|---|---|
| Enable and flip flags | DRAWMETHOD_PROPERTY_CONFIG
|
| Position and pivot | DRAWMETHOD_PROPERTY_CENTER_X, DRAWMETHOD_PROPERTY_CENTER_Y
|
| Scale and rotation | DRAWMETHOD_PROPERTY_SCALE_X, DRAWMETHOD_PROPERTY_SCALE_Y, DRAWMETHOD_PROPERTY_ROTATE
|
| Blend and fill | DRAWMETHOD_PROPERTY_ALPHA, DRAWMETHOD_PROPERTY_FILL_COLOR
|
| Color channels and tint | DRAWMETHOD_PROPERTY_CHANNEL_RED, DRAWMETHOD_PROPERTY_CHANNEL_GREEN, DRAWMETHOD_PROPERTY_CHANNEL_BLUE, DRAWMETHOD_PROPERTY_TINT_COLOR, DRAWMETHOD_PROPERTY_TINT_MODE
|
| Clip rectangle | DRAWMETHOD_PROPERTY_CLIP_POSITION_X, DRAWMETHOD_PROPERTY_CLIP_POSITION_Y, DRAWMETHOD_PROPERTY_CLIP_SIZE_X, DRAWMETHOD_PROPERTY_CLIP_SIZE_Y
|
| Repetition and span | DRAWMETHOD_PROPERTY_REPEAT_X, DRAWMETHOD_PROPERTY_REPEAT_Y, DRAWMETHOD_PROPERTY_SPAN_X, DRAWMETHOD_PROPERTY_SPAN_Y
|
| Palette remap | DRAWMETHOD_PROPERTY_COLORSET_INDEX, DRAWMETHOD_PROPERTY_COLORSET_TABLE
|
| Water effects | DRAWMETHOD_PROPERTY_WATER_MODE and the perspective, size, wave, speed, and time properties.
|
Configuration flags are combined with the bitwise OR operator.
| Flag | Purpose |
|---|---|
DRAWMETHOD_CONFIG_ENABLED
|
Enables common drawmethod processing. |
DRAWMETHOD_CONFIG_BACKGROUND_TRANSPARENCY
|
Treats zero-valued background pixels as transparent. |
DRAWMETHOD_CONFIG_FLIP_X
|
Flips horizontally. |
DRAWMETHOD_CONFIG_FLIP_Y
|
Flips vertically. |
DRAWMETHOD_CONFIG_FLIP_ROTATE
|
Changes flip handling for rotated output. |
Scale values use 256 as 100 percent. The next example flips a sprite horizontally, scales it to 150 percent, queues it, then restores the shared state.
void common = openborvariant("drawmethod_common");
void defaults = openborvariant("drawmethod_default");
int config = openborconstant("DRAWMETHOD_CONFIG_ENABLED")
| openborconstant("DRAWMETHOD_CONFIG_FLIP_X");
copy_drawmethod(common, defaults);
set_drawmethod_property(common,
openborconstant("DRAWMETHOD_PROPERTY_CONFIG"),
config);
set_drawmethod_property(common,
openborconstant("DRAWMETHOD_PROPERTY_SCALE_X"),
384);
set_drawmethod_property(common,
openborconstant("DRAWMETHOD_PROPERTY_SCALE_Y"),
384);
drawsprite(sprite, 160, 100, 1000, 0);
copy_drawmethod(common, defaults);
Queued entries receive a copy of the common drawmethod at submission time. Restoring the common state immediately after drawsprite() therefore does not undo the queued sprite's transform.
Legacy scripts may use setdrawmethod() and changedrawmethod(). Named drawmethod properties are clearer for new code and reduce dependence on positional arguments or string property names.
Queue control
drawspriteq
drawspriteq(screen, newonly, minz, maxz, dx, dy);
Renders entries from the current sprite queue to a selected screen. Passing an empty first argument targets vscreen. Optional arguments default to all entries, the full Z range, and zero offset.
void snapshot = getglobalvar("direct_draw_snapshot");
drawspriteq(snapshot, 0, 500, 1500, 0, 0);
The function does not remove rendered entries. Its newonly option matters when the queue is locked; ordinary scripts normally use 0.
clearspriteq
clearspriteq();
Clears the active sprite queue, or only its new portion while the queue is locked. Normal game rendering shares this queue with backgrounds, entities, shadows, and interface elements, so routine scripts should not call clearspriteq(). Reserve it for controlled rendering workflows where removing every pending entry is intentional.
Script timing
Queued and immediate calls behave differently around the engine's frame clear.
| Script stage | Direct-draw behavior |
|---|---|
update.c
|
Suitable for queueing commands. Immediate writes to vscreen are erased by the main screen clear that follows.
|
| Native render setup | Clears vscreen and queues backgrounds, entities, shadows, and status elements.
|
updated.c
|
Suitable for queueing with final knowledge of game state. Immediate writes to vscreen survive because the main clear has already happened.
|
| Queue render | Sorts entries by Z and sort ID, then draws them to vscreen.
|
| Frame end | Presents the video output and clears the queue for the next frame. |
Queued calls made from either update.c or updated.c still take part in the final Z sort. Direct writes to a retained subscreen can be made at other times because that surface is not part of the automatic main-screen clear.
Limits and performance
The current sprite queue holds up to 5,000 entries, providing far more capacity than ordinary game objects require. Normal gameplay does not approach this limit, including object-heavy or bullet-hell designs. Reaching it is mainly possible when procedural scripts intentionally emit thousands of individual dots, lines, boxes, or text glyphs during one frame. Extra submissions are ignored only after the full capacity is reached.
Sprite loading and screen allocation are setup operations, not frame-by-frame drawing operations. Reusing allocations avoids file access, decoding work, heap churn, and leak risk.
Large subscreens cost memory and fill bandwidth, but a completed surface consumes only one sprite-queue entry when submitted with drawscreen(). In effect, subscreens act as sprite-queue multipliers by consolidating many separately drawn elements into a single queued item. Keep off-screen surfaces near the size of the content being composed, use widths divisible by four, and redraw only when their contents need to change.
Practical guidance
- Load script-owned sprites once, retain their pointers, and release them with
free(). - Use queued functions when Z ordering with entities or interface elements matters.
- Use target-screen functions to build reusable panels, effects, masks, or snapshots.
- Clear a subscreen before rebuilding it unless retained pixels are intentional.
- Create colors with
rgbcolor()and select blends with named constants. - Treat the common drawmethod as shared state: configure it, submit the draw, then restore the default state.
- Supply
drawscreen()'s blend argument when zero-valued background pixels should become transparent. - Keep immediate
vscreendrawing in a stage after the engine's main clear, such asupdated.c. - Avoid
clearspriteq()during normal gameplay because native rendering uses the same queue. - Resubmit queued artwork every frame where it should remain visible.