文档 / 功能介绍

原神 MBTI 计算方式详细说明

一、总体架构

原神 MBTI 以玩家在米游社的游戏数据为基础,通过 五个"提瓦特维度" 的评分算法,将玩家人格映射为 32 种"神级代号"之一。

整体数据流如下:

米游社 API(index + 深渊 + 抽卡记录 + 旅行札记)
        ↓
  MbtiRawData(标量数据抽平)
        ↓
  GenshinMbtiCalculator(五维评分)
        ↓
  GenshinMbtiCodex(代号匹配)
        ↓
  GenshinMbtiResult(最终结果:五维分数 + 神级代号 + 评语)

核心设计原则:算法层(GenshinMbtiCalculator)只依赖标量数据(MbtiRawData),不直接依赖米游社模型,实现"逻辑/表单分离",便于单元测试与未来扩展。


二、数据来源

2.1 游戏档案 index 接口

调用米游社 game_record/app/genshin/api/index 接口,返回 GameRecordIndex,包含:

  • avatars:角色列表(等级、命座、好感度、武器等)
  • world_explorations:各区域世界探索度
  • homes:尘歌壶数据(洞天仙力、信任等阶、摆设数量)
  • stats:统计信息(活跃天数、成就总数、八类神瞳收集数)

此接口在 GameRecordService.GetGenshinIndexAsync 中有 5 分钟内存缓存,避免频繁请求。

2.2 深境螺旋接口

调用 spiralAbyss?schedule_type=1 获取当期深渊数据 SpiralAbyssInfo,提取 RevealRank(出场角色排行),用于维度1 的"深渊出场角色练度"计算。

2.3 本地抽卡记录

从本地数据库的 GenshinGachaItem 表中查询已同步的抽卡记录,通过 GenshinGachaService.GetGachaTypeStats 计算出总抽数、五星数、角色池当前保底距离。

2.4 旅行札记接口

调用 TravelersDiary 接口获取当月原石收入(结果自动入库),再从数据库取最近两个月原石数据求和,用于维度4 的"原石收入信号"。


三、标量数据抽平(MbtiRawData.From)

在计算评分前,米游社的嵌套模型被"抽平"为一组标量字段。这是算法层与数据层的边界:

3.1 玩家信息

  • Uid:角色 UID
  • Nickname:昵称
  • AvatarUrl:头像 URL(取自 index.HeadIcon)
  • RegionName:服务器名
  • Level:冒险等阶

3.2 维度1 字段(角色练度)

  • AvatarCount:index 中的角色总数
  • MaxLevelCount:等级 ≥ 90 的角色数
  • C6Count:命座 ≥ 6(满命)的角色数
  • Fetter10Count:好感度 ≥ 10(满好感)的角色数
  • AvgFetter:所有角色的平均好感度
  • AvgConstellation:所有角色的平均命座数
  • AbyssAvatarCount:深渊出场角色数(取 index.avatars 与 abyss.revealRank 的交集)
  • AbyssAvgLevel:深渊出场角色的平均等级
  • AbyssAvgConstellation:深渊出场角色的平均命座数

深渊交集逻辑:从深渊 RevealRank 取出出场角色的 AvatarId 集合,再与 index 中的角色列表取交集,确保只统计"实际在深渊中使用过"的角色。

3.3 维度2 字段(探索)

  • ExploreProgressAvg:所有区域探索度的平均值(0~1000,API 返回的 exploration_percentage 字段,值为百分比×10)
  • OculusRate:八类神瞳收集率的均值(0~1),每类为 min(1, 已收集数 / 上限)

神瞳分类:风/岩/雷/草/水/火/月/冰 共八类。其中至冬·冰神瞳上限暂填 0(7.0 版本开放后填入实际上限即生效),上限为 0 的类别自动跳过不参与均值计算。

3.4 维度3 字段(剧情)

  • AchievementNumber:成就总数
  • AvgFetter:所有角色的平均好感度(复用维度1 字段,用于衡量"对角色投入的感情")

