Releases / v0.72.1
v0.72.1 Wear It Well
2026-08-20 Engine
Materials on models, sorted out. A material you put on a model now actually replaces what the model came with, one part of a model can be given its own look from a script, and a model's own textures can be taken out of it as files. Plus a tint that leaves everything else alone, and a sprite you can flip in one line.
A material you apply is the material you get
Putting a Material on an imported model and pointing it at a texture left the model wearing its old picture. Its colour tinted, its emissive glowed, its roughness took — everything except the thing you could see.
A Material on a model now supersedes the materials the model was imported with. Every part draws with it, textures included, so a material naming no texture draws untextured. That is what the component is for: this whole thing is made of THIS. A model that already looks right needs no Material at all.
To change one part instead, use the model's own material list — or, now, a script.
On upgrade: a model wearing a node Material will look different, because it is finally wearing it. If you had one on there to nudge a colour or add a normal map, either move it to the parts you meant (◑ Model materials in the Inspector) or replace it with a tint, below.
Dressing one part of a model, from a script
for _, slot in ipairs(node:materials()) do
print(slot.material, slot.object, slot.textured, slot.overridden)
end
-- Clothing Torso#2 true false
-- Head Head#2 true false
-- Pants RightLeg#2 true false
node:material("Clothing").texture = "art/shirt.png"
node:material("Pants").texture = "art/jeans.png"
node:materials() says what a model's parts are called; node:material(name)
is one of them, read and assigned. Every field of a material is there — the
texture and the surface maps by path, color / emissive / specular / rim
as colours, and alpha, roughness, metallic, emissiveStrength, unlit,
fog, cell as values.
Ask before you address: a model that repeats an object name has it renamed on
import (Torso becomes Torso#2), so a guessed name reaches nothing. Every
part answers to two — its object, which is exactly one part, and its
material, which is every part wearing it. A character's Clothing is
usually its torso and both arms, which is the group a clothing system wants.
A material's fields read back now, too. mat.texture used to answer nil
however many times it had been set, so a script could tell a material what to
wear and never ask.
Taking a model's own textures out of it
A .glb keeps its images inside itself, where nothing else can point at one.
Select a model — or its node — and press ⬇ Extract textures: each material's
image is written beside the model as <model>_textures/<material>.png, and from
then on it is an ordinary project texture. Paint over it in the 🖼 Image tab,
assign it to anything, use it as the base layer a costume is drawn on.
Overriding one part of a textured model extracts that part's texture on the spot and starts the override wearing it, so "override" never means "go blank".
The same model, but red
node:setTint(color(1, 0.3, 0.3)) -- hit flash
node:setTint(teamColor) -- one prefab, four teams
node:setTint(color(1, 1, 1), 0.4) -- ghosted while being placed
node:setTint() -- back to normal
A tint is multiplied over whatever a node already draws, so the model keeps its own textures and each part keeps its own colour. It needs no Material and does not create one. In the Inspector it is the ◐ tint swatch beside a model's material list; white is no tint.
It is a component, so an animation clip can key it — a hit flash is a tint faded
back to white over a fifth of a second — and a script can read it back with
node:getcomponent("Tint").
Flipping a sprite is one line
local sp = node:sprite()
sp.flipX = mx > 0
node:sprite() is a Sprite's own drawing numbers as something you hold:
flipX, flipY, cell, ppu, size, pivotX, pivotY, each readable and
assignable. Reads answer with what you last wrote, so asking which way a
character is facing is a question you can now ask.
vec3 and vec2 also answer to :magnitude(), the name most engines use for
:length().
A table of options written as a list is now refused. node:setSprite{ 8, 1, true } set nothing at all and said nothing about it — every option is read by
name. It now tells you so, and names the keys it reads. This applies to every
call in the API that takes an options table.
Shots and headless runs tell the truth
floptle shot drew a scene in the textures its models were imported with,
whatever their materials said, and photographed planets before their ground had
loaded. It waits for the world now, and says on the error stream when it ran out
of patience rather than handing you a finished-looking picture of an unfinished
world.
floptle run reported the full span of simulated time even when it had
simulated none of it — a paused session steps happily at a dt of zero. It
reports the time it actually advanced, and says when the two differ. Runs of a
project with generated terrain now advance at all.
Games that build their world as the player travels can ask
terrain.busy() whether the background worker is occupied — true from the
moment a fill is queued, so building one system and waiting for quiet before the
next is a loop you can write.
Smaller things
- A library script has its settings before anything calls it. A script with
no
startand noupdate— one that exists so others can call into it — heldparams = niluntil something happened to tick it, so the first call into it raised. Whether it raised depended on the order nodes sat in the scene file. - ⊕ Transform ▸ … ▸ Reset transform puts a node back to the origin, no rotation, scale 1. Copy and paste values were already there.
- The right edge of the panels is no longer under the scrollbar. A slider's
label could be cut to its first letter, and the
…menus sat half over the edge. node:getcomponent("Sprite").flipXreads back as a boolean. It was a number, and0is true in Lua — so "is it flipped" was always yes.- Headless runs no longer warn that perfectly good models have nothing to draw.
Upgrading
Open your project once and the editor updates its editor-support files.
The one thing to check is any model with a Material on the node: it now wears that material instead of blending with what it came with. The Inspector's ◑ Model materials list is where to put it back per part, and ◐ tint is what to reach for if what you wanted all along was "the same model, but coloured".
Floptle Hub
Engine
The binaries are not signed yet. On macOS, right-click the app and pick Open the first time. On Windows, if SmartScreen appears, pick More info then Run anyway.