01 · 核心玩法循环
放置游戏"即时正反馈"的根。本篇拆解 Eatventure 最底层的秒级微循环—— 工位出餐 → 工人送餐 → 顾客买单卖钱 → 玩家点击升级 → 出餐更快更值钱。 上层全部进度/经济/养成都挂在这台引擎之上。每个子系统按 设计目标 → 机制 → 数据表 → 公式/伪代码 → 设计意图 拆开。
零一张图看懂这层在解什么问题
放置经营游戏的核心矛盾:离开时要有产出(挂机),在线时要有掌控感(点击)。 Eatventure 的答案是把两个时钟叠在一起——自动微循环离线也跑,点击升级随时介入立刻见效。
自动微循环
出餐-送餐-卖钱每 tick 都在跑,离线也按 10% 折算,保证挂机有进展。收入 = base × 工位里程碑倍率 × 养成乘区。
点击升级
玩家唯一的主动操作。单步成本低、回本快,下一 tick 立即生效,制造"再点一下就更强"的密集正反馈。
一工位出餐(Station / Order)—— 微循环的发动机
① 设计目标
工位是"产能的物理单位"。让玩家能并行铺开多条产线(铺得越多产能越高),同时给每条产线一个可无限升级的成长维度,把"产能"做成永远填不满的坑。
② 机制
- 每餐厅 1~12 个工位(
restaurant_layout.csv,avg 5.1),每工位绑定一道菜,按自己的周期自动出餐。 - 出一份菜放柜台 → 工人取走送餐 → 顾客买单。工位只负责"按周期产出一份"。
- 工位可升 5 星,升星推进餐厅完成度。
- "赚钱能力"不在工位身上,而在利润里程碑:攒够大钱解锁,整条产线收入跳档。
③ 数据与公式
工位配置中,升星上限为 5 星。
工位里程碑配在 station_profit_milestones.csv,核心是一个极省常量的几何矩阵:
主题/活动城 4 档 ×10 / 20 / 50 / 100
相邻工位价格公比:主线城 ×50 / 主题城 ×800
单级升级公比:B = 1.26(第0餐厅) / 1.20(其余)
station_profit_milestones.csv 真实样本(主题城 ×800 系):
| 工位序号 | 里程碑档 | 倍率 | 解锁价 |
|---|---|---|---|
| 0 | 0 / 1 / 2 / 3 | 10 / 20 / 50 / 100 | 2e14 / 3e25 / 5e32 / 1.5e43 |
| 1 | 0 | 10 | 1.6e17 (≈ 工位0 × 800) |
| 2 | 0 | 10 | 1.28e20 (≈ 工位1 × 800) |
④ 平衡与节奏
工位的两条成长轴形成节奏对比:
| 成长轴 | 单步成本 | 节奏作用 |
|---|---|---|
| 密集小升级(提速 / 加利润) | 低 | 填满分钟级的点击反馈 |
| 稀疏大里程碑(×10 → ×100) | 暴涨(高几个数量级) | "攒钱-跳档"的阶段性高潮,单调爬坡里的节奏锚点 |
⑤ 设计意图 / 可复用结论
二菜品(Item)—— 换皮的产出物,数值不在它身上
① 设计目标
解决"内容广度":60 城 × 多餐厅需要海量视觉差异,但又不能让每道菜都成为独立的数值黑洞。
② 机制
菜品 = 工位产出物 + 视觉资产(Mesh / Icon / 机器 Prefab)。每道菜挂利润升级链和速度升级链,但链上只是"升级项的名字/数量",真实利润倍率落在工位里程碑(按城/主题,不按菜)。
③ 数据与公式
菜品配置共 163 道菜。 逐字段看——没有任何利润/速度的数值字段,全是名字与资产引用:
| 菜品配置字段 | 类型 | 含义 |
|---|---|---|
| 菜名 / 图标 / 网格 / 机器 Prefab | 资产引用 | 菜名 + 视觉(图标 / 网格 / 机器 Prefab) |
| 利润升级文案 / 提速升级文案 | 本地化字符串 | 升级项的显示文案(非数值) |
| 利润升级链 / 速度升级链 | 本地化字符串数组 | 升级链的名字数组(非数值) |
| 可收获标记 | bool | 22 道为 true → 农场/采集类,工人去"收获"而非"取餐" |
菜品↔餐厅映射见菜品-餐厅映射表;80/163 跨餐厅复用。
④ 平衡与节奏
菜品复用 + 工位里程碑驱动数值,意味着同一道 "Salad" 在第 1 城和第 40 城是完全不同的收入量级——差异全靠 cityMult + 工位公比拉开,菜品本身零改动。
⑤ 设计意图 / 可复用结论
三顾客(Customer / CustomerSlot)—— 没有耐心的"收银终端"
① 设计目标
经营游戏经典做法是给顾客"耐心条",逼玩家手忙脚乱。但放置游戏要的是挂机也不亏——Eatventure 刻意砍掉耐心/愤怒离场,让顾客退化成纯粹的"收银触发器"。
② 机制
- 顾客在
CustomerSlot(柜台前)排队,工人送菜到柜台,顾客付钱离店,新顾客补位。 - 行人是街上路过的(氛围 + 入店来源),沿行人路径行走。
- 承载量 =
customer_count升级(+1~4) +table_count加桌(+1~12,见 §五)。
③ 数据与公式
顾客配置共 12 套外观。 其字段全是外观(头/帽/发/身体 ID + 肤色/衣服颜色)+ 金色材质 + Customer Multipliers 下仅两个共享乘子——全表无耐心字段:
| 字段 | 取值 | 说明 |
|---|---|---|
| 金色乘子 | 全 12 套 = 16.0 | 金色顾客付 ×16 |
| 订单量乘子 | 11 套 = 1.0,仅希腊隐形顾客 = 3.0 | 单笔订单量倍率 |
| (无耐心 / 愤怒字段) | — | 顾客不会愤怒离场,离线不流失 |
顾客类型纯外观换皮(Royal / Knight / Pirate / Greek / Hades… 12 套适配各主题城),数值完全共享。
④ 平衡与节奏
没有耐心条 → 玩家离线时顾客不会流失 → 离线产能可被稳定折算(10% / 3h,见 03)。
在线时,顾客承载量(slot 数 + 桌数)是产能的"下游瓶颈":出餐再快,柜台前没顾客也卖不掉,所以
customer_count / table_count 和出餐提速形成供需两端的配平升级。
⑤ 设计意图 / 可复用结论
四工人 / 角色(Worker / Character)—— 纯外观 + per-restaurant 数值
① 设计目标
工人是"产能的执行者"(送餐/收获)。设计上要回答:工人的数量和速度该挂在哪里?答案出人意料地干净——工人本体零数值,全部数值配在餐厅升级线里。
② 机制
- 工人在工位和柜台之间往返:取餐 → 走到柜台 → 交付。空闲工人被任务调度系统派活(见 §七)。
- 雇更多工人(
worker_count,+1~5)→ 并发送餐能力↑;移动提速 → 单趟更快。 Character是工人/顾客共用的角色实体(装扮挂这里);ChefFund主厨基金归 06。
③ 数据与公式
工人配置共 20 套外观,为纯视觉配置——关键是那两个"升级"字段其实是显示名,不是数值:
// 工人配置全部字段 名字 Name 图标 Icon 工人数量升级 NumberOfWorkersUpgrade // ← 升级线"显示名",非数值! 移速升级 WalkSpeedUpgrade // ← 同上 装备设置 EquipmentSettings // 身体/头/帽/发 资产ID+颜色 肤色 SkinColor
真实的工人数量/速度数值,全部配在餐厅升级配置(每家餐厅独立),按工位子区细分:
| 数值 | 来源 | 每档增量 | 价格 |
|---|---|---|---|
| 工人移动速度 | 餐厅升级·工人提速 | 恒定 +0.25(164 项全 = 0.25) | 指数增长(Kiosk BetterShoes 起步 8000) |
| 工人烹饪/收获速度 | 工人烹饪提速 | +1.0 / +2.0(仅 Mine 矿镐等收获工位) | 离散 |
| 工人数量 | 工人数量升级 | +1~5 | 指数离散 |
移速公式:
n = 该餐厅已购工人提速升级数(平均约 3 档/餐厅,164/52)
工人名字库:34 套国家名库(Spain / Mexico / Luxembourg…)——按城市国籍给工人随机起名,纯本地化氛围,无数值。
④ 平衡与节奏
移速升级是这套数值最优雅的一笔:效果线性恒定(+0.25/级)+ 成本指数上涨。
⑤ 设计意图 / 可复用结论
五堂食容量(Tables / CustomerSlot)—— 产能的下游闸门
① 设计目标
出餐再快,也得有"地方接客"。堂食容量是产能链的下游瓶颈,故意做成一条独立升级线,让玩家在"上游提速"和"下游扩容"之间持续配平。
② 机制
CustomerSlot = 柜台前即时排队位(外带);Tables = 堂食桌位(坐下来吃,容量更大但占空间)。加桌(table_count,+1~12)= 同时能服务的顾客数↑ = 单位时间能卖掉的菜↑。
③ 数据与公式
restaurant_layout.csv 给出每家餐厅的物理容量(72 餐厅统计);运行时区域数据对应布局,含顾客位 / 柜台 / 小费位 / 工位:
| 容量维度 | min / max / avg | 覆盖 |
|---|---|---|
| 顾客位 num_customer_slots | 0 / 40 / 14.5 | 全部 |
| 桌位 num_tables | 0 / 12 / 1.9 | 35/72 有桌(其余纯外带摊) |
| 工位 num_stations | 1 / 12 / 5.1 | 全部 |
| 区域 num_areas | 1 / 3 / 1.8 | 全部 |
早期是"0 桌纯外带小摊"(LemonadeStand 类),后期才解锁堂食扩容,容量随关卡递进。
④ 平衡与节奏
容量是"看不见的天花板":玩家把出餐提速拉满后会发现收入卡住——因为顾客位/桌位不够,菜卖不出去。这迫使玩家回头点加桌/拉客,形成节奏:
⑤ 设计意图 / 可复用结论
六外卖运输(Transports / FoodDelivery)—— 主题城的批量物流
① 设计目标
有些主题城/分区,柜台离顾客很远(矿洞井下、太空站、海港)。运输系统解决"跨区/跨距批量送客送餐",同时成为主题城的差异化机制载体(矿镐升降梯和海盗船给完全不同的节奏感)。
② 机制
运输载具(升降梯/船/飞船/马车)在停靠点间往返,批量搭载顾客/运送货物。一个周期 = 到达(到达耗时)→ 装满(最大载客量)→ 发车(满后发车耗时)。运输槽仅 7/65 餐厅有(主题专属)。
③ 数据与公式
运输配置关键字段为最大载客量 / 基础速度 / 到达耗时 / 满后发车耗时 / 是否走行人路径。 共 15 种载具:
| 载具 | 载客量 | 基速 | 到达 | 满后发车 | 走路径 |
|---|---|---|---|---|---|
| SpaceShipBig | 10 | 3.0 | 8s | 10s | 否 |
| MineLiftHuge | 8 | 3.0 | 6s | 6s | 是 |
| MineLiftBig / SpaceShipMedium | 6 | 3.0 | 6s | 6 / 8s | — |
| MediumBoat / MineLiftMedium | 4 | 3.0 | 10 / 6s | 10 / 6s | — |
| Kraken / Zeus / Royal / Horse / SmallBoat | 1~2 | 3.0(Horse 走 path) | 0~15s | 0~30s | 部分 |
| SmallBoat Pirate | 1 | 5.0(唯一例外) | 5s | 5s | 否 |
基础速度几乎全部统一 = 3.0(唯一例外 SmallBoat Pirate = 5.0)——载具差异不在速度,而在"载客量 × 周期":
④ 平衡与节奏
| 设计点 | 机制 | 效果 |
|---|---|---|
| 零容量门控 | 满后才发车(满后发车耗时) | 客流不足时载具空等,把"凑满一车"做成轻度策略(要不要升级拉客凑客流) |
| 主题差异化 | Mine / Space / Seaport / Pirates 各有专属载具 | 换城时物流手感明显不同,强化"换皮新鲜感" |
⑤ 设计意图 / 可复用结论
七工人任务调度(Task 体系)—— 微循环的状态机
① 设计目标
工人怎么知道"现在该去取哪份餐、送到哪、走哪条路"?需要一套任务调度状态机,把"出餐-送餐-收银"拆成可被空闲工人抢占执行的原子任务。
② 机制
一条任务流水线把订单拆成原子任务,分发给空闲工人:雇的工人越多(worker_count),并发执行的任务越多。寻路走 WalkableTiles(行走网格)+ Path(运行时数据,不在配置表)。
典型工人生命周期(状态机)
任务体系把工人行为拆成一组成对出现的原子任务(取餐 / 交付 / 移交 / 收获 / 沿路径移动 / 烹饪 / 收小费), 每类任务都有对应的启动系统驱动。派活逻辑核心是"找最近空闲工人":系统在所有空闲工人中挑距离最近者承接当前任务。
收获类工位:HarvestingTask(收获)替代 PickupTask。
③ 数据与公式(运行时)
任务调度是运行时逻辑(无独立配置数值),其输入参数来自前面各表:
| 输入参数 | 来源 |
|---|---|
| 工人数(并发数) | §四 工人数量 |
| 移速 | §四 +0.25/级 |
| 出餐周期 | §一/§二 工位周期 |
| 行走网格 | restaurant_layout.csv bounds_w/h(样本 157 网格点)、区域可行走网格 |
运行时有一个遍历刷新工人移动速度的系统,在升级后批量更新所有工人的移速。
④ 核心 tick 伪代码
把 §一~§七 整条链合成一个 tick 循环(核心微循环,综合各表的设计还原):
# ========== 核心微循环:每帧 tick ========== def tick(dt): # 1) 工位出餐 for st in unlocked_stations: st.timer -= dt if st.timer <= 0: st.tray.append(produce(st.item)) # 产出一份菜放托盘 st.timer = st.base_time / cooking_speed(st) # 重置周期(烹饪提速 +1/+2) # 2) 任务调度:把空闲工人派去送餐/收获 for w in idle_workers(): # worker_count 决定并发数 st = nearest_station_with_food() if st: task = HarvestingTask(st) if st.item.is_harvestable else PickupTask(st) w.assign([task, MoveOnPathTask(path_to_counter(w), speed=base*(1 + n*0.25)), # +0.25/级 DeliverTask(counter)]) # 交付到柜台 # 3) 顾客买单卖钱(无耐心:顾客不会愤怒离场) while counter.has_food() and has_waiting_customer(): # 容量 = customer_slots + tables c = pop_customer() gain = base_profit * station_milestone_mult(st) # 真实倍率在工位里程碑 ×3..×100 gain *= 16 if c.is_golden else 1 # 药水触发 → ×16(金色乘子) gain *= ascend_multiplier() # 养成乘区(装备/宠物/神器) money += gain spawn_new_customer() # 立即补位(无流失) # 4) 外卖运输:批量周期物流(主题城) for tr in transports: tr.run_cycle(dt) # 到达 → 装满最大载客量 → 满后发车 # ========== 玩家主动介入:随时点击升级,立刻见效 ========== def on_player_upgrade(kind, target): cost = upgrade_price(kind, target.level) # 见 03:指数成本 if money >= cost: money -= cost apply(kind, target) # 提速/雇工/加桌/拉客/利润里程碑 → 下一 tick 立即生效
on_player_upgrade 是玩家唯一主动操作,每次点击下一 tick 立即体现,这是"即时正反馈"的实现层。收入节奏:密集小台阶 + 稀疏大跳档
⑤ 设计意图 / 可复用结论
八订单与小费(Order / Tips)—— 收入的两个口子
① 设计目标
主收入(卖菜)之外,给一个轻量的副收入口子(小费),既增加"工人收钱"的动作可读性,又给玩家一个额外的可优化点。
② 机制
订单 = 一笔订单(顾客的需求),完成即触发收入结算。小费:工人交付后通过收小费任务单独收取(见 §七任务链);小费位是布局的运行时数据。
③ 数据与公式
收入主公式(承 03 经济与数值):
小费为附加结算项(由收小费任务结算,按订单比例的小额附加,客户端无独立倍率表)。 希腊隐形顾客的订单量乘子 = 3.0,说明个别特殊订单可放大单笔订单量。
④ 平衡与节奏
小费作为副口子,量级远小于主收入,主要服务"工人收钱"的动作反馈与微量优化,不改变经济主轴。
⑤ 设计意图 / 可复用结论
九新手引导(Tutorial)—— ECS 步骤状态机,数据不在配置里
① 设计目标
引导要解决两件事:把微循环(§一~§八)教会新玩家,以及每上一个新系统(宠物/神器/活动)都能补一段专项教学。Eatventure 的答案不是一条巨型脚本,而是一套可复用的 ECS 步骤状态机框架——反编译代码里共 ~113 个 Tutorial* 类,同一框架挂出十来条各自独立的专项教程线。
② 机制
- 实体挂上
TutorialComponent(空标记组件)即进入"教程态";当前进度存TutorialStepComponent.Value(一个 int 步骤号)。 - 组件的 Added/Removed 带事件标注,由
TutorialEventSystem/TutorialStepEventSystem等 4 个响应式事件系统广播给监听者列表(ITutorialListener/ITutorialStepListener等 4 个接口)。 - UI 侧
TutorialView/TutorialStepView实现监听接口,按_showOnTutorial开关和_tutorialSteps(步骤号白名单)决定显隐——"到第 N 步才亮这个按钮"。 - 每条专项教程 = 一串 Begin/End 系统:抽象基类
FeatureTutorialStepBeginSystem(Feature 名 + Step 号 + Begin)启动某步,FeatureTutorialStepEndSystem<T>(EndTutorialStep)在条件满足时推进到下一步。
③ 数据与结构(来自反编译代码)
引导进度直接写进存档 TutorialSavegame,全部字段只有 5 个——每条教程线一个进度位,没有统一的步骤配置表:
// TutorialSavegame 全部字段 主动技教程完成 ActiveSkillTutorialComplete // bool 宠物合成教程步骤 PetMergeTutorialStep // Nullable<int>,步骤号 宠物商店教程已示 PetShopTutorialShown // bool 俱乐部通知已示 ClubNotificationShown // bool 酿造锅教程步骤 BrewKettleTutorialStep // int,步骤号
步骤号是代码内枚举。以最长的一条(酿造锅活动教程 BrewKettleTutorialStep,14 步)为例:
40/50 开锅领药水 → 60/61 打开在用药水页 → 70 等首枚铜牌 → 80/90 打开活动金库页 → 100 首次金库升级 → 110 End
步骤号按 10 间隔留空(0/10/11/20/…/110),中途可插新步骤而不重排。宠物合成(PetMergeTutorialStep,-1~2)/宠物商店(PetShopTutorialStep,-1~3)同构但更短。
引导的"手指/高亮"表现由一组专用视图承担:
| 类 | 关键字段 | 作用 |
|---|---|---|
TutorialIndicatorView | ArrowTransform(RectTransform)+ 动画序列 + 两个 Vector2 端点 | UI 箭头在两点间往返摆动(OnEnable 起 / OnDisable 停) |
TutorialIndicatorWorldBehaviour | 动画序列 + 两个 Vector3 端点 | 同款箭头的世界空间版,指向场景内目标 |
FullScreenTutorialUiComponent | AssignedTutorialEntityRef | 全屏引导 UI 绑定到具体教程实体 |
| Spotlight 系统组 | RestaurantUpgradeTutorialRemoveSpotlightOn*System | 餐厅升级引导的聚光灯高亮及其移除时机 |
另有 TutorialCheatSystem(可整线关闭主动技/神器/人物/装备箱教程)——开发调试与老玩家跳过用。
④ 平衡与节奏:主线之外的专项教程线
除主线新手期外,每个中后期系统上线时各挂一条独立教程线(类名前缀即功能): 酿造锅活动(BrewKettle*,14 步最长)、人物(Character*)、宠物合成/宠物商店(PetMerge*/PetShop*)、 主动技(ActiveSkill*)、神器(Artifact*)、装备箱(EquipmentChest*)、餐厅购买/餐厅升级(RestaurantBuy*/RestaurantUpgrade*)、 探索(Discovery*)、家园(HomeMeta*)。其中 4 条在客户端留有视图挂点配置:
| 配置文件 | 顶层字段 | 对应功能 |
|---|---|---|
ArtifactUpgradeTutorialButtonView.json | TutorialButton / RegularButton | 神器升级按钮引导:教程态换成引导版按钮 |
RestaurantUpgradesTutorialPanelView.json | TutorialView | 餐厅升级面板引导 |
RestaurantUpgradesTutorialView.json | TutorialView + Target | 餐厅升级条目引导(Target 指向具体升级项) |
RestaurantUpgradesDisableScrollTutorialView.json | TutorialView ×2 | 引导期间禁用面板滚动(防误滑离开目标) |
主线新手期本身则没有独立步骤表——首城被当作"教学母版"逐关打磨(一关引入一个新维度,甚至跑"首城延长"A/B 实验),见 02 进度结构。
⑤ 设计意图 / 可复用结论
十本篇小结:这层的设计哲学
| 子系统 | 一句话本质 | 数据落点 |
|---|---|---|
| 工位 Station | 产能单位,"频率"维度 | 工位配置(升星上限 5) / station_profit_milestones.csv |
| 菜品 Item | 纯内容皮,数值寄生工位 | 菜品配置(163,无数值字段) / 菜品-餐厅映射表 |
| 顾客 Customer | 无耐心收银终端,金色×16靠药水 | 顾客配置(12,金色乘子=16,无耐心) |
| 工人 Worker | 纯外观,数值全 per-restaurant,移速恒+0.25 | 工人配置(20,字段全为显示名) / 工人名字库(34) |
| 堂食 Tables | 产能下游闸门,逼供需配平 | restaurant_layout.csv / 区域顾客位 |
| 运输 Transport | 主题城物流,用"容量×周期"差异化 | 运输配置(15,基础速度=3.0) |
| 任务调度 Task | 无数值纯逻辑层,把参数演成动作 | 运行时逻辑(7 类原子任务) + 区域可行走网格 |
| 订单/小费 Order/Tips | 收入结算单位 + 动作可读性副产品 | 收入公式(base×里程碑×养成) + 收小费任务 |
| 新手引导 Tutorial | ECS 步骤状态机框架,专项教程各挂一线 | 代码内枚举 + TutorialSavegame(5 字段,无步骤配置表) |
三条贯穿全篇的设计取舍
1 · 数值与皮分离
菜品/工人/顾客都是"纯外观皮 + 外置数值"。海量内容变体(163 菜 / 20 工人 / 12 顾客)跨城自由复用,数值统一外置(工位里程碑 + per-restaurant 升级线),内容产能与数值平衡彻底解耦。
2 · 频率与价值分离
工位管"出餐频率",里程碑管"每单价值";用几何公比 + 少量常量撑起跨数十数量级的经济,配置极省、调参极易。
3 · 挂机无惩罚 + 点击即反馈
砍掉顾客耐心系统(离线不流失),把暴利体验(金色×16)外移到可主动触发的药水乘区;点击升级下一 tick 立即生效,自动循环离线按 10% 折算——两个时钟叠加,挂机与在线各得其所。