Skip to content

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

  1. FaceController is the root MonoBehaviour. It owns references to left/right EyeController components, a mouth material, and eyelid rest poses.
  2. Each frame, FaceController.Update() processes active transitions (eyelid and pupil), updates both eyes, and pushes mouth shape/color to the mouth material.
  3. 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.
  4. 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:

ModeLeft EyeRight Eye
LeftOnlyUses eyelidLeftUnchanged
RightOnlyUnchangedUses eyelidRight
BothLeftUses eyelidLeftMirrors eyelidLeft
BothSeparateUses eyelidLeftUses 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 MeshRenderer material array is indexed by configurable slot numbers (body, mouth, eye left, eye right), so the system adapts to different character mesh layouts.
  • ExecuteInEditMode. FaceController updates in the editor so you get live preview of expressions and eyelid shapes without entering Play mode.
  • Managed lifecycle. EyeController and FaceController use ManagedEnable() / ManagedUpdate() rather than Unity's OnEnable() / 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

  1. Add kids.kapish.characters to your Unity project via the Package Manager.
  2. Your character mesh needs a MeshRenderer with at least four material slots (body, mouth, eye right, eye left). The slot indices are configurable on the FaceController.

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 EyeController components.
  • 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:

ShapePhonemeDescription
AMBPLips together
BetcRelaxed consonant
CEWide smile
DAIOpen mouth
EORound lips
FUTight round
GFVTeeth on lip
HLTongue up
XRestNeutral

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. FaceController runs a blink coroutine in Play mode with randomized intervals. No setup needed.
  • Live preview works in Edit mode. FaceController is [ExecuteInEditMode], so expression and eyelid changes are visible without entering Play mode.
  • Expressions are assets. Create a library of FaceExpression ScriptableObjects and reference them from dialogue systems, animation events, or state machines.

Package Contents

kids.kapish.characters.controllers

Package Info

FieldValue
Namekids.kapish.characters.controllers
Display NameCarrot.Characters.Controllers
Version0.1.0
Unity6000.0+
LicenseMIT
NamespaceCarrot.Characters.Controllers
Dependencieskids.kapish 0.1.0, kids.kapish.input 0.1.0, kids.kapish.signals 0.1.0

Assemblies

AssemblyNamespacePlatformReferences
Carrot.Characters.ControllersCarrot.Characters.ControllersAllCarrot
Carrot.Characters.Controllers.EditorCarrot.Characters.Controllers.EditorEditorCarrot.Characters.Controllers
Carrot.Characters.Controllers.FirstPersonCarrot.Characters.Controllers.FirstPersonAllCarrot, Carrot.Characters.Controllers, Carrot.Input, Unity.InputSystem
Carrot.Characters.Controllers.FirstPerson.EditorCarrot.Characters.Controllers.FirstPerson.EditorEditorCarrot, Carrot.Editor, Carrot.Characters.Controllers, Carrot.Characters.Controllers.Editor, Carrot.Characters.Controllers.FirstPerson

Source Files

Core / Runtime

FileTypeDescription
Core/Runtime/Attributes/ModuleOrderAttribute.csAttributeModuleOrderAttribute -- class-level attribute specifying execution order for controller modules. Has UpdateOrder, FixedOrder, and LateOrder integer properties. Applied to FirstPersonModule subclasses.
Core/Runtime/Interfaces/IPlayerTargetable.csInterfaceIPlayerTargetable -- contract for objects the player can target at range. Provides GetDisplayName(), GetDescription(), OnTargetEnter(), and OnTargetExit().

Core / Editor

FileTypeDescription
Core/Editor/Carrot.Characters.Controllers.Editor.asmdefAssembly DefinitionEditor-only assembly referencing Carrot.Characters.Controllers.

FirstPerson / Runtime

FileTypeDescription
FirstPerson/Runtime/FirstPersonRig.csClassFirstPersonRig -- 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.csAbstract ClassFirstPersonModule -- 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.csClass + InterfaceFirstPersonInputAdapter -- 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.csClassFirstPersonCapsule -- 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.csClassFirstPersonLook -- 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.csClassFirstPersonMotor -- 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.csClassFirstPersonGravity -- 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.csClassFirstPersonGrounding -- 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.csClassFirstPersonJump -- 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.csClassFirstPersonCrouch -- 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.csClassFirstPersonSprint -- sprint input handler. Sets SpeedMultiplier (default 1.55x) while sprint is held.
FirstPerson/Runtime/Core/FirstPersonCameraStack.csClassFirstPersonCameraStack -- 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.csClassFirstPersonAudioSource -- ensures an AudioSource child on the rig with spatial audio defaults. Exposes PlayOneShot().
FirstPerson/Runtime/Interaction/FirstPersonInteraction.csClassFirstPersonInteraction -- 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.csClassFirstPersonReticle -- reticle origin point. Ensures a child transform under CameraPitchOffset for raycast/interaction origins. Exposes CurrentRay.
FirstPerson/Runtime/Interaction/FirstPersonTargeting.csClassFirstPersonTargeting -- 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.csClassFirstPersonCinematicLook -- 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.csInterfaceIPlayerInteractable -- contract for interactive objects. Defines OnHoverEnter(), OnHoverExit(), OnInteract(FirstPersonRig), and optional GetBasePriority().
FirstPerson/Runtime/Interfaces/IPlayerDiscoverable.csInterfaceIPlayerDiscoverable -- contract for objects discovered by sustained player gaze. Defines OnDiscovered(FirstPersonRig).

FirstPerson / Editor

FileTypeDescription
FirstPerson/Editor/FirstPersonRigEditor.csClassFirstPersonRigEditor -- 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.csStatic ClassFirstPersonDebugDrawGizmos -- scene gizmo drawing for capsule, foot marker, ground normal arrow, and look direction cone. Draws for both FirstPersonRig and FirstPersonCapsule selection.

Carrot