武器升级系统开发文档

武器升级系统服务端+客户端实现,含升级算法、碎片扣减、武器等级更新

最后编辑时间:yaohua

武器升级系统开发文档

一、方案确定

方案 B — 金币+碎片双消耗,公式 1 对数平滑成长

消耗规则

每级消耗 = 基础消耗 × 品质倍率 × 等级系数

品质 倍率
白(quality=1) ×1.0
蓝(quality=2) ×1.5
紫(quality=3) ×2.0
金(quality=4) ×3.0

碎片消耗(每级):

碎片数量 = 基础碎片 × 品质倍率 × level
  • 基础碎片:取该武器对应碎片的单次合成消耗(来自 Synthesis.csv 的 quantities[0])
    • 例:武器 2000(配方1)基础碎片=30;武器 2012(配方13)基础碎片=100

金币消耗(每级):

金币 = 100 × 品质倍率 × level
  • 例:白品质武器 1→2 级,金币=100×1.0×2=200;金品质武器 1→2 级,金币=100×3×2=600

成长公式

属性成长值 = 原值 × (1 + k × ln(level))
其中 k = 0.3,ln 为自然对数

k=0.3 各级成长系数:

level ln(level) 系数 (1+0.3×ln)
1 0.000 ×1.000(基准)
10 2.303 ×1.691
20 2.996 ×1.899
30 3.401 ×2.020
40 3.689 ×2.107
50 3.912 ×2.174
60 4.094 ×2.228

升级属性

全局属性(每级必升):

属性 每级成长 60级总成长
initLife +3%×k·ln +180%×k≈+60%
sprintBonus +1%×k·ln +60%×k≈+20%
coinRate +2%×k·ln +120%×k≈+40%

注:60级时 initLife 实际约为基础值的 2.23 倍(+123%)

武器特定属性(仅原值>0时参与):

属性 是否参与 说明
distBonus 原值>0 时参与 距离加成
comboBonus 原值>0 时参与 连击加分
gemRate 原值>0 时参与 宝石概率
weaponRate 原值>0 时参与 武器概率

成长方式同 initLife,按比例缩放。


二、数据结构

武器等级表 WeaponLevel.csv(新建)

字段 类型 说明
weaponId int 武器ID(2000-2015)
level int 当前等级(1-60)
exp int 当前经验值(预留)

注:经验值系统暂不实现,第一版仅支持等级。

新增数据库表 character_weapon_levels

CREATE TABLE character_weapon_levels (
    character_id BIGINT NOT NULL,
    weapon_id INT NOT NULL,
    level INT DEFAULT 1,
    PRIMARY KEY (character_id, weapon_id)
);

三、Proto 消息

// 升级请求
message C2S_WeaponUpgradeRequest {
    int32 weapon_id = 1;       // 武器ID(2000-2015)
}

// 升级结果
message S2C_WeaponUpgradeResult {
    int32 code = 1;            // 0=成功,其他=错误码
    string msg = 2;
    int32 weapon_id = 3;       // 武器ID
    int32 new_level = 4;       // 新等级
    int32 init_life_new = 5;   // 新 initLife(服务端计算后的实际值)
    int32 dist_bonus_new = 6;  // 新 distBonus
    int32 sprint_bonus_new = 7; // 新 sprintBonus
    int32 combo_bonus_new = 8; // 新 comboBonus
    int32 coin_rate_new = 9;   // 新 coinRate
    int32 gem_rate_new = 10;   // 新 gemRate
    int32 weapon_rate_new = 11; // 新 weaponRate
}

// 物品变化同步(复用现有)
message S2C_InventoryChanged {
    int32 currency_delta = 1;
    int32 gems_delta = 2;
    map<int32, int32> items_delta = 3;
}

四、错误码

code 说明
0 成功
1 角色不存在
2 武器不存在或未拥有
3 碎片不足
4 金币不足
5 已达等级上限(60)
6 背包不存在

五、完整流程

客户端                         服务端
   |                              |
   |--- C2S_WeaponUpgradeRequest ->|
   |    (weapon_id=2000)          |
   |                              | 1. 读取 character_weapon_levels(当前等级)
   |                              | 2. 校验等级 < 60
   |                              | 3. 读取 WeaponLevel.csv + NinjaWeapon.csv(属性)
   |                              | 4. 计算升级消耗(碎片 + 金币)
   |                              | 5. 验证余额(inventory.items[碎片] >= 消耗)
   |                              | 6. 验证余额(inventory.currency >= 金币消耗)
   |                              | 7. 扣费(碎片 -N,金币 -M)
   |                              | 8. 计算新属性值(基础 × (1 + 0.3 × ln(newLevel)))
   |                              | 9. 写入 character_weapon_levels(level++)
   |                              |
   |<-- S2C_WeaponUpgradeResult ---|
   |    (code=0, new_level=2,      |
   |     init_life_new=...)        |
   |                              |
   |<-- S2C_InventoryChanged -----|
   |    (currency_delta=-200,      |
   |     items_delta={1000:-30})   |

