Describe text shaders, the font filter, Tab and Texture size

Shaders and Concepts: a text wears a shader like a sprite (#169).

Fonts: the Filter row and how Nearest rasters sharp (#169).

Keyboard: Tab belongs to the game in the Game tab (#261).

Behaviors: the Texture size node (#262). Development: sum 1544.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adminandClaude Opus 5.5 committed 2026-09-29 06:01:44 +02:00
1 parent 38ccef24f0
commit 2e15bb21de
6 files changed
+28 -5

No files matched your search

+1 -1
@@ -172,7 +172,7 @@ playing use the same two words.
the given object, nearer levels first, the order `Child by name` searches in), `Children` (the
children of an object; with **recursive** every object below it, level by level) and
`Objects of asset` (every instance of the chosen asset, spawned ones included),
`Variables`, `Properties` (every property row of the target's type, the transform rows `scale`, `rotation`, `mirrorX`, `mirrorY` included — how a graph turns a sprite), `Get position` (local and world), `Is enabled`, literals `Number`/`Text`/`Boolean`/`Texture`/`State machine`/`Sound`/`Scene`/`Animation`/`Asset`, `Add/Subtract/Multiply/Divide`,
`Variables`, `Properties` (every property row of the target's type, the transform rows `scale`, `rotation`, `mirrorX`, `mirrorY` included — how a graph turns a sprite), `Get position` (local and world), `Is enabled`, literals `Number`/`Text`/`Boolean`/`Texture`/`State machine`/`Sound`/`Scene`/`Animation`/`Asset`, `Texture size` (a texture's, frame's or pixel art's own size in pixels, 0 for none — wired into a sprite's `width`/`height`, a picture shows pixel for pixel), `Add/Subtract/Multiply/Divide`,
`Min/Max/Abs/Sqrt/Clamp`, `Lerp` (from a to b as t runs from 0 to 1, t held in 0…1; the setting
*Ease* bends the way: *Linear*, *In* starts slow, *Out* ends slow, *In-out* both, all quadratic,
or *Curve*, which follows the [curve asset](Curves) picked in the *Curve* row and may overshoot b;
+1 -1
@@ -6,7 +6,7 @@
| **Font** | An uploaded typeface, worn by a text through the ordinary property `font`; no font means the editor's own. Uploaded and picked exactly like a texture, and stored the same way. See [Fonts](Fonts). |
| **Sound** | An uploaded audio file. Uploaded, picked and stored exactly like a typeface — but nothing *wears* it: it is neither on screen nor in the scene, and a graph plays it with `Play sound`. Its own rows say how loud it is and whether it loops; the node scales the one and can overrule the other either way. See [Sounds](Sounds). |
| **State machine** | A named set of **states**, edited as nodes in its own tab. A behavior puts it into a state with `Set state`, moves it along a named transition with `State transition` and branches on it with `State branch`. Its **Scope** says where the current state lives: **Local** gives every behavior instance one of its own, **Global** keeps a single one for the whole run. See [State machines](State-machines). |
| **Shader** | A node graph that becomes the GLSL a sprite is drawn with, edited in its own tab. A sprite wears it through `shader`; the numbers the graph declares are rows below that dropdown. See [Shaders](Shaders). |
| **Shader** | A node graph that becomes the GLSL a sprite is drawn with, edited in its own tab. A sprite, a text or a particle source wears it through `shader`; the numbers the graph declares are rows below that dropdown. See [Shaders](Shaders). |
| **Particles** | A node graph that says what *one* particle is as a function of its age and of a random number drawn once for it — no forces, no simulation. Unlike the other graph assets it **is** placeable: the object made from it is a source, whose rows say how many go out and for how long they live. Because a particle is a formula, the same second always draws the same picture and the tab's slider can jump to any of them exactly. See [Particles](Particles). |
| **Text** | An asset that draws a line of text (`text`, `font`, `fontSize`, `color`, `align`) — titles, HUD readouts, button labels. Its content is an ordinary property, so a `Properties` node of type Text with `text` switched to Set changes it while playing. |
| **Enabled** | Every placeable asset has the switch `enabled` (default on). A disabled object is not drawn, does not collide and its behavior does not run — **and neither does anything parented below it**. Switching one instance off is a normal override, which makes a container a *screen*: switch it on to show it, off to hide it. From a graph, `Properties` with `enabled` switched to Set does it, and `Is enabled` asks (including the parents); a behavior hears the switch as `On enable` / `On disable`. |
+1 -1
@@ -15,7 +15,7 @@ npm run build # writes dist/ and regenerates index.html
php tools/make-user.php <name> <password> [admin|user] # an account; every tool signs in (see Accounts)
php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job)
php tests/php/snapshots.test.php # the snapshot store (#231), the same way
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1535 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 1544 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result
+15 -1
@@ -32,7 +32,21 @@ faces and `@font-face` cannot say which, and an SVG font can carry script.
dropdown of the project's font assets, with **None** for the editor's own typeface. That is
also what a reference falls back to when the asset behind it is gone, so a deleted font leaves
readable text rather than nothing. The row works in the scene view, in the game preview and
while playing — all three go through one place (`WorldViewport.syncText`).
while playing — all three go through one place (`WorldViewport.syncText`, which hands the text to
`TextPicture`: glyphs rasterised into a picture of the text's own, drawn like a sprite so a shader
can be worn — see [Shaders](Shaders)).
**Filter.** A font asset has one row, **Filter**, the same as a texture's: **Linear** (the default)
draws the glyphs smooth and renders them again, sharper, as the view zooms in. **Nearest** is for
pixel fonts: the glyphs end up at one texel per world unit and are magnified without smoothing,
so at a scale or zoom of 4 every font pixel is a block of four screen pixels. To get there they are
rasterised eight times finer and brought down again, each 8x8 block becoming one texel that is
either fully inked (half the block or more) or empty. No texel is left half covered, on any
platform — Chrome on Windows smooths canvas text sideways whatever the filter, and without this
step every glyph column there had a grey fringe. The texture stays as small as at 1:1. A pixel font
looks cleanest at the size it was drawn for (8 or 16 for most); at any other size its pixels come
out hard but unevenly wide, and a font that is no pixel font loses its thinnest strokes. Every
text wearing the font follows its filter; the text itself has no filter row.
**It arrives late.** A face is fetched over the network, so a text is drawn in the fallback for
the first frames and then reflows into the real one. That is the same "not yet, redraw when it
+1 -1
@@ -5,7 +5,7 @@ container from the selection · `Ctrl+Enter` play, and pause/resume while playin
under the pointer ([Scene editing](Scene-editing), [Behaviors](Behaviors)) · `Ctrl+V` import an image from
the clipboard as a texture when nothing copied fits the tab (ignored while a text field has focus) · Animation tab: `Ctrl+C` copy
the picked keys, `Ctrl+V` paste them at the playhead (while keys are copied this takes `Ctrl+V`
there, so an image is pasted from another tab), `Ctrl`+drag duplicate keys · `Tab` / `Shift+Tab` cycle fields · lists: `↑`/`↓` move,
there, so an image is pasted from another tab), `Ctrl`+drag duplicate keys · `Tab` / `Shift+Tab` cycle fields (in the Game tab with no field focused, Tab is a game key like any other, and the key field of `On key down` takes it too) · lists: `↑`/`↓` move,
`←`/`→` fold, `Enter` activates · number
fields: drag to scrub (`Shift` x10, `Alt` x0.1, snapping to the grid of that increment, so a
step-1 field lands on whole numbers), `↑`/`↓` step, shown with at most three decimals and without trailing zeros
+9
@@ -160,6 +160,15 @@ never there to lose. The difference is what the fragment can see — a particle'
age or index — and that a source, having rows of its own graph as well, keeps a name the shader
also declares: the uniform then stays on its default rather than taking the row.
**A text object wears one the same way** (#169), through a *Shader* row below Align. Its glyphs are
rasterised into a picture of their own and drawn on the sprite quad, so the graph sees exactly what a
sprite's graph sees: `Texture color` is the glyph pixel in the text's colour, `Texture alpha` the
glyph's coverage, and `u`/`v` run from 0 to 1 across the rectangle of the rendered text, line
height included. A new text, face or size is rasterised again and keeps the shader. Like a source,
a text keeps a name it already has (`text`, `fontSize`, `align` …): a shader parameter of that name
gets no row. In a `Properties` node the parameters are offered under *Type: Text* as they are under
*Type: Sprite*. A shaded text is one draw call, like a shaded sprite.
## What it costs, and where it stops
A shader runs in the sprite's own draw: the mesh is drawn once, with the graph's fragment in place