mcpField
D
design-skill-progression-trees

design-skill-progression-trees

github.com/fcsouza开发2462 次安装1 周前更新
代码仓库

关于 design-skill-progression-trees

面向游戏设计与开发者的技能和进度系统设计指南,采用有向无环图组织解锁路径。内容涵盖服务端验证、成本曲线、重置机制及节点效果建模。

主要功能

  1. 01以 DAG 建模前置条件
  2. 02服务端校验解锁请求
  3. 03设计渐进式成本曲线
  4. 04规划技能重置机制
  5. 05用 JSONB 存储效果数据
  6. 06检查循环与不可达节点

适用场景

  1. 01设计技能或天赋解锁图
  2. 02实现安全的玩家解锁接口
  3. 03规划重置费用与退款
  4. 04排查进度图的结构问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/fcsouza/agent-skills --skill design-skill-progression-trees

安装后在 Claude Code 中输入/design-skill-progression-trees确认已加载。

附带文件boilerplate/skill-tree.tstemplates/skill-tree-template.md

详细介绍

技能与成长树

用途

基于 DAG 的成长系统,适用于任何游戏类型——技能树、天赋树、能力解锁、被动属性图和声望层级。涵盖数据建模、服务端权威验证、成本缩放和重置机制。

适用场景

触发词:技能树、天赋树、能力树、成长系统、解锁系统、声望、节点图、技能点、能力解锁、被动树、天赋构筑、重置、技能重置

前置条件

  • game-design-fundamentals — 核心循环和奖励系统知识
  • postgres-game-schema — 玩家和货币数据模型
  • game-economy-design — 成本平衡和货币集成

核心原则

Chris Taylor(Supreme Commander):“优秀的科技树能让玩家感觉拥有无限选择,同时确保每条路线都切实可行。” Will Wright(The Sims、Spore):“当简单规则相互作用时,涌现系统便会出现——技能树应当相互组合,而非规定玩法。” Jonathan Blow(Braid、The Witness):“每次解锁都应让人感觉是一次真正的领悟,而不是勾选一个复选框。”

  1. DAG,而非树 — 前置条件应构成有向无环图;出现环路会破坏验证、回滚和一切相关功能。管理端写入时必须确保无环。
  2. 服务端权威解锁验证 — 绝不信任客户端的解锁请求。写入前,必须在服务端验证前置条件、货币和冷却时间。
  3. 保存前置条件快照以便回滚 — 在解锁时保存完整的前置条件状态。如果重置或补丁更改了图,就可以审计玩家解锁时哪些内容是有效的。
  4. 成本缩放遵循指数曲线 — 固定成本会导致终局节点相对于玩家收入而言过于便宜。使用 cost = baseCost * tier^exponent(指数通常为 1.5-2.0)。
  5. 重置应以玩家为先 — 始终提供重置机制,可以是带冷却时间的免费重置,也可以是付费的软货币重置。绝不让玩家永久受困于糟糕的选择。
  6. 效果是数据,而非代码 — 将效果存储为 JSONB({ "stat": "attack", "modifier": "+10%" })。在服务端应用。这样无需部署即可热重载、进行 A/B 测试和发布平衡补丁。
  7. 渐进式揭示 — 不要一次性显示所有节点。满足前置条件后再揭示节点。这样可以减轻玩家的压力,并创造探索惊喜(Jonathan Blow)。

分步说明

1. 定义节点类别

为每个节点分类:主动技能(玩家主动触发的能力)、被动属性(始终生效的加成)、解锁项(内容或机制的门槛)、声望(终局后重置获得的加成)。

2. 对 DAG 建模

使用存储在 JSONB 中的邻接表。每个节点都有一个包含节点 ID 的 prerequisites 数组。每次管理端写入时都要验证是否无环。

3. 编写 Drizzle Schema

创建 skill_nodes(成长树定义)和 player_skills(玩家状态)表。完整实现请参见样板代码。

4. 实现解锁 API

端点:POST /skills/unlock/:nodeId。流程:检查是否满足前置条件,检查货币是否充足,通过原子事务完成扣除货币、插入 player_skills 记录并保存快照。

5. 缓存已解锁节点