活跃天数(ActiveDayNumber)已移除:不反映剧情参与度,每天打深渊的跳过党也可能活跃天数很高。

3.5 维度4 字段(抽卡 + 原石,由 Features 层填充)

  • TotalPulls:所有卡池的总抽数
  • FiveStars:所有卡池的五星总数
  • CurrentPity:角色池当前保底距离(取 301 池优先,回退 400 池)
  • Dim4HasGacha:是否存在抽卡数据(无数据时维度4 退化为中性 50)
  • CurrentMonthPrimogems:当月原石收入(旅行札记)
  • RecentTwoMonthsPrimogems:最近两个月原石总收入
  • Dim4HasPrimogems:是否成功获取原石数据(失败时维度4 不使用原石信号)

3.6 维度5 字段(壶经营)

  • HomeComfort:洞天仙力(取所有洞天中最高值)
  • HomeLevel:信任等阶(取洞天仙力最高处的等阶)
  • HomeItems:摆设数量(取洞天仙力最高处)

四、五维评分算法

每个维度产出 0~100 的整数分数,其中 0 代表一端,100 代表另一端。

4.0 工具函数

三个核心工具函数贯穿所有维度:

Clamp01(x)       = Math.Clamp(x, 0, 1)          将值限制在 [0, 1]
NormalizeHigh(v) = Clamp01(v / reference)       将值除以参考上限后截断
RatioScore(a, b) = Round(100 × a / (a + b + ε)) 两端比值打分,ε=1e-6 防除零

RatioScore 的直觉:这是一个"天平"函数。a 端和 b 端各自有一个 0~1 的得分,谁的得分更高,分数就偏向谁。两端相等时返回 50(中性)。完全偏向 a 端时返回接近 100,完全偏向 b 端时返回接近 0。


4.1 维度1:强度党(0) vs 真爱党(100)

设计思路:对比"角色练度的强度投入"与"角色练度的情感投入"。

满命率已移除:满命率是中性指标——强度党为追求极致伤害抽满命(如满命夜兰),真爱党为心爱角色抽满命(如满命安柏),无法区分倾向,不参与两端计算。

强度端得分(3 项加权求和,权重之和 = 1.0):

  • 深渊出场练度 AbyssInvestment × 权重 0.40
  • 满级率 MaxLevelCount / AvatarCount × 权重 0.35
  • 平均命座归一化 Clamp01(AvgConstellation / 6) × 权重 0.25

其中 AbyssInvestment(深渊出场角色练度):

AbyssInvestment = (Clamp01(AbyssAvgLevel / 90) + Clamp01(AbyssAvgConstellation / 6)) / 2

取出场角色平均等级和平均命座各归一化后的均值。无深渊出场数据时为 0。

真爱端得分(2 项加权求和,权重之和 = 1.0):

  • 好感满率 Fetter10Count / AvatarCount × 权重 0.55
  • 平均好感归一化 Clamp01(AvgFetter / 10) × 权重 0.45

最终分数

ScoreDim1 = RatioScore(love, strength)

love 越高越偏真爱(100),strength 越高越偏强度(0)。


4.2 维度2:摆烂党(0) vs 探索者(100)

设计思路:直接衡量地图探索投入度。探索度拉满 + 神瞳全收集 = 探索者;啥都不探索 = 摆烂党。

维度2 采用直接评分法(非 RatioScore),因为"探索程度"是一个单一维度的递增指标,不存在两端对立。壶经营已独立到维度5。

探索度得分(2 项加权):

explore = 0.6 × Clamp01(ExploreProgressAvg / 1000) + 0.4 × OculusRate
  • 世界探索度均值归一化(权重 0.60,API 返回值为 0~1000,即百分比×10)
  • 八类神瞳收集率均值(权重 0.40,已经是 0~1)

最终分数

ScoreDim2 = Round(explore × 100)

explore 越高越偏探索者(100),越低越偏摆烂党(0)。


4.3 维度3:剧情党(0) vs 跳过党(100)

