Character Designer
Character looks are parts on one shared skeleton — a body, outfits, faces, hair — toggled per player. Adding a part category doesn't take code: naming the mesh is registering it. This page is also the contract your own parts follow so they show up equipped and categorized correctly.
How it works
The character model is a single rigged file containing multiple skinned meshes on one armature. The catalog is built by scanning those mesh names: the first token is the category, an optional gender token restricts a part, the rest names the variant. Unknown categories appear automatically; there is no registration step.
Switching looks toggles renderers on the one instance, so every part follows the animated skeleton by construction — no bone re-binding, no per-part rigs. Worn equipment (from the Inventory module) attaches separately to skeleton bones and layers on top.
The Designer window
Tools → Party Core Kit → Character Designer opens the editor front-end: a live, draggable preview of the assembled character on the left, and a tab per category (Body, Hair, Shirt, …) across the top. Each tab lists one tile per part— the tile's thumbnail shows the puppet wearing that specific part, and its Equip/Unequip button toggles it, with the equipped part marked by an accent frame. Exactly one part per category is worn at a time; equipping another simply swaps it.
The left column also holds the Male/Female toggle (picks re-map to the matching slot instead of resetting), Randomize, Rescan Assets (re-reads the folders below after you add files) and Save Prefab, which writes the character prefab the player prefab points at — the selection is stored on the prefab's CharacterCustomizer for runtime re-customization.
The naming contract: mesh names are the catalog
In the combined-pack workflow (the intended one), every skinned mesh object inside the pack FBX names itself into the catalog:
// Mesh object names inside the pack FBX -- this IS the catalog.
// Pattern: <Category>[-<Male|Female>][-<Variant>] ('-' or '_')
Body // category "Body", unisex, variant "Default"
Shirt-Simple // category "Shirt", unisex, variant "Simple"
Hair-Female-Ponytail // category "Hair", shown for Female characters only
Glasses-Round // NEW category "Glasses" -- its tab just appears- Category — first token; becomes a tab in the designer window. Any new token is a new category, no code change.
- Gender — an optional
Male/Femaletoken anywhere after the category. Gendered parts only appear for that body; everything else is unisex. - Variant— the remaining tokens become the tile's display name (
Hair-Female-Ponytailshows as "Ponytail"); with none it reads "Default".
The folder contract: textures, materials and animations
The Designer folder is scanned by convention — the folder structure is the data entry UI. Materials are generated for you: each part folder's texture set becomes a URP Lit material (the plain .png is the base color, a _normal suffix is wired as the normal map).
Character/Designer/ // the scan root
Body/
Chibi Pack/ // the combined pack lives in the Body folder
Chibi Pack.fbx // one armature, every part a skinned mesh inside
Texture.png // base color -- becomes the pack's URP material
Texture_normal.png // "_normal" suffix -> wired as normal map
Animations/
Idle.fbx // one clip per file, looped automatically
Run.fbx
Jump.fbx // one-shot keywords (jump, land, ...) stay unlooped
Shirt/
Simple/ // texture-only folder: no FBX, just a texture set
Simple.png // -> generated material for the pack's Shirt meshes
Hair/
Male/ // a Male/Female segment sets the part's gender --
Simple Hair/ // it is NOT part of the category
Simple Hair.fbx // standalone part (fallback workflow, static mesh)
Simple Hair.png- Texture-only folders dress pack parts. A folder with textures but no FBX is a material source: its category (matched singular/plural-insensitively, so a
Shirtmesh finds aShirts/folder too) and gender decide which pack meshes it skins. Pack parts without their own folder fall back to the pack's shared texture atlas. - Animations ride with the body. Clip FBXs go in an
Animations/subfolder next to the pack (one clip per file). Locomotion clips are auto-looped; names containing one-shot keywords (jump, land, punch, …) stay unlooped. - Standalone parts still work. A folder with its own FBX is a self-contained part — the fallback for quick tests. Static meshes attach in place but do not follow the animated skeleton; production parts belong in the pack.
Adding your own part
Model it on the shared armature
In your DCC tool, parent and skin the new mesh to the pack's one armature — same file, same skeleton, weights painted once.Name the mesh object by the convention
<Category>[-<Male|Female>][-<Variant>]— e.g.Hat-Wizard. A new category token creates its own tab automatically.Give it a look (optional)
Add a matching texture folder (e.g.Designer/Hat/Wizard/Wizard.png) for a dedicated material — or skip it and inherit the pack's atlas.Re-export and Rescan
Overwrite the pack FBX, then hit Rescan Assets in the designer window. The part appears as a tile under its tab, wearing-preview and Equip button included.
Two category rules to know: Body is the one required category — a character cannot exist without it, so the equipped body can be swapped but never unequipped — and tabs order Body, Face and Hair first, then everything else alphabetically.
The two components
A CharacterPartCatalog asset is what the scan produces: the list of categories in display order, and every part it found. A CharacterCustomizer on the character reads that catalog and assembles the look.
| Field | Type | What it does |
|---|---|---|
catalog | CharacterPartCatalog | The parts this character can wear. Produced by the Designer window's scan, not authored by hand. |
attachRoot | Transform | Parts are instantiated under this transform. Defaults to the customizer's own GameObject. |
animatorController | RuntimeAnimatorController | Assigned to the character's Animator after every rebuild — the movement controller, carrying the Mode / Hor / Vert / State / IsJump / Speed parameter contract. |
selection | CharacterSelection | The current look: a gender plus one entry per category, each naming a part id and whether it is shown. |
And on each part in the catalog:
| Field | Type | Default | What it does |
|---|---|---|---|
id | string | — | Unique, stable id — the part's folder path relative to the Designer root, e.g. "Hair/Male/Simple Hair". Selections reference parts by this id, so it survives a catalog rescan. |
displayName | string | — | Shown in the designer UI — the part's folder name. |
categoryId | string | — | Category this part belongs to, e.g. "Body", "Face", "Clothes/Shirts". |
gender | PartGender | Unisex | Restricts the part to one gender, or leaves it available to both. Unisex parts appear in every list. |
model | GameObject | — | The part's mesh. In combined-pack mode this points at the whole pack instead, and rendererName picks the mesh out of it. |
material | Material | — | The material applied to this part. |
rendererName | string | — | Combined-pack mode: the name of this part's SkinnedMeshRenderer inside the shared multi-mesh model. Empty means the part is its own standalone model. |
animationSources | List<GameObject> | — | FBX files carrying animation clips for this part's rig. Only meaningful on rigged parts — in practice, the body. |
Driving it from code
The Designer window is one driver of this API, not the only one. A character creator, a random-look button on a lobby seat, or a cheat that swaps a hat all go through the same small surface:
using Curitor.PartyCoreKit.Character.Customization;
using UnityEngine;
public class LookControls : MonoBehaviour
{
[SerializeField] private CharacterCustomizer customizer;
private void Awake()
{
// Anything that cares about the look -- a preview, a save
// participant -- listens rather than polls.
customizer.SelectionChanged += () => Debug.Log("Look changed.");
}
private void NextHair() => customizer.CyclePart("Hair", +1);
private void PrevHair() => customizer.CyclePart("Hair", -1);
private void PickShirt(string partId) => customizer.SetPart("Clothes/Shirts", partId);
private void BareHeaded() => customizer.SetPartEnabled("Hair", false);
private void Surprise() => customizer.Randomize();
// Restoring a whole look at once -- this is what the save
// participant calls when a character spawns.
private void Restore(CharacterSelection saved) => customizer.ApplySelection(saved);
}The rest of the surface: SetGender, SetCatalog, SetAnimatorController, and Rebuild for the case you have changed the selection object directly and want the character reassembled. CharacterSelection.CreateDefault builds a valid starting look from a catalog.