The asset graph

The Asset graph is where an export is designed; the Export window is where it is run. The Asset step turns sprite lists into files, and the way it does that is a graph: sprite lists come in, are arranged on a sheet, and go out as files. Those are the three kinds of wire, and they cannot be mixed up — a list cannot be wired into a Sheet without being arranged first, and a sheet cannot be tagged.

Its inputs are Sprites nodes (and Image file nodes, for pictures from outside), each bringing in sprite lists: an action seen by a camera, drawn with a Style, with a part set, cut into frames, with every layer. A Sprites node names all of them, fixed: the window header’s choices are not its, so what an asset writes never depends on what happens to be shown. Its outputs are one node per file. A Sheet node is a sprite sheet in memory: it shows the sheet on its face and writes nothing. Every file is a File node wired from it — PNG image for the sheet itself, then its JSON, its .aseprite, its Godot SpriteFrames, its normal map. To export a sprite sheet, wire Sheet → PNG. A graph may have as many of each as the project has files to write.

Everything an export can be told is on a node, but which folder it goes to: that is the Export window’s, which lists every file the graph will write there, by name and kind, before you press Export — the quickest way to see what a change to the graph does to the files. How a sheet’s frames are named is on its PNG image node (and in the Inspector with its Sheet), its layout on the Arrange feeding it, and each other file it writes — with that file’s own settings: a GIF’s scale, a video’s rate, which shadow, which palette variants — is a File node wired from it. A project that never rewires the graph stores none: it is one sheet of every sprite list.

A picture can show any layer. Under a Sheet’s picture here, Layer picks the layer it draws — until it is told, the one the 2D View’s own Layer draws — the sprite, its lines, its flat colours, its normals, depth, emission, a Style’s extra output — or Every layer at once: a frame’s side by side, a list’s a row each.

That is the graph you start with, the Asset pipeline Main:

Sprites (every action except Static)  →  Arrange  →  Sheet (sheet)  →  PNG image

The window header’s choices

Action, Style and Asset are independent pipelines, each a graph of its own. The right end of the workspace tabs’ row holds four selects — Camera, Action, Style, Asset — after Play/Pause: one choice of each, across every step.

Step Draws
Action the chosen action, seen by the chosen camera
Style the chosen action, seen by the chosen camera, drawn by the chosen Style
Asset what its own graph’s Sprites nodes name — not the header’s choices

The Styles list (Style step) and the Assets list (Asset step) are the same choices as the header’s Style and Asset selects: a click in either shows the same thing. The Export window starts with the Asset pipeline chosen in the header’s Asset select, and writes it alone; its files are named as when every Asset pipeline is written (pxr render --pipeline all writes them all).

Where the sprite lists come from

The sprite lists are made here, by the Sprites nodes, and only here: which actions go in the sheets is what the Sprites nodes take, nothing else. A Sprites node names a camera, an action, a Style and a part set, and holds the cut: how the action’s motion becomes frames.

Its Action is one of:

  • Every one except Static (every-one-except-static, what a new Sprites node, the Asset pipeline a project starts with and a new project take): every action of the Actions list but Static, the rest pose standing still — Static all the same when the project has no other action (a still model), so a crate still has its sprite.
  • Every one (empty, or *): every action, Static among them.
  • An action by name, Static among them: that action alone, or several (walk, attack from pxr graph set). A clip of the model is drawn when a Sprites node names it whether or not it is one of the project’s actions — named, its frames are always there, in the node’s picture, in what it feeds and in the export.

A name that is nothing (an action deleted or renamed elsewhere) is said on the node, in plain words: No frames of Sway seen by South: choose an action and a camera the project has on the Sprites node.

The cut is on its face and in the Inspector:

  • Frames/s (fps): one frame every so many seconds, 12 a second until told.
  • Kept frames (smart): so many smart frames, chosen as an animator would — the poses where the motion turns, more where it is fast, each held until the next. 0 is off. Smart frames on its face turns them on, Use frames/s goes back to an even step.
  • Picked frames (picked): times in seconds, picked by hand. With the Sprites node chosen, the Timeline behind the asset graph shows its frames: click the key strip to add or remove one, or drag a key along it (one undo step each). The face says N frames picked on the timeline.

