scenario-game-assets
Use when creating game art through Scenario MCP: sprites, sprite sheets, icons, props, loot, tilesets and autotile terrain transitions, seamless tiles, isometric buildings, top-down maps, pixel art, UI buttons and panels, parallax background layers or depth planes for side-scroll
Install
npx skills add https://github.com/scenario-labs/skills/tree/main/skills/scenario-game-assets
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install scenario-labs-skills@llmmart
git clone https://github.com/scenario-labs/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole scenario-labs/skills collection as a plugin from our marketplace. Git is the plain clone.
README
Maintaining the template references
The isometric and sprite-grid builders produce geometry independently of generative model availability. Tests cover neighbor edges, regions, headroom, cell coverage, guide placement, and reproducible output. The PNGs are build artifacts; the JSON manifests describe their placement. Sprite grids live with scenario-sprite-animation; a guide's upload does not establish that a generative model honors its alignment.
Publication, Scenarians only
The runtime procedure is centralized in the shared asset lifecycle, linked directly from both template skills. It covers scope, reuse, staging, publication gaps, and public-access verification. Private upload receipts stay local. The public manifests acquire asset IDs only after access is verified.
Keep template geometry and lookup tags in the owning skill's JSON manifest. Run the builder and its regression suite together when changing it; a geometry or placement-contract change gets a new version. A staged guide is not evidence that a generative model obeys it: validate the intended map or sprite-sheet workflow separately.
Skill manifest
Scenario Game Assets
Overview
Scenario's public catalog carries purpose-trained models per asset type (sprites, icons, props, tilesets, isometric scenes, pixel art, concept art) plus utilities for background removal, upscaling, and pixel cleanup. Discover by asset type, inspect the schema, generate, post-process, export. A sheet whose cells are one character's animation frames, and any walk cycle, loop, or GIF, belongs to scenario-sprite-animation. Connection and core loop: see the scenario skill. If a sibling skill named here is missing from your available skills, ask the user to install it (npx skills add scenario-labs/skills --skill <name>); unattended, proceed from tool schemas and flag the gap.
Quick reference
| Task | Call |
|---|---|
| Find a model by asset type | recommend with the asset need in the user's own words (sprite, game icon, tileset, isometric, pixel art); search is for a member known by name |
| Inspect inputs | model_schema_get (always before model_run) |
| Generate | model_run (dry_run=true prices a batch; wait=false in waves under the concurrency ceiling, relaunching 429-refused ones), then jobs_wait |
| Transparent background | recommend for a native-alpha generator first; else for background removal on the asset |
| Upscale or enhance | recommend for upscaling (2x to 16x tools exist) |
| Pixel-art cleanup | recommend for cleanup (grid snapping, palette reduction) |
| Parallax background planes | Split a painted background with the layer tool (recommend, capability: "img2img"), or generate one plane per run; see Parallax backgrounds |
| Export for an engine | asset_download with format="png" |
Worked example: a transparent potion icon set
Request: "four style-matched potion icons with transparent backgrounds for an RPG inventory."
recommendwith the user's own words asprompt, then pick bynext_step(seescenario); catalogs differ per team, so never hardcode model IDs. Settle transparency here, before anything runs: prefer a member whoserecommendentry or schema says it outputs alpha, since it makes the icons transparent at the source and skips a billed removal pass per icon; treat any other pick as opaque.model_schema_getmodel_id="model_runparameters={"prompt": "health potion, corked glass bottle, glowing red liquid, bold outline, centered, plain background"} (a plain field cuts cleanly in a removal pass; a native-alpha model wants the subject alone, never the word transparent). The schema's sample-count parameter repeats one prompt for variants of one item; different items are separate runs varying only the item ("mana potion", "stamina potion") with the wording template fixed. Price withdry_run=truefirst (one dry run prices one leg; multiply across the batch), then launch withwait=false.jobs_waitjob_ids=["- Transparency, only for an opaque pick:
recommendfor background removal (Photoroom, Pixelcut, 851 Labs at authoring time),model_schema_getthe pick, thenmodel_runwith its image field set to the generated asset id. - Optional: upscale keepers (
recommendfor upscaling,model_schema_getas always; upscalers go 2x to 8x and beyond). asset_downloadasset_id, format="png" (PNG keeps alpha). Follow redirects:curl -L -o potion.png "<url>".
Style consistency
- The cheap levers first: reuse one
seedacross the batch, and pin the model's prompt-expansion flag off where the schema has one; an expander rewrites each prompt independently and defeats a fixed template. - Reference images:
upload_assetthe art direction images, then pass the asset ids (never local paths) to the model's image or reference parameters. asset_describeextracts a reusable style synthesis (billed;dry_run: trueinsideparametersestimates). Discover withscenario_tools_search; use itspermissionto choose the executor for estimates and runs (seescenario).searchtarget="assets" images={like: ["asset_..."]} finds assets already matching the target look.- For a locked-in project style, train a custom LoRA on the project's own art (the
scenario-model-trainingskill); trained models use the same generation loop.
Preparing a variation-batch reference
Restyling one approved component into a set (button, panel, popup well, icon family) fails on the reference more often than the prompt: the model treats everything composited onto the object as the object.
- Crop to the object's own bounds. Transparent padding reads as composition: scale and offset drift every run, so sprite-measuring code gets a different box. Trim the alpha, generate, re-pad to a fixed canvas. When the model's supported aspect ratios exclude the source's, aspect also drifts per run; reconcile UI components with a nine-slice-aware rescale (stretch middle bands, keep corners and lettering undistorted) before re-padding.
- Strip baked effects before generating. A drop shadow, outer glow, or bevel in the reference reads as silhouette and comes back thickened, doubled, or fused to the object. Feed flat art and re-apply the effect in engine, where it stays adjustable. For a final PNG matching a shadowed source, lift the source's effect layer by alpha (on a transparent source the shadow is semi-transparent and the object opaque, but so is the object's antialiased rim: take the shadow as the semi-transparent pixels outside a 1 to 2 px dilation of the opaque mask, and keep the rim with the object) and composite it under each output.
- Say which parts are functional. A nine-slice panel needs stretchable middles and fixed corners; diffusion has no concept of either. Name the constraint in the prompt, then check that it held.
- One object, plain field, no scene. Several objects in one reference get recombined into a hybrid.
- Check the alpha edge on any transparent output. Removed shadows leave a semi-transparent fringe; native-alpha models can ship a large semi-transparent glow around the object. Both read as a halo on a colored UI background. Check interior alpha too: a native-alpha model can punch one item's see-through surface (empty glass) to alpha 0 while painting the next item's opaque, so the set disagrees in an engine slot.
Verify the set by measurement, not by eye: compare each output's alpha bounding box against the source before accepting the batch.
Isometric tiles and masked fills
Choose the grid before the style. A 2:1 diamond is a common game projection; a projected hex is a different footprint. Share one projection, placement anchor, light direction, and shadow side across a set. A tile's ground footprint is not its full sprite silhouette: trees and buildings rise above it and may overlap the tile behind. Never clip a tall object through the ground mask. Check a small assembled map for joins, path connections, occlusion, and visual quality before scaling the batch; a clean alpha boundary alone is not acceptance.
Use the isometric template guide to choose a neutral ground or slab reference and separate ground, side, and object regions. The JSON manifest records their geometry, filenames, and source hashes. The template builder, run by a maintainer or an agent needing local geometry, creates the PNGs and manifest. It generates geometry only, not finished game art. Keep the geometry reference separate from the approved style reference; neutral geometry must not force a palette or faceted look. Resolve uploaded assets and publish new versions through the scenario skill's shared asset lifecycle; upload local inputs only when no matching asset is accessible.
Dedicated isometric members exist in the public catalog. recommend with the user's tile need, then model_schema_get. Read the reference, mask, seed, and strength fields instead of assuming they exist or that low strength preserves geometry: conventions vary. Keep the camera and lighting wording fixed while varying the subject. Reuse a seed only when supported. If the model drifts in projection or light despite a consistent reference, use scenario-consistency, then consider scenario-model-training on approved tiles.
For a ground fill or content explicitly bounded to a shape, use masked img2img. If the specialty pick has no mask field, recommend again with capability: "img2img" and the mask need. Read mask polarity and any required base-image field from model_schema_get (scenario-image covers model-specific conventions). Template region images are white inside; adapt them to the model's convention, including alpha if required. The base canvas must match the mask dimensions and placement; an approved style reference does not replace it. When that canvas is opaque, preserve its background during the edit: asking for transparent output simultaneously contradicts the protected-canvas constraint. Extract transparency afterward. Use the ground region for terrain, and the taller object region for trees or buildings; the latter is editing space, never a final silhouette mask.
Run one tile first. Measure changes outside the edit region against the source canvas and inspect the result. A few-pixel overrun on a deliberately bounded ground fill can be clipped to its mask; extensive drift needs diagnosis of polarity, base canvas, and consumed references before retrying. For taller sprites, preserve the object silhouette with native alpha or background removal and verify the ground anchor separately. A deterministic ground mask cannot repair a wrong camera or a poor composition.
A "low-poly" look and a low-poly mesh are different deliverables. The look is a 2D style word on an image model. The mesh is scenario-3d: whether the image-to-3D pick (recommend, capability: "img23d") exposes a polycount target or a topology choice is read off model_schema_get, never assumed, and when it exposes neither a separate remesh utility (its own billed run, found with recommend, capability: "3d23d") brings the count down afterwards. Either way the concept image feeding it wants flat shading, a clean silhouette, and a plain background so the geometry reads.
Terrain tilesets and transitions
A top-down terrain set is a layout contract with the engine: every edge and corner case must exist and join its neighbors. recommend with the user's words, tile size included ("32x32 top-down tileset, grass to dirt transitions"), then model_schema_get. The pixel tileset member at authoring time had a style enum: a full tileset from one terrain prompt, a two-terrain transition mode (extraPrompt or extraImage for the second), plus single tile, variation, and object modes. Its width and height size one tile, not the sheet, and they and the mode move the price, so dry_run first. It promises a simple Wang set, not a 47-tile blob layout: map the tiles onto the engine's autotile rules, and paint a small test map with the downloaded sheet in the engine's tile editor and check the joins before more terrains.
Parallax backgrounds
A parallax background is a stack of depth planes the engine scrolls at different speeds: an opaque sky or far plane, then two or three cutout planes with transparent gaps the planes behind show through. Two routes produce the stack; writing "layers" or "parallax" into one generation prompt produces neither, it returns a single picture of stacked layers, and on a style-trained model it returns several layers painted into one image.
- Split a painted background you already have. The layer extractor is a contested lane (Scenario's own tool competes with third-party splitters), so
recommendwithcapability: "img2img"and the need in the user's words ("separate this background into depth planes for parallax"), thenmodel_schema_getthe pick. The purpose-built tool at authoring time took aseparationInstruction, a splitting rule and not a scene description ("four planes, front to back: foreground bushes, near trees, far hills, sky"), amaxLayerscap that moves the price, and an inpaint choice for filling the hole each cut leaves, also priced. It returns the cutouts plus a rebuilt background (the rearmost plane), one asset each, so review them one at a time withasset_display. A plane cut from a viewport-sized painting is only as wide as the viewport, and a scrolling plane needs bleed past the camera on both sides. Canvas-expansion tools repaint an opaque image and would fill a cutout's transparent gaps, so widen the painting before splitting it (recommendwithcapability: "img2img"and the expand need; size the painting for the largest required scroll range; all split planes inherit that width), or take the second route for the near planes. - Generate one plane per run. Fix the camera, horizon line, light direction and palette words, then vary only the plane's content: the sky and far plane as an ordinary opaque image, each nearer plane as a cutout (a native-alpha generator, or a plain field plus the removal pass above; never the word transparent at a diffusion model). Say what the plane must not contain ("no ground, no sky") so the planes do not repeat each other, and size each plane for the viewport plus its full horizontal scroll-offset range; larger offsets need more bleed, while looping requires seamless edges.
Check the stack before exporting: model_scenario-compose-image (a fixed first-party id, Scenario's single deterministic image compositor, so discovery would only re-derive it) takes the planes as layers, each with source (the asset id) and zIndex in depth order, over canvasMode: "custom" with numeric canvasWidth and canvasHeight and backgroundColor: "transparent". Use the camera viewport dimensions, preserve each plane's source size, and position the shared canvas so bleed lies outside both viewport edges. Equal-width split planes work when that bleed covers each plane's offset range. A second composite with the near planes shifted a few percent in x (a percent string) within its available bleed previews the parallax offset the engine will produce; a seam, a doubled horizon or a plane whose cutout edge shows a halo is visible here, before any engine work. Verify actual alpha, not just the thumbnail. Reject a stack with cutout seams, missing scenery, or foreground objects left in the rebuilt background: shifting the planes exposes those duplicates. Revise the separation or inpaint settings, or use the one-plane-per-run route, within the user's budget; otherwise report a failed preview instead of an engine-ready set. asset_download each accepted plane with format="png" (the far plane too, so the set shares one format), tag them with their depth order, and leave scroll speeds and looping to the engine.
Common mistakes
- Prompting "transparent background" at a diffusion model: outputs are opaque. Cut the background afterward with a removal tool, or pick a native-alpha model.
- Exporting JPG sprites: JPG has no alpha channel; keep format="png".
- Shipping AI pixel art with off-grid pixels or noisy palettes: post-process with a pixel cleanup tool (found with
recommend) for grid snapping and a strict palette. - Skipping
model_schema_get: specialty models (the pixel-art family) are txt2img-only with their own fields; generic parameters get rejected. - Asking one generation for "the layers" of a parallax background: one image comes back; depth planes are a split of a finished painting or one run per plane (see Parallax backgrounds).
- Hand-stitching tilesets: tileset members generate the edge and corner cases; texture-specific upscalers preserve tiling.
- Single-sampling lettered assets: the same recipe can render one word and fail another (dark embossed text, not the reference typography). Generate several samples per run (schema's sample-count parameter) and pin exact hex colors in the prompt when the palette drifts.
Files (skills)
-
scripts
-
build_isometric_templates.py 4.4 KB
#!/usr/bin/env python3 """Build neutral geometry references and masks; requires Pillow.""" import argparse import hashlib import json from pathlib import Path from PIL import Image, ImageChops, ImageDraw, ImageFont SIZE = 1024 ANCHOR = (512, 704) TAG = "skill:scenario-game-assets:isometric" def geometry(shape): if shape == "diamond": offsets = [(0, -192), (384, 0), (0, 192), (-384, 0)] steps = [(384, 192), (-384, 192)] elif shape == "hex": # A regular ground-plane hex projected at 30 degrees, rounded to pixels. offsets = [(-192, -166), (192, -166), (384, 0), (192, 166), (-192, 166), (-384, 0)] steps = [(576, 166), (0, 332)] else: raise ValueError(shape) return [(x + ANCHOR[0], y + ANCHOR[1]) for x, y in offsets], steps def mask(points): result = Image.new("L", (SIZE, SIZE)) ImageDraw.Draw(result).polygon(points, fill=255) return result def build(shape, thickness): points, steps = geometry(shape) ground = mask(points) whole = ground.copy() reference = Image.new("RGBA", (SIZE, SIZE)) draw = ImageDraw.Draw(reference) if thickness: for a, b in zip(points, points[1:] + points[:1]): if b[0] < a[0]: face = [a, b, (b[0], b[1] + thickness), (a[0], a[1] + thickness)] draw.polygon(face, fill=(154, 160, 166, 255)) ImageDraw.Draw(whole).polygon(face, fill=255) draw.polygon(points, fill=(215, 219, 222, 255)) envelope = whole.copy() # Sweep the footprint upward: this is editing space, not a final alpha matte. for offset in range(1, 449): ImageDraw.Draw(envelope).polygon( [(x, y - offset) for x, y in points], fill=255) return { "reference": reference, "ground-region": ground, "side-region": ImageChops.subtract(whole, ground), "object-region": envelope, }, points, steps def write_bundle(destination): destination.mkdir(parents=True, exist_ok=True) manifest = {"version": 1, "tag": TAG, "canvas": [SIZE, SIZE], "region_convention": "white=region; adapt to the model schema", "templates": []} sheet = Image.new("RGB", (1440, 960), "#f1f0ec") draw = ImageDraw.Draw(sheet) font = ImageFont.load_default(size=24) draw.text((40, 24), "ISOMETRIC BASES / v1", font=font, fill="#242d35") for index, (shape, thickness) in enumerate( [("diamond", 0), ("diamond", 32), ("hex", 0), ("hex", 32)]): name = f"{shape}-{'slab' if thickness else 'ground'}-v1" images, points, steps = build(shape, thickness) files = {} for role, image in images.items(): filename = f"{name}-{role}.png" if role == "side-region" and not image.getbbox(): filename = "empty-side-region-v1.png" image.save(destination / filename) files[role] = {"file": filename, "sha256": hashlib.sha256( (destination / filename).read_bytes()).hexdigest()} manifest["templates"].append({ "name": name, "anchor": list(ANCHOR), "ground_polygon": points, "grid_steps": steps, "thickness": thickness, "projection": "2:1 dimetric" if shape == "diamond" else "30 degree hex", "files": files, }) x, y = 40 + (index % 2) * 700, 90 + (index // 2) * 420 preview = images["reference"].resize((390, 390), Image.Resampling.LANCZOS) sheet.paste(preview, (x, y), preview) draw.text((x, y), name, font=font, fill="#242d35") draw.text((x + 400, y + 125), "Ground region", fill="#42525e", font=font) draw.text((x + 400, y + 275), "Object space", fill="#42525e", font=font) for role, dy in [("ground-region", 15), ("object-region", 165)]: small = images[role].resize((120, 120), Image.Resampling.NEAREST) sheet.paste(small, (x + 430, y + dy)) draw.text((40, 930), "Neutral geometry only. White masks mark regions; object space is not a clipping mask.", fill="#42525e", font=ImageFont.load_default(size=18)) sheet.save(destination / "contact-sheet.png") (destination / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") return manifest if __name__ == "__main__": parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("output", type=Path) write_bundle(parser.parse_args().output)
-
-
isometric-templates.json 5.2 KB
{ "version": 1, "tag": "skill:scenario-game-assets:isometric", "canvas": [1024, 1024], "region_convention": "white=region; adapt to the model schema", "templates": [ { "name": "diamond-ground-v1", "anchor": [512, 704], "ground_polygon": [ [512, 512], [896, 704], [512, 896], [128, 704] ], "grid_steps": [ [384, 192], [-384, 192] ], "thickness": 0, "projection": "2:1 dimetric", "files": { "reference": { "file": "diamond-ground-v1-reference.png", "sha256": "79d6dc0b2921756068659e3c42068d50612a7cfe713106e5e429e52afe873e05", "asset_id": "asset_N6hMk5eQjifLnVfBXHNTFaLs" }, "ground-region": { "file": "diamond-ground-v1-ground-region.png", "sha256": "50068f2b97160a220c61332d45adaa5be5847a5b95231f5c70fec9623ae304e2", "asset_id": "asset_kn14fDMRzxY4PQpJKi5xdvA6" }, "side-region": { "file": "empty-side-region-v1.png", "sha256": "c8b6bb3e837f846489438b47ae42f3960b820a17095ea66df3c5fa00fcd60665", "asset_id": "asset_dmRcoYtxibBRjoQdrnrVrtop" }, "object-region": { "file": "diamond-ground-v1-object-region.png", "sha256": "18f60fa47c914317d14284df079e2eb8500a26821348359682e4109829ea82fb", "asset_id": "asset_gftmMMh2pcMGASMpF7oxDAPM" } } }, { "name": "diamond-slab-v1", "anchor": [512, 704], "ground_polygon": [ [512, 512], [896, 704], [512, 896], [128, 704] ], "grid_steps": [ [384, 192], [-384, 192] ], "thickness": 32, "projection": "2:1 dimetric", "files": { "reference": { "file": "diamond-slab-v1-reference.png", "sha256": "75a6ef77324d9b7be6413cc393353c7c9f7eabe8aff642fbea21fc1ab1bb9abb", "asset_id": "asset_cAfnjzYzwJHodBFjQS58uYiV" }, "ground-region": { "file": "diamond-slab-v1-ground-region.png", "sha256": "50068f2b97160a220c61332d45adaa5be5847a5b95231f5c70fec9623ae304e2", "asset_id": "asset_kn14fDMRzxY4PQpJKi5xdvA6" }, "side-region": { "file": "diamond-slab-v1-side-region.png", "sha256": "b4115b8b18be7329b372f5d35999d3705b521af69ce348b307bfd1dcc2f081a4", "asset_id": "asset_L2je76tvAxtVjoNNUAkPig9x" }, "object-region": { "file": "diamond-slab-v1-object-region.png", "sha256": "2f3390b039df909c963950812a81175f9a328290c20b5e398244c443ed0dd5d2", "asset_id": "asset_QJgeUyd3R4akRotKJbCj7hHe" } } }, { "name": "hex-ground-v1", "anchor": [512, 704], "ground_polygon": [ [320, 538], [704, 538], [896, 704], [704, 870], [320, 870], [128, 704] ], "grid_steps": [ [576, 166], [0, 332] ], "thickness": 0, "projection": "30 degree hex", "files": { "reference": { "file": "hex-ground-v1-reference.png", "sha256": "426f3becc118a21574bfeec6e17b0091d01916303e6c927652fac22f7408a0d1", "asset_id": "asset_RSag6LfWcoCHggGs9PqQUrw7" }, "ground-region": { "file": "hex-ground-v1-ground-region.png", "sha256": "5e90f1946ee084873d5dc0c54ad52ee1337ecfc83365bd3a45064fbcb202b539", "asset_id": "asset_HtuA8HcpPvW2b3JqVR2Z1gWP" }, "side-region": { "file": "empty-side-region-v1.png", "sha256": "c8b6bb3e837f846489438b47ae42f3960b820a17095ea66df3c5fa00fcd60665", "asset_id": "asset_dmRcoYtxibBRjoQdrnrVrtop" }, "object-region": { "file": "hex-ground-v1-object-region.png", "sha256": "1386f492535b417685cdffd8313a1284d58378af5a4f7d4aad9333d46cfb760a", "asset_id": "asset_36vhM61E7ddRS9tZzZ6WsUGk" } } }, { "name": "hex-slab-v1", "anchor": [512, 704], "ground_polygon": [ [320, 538], [704, 538], [896, 704], [704, 870], [320, 870], [128, 704] ], "grid_steps": [ [576, 166], [0, 332] ], "thickness": 32, "projection": "30 degree hex", "files": { "reference": { "file": "hex-slab-v1-reference.png", "sha256": "80344bbab84d201b4a15c8569be0378e6bf4b82c042bf833c4764f34eb237587", "asset_id": "asset_X176ACo6FPT1s8dcYTN7FtBG" }, "ground-region": { "file": "hex-slab-v1-ground-region.png", "sha256": "5e90f1946ee084873d5dc0c54ad52ee1337ecfc83365bd3a45064fbcb202b539", "asset_id": "asset_HtuA8HcpPvW2b3JqVR2Z1gWP" }, "side-region": { "file": "hex-slab-v1-side-region.png", "sha256": "95c1d1341fb603871e5f5e37eecf49c413d432264fac08b9ded8ddbd142e3b25", "asset_id": "asset_VmAtN7QvC5GoutcsCrmaU1NE" }, "object-region": { "file": "hex-slab-v1-object-region.png", "sha256": "902266c666ffd100912f9a20dd6691092e5b9f8cd1b00d5eb2f587c8be553e17", "asset_id": "asset_iK7eBLaH6jLF9ADQ28x717gn" } } } ] } -
isometric-templates.md 3.6 KB
# Isometric templates These neutral bases separate grid geometry from art direction. Each PNG is 1024 by 1024 pixels. The placement anchor is the ground center at (512, 704), not the image center or bottom edge. | Template | Projection | Use | | ------------------- | ----------------------------------- | ------------------------------- | | `diamond-ground-v1` | 2:1 diamond | Continuous terrain and paths | | `diamond-slab-v1` | 2:1 diamond, 32 px side | Raised plots and building bases | | `hex-ground-v1` | Regular hex projected at 30 degrees | Hex terrain maps | | `hex-slab-v1` | Same hex, 32 px side | Raised hex plots | The manifest records the ground polygon, two lattice step vectors, anchor, thickness, and file hashes. Do not mix projections in one map. Slab variants are raised objects: continuous flat maps should use ground variants to avoid exposed sides between neighbors. Resolve the selected template and file roles through the shared asset lifecycle linked from SKILL.md. If none is accessible, build locally with `uv run --with pillow python scripts/build_isometric_templates.py <output-directory>` from this skill directory, then upload the required PNGs through MCP. - `reference`: neutral RGBA base. For an opaque masked fill, composite onto a chosen plain opaque canvas, preserving size and position. This is a workflow choice unless the schema requires it; do not infer editability from base-image transparency alone. - `ground-region`: white ground footprint, black outside. - `side-region`: white visible sides, black elsewhere. Flat templates share one all-black `empty-side-region-v1.png`: no side faces means no editable pixels. - `object-region`: ground and sides plus 448 px of vertical editing space. This is not an object silhouette or an alpha matte. Region files describe geometry, not model mask polarity. Inspect the source and mask pixels before the generation probe, reusing verified local files and previous checks for known uploads; download only when those are unavailable. Convert to the mask convention in the selected model's schema and use the uploaded, converted mask ID in the run. Retain an unmodified source canvas for measuring edits outside the allowed region. Never feed the labeled contact sheet as a generation reference. Flat templates keep the shared empty side mask for a uniform bundle, but need no side-edit run. Do not substitute that empty mask for the ground region. Continuous ground needs matching appearance at shared edges as well as matching geometry. Request level lighting across the ground tile, without a per-tile gradient, vignette, border, or cast shadow; reserve directional shading for raised objects. Inspect a repeated map before expanding the set: clipping fixes the footprint, not tonal seams. For a small test, generate terrain, a connecting path, and a tree using the same base and a separate style reference. Budget extra model runs, including background removal, separately from the generation probes. Assemble neighbors with the manifest's step vectors, sorted by ground-anchor Y for simple upright sprites. Check edges and path endpoints at native resolution, the tree's anchor and unobstructed crown, and the whole map at game scale. The whole crown must fit inside the object region with margin; pixels above its top edge are outside the editing area. Bridges and multi-cell objects need their own engine sorting rules. Record camera or silhouette drift before any corrective crop; reject outputs that only look correct after hiding the defect. -
README.md 1.1 KB
# Maintaining the template references The isometric and sprite-grid builders produce geometry independently of generative model availability. Tests cover neighbor edges, regions, headroom, cell coverage, guide placement, and reproducible output. The PNGs are build artifacts; the JSON manifests describe their placement. Sprite grids live with `scenario-sprite-animation`; a guide's upload does not establish that a generative model honors its alignment. ## Publication, Scenarians only The runtime procedure is centralized in [the shared asset lifecycle](../scenario/references/shared-assets.md), linked directly from both template skills. It covers scope, reuse, staging, publication gaps, and public-access verification. Private upload receipts stay local. The public manifests acquire asset IDs only after access is verified. Keep template geometry and lookup tags in the owning skill's JSON manifest. Run the builder and its regression suite together when changing it; a geometry or placement-contract change gets a new version. A staged guide is not evidence that a generative model obeys it: validate the intended map or sprite-sheet workflow separately. -
SKILL.md 17.3 KB
--- name: scenario-game-assets description: "Use when creating game art through Scenario MCP: sprites, sprite sheets, icons, props, loot, tilesets and autotile terrain transitions, seamless tiles, isometric buildings, top-down maps, pixel art, UI buttons and panels, parallax background layers or depth planes for side-scrollers, and character or concept art; or when assets need transparent backgrounds, background removal, style-consistent variation batches, upscaling, pixel-grid cleanup, or PNG export for Unity, Godot, or Unreal." license: MIT --- # Scenario Game Assets ## Overview Scenario's public catalog carries purpose-trained models per asset type (sprites, icons, props, tilesets, isometric scenes, pixel art, concept art) plus utilities for background removal, upscaling, and pixel cleanup. Discover by asset type, inspect the schema, generate, post-process, export. A sheet whose cells are one character's animation frames, and any walk cycle, loop, or GIF, belongs to `scenario-sprite-animation`. Connection and core loop: see the `scenario` skill. If a sibling skill named here is missing from your available skills, ask the user to install it (`npx skills add scenario-labs/skills --skill <name>`); unattended, proceed from tool schemas and flag the gap. ## Quick reference | Task | Call | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | Find a model by asset type | `recommend` with the asset need in the user's own words (sprite, game icon, tileset, isometric, pixel art); `search` is for a member known by name | | Inspect inputs | `model_schema_get` (always before `model_run`) | | Generate | `model_run` (`dry_run=true` prices a batch; `wait=false` in waves under the concurrency ceiling, relaunching 429-refused ones), then `jobs_wait` | | Transparent background | `recommend` for a native-alpha generator first; else for background removal on the asset | | Upscale or enhance | `recommend` for upscaling (2x to 16x tools exist) | | Pixel-art cleanup | `recommend` for cleanup (grid snapping, palette reduction) | | Parallax background planes | Split a painted background with the layer tool (`recommend`, `capability: "img2img"`), or generate one plane per run; see Parallax backgrounds | | Export for an engine | `asset_download` with format="png" | ## Worked example: a transparent potion icon set Request: "four style-matched potion icons with transparent backgrounds for an RPG inventory." 1. `recommend` with the user's own words as `prompt`, then pick by `next_step` (see `scenario`); catalogs differ per team, so never hardcode model IDs. Settle transparency here, before anything runs: prefer a member whose `recommend` entry or schema says it outputs alpha, since it makes the icons transparent at the source and skips a billed removal pass per icon; treat any other pick as opaque. 2. `model_schema_get` model_id="<picked id>". Note the prompt field, size fields, and any sample-count parameter. 3. `model_run` parameters={"prompt": "health potion, corked glass bottle, glowing red liquid, bold outline, centered, plain background"} (a plain field cuts cleanly in a removal pass; a native-alpha model wants the subject alone, never the word transparent). The schema's sample-count parameter repeats one prompt for variants of one item; different items are separate runs varying only the item ("mana potion", "stamina potion") with the wording template fixed. Price with `dry_run=true` first (one dry run prices one leg; multiply across the batch), then launch with `wait=false`. 4. `jobs_wait` job_ids=["<returned job_id>"] (up to 32 ids, one call covers the batch); on timeout re-call with the returned `pending_job_ids`, never a second `model_run`. Then `asset_display` to review, one `asset_id` per call. 5. Transparency, only for an opaque pick: `recommend` for background removal (Photoroom, Pixelcut, 851 Labs at authoring time), `model_schema_get` the pick, then `model_run` with its image field set to the generated asset id. 6. Optional: upscale keepers (`recommend` for upscaling, `model_schema_get` as always; upscalers go 2x to 8x and beyond). 7. `asset_download` asset_id, format="png" (PNG keeps alpha). Follow redirects: `curl -L -o potion.png "<url>"`. ## Style consistency - The cheap levers first: reuse one `seed` across the batch, and pin the model's prompt-expansion flag off where the schema has one; an expander rewrites each prompt independently and defeats a fixed template. - Reference images: `upload_asset` the art direction images, then pass the asset ids (never local paths) to the model's image or reference parameters. - `asset_describe` extracts a reusable style synthesis (billed; `dry_run: true` inside `parameters` estimates). Discover with `scenario_tools_search`; use its `permission` to choose the executor for estimates and runs (see `scenario`). - `search` target="assets" images={like: ["asset_..."]} finds assets already matching the target look. - For a locked-in project style, train a custom LoRA on the project's own art (the `scenario-model-training` skill); trained models use the same generation loop. ## Preparing a variation-batch reference Restyling one approved component into a set (button, panel, popup well, icon family) fails on the reference more often than the prompt: the model treats everything composited onto the object as the object. - **Crop to the object's own bounds.** Transparent padding reads as composition: scale and offset drift every run, so sprite-measuring code gets a different box. Trim the alpha, generate, re-pad to a fixed canvas. When the model's supported aspect ratios exclude the source's, aspect also drifts per run; reconcile UI components with a nine-slice-aware rescale (stretch middle bands, keep corners and lettering undistorted) before re-padding. - **Strip baked effects before generating.** A drop shadow, outer glow, or bevel in the reference reads as silhouette and comes back thickened, doubled, or fused to the object. Feed flat art and re-apply the effect in engine, where it stays adjustable. For a final PNG matching a shadowed source, lift the source's effect layer by alpha (on a transparent source the shadow is semi-transparent and the object opaque, but so is the object's antialiased rim: take the shadow as the semi-transparent pixels outside a 1 to 2 px dilation of the opaque mask, and keep the rim with the object) and composite it under each output. - **Say which parts are functional.** A nine-slice panel needs stretchable middles and fixed corners; diffusion has no concept of either. Name the constraint in the prompt, then check that it held. - **One object, plain field, no scene.** Several objects in one reference get recombined into a hybrid. - **Check the alpha edge on any transparent output.** Removed shadows leave a semi-transparent fringe; native-alpha models can ship a large semi-transparent glow around the object. Both read as a halo on a colored UI background. Check interior alpha too: a native-alpha model can punch one item's see-through surface (empty glass) to alpha 0 while painting the next item's opaque, so the set disagrees in an engine slot. Verify the set by measurement, not by eye: compare each output's alpha bounding box against the source before accepting the batch. ## Isometric tiles and masked fills Choose the grid before the style. A 2:1 diamond is a common game projection; a projected hex is a different footprint. Share one projection, placement anchor, light direction, and shadow side across a set. A tile's ground footprint is not its full sprite silhouette: trees and buildings rise above it and may overlap the tile behind. Never clip a tall object through the ground mask. Check a small assembled map for joins, path connections, occlusion, and visual quality before scaling the batch; a clean alpha boundary alone is not acceptance. Use the [isometric template guide](isometric-templates.md) to choose a neutral ground or slab reference and separate ground, side, and object regions. The [JSON manifest](isometric-templates.json) records their geometry, filenames, and source hashes. The [template builder](scripts/build_isometric_templates.py), run by a maintainer or an agent needing local geometry, creates the PNGs and manifest. It generates geometry only, not finished game art. Keep the geometry reference separate from the approved style reference; neutral geometry must not force a palette or faceted look. Resolve uploaded assets and publish new versions through the `scenario` skill's [shared asset lifecycle](../scenario/references/shared-assets.md); upload local inputs only when no matching asset is accessible. Dedicated isometric members exist in the public catalog. `recommend` with the user's tile need, then `model_schema_get`. Read the reference, mask, seed, and strength fields instead of assuming they exist or that low strength preserves geometry: conventions vary. Keep the camera and lighting wording fixed while varying the subject. Reuse a seed only when supported. If the model drifts in projection or light despite a consistent reference, use `scenario-consistency`, then consider `scenario-model-training` on approved tiles. For a ground fill or content explicitly bounded to a shape, use masked `img2img`. If the specialty pick has no mask field, `recommend` again with `capability: "img2img"` and the mask need. Read mask polarity and any required base-image field from `model_schema_get` (`scenario-image` covers model-specific conventions). Template region images are white inside; adapt them to the model's convention, including alpha if required. The base canvas must match the mask dimensions and placement; an approved style reference does not replace it. When that canvas is opaque, preserve its background during the edit: asking for transparent output simultaneously contradicts the protected-canvas constraint. Extract transparency afterward. Use the ground region for terrain, and the taller object region for trees or buildings; the latter is editing space, never a final silhouette mask. Run one tile first. Measure changes outside the edit region against the source canvas and inspect the result. A few-pixel overrun on a deliberately bounded ground fill can be clipped to its mask; extensive drift needs diagnosis of polarity, base canvas, and consumed references before retrying. For taller sprites, preserve the object silhouette with native alpha or background removal and verify the ground anchor separately. A deterministic ground mask cannot repair a wrong camera or a poor composition. A "low-poly" look and a low-poly mesh are different deliverables. The look is a 2D style word on an image model. The mesh is `scenario-3d`: whether the image-to-3D pick (`recommend`, `capability: "img23d"`) exposes a polycount target or a topology choice is read off `model_schema_get`, never assumed, and when it exposes neither a separate remesh utility (its own billed run, found with `recommend`, `capability: "3d23d"`) brings the count down afterwards. Either way the concept image feeding it wants flat shading, a clean silhouette, and a plain background so the geometry reads. ## Terrain tilesets and transitions A top-down terrain set is a layout contract with the engine: every edge and corner case must exist and join its neighbors. `recommend` with the user's words, tile size included ("32x32 top-down tileset, grass to dirt transitions"), then `model_schema_get`. The pixel tileset member at authoring time had a `style` enum: a full tileset from one terrain prompt, a two-terrain transition mode (`extraPrompt` or `extraImage` for the second), plus single tile, variation, and object modes. Its `width` and `height` size one tile, not the sheet, and they and the mode move the price, so `dry_run` first. It promises a simple Wang set, not a 47-tile blob layout: map the tiles onto the engine's autotile rules, and paint a small test map with the downloaded sheet in the engine's tile editor and check the joins before more terrains. ## Parallax backgrounds A parallax background is a stack of depth planes the engine scrolls at different speeds: an opaque sky or far plane, then two or three cutout planes with transparent gaps the planes behind show through. Two routes produce the stack; writing "layers" or "parallax" into one generation prompt produces neither, it returns a single picture of stacked layers, and on a style-trained model it returns several layers painted into one image. - **Split a painted background you already have.** The layer extractor is a contested lane (Scenario's own tool competes with third-party splitters), so `recommend` with `capability: "img2img"` and the need in the user's words ("separate this background into depth planes for parallax"), then `model_schema_get` the pick. The purpose-built tool at authoring time took a `separationInstruction`, a splitting rule and not a scene description ("four planes, front to back: foreground bushes, near trees, far hills, sky"), a `maxLayers` cap that moves the price, and an inpaint choice for filling the hole each cut leaves, also priced. It returns the cutouts plus a rebuilt background (the rearmost plane), one asset each, so review them one at a time with `asset_display`. A plane cut from a viewport-sized painting is only as wide as the viewport, and a scrolling plane needs bleed past the camera on both sides. Canvas-expansion tools repaint an opaque image and would fill a cutout's transparent gaps, so widen the painting before splitting it (`recommend` with `capability: "img2img"` and the expand need; size the painting for the largest required scroll range; all split planes inherit that width), or take the second route for the near planes. - **Generate one plane per run.** Fix the camera, horizon line, light direction and palette words, then vary only the plane's content: the sky and far plane as an ordinary opaque image, each nearer plane as a cutout (a native-alpha generator, or a plain field plus the removal pass above; never the word transparent at a diffusion model). Say what the plane must not contain ("no ground, no sky") so the planes do not repeat each other, and size each plane for the viewport plus its full horizontal scroll-offset range; larger offsets need more bleed, while looping requires seamless edges. Check the stack before exporting: `model_scenario-compose-image` (a fixed first-party id, Scenario's single deterministic image compositor, so discovery would only re-derive it) takes the planes as `layers`, each with `source` (the asset id) and `zIndex` in depth order, over `canvasMode: "custom"` with numeric `canvasWidth` and `canvasHeight` and `backgroundColor: "transparent"`. Use the camera viewport dimensions, preserve each plane's source size, and position the shared canvas so bleed lies outside both viewport edges. Equal-width split planes work when that bleed covers each plane's offset range. A second composite with the near planes shifted a few percent in `x` (a percent string) within its available bleed previews the parallax offset the engine will produce; a seam, a doubled horizon or a plane whose cutout edge shows a halo is visible here, before any engine work. Verify actual alpha, not just the thumbnail. Reject a stack with cutout seams, missing scenery, or foreground objects left in the rebuilt background: shifting the planes exposes those duplicates. Revise the separation or inpaint settings, or use the one-plane-per-run route, within the user's budget; otherwise report a failed preview instead of an engine-ready set. `asset_download` each accepted plane with `format="png"` (the far plane too, so the set shares one format), tag them with their depth order, and leave scroll speeds and looping to the engine. ## Common mistakes - Prompting "transparent background" at a diffusion model: outputs are opaque. Cut the background afterward with a removal tool, or pick a native-alpha model. - Exporting JPG sprites: JPG has no alpha channel; keep format="png". - Shipping AI pixel art with off-grid pixels or noisy palettes: post-process with a pixel cleanup tool (found with `recommend`) for grid snapping and a strict palette. - Skipping `model_schema_get`: specialty models (the pixel-art family) are txt2img-only with their own fields; generic parameters get rejected. - Asking one generation for "the layers" of a parallax background: one image comes back; depth planes are a split of a finished painting or one run per plane (see Parallax backgrounds). - Hand-stitching tilesets: tileset members generate the edge and corner cases; texture-specific upscalers preserve tiling. - Single-sampling lettered assets: the same recipe can render one word and fail another (dark embossed text, not the reference typography). Generate several samples per run (schema's sample-count parameter) and pin exact hex colors in the prompt when the palette drifts.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.