How to Cut a Sprite Sheet and Import It Into Unity, Godot and GameMaker

Slice-by-grid, import strips, origin at the feet. The import step is where a correct sheet becomes wrong — the settings that matter in Unity, Godot and GameMaker, and the three errors that cause all of them.

October 8, 2026

How to Cut a Sprite Sheet and Import It Into Unity, Godot and GameMaker

Cutting the sheet is the easy part. Any image editor with a grid overlay can do it, and the export dialog will happily hand you a PNG that looks correct in the preview window. The import is where the work actually lives. Most people asking how to cut and import a sprite sheet in your engine are not asking about the cutting at all — they are asking why their character slides one pixel per frame, why the walk cycle snaps back to frame zero, or why every edge is a soft grey smear instead of a hard black line.

I have shipped 2D work in all three of these engines. The same three mistakes explain nearly every broken import I have debugged: wrong ordering (row-major versus column-major), a cell size off by one pixel, and a texture filter that was never meant for pixel art. Fix those three and 90 percent of your import problems disappear before you touch a single Animator state.

This post walks each engine from the failure backwards — starting with the symptom, then the setting that causes it, then the correct value.

The three shared errors, stated up front

Before the engine-specific walkthroughs, here is the list. If you read nothing else, read this.

Symptom

Cause

Fix

Frames play in a diagonal scramble

Row-major vs column-major mismatch

Confirm the sheet's reading order and match the engine's slice order

Every frame shears or shows a sliver of the neighbour

Cell size off by one (or includes a 1px gutter you did not account for)

Measure the actual cell in pixels; do not trust the export dialog

Soft, blurry, grey edges on every sprite

Bilinear filtering applied to pixel art

Set filter mode to Point / Nearest, and compression off

The off-by-one cell size is the most expensive bug on this list because it compounds. A 1-pixel error on a 32-pixel cell means by frame 30 you are reading 30 pixels into the wrong columns. The art looks fine in the first frame and catastrophic in the last, so people assume it is an animation problem. It is not. It is arithmetic.

Row-major versus column-major

A sheet exported as a single row is unambiguous. A sheet exported as a grid is not. Some tools write frame 0, 1, 2 across the top row, then continue on the second row. Others write frame 0, 1, 2 down the first column. When you slice by grid in any engine, the engine has one assumption baked in, and it is usually the one your exporter did not use.

The fix is not to re-export. The fix is to count. Take a 4-by-4 sheet with 16 frames and slice it. If the walk cycle plays as frame 0, frame 4, frame 8, frame 12, your sheet is column-major and your engine is reading row-major. You have two options: re-export transposed, or reorder the frame indices in the engine. Reordering is usually faster, because the sheet is already in your project.

Texture filtering

Pixel art has hard edges. Bilinear filtering samples between pixels to smooth motion, which is correct for photographic textures and wrong for a 16x16 character. Unity, Godot and GameMaker all default to some form of linear filtering depending on the import path. Turn it off per-texture, not globally, or you will break your UI atlases.

Unity: sprite mode, slice by grid, and the pivot at the feet

Sprite Mode Multiple and the slice grid

Import the PNG. In the Inspector, set Texture Type to Sprite (2D and UI). Set Sprite Mode to Multiple. Open the Sprite Editor and choose Slice. Use Grid By Cell Size and enter the exact pixel dimensions of one frame — not the frame plus gutter, not the frame rounded to a power of two. If your character is 48x64, enter 48 and 64.

If the sheet has padding between frames, set Padding to the exact pixel value. Do not guess. Zoom to 800 percent in an image editor and measure.

Compression off, filter mode Point

In the same Inspector panel:

  • Compression: None. For pixel art this is non-negotiable. DXT and ASTC block compression will mush your palette.
  • Filter Mode: Point (no filter).
  • Max Size: at least the sheet's native width.
  • Pixels Per Unit: this is your scale contract. If your tiles are 16 pixels and you want one tile to equal one world unit, set PPU to 16.

Unity's Max Size silently downsamples sheets larger than the default. A 4096-wide sheet dropped to 2048 will still slice, still animate, and still look subtly wrong forever. Check the actual imported dimensions in the preview, not the source file.

The pivot at the feet

Open the Sprite Editor and set the pivot per-sprite. For a platformer character, use Custom and place the pivot at the bottom-centre of each cell — the feet. This matters because Unity rotates, flips and positions around the pivot. A centre pivot makes a jumping character rotate around its belly and a flipped sprite shift sideways by half a cell. Feet pivot keeps the ground contact consistent across every frame.

Building the Animator clip and landing the loop point

Select the sliced sprites, drag them into the Scene, and Unity will offer to create an Animation Clip. Say yes, then open the Animation window. The clip holds keyframes on the Sprite Renderer → Sprite track.

The loop point is the part people get wrong. If you have 6 frames and you key frame 0 through 5, the clip plays all six and then snaps back. That is correct. If you accidentally key frame 0 twice — once at the start and once at the end — you get a hitch, because frame 0 is displayed for two sample periods. Count your keyframes against your frame count. They must match.

Set the clip's Loop Time to true and the Sample Rate to your target. A 6-frame walk at 12 fps means each frame occupies 1/12 of a second, so the clip length is 0.5 seconds.