Picked frames win when there are any, then kept frames, then frames a second. A Sprites node added to a graph starts cut as the graph’s first one is. An action made of nodes or keys is cut at its own frames. Hold (ms) beside the Timeline’s frame counter sets the shown frame’s hold (see holding one frame longer): the action’s, so every Sprites node that takes the clip keeps it. The Action step is not changed by any of this: it plays the whole motion, and a sprite frame lands on the time it was cut at.

Between the Sprites node and the Arrange go the nodes that slice a list:

Node What it does
Frame range Only frames from to to of what comes in, counted from 0: the wind-up of an attack as a list of its own, one frame of an idle as an icon, a list over two sheets. A range keeps what each frame showed — its time in the clip, its part set, its root travel. A range of a range counts in what came in; part of a loop no longer loops; a range past the end keeps the last frame.
Hold Every frame held so many times as long: 2 plays the list at half speed.
Sprites (South, attack, Pixel Art, smart 6) → Frame range (2 to 5) → Hold (×2) → Arrange → Sheet → PNG image

Choosing a Sprites, Frame range or Hold node makes the 2D View play the list it gives, cut, sliced and layered as it will be written. Neither node costs a render: they decide which frames go where before a pixel exists.

To use a chain again, pick Sprites → Frame range → Hold and group them (Ctrl+G), then Save kit… keeps the group as a node kit under My nodes; or save the whole Asset pipeline as a kit (see Asset pipelines).

Renders, and what their files are called

A render is a framing, a Style and how each clip is cut. Sprites nodes share one render while they agree: the same Style, framed together, and no clip cut two ways. So a walk at 8 frames a second and an attack picked by hand are one render and one sheet. A second node that cuts the walk into smart frames, or draws it with another Style, is a render of its own.

The first render writes its files as the graph names them. Another is written beside them under the words that differ from the first’s: its camera, its Style’s name, then the cut of each clip it cuts otherwise — 8fps (or 12_5fps), smart6, picked4 (four frames picked by hand). So out/hero.png, then out/hero_smart6.png and out/hero_Pixel_Art_2_8fps.png, each on the palette of its Style. pxr names lists each render’s files, with the frames a Frame range keeps, and refuses two renders that would write one file — two different hand-picked cuts of four frames of the same clip — before anything renders: would be written twice.

From a script:

pxr graph add game.pixor --which asset --node sprites --set action=attack --set smart=6   # node 4, merged in by node 5
pxr graph add game.pixor --which asset --node frame-range --set from=2 --set to=5 --alone --in frames=4   # node 6
pxr graph add game.pixor --which asset --node hold --set times=2 --alone --in frames=6                    # node 7
pxr graph wire game.pixor --which asset --from 7 --to 5 --slot frames2   # the cut attack into the Merge, in place of node 4
pxr graph set game.pixor --which asset --node frame-range --set to=6
pxr project set game.pixor --add-style portrait
pxr graph add game.pixor --which asset --node sprites --set action=walk --set style=portrait --set fps=8
pxr names game.pixor

The Frame range and the Hold go between the attack’s Sprites node and the Merge, so they cut the attack alone. Added without --alone, they would go before the Arrange, after the Merge, and cut every list the Merge joins.

pxr render --fps 8, --smart-frames 6 and --keys 0,0.25,0.5 set every Sprites node’s cut at once. A blueprint run with pxr run game.pixorblueprint --set hold.times=4 reaches every Asset pipeline’s Hold node, --set icons/hold.times=4 only the pipeline icons’.

The panel

Once a graph is stored, the 2D View above it shows what the chosen output writes — its takes, blocks, places, stacks and overlays — drawn by the very code that writes the file; before that, it is a row per action and side, which you can drag to reorder.

