Unity 2D (developer and artist)

SkillDev tools

Guides your agent through building 2D games in Unity, covering sprites, tilemaps, lighting, and player controllers.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Unity 2D (developer and artist) skill

About this skill

Use when building a 2D game in Unity 6.3 URP: pixel-art or HD sprite import and PPU, sprite atlases, Tilemaps, Rule Tiles and reskins, the 2D Renderer and 2D lights with normal maps, 2D animation, a platformer or top-down controller ("make a platformer controller that feels good", coyote time, jump

What this skill tells your AI

The instructions your AI receives, as published by scenario-labs/skills in skills/game-engines/unity/scenario-unity-2d/SKILL.md and read by ahel’s review.

Expert 2D work in Unity 6.3 is a chain of coupled settings proven by numbers and a frame: one PPU from art to camera, atlases judged by what they save, tiles and lights authored as data and costed, a camera that moves in whole pixels, and a controller whose feel is measured, not asserted. Everything below was run in Unity 6000.3.21f1 through batch jobs and PlayMode tests (references/procedures.md). 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.

REQUIRED BACKGROUND: scenario-unity-expert (channels, review loop, 6.3 traps).

Stance (the expert delta)

  1. Pixel perfect is a pipeline, and URP already ships the camera. One PPU on every sprite (tile px = 1 unit), Point, Compression None, whole-pixel pivots, Assets PPU = sprite PPU. Never install com.unity.2d.pixel-perfect in URP: it is the Built-in component (6.3 Manual). Measured: 100% uniform 6 x 6 blocks at 1080p.
  2. Pick Grid Snapping by what must be pixelated, and judge pixels in motion. Pixel Snapping snaps sprites only; lights, bloom, particles stay at screen resolution (2qeNu2QApAM [00:01:47]); Upscale Render Texture pixelates everything. Retro AA vs Point is shimmer vs blur (Manual), and in URP 17.3 it only acts under Stretch Fill. Measured over a sub-pixel pan: integer framing moves in whole art pixels (rigid 0.9997); Stretch Fill Point leaves 0.6% of pixels shimmering per frame, Retro AA 0.3% at twice the softness; no Pixel Perfect Camera, 2.8%.
  3. Under the SRP Batcher an atlas cuts textures, not Batches or SetPass. Measured 21/21 batches, 2/2 SetPass, bound textures 20 to 1 (Unity hXlpnwD-TgY [00:05:55], [00:07:32]); memory only drops when usage is high. Compress once, on the atlas, except pixel-art pages stay None: compression shifts pixel colors (2qeNu2QApAM [00:02:24]). AuditAtlases reproduces the 6.3 Sprite Atlas Analyzer reports plus Read/Write; grouping by co-visibility is the step no tool does.
  4. The controller owns the velocity; forgiveness is design, each trick behind a toggle. Latch input in Update; collisions, jump, horizontal, gravity, apply in FixedUpdate; coyote and buffer are timestamp windows (Tarodev source). "Working on the player's intent" (Thorson, GMTK yorTG9at90g [00:11:03]), so coyote, buffer, variable jump, fall clamp, apex hang and corner correction each get an on/off toggle for A/B play (Tarodev 3sWTzMsmdx8 [00:02:24]), and hazard hitboxes are smaller than the art (GMTK [00:09:28]). Two choices are feel decisions, never defaults: the variable-height method (velocity cut 50% precise, gravity x3 smooth, sustain while held long; Dawnosaur 2S3g8CgBG1g [00:00:36]) and the air friction (Tarodev AirDeceleration 30 drifts 3.1 units, 5.7 body widths, after release; Celeste's "massive air friction" drops almost straight down, GMTK [00:04:14]; 240 stops in 3 steps).
  5. Lights are routed by sorting layers and paid per light batch. Consecutive sorting layers share a light batch only if every visible light lights both or neither, and the normal, shadow and light passes run once per batch (URP 17.3 source). A player fill light that skips the Characters layer (Sasquatch 1h-hSlffawM [00:01:35]) split the demo level from 1 batch into 4 and light fill from 0.99 to 3.64 screens. Cost is fill rate (e-book p. 131): bench at 4K over 6 runs, shadows x5.4 to x5.9, the batch split +72 to +101%, normal maps +15 to +36%, large vs small lights +8 to +43% (noisy). Never a pitch-black area (Global 0.25, GDC YhrwKF_i-BI [00:11:43]); enable normal maps on the light (GDC [00:12:31]); Fast on low tiers.
  6. Sprite animation never blends and follows the controller. Exit time off, duration 0, Any State self-transition off; parameters from what the controller did, not from input (Brackeys hkaysu1Z-N8 [00:16:17], [00:18:57]).
  7. The camera and the juice are part of feel. Loose vertical damping rising, tight once falling past a speed threshold, back at vertical speed 0 (hysteresis), lerped (Sasquatch 9dzBrLUIF8g [00:05:37], [00:07:14]): in a 40-unit drop a fixed 0.6 s damping let the feet reach the bottom edge, the switch kept 3.2 to 3.4 units below in view. Swap room cameras on trigger EXIT by exit direction ([00:14:43]); bounds end exactly at a secret room ([00:02:10]). Hit stop 20 ms (Nijman AJdEqssNZ-U [00:18:22]) to 67 ms (Celeste, GMTK [00:08:40]) on unscaled time, extend not stack; shake with an off option (Nijman [00:32:14]).
  8. Be honest about GUI-only editors. Tile Palette, Rule Tile inspector, AutoTile masks, Sprite Editor, Skinning Editor and the Animator window are mouse tools. Agents author through Tilemap.SetTiles, RuleTile.m_TilingRules, RuleOverrideTile.ApplyOverrides, ISpriteEditorDataProvider, AnimatorController; rigging and AutoTile masks stay human (references/gui-paths.md).

Establish first

Ask once: pixel art or HD (defaults: pixel, 16 PPU, 320 x 180); target screens including 21:9 and phones; camera model (static rooms, follow with bias and fall damping, lead toward action); real-time 2D lights or painted light, and the lowest tier; controller profile (precision: own the velocity, high air friction; momentum: low air friction; physics-rich: Dynamic body with forces; top-down: velocity); jump height in tiles and the variable-height method; hazards and one-way platforms; input devices. Then ut_2d.project(P).

Workflow

  1. Project. TwoDSetup.ProjectSetup: sorting layers back to front (few: they drive light batching), Player and Ground layers. GATE: order read back, no pixel-perfect package, Input System, fixed step 0.02 s.
  2. Import. SpriteJobs.ImportPixelArt (slicing, pixel pivots, _NormalMap) or ImportHD (PPU = height / (2 x ortho), e-book p. 21); shaped tiles get SetPhysicsOutline (16 px sprites generate a 4-point rectangle). GATE: AuditSprites 0 errors; one PPU.
  3. Atlases. AtlasJobs.BuildAtlas per co-visible group, lit and plain apart, pixel pages uncompressed. GATE: 1 page, 0 findings, grouping rule written.
  4. Level. TilemapJobs.CreateRuleTile + BuildLevel; reskins with CreateRuleOverrideTile; Composite Merge, Outlines, Static; per-tile colliders only for runtime edits, then Use Delaunay Mesh. GATE: rule_mismatches == 0 (reskin read-back too), drop probe within 0.05 units, capture shows no seams.
  5. Actors and animation. AnimJobs.BuildSpriteAnimator, LevelJobs.Populate (player, Hazard2D spikes with inset hitboxes, one-way platforms). GATE: LintAnimator 0 errors; hitbox-to-art ratios below 1.
  6. Lights. LightJobs.LightRig, then LightJobs.LightCost (tier; engine check once the camera exists). GATE: Audit2D 0 errors; replica light batches = URP's own count; every batch break and every light over half the view justified; normals on/off capture pair differs.
  7. Camera. CameraJobs.SetupPixelPerfect, CapturePixelPerfect; a sub-pixel pan (ut_2d.pan_shots) scored by ut_2d.shimmer_check; follow cameras through CinemachineJobs.Setup (CM 3.1.7 pinned) plus CinemachineFallDamping and CinemachineRoomSwap. GATE: pixel_block_check >= 0.999 at 1080p and 720p, pan min rigid >= 0.99, void_columns 0 at 21:9, bounds_hide_secrets ok, contact sheet looked at.
  8. Feel. PlayMode tests with simulated input (ut_run.run_tests(P, "PlayMode", graphics=True)). GATE: apex = 50 Hz prediction, windows within one step, one press = one jump at 30/60/144 fps, each toggle A/B, method and air-drift numbers stated. Feel itself is a human verdict: ship toggles and numbers.
  9. Deliver. Audit2D.Scene clean; AgentProfile.PlayModeTimings budget pass; report each step with its Unity call, then Verified and Assumed.

Numbers

ValueRelative toSource
PPU = H / (2 x ortho size): 216 at 4K Size 5HD art, closest zoome-book p. 21
320 x 180: x4 720p, x6 1080p, x8 1440p, x12 4K; ortho 5.625 at 16 PPUpixel referenceManual; [added] arithmetic
MaxSpeed 14, Accel 120, GroundDecel 60, AirDecel 30, JumpPower 36, FallAccel 110, MaxFall 40, release x3, Coyote 0.15 s, Buffer 0.2 sTarodev stats (u/s, u/s^2)Tarodev repo
apex 5.536 at 50 Hz (formula 5.89), rise 0.32 s; tap: gravity x3 2.08, cut 0.5 1.81, sustain (JumpPower 18.7) 1.78 of 5.52those statsmeasured, replay
air drift after release at top speed: AirDecel 30 3.13 units (24 steps), 240 0.27 (3 steps)0.55-unit bodymeasured
Celeste: about 6 frames to top speed, 3 to stop, jump about 3 body heights (Mario 4, Meat Boy 6)60 fps framesGMTK [00:02:01], [00:03:42]
apex band gravity x0.5 below 5 u/s held (+0.08 s); corner nudge 0.25 unitsadded tricksDawnosaur [00:02:06]; [added]
hazard hitbox inset 2 px sides, 3 px top: 0.75 x 0.625 of a 16 x 8 spikeart[added] values
fall damping 0.6 s loose, 0.1 s tight, switch below -15 u/s, back at 0, 0.2 s blendfollow camera[added] values; rule Sasquatch
hit stop 0.02 to 0.07 sreal timeNijman; GMTK
Global light 0.25; light render scale 0.5 (template)lightingGDC; observed
one atlas page; wastage flag > 400 KBper atlashXlpnwD-TgY
12 samples per second for sprite clipsframe-by-frameBrackeys [00:03:42]

Quality gates

  • Measurable: audits at 0 errors (AuditSprites, AuditAtlases, Audit2D, LintAnimator); rule_mismatches == 0; drop probe < 0.05; pixel_block_check >= 0.999; pan rigid >= 0.99; void_columns 0 at 21:9; light batches replica = engine; PlayMode suite 0 failures; frame budget pass with ut_stat.summarize_csv.
  • Visual: every capture through ut_review.review_images and a contact sheet you open, 2x crops for pixel edges; lighting pair; critique.md sections A to H.

Common mistakes

MistakeWhat it looks likeFix
2D Pixel Perfect package in URPwrong component, settings ignoredURP PixelPerfectCamera
PPU per sprite, Bilinear, compressedblur, color shift, uneven pixelsImportPixelArt + AuditSprites
shimmer judged on stillscrisp frames, crawling pixels in motionpan_shots + shimmer_check; integer Crop Frame
batch capture right after OpenSceneonly the Global light showsCameraJobs.WarmLights2D
Light2D added by scriptshadows on, normals off (normalMapQuality read-only)set both; SerializedObject m_NormalMapQuality
a light skipping one sorting layerlight batches split, every light redrawnsame Target Sorting Layers where the look allows; LightCost
Accurate normals, big lights, many shadows on mobilefill-rate bound frameFast, smaller lights, 1 to 2 shadow lights
atlas judged by Batches or SetPass"atlas does nothing"used textures and memory
compressed pixel-art atlas pagecolor shifts in flat pixelsCompression None on pixel pages
_timeJumpWasPressed = 0 (Tarodev)phantom jump on first landingfloat.MinValue
input read in FixedUpdatelost presses at high fpslatch in Update
ground and ceiling casts that hit triggers or one-way platforms"grounded" on a trigger, head bonks under a platformuseTriggers = false; skip one-way colliders (as PlatformerController2D)
one AirDeceleration for every gameslides off small platforms, or no momentumchoose by level; state the drift
hazard hitbox = artdeath on a 1 px grazeHazard2D.FitHitbox inset
Rotated rule transform on groundgrass on wallsFixed or MirrorX
duplicated Rule Tile for a reskinrule fixes applied twice or missedRule Override Tile
blended sprite transitionslate, smeared switchesexit time off, duration 0
fixed vertical damping on a follow cameraplayer falls out of the frameCinemachineFallDamping
room swap on trigger enterwrong camera when the player turns backswap on exit by direction
legacy Input.GetAxisruntime exception (templates)Input System actions

Handoffs

  • Receives: sprites and tiles from scenario-game-assets, scenario-sprite-pipeline, scenario-textures (PNG, intended PPU, palette); input actions from scenario-unity-architecture; the pipeline asset from scenario-unity-rendering-lighting.
  • Delivers: Cinemachine 3 rigs to scenario-unity-animation (bounds, ortho, pixel ratio, fall damping, room swaps); Sprite Custom Lit and Sort 3D as 2D shaders to scenario-unity-shaders; pixel-grid particles to scenario-unity-vfx; light batches, fill and bench numbers to scenario-unity-performance; texture formats to scenario-unity-mobile; Web size to scenario-unity-web; postprocessors and CI runs to scenario-unity-pipeline-automation; HUD to scenario-unity-ui; non-platformer physics to scenario-unity-gameplay. Every packet carries job ids, audit counts, PlayMode metrics and contact sheets.

Unity 6.3 notes

  • UnityEngine.Rendering.Universal.PixelPerfectCamera (URP 17.3): Grid Snapping dropdown; Filter Mode has no public setter; the camera position is rounded to the pixel grid before every render.
  • Universal 2D template: Input System only, Sprite Atlas V2, 2D Tooling 1.0.3 (Window > Analysis > Sprite Atlas Analyzer), Tilemap Extras 6.0.2 (AutoTile masks: no public API), 2D Animation 13.0.5, SRP Batcher on. Window > 2D > Light Batching Debugger shows the light batches LightCost computes.
  • Rigidbody2D.linearVelocity, bodyType; Physics2D.autoSyncTransforms obsolete; LowLevelPhysics2D renamed PhysicsCore2D in 6.5.
  • 3D in 2D: Mesh2D-Lit-Default and Sort 3D as 2D; 2D lights reach meshes through a Sorting Group.
  • Cinemachine defaults to 2.10.7: pin com.unity.cinemachine 3.1.7; core in 6.6. Runtime atlases arrive in 6.4; PVRTC removed in 6.4.

References

  • references/procedures.md: procedures P0 to P14, Unity APIs, live tests, measured results.
  • references/expert-notes.md: principles by expert, timestamps, deciding conditions.
  • references/critique.md: the rubric (import, atlases, tiles, lights, camera, feel, animation and juice, report).
  • references/gui-paths.md: windows and menus for the same work.
  • references/sources.md: sources, credentials, timestamps, revision history.
  • scripts/ut_2d.py: install, resolution and feel math (jump methods, air drift), art generators, pixel_block_check, shimmer_check, void_columns, bounds_hide_secrets, metrics.
  • scripts/AgentKit/TwoD/: editor jobs (setup, sprites, atlases, tilemaps, level, lights and light cost, camera, Cinemachine, animation, audit, LowLevelPhysics2D).
  • scripts/Runtime/TwoD/: PlatformerController2D, PlatformerStats, Hazard2D, FallDampingSwitch, PlatformerAnimatorBridge, HitStop, CameraShake2D, TopDownMover2D; scripts/Runtime/TwoDCinemachine/: Cinemachine2DRig, CinemachineFallDamping, CinemachineRoomSwap (compile only with CM3).

Signals

GitHub stars
681
Forks
82
Last commit
Sep 2026
Advanced
Item type
skill
Key
scenario-unity-2d
Source
github.com/scenario-labs/skills