按玩家会话将已解锁的节点 ID 存储在 Redis 中。解锁或重置时使缓存失效。树查询十分频繁,逐个请求查询数据库太慢。

6. 添加重置端点

端点:POST /skills/respec。流程:扣除重置费用(或检查冷却时间),删除所有 player_skills 行,将技能点退还到玩家钱包,并使 Redis 缓存失效。

7. 添加成长树健康检查

仅限管理端使用的工具:环路检测(在前置条件图上执行 DFS)、孤立节点检测(前置条件引用了已删除节点的节点)、不可达节点检测。

Drizzle Schema

// skill_nodes — defines the tree structure (admin-managed)
export const skillNodes = pgTable("skill_nodes", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  name: text("name").notNull(),
  description: text("description"),
  category: text("category").notNull(), // 'active' | 'passive' | 'unlock' | 'prestige'
  tier: integer("tier").notNull().default(1),
  prerequisites: jsonb("prerequisites").$type<string[]>().default([]),
  cost: integer("cost").notNull().default(1),
  effects: jsonb("effects").$type<Record<string, unknown>[]>().default([]),
  isVisible: boolean("is_visible").notNull().default(true),
  metadata: jsonb("metadata").$type<Record<string, unknown>>().default({}),
  createdAt: timestamp("created_at").notNull().defaultNow(),
  updatedAt: timestamp("updated_at").notNull().defaultNow(),
});

// player_skills — tracks which nodes each player has unlocked
export const playerSkills = pgTable("player_skills", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  playerId: text("player_id").notNull().references(() => players.id, { onDelete: "cascade" }),
  nodeId: text("node_id").notNull().references(() => skillNodes.id),
  unlockedAt: timestamp("unlocked_at").notNull().defaultNow(),
  snapshot: jsonb("snapshot").$type<Record<string, unknown>>().default({}),
});

API 模式

端点方法描述
/skills/treeGET完整技能树,包含每个节点的玩家解锁状态
/skills/unlock/:nodeIdPOST解锁节点——验证前置条件、扣除费用并执行原子写入
/skills/respecPOST完全重置——扣除费用、删除 player_skills 并返还点数

交叉引用

  • game-economy-design — 技能点的费用平衡与货币消耗机制
  • postgres-game-schema — 玩家和货币表的架构
  • redis-game-patterns — 按会话缓存已解锁节点集合
  • game-design-fundamentals — 核心循环整合与成长节奏

陷阱与反模式

  • “客户端技能树状态”——在客户端存储解锁状态;很容易被黑,必然导致不同步
  • “没有重置机制”——做出错误选择的玩家会退出;务必提供重置途径
  • “固定费用曲线”——每个节点费用相同;随着收入增长,后期节点会变得便宜得微不足道
  • “缺少循环检测”——一个循环前置条件就会导致验证陷入无限循环;写入时务必确保是有向无环图(DAG)
  • “效果硬编码”——将效果写死在代码中,而不是存储为 JSONB 数据;每次平衡性调整都需要部署
  • “显示所有节点”——一开始就展示整棵技能树会让新玩家不知所措,还会破坏探索乐趣

设计理念

Chris Taylor(Supreme Commander、Total Annihilation):科技树应让玩家感受到自己正在构建强大的事物。每个分支都应让人觉得能开启新的战略可能性。如果玩家看到两个分支,其中一个明显更好,那么这棵树就设计失败了。

Will Wright(The Sims、SimCity、Spore):最好的成长系统,是通过简单规则之间的交互催生出涌现式构筑的系统。不要设计 50 种独特能力,而要设计 10 种可组合的能力,让它们能组合出 50 种玩法。

Jonathan Blow(Braid、The Witness):解锁技能应让人觉得自己理解了新事物,而不是填满了进度条。如果玩家解锁后没有觉得自己更聪明或更强大,那么这个节点就白费了。

来源

  • 《设计科技树与成长系统》——2016 年 GDC,Chris Taylor
  • 《游戏设计的艺术》——Jesse Schell,关于技巧与机遇的章节
  • 《多人游戏中的能力系统平衡》——2019 年 GDC
  • 《简单系统催生的涌现式玩法》——Will Wright,GDC 经典演讲
  • 《成长设计中的玩家自主性》——2020 年 GDC

评论

支持 Markdown