Asset graph is a panel of the Asset step, under the 2D View, on the same canvas as the Scene graph: boxes, wires, settings on each node’s face, a plus to add a node into the chain wired up, a minus to delete one, Frame to see it all, Tidy to lay it out again, Note to put a note on it, Group to make the nodes picked one group, and Save kit… to keep the chosen group, or with none chosen the whole graph, as a kit (Save the Asset graph as a kit) — the bar every graph has, with the keys every graph has (Working in it). Its node library down its left lists Inputs and Outputs, the project’s Pipelines and My pipelines, then Between and your own kits under My nodes. Right-click the canvas › Back to one sheet puts it back to one sheet with the files it had. Icons writes inventory icons. The line along the bottom says what the graph writes. Generated files, first on the bar, opens the generated folder in your file manager.

The Asset step: the sheet in the 2D View, the Sheet node in the Inspector, and the Asset graph below

Every file node has Open: it opens that node’s file in the application your system opens that kind of file with — the sheet in an image editor, the .aseprite in Aseprite, the video in a player, the archive in an archive manager (its tooltip names which). A file that is not generated yet, or is out of date, is generated first, with a loader on Open meanwhile.

Every setting of an asset node is a property (see nodes): a row with a port at its left, set on the node or wired from a value. Delete deletes the chosen node, as the minus does; the last node that writes stays.

Every node between the lists and the files shows what it hands on, laid out on a sheet: a Layer node its layer, a Frame range its frames, a Flip its mirror, an Arrange its sheet. A picture shows everything its Sprites nodes name, whatever the window header shows: every camera, action and Style, from every render they are drawn in. Cameras framed apart (another view, pitch or size), another Style or a clip cut another way are renders of their own, written to sheets of their own; a picture fed by several lays out one block for each sheet, under the name of the file it is written to (hero.png, then hero_top.png), its rows as that sheet’s are. The renders the 2D View does not show are drawn behind it, one after the other, and kept until the project changes. One node never stops another’s picture: each is laid out from what feeds it alone, and a node that cannot be — a picture file not chosen yet, a Merge with nothing wired into it — says why in red where its picture would be, and so do the nodes it feeds, while the others keep theirs. The pictures are laid out from every list, drawn whole: while a chosen Frame range or Hold has the 2D View play its one list, they keep the lists last drawn whole, and a node added meanwhile gets its picture from them. When something they are drawn from changes meanwhile — a part hidden in the Outliner, a material, a lamp — they are drawn again behind the 2D View, which keeps playing the range. Every file node shows its file before it is exported, made by the same writer an export uses: a picture of it on the node, and the whole of it in the 2D View when the node is chosen — a PNG or a map as a picture, a GIF, an APNG or a video playing, a JSON, a Godot file or a text file as text, and a ZIP as the list of what it holds. A node that writes several files (a GIF per clip, a layer per file) has a File list above the view.

Fast files, and Generate

What is fast to make is shown as the graph changes; what is slow waits for its node’s Generate (D51). Fast: everything between the lists and the files, the Sheet, and the PNG image, JSON, Godot SpriteFrames, Palette, Text file, Emission map, Shadow layer and Line and flat layers. Slow: GIF, APNG and Video per clip, the Aseprite file, Normal and Depth maps, Palette variants, Light kit, PICO-8 cart, Report, ZIP archive, Animation file and Icons — what encodes every frame again, or renders. (Paper-doll and Parallax layers are renders of their own: the Export window makes them.)

A slow node shows Generate instead of its file. Pressed, the file is made off to one side — the button turns a loader, and you keep working — then the node shows its picture, the 2D View the file, and the button reads Regenerate. Change anything feeding it, and the node keeps what it made but says Out of date in place of making it again: press Regenerate, or Open, which generates first.

Generated files

What Generate and Open make are real files, in a temporary folder of the open project (under your system’s temporary folder, named pixor-generated- and the project), a folder an Asset pipeline and one a node. Generated files on the bar opens it. It cleans itself: deleting a node deletes its files, deleting an Asset pipeline its folder, and closing the project or quitting Pixor the whole folder; a folder a crash left behind is removed the next time Pixor starts. They are never what an export writes: the Export window writes every file fresh.

