Game Assets

Reference Notes

GD 2000 — Game Design

2026-11-04

Roadmap

The idea

  1. What an asset is, and the families of assets
  2. The pipeline: find → license → inspect → organize → import → configure → integrate → playtest

The practice

  1. Licenses, provenance, and credits
  2. Reading a pack; organizing a project
  3. 2D sheets and tilesets; 3D models and rigs; audio roles and formats

The lab

  1. Excalibur: load, slice, animate, tile, sound
  2. The checklist, and the failures to expect

The Asset Families

Family What it includes Job in the game Typical format
2D art sprites, sheets, tilesets, backgrounds the visible world PNG
3D art meshes, materials, texture maps, props the visible world, with depth glTF / GLB, FBX
Animation sprite frames, skeletal clips, blends motion and feedback PNG sheets, GLB clips
Audio SFX, ambience, music, voice feedback and tone OGG, WAV, MP3
Text fonts, dialogue, localization legibility and voice TTF/OTF, JSON, CSV
Data levels, tilemaps, atlases, configs structure and content JSON, YAML, TMX
Shaders + video effects, cutscenes, menus look and spectacle GLSL/WGSL, WebM

An asset is a file the game loads — not a feature you implement. That is what makes assets replaceable, and what makes a pipeline worth building.

The Pipeline

1 · Find
search, tags, license filter

2 · License
credit? share-alike?

3 · Inspect
open the zip, measure the grid

4 · Organize
source vs runtime, credits

5 · Import
load once, at startup

6 · Configure
anchor, scale, filtering

7 · Integrate
animate, tile, mix

8 · Playtest
does it read? does it fit?

Playtest sends you back to Find: a pipeline is a loop. Every step has a failure mode — the wrong license, a missing file, the wrong grid, the wrong path, the wrong scale.

Licenses

License Credit? Share-alike? What it means for your game
CC0 no no Do anything. Credit as a courtesy.
CC-BY 3.0 / 4.0 yes no Credit, link, and say what you changed.
CC-BY-SA 3.0 / 4.0 yes yes Your changes to the art stay CC-BY-SA.
OGA-BY 3.0 / 4.0 yes no CC-BY without the anti-DRM clause.
GPL 2.0 / 3.0 yes yes Avoid unless the whole project is GPL.
  • More than one license listed? Pick one and follow it.
  • “Royalty-free” is a price model, not a license — read the EULA.
  • Public domain is CC0 in effect. All rights reserved means you may not use it, even though you can download it.
  • CC0 is the easy button. CC-BY is the normal case. CC-BY-SA and GPL are the ones that can bite.

Credits and Provenance

Record five things for every asset: title, author, license, URL, and what you changed.

OGA’s recommended credit line:

“Asset Name” by Author, licensed CC-BY 3.0: https://opengameart.org/content/…

  • Put the credit line in CREDITS.md and on a credits screen.
  • Save the page — as a PDF, a screenshot, or an archived link. Pages disappear and packs get re-uploaded.
  • CC-BY: say what you changed — cropped, recoloured, resized.
  • Write the credit when you download, not at the end of the project.

Open the Box

Ask of every pack:

  • Does the preview match the files? (The preview is not always in the zip.)
  • What is the frame size and margin? What is the tile size?
  • Is there alpha? Is there a README or license file?
  • What is junk — thumbnails, .db files, nested zips, duplicate exports?
  • Which files are source (.blend, .psd, .wav) and which are runtime (.glb, .png, .ogg)?

A pack is a box of parts, not a game. You choose the subset; leave the rest out of the repo.

Organize

assets/source/            <- masters; never served
  character.blend  ui.psd  jump.wav
public/assets/            <- runtime; served at /assets/
  sprites/  player.png  enemy.png
  tiles/    platformer.png
  models/   dungeon.glb
  audio/    jump.ogg  theme.ogg
  ui/       heart.png
CREDITS.md
  • Keep source and runtime separate: author a .blend, ship a .glb; edit a .wav, ship an .ogg.
  • With Vite, public/ is served at the site root: public/assets/x.png becomes assets/x.png in your code.
  • Name by role, not by source: player-run.png, not p1_spritesheet (1).png.
  • Big binaries go in Git LFS — or out of the repo entirely.

2D: Sheets and Atlases

Grid sheet Atlas
Frame rects computed from a cell size listed explicitly
Ships with nothing extra a sidecar file
Example a tileset: 70×70 cells, 2 px margin Kenney’s player sheet: a .txt of rects

