Making a pet: the folder
A pet starts as a folder of sprite sheets, which tools/eggpack/eggpack.py checks and packs into one .egg file (egg-format.md). The layout is AnimData.xml plus one <Name>-Anim.png sheet per animation, the convention used by common sprite collections. pets/goose/ is a complete example.
mypet/
AnimData.xml the animations: names, frame size, how long each frame shows
Idle-Anim.png one sprite sheet per animation
Walk-Anim.png
...
pet.json optional: name, words about the pet, credit and licence, its own scene
meadow.png ... optional: scene pictures that pet.json names
python3 tools/eggpack/eggpack.py mypet --check # what each state will play, and any errors
python3 tools/eggpack/eggpack.py mypet -o ~/.nestbox/pets/mypet.egg # pack it; the simulator picks it up
The packer asks the engine which animations each state plays, so build it first, on any platform: cmake -S . -B build && cmake --build build (or point NESTBOX_BUILD_DIR at another build).
Sprite sheets
<Name>-Anim.png: frames left to right, one row per facing: 1 row (it faces you) or 8 rows in this order: down (toward you), down-right, right, up-right, up (away), up-left, left, down-left.- All frames of an animation are the same size, given in
AnimData.xml. Every frame of every animation shares one centre point, the middle of the frame. The device places that centre the same distance above the pet's feet in every animation, so frames line up without offsets. The feet are measured onIdle. - Alpha below 128 is transparent. At most 255 colours across the whole pet.
- On the 280×240 screen each sprite pixel is a 2×2 block, or
"scale"in pet.json (1 to 4) screen pixels. A pet 20–30 px tall (in sprite pixels) fits well.
AnimData.xml
<AnimData>
<Anims>
<Anim>
<Name>Walk</Name>
<FrameWidth>32</FrameWidth>
<FrameHeight>40</FrameHeight>
<Durations><Duration>8</Duration><Duration>8</Duration><Duration>8</Duration><Duration>8</Duration></Durations>
</Anim>
<Anim><Name>Strike</Name><CopyOf>Attack</CopyOf></Anim>
</Anims>
</AnimData>
- Durations are in 1/60 s ticks, one per frame, at most 255.
CopyOfreuses another animation's sheet.- Strikes may mark
RushFrame,HitFrameandReturnFrame(frame numbers). A state that loops "the strike" plays just those frames. Attack, Strike, Scratch, Kick and similar strikes also slide the pet forward between rush and return;"dash": ["MyLunge"]in pet.json names a different set.
What the pet must have
- **
IdleandWalk, with all 8 rows.** The pet walks in every direction on its floor. - Everything else is optional. Each state has a list of animation names and plays the first one the folder has; an animation named after the state itself (
Party-Anim.pngforparty) always comes first. The lists are in the state table in egg-format.md, and--checkprints what each state will play.
pet.json
{
"name": "Goose",
"credit": "NestBox",
"license": "CC BY 4.0",
"scale": 2,
"theme": "A white farm goose on a Swiss mountain farm: ...",
"notes": { "RearUp": "the goose hiss: neck low and forward, wings spread (grumpy)" },
"scene": {
"background": "meadow.png",
"night": "meadow_night.png",
"scale": 2,
"keep": [50, 50, 136, 119],
"floor": [[52, 54], [138, 54], [138, 69], [112, 71], [94, 88], [77, 111], [80, 118], [4, 118], [4, 94]],
"home": [72, 102],
"workspot": { "at": [74, 59], "on": "workspot_on.png", "off": "workspot_off.png", "night": "workspot_night.png" }
}
}
(The goose's floor has more points; this is a shortened copy of pets/goose/pet.json.)
name,theme,personality,style,about,color,credit,license,notes: the words described in egg-format.md. Agents see them inGET /agents.scale: screen pixels per sprite pixel, 1 to 4; without it, the screen's own (2 on the LCD).dash: the strikes that slide the pet forward (see AnimData.xml above).private:truefor art that may not be redistributed. The packer marks the egg, and the firmware build leaves it out unlessNESTBOX_INCLUDE_PRIVATE=1.sceneis optional; without one the pet lives in a plain room with a small desk sized to it.background, and optionallymorning,afternoon,eveningandnight: the scene's pictures, 280×240, or smaller with"scale"in the scene (140×120 at 2). The device shows the one for the time of day; the fallbacks are in egg-format.md.- Every position in the scene (
floor,home,keep,workspot,target,swish) is in the pictures' own pixels, which are screen pixels only at scene scale 1. The goose's 140×120 meadow at scale 2 has its home at[72, 102], which is (144, 204) on the screen. floor: a polygon where the pet's feet may stand. Leave room for the body, which is drawn above and around the feet.home: where it works and sleeps.keep: the part of the scene to keep in view on a smaller screen.workspot: its desk, always in the scene:onwhile it works,offotherwise,nightwhile it sleeps.atis its top-left corner. The pet walks around it and goes behind it; its footprint is the bottom of theonpicture.
The hoop shot (optional)
A pet can shoot a ball at something in its scene while the user types fast, instead of playing WorkFast. It needs:
Shoot-Anim.pngwith 8 rows and its release marked asHitFrame;Ball-Anim.pngwith 1 row (the ball, centred in its frame); optionallyCatch-Anim.pngwith 1 row.- In the scene,
"target": [x, y], the point the ball goes through (a hoop's rim, say), and optionally"swish": {"at": [x, y], "day": "swish.png", "night": "swish_night.png"}, a picture drawn over the scene while the ball drops through.
How the device flies the ball is in egg-format.md. The goose has no hoop shot.
Checking and loading
--checklists what every state will play and reports errors: missing Idle or Walk, wrong sheet sizes, row counts other than 1 or 8, too many colours, scene sizes, a home outside the floor.-o FILEpacks the folder (--nameoverrides pet.json's name). A folder with errors is not packed.{"pet": "mypet"}to the device brings it out.
Art from a third-party sprite collection keeps its own licence. Check it before you share an egg made from it.