Enemy Art Asset Conventions - UQcsse3200/2026-studio-3 GitHub Wiki

Enemy Art Asset Conventions

Owner: @xiaojaxon ยท Last updated: 2026-10-14 (action and form regions added for #438)

Why this page exists

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.

What a complete enemy needs

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.

Sizing

  • 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 default never 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".

Derived frames

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.

.atlas format

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,0 for untrimmed.
  • rotate: false โ€” region stored unrotated; keep false.
  • index โ€” groups regions into an animation. idle with index: 0 and index: 1 form a 2-frame idle animation. A single index: -1 region is a standalone named region (default).

Packing workflow (consolidated here)

One person owns packing this sprint to keep style and format consistent:

  1. Artist finishes the frames at 512ร—512.
  2. Run assets/enemies/tools/PackEnemy.java (it lays out every <enemy_id>_<animation>_<index>.png and writes the .atlas), libGDX TexturePacker, or hand-write the .atlas from the template above.
  3. Save <enemy_id>.png and <enemy_id>.atlas together.
  4. Verify in game: idle loops, hurt flashes, death plays on defeat.
  5. Commit both files; keep working files in assets/.

Checklist

  • 512ร—512 frames on a power-of-two sheet
  • Regions named exactly default, idle, attack, hurt, death (plus defend, cast or <animation>_<form> where the enemy needs them)
  • default has index: -1; each animation uses sequential index: 0,1,โ€ฆ
  • enemies.json has a matching id, tier, and behaviour
  • Both .png and .atlas committed
โš ๏ธ **GitHub.com Fallback** โš ๏ธ