设计思路:"看剧情" = "做了任务" + "对角色投入了感情",两者缺一不可。

核心问题与解决

  • 成就完成率只能反映"做了多少任务"——原神过任务就给成就,跳过剧情也能拿满成就,所以成就高 ≠ 看了剧情
  • 好感度间接反映"对角色投入的感情/时间"——认真看剧情的玩家会对角色产生感情,更愿意提升好感;跳过党对角色无感情,只练深渊角色,好感低
  • 活跃天数已移除:不反映剧情参与度,每天打深渊的跳过党也可能活跃天数很高
  • 角色拥有率已移除:可通过抽卡获得,不反映剧情参与度

沉浸度得分(几何平均):

ach = Clamp01(AchievementNumber / 600)       // 做了多少任务
fetter = Clamp01(AvgFetter / 10)             // 投入了多少时间/感情
immersion = √(ach × fetter)                  // 几何平均,两者缺一不可

为什么用几何平均(乘法)而不是加法?

  • 加法的问题:成就 0.9 + 好感 0.2 = 0.55,仍偏剧情——跳过党做满任务但没感情,被误判为剧情党
  • 几何平均的优势:好感低会"拖低"整体,成就再高也救不回来——√(0.9 × 0.2) = 0.42 → 跳过党
玩家类型成就好感几何平均判定
认真看剧情0.90.80.85剧情党
跳过党做满任务0.90.20.42跳过党
只看主线不肝支线0.40.80.57偏剧情
萌新0.20.30.24跳过党

最终分数(反向映射):

ScoreDim3 = Round(100 - immersion × 100)

沉浸度越高,分数越低(偏剧情党,0);沉浸度越低,分数越高(偏跳过党,100)。

好感度在维度1 和维度3 中均被使用,但角度不同:维度1 用好感 vs 练度(RatioScore 对比,衡量"爱 vs 强");维度3 用成就 × 好感(几何平均,衡量"有感情的参与度")。


4.4 维度4:赌狗(0) vs 规划党(100)

设计思路:对比"抽卡行为的冲动程度"与"原石规划的理性程度"。

核心改进:用"五星密度"(五星数/总抽数)代替"绝对五星数量"。绝对数量与游玩时长正相关——玩了3年的规划党可能抽了5000次但每次精打细算,不能因为抽得多就判为赌狗。五星密度消除了游玩时长的影响:密度高 → 只抽特定角色池、出五星就停手 → 规划党;密度低 → 什么池子都下 → 赌狗。

维度4 支持"原石收入信号"双模式:当 UsePrimogemsForDim4 = true 且成功获取原石数据时使用四因子算法;否则回退到三因子算法。

前置归一化

currentPityRate  = Clamp01(CurrentPity / 90)                    // 保底距离,90 抽为角色池上限
pullsRate        = NormalizeHigh(TotalPulls, 5000)               // 总抽数,参考上限 5000
fiveStarDensity  = Clamp01((FiveStars/TotalPulls - 0.005)        // 五星密度,期望值约 1.6%
                           / (0.04 - 0.005))                     // 0.5%以下=纯赌狗,4%以上=纯规划

无抽数时 fiveStarDensity 取中性值 0.5。

模式 A:启用原石信号(四因子)

UsePrimogemsForDim4 = trueDim4HasPrimogems = true 时启用。原石收入高 = 在囤原石 → 规划党;原石收入低 = 有石就抽 → 赌狗。

monthRate      = Clamp01(CurrentMonthPrimogems / 6000)          // 当月原石收入
twoMonthRate   = Clamp01(RecentTwoMonthsPrimogems / 12000)      // 近两月原石总收入
primogemsRate  = 0.5 × monthRate + 0.5 × twoMonthRate           // 原石信号综合得分

赌狗端得分(4 项加权,权重之和 = 1.0):

gamble = (1 - fiveStarDensity) × 0.30       // 五星密度低=什么池都抽
       + (1 - currentPityRate) × 0.25       // 保底距离远=刚抽完
       + (1 - primogemsRate) × 0.25         // 原石收入低=有石就花
       + pullsRate × 0.20                   // 总抽数多=抽得频繁