六、服务端实现

新增文件

server/WeaponUpgradeService.cs

职责:

  1. 加载 NinjaWeapon.csv(内存缓存)
  2. 加载/管理 character_weapon_levels
  3. 计算升级消耗(GetUpgradeCost)
  4. 计算新属性(CalculateUpgradedStats)
  5. 验证余额(CanUpgrade)
public class WeaponUpgradeService {
    // 加载数据
    public void LoadTables(string tableDir);

    // 计算升级消耗
    public (int fragmentCost, int goldCost) GetUpgradeCost(int weaponId, int currentLevel, int quality);

    // 计算新属性
    public WeaponStats CalculateUpgradedStats(int weaponId, int newLevel);

    // 验证是否可升级
    public (bool canUpgrade, int errorCode) CanUpgrade(long characterId, int weaponId);

    // 执行升级
    public async Task<UpgradeResult> UpgradeAsync(long characterId, int weaponId);
}

public class WeaponStats {
    public int InitLife;
    public int DistBonus;
    public int SprintBonus;
    public int ComboBonus;
    public int CoinRate;
    public int GemRate;
    public int WeaponRate;
}

server/WeaponUpgradeController.cs

职责:

  • WebSocket 处理(C2S_WeaponUpgradeRequest → S2C_WeaponUpgradeResult + S2C_InventoryChanged)
  • 调用 WeaponUpgradeService

修改文件

  • WebSocketServer.cs — 注册 MsgId.C2S_WeaponUpgradeRequest
  • Program.cs — DI 注册 WeaponUpgradeService

七、客户端实现

WebSocketService.cs

public event Action<S2C_WeaponUpgradeResult> OnWeaponUpgradeResult;

case MsgId.S2C_WeaponUpgradeResult:
    var upResult = _decoder.DecodeS2C_WeaponUpgradeResult(pkg.Data);
    MainThreadDispatcher.Invoke(() => OnWeaponUpgradeResult?.Invoke(upResult));
    break;

public void RequestWeaponUpgrade(int weaponId) {
    var req = new C2S_WeaponUpgradeRequest { WeaponId = weaponId };
    Send(MsgId.C2S_WeaponUpgradeRequest, req);
}

InventoryService.cs / WeaponManager.cs

  • 存储各武器当前等级(从 S2C_WeaponUpgradeResult 同步)
  • UI 显示等级和属性变化

八、注意事项

  1. 等级存储:character_weapon_levels 每角色每武器独立记录,未解锁的武器默认等级 1
  2. 属性计算时机:升级后属性由服务端计算并返回,客户端不做演算
  3. 原子性:扣费(碎片+金币)和写入等级在同一事务或连续 DB 操作中完成
  4. 品质倍率:从 NinjaWeapon.csv quality 列读取,白=1/蓝=2/紫=3/金=4,再映射为倍率
  5. 基础碎片数量:从 Synthesis.csv 的 quantities[0] 读取(无需解析完整多材料格式)

九、已完成清单

  • [x] 更新 Ninjiafall1Proto.proto(添加 C2S_WeaponUpgradeRequest / S2C_WeaponUpgradeResult)
  • [x] 重新编译 protobuf(build.sh)
  • [x] 服务端:WeaponUpgradeService.cs — CSV加载、消耗计算(ln平滑成长,k=0.3,MaxLevel=60)、属性计算
  • [x] 服务端:WeaponUpgradeController.cs(WebSocket)— C2S_WeaponUpgradeRequest → S2C_WeaponUpgradeResult + S2C_InventoryChanged
  • [x] 服务端:注册 MsgId.C2S_WeaponUpgradeRequest 到 WebSocketServer.cs
  • [x] 服务端:Program.cs — DI 注册 WeaponUpgradeService
  • [ ] 数据库:character_weapon_levels 表(建表语句已确认在 WeaponUpgradeService.cs 中)
  • [x] 客户端:ProtobufTypes.cs — 添加 Protobuf 消息(C2S_WeaponUpgradeRequest / S2C_WeaponUpgradeResult,11字段)
  • [x] 客户端:ProtobufCodec.cs — 编解码支持
  • [x] 客户端:WebSocketService.cs — OnWeaponUpgradeResult + RequestWeaponUpgrade(int weaponId)
  • [ ] 测试:碎片不足 / 金币不足 / 正常升级 / 等级上限判断(待联调)
  • 留下精彩的评论吧~(0条)

所有内容仅供学习与交流,转载须标明链接。未经同意,禁止作为商业用途,有特殊需求请联系站长。