Godot: SpriteFrames, frame order, and the loop flag

Importing as a sprite sheet

In Godot 4, drop the PNG into your project. Select it in the FileSystem dock. In the Import tab, set Import As to Sprite2D and enable the sheet option, or import it as an AtlasTexture source. For AnimatedSprite2D workflows, the fastest path is to leave the texture as a plain Texture2D and build the frames manually inside the SpriteFrames resource, using the region rect of each frame.

AnimatedSprite2D and SpriteFrames

Add an AnimatedSprite2D node. In the Inspector, create a new SpriteFrames resource. Add an animation, name it walk. Then click the grid icon to add frames from a sheet, and use Add frames from a Sprite Sheet to define the horizontal and vertical frame counts and the cell size in pixels.

Two things trip people here:

  • Frame order is the order you add them. The dialog adds row by row. If your sheet is column-major, add frames manually in the correct order rather than trusting the dialog.
  • The loop flag is per-animation, on the SpriteFrames resource, not on the AnimatedSprite2D node. An idle should loop; a death animation should not. Set this per animation.

The FPS field is not frames per second of your animation

This is the one that confuses everyone. The Speed (FPS) field on a SpriteFrames animation is the playback rate in frames per second. It is not the frame count. If you have 6 frames and set FPS to 6, one full cycle takes 1 second. If you set FPS to 12, the cycle takes 0.5 seconds. The field name is accurate; the confusion is assuming it should equal the number of frames.

Godot's AnimatedSprite2D has a speed_scale property that multiplies the SpriteFrames FPS. If your animation plays at half the speed you expect, check speed_scale before you touch the FPS field. It defaults to 1.0, but animation player state can leave it at something else after a scene transition.

GameMaker: sprite strips, sub-images, and image_speed

Importing the strip

Create a new sprite. Press Import and select the PNG. GameMaker will ask whether to import as a strip. Say yes. It will then ask for the number of sub-images. This number must equal your frame count exactly. A 6-frame walk imported as 5 sub-images will drop the last frame; imported as 7, it will show a blank cell.

After import, open the sprite's Image Editor. Each sub-image is a frame. GameMaker does not automatically detect gutters — if your strip has padding, you need to trim it in your image editor before import, or crop each sub-image manually.

The 0.5-over-6-sub-images trap

image_speed is the number of sub-images advanced per game step. A common mistake is writing image_speed = 0.5 / 6 thinking that normalises the cycle. It does not. At 60 steps per second, image_speed = 0.1 advances one sub-image every 10 steps, so a 6-frame animation completes in 60 steps — one second. If you want a 6-frame walk to complete in 0.5 seconds, you need image_speed = 0.2.

The trap is that people write 0.5 / sprite_get_number(sprite_index) assuming the engine divides by sub-image count. It does not. image_speed is sub-images per step. Calculate it as frame_count / (target_seconds * room_speed).

Setting the origin

In the sprite editor, set the Origin to the bottom-centre of the cell for a ground character. GameMaker uses the origin for x/y positioning, rotation and scaling. A centre origin makes a jumping character scale around its middle and a flipped sprite jump sideways. Same lesson as Unity, different menu.

What to do when the sheet is not a perfect grid

Not every sheet is uniform. Character sheets with variable-size frames, attack animations with a larger hitbox frame, or sheets exported with irregular spacing all break grid slicing.

Three options in order of preference:

  1. Re-export as a uniform grid. If you control the source, do this. It is always cheaper than fighting the engine.
  2. Use manual slicing. Unity's Sprite Editor, Godot's region rects and GameMaker's Image Editor all let you define each frame's rectangle by hand. Tedious, but reliable.
  3. Use an atlas tool. Pack the frames into a uniform atlas with a JSON or XML descriptor, then read the descriptor in-engine. Unity's Sprite Atlas and Godot's AtlasTexture both support this. GameMaker can read a JSON at runtime and build sprites with sprite_add if you need procedural frames.

For sheets that mix frame sizes — common in boss animations — option 2 is usually the only correct answer. Do not try to force a uniform grid onto a sheet that does not have one. The off-by-one error you generate is worse than the manual work you avoided.

A short checklist before you blame the animation

Run through this every time an import looks wrong:

  • Confirm the sheet's reading order. Count the frames.
  • Measure one cell in pixels. Enter that exact number.
  • Turn off compression. Set filter to Point.
  • Set the pivot or origin to the feet for ground characters.
  • Count keyframes or sub-images against the frame count.
  • Check the FPS field is a rate, not a count.

If your animation still misbehaves after those six, the problem is in the sheet, not the engine. Cut it again with a known-good grid and re-import.

The fastest debugging tool for a bad import is a numbered frame overlay. Open the sheet in an image editor, stamp the frame index on each cell, import the stamped version, and watch it play. The moment the numbers go out of order, you have found the bug — and it is almost always one of the three.

Neta Studio builds 3D worlds and games in the browser, including Agent Sprite Forge, The Cyclops' Island and Mini World. The same import discipline applies when you move 2D assets into a 3D pipeline: get the grid right, get the filtering right, get the pivot right, and the engine stops fighting you.


Keep going


Make the world it walks in

A sprite is a character with nowhere to stand. Every world on the 3D game builder was made from one written description and published as a page you can play.

Open the 3D Game Builder →