The chosen node’s settings are in the Inspector too — and with nothing chosen, the graph’s own Sheet’s. Under a Sheet’s settings there, every file it can write is a toggle: ticking JSON or Video per clip adds that File node, wired from the sheet, and unticking removes it. So an export is set in a few clicks without touching a wire, and it is still a graph: every toggle is a node you can see, with its own settings.

The nodes

N x N nodes. Layer, Frame range, Hold, Flip, Tag, Arrange and Place do the same thing to as many lists as are wired into them, and give each back on an output of its own, across from its input: one Layer node takes the normals of four sprite lists and hands on four lists. A spare input is always there for the next one. Merge is the other way: any number of lists in, one out.

Node Takes Gives What it does
Sprites — frames Input. Sprite lists, fixed here whatever the window header shows: with several scenes, the scene chosen (or every one); the camera chosen (or every one) — a camera is a side — the action chosen (Every one except Static, Every one, or one by name, Static among them), drawn with the Style chosen (the project’s first of each until told), and cut as its Frames/s, Kept frames or Picked frames say (see where the sprite lists come from). Camera and action also take, from pxr graph set, a comma-separated list (South, East, North) or a name’s start and * for a ring (turret*). A part set of the project renders every clip again with that set on over every frame (equipment, damage states). Every layer comes with the list; a Layer node picks one, and without one the sheet is of the finished sprite.
Image file — frames Input: a picture from outside, as a sprite list of its own: a PNG — one frame, or a strip or grid cut into frames Frame width by Frame height, each held Each frame — or a GIF, with its frames, their delays and whether it loops. Called is its row’s name. The picture takes the sheet’s palette (each pixel its nearest colour; what is more than half transparent is empty) and is centred on a cell; a frame bigger than a cell is refused, with both sizes. It goes on the sheets its node is wired to and on no other.
Merge frames, … frames Every list wired in, as one: many sprite lists on one sheet.
Layer frames × N frames × N One layer of each list: final, lines or flat, or one of the maps a game lights the sprite with — normal, depth, emission — which makes the sheet it lands on a picture of colours rather than palette indices — or shadow, the shadow each frame casts, in black (the one the project’s Shadow asks for, else a contact shadow), which an Overlay puts under the sprite; or the name of one of the style graph’s extra outputs, whose rows are then named after it (idle_South_rim). A render’s own pictures are layers too: with props or pieces, a paper doll’s base and each piece by its name (idle_South_helmet); with parallax layers, layer0 (the farthest) to the last. Each is rendered only when a Layer names it, by pxr render — which is what the app’s Export window runs. Separate a list’s layers with a Layer node per layer, each to its own Sheet; combine them with a Merge, a Stack or an Overlay.
Frame range frames × N frames × N Frames from to to of each list, counted from 0: from=3 to=3 for a still or an icon (a new one is from=0 to=0, the first frame), from=2 to=5 for a few. Separate a list over several sheets with a Frame range per part.
Hold frames × N frames × N Every frame of each list held times as long.
Flip frames × N frames × N Each list mirrored left to right, standing for another camera: a Sprites node of East flipped as West is walk_West, drawn from walk_East — pixels, normals, hit boxes and root motion mirrored. A symmetric model need not render its other side.
Tag frames × N frames × N Whether the rows loop (Keep, Yes or No), and a pivot of their own (x,y in cell pixels) for a clip that stands differently. A clip that does not loop is a tag that plays once in the JSON and the .aseprite; the pivot is on every frame of the JSON and a key of the pivot slice.
Arrange frames × N sheet × N Each list on a sheet: grid or strip, padding, extrude, power of two, trimming, repeated frames stored once, a maximum width to wrap rows under, a maximum height past which the sheet is split into pages (whole clips to a page, each its own set of files), and frames per row, which runs every clip on after the last as a contact sheet does.
Place sheet × N sheet × N Each sheet moved to a place on the image.
Stack sheet, sheet sheet Two sheets as one, the second under or beside the first, with a gap.
Overlay bottom, top sheet One sheet laid exactly over another, the way the engine stacks layers: the top’s empty pixels show the bottom. Each layer can be turned off to see the other alone. The two must line up frame for frame — the same frames in the same places — or the export says which frame does not.
Sheet sheet file A sprite sheet in memory. It writes nothing: its files are File nodes wired from it. Called is its name.
File sheet file Output: one file on disk, written from the Sheet it is wired from, with its own settings on its face, Open to open its file, and Generate when it is slow to make. PNG image (the sheet itself, and what its files are called: Files to, the path the PNG writes, whose name the others take, under the folder the Export window is given; File names, the template its frames are named by, with the first name it makes shown under it; Recipe in files, whether the files carry how they were made; Its own files gives the sheet a path, names and recipe of its own, and off, its files go where the project exports — the Inspector shows these with the Sheet too), JSON, Aseprite file, Godot SpriteFrames, Normal map, Depth map, Emission map, Line and flat layers, Palette, Light kit, PICO-8 cart, Report; GIF per clip and APNG per clip (Scale); Video per clip (Scale, Frames a second, Background); Shadow layer (contact or drop); Palette variants (the list of them); Paper-doll layers (Pieces); Parallax layers (Layers, Haze far layers). Deleting a Sheet deletes its File nodes.
Text file sheet file Output: a text file from a template over the frames (below).
Animation file frames file Output: an animation of each sprite list wired in, with no sheet between: a GIF, an APNG or an uncompressed video (Each list as), Scale times bigger, a video at Frames a second over Background. To starts every file’s name: out/spin writes out/spin_walk_South.gif.
Icons — file Output: inventory icons of the model, from the ¾ angle under the studio light, outlined in the Rarity’s colour (or From the file’s name), Framed or not, at each of the Sizes: a PNG an icon and an atlas and a JSON index a size, into the folder To. An icon is a render of its own, so it takes nothing in; pxr makes them after the sheets, into the export’s folder. See inventory icons.
ZIP archive file, … file Output: a ZIP of the files of the file nodes wired into it (To is where it goes) — a Sheet wired in is everything that sheet writes — or, with nothing wired, of every file the export wrote. The paper-doll and parallax layers go in with the rest. The same files give the same archive, byte for byte. What is handed to someone else, or uploaded to a store.

