9.1 定了付费点摆在哪,9.2 拆了三类常规付费点的价值。还剩两个问题:这些付费点在什么时候出现,以及怎样在赚钱的同时不把免费玩家逼走。这两个问题在本节合成一件事——付费节奏与体验保护。节奏是时间轴上的布局,保护是免费玩家的底线。前者管「什么时候卖」,后者管「免费玩家还能不能好好玩」。
先明确一个立场:体验保护不是慈善,是长期收入的前提。免费玩家是付费玩家的对手、队友和内容消费者——一个没有免费玩家的 SLG,付费玩家也留不住。所以保护免费体验,本质是保护付费玩家的游戏环境。本节把它写成可量化的门槛,而不是一句口号。
付费节奏:什么时候出现
付费节奏指的是每个付费点在生命周期第几天首次出现。全都在第 1 天铺上去,玩家一进游戏就被付费页糊脸,直接劝退;全都不出现,又浪费了转化窗口。本套的节奏表在 config/n09_protection.json 的 rhythm 里:
| 天数 | 付费点 | 说明 |
|---|---|---|
| 第 1 天 | 首充 | 新手首充,第 1 天即出现 |
| 第 1 天 | 赛季通行证 | 赛季开服即上架,绑定整个赛季 |
| 第 3 天 | 月卡 | 度过最早期后再上架,避免劝退 |
| 第 7 天 | 资源礼包 | 成长期第一次资源缺口时补给 |
节奏设计有两条原则:一是新手期只放「低门槛、高感知」的点(首充 6 元、通行证一眼看到全部奖励),让玩家先感受到「这游戏值得付一次」;二是把「资源缺口」和「付费点」对齐——资源礼包放在第 7 天,正是因为成长期第一次出现资源紧张,这时候玩家才有购买动机。在玩家不需要的时候卖东西,是无效曝光;在玩家最需要的时候卖,才是节奏。
体验保护:免费玩家的达成度
体验保护要回答一个具体问题:免费玩家能不能在合理时间内跟上内容。这里引入一个可量化的指标——达成度:
达成度(第 d 天) = 免费玩家累计资源 / 第 d 天的内容需求资源 × 100%
- 累计资源 = 免费玩家日均产出 × 天数。
- 内容需求资源 = 游戏到第 d 天累积释放的内容(城建、科技、兵种解锁)所需的资源总量,按里程碑插值。
达成度越高,说明免费玩家越跟得上;达成度首次跌破门槛的那一天,就是「付费墙」——从这里开始,免费玩家的进度明显落后于内容释放速度。下面这段是 D 层配置,定义免费 / 付费玩家的日产出、内容需求里程碑与保护门槛:
{
"version": 1,
"note": "第 9 章付费节奏与体验保护参数(n09 前缀)。免费/付费玩家日资源产出、内容需求里程碑、体验保护门槛与付费节奏表。资源一律整数单位,比率整数百分比,禁止浮点。",
"players": {
"free": { "dailyResource": 60000 },
"payer": { "dailyResource": 180000 }
},
"contentMilestones": [
{ "day": 0, "requiredResource": 0 },
{ "day": 7, "requiredResource": 450000 },
{ "day": 14, "requiredResource": 1150000 },
{ "day": 30, "requiredResource": 3200000 },
{ "day": 60, "requiredResource": 7600000 }
],
"protection": {
"minDay7AchievementPct": 75,
"minPaywallDay": 10,
"achievementFloorPct": 80
},
"sensitivityFreeDaily": [54000, 60000, 66000],
"rhythm": [
{ "day": 1, "point": "first_charge", "note": "新手首充,第 1 天即出现" },
{ "day": 1, "point": "battle_pass", "note": "赛季开服即上架,绑定整个赛季" },
{ "day": 3, "point": "monthly_card", "note": "度过最早期后再上架,避免劝退" },
{ "day": 7, "point": "resource_bundle", "note": "成长期第一次资源缺口时补给" }
]
}
保护门槛有三个:minDay7AchievementPct: 75(第 7 天达成度不低于 75%)、minPaywallDay: 10(付费墙不早于第 10 天)、achievementFloorPct: 80(达成度门槛,跌破即算付费墙)。下面这段是 S 层仿真脚本,逐日算达成度曲线、定位付费墙、做门槛判定与敏感性扫描:
# file: sim/n09_rhythm_protection.py
# tier: S
# verify: python3 sim/n09_rhythm_protection.py --config config/ --out out/
"""付费节奏与体验保护仿真:读 config/n09_protection.json 的免费 / 付费玩家日产出、
内容需求里程碑与保护门槛,逐日算免费玩家的达成度曲线(累计资源 / 内容需求),
找出「付费墙」出现的天数(达成度首次跌破门槛),并对保护门槛给出 PASS/WARN 判定,
再做免费日产出 ±20% 的敏感性扫描。产出 out/n09_protection.json + .csv + .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 required_at(day: int, milestones: list[dict]) -> int:
"""内容需求资源:在里程碑之间线性插值,超出最后里程碑按最后一段斜率外推。"""
if day <= milestones[0]["day"]:
return milestones[0]["requiredResource"]
for i in range(1, len(milestones)):
a, b = milestones[i - 1], milestones[i]
if day <= b["day"]:
span = b["day"] - a["day"]
return a["requiredResource"] + (b["requiredResource"] - a["requiredResource"]) * (day - a["day"]) // span
a, b = milestones[-2], milestones[-1]
slope = (b["requiredResource"] - a["requiredResource"]) // (b["day"] - a["day"])
return b["requiredResource"] + slope * (day - b["day"])
def achievement_pct(cum: int, required: int) -> int:
if required <= 0:
return 100
return min(100, cum * 100 // required)
def paywall_day(daily: int, milestones: list[dict], floor_pct: int, horizon: int = 60) -> int:
"""达成度首次跌破门槛的天数;若整个窗口都达标返回 0。"""
for d in range(1, horizon + 1):
if achievement_pct(daily * d, required_at(d, milestones)) < floor_pct:
return d
return 0
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 = load(args.config, "n09_protection")
ms = cfg["contentMilestones"]
prot = cfg["protection"]
free_daily = cfg["players"]["free"]["dailyResource"]
payer_daily = cfg["players"]["payer"]["dailyResource"]
horizon = ms[-1]["day"]
rows = []
curve = []
for d in range(1, horizon + 1):
req = required_at(d, ms)
fc, pc = free_daily * d, payer_daily * d
fa = achievement_pct(fc, req)
pa = achievement_pct(pc, req)
curve.append(fa)
rows.append({"day": d, "required": req, "freeCum": fc, "freePct": fa, "payerPct": pa})
wall = paywall_day(free_daily, ms, prot["achievementFloorPct"], horizon)
day7 = achievement_pct(free_daily * 7, required_at(7, ms))
checks = [
{"id": "day7_achievement", "want": f">={prot['minDay7AchievementPct']}", "got": day7,
"pass": day7 >= prot["minDay7AchievementPct"]},
{"id": "paywall_day", "want": f">={prot['minPaywallDay']}", "got": wall,
"pass": wall >= prot["minPaywallDay"]},
]
sens = []
for daily in cfg["sensitivityFreeDaily"]:
w = paywall_day(daily, ms, prot["achievementFloorPct"], horizon)
sens.append({"freeDaily": daily, "paywallDay": w, "day7Pct": achievement_pct(daily * 7, required_at(7, ms))})
with open(os.path.join(args.out, "n09_protection.csv"), "w", encoding="utf-8", newline="") as fh:
w = csv.writer(fh)
w.writerow(["day", "required", "freeCum", "freePct", "payerPct"])
for r in rows:
w.writerow([r["day"], r["required"], r["freeCum"], r["freePct"], r["payerPct"]])
render(curve, os.path.join(args.out, "n09_protection.svg"),
"free player achievement pct by day (floor = 80)")
report = {
"version": 1, "freeDaily": free_daily, "payerDaily": payer_daily,
"floorPct": prot["achievementFloorPct"], "paywallDay": wall, "day7AchievementPct": day7,
"checks": checks, "allPass": all(c["pass"] for c in checks), "sensitivity": sens,
"rhythm": cfg["rhythm"],
"sample": [{"day": r["day"], "required": r["required"], "freePct": r["freePct"], "payerPct": r["payerPct"]}
for r in rows if r["day"] in (7, 14, 30, 60)],
}
with open(os.path.join(args.out, "n09_protection.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())
本机实跑(python3 sim/n09_rhythm_protection.py --config config/ --out out/)产出 out/n09_protection.json 的关键字段:
freeDaily=60000 payerDaily=180000 floorPct=80
paywallDay=11 day7AchievementPct=93
checks: day7_achievement(>=75)=93 PASS ; paywall_day(>=10)=11 PASS
sensitivity: 54000->paywall 8, day7 84 | 60000->paywall 11, day7 93 | 66000->paywall 15, day7 100
out/n09_protection.csv 里几个里程碑天的达成度(免费 / 付费):
day,required,freeCum,freePct,payerPct
7,450000,420000,93,100
14,1150000,840000,73,100
30,3200000,1800000,56,100
60,7600000,3600000,47,100
读法:免费玩家第 7 天达成度 93%(内容需求 45 万,累计 42 万),跟得上;到第 14 天掉到 73%,第 30 天 56%,第 60 天 47%——内容释放速度超过了免费玩家的产出速度,达成度持续下滑。付费玩家(3 倍产出)全程 100%,从未落后。达成度首次跌破 80% 的门槛是在第 11 天(第 11 天累计 66 万,需求 85 万,达成度 77%),这就是付费墙的位置。
门槛判定:PASS 与敏感性
两个门槛判定都是 PASS:第 7 天 93% ≥ 75%、付费墙第 11 天 ≥ 第 10 天。但 PASS 不代表安全——脚本顺手做了免费日产出 ±10% 的敏感性扫描:
| 免费日产出 | 第 7 天达成度 | 付费墙天数 | 判定 |
|---|---|---|---|
| 54000(-10%) | 84% | 8 | ❌ 早于第 10 天 |
| 60000(基准) | 93% | 11 | ✅ |
| 66000(+10%) | 100% | 15 | ✅ |
免费日产出只要降 10%,付费墙就从第 11 天提前到第 8 天,直接跌破保护线。这揭示了一个关键事实:体验保护的裕度很窄。免费玩家的日产出是一个被很多系统牵动的数(建筑产出、活动补贴、税收)——任何一个系统调低它,都可能把付费墙往前推。所以保护门槛不能只看「当前 PASS」,必须看裕度:基准产出离「付费墙跌破第 10 天」的临界点有多远。本套的基准留了约 10% 的裕度,属于偏紧。
付费墙不是墙,是「加速比」
要正确理解付费墙,得先纠正一个直觉:付费墙不是「不付费就过不去」。看曲线——免费玩家第 60 天达成度 47%,但达成度是累计资源 / 需求,免费玩家并没有停下,只是速度慢。付费玩家 3 倍产出,是加速比 3:1,不是「能玩 / 不能玩」。
加速比 = 付费日产出 / 免费日产出 = 180000 / 60000 = 3
所以体验保护的正确表述不是「免费玩家能到 100%」,而是「免费玩家能在一个可接受的时间内达成核心目标」。核心目标(比如主城升到关键等级、解锁核心兵种)应该在免费玩家愿意投入的时间内可达。如果核心目标需要 90 天,而玩家平均只玩 30 天——那才叫体验崩了。保护的本质是「时间可达性」,不是「零付费可达性」。
这也解释了为什么付费墙定在「达成度跌破 80%」而不是「跌破 0%」:玩家不需要 100% 跟上内容,只需要感觉「我没被落下太多」。80% 是一个心理阈值——低于它,玩家开始明显感到「不付费就跟不上」,负面情绪开始累积。门槛设在哪里,是体验设计的选择;能不能守住,是数值设计的责任。
三个体验保护门槛
把本节的判据抽成三条可复用的门槛:
- 新手保护:第 7 天达成度 ≥ 75%。新手期是留存生死线,免费玩家在第一个星期必须「跟得上」。
- 付费墙位置:不早于第 10 天。太早出现付费墙,玩家还没建立情感投入就被劝退。
- 裕度:基准免费产出离付费墙临界点至少留 10%。只看当前 PASS 不够,要留缓冲——因为免费产出会被多个系统牵动。
三条门槛合起来,就是付费节奏与体验保护的验收标准:节奏表管「什么时候卖」,三条门槛管「免费玩家还留得住吗」。前者是主动作,后者是安全线。
边界必须说清:本节的内容需求里程碑(7 天 45 万、60 天 760 万)是设计假设,不是实测值。真实的内容释放速度取决于具体玩法设计,玩家真实的日产出还受在线时长、活动参与度影响。这些属于 X 层——需真实业务环境,命令行不可复现。本节能做的是:给定产出与需求曲线,算出达成度、付费墙与裕度;它做不了的是:告诉你需求曲线该定成多少。后者要靠真实留存与付费数据回归。
小结
付费节奏与体验保护 = 时间轴上的付费点布局 + 免费玩家达成度门槛。本节的 S 层脚本用 config/n09_protection.json 跑出免费玩家达成度曲线(第 7 天 93% → 第 60 天 47%)、定位付费墙在第 11 天、并做敏感性扫描证明「免费产出降 10% 就跌破保护线」。付费墙在哪、裕度够不够,从今天起是两个能被脚本复算的数。
一句话记住本节:节奏管什么时候卖,保护管免费玩家还留不留得住。前者是主动作,后者是安全线。
回到这一章的三节:9.1 铺布局、9.2 拆常规付费点、9.3 定节奏与保护线——付费点从「摆在哪、值多少、什么时候卖、会不会逼走人」四个问题,全部落成了可跑的数。下一章进入定价本身:给定这些付费点,价格该定几档、锚点怎么设。
阅读导航:上一节:9.2 首充、月卡、通行证 · 下一节:10.1 定价锚点与档位设计 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。