Character System
Character System
Section titled “Character System”Comprehensive documentation for the Rent Earth character system using the GanzSe Low Poly Modular Character Pack. This document covers the Unity prefab structure, appearance customization, equipment slots, and data flow.
Architecture Overview
Section titled “Architecture Overview”┌─────────────────────────────────────────────────────────────────┐│ Unity Client ││ - Adventurer prefab (GanzSe modular character) ││ - Armor overlays (6 visual slots) ││ - Face customization (6 face slots) ││ - Kevin Iglesias animations (Humanoid rig) │└─────────────────────────────────────────────────────────────────┘ ↕ WebSocket / Protobuf┌─────────────────────────────────────────────────────────────────┐│ Axum Game Server ││ - Authoritative equipment state ││ - Appearance validation ││ - Stat calculations from equipped items │└─────────────────────────────────────────────────────────────────┘ ↕ PostgREST RPC┌─────────────────────────────────────────────────────────────────┐│ Supabase ││ - character_appearance table ││ - character_equipment table ││ - items table (armor definitions) │└─────────────────────────────────────────────────────────────────┘GanzSe Modular System
Section titled “GanzSe Modular System”How It Works
Section titled “How It Works”The GanzSe system uses an OVERLAY approach:
- Base Character Mesh = Single unified body mesh (not separate body parts)
- Base Character Root = Skeleton with 50+ bones
- Armor pieces = Overlay meshes that render ON TOP of the body
- Face details = Additional meshes attached to head bones
Asset Locations
Section titled “Asset Locations”Assets/Resources/Prefabs/├── Models/Character/│ ├── Base/│ │ ├── GanzSe Free Modular Character 1_1.fbx (v1.1 base model)│ │ ├── StylizedLit.mat (Quilbi URP material)│ │ └── Base Palette Texture URP.png (color palette)│ ├── Armor/ (108 prefabs)│ │ ├── Head Armor Type * Color * Part.prefab│ │ ├── Chest Armor Type * Color * Part.prefab│ │ ├── Arm Armor Type * Color * Part.prefab│ │ ├── Belt Armor Type * Color * Part.prefab│ │ ├── Legs Armor Type * Color * Part.prefab│ │ └── Feet Armor Type * Color * Part.prefab│ └── Face/ (107 prefabs)│ ├── Eyes Type * Color * Part.prefab│ ├── Eyebrow Type * Color * Part.prefab│ ├── Hair Type * Color * Part.prefab│ ├── Face Hair Type * Color * Part.prefab│ ├── Nose Type * Part.prefab│ └── Ears Type * Part.prefab└── Characters/Adventurer/ ├── Adventurer.prefab (main character prefab) └── AdventurerAnimatorController.controllerAdventurer Prefab Structure
Section titled “Adventurer Prefab Structure”Adventurer├── Animator (AdventurerAnimatorController + Humanoid Avatar)├── Base Character Mesh (SkinnedMeshRenderer - StylizedLit material)├── Base Character Root (Armature/Skeleton)│ └── [bone hierarchy for animation]│├── ARMOR_SLOTS/│ ├── HEAD_SLOT → helmet, hat, hood│ ├── CHEST_SLOT → chest armor, robes│ ├── ARMS_SLOT → arm guards, gauntlets│ ├── BELT_SLOT → belt accessories│ ├── LEGS_SLOT → leg armor, greaves│ └── FEET_SLOT → boots, shoes│└── FACE_SLOTS/ ├── EYES_SLOT → eye mesh variants ├── EYEBROWS_SLOT → eyebrow mesh variants ├── HAIR_SLOT → hairstyle meshes ├── FACIAL_HAIR_SLOT → beard/mustache meshes ├── NOSE_SLOT → nose mesh variants └── EARS_SLOT → ear mesh variantsEquipment System
Section titled “Equipment System”Visual Armor Slots (6)
Section titled “Visual Armor Slots (6)”These slots render visible armor on the character model:
| Slot ID | Slot Name | Proto Enum | GanzSe Category | Prefab Pattern |
|---|---|---|---|---|
| 0 | Head | SLOT_HEAD | HEADS | Head Armor Type * Color * |
| 1 | Chest | SLOT_CHEST | CHESTS | Chest Armor Type * Color * |
| 2 | Arms | SLOT_ARMS | ARMS | Arm Armor Type * Color * |
| 3 | Legs | SLOT_LEGS | LEGS | Legs Armor Type * Color * |
| 4 | Feet | SLOT_FEET | FEET | Feet Armor Type * Color * |
| 5 | Belt | SLOT_BELT | BELTS | Belt Armor Type * Color * |
Non-Visual Slots (6)
Section titled “Non-Visual Slots (6)”Stats-only equipment (no mesh representation on GanzSe model):
| Slot ID | Slot Name | Proto Enum | Description |
|---|---|---|---|
| 6 | Weapon | SLOT_WEAPON | Main-hand weapon |
| 7 | Off-Hand | SLOT_OFFHAND | Shield, tome, off-hand |
| 8 | Ring 1 | SLOT_RING_1 | Accessory ring |
| 9 | Ring 2 | SLOT_RING_2 | Accessory ring |
| 10 | Necklace | SLOT_NECKLACE | Amulet, pendant |
| 11 | Back | SLOT_BACK | Cape, cloak, backpack |
Hotbar Quick Slots (4)
Section titled “Hotbar Quick Slots (4)”| Slot ID | Keybind | Description |
|---|---|---|
| 12 | 1 | Quick consumable |
| 13 | 2 | Quick consumable |
| 14 | 3 | Quick consumable |
| 15 | 4 | Quick consumable |
GanzSe Armor Variants Available
Section titled “GanzSe Armor Variants Available”| Category | Types | Colors | Total |
|---|---|---|---|
| Head Armor | 6 (5 + v1.1) | 3 | 18 |
| Chest Armor | 6 (5 + v1.1) | 3 | 18 |
| Arm Armor | 6 (5 + v1.1) | 3 | 18 |
| Belt Armor | 6 (5 + v1.1) | 3 | 18 |
| Legs Armor | 6 (5 + v1.1) | 3 | 18 |
| Feet Armor | 6 (5 + v1.1) | 3 | 18 |
| Total | 108 |
Appearance System
Section titled “Appearance System”Face Slot Mappings
Section titled “Face Slot Mappings”| Database Field | GanzSe Category | Variants |
|---|---|---|
eye_shape + eye_color | EYES | 5 types × 5 colors = 25 |
eyebrow_style + eyebrow_color | EYEBROWS | 5 types × 5 colors = 25 |
hair_style + hair_color | HAIRS | 5 types × 5 colors = 25 |
facial_hair_style + facial_hair_color | FACE HAIRS | 5 types × 5 colors = 25 |
nose_style | NOSES | 5 types |
ear_style | EARS | 2 types |
Helmet/Hair Interaction
Section titled “Helmet/Hair Interaction”When a helmet (HEAD_SLOT) is equipped:
- Disable HAIR_SLOT mesh to prevent clipping
- Optionally disable EARS_SLOT depending on helmet type
public void OnHelmetEquipped(bool hasHelmet){ hairSlot.SetActive(!hasHelmet); // earsSlot visibility depends on helmet model}Database Schema
Section titled “Database Schema”Character Table
Section titled “Character Table”CREATE TABLE rentearth.character ( id uuid PRIMARY KEY, user_id uuid REFERENCES auth.users(id), slot smallint CHECK (slot BETWEEN 0 AND 9), name text NOT NULL, archetype text NOT NULL, level integer DEFAULT 1, experience bigint DEFAULT 0, created_at timestamptz, updated_at timestamptz);Character Appearance Table
Section titled “Character Appearance Table”CREATE TABLE IF NOT EXISTS rentearth.character_appearance ( character_id uuid PRIMARY KEY REFERENCES rentearth.character(id),
-- Body (base mesh - not swappable in GanzSe free version) skin_color smallint NOT NULL DEFAULT 0, -- Future: texture tint body_type smallint NOT NULL DEFAULT 0, -- Future: mesh variants
-- Face slots (mapped to GanzSe prefabs) eye_style smallint NOT NULL DEFAULT 0, -- 0-4 (Type 1-5) eye_color smallint NOT NULL DEFAULT 0, -- 0-4 (Color 1-5) eyebrow_style smallint NOT NULL DEFAULT 0, -- 0-4 eyebrow_color smallint NOT NULL DEFAULT 0, -- 0-4 nose_style smallint NOT NULL DEFAULT 0, -- 0-4 hair_style smallint NOT NULL DEFAULT 0, -- 0-4 hair_color smallint NOT NULL DEFAULT 0, -- 0-4 facial_hair_style smallint NOT NULL DEFAULT 0, -- 0-4 (0=none uses Type 1) facial_hair_color smallint NOT NULL DEFAULT 0, ear_style smallint NOT NULL DEFAULT 0, -- 0-1 (Type 1-2)
-- Voice voice_type smallint NOT NULL DEFAULT 0,
created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now());Character Equipment Table
Section titled “Character Equipment Table”CREATE TABLE IF NOT EXISTS rentearth.character_equipment ( character_id uuid PRIMARY KEY REFERENCES rentearth.character(id),
-- Visual armor slots (GanzSe overlays) slot_head uuid REFERENCES rentearth.items(id), slot_chest uuid REFERENCES rentearth.items(id), slot_arms uuid REFERENCES rentearth.items(id), slot_belt uuid REFERENCES rentearth.items(id), slot_legs uuid REFERENCES rentearth.items(id), slot_feet uuid REFERENCES rentearth.items(id),
-- Non-visual slots slot_weapon uuid REFERENCES rentearth.items(id), slot_offhand uuid REFERENCES rentearth.items(id), slot_ring_1 uuid REFERENCES rentearth.items(id), slot_ring_2 uuid REFERENCES rentearth.items(id), slot_necklace uuid REFERENCES rentearth.items(id), slot_back uuid REFERENCES rentearth.items(id), -- cape, cloak, backpack
-- Hotbar (consumable references) hotbar_1 uuid REFERENCES rentearth.items(id), hotbar_2 uuid REFERENCES rentearth.items(id), hotbar_3 uuid REFERENCES rentearth.items(id), hotbar_4 uuid REFERENCES rentearth.items(id),
-- Cached stats gear_score integer NOT NULL DEFAULT 0, updated_at timestamptz DEFAULT now());Character Stats Table
Section titled “Character Stats Table”CREATE TABLE rentearth.character_stats ( character_id uuid PRIMARY KEY REFERENCES rentearth.character(id), -- Position world_x real NOT NULL DEFAULT 0, world_y real NOT NULL DEFAULT 0, world_z real NOT NULL DEFAULT 0, rotation_yaw real NOT NULL DEFAULT 0, -- Vitals health_current integer NOT NULL DEFAULT 100, health_max integer NOT NULL DEFAULT 100, mana_current integer NOT NULL DEFAULT 100, mana_max integer NOT NULL DEFAULT 100, stamina_current integer NOT NULL DEFAULT 100, stamina_max integer NOT NULL DEFAULT 100, -- Combat attack_power integer NOT NULL DEFAULT 10, defense integer NOT NULL DEFAULT 10, speed real NOT NULL DEFAULT 5.0, -- Zone current_zone text NOT NULL DEFAULT 'starting_area', last_safe_x real NOT NULL DEFAULT 0, last_safe_y real NOT NULL DEFAULT 0, last_safe_z real NOT NULL DEFAULT 0, -- Time last_login_at timestamptz, total_playtime interval DEFAULT '0 seconds', updated_at timestamptz);Proto Definitions
Section titled “Proto Definitions”Equipment Slot Enum
Section titled “Equipment Slot Enum”enum EquipmentSlot { // Visual armor slots (GanzSe overlays) SLOT_HEAD = 0; SLOT_CHEST = 1; SLOT_ARMS = 2; SLOT_LEGS = 3; SLOT_FEET = 4; SLOT_BELT = 5;
// Non-visual slots SLOT_WEAPON = 6; SLOT_OFFHAND = 7; SLOT_RING_1 = 8; SLOT_RING_2 = 9; SLOT_NECKLACE = 10; SLOT_BACK = 11; // Cape, cloak, backpack
// Hotbar SLOT_QUICK_1 = 12; SLOT_QUICK_2 = 13; SLOT_QUICK_3 = 14; SLOT_QUICK_4 = 15;}Character Appearance Proto
Section titled “Character Appearance Proto”message CharacterAppearance { // Face customization (indexes into GanzSe prefabs) int32 eye_style = 1; // 0-4 int32 eye_color = 2; // 0-4 int32 eyebrow_style = 3; // 0-4 int32 eyebrow_color = 4; // 0-4 int32 nose_style = 5; // 0-4 int32 hair_style = 6; // 0-4 int32 hair_color = 7; // 0-4 int32 facial_hair_style = 8; // 0-4 int32 facial_hair_color = 9; // 0-4 int32 ear_style = 10; // 0-1
// Future body customization int32 skin_color = 11; int32 body_type = 12; int32 voice_type = 13;}Visual Equipment Proto
Section titled “Visual Equipment Proto”message VisualEquipment { // GanzSe armor type + color encoded as: (type * 10) + color // e.g., Type 3 Color 2 = 32 int32 head_visual = 1; int32 chest_visual = 2; int32 arms_visual = 3; int32 belt_visual = 4; int32 legs_visual = 5; int32 feet_visual = 6;}Unity Integration
Section titled “Unity Integration”CharacterAppearanceController
Section titled “CharacterAppearanceController”public class CharacterAppearanceController : MonoBehaviour{ [Header("Slot Containers")] [SerializeField] private Transform armorSlotsRoot; [SerializeField] private Transform faceSlotsRoot;
[Header("Slot References")] [SerializeField] private Transform headSlot; [SerializeField] private Transform chestSlot; [SerializeField] private Transform armsSlot; [SerializeField] private Transform beltSlot; [SerializeField] private Transform legsSlot; [SerializeField] private Transform feetSlot;
[SerializeField] private Transform eyesSlot; [SerializeField] private Transform eyebrowsSlot; [SerializeField] private Transform hairSlot; [SerializeField] private Transform facialHairSlot; [SerializeField] private Transform noseSlot; [SerializeField] private Transform earsSlot;
private const string ARMOR_PATH = "Prefabs/Models/Character/Armor/"; private const string FACE_PATH = "Prefabs/Models/Character/Face/";
public void ApplyAppearance(CharacterAppearance appearance) { // Clear existing face meshes ClearSlot(eyesSlot); ClearSlot(eyebrowsSlot); ClearSlot(hairSlot); ClearSlot(facialHairSlot); ClearSlot(noseSlot); ClearSlot(earsSlot);
// Load face prefabs based on appearance data LoadFacePart(eyesSlot, "Eyes", appearance.EyeStyle + 1, appearance.EyeColor + 1); LoadFacePart(eyebrowsSlot, "Eyebrow", appearance.EyebrowStyle + 1, appearance.EyebrowColor + 1); LoadFacePart(hairSlot, "Hair", appearance.HairStyle + 1, appearance.HairColor + 1); LoadFacePart(facialHairSlot, "Face Hair", appearance.FacialHairStyle + 1, appearance.FacialHairColor + 1); LoadFacePart(noseSlot, "Nose", appearance.NoseStyle + 1, 0); LoadFacePart(earsSlot, "Ears", appearance.EarStyle + 1, 0); }
public void EquipArmor(EquipmentSlot slot, int armorType, int armorColor) { Transform targetSlot = GetArmorSlot(slot); ClearSlot(targetSlot);
string category = GetArmorCategory(slot); string prefabName = $"{category} Armor Type {armorType} Color {armorColor} Part"; GameObject prefab = Resources.Load<GameObject>(ARMOR_PATH + prefabName);
if (prefab != null) { Instantiate(prefab, targetSlot); } }
private void LoadFacePart(Transform slot, string category, int type, int color) { string prefabName = color > 0 ? $"{category} Type {type} Color {color} Part" : $"{category} Type {type} Part";
GameObject prefab = Resources.Load<GameObject>(FACE_PATH + prefabName); if (prefab != null) { Instantiate(prefab, slot); } }
private void ClearSlot(Transform slot) { foreach (Transform child in slot) Destroy(child.gameObject); }
private Transform GetArmorSlot(EquipmentSlot slot) => slot switch { EquipmentSlot.SlotHead => headSlot, EquipmentSlot.SlotChest => chestSlot, EquipmentSlot.SlotArms => armsSlot, EquipmentSlot.SlotBelt => beltSlot, EquipmentSlot.SlotLegs => legsSlot, EquipmentSlot.SlotFeet => feetSlot, _ => null };
private string GetArmorCategory(EquipmentSlot slot) => slot switch { EquipmentSlot.SlotHead => "Head", EquipmentSlot.SlotChest => "Chest", EquipmentSlot.SlotArms => "Arm", EquipmentSlot.SlotBelt => "Belt", EquipmentSlot.SlotLegs => "Legs", EquipmentSlot.SlotFeet => "Feet", _ => "" };}Animation Setup
Section titled “Animation Setup”The Adventurer uses Kevin Iglesias’ Human Animations pack:
Animator Parameters
Section titled “Animator Parameters”| Parameter | Type | Description |
|---|---|---|
Speed | float | 0=Idle, 0.5=Walk, 1.0=Run |
IsGrounded | bool | For jump/fall states |
Jump | trigger | Trigger jump animation |
Animation Clips Location
Section titled “Animation Clips Location”Assets/Kevin Iglesias/Human Animations/├── HumanM@Idles├── HumanM@Walk01_Forward├── HumanM@Run01_Forward├── HumanM@Jump01 [RM]└── HumanM@Fall01SpineProxy (Optional)
Section titled “SpineProxy (Optional)”For upper/lower body blending:
Assets/Kevin Iglesias/Human Animations/Scripts/SpineProxy.csData Flow
Section titled “Data Flow”Character Enter Game
Section titled “Character Enter Game”1. Unity → Axum: EnterGameRequest { character_id }2. Axum → Supabase: load_character_full(character_id)3. Supabase returns: character + appearance + equipment4. Axum → Unity: CharacterData { appearance, equipment, stats }5. Unity: - Spawn Adventurer prefab - ApplyAppearance(appearance) - EquipArmor for each equipped slot - Set position and start animationsEquip Item
Section titled “Equip Item”1. Unity → Axum: EquipItemRequest { item_id, slot }2. Axum: Validate (ownership, level, class)3. Axum → Supabase: Update character_equipment4. Axum → Unity: EquipmentUpdate { slot, visual_id }5. Unity: EquipArmor(slot, type, color)Appearance Change (Barber)
Section titled “Appearance Change (Barber)”1. Unity → Axum: ChangeAppearanceRequest { appearance }2. Axum: Validate (changeable fields only)3. Axum → Supabase: Update character_appearance4. Axum → Unity: AppearanceUpdate { appearance }5. Unity: ApplyAppearance(appearance)Item Rarity & Sockets
Section titled “Item Rarity & Sockets”Rarity Levels
Section titled “Rarity Levels”| Rarity | Color | Stat Multiplier | Max Sockets |
|---|---|---|---|
| Common | White | 1.0x | 0 |
| Uncommon | Green | 1.1x | 0 |
| Rare | Blue | 1.25x | 1 |
| Epic | Purple | 1.5x | 2 |
| Legendary | Orange | 2.0x | 3 |
Gem Types
Section titled “Gem Types”enum GemType { GEM_ATTACK = 0; GEM_DEFENSE = 1; GEM_HEALTH = 2; GEM_MANA = 3; GEM_SPEED = 4; GEM_CRIT = 5; GEM_CRIT_DMG = 6; GEM_LIFESTEAL = 7; GEM_RESIST_FIRE = 8; GEM_RESIST_ICE = 9; GEM_RESIST_LIGHTNING = 10;}Gear Score Calculation
Section titled “Gear Score Calculation”gear_score = sum(item_level * rarity_weight * enhancement_bonus)
where: rarity_weight = 1, 1.2, 1.5, 2.0, 3.0 (common -> legendary) enhancement_bonus = 1 + (enhancement_level * 0.1)Durability System
Section titled “Durability System”Durability Loss
Section titled “Durability Loss”| Action | Durability Cost |
|---|---|
| Melee attack | Weapon: 1 |
| Ranged attack | Weapon: 1 |
| Take damage | Random armor: 1-3 |
| Block with shield | Off-hand: 2 |
| Die | All equipped: 10% of max |
Durability Warnings
Section titled “Durability Warnings”| Threshold | Display |
|---|---|
| 50% | Yellow durability bar |
| 25% | Orange bar + chat warning |
| 10% | Red bar + prominent warning |
| 0% | Item breaks, unusable until repaired |
Color Palettes
Section titled “Color Palettes”Skin Colors (16)
Section titled “Skin Colors (16)”| Index | Name | Hex |
|---|---|---|
| 0 | Pale | #FFE4C4 |
| 1 | Fair | #FFDAB9 |
| 2 | Light | #F5DEB3 |
| 3 | Medium Light | #DEB887 |
| 4 | Medium | #D2B48C |
| 5 | Olive | #BDB76B |
| 6 | Tan | #C4A484 |
| 7 | Brown | #A0785A |
| 8 | Dark Brown | #8B5A2B |
| 9 | Deep Brown | #6B4423 |
| 10 | Ebony | #3D2B1F |
| 11 | Ashen | #C0C0C0 |
| 12 | Blue (fantasy) | #87CEEB |
| 13 | Green (fantasy) | #98FB98 |
| 14 | Purple (fantasy) | #DDA0DD |
| 15 | Red (fantasy) | #FFB6C1 |
Hair Colors (16)
Section titled “Hair Colors (16)”| Index | Name | Hex |
|---|---|---|
| 0 | Black | #1C1C1C |
| 1 | Dark Brown | #3B2F2F |
| 2 | Brown | #6B4423 |
| 3 | Light Brown | #A0522D |
| 4 | Auburn | #A52A2A |
| 5 | Red | #B22222 |
| 6 | Ginger | #E97451 |
| 7 | Strawberry | #E4717A |
| 8 | Blonde | #F0E68C |
| 9 | Platinum | #E5E5E5 |
| 10 | White | #FFFFFF |
| 11 | Gray | #808080 |
| 12 | Blue | #4169E1 |
| 13 | Green | #228B22 |
| 14 | Purple | #9370DB |
| 15 | Pink | #FF69B4 |
Barber Shop
Section titled “Barber Shop”Players can change some appearance options at a Barber NPC.
Changeable at Barber
Section titled “Changeable at Barber”- Hair style & color
- Facial hair style & color
- Eyebrow style & color
Permanent (Creation Only)
Section titled “Permanent (Creation Only)”- Skin color
- Body type
- Face shape
- Eye color/shape
- Nose style
- Ear style
Barber Costs
Section titled “Barber Costs”| Change Type | Cost |
|---|---|
| Hair style | 100 gold |
| Hair color | 50 gold |
| Facial hair | 75 gold |
| Eyebrows | 25 gold |
RPC Functions
Section titled “RPC Functions”load_character(user_id, slot)
Section titled “load_character(user_id, slot)”Returns complete character data for game entry:
{ "id": "uuid", "name": "Adventurer", "archetype": "warrior", "level": 15, "stats": { "world_x": 125.5, "health_current": 450, ... }, "appearance": { "eye_style": 2, "hair_style": 3, ... }, "equipment": { "slot_head": "item-uuid", ... }}save_character_state(…)
Section titled “save_character_state(…)”Periodic state persistence from Axum:
rentearth.save_character_state( p_character_id uuid, p_world_x real, p_world_y real, p_world_z real, p_health_current integer, p_mana_current integer, p_level integer, p_experience bigint)Security
Section titled “Security”- Position Validation: Axum validates all movement to prevent teleport hacks
- Service Role Only: All RPC functions require
service_role - Rate Limiting: Position updates are batched and rate-limited
- Equip Validation: Level, class, and ownership checks on server
Implementation Status
Section titled “Implementation Status”| Component | Status | Notes |
|---|---|---|
| Adventurer Prefab | ✅ Complete | GanzSe v1.1 base model |
| Armor Slot System | ✅ Complete | 6 visual overlay slots |
| Face Slot System | ✅ Complete | 6 face customization slots |
| Animation Controller | ✅ Complete | Kevin Iglesias Humanoid rig |
| Database Schema | 🔲 Planned | Supabase migrations |
| Server Validation | 🔲 Planned | Axum equip/appearance handlers |
| Barber NPC | 🔲 Planned | Appearance change UI |
Related Documents
Section titled “Related Documents”- Game Design Document - Core architecture and technologies
- Combat System - Stats and entity state machines
- Bank & Storage - Item storage system
- Pool Oracle - Prefab management