PX-Art

GUIDE

How do you use AI-generated assets in Godot?

Godot reads PX-Art output as ordinary files: sprite sheet PNGs become SpriteFrames on an AnimatedSprite2D, and an arranged scene exports as Tiled JSON that carries grid coordinates, layer offsets and per-object asset ids. Import the PNGs with nearest-neighbor filtering, slice each strip by frame count, and read the px_art properties to reconnect objects to their source assets.

What actually crosses the boundary

There is a thin Godot-side adapter, but you do not depend on it: everything PX-Art produces for Godot is plain files with documented shapes.

  • Sprite sheet PNGs — one strip per action and facing, uniform cell size, shared baseline.
  • Plain ZIP — images plus a manifest; manifest.pxart is the JSON entry file inside the archive.
  • Tiled JSON — an arranged scene exported as a map, described below.
  • Scene bundle — the scene JSON, the Tiled map, the dependency asset paths, SHA-256 hashes and short-lived download links. This is the form a coding agent pulls over read-only MCP with get_scene_bundle.

Import settings before anything else

Pixel art in Godot 4 needs filtering off, or every sprite arrives blurred and every tile seam glows.

  1. Set the project defaultProject Settings → Rendering → Textures → Canvas Textures → Default Texture Filter = Nearest. This saves you from setting it per node later.
  2. Turn off mipmaps and filtering on the imported PNGsSelect the files in the FileSystem dock, open the Import tab, uncheck Mipmaps and Filter, then Reimport. Do this for the whole assets folder at once.
  3. Decide your snappingFor crisp movement, enable Snap 2D Transforms to Pixel in Rendering → 2D. Leave it off if you rely on sub-pixel camera smoothing.
  4. Keep one folder per characterPX-Art already groups assets by game project and module; mirroring that under res://assets/ keeps the mapping obvious when an agent later resolves an asset id back to a file.

Turning a strip into SpriteFrames

You can do it in the inspector — add an AnimatedSprite2D, create a SpriteFrames resource, use Add Frames from Sprite Sheet — but for more than a handful of animations a script is faster and repeatable. This EditorScript slices one horizontal strip:

@tool
extends EditorScript

const FRAME_W := 64
const FRAME_H := 64
const SRC := "res://assets/dockhand/walk_south_8f.png"

func _run() -> void:
    var tex: Texture2D = load(SRC)
    var frames := SpriteFrames.new()
    var anim := "walk_south"
    frames.add_animation(anim)
    frames.set_animation_loop(anim, true)   # false for attack, hurt, one-shot actions
    frames.set_animation_speed(anim, 10.0)  # 10 fps == 100 ms per frame
    var count: int = tex.get_width() / FRAME_W
    for i in count:
        var cell := AtlasTexture.new()
        cell.atlas = tex
        cell.region = Rect2(i * FRAME_W, 0, FRAME_W, FRAME_H)
        frames.add_frame(anim, cell)
    ResourceSaver.save(frames, "res://assets/dockhand/dockhand.tres")

Two things make this safe. First, FRAME_W is exact, because the strip is assembled with one uniform scale. Second, you do not need to flip anything at runtime: the three mirrored facings — west, south-west, north-west — are delivered as real images, so all eight directions are ordinary animations named walk_south, walk_east, walk_west and so on. Loop flags follow the action type: looping actions were sampled without a duplicated end pose, one-shot actions kept their true first and last frames.

Bringing an arranged scene in

The Arrange workspace stores scene size, layers, coordinates, asset references and map cells, and exports the result as Tiled JSON. What Godot has to read:

  • orientation is orthogonal or isometric.
  • Tile layers are row-major gid arrays where 0 means an empty cell.
  • The tileset is an image-collection tileset — one image per cell — with firstgid of 1.
  • Layers carry both x/y, the original grid coordinates, and offsetx/offsety, the canvas pixel origin. You do not have to reconstruct trimmed negative coordinates by guessing.
  • Non-tile nodes are exported as an objectgroup, and each object carries custom properties: px_art_node_id, px_art_z, px_art_asset_id, px_art_asset_file, px_art_tags.

An object entry looks like this:

{
  "type": "objectgroup",
  "name": "props",
  "objects": [
    {
      "id": 7,
      "name": "crate_stack",
      "x": 320, "y": 224, "width": 32, "height": 48,
      "properties": [
        { "name": "px_art_node_id",    "type": "string", "value": "n_2f81" },
        { "name": "px_art_z",          "type": "int",    "value": 40 },
        { "name": "px_art_asset_id",   "type": "string", "value": "a_9c14" },
        { "name": "px_art_asset_file", "type": "string", "value": "crate_stack.png" },
        { "name": "px_art_tags",       "type": "string", "value": "prop,harbor,wood" }
      ]
    }
  ]
}

