Sprites¶
2D sprites, drawn between the 3D scene and the UI. They are how you build a 2D game on psxsplash, and how you add HUD elements, markers and effects to a 3D one.
Sprites are cheap: they are flat textured quads with no lighting, no GTE transform and no depth sort against the world. The engine keeps a pool of 128 of them.
Creating a sprite sheet¶
Right-click in the Project window: Create > PSXSplash > Sprite Sheet.
This makes a PSXSpriteSheet asset.
| Field | What it means |
|---|---|
| Sheet Name | the name Lua uses. Max 24 characters. |
| Source Texture | the atlas. Quantized and packed into VRAM alongside UI and 3D textures. |
| Bit Depth | 4-bit is cheapest and suits flat 2D art. |
| Cell Width / Height | one frame. The texture's dimensions should be whole multiples of these. |
Cells are numbered left to right, then top to bottom, starting at 0.
A sprite sheet is a cutout texture
Sprites are exported with alpha cutout on, so transparent pixels stay transparent. A 4-bit sheet has fifteen usable colours, not sixteen: entry 0 is reserved for transparency. A sixteenth colour makes the quantizer start inventing dithered approximations of art that was drawn to be exact.
Animations¶
Each sheet carries a list of animations defined over its own cells:
| Field | What it means |
|---|---|
| Animation Name | the name Lua uses. Max 24 characters. |
| First Frame | the cell the run starts at |
| Frame Count | how many consecutive cells it covers |
| Frame Duration | vsync frames each cell is held. At 60Hz, 6 gives 10fps |
| Loop | restart at the first frame, or hold the last |
Animations advance on a real-time accumulator, not once per rendered frame, so a walk cycle plays at the same speed whatever the frame rate is doing.
Adding sprites to a scene¶
Add a PSX > PSX Sprite Sheet Ref component (PSXSprite) to any GameObject in
the scene, one per sheet, and assign the sheet. Only referenced sheets are packed
into that scene's splashpack.
Sprite instances are not authored. That is the opposite of the UI, and deliberate: a game spawns and destroys sprites as players join, bullets fire and props die, and none of that is known at export time. What has to be authored is the data, because that is what must be resident in VRAM.
So the sprites themselves are created at runtime from Lua:
function onSceneCreationEnd()
local id = Sprite.Create("crew")
Sprite.PlayAnim(id, "idle")
Sprite.SetPos(id, 160, 120)
end
Binding a sprite to an actor¶
The usual pattern for a character: create the sprite once, bind it, and never touch its position again.
The sprite follows the actor every frame with no per-frame Lua. Plane 0 is the
screen plane, where the actor's X/Z is the 2D ground plane and Y (height) is
ignored - a jumping actor should not slide up the screen. The offsets shift the
sprite from that point, usually up and left so it is centred on the actor's feet.
Layers and the view offset¶
Layer decides draw order. Within a layer, order falls to creation order, so create backgrounds before foregrounds and you will rarely need to think about it.
For a scrolling 2D game, move the world rather than every sprite:
Sprite.SetViewOffset(camX, camY) -- shifts every sprite...
Sprite.SetIgnoreViewOffset(hudId, true) -- ...except the pinned ones
Facing¶
For a character with directional art, let the engine pick the animation:
Eight animations starting at walk, chosen from the actor's yaw. The current
frame and timer carry across a switch, so a character turning while walking does
not stutter back to frame 0.
Limits and pitfalls¶
| Limit | Value |
|---|---|
| Sheets per scene | 16 |
| Animations per scene | 64 (across all sheets) |
| Live sprites | 128 |
| Sheet and animation name | 24 characters |
The first two are checked at export and reported with the offending sheet named, so they fail in Unity rather than as an assert on the console.
The pool is 128 sprites, and failure is silent
Sprite.Create returns -1 when the pool is full, and every other Sprite.*
call ignores an invalid id without complaining. Check the id once at creation.
Create your sprites at scene start and re-frame them rather than creating and
destroying per frame.
There is no per-sprite alpha
Sprite.SetColor tints, it cannot fade. A "dimmed" overlay drawn as a
translucent rectangle will simply be opaque. To darken part of the screen, draw
a mask with a hole cut in it.
Sheet and animation names are checked at runtime, not at build
Sprite.SheetIndex and Sprite.AnimIndex return -1 for a name the scene does
not have, and Sprite.PlayAnim on a bad name does nothing at all - the sprite
keeps its previous animation. Resolve names once at scene start and log the
failures; a typo otherwise reads as a character that slides around in an idle
pose.
See also¶
- Tilemaps - a tileset is a sprite sheet
SpriteAPI reference