做选型决策之前,先得有一张能对话、也能被程序消费的品类表。先把定位说清楚:站点里 小游戏创作平台专题 那 24 篇讲的是做小游戏创作平台(SaaS 创业口径:商业计划、融资、编辑器产品),而本章是为单款游戏做品类选型决策——用同一套可跑、可调参、可断言的评分模型,帮你在超休闲、合成、放置等品类之间做取舍。两者口径不同:前者面向平台方,后者面向一款具体产品的立项人。
为什么需要一张品类表
品类讨论最常见的失败,是「鸡同鸭讲」。有人说「休闲游戏留存差」,指的是超休闲;有人反驳「我这款留存很好」,指的是模拟经营——两者根本不在同一个格子里。要避免这种争论,第一步不是争论优劣,而是先把格子画出来。
一张好的品类表要满足三个条件:
- 互斥且可判:一个产品能明确落进某个格子,而不是「像 A 又像 B」。
- 维度正交:分类用的几个轴互不重复,避免把同一个信息数两遍。
- 机器可读:能被脚本读进来做统计、打分、画图,而不是只躺在 PPT 里。
第 2 章的竞品拆解已经给了「怎么看一个竞品」的方法;这一章要把方法沉淀成可复用的结构。第 3.2 节给每个格子填上商业特征数字,第 3.3 节再把这些数字算成一个可比较的分数。本节先解决「格子怎么画」。
分类的三个正交轴
本书用三个轴来切分小游戏品类,它们互相正交,合起来足以定位一款产品。
轴一:玩法核心
玩法核心回答「玩家每分钟在做什么」。小游戏的主流核心循环大致收敛为几类:单点操作(点/滑,无成长)、合成(同类合并升级)、放置(离线累积、上线收取)、闯关(按关推进)、实时竞技(同场淘汰)、经营(经营—产出—扩建)、布防(塔防波次)、解谜(思考型单关)。这条轴决定了原型阶段要验证什么。
轴二:变现模式
变现模式回答「钱从哪里来」。小游戏生态里主要有三种形态:
- IAA:以内购为辅助、以广告为主要收入;
- 混合:广告与内购并重,通常内购提供去广告、加速、皮肤等;
- IAP:纯内购驱动。
这里有一个值得记住的生态事实:小游戏里纯 IAP 极为罕见。原因不难理解——小游戏的用户预期是「免费点开就玩」,把核心体验锁在内购后面,会直接掐死自然量与分享链路。所以真实的品类分布里,IAA 与混合占了绝大多数。
轴三:内容量级与单局时长
内容量级回答「这套内容能撑多久」,单局时长回答「一次打开的承诺有多重」。两者常一起出现:内容量级高的品类(如闯关、经营)往往单局偏长,而内容量级低的品类(如超休闲、IO)单局更短、更依赖即时反馈。这条轴直接影响后文的包体预算与关卡产能评估。
把分类表写成可被程序消费的 config(D 层)
下面这段是 D 层静态配置,即品类分类体系本身。它用三个轴把 8 个品类落格,并附带 8 个评价维度与一组整数权重(权重供 3.3 节使用)。注意:分类属性是行业归纳的示例口径,标为「公开可观察」,不是任何平台后台实测;所有数字都是整数,无浮点。
{
"version": 1,
"series": "《小游戏开发实战》",
"targetGame": "merge-island",
"observedAt": "2026-10",
"note": "小游戏品类分类体系与评价维度。分类属性为【示例 / 公开可观察】口径的行业归纳,不是任何平台后台实测;维度权重是整数百分比,合计 100,供第 3 章加权评分与敏感度分析使用。",
"units": { "sessionSec": "单局时长(秒,整数)", "weight": "整数百分比,合计 100", "score": "0–100 整数有利度分" },
"genres": [
{ "id": "hyper-casual", "cn": "超休闲", "en": "Hyper-casual", "coreLoop": "单指点击 / 滑动,无长期成长线", "monetization": "IAA", "sessionSec": 45, "contentVolume": "低", "example": "示例:单点操作类休闲产品", "note": "上手零门槛,靠买量放大;留存天花板低,内容复用弱。" },
{ "id": "merge", "cn": "合成", "en": "Merge", "coreLoop": "拖拽同类合并升级,产出资源再投入", "monetization": "混合", "sessionSec": 90, "contentVolume": "中", "example": "示例:合成 + 装扮类产品", "note": "反馈密集、易做长线;难点是合成链与资源经济。" },
{ "id": "idle", "cn": "放置", "en": "Idle", "coreLoop": "离线累积,上线收取与升级", "monetization": "混合", "sessionSec": 120, "contentVolume": "中", "example": "示例:放置养成类产品", "note": "对留存与在线时长友好;前期爽感弱,需快速给正反馈。" },
{ "id": "level", "cn": "闯关", "en": "Level-based", "coreLoop": "按关卡推进,难度逐级抬升", "monetization": "IAA", "sessionSec": 75, "contentVolume": "高", "example": "示例:三消 / 跑酷类产品", "note": "关卡即内容,产能压力大;包体与首包约束更紧。" },
{ "id": "io", "cn": "IO", "en": "IO Arena", "coreLoop": "多人同场实时竞技,单局淘汰制", "monetization": "IAA", "sessionSec": 60, "contentVolume": "低", "example": "示例:.io 竞技类产品", "note": "天然具备裂变话题;依赖实时同步与匹配,长线内容薄。" },
{ "id": "sim", "cn": "模拟经营", "en": "Simulation", "coreLoop": "经营—产出—扩建的循环", "monetization": "混合", "sessionSec": 150, "contentVolume": "高", "example": "示例:小镇经营类产品", "note": "留存与付费天花板高;系统复杂,开发与调优成本最大。" },
{ "id": "tower-defense", "cn": "塔防", "en": "Tower Defense", "coreLoop": "布防—波次—升级的防守循环", "monetization": "混合", "sessionSec": 100, "contentVolume": "中", "example": "示例:塔防闯关类产品", "note": "数值与关卡可复用;核心手感在节奏与平衡。" },
{ "id": "puzzle", "cn": "益智", "en": "Puzzle", "coreLoop": "解谜 / 推理,单关思考型", "monetization": "IAA", "sessionSec": 80, "contentVolume": "高", "example": "示例:解谜益智类产品", "note": "口碑与自然量好;关卡设计与验证成本高。" }
],
"dimensions": [
{ "id": "devCost", "cn": "开发成本可控度", "direction": "higher_better", "desc": "越高表示同等团队规模下越快做出来" },
{ "id": "monetization", "cn": "变现效率", "direction": "higher_better", "desc": "越高表示人均收入潜力越大" },
{ "id": "retentionCeiling", "cn": "留存天花板", "direction": "higher_better", "desc": "越高表示长期留存的可用上限越高" },
{ "id": "uaDifficulty", "cn": "买量可控度", "direction": "higher_better", "desc": "越高表示获客越便宜、越可控" },
{ "id": "viralPotential", "cn": "裂变潜力", "direction": "higher_better", "desc": "越高表示越容易自发传播" },
{ "id": "contentReuse", "cn": "内容复用度", "direction": "higher_better", "desc": "越高表示一套内容能撑更久" },
{ "id": "platformFit", "cn": "平台适配度", "direction": "higher_better", "desc": "越高表示越契合小游戏包体与首包约束" },
{ "id": "differentiation", "cn": "差异化空间", "direction": "higher_better", "desc": "越高表示越容易做出不同" }
],
"weights": { "devCost": 18, "monetization": 20, "retentionCeiling": 16, "uaDifficulty": 14, "viralPotential": 10, "contentReuse": 8, "platformFit": 8, "differentiation": 6 }
}
这张表把「分类」和「评价」分开了:genres 是分类(互斥落格),dimensions 是评价轴(每个品类都能在每条轴上给分),weights 是权重(决策偏好)。分开的好处是,3.3 节改权重时不需要动分类,改分类时也不需要动权重。
读表统计:分布与单局时长(S 层)
分类表要能回答「生态长什么样」。下面这段是 S 层仿真脚本,读 config/ch03_genres.json,对 8 个品类按变现模式、内容量级做计数,并给出单局时长的 min / max / 中位数,产出 JSON 与一张纯 Python SVG 条形图。
#!/usr/bin/env python3
# file: sim/ch03_taxonomy.py
# tier: S
# verify: /opt/homebrew/bin/python3 sim/ch03_taxonomy.py --config config/ --out out/
"""品类分类体系的统计维度:读 config/ch03_genres.json,对 8 个品类按
变现模式 / 内容量级 / 单局时长做计数与分布,产出 JSON + SVG。
模型(全整数,无浮点):
- byMonetization / byContentVolume:计数分布。
- sessionSec:单局时长(秒),给出 min / max / 中位数(偶数取中间两数地板平均)。
- 图:各变现模式的品类数量条形图,零计数也画出(说明生态结构)。
纯标准库 + xml.etree 手写 SVG,零依赖。
"""
from __future__ import annotations
import argparse
import json
import os
import xml.etree.ElementTree as ET
def load_config(path: str) -> dict:
with open(path, encoding="utf-8") as f:
return json.load(f)
def count_by(genres: list[dict], key: str) -> dict:
out: dict[str, int] = {}
for g in genres:
out[g[key]] = out.get(g[key], 0) + 1
return out
def median_int(values: list[int]) -> int:
vs = sorted(values)
n = len(vs)
if n == 0:
return 0
if n % 2 == 1:
return vs[n // 2]
return (vs[n // 2 - 1] + vs[n // 2]) // 2
def write_svg(path: str, labels: list[str], counts: list[int], title: str) -> int:
W, H, PAD, gap = 640, 360, 60, 30
n = len(labels)
vmax = max(counts) or 1
plot_h = H - 2 * PAD
plot_w = W - 2 * PAD
bar_w = (plot_w - gap * (n + 1)) / n
svg = ET.Element("svg", {"xmlns": "http://www.w3.org/2000/svg",
"width": str(W), "height": str(H),
"viewBox": f"0 0 {W} {H}"})
ET.SubElement(svg, "rect", {"width": str(W), "height": str(H), "fill": "#ffffff"})
ET.SubElement(svg, "text", {"x": str(PAD), "y": "28", "font-size": "15",
"font-family": "sans-serif"}).text = title
ET.SubElement(svg, "line", {"x1": str(PAD), "y1": str(H - PAD),
"x2": str(W - PAD), "y2": str(H - PAD),
"stroke": "#9ca3af", "stroke-width": "1"})
for i, (lab, c) in enumerate(zip(labels, counts)):
x = PAD + gap + i * (bar_w + gap)
h = c / vmax * plot_h
y = H - PAD - h
ET.SubElement(svg, "rect", {"x": f"{x:.1f}", "y": f"{y:.1f}",
"width": f"{bar_w:.1f}", "height": f"{h:.1f}",
"fill": "#2563eb" if c > 0 else "#d1d5db"})
ET.SubElement(svg, "text", {"x": f"{x + bar_w / 2:.1f}", "y": f"{y - 6:.1f}",
"font-size": "12", "text-anchor": "middle",
"font-family": "sans-serif"}).text = str(c)
ET.SubElement(svg, "text", {"x": f"{x + bar_w / 2:.1f}", "y": str(H - PAD + 20),
"font-size": "12", "text-anchor": "middle",
"font-family": "sans-serif"}).text = lab
ET.ElementTree(svg).write(path, encoding="utf-8", xml_declaration=True)
return os.path.getsize(path)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--config", default="config/")
ap.add_argument("--out", default="out/")
args = ap.parse_args()
cfg = load_config(os.path.join(args.config, "ch03_genres.json"))
os.makedirs(args.out, exist_ok=True)
genres = cfg["genres"]
by_mon = count_by(genres, "monetization")
by_vol = count_by(genres, "contentVolume")
sessions = [g["sessionSec"] for g in genres]
# 固定展示顺序(含零计数),保证图表可复现并暴露生态结构
mon_labels = ["IAA", "混合", "IAP"]
mon_counts = [by_mon.get(m, 0) for m in mon_labels]
vol_labels = ["低", "中", "高"]
vol_counts = [by_vol.get(v, 0) for v in vol_labels]
svg_bytes = write_svg(os.path.join(args.out, "ch03_taxonomy.svg"),
mon_labels, mon_counts,
"各变现模式的品类数量(8 个品类)")
report = {
"version": 1,
"source": "sim/ch03_taxonomy.py",
"genreCount": len(genres),
"dimensionCount": len(cfg["dimensions"]),
"byMonetization": by_mon,
"byContentVolume": by_vol,
"sessionSecMin": min(sessions),
"sessionSecMax": max(sessions),
"sessionSecMedian": median_int(sessions),
"genreIds": [g["id"] for g in genres],
"svg": os.path.join(args.out, "ch03_taxonomy.svg"),
"svgBytes": svg_bytes,
}
with open(os.path.join(args.out, "ch03_taxonomy.json"), "w", encoding="utf-8") as f:
json.dump(report, f, ensure_ascii=False, indent=2)
print(json.dumps({k: report[k] for k in (
"genreCount", "dimensionCount", "byMonetization", "byContentVolume",
"sessionSecMin", "sessionSecMax", "sessionSecMedian", "svgBytes",
)}, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
注意脚本里 vol_labels / vol_counts 已经算好了内容量级分布,但只把变现模式画进了 SVG——这是刻意的:一张图只讲一件事,剩下的分布用文本报告承载,读者按需取用。
实跑结果
在本机工作区执行 /opt/homebrew/bin/python3 sim/ch03_taxonomy.py --config config/ --out out/,输出如下(同机相对量级,绝对数值不可移植):
{
"genreCount": 8,
"dimensionCount": 8,
"byMonetization": { "IAA": 4, "混合": 4 },
"byContentVolume": { "低": 2, "中": 3, "高": 3 },
"sessionSecMin": 45,
"sessionSecMax": 150,
"sessionSecMedian": 85,
"svgBytes": 1153
}
几个可以直接读出的事实:
- 变现模式:IAA 4 个、混合 4 个、纯 IAP 0 个。8 个品类里没有一个落在纯内购格子里,这正是前文说的生态事实——不是巧合,是小游戏用户预期的必然结果。
- 内容量级:低 2、中 3、高 3。高内容量级品类占了三席(闯关、模拟经营、益智),意味着它们对关卡产能与包体预算的压力最大。
- 单局时长:最短 45 秒(超休闲),最长 150 秒(模拟经营),中位数 85 秒。中位数偏短,说明小游戏的默认预期是「碎片时间」;做模拟经营这类长单局品类,等于主动逆着平台习惯走,需要在体验上给足补偿。
这张表的用法与边界
用法上,这张表是后续两步的输入:3.2 节给每个格子的 8 个维度填数字,3.3 节把数字按权重加总、排名、做敏感度分析。分类表本身不含任何「谁更好」的判断——它只负责把产品放进可比较的格子。
边界也要说清:这里选的 8 个品类是覆盖主流、便于教学的集合,不是穷举。现实中还有大量混合形态(合成 + 塔防、放置 + 经营),它们的处理方式是拆回轴上去看:先看主循环更像谁,再看变现与内容量级落哪一格,而不是硬造一个新格子。分类的价值在于降低沟通成本,不在于穷尽所有产品。
另外,observedAt 标了 2026-10,意味着这些分布是观察时点的快照。品类热度会迁移,任何「现在流行 X」的结论都要带着时点读。这也正是把分类做成 config 而非写死在正文里的原因——更新一次数据,下游的统计、矩阵、评分会一起更新。
小结
本节把「品类」从口头概念变成了三轴结构:玩法核心 / 变现模式 / 内容量级(含单局时长),并把 8 个品类落成一张机器可读的 config。S 层脚本证明这张表能被程序消费,读出了 IAA 与混合各占一半、纯 IAP 为零、单局时长中位数 85 秒等结构性事实。下一节在这个骨架里填入商业特征数字,把它变成一张可比较的矩阵。
阅读导航:上一节:从竞品到差异化机会 · 下一节:品类的商业特征矩阵 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。