A minimal importer that turns that object group into Sprite2D nodes:

func load_objects(map_path: String, layer_name: String, parent: Node2D) -> void:
    var f := FileAccess.open(map_path, FileAccess.READ)
    var map: Dictionary = JSON.parse_string(f.get_as_text())
    for layer in map["layers"]:
        if layer["type"] != "objectgroup" or layer["name"] != layer_name:
            continue
        var ox: float = layer.get("offsetx", 0.0)
        var oy: float = layer.get("offsety", 0.0)
        for obj in layer["objects"]:
            var props := {}
            for p in obj.get("properties", []):
                props[p["name"]] = p["value"]
            var s := Sprite2D.new()
            s.name = String(obj["name"])
            s.texture = load("res://assets/props/" + String(props["px_art_asset_file"]))
            s.centered = false
            s.position = Vector2(obj["x"] + ox, obj["y"] + oy)
            s.z_index = int(props.get("px_art_z", 0))
            s.set_meta("px_art_asset_id", props.get("px_art_asset_id", ""))
            parent.add_child(s)

Keeping px_art_asset_id as node metadata is the part worth the extra two lines: it is the key a coding agent uses with get_asset_downloads or search_assets over read-only MCP to re-fetch or replace exactly that asset later. See MCP game assets and AI game assets for coding agents for how that access works.

If you have no tileset yet and want to build one from scratch, that is the other direction — start at generating a tileset for Tiled and come back here once the ZIP exists.

Limits and caveats

  • The Godot-side adapter is deliberately thin. Treat the files as the contract: PNG strips, Tiled JSON, and the scene bundle with SHA-256 hashes. If you write your own importer, you are not fighting a black box.
  • Unity is not supported. Nothing here is written or tested for it, and PX-Art does not claim it.
  • Download links in a scene bundle are short-lived. Resolve and fetch them at import time rather than committing the URLs.
  • The Arrange workspace needs a large canvas and precise mouse work. On a phone you can browse assets and take notes, but not compose a scene.
  • Generation is probabilistic. Check assets before they land in a build; the pipeline's own quality checks reduce bad output but do not certify it.
  • Legally generated assets can be used commercially, but you still need to check third-party rights, applicable law and AI provider restrictions. AI output is not guaranteed to be exclusive or registrable for copyright.
  • Checkout is still under review and not open yet. New accounts start with 20 credits.

FAQ

Why do my imported sprites look blurry in Godot?

Texture filtering is on. Set Project Settings, Rendering, Textures, Canvas Textures, Default Texture Filter to Nearest, then select the PNGs in the FileSystem dock and uncheck Filter and Mipmaps in the Import tab before reimporting.

Do I need to flip sprites at runtime for the west-facing directions?

No. PX-Art delivers west, south-west and north-west as real images mirrored from their counterparts, so all eight facings arrive as ordinary strips. Build one animation per facing and set flip_h nowhere.

How do I know a scene's grid coordinates survived the export?

Each layer in the exported Tiled JSON carries x and y, the original grid coordinates, alongside offsetx and offsety, the canvas pixel origin. Your importer adds the offset instead of guessing at coordinates that were trimmed off during export.

What are the px_art properties on exported objects?

Non-tile nodes export as an objectgroup and each object carries px_art_node_id, px_art_z, px_art_asset_id, px_art_asset_file and px_art_tags. Keep px_art_asset_id on the node so an agent can resolve the object back to the exact source asset.

Does PX-Art support Unity?

No. There is a thin Godot-side adapter, and everything else is plain files: sprite sheet PNGs, ZIP packages, Tiled JSON and scene bundles. Unity support is not claimed.

Can a coding agent fetch scene data instead of me exporting by hand?

Yes, read-only. The perfectpixel-scene MCP server exposes get_scene_bundle, get_scene_metadata, get_nodes, get_scene_region and analyze_layout, plus resources such as scene://{sceneId}/tiled. It has no write, generate or delete tools.

Related

Get assets into your Godot project

Generate characters, actions and map pieces in one game project, arrange a scene, and export Tiled JSON plus sprite sheets your importer can read.

Create a free accountSee pricing

New accounts start with 20 credits. Checkout is still under review and not open yet.