Enemy Art Asset Conventions - UQcsse3200/2026-studio-3 GitHub Wiki
Owner:
@xiaojaxonยท Last updated: 2026-10-14 (action and form regions added for #438)
Sprint 1's ten assets were made by one artist, so consistency came for free. Sprint 2 spreads
enemy art across four people. This page is the single source of truth for sizing, derived
frames, and .atlas format, so every artist ships assets that drop into the game without
rework.
Every enemy ships a full animation set. Region names must match exactly:
| Region | Frames | Purpose |
|---|---|---|
default |
1 (alias) | Fallback / thumbnail; points at the first idle frame |
idle |
2 | Looping stance, played continuously |
attack |
1+ | One-shot strike |
hurt |
1 | One-shot flinch (played at 0.15 s) |
death |
1+ | One-shot defeat |
Optional regions, only for enemies whose AI can produce the matching intent (see Enemy Action and Form Animations):
| Region | Frames | Purpose |
|---|---|---|
defend |
1+ | One-shot block pose when the enemy defends; falls back to idle if missing |
cast |
1+ | One-shot pose when the enemy applies an effect (e.g. taunt); falls back to idle
|
<animation>_<form> |
as the base animation | A form's version of an animation, e.g. idle_enraged, hurt_taunt; falls back to the base animation |
A sprite sheet and atlas pair is named <enemy_id>.png and <enemy_id>.atlas, where
<enemy_id> is the enemy's id in enemies.json. The engine finds the files by this name
automatically โ naming them correctly is the registration.
- One frame = 512 ร 512 px.
- One sheet = 1024 ร 1024 px (4 frames in a 2ร2 grid).
- A full set (5 frames) needs a 2048 ร 1024 px sheet (4ร2 grid).
- With optional regions, up to 8 frames still fit 2048 ร 1024; 9โ16 frames use a 2048 ร 2048 px
sheet (4ร4 grid). The original five frames keep their positions, so
defaultnever moves. - Keep every dimension a power of two (512 / 1024 / 2048).
- Every frame of an enemy shares the same canvas size and pivot, so the animation doesn't "jump".
Don't draw every frame from scratch. Start from one finished idle frame and derive the rest:
- idle โ duplicate the base frame, add a 1โ2 px breathing/bob shift for the 2nd frame.
- attack โ derive from idle: extend the weapon or lunge. One frame reads clearly.
- hurt โ derive from idle: red/white flash tint plus a small (2โ4 px) recoil. One frame.
- death โ derive from idle: desaturate, then collapse or rotate slightly.
Rules: same canvas size, same origin, same palette. Derived frames are edits of the source frame, not new drawings โ this is what keeps four artists' work visually consistent.
The .atlas file is plain text in libGDX TexturePacker format. Annotated example for a full
5-frame enemy:
rage_brute.png
size: 2048, 1024
format: RGBA8888
filter: Linear,Linear
repeat: none
default
rotate: false
xy: 0, 0
size: 512, 512
orig: 512, 512
offset: 0, 0
index: -1
idle
rotate: false
xy: 0, 0
size: 512, 512
orig: 512, 512
offset: 0, 0
index: 0
idle
rotate: false
xy: 512, 0
size: 512, 512
orig: 512, 512
offset: 0, 0
index: 1
attack
rotate: false
xy: 1024, 0
size: 512, 512
orig: 512, 512
offset: 0, 0
index: 0
hurt
rotate: false
xy: 1536, 0
size: 512, 512
orig: 512, 512
offset: 0, 0
index: 0
death
rotate: false
xy: 0, 512
size: 512, 512
orig: 512, 512
offset: 0, 0
index: 0
Field meanings:
- Line 1 โ the PNG filename this atlas describes.
-
sizeโ full sheet dimensions in pixels. -
format: RGBA8888โ 32-bit with alpha channel. Always use this. -
filter: Linear,Linearโ scaling filter; keep as-is. -
repeat: noneโ do not tile; keep as-is. -
xyโ top-left pixel of the region in the sheet (y grows downward). -
size(inside a region) โ drawn frame size (512ร512 for untrimmed frames). -
orig/offsetโ original untrimmed size and trim offset;512,512/0,0for untrimmed. -
rotate: falseโ region stored unrotated; keep false. -
indexโ groups regions into an animation.idlewithindex: 0andindex: 1form a 2-frameidleanimation. A singleindex: -1region is a standalone named region (default).
One person owns packing this sprint to keep style and format consistent:
- Artist finishes the frames at 512ร512.
- Run
assets/enemies/tools/PackEnemy.java(it lays out every<enemy_id>_<animation>_<index>.pngand writes the.atlas), libGDX TexturePacker, or hand-write the.atlasfrom the template above. - Save
<enemy_id>.pngand<enemy_id>.atlastogether. - Verify in game: idle loops, hurt flashes, death plays on defeat.
- Commit both files; keep working files in
assets/.
- 512ร512 frames on a power-of-two sheet
- Regions named exactly
default,idle,attack,hurt,death(plusdefend,castor<animation>_<form>where the enemy needs them) -
defaulthasindex: -1; each animation uses sequentialindex: 0,1,โฆ -
enemies.jsonhas a matchingid,tier, andbehaviour - Both
.pngand.atlascommitted