A grid sheet is arithmetic; an atlas is data. Sidecars vary: TexturePacker JSON, Starling XML, Tiled TMX — and encodings vary too (Kenney’s p1_walk.json is UTF-16, so strip the BOM before JSON.parse).

p1_walk01 = 0 0 72 97
p1_walk02 = 73 0 72 97
p1_walk03 = 146 0 72 97

3D: What Is in a Model

A model is not one file:

  • Mesh — vertices and triangles; the silhouette.
  • UVs — which part of the texture each triangle uses.
  • Normals / tangents — how it catches light.
  • Materials — the shading recipe.
  • Texture maps — albedo, roughness/metallic, normal, AO, emissive.
  • Rig — skeleton plus skin weights.
  • Clips — named animations; a state machine picks and blends them.
  • LODs — lower-poly meshes for distance.

Formats: glTF/GLB is the web format; FBX is the DCC interchange; OBJ is geometry only. Scale and units are a contract — glTF is metres, Y-up; FBX is often centimetres, Z-up.

Audio: Roles and Formats

Role Length Count Job
SFX 50 ms – 2 s dozens feedback: the hit, the jump, the pickup
UI 20 – 200 ms a handful confirmation: the click, the hover
Ambience 10 s – 2 min loop one per area place: wind, crowd, dripping water
Music 1 – 4 min loop one per scene tone and pacing
Voice 1 – 30 s story-dependent character and story
  • WAV uncompressed; OGG Vorbis small and open; MP3 universal fallback; Opus/AAC modern but check support.
  • 44.1 kHz stereo 16-bit is about 10 MB per minute in memory. Stream music; preload SFX.
  • Loops need seamless points. Normalise to one loudness target (around −16 LUFS integrated, true peak below −1 dBTP), then mix music under SFX.
  • Browsers block audio until a user gesture. The play button is that gesture.

Excalibur: Load, Slice, Animate

const sheetImage = new ex.ImageSource('assets/sprites/player.png');
const loader = new ex.Loader([sheetImage]);

const game = new ex.Engine({ width: 960, height: 540, pixelArt: true });

game.start(loader).then(() => {
  const sheet = ex.SpriteSheet.fromImageSource({
    image: sheetImage,
    grid: { rows: 4, columns: 4, spriteWidth: 32, spriteHeight: 32 },
  });

  const run = ex.Animation.fromSpriteSheet(
    sheet, [0, 1, 2, 3], 100, ex.AnimationStrategy.Loop
  );

  player.graphics.use(run, { anchor: ex.vec(0.5, 1) });
});

getSprite(x, y) is (column, row); Animation.fromSpriteSheet takes row-major indices. Irregular atlas? Build the frames: new ex.Animation({ frames: sprites.map(g => ({ graphic: g })), frameDuration: 100 }).

Excalibur: Tiles and Sound

const tiles = ex.SpriteSheet.fromImageSource({
  image: tilesImage,
  grid: { rows: 13, columns: 12, spriteWidth: 70, spriteHeight: 70 },
  spacing: { margin: { x: 2, y: 2 } },
});
const map = new ex.TileMap({
  tileWidth: 70, tileHeight: 70, rows: 15, columns: 40,
});
for (let y = 0; y < level.length; y++) {
  for (let x = 0; x < level[y].length; x++) {
    const i = level[y][x];
    const tile = map.getTile(x, y);
    if (!tile || i === 0) continue;
    tile.addGraphic(tiles.getSprite(i % 12, Math.floor(i / 12)));
    tile.solid = SOLIDS.has(i);
  }
}
scene.add(map);

const jump = new ex.Sound('assets/audio/jump.ogg', 'assets/audio/jump.wav');
jump.volume = 0.6;
jump.play();

The level is data; the tileset is art. i → (column, row) is % and Math.floor.

The Lab Checklist

Do this

  1. Pick one OGA pack — CC0 or CC-BY.
  2. Download, inspect, copy what you need into public/assets/.
  3. Load it with ex.Loader; replace the placeholder player with an animated sprite.
  4. Build the level from a tileset with ex.TileMap.
  5. Add at least two SFX and one music loop.
  6. Write CREDITS.md with the exact license lines.

Done means

  • no 404s in the console
  • the player animates and faces the right way
  • the level is tiled, and solid tiles collide
  • sound plays after a click
  • the credits match the licenses
  • pnpm dev and pnpm build both work

Common failures: wrong grid → sliced sprites · wrong path → invisible actor · wrong anchor → floating player · no gesture → silence · missing credit → an academic honesty problem, not a style note.