规划端得分(4 项加权,权重之和 = 1.0):

planner = fiveStarDensity × 0.30            // 五星密度高=出五星就停
        + currentPityRate × 0.25            // 保底距离近=在囤抽数
        + primogemsRate × 0.25              // 原石收入高=在囤原石
        + (1 - pullsRate) × 0.20            // 总抽数少=克制

模式 B:不使用原石信号(三因子,降级回退)

当原石信号关闭或获取失败时使用三因子算法:

gamble = (1 - fiveStarDensity) × 0.40
       + (1 - currentPityRate) × 0.35
       + pullsRate × 0.25

planner = fiveStarDensity × 0.40
        + currentPityRate × 0.35
        + (1 - pullsRate) × 0.25

最终分数

ScoreDim4 = RatioScore(planner, gamble)

planner 越高越偏规划党(100),gamble 越高越偏赌狗(0)。

无抽卡数据时Dim4HasGacha = false,直接返回 50(中性),不参与代号倾向判定。


4.5 维度5:闲云野鹤(0) vs 壶中仙(100)

设计思路:直接衡量尘歌壶经营投入度。洞天仙力拉满 + 信任等阶满 + 摆设多 = 壶中仙;不经营壶 = 闲云野鹤。

维度5 采用直接评分法(非 RatioScore),因为"壶经营程度"是一个单一维度的递增指标。原维度5 的"休闲 vs 硬核"(活跃天数+成就+角色数)与维度3 数据重叠率高,已替换为壶经营评分,使五个维度各自独立。

壶经营得分(3 项加权求和,权重之和 = 1.0):

home = Clamp01(HomeComfort / 30000) × 0.50
     + Clamp01(HomeLevel / 10) × 0.30
     + Clamp01(HomeItems / 2000) × 0.20
  • 洞天仙力归一化(上限 30000,权重 0.50)
  • 信任等阶归一化(上限 10,权重 0.30)
  • 摆设数量归一化(上限 2000,权重 0.20)

最终分数

ScoreDim5 = Round(home × 100)

home 越高越偏壶中仙(100),越低越偏闲云野鹤(0)。


五、神级代号匹配

5.1 布尔倾向与索引计算

五个维度各自产出一个 0~100 的分数,以 50 分为阈值 转换为布尔倾向:

  • b1 = (d1 >= 50) → 真爱党(True)/ 强度党(False)
  • b2 = (d2 >= 50) → 探索者(True)/ 摆烂党(False)
  • b3 = (d3 >= 50) → 跳过党(True)/ 剧情党(False)
  • b4 = (d4 >= 50) → 规划党(True)/ 赌狗(False)
  • b5 = (d5 >= 50) → 壶中仙(True)/ 闲云野鹤(False)

将 5 个布尔值组合为一个 5 位二进制索引(0~31):

idx = b1 | (b2 << 1) | (b3 << 2) | (b4 << 3) | (b5 << 4)

位权分配:

  • bit0(值 1):真爱
  • bit1(值 2):探索者
  • bit2(值 4):跳过
  • bit3(值 8):规划
  • bit4(值 16):壶中仙

5.2 32 种代号

代号库存储在 Assets/Config/GenshinMbtiCodex.json,共 32 条代号(version: 3),每条代号包含:代号名、对应形象、主色/副色(用于 UI 渐变)、暖心评语、毒舌评语。

索引按 (b1, b2, b3, b4, b5) 五位布尔组合,从 0(全 False)到 31(全 True)排列。例如:

  • index 0(强度+摆烂+剧情+赌狗+闲云)
  • index 16(强度+摆烂+剧情+赌狗+壶中仙)— 仅 b5 翻转
  • index 31(真爱+探索+跳过+规划+壶中仙)— 全 True

完整 32 条代号详见 GenshinMbtiCodex.json 文件。

5.3 匹配逻辑