The Asset graph: Sprites, Arrange, Sheet and the PNG image it writes, with the node library on the left

A plan, not a render. The graph decides which frames go where before a pixel exists, so the Asset step works with the Style step empty, and pxr names prints every name and id the graph will write without rendering.

Frames keep their identity. Re-arranging a sheet moves rectangles and leaves every frame’s id alone (see What the frames are called, and what they are).

Asset pipelines

A project has as many Asset pipelines as it likes, each an asset graph of its own under its name — the character’s sheet, an icon set, a store page’s stills — and the Export window writes the one you choose (pxr render --pipeline all writes them all). The Assets list down the left of the Asset step and the header’s Asset select choose the one on the canvas. New Asset pipeline under the list makes a copy of the one shown, named after it (Main 2), and a row’s right-click menu renames it, Duplicates it, deletes it (Delete NAME…, which asks first: the first too, while another is left), and:

  • Save kit… (Save the Asset pipeline as a kit) writes the pipeline as a kit. Saved in the library of pipelines (where the dialog opens), it is listed in the node library of every project under My pipelines.
  • Use kit… (Use an Asset pipeline kit), or a click on one under My pipelines, makes a pipeline saved from another project this project’s own. Its names — the pipeline’s, its sheets’, its files’ — are made ones nothing here has.
pxr project set game.pixor --add-pipeline icons
pxr graph set game.pixor --which asset --pipeline icons --node arrange --set layout=strip
pxr graph save game.pixor --which asset --pipeline icons --as-pipeline -o icons.pixorkit
pxr project set other.pixor --use-pipeline icons.pixorkit
pxr project set other.pixor --remove-pipeline icons

Pictures from outside

