Appearance Customization
The NPCAppearance system allows you to customize your NPC's visual appearance, including physical features, clothing, and accessories.
Table of Contents
- Overview
- Basic Appearance Setup
- Customization Fields
- Face Layers
- Body Layers
- Accessory Layers
- Random Appearance Generation
- Advanced Customization
- Best Practices
Overview
The appearance system uses a builder pattern to configure various aspects of the NPC's visual appearance:
protected override void OnCreated()
{
base.OnCreated();
Appearance
.Set<CustomizationFields.Gender>(0.5f)
.Set<CustomizationFields.Height>(1.1f)
.Set<CustomizationFields.SkinColor>(new Color32(150, 120, 95, 255))
.WithFaceLayer<FaceLayerFields.Eyes>("Avatar/Layers/Face/Eyes_Happy", Color.black)
.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/T-Shirt", Color.red)
.Build(); // Generates mugshot and applies appearance
}
Far-distance impostor textures are prefab defaults, not runtime appearance changes. Configure them in ConfigurePrefab with builder.WithAppearanceDefaults(...) and avatar.WithImpostor(...) or avatar.WithImpostorTexture(...); then use this runtime Appearance builder for the NPC's live avatar, mugshot, clothing, and layer changes.
Basic Appearance Setup
Setting Up Appearance
Appearance customization is done in the OnCreated method:
protected override void OnCreated()
{
base.OnCreated();
// Configure appearance
Appearance
.Set<CustomizationFields.Gender>(0.0f) // 0=male, 1=female
.Set<CustomizationFields.Height>(1.0f)
.Set<CustomizationFields.Weight>(0.35f)
.Set<CustomizationFields.SkinColor>(new Color32(150, 120, 95, 255))
.Build(); // Always call Build() at the end
}
Building the Appearance
Important: Always call Build() at the end of your appearance configuration. This:
- Generates the NPC's mugshot
- Applies the appearance to the avatar
- Finalizes the visual changes
Appearance
.Set<CustomizationFields.Gender>(0.5f)
.Set<CustomizationFields.Height>(1.0f)
.Build(); // Required!
Customization Fields
Basic Physical Properties
// Gender (0.0 = male, 1.0 = female)
Appearance.Set<CustomizationFields.Gender>(0.0f);
// Height (0.0 = short, 1.0 = tall)
Appearance.Set<CustomizationFields.Height>(1.0f);
// Weight (0.0 = thin, 1.0 = heavy)
Appearance.Set<CustomizationFields.Weight>(0.35f);
// Skin color
Appearance.Set<CustomizationFields.SkinColor>(new Color32(150, 120, 95, 255));
// Eye color
Appearance.Set<CustomizationFields.EyeBallTint>(Color.blue);
// Hair color
Appearance.Set<CustomizationFields.HairColor>(Color.brown);
// Hair style (path to hair asset)
Appearance.Set<CustomizationFields.HairStyle>("Avatar/Hair/Spiky/Spiky");
Facial Features
// Eye properties
Appearance.Set<CustomizationFields.PupilDilation>(0.66f);
Appearance.Set<CustomizationFields.EyeBallTint>(Color.white);
// Eyebrow properties
Appearance.Set<CustomizationFields.EyebrowScale>(0.85f);
Appearance.Set<CustomizationFields.EyebrowThickness>(0.6f);
Appearance.Set<CustomizationFields.EyebrowRestingHeight>(0.1f);
Appearance.Set<CustomizationFields.EyebrowRestingAngle>(0.05f);
// Eyelid states (left, right)
Appearance.Set<CustomizationFields.EyeLidRestingStateLeft>((0.5f, 0.5f));
Appearance.Set<CustomizationFields.EyeLidRestingStateRight>((0.5f, 0.5f));
Color Values
Use appropriate color types for different properties:
// Color32 for skin (RGBA values 0-255)
Appearance.Set<CustomizationFields.SkinColor>(new Color32(150, 120, 95, 255));
// Color for eyes, hair, etc. (RGB values 0-1)
Appearance.Set<CustomizationFields.EyeBallTint>(Color.blue);
Appearance.Set<CustomizationFields.HairColor>(new Color(0.1f, 0.1f, 0.1f));
Face Layers
Face layers control facial features and expressions:
// Eyes with expression and color
Appearance.WithFaceLayer<FaceLayerFields.Eyes>("Avatar/Layers/Face/Face_Agitated", Color.black);
// Facial hair
Appearance.WithFaceLayer<FaceLayerFields.FacialHair>("Avatar/Layers/Face/Beard", Color.brown);
// Face expressions
Appearance.WithFaceLayer<FaceLayerFields.Face>("Avatar/Layers/Face/Face_Happy", Color.white);
Available Face Layer Fields
FaceLayerFields.Eyes: Eye expressions and stylesFaceLayerFields.FacialHair: Beards, mustaches, etc.FaceLayerFields.Face: Face expressions and overlays
Face Layer Paths
Common face layer paths:
// Eye expressions
"Avatar/Layers/Face/Face_Agitated"
"Avatar/Layers/Face/Face_Happy"
"Avatar/Layers/Face/Face_Neutral"
"Avatar/Layers/Face/Face_Sad"
// Facial hair
"Avatar/Layers/Face/Beard"
"Avatar/Layers/Face/Mustache"
"Avatar/Layers/Face/Goatee"
Body Layers
Body layers control clothing and body appearance:
// Shirts
Appearance.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/T-Shirt", Color.red);
Appearance.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/Shirt", Color.white);
Appearance.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/RolledButtonUp", Color.blue);
// Pants
Appearance.WithBodyLayer<BodyLayerFields.Pants>("Avatar/Layers/Bottom/Jeans", Color.blue);
Appearance.WithBodyLayer<BodyLayerFields.Pants>("Avatar/Layers/Bottom/Jorts", new Color(0.15f, 0.2f, 0.3f));
// Undergarments
Appearance.WithBodyLayer<BodyLayerFields.Undergarments>("Avatar/Layers/Underwear/Boxers", Color.white);
Available Body Layer Fields
BodyLayerFields.Shirts: Tops, shirts, jacketsBodyLayerFields.Pants: Bottoms, pants, shortsBodyLayerFields.Undergarments: Underwear, bras, etc.
Body Layer Paths
Common body layer paths:
// Tops
"Avatar/Layers/Top/T-Shirt"
"Avatar/Layers/Top/Shirt"
"Avatar/Layers/Top/RolledButtonUp"
"Avatar/Layers/Top/Hoodie"
// Bottoms
"Avatar/Layers/Bottom/Jeans"
"Avatar/Layers/Bottom/Jorts"
"Avatar/Layers/Bottom/Shorts"
"Avatar/Layers/Bottom/Sweatpants"
Accessory Layers
Accessory layers control shoes, hats, and other accessories:
// Shoes
Appearance.WithAccessoryLayer<AccessoryFields.Feet>("Avatar/Accessories/Feet/Sneakers/Sneakers", Color.red);
Appearance.WithAccessoryLayer<AccessoryFields.Feet>("Avatar/Accessories/Feet/Boots", Color.brown);
// Hats
Appearance.WithAccessoryLayer<AccessoryFields.Head>("Avatar/Accessories/Head/BaseballCap", Color.blue);
// Jewelry
Appearance.WithAccessoryLayer<AccessoryFields.Neck>("Avatar/Accessories/Neck/Necklace", Color.gold);
Available Accessory Fields
AccessoryFields.Feet: Shoes, boots, sneakersAccessoryFields.Head: Hats, caps, helmetsAccessoryFields.Neck: Necklaces, scarvesAccessoryFields.Hands: Gloves, ringsAccessoryFields.Waist: Belts, holsters
Accessory Paths
Common accessory paths:
// Shoes
"Avatar/Accessories/Feet/Sneakers/Sneakers"
"Avatar/Accessories/Feet/Boots"
"Avatar/Accessories/Feet/Sandals"
// Hats
"Avatar/Accessories/Head/BaseballCap"
"Avatar/Accessories/Head/Beanie"
"Avatar/Accessories/Head/Hat"
Random Appearance Generation
Generate Random Appearance
For quick testing or random NPCs:
protected override void OnCreated()
{
base.OnCreated();
// Generate completely random appearance
Appearance.GenerateRandomAppearance();
Appearance.Build();
}
Partial Random Generation
You can set some properties and randomize others:
Appearance
.Set<CustomizationFields.Gender>(0.0f) // Force male
.Set<CustomizationFields.Height>(1.0f) // Force height
.GenerateRandomAppearance() // Randomize everything else
.Build();
Advanced Customization
Complex Appearance Example
Here's a complete example with all customization options:
protected override void OnCreated()
{
base.OnCreated();
Appearance
// Core biometrics
.Set<CustomizationFields.Gender>(0.0f) // Male
.Set<CustomizationFields.Height>(1.0f)
.Set<CustomizationFields.Weight>(0.35f)
.Set<CustomizationFields.SkinColor>(new Color32(150, 120, 95, 255))
// Eye properties
.Set<CustomizationFields.EyeBallTint>(Color.white)
.Set<CustomizationFields.PupilDilation>(0.66f)
// Eyebrow properties
.Set<CustomizationFields.EyebrowScale>(0.85f)
.Set<CustomizationFields.EyebrowThickness>(0.6f)
.Set<CustomizationFields.EyebrowRestingHeight>(0.1f)
.Set<CustomizationFields.EyebrowRestingAngle>(0.05f)
// Eyelid states
.Set<CustomizationFields.EyeLidRestingStateLeft>((0.5f, 0.5f))
.Set<CustomizationFields.EyeLidRestingStateRight>((0.5f, 0.5f))
// Hair
.Set<CustomizationFields.HairColor>(new Color(0.1f, 0.1f, 0.1f))
.Set<CustomizationFields.HairStyle>("Avatar/Hair/Spiky/Spiky")
// Face layers
.WithFaceLayer<FaceLayerFields.Eyes>("Avatar/Layers/Face/Face_Agitated", Color.black)
// Body layers
.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/T-Shirt", Color.red)
.WithBodyLayer<BodyLayerFields.Pants>("Avatar/Layers/Bottom/Jeans", new Color(0.15f, 0.2f, 0.3f))
// Accessories
.WithAccessoryLayer<AccessoryFields.Feet>("Avatar/Accessories/Feet/Sneakers/Sneakers", Color.red)
.Build(); // Always call Build() at the end
}
Gender-Specific Customization
You can customize based on gender:
protected override void OnCreated()
{
base.OnCreated();
float gender = 0.5f; // 0 = male, 1 = female
Appearance
.Set<CustomizationFields.Gender>(gender);
if (gender < 0.5f) // Male
{
Appearance
.Set<CustomizationFields.HairStyle>("Avatar/Hair/Spiky/Spiky")
.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/T-Shirt", Color.blue);
}
else // Female
{
Appearance
.Set<CustomizationFields.HairStyle>("Avatar/Hair/Long/Long")
.WithBodyLayer<BodyLayerFields.Shirts>("Avatar/Layers/Top/Blouse", Color.pink);
}
Appearance.Build();
}
Best Practices
Best Practices for Appearance Customization
Appearance configuration is a dynamic process and must be handled during the runtime initialization phase of the NPC.
Do's
- Configure Appearance in
OnCreated(): Always apply appearance changes (using theAppearanceproperty) within theprotected override void OnCreated()method of your custom NPC class. - Finalize Configuration with
Build(): TheBuild()method is mandatory to apply all pending changes to the NPC's visual state. Always call it once after defining all customization fields. - Provide Error Fallbacks: Use
GenerateRandomAppearance()within acatchblock to ensure the NPC is visually stable even if customization parameters fail to apply. - Use Appropriate Value Types: Ensure you pass float or Color types for customization values, as required by the specific
CustomizationFieldsenum.
Don'ts
- Don't Configure in
ConfigurePrefab(): Never attempt to set appearance or dynamic visual properties within the staticConfigurePrefab()method. This method is reserved for identity, schedule, and static defaults. - Don't Omit
Build(): Appearance changes will not take effect unlessBuild()is explicitly called. - Don't Use Invalid Assets/Paths: Ensure all asset bundle paths or direct asset references are validated before deployment.
Error Handling
Wrapping appearance configuration ensures robustness. If your custom configuration fails (e.g., due to bad parameters or missing assets), the NPC will fall back to a safe, random appearance.
protected override void OnCreated()
{
base.OnCreated();
try
{
// Define dynamic appearance here
Appearance
.Set<CustomizationFields.Gender>(0.0f)
.Set<CustomizationFields.Height>(1.0f)
.Build();
}
catch (Exception ex)
{
// Use MelonLogger to report failure
MelonLogger.Error($"Failed to set appearance for {FullName}: {ex.Message}");
// Fallback: Generate a random, safe appearance
Appearance.GenerateRandomAppearance();
Appearance.Build();
}
}
Complete Appearance Examples
If you want a working reference to copy/paste from, the S1API NPC Example Repository includes multiple NPCs with fully-specified WithAppearanceDefaults(...) blocks:
Next Steps
Now that you understand appearance customization, explore:
- Scheduling System - NPC movement and activities
- Dialogue System - Interactive conversations
- Runtime Management - NPC lifecycle and properties