Unity 2D (developer and artist)
SkillDev toolsGuides 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.
No other account needed.
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)
- 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-perfectin URP: it is the Built-in component (6.3 Manual). Measured: 100% uniform 6 x 6 blocks at 1080p. - 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%.
- 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]).
AuditAtlasesreproduces the 6.3 Sprite Atlas Analyzer reports plus Read/Write; grouping by co-visibility is the step no tool does. - 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).
- 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.
- 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]).
- 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]).
- 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
- 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. - Import.
SpriteJobs.ImportPixelArt(slicing, pixel pivots,_NormalMap) orImportHD(PPU = height / (2 x ortho), e-book p. 21); shaped tiles getSetPhysicsOutline(16 px sprites generate a 4-point rectangle). GATE:AuditSprites0 errors; one PPU. - Atlases.
AtlasJobs.BuildAtlasper co-visible group, lit and plain apart, pixel pages uncompressed. GATE: 1 page, 0 findings, grouping rule written. - Level.
TilemapJobs.CreateRuleTile+BuildLevel; reskins withCreateRuleOverrideTile; 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. - Actors and animation.
AnimJobs.BuildSpriteAnimator,LevelJobs.Populate(player,Hazard2Dspikes with inset hitboxes, one-way platforms). GATE:LintAnimator0 errors; hitbox-to-art ratios below 1. - Lights.
LightJobs.LightRig, thenLightJobs.LightCost(tier; engine check once the camera exists). GATE:Audit2D0 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. - Camera.
CameraJobs.SetupPixelPerfect,CapturePixelPerfect; a sub-pixel pan (ut_2d.pan_shots) scored byut_2d.shimmer_check; follow cameras throughCinemachineJobs.Setup(CM 3.1.7 pinned) plusCinemachineFallDampingandCinemachineRoomSwap. GATE:pixel_block_check>= 0.999 at 1080p and 720p, pan min rigid >= 0.99,void_columns0 at 21:9,bounds_hide_secretsok, contact sheet looked at. - 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. - Deliver.
Audit2D.Sceneclean;AgentProfile.PlayModeTimingsbudget pass; report each step with its Unity call, then Verified and Assumed.
Numbers
| Value | Relative to | Source |
|---|---|---|
| PPU = H / (2 x ortho size): 216 at 4K Size 5 | HD art, closest zoom | e-book p. 21 |
| 320 x 180: x4 720p, x6 1080p, x8 1440p, x12 4K; ortho 5.625 at 16 PPU | pixel reference | Manual; [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 s | Tarodev 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.52 | those stats | measured, replay |
| air drift after release at top speed: AirDecel 30 3.13 units (24 steps), 240 0.27 (3 steps) | 0.55-unit body | measured |
| Celeste: about 6 frames to top speed, 3 to stop, jump about 3 body heights (Mario 4, Meat Boy 6) | 60 fps frames | GMTK [00:02:01], [00:03:42] |
| apex band gravity x0.5 below 5 u/s held (+0.08 s); corner nudge 0.25 units | added tricks | Dawnosaur [00:02:06]; [added] |
| hazard hitbox inset 2 px sides, 3 px top: 0.75 x 0.625 of a 16 x 8 spike | art | [added] values |
| fall damping 0.6 s loose, 0.1 s tight, switch below -15 u/s, back at 0, 0.2 s blend | follow camera | [added] values; rule Sasquatch |
| hit stop 0.02 to 0.07 s | real time | Nijman; GMTK |
| Global light 0.25; light render scale 0.5 (template) | lighting | GDC; observed |
| one atlas page; wastage flag > 400 KB | per atlas | hXlpnwD-TgY |
| 12 samples per second for sprite clips | frame-by-frame | Brackeys [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_columns0 at 21:9; light batches replica = engine; PlayMode suite 0 failures; frame budget pass withut_stat.summarize_csv. - Visual: every capture through
ut_review.review_imagesand a contact sheet you open, 2x crops for pixel edges; lighting pair;critique.mdsections A to H.
Common mistakes
| Mistake | What it looks like | Fix |
|---|---|---|
| 2D Pixel Perfect package in URP | wrong component, settings ignored | URP PixelPerfectCamera |
| PPU per sprite, Bilinear, compressed | blur, color shift, uneven pixels | ImportPixelArt + AuditSprites |
| shimmer judged on stills | crisp frames, crawling pixels in motion | pan_shots + shimmer_check; integer Crop Frame |
| batch capture right after OpenScene | only the Global light shows | CameraJobs.WarmLights2D |
| Light2D added by script | shadows on, normals off (normalMapQuality read-only) | set both; SerializedObject m_NormalMapQuality |
| a light skipping one sorting layer | light batches split, every light redrawn | same Target Sorting Layers where the look allows; LightCost |
| Accurate normals, big lights, many shadows on mobile | fill-rate bound frame | Fast, smaller lights, 1 to 2 shadow lights |
| atlas judged by Batches or SetPass | "atlas does nothing" | used textures and memory |
| compressed pixel-art atlas page | color shifts in flat pixels | Compression None on pixel pages |
_timeJumpWasPressed = 0 (Tarodev) | phantom jump on first landing | float.MinValue |
| input read in FixedUpdate | lost presses at high fps | latch in Update |
| ground and ceiling casts that hit triggers or one-way platforms | "grounded" on a trigger, head bonks under a platform | useTriggers = false; skip one-way colliders (as PlatformerController2D) |
| one AirDeceleration for every game | slides off small platforms, or no momentum | choose by level; state the drift |
| hazard hitbox = art | death on a 1 px graze | Hazard2D.FitHitbox inset |
| Rotated rule transform on ground | grass on walls | Fixed or MirrorX |
| duplicated Rule Tile for a reskin | rule fixes applied twice or missed | Rule Override Tile |
| blended sprite transitions | late, smeared switches | exit time off, duration 0 |
| fixed vertical damping on a follow camera | player falls out of the frame | CinemachineFallDamping |
| room swap on trigger enter | wrong camera when the player turns back | swap on exit by direction |
legacy Input.GetAxis | runtime 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
LightCostcomputes. Rigidbody2D.linearVelocity,bodyType;Physics2D.autoSyncTransformsobsolete; 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.cinemachine3.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