An Image file node puts what was drawn elsewhere on a Pixor sheet: a logo on a title sheet, a hand-painted effect, frames from another tool. Add it, Choose… a PNG or a GIF, and it joins the sprite lists through a Merge; wire it to a sheet of its own to keep it apart.

pxr graph add game.pixor --which asset --node image-file --set name=logo --set file=art/logo.png
pxr graph add game.pixor --which asset --node image-file --set name=spark --set file=art/spark.gif
pxr graph list game.pixor --which asset                                             # every node, numbered
pxr graph set game.pixor --which asset --node logo --set cell_w=16 --set cell_h=16   # a strip of 16 px frames
pxr graph set game.pixor --which asset --node logo --set own_colours=true            # keep its colours

--node takes a node’s number from pxr graph list, its name (logo) or its kind (frame-range) when the graph has one node of it.

A picture takes the sheet’s palette: each pixel the nearest of its colours. Tick Its own colours and it keeps its own instead: they are added to the end of the sheet’s palette (most used first, as many as the palette has room for), so every file of the sheet — the PNG, the .aseprite, the GIFs, the palette files — carries them, and a palette variant leaves them as they are. The sheet’s own frames do not change.

A picture bigger than the cell grows the cells of every file it goes on: the other frames are centred in the bigger cells, and their pivot, hitboxes and sockets move with them.

Several files from one render

A graph may have several Sheet nodes, each with a PNG wired from it: one render then writes a character sheet, an icon set and a promo still. A Sheet with no PNG writes no image — only the files that are wired from it, which is how a sheet’s JSON alone is written. A second Sheet takes what the first takes until it is wired to lists of its own. Each has its own path, name template and recipe, or takes the project’s; the files beside it — and their shadow, variants and scales — are its File nodes. When two take the project’s, the first writes the project’s sheet and the next writes beside it under its own name (out/hero_icons.png for one called icons), so nothing is written twice; two that name one path of their own are refused with both names. A Sheet added from a script is called what no other output is (sheet 2), as in the app, and pxr names lists the files under the names the render gives them.

Cameras with the same view, pitch, size and look are framed together and write one sheet, a row for each: a ring’s sides line up. A camera set up otherwise writes its own sheets, and a Sprites node takes the rows of the group being written. A Sprites node that names cameras from two groups — South, top — gathers them into one file instead, written once every camera has rendered, beside the project’s sheet under its Sheet node’s name (out/hero_both.png); rows are named for their camera (walk_South, walk_top). One sheet has one frame size and one palette, so cameras that frame at different sizes are refused rather than squeezed together: give them the same size.

An icon grid is three nodes: a Sprites node of the idle action from camera South, an Arrange as a strip, and a Sheet whose PNG goes to out/icons.png. A contact sheet is the same with padding. They are not features; they are graphs.

From a script:

pxr graph add game.pixor --which asset --node sprites --set action=idle --set camera=South --alone   # node 11
pxr graph add game.pixor --which asset --node arrange --set layout=strip --alone --in frames=11      # node 12
pxr graph add game.pixor --which asset --node sheet --set name=icons --set path=out/icons.png --set formats=png --alone --in sheet=12
pxr graph set game.pixor --which asset --node icons --set formats=json,godot   # its File nodes
pxr graph wire game.pixor --which asset --from 10 --to 12 --slot frames2       # the spark into the Arrange's spare input
pxr graph add game.pixor --which asset --node sheet --set name=sparks --set path=out/sparks.png --set formats=png --alone --in sheet=12.1   # output 1 of an N x N node
pxr graph list game.pixor --which asset          # every node, and every output's path
pxr graph drop game.pixor --which asset --node logo   # the logo's Image file node
pxr render game.pixor                            # out/game.png, out/icons.png (.json, .tres), out/sparks.png

The walk drawn in the portrait Style is framed apart, so it writes its own sheets beside these: out/game_portrait_8fps.png, and the spark, which has no Style of its own, again in out/sparks_South_portrait_8fps.png.

The numbers are those the walkthrough on this page gives; pxr graph list prints a project’s own. --alone keeps the icon grid out of the main sheet’s chain: an idle list merged into it as well would be the same frames twice, which the name template refuses.

