Appearance
kids.kapish.characters
unity
Procedural facial animation, expressions, and lipsync for Unity characters.
Architecture
The package provides a shader-driven procedural face system. Instead of blend shapes or bone rigs, facial features are rendered entirely via shader uniforms -- eyelids use bezier curves, pupils are positioned in UV space, and mouth shapes are selected from a sprite sheet. This keeps the mesh simple (a single MeshRenderer with multiple material slots) while allowing smooth, data-driven animation.
Core Flow
FaceControlleris the root MonoBehaviour. It owns references to left/rightEyeControllercomponents, a mouth material, and eyelid rest poses.- Each frame,
FaceController.Update()processes active transitions (eyelid and pupil), updates both eyes, and pushes mouth shape/color to the mouth material. EyeController.ManagedUpdate()writes sclera, iris, pupil, and eyelid parameters to the eye shader material each frame. Pupil position is clamped to prevent overflow past the iris edge.- Eyelid shape is defined by
EyelidData-- four bezier control heights with handle vectors. The shader evaluates bezier curves per-pixel for smooth eyelid edges.
Expression System
Expressions are ScriptableObject assets (FaceExpression) that store target eyelid shapes. FaceControlMode determines symmetry:
| Mode | Left Eye | Right Eye |
|---|---|---|
LeftOnly | Uses eyelidLeft | Unchanged |
RightOnly | Unchanged | Uses eyelidRight |
BothLeft | Uses eyelidLeft | Mirrors eyelidLeft |
BothSeparate | Uses eyelidLeft | Uses eyelidRight |
Transitions are driven by EyelidTransition, which interpolates from current to target EyelidData over a configurable duration with optional delay. The interpolation respects the target's AnimationCurve for easing.
Lipsync
MouthShape maps Preston-Blair phonemes to integer indices that select columns in the mouth sprite sheet texture. Call FaceController.ChangeMouth(MouthShape.A) to set a viseme -- the mouth shader updates immediately.
Blinking
FaceController runs a coroutine that automatically blinks both eyes at random intervals (4.5--5.5 seconds). The blink is a fast 0.14-second close/open cycle using AnimationCurve.EaseInOut for natural motion.
Pupil Gaze
PupilTransition smoothly moves pupil position over time using an AnimationCurve. Pupil overflow is clamped so the pupil never clips the iris edge, and left-eye positions are automatically mirrored.
Assembly
- Assembly name:
Carrot.Characters - Root namespace:
Carrot.Characters - References:
Carrot(base package)
Key Design Decisions
- Shader-driven rendering. No blend shapes or skeletal animation. All facial features are controlled via material uniforms, keeping the mesh topology trivial.
- Material slots, not submeshes. The
MeshRenderermaterial array is indexed by configurable slot numbers (body, mouth, eye left, eye right), so the system adapts to different character mesh layouts. ExecuteInEditMode.FaceControllerupdates in the editor so you get live preview of expressions and eyelid shapes without entering Play mode.- Managed lifecycle.
EyeControllerandFaceControlleruseManagedEnable()/ManagedUpdate()rather than Unity'sOnEnable()/Update()to keep initialization order explicit. - ScriptableObject expressions. Expressions are assets, not inline data. This encourages reuse across characters and enables expression libraries.
Using kids.kapish.characters
Procedural facial animation, expressions, and lipsync for Unity characters.
Setup
- Add
kids.kapish.charactersto your Unity project via the Package Manager. - Your character mesh needs a
MeshRendererwith at least four material slots (body, mouth, eye right, eye left). The slot indices are configurable on theFaceController.
Common Patterns
1. Wire up a face
Add FaceController to your character's face GameObject. Assign:
- Mesh Renderer -- the renderer with your character's face submeshes.
- Material Body -- the body/skin material.
- Shader Mouth / Texture Mouth -- the mouth shader and phoneme sprite sheet.
- Eye Left / Eye Right -- child GameObjects with
EyeControllercomponents. - Material Slots -- indices matching your mesh's submesh order.
csharp
using Carrot.Characters.Faces;
// FaceController is typically configured in the Inspector.
// Access it at runtime:
FaceController face = GetComponent<FaceController>();2. Create and apply expressions
Create expression assets via Create > Carrot > Characters > Face Expression in the Project window. Configure eyelid shapes and control mode in the Inspector.
csharp
using Carrot.Characters.Faces;
// Transition to an expression over 0.3 seconds
[SerializeField] private FaceExpression angryExpression;
face.ChangeExpression(angryExpression, 0.3f);
// Return to the rest expression over 0.5 seconds
face.ChangeExpressionToRest(0.5f);
// Change the rest pose itself (e.g. for a new character state)
[SerializeField] private FaceExpression sleepyRest;
face.SetRestExpression(sleepyRest);3. Expression control modes
When authoring a FaceExpression, the FaceControlMode determines symmetry:
- BothLeft (default) -- author the left eye only; the right eye mirrors it.
- BothSeparate -- author left and right independently for asymmetric expressions (winks, smirks).
- LeftOnly / RightOnly -- only affect one eye, leaving the other at its current state.
4. Lipsync with mouth shapes
Drive mouth visemes from your audio/lipsync system:
csharp
using Carrot.Characters.Faces;
// Set a Preston-Blair phoneme shape
face.ChangeMouth(MouthShape.A); // MBP (lips together)
face.ChangeMouth(MouthShape.D); // AI (wide open)
face.ChangeMouth(MouthShape.X); // Rest (neutral)The MouthShape enum maps to standard Preston-Blair phoneme positions:
| Shape | Phoneme | Description |
|---|---|---|
A | MBP | Lips together |
B | etc | Relaxed consonant |
C | E | Wide smile |
D | AI | Open mouth |
E | O | Round lips |
F | U | Tight round |
G | FV | Teeth on lip |
H | L | Tongue up |
X | Rest | Neutral |
5. Pupil gaze
Animate where the character is looking:
csharp
using Carrot.Characters.Faces;
// Smoothly shift gaze over 0.2 seconds
face.ChangePupilPosition(new Vector2(0.3f, 0.1f), 0.2f);
// Set pupil size and iris size directly
face.EyesPupilSize = 0.9f;
face.EyesIrisSize = 0.25f;Pupil position is normalized: (0, 0) is center, positive X is right, positive Y is up. Left-eye mirroring is handled automatically.
6. Configuring eye appearance
Eye colors and sizes are set on the FaceController and applied to both eyes:
csharp
using Carrot.Characters.Faces;
using UnityEngine;
FaceController face = GetComponent<FaceController>();
// These are typically set in the Inspector, but can be changed at runtime.Inspector fields include sclera color, pupil color, iris color, iris size, eyelid color, and pupil focus animation curve.
Tips
- Blinking is automatic.
FaceControllerruns a blink coroutine in Play mode with randomized intervals. No setup needed. - Live preview works in Edit mode.
FaceControlleris[ExecuteInEditMode], so expression and eyelid changes are visible without entering Play mode. - Expressions are assets. Create a library of
FaceExpressionScriptableObjects and reference them from dialogue systems, animation events, or state machines.
Package Contents
kids.kapish.characters.controllers
Package Info
| Field | Value |
|---|---|
| Name | kids.kapish.characters.controllers |
| Display Name | Carrot.Characters.Controllers |
| Version | 0.1.0 |
| Unity | 6000.0+ |
| License | MIT |
| Namespace | Carrot.Characters.Controllers |
| Dependencies | kids.kapish 0.1.0, kids.kapish.input 0.1.0, kids.kapish.signals 0.1.0 |
Assemblies
| Assembly | Namespace | Platform | References |
|---|---|---|---|
Carrot.Characters.Controllers | Carrot.Characters.Controllers | All | Carrot |
Carrot.Characters.Controllers.Editor | Carrot.Characters.Controllers.Editor | Editor | Carrot.Characters.Controllers |
Carrot.Characters.Controllers.FirstPerson | Carrot.Characters.Controllers.FirstPerson | All | Carrot, Carrot.Characters.Controllers, Carrot.Input, Unity.InputSystem |
Carrot.Characters.Controllers.FirstPerson.Editor | Carrot.Characters.Controllers.FirstPerson.Editor | Editor | Carrot, Carrot.Editor, Carrot.Characters.Controllers, Carrot.Characters.Controllers.Editor, Carrot.Characters.Controllers.FirstPerson |
Source Files
Core / Runtime
| File | Type | Description |
|---|---|---|
Core/Runtime/Attributes/ModuleOrderAttribute.cs | Attribute | ModuleOrderAttribute -- class-level attribute specifying execution order for controller modules. Has UpdateOrder, FixedOrder, and LateOrder integer properties. Applied to FirstPersonModule subclasses. |
Core/Runtime/Interfaces/IPlayerTargetable.cs | Interface | IPlayerTargetable -- contract for objects the player can target at range. Provides GetDisplayName(), GetDescription(), OnTargetEnter(), and OnTargetExit(). |
Core / Editor
| File | Type | Description |
|---|---|---|
Core/Editor/Carrot.Characters.Controllers.Editor.asmdef | Assembly Definition | Editor-only assembly referencing Carrot.Characters.Controllers. |
FirstPerson / Runtime
| File | Type | Description |
|---|---|---|
FirstPerson/Runtime/FirstPersonRig.cs | Class | FirstPersonRig -- central rig component. Requires CharacterController. Auto-builds hierarchy (CameraPivot/PitchOffset/CameraBob), discovers and caches modules, manages per-frame velocity accumulation, and dispatches Update/FixedUpdate/LateUpdate to sorted module lists. Provides AddVelocity(), AddExternalForce(), SetVerticalVelocity(), and module management API (EnsureModule<T>, GetModule<T>, RemoveModule<T>, RequireModule<T>). |
FirstPerson/Runtime/Modules/FirstPersonModule.cs | Abstract Class | FirstPersonModule -- base class for all rig modules. Extends CarrotBehaviour. Provides virtual hooks: OnModuleInitialize(), OnModuleEnabled(), OnModuleDisabled(), OnModuleUpdate(float), OnModuleFixedUpdate(float), OnModuleLateUpdate(float). Internal invoke methods guard on IsEnabled. |
FirstPerson/Runtime/Input/FirstPersonInputAdapter.cs | Class + Interface | FirstPersonInputAdapter -- adapts Unity Input System actions to IFirstPersonInputSource. Exposes Move, Look, JumpDown, SprintHeld, CrouchHeld, CrouchToggleDown, InteractDown. Includes optional fixed-safety edge buffering to prevent missed presses across frame boundaries. |
FirstPerson/Runtime/Physical/FirstPersonCapsule.cs | Class | FirstPersonCapsule -- manages the CharacterController capsule dimensions. Handles standing/crouch height transitions with configurable eye heights, resize speed, and head-block detection via binary-search CanFitHeight(). Drives camera pivot Y position. |
FirstPerson/Runtime/Physical/FirstPersonLook.cs | Class | FirstPersonLook -- mouse look module. Applies yaw to the rig root and pitch to the camera pivot with configurable sensitivity and pitch clamping (-85 to 85 degrees). |
FirstPerson/Runtime/Physical/FirstPersonMotor.cs | Class | FirstPersonMotor -- movement module. Reads input, calculates world-space movement direction, applies acceleration/deceleration with reduced air control, projects velocity onto ground plane, and contributes to the rig's velocity accumulator. Integrates sprint and crouch speed multipliers. |
FirstPerson/Runtime/Physical/FirstPersonGravity.cs | Class | FirstPersonGravity -- gravity and vertical velocity. Applies gravity when airborne, clears downward velocity on ground, clamps to terminal velocity. Exposes ApplyImpulse() for jump and launch pad systems. |
FirstPerson/Runtime/Physical/FirstPersonGrounding.cs | Class | FirstPersonGrounding -- ground detection using CharacterController.isGrounded plus SphereCast for detailed surface info. Tracks IsGrounded, IsWalkable, IsSupported, OnSteepSlope, ground normal/point/collider, and dispatches Landed / LeftGround signals (parameterless). |
FirstPerson/Runtime/Physical/FirstPersonJump.cs | Class | FirstPersonJump -- jump input handler with configurable force and jump buffer window. Applies upward impulse to FirstPersonGravity when grounded and buffered input is available. |
FirstPerson/Runtime/Physical/FirstPersonCrouch.cs | Class | FirstPersonCrouch -- crouch input handler supporting hold-to-crouch and toggle modes. Delegates capsule resize to FirstPersonCapsule.RequestCrouch(). Provides SpeedMultiplier for motor integration. |
FirstPerson/Runtime/Physical/FirstPersonSprint.cs | Class | FirstPersonSprint -- sprint input handler. Sets SpeedMultiplier (default 1.55x) while sprint is held. |
FirstPerson/Runtime/Core/FirstPersonCameraStack.cs | Class | FirstPersonCameraStack -- manages world and hands cameras under the CameraBob anchor. Configures FOV, clip planes, culling masks (ViewModel layer split), and provides additive FOV API with framerate-independent smoothing. Self-heals if cameras are deleted at runtime. |
FirstPerson/Runtime/Core/FirstPersonAudioSource.cs | Class | FirstPersonAudioSource -- ensures an AudioSource child on the rig with spatial audio defaults. Exposes PlayOneShot(). |
FirstPerson/Runtime/Interaction/FirstPersonInteraction.cs | Class | FirstPersonInteraction -- arms-reach interaction via SphereCast. Maintains a priority-sorted candidate list using distance and center-screen alignment scoring. Handles hover enter/exit and interact input dispatch to IPlayerInteractable. |
FirstPerson/Runtime/Interaction/FirstPersonReticle.cs | Class | FirstPersonReticle -- reticle origin point. Ensures a child transform under CameraPitchOffset for raycast/interaction origins. Exposes CurrentRay. |
FirstPerson/Runtime/Interaction/FirstPersonTargeting.cs | Class | FirstPersonTargeting -- long-range targeting via Raycast. Tracks IPlayerTargetable enter/exit, and optionally tracks IPlayerDiscoverable objects that require sustained focused gaze to discover. Dispatches Discovered (Signal<IPlayerDiscoverable>). |
FirstPerson/Runtime/Interaction/FirstPersonCinematicLook.cs | Class | FirstPersonCinematicLook -- takes over camera look for cinematic moments. Smoothly slerps to a target or direction with optional FOV zoom. Disables player look input when active; ReturnControl() restores it. |
FirstPerson/Runtime/Interfaces/IPlayerInteractable.cs | Interface | IPlayerInteractable -- contract for interactive objects. Defines OnHoverEnter(), OnHoverExit(), OnInteract(FirstPersonRig), and optional GetBasePriority(). |
FirstPerson/Runtime/Interfaces/IPlayerDiscoverable.cs | Interface | IPlayerDiscoverable -- contract for objects discovered by sustained player gaze. Defines OnDiscovered(FirstPersonRig). |
FirstPerson / Editor
| File | Type | Description |
|---|---|---|
FirstPerson/Editor/FirstPersonRigEditor.cs | Class | FirstPersonRigEditor -- custom inspector for FirstPersonRig. Extends CarrotBehaviourInspector to add a custom module management UI with add/remove dropdown for non-core module types. |
FirstPerson/Editor/FirstPersonDebugDrawGizmos.cs | Static Class | FirstPersonDebugDrawGizmos -- scene gizmo drawing for capsule, foot marker, ground normal arrow, and look direction cone. Draws for both FirstPersonRig and FirstPersonCapsule selection. |