Unity pixel art
Claude Agent Skills for building casual iOS games in Unity 6 — orchestration, MCP Editor control, generative 2D/3D/audio assets, graphics, UI, monetization, QA & release.
npx -y skills add tea-x-random/unity-game-skills --skill unity-pixel-artAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Generate production-ready PIXEL-NATIVE 2D game sprites, sprite sheets, tilesets, icons, rotations, and animation frames for Unity using PixelLab for final art, with Gemini only for exploratory concepts/style boards. Use when the requested game art is pixel art, low-res sprite art, tilemaps, top-down/side-view 2D sprites, directional character sheets, animation strips, or any final 2D pixel asset. Enforces golden-anchor-first production at the GAME level (bitforge conditioning on approved goldens), a master-palette color_image lock on every call, art-spec-driven generation, tile-derived canvas sizes, transparent backgrounds, Unity point-filter import, pixel-perfect camera compatibility, alpha/palette/sheet QA plus a deterministic frame-vs-anchor diff gate, and explicitly forbids Tripo/3D-render downscaling as the source of pixel art.
SKILL.md
24.4 KB, as published. Nobody here has run it
Unity Pixel Art
Create pixel-native game assets for Unity. Use Gemini for exploration only; use PixelLab for final pixels.
Routing rule
- Gemini → exploration: mood boards, rough silhouettes, palette exploration, UI/style-guide thumbnails, non-final reference sheets.
- PixelLab → final pixel art: characters, props, tilesets, icons, directional sheets, animation frames, variants, and recolors.
- Do not make pixel art by rendering Tripo/3D and downscaling it. 3D pre-render is acceptable for high-res/painterly 2D, but pixel art must be authored/generatively produced at the target pixel canvas so silhouettes, clusters, outlines, and palette choices are deliberate.
If another skill routes a pixel-art asset to unity-image-generator or unity-3d-generator, override it here: Gemini may provide concept references; PixelLab produces the approved source sprites.
Required references
Read only the branch you need:
references/pixellab-api.md— PixelLab models, script usage, endpoint/SDK notes.references/pixel-import.md— Unity import settings, slicing, and pixel-perfect camera snippets.
Anchor-first production workflow
Golden-anchor-first at the GAME level (hard rules):
- Only the game golden (
art-spec conditioning.golden_assets.game) — or an explicitly approved golden reroll — may be generated text-only withpixflux. Every subsequent character/prop SUBJECT MUST bebitforgeconditioned on the appropriate golden anchor via--init-image. - Conditioning channel (verified live 2026-07-01): bitforge's
style_image/--style-strengthproduces structured noise at every strength on the live API — do not use it. The working channel is--init-image+--init-image-strength(1–999): the golden is a structural/style init and the description re-subjects it. Calibration: cross-subject derivation ~75–150 (110 is the script's autofill default; 175+ bleeds the anchor's identity — helmet/outfit/colors leaking into the new subject); same-asset variants/recolors 250–400. QA every cross-subject result for anchor-subject bleed. - Family model (aligns with unity-art-direction): the game golden seeds each family golden (
conditioning.golden_assets.<family>); family members condition on their family golden. Pass--family <name>and the script auto-fills--init-imagefrom the spec (bitforge only; the reference is auto crop/padded — never resampled — to the target canvas, which the API requires to match). - Canvas class per subject:
craft.char_tilesis the STANDARD character footprint; short subjects (grunts, critters) use their own smaller footprint (e.g. 1×1 tile) — a short subject forced onto a tall canvas comes back as stacked duplicates. - Field assets (tiles/textures) do not init on a subject golden — a character init forces object-ness onto what should be a flat field. Tiles/textures: palette lock + prompt tokens carry coherence,
--outline lineless --shading "flat shading", description says "edge-to-edge full bleed, no border, no outline, no frame, no objects". The FIRST approved tile becomes the terrain family golden; later tiles may init on IT (same asset class) at moderate strength. Best-of-N and keep the besttile.edge_wrapscore — seams vary strongly between rolls. - Large-canvas escape hatch: for approved 128+ boss/set-piece canvases,
pixfluxwith the master palette + locked outline/shading enums + the frozenidentity_string, then vision-QA the result against the game golden.
- Resolve the art contract. Every production call runs under
art-spec.yaml— the script resolves it (--art-spec,$UNITY_ART_SPEC, or probedAssets/*/Art/_ArtDirection/art-spec.yaml+ legacy roots) and auto-fills the master palette, golden anchor, outline enum, tile-derived canvas (--canvas tile|character), and PPU provenance. Production calls without a resolvable spec FAIL; spec-less exploration requires an explicit--no-art-spec. - Explore cheaply with Gemini if needed. Generate concept boards or silhouette sheets only. Do not approve Gemini pixels as final pixel-art assets.
- Generate the anchor sprite in PixelLab. Game golden →
pixflux; everything else →bitforgeon its golden (rules above). Use a tile-derived native canvas, transparent/no-background output, and the master palette ascolor_image. This anchor becomes the only source of truth for the asset. - QA the anchor before variants. Run alpha/padding/palette/silhouette checks with
unity-image-generator/scripts/validate_sprite.py. For goldens and canon sprites a VISION check is MANDATORY, not optional — the deterministic gates cannot see subject errors: a duplicated/stacked subject (a tall canvas often comes back as a big+small character or stacked duplicates) passes every alpha/palette check. Verify single subject, full body visible, correct subject, no anchor-subject bleed — viacritique_image.pyor direct image inspection, BEFORE approving. Prompt-side prevention: phrase goldens as "exactly one <subject>, full body, head to feet visible, single character only, no duplicates" and pass--coverage-percentage 80on character canvases. Recurring characters — register the pixel canon now: the pixel-track canon sheet is the approved anchor plus itsrotate-derived directional views composited into one PNG atAssets/<Game>/Art/_ArtDirection/sheets/<char_id>_canon.png(a 3-line PIL paste of the anchor + rotations side by side; for a single-view game the approved anchor alone, copied to the canon path, is the blessed canon). Never use a Gemini turnaround as pixel canon. Registercharacters.<id>.{canon_sheet,anchor_sprite,identity_string}in the art-spec before generating any derived asset. - Derive frames/rotations/variants from the anchor. Keep one base sprite driving directions, walk/idle/attack strips, damage states, and palette variants:
- Directions →
rotate(set--from-direction/--to-direction). - Animation → the skeleton workflow (
estimate-skeleton→ author per-frame poses →animate-skeleton). This is structurally consistent across frames.animate-textis the drift-prone fallback; never author each frame independently. - Recolors / damage states / edits → BitForge conditioned on the anchor (
--init-imagewith--init-image-strength 250–400), orinpaintfor local fixes. - Derived frames/rotations use the anchor's extracted sub-palette (a subset of the master — never the full game palette, which permits cross-asset color borrowing; never new colors). Emit the swatch from the approved anchor (
compare_frames_to_anchor.py --anchor <anchor.png> --emit-subpalette <id>_subpalette.png) and pass it via--color-image; omitting--color-imagefalls back to the full master palette, which this rule forbids for derived frames.
- Directions →
- Gate frames with
scripts/compare_frames_to_anchor.py(palette-membership + baseline/bbox + loose silhouette-IoU vs the anchor) before slicing. Manual eyeballing does not replace it. - Repack sheets with padding/extrusion. Use
extrude_atlas.pybefore Unity slicing so point filtering and atlas packing do not bleed neighboring cells. - Import with pixel settings. Point filter, no compression, no mipmaps, PPU from
art-spec craft.pixels_per_unit, multiple-sprite slicing for sheets, SpriteAtlas family grouping, and Pixel Perfect Camera settings. - Promote only approved assets. The final artifact must include the sprite PNG/sheet, QA reports, manifest, Unity import contract, prefab, and BeautyCell screenshot before it enters the approved registry.
Canvas and style rules
- Derive canvases from the spec's tiles (
craft.tile_size×craft.char_tiles), never ad hoc; sanity-band by readability: icons16–32, pickups/props32–64, characters32–128, bosses/large set pieces128+only when the camera supports it. - Generate at the final pixel canvas, not a high-res illustration to shrink later.
- State the exact view using a real
CameraViewenum:side,low top-down, orhigh top-down(there is no plaintop-down/3/4/isometricview — isometric is the--isometricflag). Pickdirectionfrom the 8-way compass set (south,south-east,east, …). State pivot/baseline. - Any PixelLab call without the palette lock is invalid. Every call carries a
color_image: the game'smaster-palette.pngfor anchors/goldens, the anchor's extracted sub-palette for derived frames/rotations.--palettehex lists are for exploration only. - Keep animation frame counts small and game-readable: idle
4–6, walk/run6–8, attack6–10, hit2–4, death6–10. - Prefer one row per clip (
idle,walk,attack) or a documented grid (rows=clips,cols=frames) with a sidecar manifest.
PixelLab helper script
Use the bundled script for final generation. It resolves keys as --api-key → PIXEL_LABS_API_KEY, and the art-spec as --art-spec → $UNITY_ART_SPEC → probed project paths. With a resolved spec it auto-attaches the master palette (conditioning.master_palette_png → color_image), the golden anchor (conditioning.golden_assets.<--family|game> → --init-image, bitforge only, default strength 110), the outline enum, and the per-game default view/shading enums (craft.view / craft.shading → --view/--shading; explicit CLI flags win); production calls without a spec (or without a palette lock) fail. Exploration only: add --no-art-spec.
Game golden — the ONLY text-only roll (everything after conditions on a golden):
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py pixflux \
--description "pixel art knight hero, side view, single color black outline, transparent background" \
--canvas character --no-background --view side --direction east \
--output "Assets/<Game>/Art/Source/SourceImages/hero_knight_golden.png" \
--manifest "Assets/<Game>/Art/Source/SourceImages/hero_knight_golden.pixellab.json"
Every subsequent subject — bitforge init-conditioned on its golden. --init-image-strength is a 1–999 scale. NEW subjects from a golden ~75–150 (autofill default 110; higher bleeds the anchor's identity into the new subject); same-asset variants/recolors 250–400. (--style-image/--style-strength are traps on the live API — noise at every strength.)
# new family member: --family picks conditioning.golden_assets.<family> as --init-image
# (short subject -> its own footprint, not the standard char canvas)
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py bitforge \
--description "pixel art goblin grunt, single small goblin, same game style, side view, transparent background" \
--width 32 --height 32 --family enemies \
--no-background --view side --direction east \
--output "Assets/<Game>/Art/Source/SourceImages/goblin_grunt.png" \
--manifest "Assets/<Game>/Art/Source/SourceImages/goblin_grunt.pixellab.json"
# same-asset variant: condition on the asset's own anchor, sub-palette lock
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py bitforge \
--description "same coin, cracked damaged variant, same palette, same silhouette center" \
--width 32 --height 32 \
--init-image "Assets/<Game>/Art/Approved/coin/coin.png" --init-image-strength 320 \
--color-image "Assets/<Game>/Art/Approved/coin/coin_subpalette.png" \
--no-background \
--output "Assets/<Game>/Art/Source/SourceImages/coin_damaged.png"
# field asset (tile/texture): NO subject init — palette + tokens + lineless/flat
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py pixflux \
--description "pixel art grass ground texture, flat field viewed from directly above, edge-to-edge full bleed, no border, no outline, no objects, seamless tileable" \
--canvas tile --view "high top-down" --outline lineless --shading "flat shading" \
--output "Assets/<Game>/Art/Source/SourceImages/grass_tile.png"
Animation: skeleton-first (structural consistency)
Image models drift frame-to-frame. For a character that must keep its identity across a walk/idle/attack cycle, drive frames with a posed skeleton, not text. Reuse ONE keypoint template per humanoid archetype (art-spec characters.<id>.skeleton_template, e.g. _ArtDirection/sheets/biped_48.skeleton.json) so the whole cast shares proportions:
# 1. rest-pose skeleton from the approved anchor -> editable JSON template
# (save/reuse it as the game's biped template for every humanoid)
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py estimate-skeleton \
--image "Assets/<Game>/Art/Approved/hero_knight/hero_knight.png" \
--output "Assets/<Game>/Art/_ArtDirection/sheets/biped_48.skeleton.json"
# 2. author per-frame poses in knight_walk_frames.json (a list of frames —
# each either a bare keypoint list [{"x","y","label","z_index"},...] or
# {"keypoints":[...]}; the script normalizes both), then:
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py animate-skeleton \
--skeleton-json knight_walk_frames.json \
--view side --direction east \
--reference-image "Assets/<Game>/Art/Approved/hero_knight/hero_knight.png" \
--color-image "Assets/<Game>/Art/Approved/hero_knight/hero_knight_subpalette.png" \
--output "Assets/<Game>/Art/Source/SourceImages/knight_walk.png" # -> _00.png… + _strip.png
Attack/action animation doctrine (field-verified 2026-07-01):
- Animate the FRONT limb — and mirror correctly. Keypoint LEFT/RIGHT labels are the CHARACTER's anatomical sides (COCO convention), not the viewer's: facing east, the character's LEFT limbs are the camera-front/travel side (rest-pose x tells you — the front limb has the larger x when facing east). A slash authored on the back arm plays "correctly" and reads as NOTHING. Check rest-pose x before authoring. Deriving a mirrored facing = negate x AND swap every LEFT/RIGHT label pair (shoulder/elbow/arm, hip/knee/leg, eye/ear) — mirroring x alone silently attacks with the back arm. Pre-flight: the
--view/--directionpassed to animate-skeleton MUST equal the reference anchor's stored facing (asset-contract) — a mismatched skeleton degrades pose adherence without erroring. - Use ABSOLUTE extreme poses, not small offsets — phase grammar, not evenly-spaced swing positions. ±0.05 normalized offsets read as nothing at 32–64px. Frames are DISTINCT phases (Dead Cells: keys first, "never in-between"): 4-frame attack = windup (wrist pulled back OPPOSITE the strike, torso counter-twisted) → strike-smear → follow-through (forward-down) → recover; 3-frame player attack DROPS the windup (strike/follow/recover — player anticipation reads as input lag; enemies instead HOLD the windup ≥0.3s in clip timing). Magnitudes at a 64px canvas: striking wrist travels ≥0.25 normalized (≥16px, ~90–180° about the shoulder) between windup and strike, ending PAST anatomical reach (~0.95–1.0 forward extent, elbow ~0.85 — elongation/exaggeration is doctrine); NOSE/NECK lean +0.04–0.07; both HIPs shift 0.02–0.03; front knee lunges. The whole SILHOUETTE must change — at this size the arm alone is 2px wide. On the strike frame prompt for the smear ("angular motion smear on the weapon arc") and accept detail loss — legibility of movement beats detail parity. Offset all keypoints ~20–25% toward the trailing canvas edge so the strike side has empty space for the model to draw effect pixels (PixelLab's own attack guidance); compensate at the import pivot. Set fixed-head OFF for attack strips (the head must lean); ON for idle/walk. Wrist travel IS the weapon-arc control — the 18-keypoint schema has no weapon joint.
- Poses vs timing are separate artifacts. PixelLab owns POSES; Unity clip keyframe times own TIMING (attack clips are non-uniform — strike 50–70ms, holds carry the weight; table in unity-animation). Never regenerate a strip because it "feels slow/fast" — that is a free keyframe edit. Near-identical frames from a correct-looking pose set is a KNOWN pose-conditioned-diffusion failure mode (regression to the reference), which is why the motion gate is permanent; pre-flight the keypoints (wrist delta ≥0.25 normalized) before spending credits, and repair failing frames with
--init-imagesfreeze, not rerolls. - The model animates the BODY, not the weapon. Pose-conditioned generation will not draw a blade arc — a hip-level sword stays a hip-level detail. Weapon attacks REQUIRE a separate slash/impact VFX overlay sprite: generate it standalone (
pixflux,--outline lineless --shading "flat shading" --no-background, "curved crescent slash trail, motion effect only, no character"), contract it as rolevfx(free-sized, exempt from tile-multiple), and code-animate the overlay (0.15s scale 0.7→1.15 + rotate + fade) in front of the character, plus ~0.05s hitstop on contact. The overlay carries the read; the body strip supports it (gate the strip at--min-inter-frame-motion 0.25when paired with an overlay, 0.35 solo). - PixelLab canvases carry transparent padding rows below the feet (3-11px measured) — the FEET, not the canvas bottom, are the baseline; import pivots must be alpha-derived (see unity-asset-pipeline).
Canvas defaults to the reference image; identity/pose adherence is governed by the live API's single guidance_scale (server default 4.0), tunable via --guidance-scale. Live-endpoint constraints (all auto-handled by the script, 2026-07-01): the whole command runs over raw HTTP — SDK 1.0.5 is incompatible (it nulls absent inpainting_images/mask_images, which the API rejects, and can't send guidance_scale); the canvas must be square 16/32/64/128/256 (non-square characters are padded baseline-preserving and frames cropped back); each call takes an exact pose count set by the canvas (e.g. 3 at 64×64 — longer clips are batched automatically); z_index must be an integer (estimate-skeleton emits fractional ones; the script rounds). Use rotate to spin the base/keyframes into other directions. Fix single bad frames instead of re-rolling the strip: inpaint (white mask) on that frame, or re-run animate-skeleton with --init-images freezing the approved frames. animate-text exists as a fallback when no skeleton can be authored, but it drifts more. Gate every strip with compare_frames_to_anchor.py before slicing.
Before a paid batch, check credits and use --dry-run to verify dimensions, prompt, and conditioning inputs:
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py balance
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/generate_pixel_art.py --dry-run bitforge --description "..." --canvas character --family enemies --output /tmp/preview.png
(--dry-run obeys the same art-spec gate, so it previews the exact production payload; spec-less exploration adds --no-art-spec there too.)
QA gates
Run after every final PixelLab output:
python3 $UNITY_GAME_SKILLS_HOME/unity-image-generator/scripts/validate_sprite.py \
"Assets/<Game>/Art/Source/SourceImages/coin_anchor.png" \
--art-spec "Assets/<Game>/Art/_ArtDirection/art-spec.yaml" \
--require-alpha --min-padding 1 --max-width 128 --max-height 128 \
--json-report "Assets/<Game>/Art/Source/QA/coin_anchor.sprite-qa.json"
(Tile-aligned characters whose baseline sits on the bottom canvas edge use --min-padding 0 — per-sprite edge padding is an atlas concern and is added by extrude_atlas.py at repack, not at generation. With an art-spec, exact palette membership is checked against the master-palette.png swatch pixels — the same artifact generation conditioned on — not the spec hex lists.)
MANDATORY for every animation strip / rotation set — deterministic frame-vs-anchor identity diff (palette membership, baseline/bbox drift, loose silhouette-IoU identity-swap floor). Add --action for attack/hit/death clips — it additionally requires visible inter-frame MOTION (≥0.35 pixel-change between at least one frame pair; calibrated live: a slash that read as broken in-game measured 0.27, a real run cycle 0.78). Identity gates alone pass near-identical standing poses that play "correctly" in the Animator yet read as nothing. Two additional required checks for attack strips: the LARGEST inter-frame delta must land on the windup→strike pair (a strip peaking on the recover frame is mis-authored — pass --strike-pair), and the silhouette (alpha-mask) must change between frames — raw pixel change passes recolor/AA shimmer that reads as nothing. Exit 1 = do not slice; repair the failing frame (inpaint / --init-images freeze) instead of re-rolling the strip:
python3 $UNITY_GAME_SKILLS_HOME/unity-pixel-art/scripts/compare_frames_to_anchor.py \
--anchor "Assets/<Game>/Art/Approved/hero_knight/hero_knight.png" \
--strip "Assets/<Game>/Art/Source/SourceImages/knight_walk_strip.png" --cols 8 \
--json-report "Assets/<Game>/Art/Source/QA/knight_walk.frame-diff.json"
For tilesets:
python3 $UNITY_GAME_SKILLS_HOME/unity-image-generator/scripts/validate_sprite.py \
"Assets/<Game>/Art/Source/SourceImages/grass_tile.png" \
--art-spec "Assets/<Game>/Art/_ArtDirection/art-spec.yaml" \
--tile --square --power-of-two --expected-finish flat \
--json-report "Assets/<Game>/Art/Source/QA/grass_tile.sprite-qa.json"
Gate the wrap axes by the tile's ROLE: --wrap-axes horizontal for a side-scroller ground band (repeats only left-right; the top edge is the visible surface), --wrap-axes both (default) for open tilemap ground. Seams vary strongly between rolls — best-of-N and keep the best wrap score.
Reject pixel assets for:
- any PixelLab call made without the palette lock (
color_image— master palette, or anchor sub-palette for derived frames); - a production SUBJECT generated without init-conditioning on its golden anchor (pixflux is legal only for the game golden, family-golden seeds for asset classes that can't derive from a subject anchor — e.g. the first terrain tile — and the large-canvas escape hatch);
- any bitforge call using
--style-image/--style-strengthas the conditioning channel (live API returns noise — use--init-image); - generated at the wrong native canvas (canvas not derived from
craft.tile_size/char_tiles); - anti-aliased/high-res painted edges that are not pixel-native;
- palette drift outside the approved palette;
- inconsistent baseline/pivot across frames (
compare_frames_to_anchor.pyexit 1); - loose alpha padding or non-transparent corners;
- animation frames that change identity, outfit, proportions, or weapon placement — including cross-subject anchor-subject bleed;
- imported with bilinear filtering, compression, mipmaps, or wrong PPU.
Unity import summary
Use references/pixel-import.md for snippets. Minimum settings:
TextureImporterType.SpriteFilterMode.PointTextureImporterCompression.UncompressedmipmapEnabled = falseSpriteImportMode.Multiplefor sheetsspritePixelsPerUnit=art-spec craft.pixels_per_unit(project PPU SSOT; contractruntime.pixels_per_unitis the no-spec fallback)- Pixel Perfect Camera (
com.unity.2d.pixel-perfect) when the game camera renders pixel art.
Handoff to other skills
unity-image-generator: concept boards, non-pixel static art, UI mockups, vision critique, sprite validators.unity-animation: Animator setup, frame timing, events, slicing; source frames come from this skill for pixel art.unity-asset-pipeline: asset contract, prefab factory, registry, BeautyCell gate.unity-scene-composition: screen-space scale, readability, layer density, camera contract.
Gives 0 of the 12 instructions most images graphics skills give
Counted across 371 of the 372 authors here whose files we hold, read 2026-08-06
- create a complete brand world in one imagein 19 of 371, across 5 files
- infer the brand strategy before generatingin 19 of 371, across 5 files
- use a clean presentation gridin 19 of 371, across 5 files
- confirm connection status is activein 19 of 371, across 4 files
- base the visual system on meaningin 17 of 371, across 3 files
- use very little textin 17 of 371, across 3 files
- make every panel feel connectedin 17 of 371, across 3 files
- call RUBE_SEARCH_TOOLS firstin 17 of 371, across 3 files
- convert dash-format node IDs to colon formatin 17 of 371, across 5 files
- match reference quality and rhythm if providedin 16 of 371, across 2 files
- narrow scope or reduce depth to avoid oversized payloadsin 16 of 371, across 4 files
- generate a simple and memorable logoin 15 of 371, across 1 file
Said here and by no other author read
- Use Gemini for concept exploration only
- Use PixelLab for all final pixel art
- Generate art at the final target pixel canvas
- Resolve an art contract for every production call
- Condition all subsequent subjects on a golden anchor
- Pass a master palette lock on every call
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once.