pxr graph add puts a node into the chain, wired up: a Frame range or a Hold before the Arrange, a sheet node after it, and a second Sprites node beside the first through a Merge. --set on a Sheet takes path, names and recipe; formats adds and removes its File nodes — the PNG always among them, as in an export’s own list; drop its node to write none — and the words of the settings its File nodes hold make and set those nodes: shadow=drop, variants=hit-flash,hue:120, anim_scale=4, paper_doll=yes, pieces=helmet:Helmet,Visor, parallax=3, haze=none. A File node’s own settings are set on it by its number: --node 5 --set fps=60.

Lines beside colours

A sheet may carry more than one layer of the same lists. Wire the Sprites node twice — once straight on, once through a Layer of lines — and either join them with Merge before one Arrange, or arrange each and Stack the two sheets beside each other. The JSON names the lines rows walk_South_lines, and their ids differ from the colour frames’ by their layer, which is what they are. A template that does not say {layer} adds it to those frames’ names anyway (hero_walk_South_00_lines.png), so two frames never land on one name; the sprite’s own frames keep theirs. A Sprites node of a part set does the same with {set} (hero_attack_South_00_bare.png), and the set is part of those frames’ ids. Or lay the two exactly over each other with an Overlay, the way the engine will draw them.

Text files from a template

An engine Pixor has never heard of needs a descriptor, and that is a text file over the same frame metadata the JSON carries. A Text file node writes one from a template:

{sheet} {width}x{height}
{#frames}{name} {x} {y} {w} {h} {duration}{sep}
{/frames}

{#frames} ... {/frames} writes its body once per frame. Outside it, the words are the sheet’s: {project}, {object}, {sheet} (the image’s file name), {width}, {height}, {cell_w}, {cell_h}, {pivot_x}, {pivot_y}, {frames}. Inside it, a frame’s: {name}, {id}, {action}, {side}, {row}, {index}, {n} (the frame’s number on the sheet), {layer}, {x}, {y}, {w}, {h}, {source_x}, {source_y}, {duration} (milliseconds), {pivot_x} and {pivot_y} (the clip’s own, when a Tag gave it one), {loop} (true or false), {root_x}, {root_y}, {root_dx} and {root_dy} (root motion, as the JSON gives it), {boxes} and {sockets} (as JSON), and {sep}, which is a comma except after the last frame — enough for a JSON list. {index:3} pads a number with zeros; {{ and }} write a brace.

A template can live in its own file beside the project instead: give the node a Template file (--set file=frames.tpl from a script), and it is read each time the output is written, so a studio edits its descriptor where it keeps its other files.

There are no conditionals, no arithmetic and no includes. It is a way to write a file, not a language. A word Pixor does not know is refused when the template is typed, not written into a file.

A layout used as one group

An icon grid, a contact sheet, a studio’s own way of laying out a character: Save kit… on the bar, with no group chosen, saves the asset graph as a kit (Save the Asset graph as a kit): its layout, what feeds its Sheet, as one group that takes sprite lists in (each Sprites node becomes an input) and gives the sheet out. The Sheet and the files hanging from it stay the project’s. + › Use kit… (Add a kit as a group) puts it in another project’s asset graph as one group, to wire into a Sheet there or put in place of an Arrange. Double-click it, or press the arrow on its title, to open it in place; the arrow folds it again. From a script:

pxr graph save hero.pixor --which asset -o icon-grid.pixorkit --name "Icon grid"
pxr graph add villain.pixor --which asset --kit icon-grid.pixorkit --alone --in 0=0   # fed by node 0
pxr graph swap villain.pixor --which asset --node arrange --kit icon-grid.pixorkit

What it will not do

  • Binary formats stay code. PNG, .aseprite, GIF, APNG, Godot SpriteFrames, the light kits and .p8 are Sheet and File nodes; a template writes text.
  • No plugin binaries and no script nodes, here as in the other graphs.

What this page describes is in the full version; the free browser demo keeps to the default look and the PNG sheet.

Try in browser Get Pixor