Sprite
NPC 精灵。每个 NPC 都有独特的视觉形象,由少量组件组合生成。
NPC 精灵系统
NPC 精灵是组合式的,而非预先绘制。每个 NPC 由以下部分生成:
- 身体 — 基础身体 sprite
- 肤色 — 叠加层
- 发型 — 后层 + 前层
- 服装 — 对应职业
- 配饰 — 眼镜、帽子等
- 表情 — 面部叠加层(微笑、皱眉、中性等)
这使我们能用少量组件 sprite 生成数百个不同的 NPC。
Sprite 布局
每个 NPC sprite 是一个 32×48 px 的分层组合:
┌──────────────────┐
│ Hair (back) │ y: 0–12
├──────────────────┤
│ Face + Body │ y: 8–40
├──────────────────┤
│ Legs/Feet │ y: 36–48
└──────────────────┘Sprite 水平居中,脚部在底部。
组件图集
/assets/npcs/
├── components.png
├── components.json
└── generator.ts组件
| 组件 | 变体数 | 备注 |
|---|---|---|
| 身体基础 | 3 | 纤细、普通、壮实 |
| 肤色 | 5 | 浅色、浅棕、棕色、深棕、暗棕 |
| 发型(后层) | 8 种 | |
| 发型(前层) | 12 种 | 与后层风格匹配 |
| 服装 | 20 | 按职业 + 休闲变体 |
| 配饰 | 6 | 眼镜、帽子变体 |
| 表情 | 5 | 开心、中性、难过、生气、惊讶 |
唯一组合总数: 约 72,000 种可能的 NPC
NPC 数据模型
ts
interface NPCSpriteData {
npcId: string;
body: 'slim' | 'average' | 'stocky';
skin: SkinTone;
hairBack: HairStyle;
hairFront: HairStyle;
hairColor: string;
outfit: OutfitId;
accessories: AccessoryId[];
defaultExpression: Expression;
}生成规则
NPC 在世界创建时一次性分配 sprite 数据,依据:
- 职业 → 默认服装
- 性格 → 影响默认表情
- 随机种子 → 身体、发型、颜色、配饰
相同 NPC ID + 相同种子 = 相同 sprite。可复现。
渲染管线
NPC sprite 以分层 sprite 方式渲染,而非预先合成:
ts
class NPCSprite extends Container {
private layers: Record<string, Sprite>;
constructor(data: NPCSpriteData) {
super();
this.layers = {
hairBack: new Sprite(Assets.get(`hair-back-${data.hairBack}`)),
body: new Sprite(Assets.get(`body-${data.body}-${data.skin}`)),
outfit: new Sprite(Assets.get(`outfit-${data.outfit}`)),
hairFront: new Sprite(Assets.get(`hair-front-${data.hairFront}`)),
accessories: new Sprite(Assets.get(`acc-${data.accessories[0]}`)),
expression: new Sprite(Assets.get(`face-${data.defaultExpression}`)),
};
for (const layer of Object.values(this.layers)) {
this.addChild(layer);
}
}
setExpression(expr: Expression) {
this.layers.expression.texture = Assets.get(`face-${expr}`);
}
setOutfit(outfit: OutfitId) {
this.layers.outfit.texture = Assets.get(`outfit-${outfit}`);
}
}Z 轴顺序
- 发型后层
- 身体
- 服装(覆盖身体)
- 发型前层(覆盖面部)
- 配饰
- 表情(面部最上层)
表情
| NPC 情绪 | 表情 Sprite |
|---|---|
| 开心 | face-happy |
| 满足 | face-content |
| 中性 | face-neutral |
| 难过 | face-sad |
| 生气 | face-angry |
| 焦虑 | face-anxious |
| 无聊 | face-bored |
每个 Tick 通过 SSE 更新。
性能
每个 NPC 约 6 层 sprite。30 个 NPC 的情况:
- 总 sprite 绘制:180 次(远低于 200 次的预算)
- 内存:约 30 NPC × 6 层 × 32×48×4 字节 ≈ 110KB
完全在预算范围内。
📐 详细角色规范见 设计 — 角色规范