GenshinMbtiCodex.Get(index) 从内部字典中按索引查找代号:

  1. 若索引命中 → 返回对应代号
  2. 若索引未命中 → 尝试返回 index=0 的兜底代号
  3. 若代号库为空 → 返回 MbtiPersona.Fallback("提瓦特的旅人")

六、重测微调机制

用户点击"重新测试"后,页面展开 5 个滑块(0~100),分别对应五个维度的分数。用户手动微调后,点击确认:

  1. 将滑块值写入 _manualScores 数组,覆盖算法原始分数
  2. 重新计算每个维度的 PositiveSide(分数 ≥ 50 为正端)
  3. 重新计算 5 位二进制索引 idx
  4. 从代号库重新匹配神级代号
  5. 重绘雷达图、分数条、代号、评语

重测不是真随机,而是让用户"承认自己的倾向"。例如玩家手动将维度4 拉到 100(规划党),系统会重新匹配到对应的代号。这确保测出来的结果让玩家觉得"太准了,简直在照镜子"。


七、优雅降级策略

MBTI 功能对数据缺失有完善的降级处理,不会因单一数据源失败而崩溃:

  • index 获取失败:抛出异常交由 UI 处理(显示错误提示 + 回到开始页)
  • 深渊获取失败:维度1 的 AbyssInvestment 退化为 0,仅用 index 角色数据评分(满级率、命座率、好感率),不影响其他维度
  • 抽卡数据缺失(用户未同步抽卡记录):Dim4HasGacha = false,维度4 直接返回 50(中性),不参与代号倾向判定
  • 旅行札记获取失败Dim4HasPrimogems = false,维度4 自动回退到三因子算法(不使用原石信号),不影响其他维度
  • 代号库加载失败:返回 MbtiPersona.Fallback("提瓦特的旅人"),不阻塞功能
  • 头像加载失败:忽略错误,不显示头像

八、配置常量参考

所有可调参数集中在 GenshinMbtiConfig 中,便于调参和二期功能复用:

维度1 权重

  • W1_Abyss = 0.40(深渊出场练度)
  • W1_Lvl90 = 0.35(满级率)
  • W1_Const = 0.25(平均命座)
  • W1_C6 已移除(满命率是中性指标,不参与两端)
  • W1_Fetter10 = 0.55(好感满率)
  • W1_AvgFetter = 0.45(平均好感)

维度2/5 共用上限(探索/壶经营)

  • ComfortCap = 30000(洞天仙力上限)
  • HomeLevelCap = 10(信任等阶上限)
  • HomeItemCap = 1000(摆设数量参考上限)
  • 神瞳收集上限(OculusCap 字典,随版本变化):

- 风 65 / 岩 130 / 雷 180 / 草 270 / 水 270 / 火 270 / 月 270 - 冰 0(至冬·冰神瞳,7.0 版本开放后填入实际上限即生效,上限为 0 时自动跳过)

维度3

  • AchievementCap = 600(成就总数归一上限)
  • 活跃天数已移除(不反映剧情参与度)
  • 角色拥有率已移除(可通过抽卡获得)
  • 算法:成就完成率 × 好感度,几何平均

维度4 参考量

  • TotalPullsRef = 5000(总抽数参考上限)
  • PityCap = 90(保底距离上限,角色池 90 抽)
  • FiveStarDensityMin = 0.005(五星密度归一化下限,0.5%)
  • FiveStarDensityMax = 0.04(五星密度归一化上限,4%)
  • UsePrimogemsForDim4 = true(是否启用原石余额信号,当前已启用)
  • PrimogemsMonthRef = 6000(当月原石收入参考上限)
  • PrimogemsTwoMonthRef = 12000(最近两个月原石总收入参考上限)

维度5 权重(壶经营综合得分)

  • W5_Comfort = 0.50(洞天仙力权重)
  • W5_HomeLevel = 0.30(信任等阶权重)
  • W5_HomeItems = 0.20(摆设数量权重)

最后更新:2026-08-10 · 阅读 4588 次