今天第一篇讲的是「玩家数据怎么上云」,这一篇回到游戏的骨架——数值。一个人用 DeepSeek 做游戏,最容易犯的错就是「拍脑袋填数字」:前期太简单、中期突然劝退、后期直接崩坏。根因通常不是创意问题,而是数值散落在代码里、改动成本极高、又没人替你算。今天用三步法解决它:把数值抽到数据层、用目标击杀时间反推公式、用 AI 跑模拟校验。
今日速览
- 数值不要写成脚本里的常量,用 Custom Resource(
class_name XxxStats extends Resource)承载,存成.tres文件,在 Inspector 里像填表一样调。 @export把字段暴露到编辑器;配@export_range(0.0, 100.0, 0.1)限范围、@export_group("战斗")分组,调参不用碰代码。- 平衡的本质是相对关系,不是绝对值:先定一个基准(例如「1 级小怪 3 刀死」),其余全部按比例推。
- 伤害公式优先选乘法式(
dmg = atk * (1 - def/(def+K))),它平滑、不发散;减法式(atk - def)直观但一破防就归零,容易崩。 - 成长曲线别用纯线性(太温吞)也别用纯指数(后期爆炸),常用
base * growth^n再加软上限。 - 平衡不是算出来的,是测出来的:写个无头(headless)脚本跑蒙特卡洛模拟,让 DeepSeek 替你写这层代码。
核心学习内容
一、把数值从代码里抽出来:Custom Resource
Godot 官方的 Resources 文档说得很直白:资源是纯数据容器,节点用资源里的数据。所以把「敌人/武器/关卡」的数值定义成资源,是引擎鼓励的做法:
class_name EnemyStats extends Resource
@export_group("基础")
@export var max_hp: int = 30
@export_multiline var note: String = ""
@export_group("战斗")
@export_range(0.0, 100.0, 0.5) var attack: float = 6.0
@export_range(0.0, 0.95, 0.01) var defense_ratio: float = 0.1
@export_range(0.1, 5.0, 0.1) var attack_speed: float = 1.0
存成 res://data/enemy_slime.tres 后有三个好处:可视化调参(策划/你自己在编辑器里拖滑块)、可 diff(.tres 是文本,能进 git 看每次数值改动)、数据驱动(换一套数值就是换一个文件,玩法代码一行不改)。这正是「AI 帮你写一次,之后你手动微调」的理想分工。
二、伤害公式:先选地形,再调参数
不要一上来就纠结「攻击力填 12 还是 15」。先确定公式的形状。两种主流:
- 减法式
dmg = atk - def:直观,但攻击力略低于防御就归零,强敌一出现就「打不动」,容错极差。 - 乘法式
dmg = atk * (1 - def / (def + K)):K是「半减常数」,防御等于K时正好减伤 50%,永远正向、永远有边际收益递减,几乎不会崩。
选型之后,用期望 DPS 和有效生命 EHP两个指标去核对:DPS = 单次伤害 × 每秒命中数 × 暴击期望,EHP = HP / (1 - 减伤)。平衡就是让「我方 DPS 曲线」和「敌方 EHP 曲线」保持一个舒服的剪刀差。
三、成长曲线与难度阶梯:用目标 TTK 反推
最实用的锚点是 TTK(Time To Kill,击杀时间)。先定目标手感,再倒推数字:
- 杂兵 2–4 秒,精英 15–30 秒,Boss 2–5 分钟。
- 给定 TTK 和期望 DPS,
所需 HP = TTK × DPS,HP 就这么定出来了,而不是拍脑袋。 - 成长用
value(n) = base * growth^n,把growth控在 1.05–1.15,再加软上限(如def/(def+K)天然封顶)。
玩家成长和敌人成长要成对设计:玩家变强 10%,敌人就同步变强 8–12%,保持挑战恒定,这才是「越玩越爽」而不是「越玩越累」。
四、用 AI 做平衡:模拟 + 校验
数值的最终裁判是数据。写一个 headless 模拟(Godot 支持 --headless 运行脚本,或干脆用 Python 读 .tres/CSV)批量跑对局,输出「胜率、平均 TTK、DPS 分布」,你就能一眼看出哪一段曲线塌了。这类脚手架代码最适合交给 DeepSeek 生成,你只负责定义指标和验收。
🎯 DeepSeek 提示词
提示词 1:生成数值资源 + 伤害计算函数
你是资深 Godot 4.7 游戏开发者。请用 GDScript 写一个数值层:
1. 定义一个 class_name CombatStats extends Resource,用 @export 暴露:
max_hp(Int)、base_attack(Float)、defense(Float)、crit_chance(0-1)、
crit_multiplier(Float)、attack_speed(Float)。用 @export_group 分组,
用 @export_range 限制合理区间,字段加中文 tooltip(用 ## 文档注释)。
2. 写一个 Autoload 单例 CombatResolver,提供:
func calculate_damage(attacker: CombatStats, defender: CombatStats) -> Dictionary
返回 {damage:int, is_crit:bool}。
3. 伤害公式用乘法减伤:final = atk * (1 - def/(def + 50));暴击用 randf() < crit_chance。
4. 对被击方 HP 计算期望击杀时间:func est_ttk(attacker, defender) -> float。
输出:仅给出两个完整脚本 + 顶部 3 行「挂载/创建 .tres」说明。
约束:Godot 4.7,snake_case,不要用已废弃 API。
用法:项目开局就跑一次,你得到一套「数值可调、公式统一」的战斗底座,之后所有敌人只需新建 .tres。
提示词 2:用目标 TTK 反推数值与成长曲线
你是游戏数值策划。我的游戏是 2D 俯视角动作肉鸽,目标是让「1 级杂兵 3 刀死、
10 级杂兵约 4 刀死」,即保持 TTK 大致恒定、难度缓慢上升。
已知:玩家 1 级攻击力 6,每级 +8%(线性成长),暴击率固定 10%、暴击倍率 1.5。
请帮我:
1. 设计杂兵 max_hp 随等级 1→20 的成长曲线,给出公式和一张数值表。
2. 反推防御/减伤参数,保证伤害不会出现「0 或负」。
3. 指出这条曲线在第几级会出现「玩家 DPS 反超/落后」的剪刀差拐点。
4. 给出 3 个可调参数(k 值、growth、软上限)分别会把难度推向哪边。
输出格式:先结论(3 句话),再公式,再 Markdown 数值表(等级 1-20),最后调参建议。
约束:不要写故事,直接给可落地的数字与公式。
用法:任何时候要加新难度层,把这段改几个数字重跑,让 AI 先把数学问题算清,你再去编辑器里填。
提示词 3:让 DeepSeek 写平衡模拟脚本
你是 Godot 4.7 工程师。请写一个可 headless 运行的 GDScript 工具脚本
res://tools/balance_sim.gd(extends SceneTree),用命令行执行:
godot --headless --script res://tools/balance_sim.gd
功能:
1. 扫描 res://data/enemies/*.tres,用 ResourceLoader 加载所有 CombatStats。
2. 模拟 1000 场「玩家 vs 单个敌人」的战斗:按 attack_speed 累计伤害,直到一方死亡。
3. 统计每个敌人:玩家胜率、平均 TTK、平均承受伤害、TTK 的 P10/P50/P90。
4. 把结果打印成对齐的 Markdown 表格(敌人名 | 胜率 | TTK 均值 | P90)。
5. 若某敌人胜率 < 60% 或 TTK > 目标值,用 ⚠️ 标出。
输出:完整脚本 + 运行命令说明。
约束:不依赖任何插件,Godot 4.7,脚本退出时调用 quit()。
用法:每次改完数值 .tres,跑一遍模拟,用「胜率 + TTK 分布」代替「我手感觉得差不多」。
学习心得
对「用 DeepSeek 做一款 Steam 游戏」来说,数值是最改起来最贵的一环——玩法代码 AI 能帮你改,但数值分布一旦错了,玩家差评和退款不会给你第二次机会。这套三步法(数据层承载 + TTK 反推公式 + AI 跑模拟)的价值在于:把「平衡」从玄学变成可度量、可回归的工程。你今天真正要学会的不是某个公式,而是**「让 AI 算,让数据判,你只调手感」**这条流水线。
明日关注
- 把现有敌人/武器的数值抽成
CombatStats的.tres文件,确认改数值不用碰代码。 - 为你的核心战斗定一条目标 TTK(杂兵/精英/Boss 各一个),写进设计笔记。
- 跑通
balance_sim.gd的 headless 模拟,输出一张胜率 + TTK 表格,找出第一个「塌掉」的敌人。
本文由自动化学习流水线每日生成,坚持即学即记。
评论