6.2 算的是「抽一次要花多少玉璧」,回答的是消耗侧的问题。本节换个方向,回答供给侧的问题:同样一笔钱,买玉璧直充、买资源礼包、还是买加速包,哪种更划算?这个问题在 SLG 里天天出现,答案却常常是「拍脑袋觉得超值」。本节把它变成一张可排序、可复算的性价比表。定价档位本身留到第 10 章,定价锚点与档位设计 讲的是「卖多少钱」,本节讲的是「这个价买到的东西值多少」。
礼包是 SLG 商业化里最灵活的一类付费点:它不改变抽卡概率,也不改变定价档位,只改变**「一次买多少、打包成什么样」**。正因为灵活,它最容易失控——同样一笔钱,可以打包成纯玉璧、资源、或加速,三者的性价比天差地别。本节给出的,就是把这三种打包方式放到同一把尺子上比较的方法。
礼包为什么需要「价值锚点」
一个礼包的内容是混合的:可能给玉璧、给资源(木石铁粮)、给加速道具。三种东西单位不同,没法直接比。要比较,就得先把它们折算到同一个尺子上——这把尺子就是价值锚点。
本套的锚点写在 config/pricing.json 里(由主控维护,本卷只读):
resourcePerJade: 100:1 玉璧 ≈ 100 单位资源。speedupPerJade: 60:1 玉璧 ≈ 60 分钟加速。- 玉璧本身按 1:1 计(锚点的单位就是玉璧)。
有了锚点,任何礼包都能折算成一个玉璧等价价值。再用它的售价(按 10 玉璧/元折成玉璧)去除,就得到一个无量纲的性价比千分比:
性价比‰ = 礼包内容的玉璧等价价值 / 礼包售价(折玉璧) × 1000
1000‰ 是基准线:等于「你花的玉璧,买回了等值的东西」。高于 1000‰ 是溢价(礼包比直充划算),低于 1000‰ 是折价(不如直充玉璧)。这条基准线就是锚点存在的意义——没有基准线,「超值」只是文案。
从内容到价值:一个礼包的账
以「混装大礼包」为例(售价 32800 分 = 3280 玉璧),内容是 3280 玉璧 + 50000 资源 + 3000 分钟加速:
- 玉璧:3280
- 资源:
50000 // 100 = 500玉璧等价 - 加速:
3000 // 60 = 50玉璧等价 - 合计价值:
3280 + 500 + 50 = 3830玉璧 - 性价比:
3830 × 1000 // 3280 = 1167‰
也就是说这个礼包比直充玉璧多给约 17% 的价值。礼包的设计目标就是让这个数落在某个区间里:新手礼包可以给到 1500‰(拉首充),常规补给包通常贴着 1000‰(不亏不赚),而加速包如果定价偏高会掉到 1000‰ 以下(玩家会直接用玉璧买加速)。
把每个礼包的价值拆成三列,就能看清「钱花在哪」:
| 礼包 | 玉璧价值 | 资源价值 | 加速价值 | 合计 | 售价(玉璧) |
|---|---|---|---|---|---|
| 新手礼包 | 0 | 90 | 0 | 90 | 60 |
| 资源补给包 | 0 | 120 | 0 | 120 | 120 |
| 加速包 | 0 | 0 | 150 | 150 | 300 |
| 玉璧包 | 680 | 0 | 0 | 680 | 680 |
| 混装大礼包 | 3280 | 500 | 50 | 3830 | 3280 |
这张表暴露了一个规律:纯玉璧礼包和纯资源礼包的性价比都贴近 1000‰,只有加速包严重偏低。因为玉璧是锚点的单位(1:1),资源按锚点 1:100 折算后自然贴合,而加速的锚点(1:60)是三者里最「贵」的——同样是 1 玉璧,玩家心里能换到的加速比锚点给的多。锚点定得越「抠」,对应礼包的性价比就越低。这也解释了一个反直觉的现象:改一个锚点数字,所有相关礼包的性价比就全变了——性价比不是礼包的固有属性,而是「礼包内容 × 锚点假设」的产物。
仿真:一份可排序的性价比表
下面这段是 S 层仿真脚本。它读 config/n06_gacha.json 的礼包与 config/pricing.json 的锚点、折扣档,算出每个礼包的基础性价比与 d30 / d50 折扣后的性价比,输出 JSON + CSV + SVG:
# file: sim/n06_bundle_value.py
# tier: S
# verify: python3 sim/n06_bundle_value.py --config config/ --out out/
"""礼包性价比仿真:读 config/n06_gacha.json 的礼包与 config/pricing.json 的价值锚点
(resourcePerJade / speedupPerJade)与折扣档,把礼包内容折算成玉璧等价价值,算性价比千分比
(1000‰ = 与直购玉璧 1:1),并给出 d30 / d50 折扣后的性价比。
产出 out/n06_bundle.json + out/n06_bundle.csv + out/n06_bundle.svg。"""
from __future__ import annotations
import argparse
import csv
import json
import os
from svg_line import render
def load(cfg_dir: str, name: str) -> dict:
with open(os.path.join(cfg_dir, f"{name}.json"), encoding="utf-8") as fh:
return json.load(fh)
def bundle_value_jade(bundle: dict, anchor: dict) -> int:
"""礼包内容的玉璧等价价值:玉璧 + 资源/资源锚 + 加速/加速锚(整数地板除)。"""
return (bundle["jade"] + bundle["resource"] // anchor["resourcePerJade"]
+ bundle["speedupMinutes"] // anchor["speedupPerJade"])
def main() -> int:
here = os.path.dirname(os.path.abspath(__file__))
ap = argparse.ArgumentParser()
ap.add_argument("--config", default=os.path.join(here, "..", "config"))
ap.add_argument("--out", default=os.path.join(here, "..", "out"))
args = ap.parse_args()
os.makedirs(args.out, exist_ok=True)
cfg, pricing = load(args.config, "n06_gacha"), load(args.config, "pricing")
anchor = pricing["bundleAnchor"]
cents_per_jade = 100 // pricing["currency"]["perYuan"]
disc = {d["id"]: d["offPct"] for d in pricing["discount"]["tiers"]}
rows = []
for b in cfg["bundles"]:
vj = bundle_value_jade(b, anchor)
price_jade = b["priceCents"] // cents_per_jade
price30 = (b["priceCents"] * (100 - disc["d30"]) // 100) // cents_per_jade
price50 = (b["priceCents"] * (100 - disc["d50"]) // 100) // cents_per_jade
rows.append({"id": b["id"], "name": b["name"], "priceCents": b["priceCents"],
"valueJade": vj, "priceJade": price_jade,
"basePermille": vj * 1000 // price_jade,
"d30Permille": vj * 1000 // price30, "d50Permille": vj * 1000 // price50})
rows.sort(key=lambda r: r["basePermille"], reverse=True)
with open(os.path.join(args.out, "n06_bundle.csv"), "w", encoding="utf-8", newline="") as fh:
w = csv.writer(fh)
w.writerow(["id", "name", "priceCents", "valueJade", "priceJade", "basePermille", "d30Permille", "d50Permille"])
for r in rows:
w.writerow([r["id"], r["name"], r["priceCents"], r["valueJade"], r["priceJade"],
r["basePermille"], r["d30Permille"], r["d50Permille"]])
render([r["basePermille"] for r in rows], os.path.join(args.out, "n06_bundle.svg"),
"bundle value permille (1000 = parity with jade)")
report = {"version": 1, "anchor": anchor, "centsPerJade": cents_per_jade, "bundles": rows,
"best": rows[0]["id"], "worst": rows[-1]["id"], "medianPermille": rows[len(rows) // 2]["basePermille"]}
with open(os.path.join(args.out, "n06_bundle.json"), "w", encoding="utf-8") as fh:
json.dump(report, fh, ensure_ascii=False, indent=2)
print(json.dumps(report, ensure_ascii=False))
return 0
if __name__ == "__main__":
raise SystemExit(main())
本机实跑产出的性价比表 out/n06_bundle.csv(按基础性价比降序):
id,name,priceCents,valueJade,priceJade,basePermille,d30Permille,d50Permille
b_starter,新手礼包,600,90,60,1500,2142,3000
b_mixed,混装大礼包,32800,3830,3280,1167,1668,2335
b_resource,资源补给包,1200,120,120,1000,1428,2000
b_jade,玉璧包,6800,680,680,1000,1428,2000
b_speedup,加速包,3000,150,300,500,714,1000
五个礼包的性价比从 1500‰ 排到 500‰,基准线 1000‰ 正好把「划算」与「不划算」切开。几个读法:
- 新手礼包 1500‰:60 玉璧的价格给到 90 玉璧的价值,是典型的「拉首充」设计——新手期给溢价,是为了把非付费玩家推过第一道付费门槛。
- 资源补给包与玉璧包都是 1000‰:贴着基准线。这说明礼包内容就是「按锚点定价」的——资源包给
12000 // 100 = 120玉璧等价资源,卖 120 玉璧,不多给不少给。它们是「不想动脑时的等价兑换」。 - 加速包 500‰:只有基准线的一半。同样 300 玉璧,直充能买 300 玉璧的东西,加速包只给 150 玉璧等价。这是锚点在说「加速道具在玩家心里比锚点更便宜」——要么锚点定错了,要么这个礼包就是设计成不划算的(逼玩家直充)。
三个礼包、三种定价意图
把表拆开看,这几个数不是随机的,它们对应三种不同的商业化意图:
- 1500‰(新手礼包)——拉门槛。新手期的核心指标是「首充转化」,溢价是买转化的成本。玩家花了 6 元,拿到的东西比直充多 50%,这个「多」是设计给的,不是算错。
- 1000‰(资源包 / 玉璧包)——等价兑换。它们是货架上的「标准品」,给的是「不想比较时闭眼买」的选项。性价比贴着基准线,意味着礼包内容严格按锚点定价。
- 500‰(加速包)——引导直充。加速道具在玩家心里的价值低于锚点,如果礼包给到 1000‰,玩家会拿礼包当「加速优惠券」大量囤积,冲击直充玉璧。把它压到 500‰,是有意让礼包不如直充,保护直充这个基本盘。
这三档意图合起来说明一件事:性价比不是一个「越高越好」的指标,而是一个「该高就高、该低就低」的设计旋钮。新手礼包要高(拉转化),日常补给要平(不亏不赚),战略物资(加速)要低(护直充)。
折扣怎么改变性价比排序
折扣档来自 config/pricing.json 的 discount.tiers:d30 是 7 折、d50 是 5 折。折后性价比 = 价值不变、售价打折,所以折扣直接乘在性价比上:
| 礼包 | 基础 | d30(7 折) | d50(5 折) |
|---|---|---|---|
| 新手礼包 | 1500‰ | 2142‰ | 3000‰ |
| 混装大礼包 | 1167‰ | 1668‰ | 2335‰ |
| 资源补给包 | 1000‰ | 1428‰ | 2000‰ |
| 玉璧包 | 1000‰ | 1428‰ | 2000‰ |
| 加速包 | 500‰ | 714‰ | 1000‰ |
关键观察在最后一行:加速包基础 500‰ 是全场最差,但打 5 折后变成 1000‰,正好拉回基准线。这解释了 SLG 里一个常见现象——折扣是「把不划算的东西暂时变成划算」的工具。加速包平时放在那里没人买,一旦挂上 5 折标签,性价比瞬间对齐直充玉璧,玩家会觉得自己「捡到了」。折扣没有改变礼包的内容,只改变了分母。
折后性价比 = 基础性价比 × 100 / (100 − offPct)。d30(offPct = 30)的倍率是 100 / 70,d50 的倍率是 100 / 50 = 2。用整数算:加速包 500 × 100 / 70 = 714‰,500 × 100 / 50 = 1000‰——和表里的数完全对上。记住这个倍率,就不用每次重跑脚本:折扣对性价比的抬升与礼包内容无关,只与折扣率有关。
第 10 章会把折扣的副作用(价格锚点被击穿、原价购买者不满、复购预期)讲透,本节只给出可复算的性价比口径——任何一次折扣活动上线前,先用这张表算一遍折后排序,就知道会不会「把最划算的礼包折成最不划算的」。
锚点从哪里来
锚点不是凭空写的。resourcePerJade: 100 背后是一套换算逻辑:玩家每小时产出多少资源(卷一第 10 章的产出曲线)、直充 1 元得 10 玉璧、以及「1 玉璧换多少资源玩家才觉得值」的市场参照。三者对齐后,锚点才成立。
举个具体的对齐例子:直充 1 元得 10 玉璧(perYuan: 10)。如果玩家一小时能产出约 1000 资源,而「1 元 = 10 玉璧 = 1000 资源」在直觉上成立,那么锚点就是 1000 / 10 = 100 资源/玉璧。锚点本质上是「把时间价值换算成货币价值」的汇率:资源是时间产出的,玉璧是钱买的,锚点就是两者之间的那座桥。桥搭得准,礼包的性价比才有意义;桥搭歪了,再漂亮的性价比表也是错的。
这里必须划清可复现边界:锚点的数值来源不可复现。它要么来自真实付费数据(玩家实际愿意用多少玉璧换 100 资源),要么来自竞品的公开可观察定价,两者都属于 X 层——需真实业务环境,命令行不可复现。本节的脚本能做的是:给定锚点,算出所有礼包的性价比;它做不了的是:告诉你锚点该定成 100 还是 80。后者要靠真实投放数据回归,不在本套范围内。
锚点的三个用法
价值锚点不是一个数字,是一套口径。它在商业化里有三个用法:
- 定价:礼包定价先按锚点算出价值,再决定给几成溢价。溢价区间(比如新手 1500‰、常规 1000‰)是有意的设计选择,不是随机数。
- 排序:所有礼包放进同一张表,性价比自动排序。运营做「超值礼包」活动时,选的是排序靠前的礼包,而不是文案写「超值」的礼包。
- 折扣校准:折后性价比 = 基础 ×
100 / (100 - offPct)。用这条公式,任何一个折扣档对任何一个礼包的效果都能提前算出来,不用等上线看数据。
最后强调边界:锚点本身是设计假设,不是客观真理。resourcePerJade: 100 意味着「设计者认为 1 玉璧值 100 资源」;如果玩家实际用脚投票(都去直充玉璧买资源),说明锚点定高了。锚点的校准需要真实付费数据,属于上一节说的 X 层边界。
两个常见错误
用这张表做礼包设计时,有两个反复出现的错误:
- 把性价比当成唯一指标。性价比高不等于该上架。一个 3000‰ 的礼包会瞬间击穿所有直充的性价比,玩家会等这个礼包而不直充——溢价本身会稀释直充的价值。所以新手礼包 1500‰ 只在「首充一次」的约束下成立,不能天天卖。
- 忘了锚点是相对值。性价比是「内容 ÷ 售价」的相对量,两个礼包比的是彼此,不是绝对价值。如果锚点整体上调 20%,所有礼包的性价比会同比例下降,但排序不变。排序才是设计要盯的东西,绝对值随锚点走。
礼包性价比 vs 抽卡成本
6.2 算过:抽到首个 SSR 平均要 40.4 抽 × 150 玉璧 = 6067 玉璧。把 6.3 的礼包放进这个坐标里,就能回答玩家最常问的一个问题——买礼包能不能省下抽卡的钱。
以玉璧包(6800 分 = 680 玉璧)为例:它的性价比是 1000‰,也就是买它和直充玉璧等价,换算成抽数就是 680 // 150 = 4 抽。它不会让抽卡变便宜,只是把「充 68 元」拆成一个更好接受的礼包形态。
而新手礼包(600 分 = 60 玉璧,性价比 1500‰)折算的「等价玉璧」是 90 玉璧,90 // 150 = 0——连一抽都不到。这说明礼包的性价比溢价要落到玩家真正需要的东西上才有意义:新手礼包给的是资源不是玉璧,它的价值在开荒期,不在抽卡。
把两节的坐标对齐,一个付费点的三条线就清楚了:
- 6.1 定结构:卡池的概率表怎么公示。
- 6.2 定成本:一次抽卡的期望成本是多少玉璧。
- 6.3 定性价比:一笔钱换成礼包,能抵多少次抽卡、多少资源。
三者串起来,才是「一个付费点从结构到定价的完整账」。
小结
礼包性价比 = 内容玉璧等价价值 / 售价玉璧 × 1000,基准线 1000‰。本节的 S 层脚本用 config/pricing.json 的锚点把五个礼包算成一张可排序的表,跑出新手礼包 1500‰、加速包 500‰ 的分布,并演示了折扣如何把 500‰ 拉回 1000‰。礼包值不值,从今天起是一个能被脚本复算的数,而不是一句文案。
一句话记住这一章:结构看公示、成本看期望、性价比看锚点。三个数都能跑,三个数都能对账。
回到这一章的三节:6.1 定结构、6.2 算抽卡成本、6.3 算礼包性价比——一个付费点的结构、成本、性价比三件事,都有可跑的账。下一章离开单个付费点,进入「跟谁打」的问题:匹配与分段。
阅读导航:上一节:6.2 期望成本与「多少抽出货」 · 下一节:7.1 匹配分与 ELO/Glicko 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。