全程用 AI 做一款商业级手游 · EP6 留存系统:让玩家明天还回来 EP4/EP5 把变现做出来了。但变现有个前提——得有人来玩。一款 F2P 游戏的引擎是两个齿轮咬合留存供给流量变现把流量变成钱。这一集EP6做留存这半边三件套离线收益、每日连签、主题皮肤。这一集的三件套接上 UI 后真机里长这样Unity 实拍——每日登录的 7 天连签阶梯D7 金色大奖 皮肤面板4 套皮肤各带配色预览皮肤不是摆设——点霓虹装备后棋盘上的方块配色实时切换同一局左前面是经典配色这里换成霓虹下面讲实现。我想先重点讲一个看似不起眼、却决定了这套逻辑能不能被验证的工程决定——怎么处理今天。工程关键把今天做成可注入的参数连签系统天生难测它依赖日期变化。如果代码里直接写DateTime.Now那测试就得改系统时钟、或等到明天——根本没法在一次execute_code里跑完首登→次日→断签。解法是把时间从逻辑里抽出来留存账本只认一个long todayDayUTC 天序号由调用方传入。真机用DayOf(现在的 unix 秒)测试直接传100, 101, 103。publicclassRetentionModel:SingletonRetentionModel{publicreadonlyPropertylonglastLoginDaynew(ret.lastLoginDay,0L);publicreadonlyPropertyintloginStreaknew(ret.loginStreak,0);publicreadonlyPropertyinttotalLoginDaysnew(ret.totalLoginDays,0);publicCheckInResultCheckIn(longtodayDay){if(lastLoginDay.ValuetodayDaytotalLoginDays.Value0)returnCheckInResult.AlreadyToday;// 同日重复boolconsecutivelastLoginDay.Value!0todayDaylastLoginDay.Value1;loginStreak.Valueconsecutive?loginStreak.Value1:1;// 接上昨天→1否则归1lastLoginDay.ValuetodayDay;totalLoginDays.Value1;returnCheckInResult.NewLogin;}publicstaticlongDayOf(longunixSeconds)unixSeconds/86400L;}把不确定性时钟挡在边界上核心逻辑就变成纯函数式的——给定todayDay输出确定。这一条让连签的 8 个断言能在一秒内跑完。三件套都复用同一条经济曲线留存奖励发的也是金币所以它们全部复用 EP4 的EconomyConfig——奖励金额跟着玩家进度最高分缩放。每日奖励表只配coefpublicintClaimDaily(longtodayDay){if(Model.CheckIn(todayDay)CheckInResult.AlreadyToday)return0;intstreakModel.loginStreak.Value;vardayDaily.ForStreak(streak);// 7 天循环intcoinsEcon.CoinsFor(day.coinCoef,UserDataModel.Instance.curLevel.Value);CoinModel.Instance.ChangeCoin(coins,daily:dstreak);// ...发道具、广播 DailyRewardClaimedMsgreturncoins;}离线收益同理——按离开时长封顶 8 小时折算成一个 coef再走经济曲线publicintPreviewOffline(longlastQuitUnix,longnowUnix){if(lastQuitUnix0||nowUnixlastQuitUnix)return0;floathours(nowUnix-lastQuitUnix)/3600f;if(hoursOfflineCapHours)hoursOfflineCapHours;// 8h 封顶returnEcon.CoinsFor(OfflineCoefPerHour*hours,UserDataModel.Instance.curLevel.Value);}画出来是这样——左边 7 天连签阶梯D7 是大奖最高分 30 整体比最高分 1 高因为随进度缩放右边离线收益涨到 8 小时后封顶封顶是关键设计没有它玩家放一周回来一次性领走天量金币经济就崩了有它最优策略变成每 8 小时回来一次——这正是离线收益想培养的回流习惯。主题皮肤三种解锁规则数据化皮肤是消耗变现之外的另一种付费/留存抓手。三种解锁规则免费 / 金币 / 最高分全在数据资产里配加皮肤不改代码publicUnlockResultTryUnlock(stringid){if(!Cfg.TryGet(id,outvart))returnUnlockResult.NoSuchTheme;if(IsOwned(id))returnUnlockResult.Owned;switch(t.unlock){caseUnlockKind.Free:AddOwned(id);returnUnlockResult.UnlockedFree;caseUnlockKind.BestScore:if(UserDataModel.Instance.bestScore.Valuet.cost)returnUnlockResult.NeedScore;AddOwned(id);returnUnlockResult.UnlockedFree;caseUnlockKind.Coins:if(!CoinModel.Instance.ChangeCoin(-t.cost,theme:id))returnUnlockResult.NeedCoins;AddOwned(id);returnUnlockResult.Bought;default:returnUnlockResult.NoSuchTheme;}}金币解锁直接复用CoinModel.ChangeCoin的扣费——余额不足它返回 false解锁自然失败。整个系统又一次站在 EP4 那套货币地基上。验证24 条断言 24/24 PASS A 首登发放金币0 / streak1 / 领取消息触发 A 同日重复领取0 / 不增加累计登录 A 次日连签 streak2 A 断签后 streak 重置为1 / 累计登录3(去重) B 离线2h预览0 / 封顶(100h8h) / 从未离开0 B 离线结算余额增加 / 消息金币一致 C 默认拥有classic / 免费解锁 / 金币不足失败 / 金币足够购买且扣费 C 最高分不够失败 / 最高分达标解锁 / 装备成功且广播 / 装备未拥有失败钉死的核心连签断签必须归 1不能漏一天还连着、同日重复不能重复领、离线必须封顶100h8h、主题金币不足/最高分不够必须解锁失败且不发放、装备只能装已拥有的。留存奖励直接发钱错一条都是白送或刷取漏洞所以全用断言锁死。这一集的产物与诚实的话RetentionModel登录账本时间可注入RetentionController连签 离线发放DailyRewardConfig7 天表ThemeModel/ThemeConfig皮肤。两个数据资产进 Resources运行时加载。24 条断言全绿。诚实地讲这一集做的是留存的逻辑与数值层。还没做的弹窗 UI登录领奖弹窗、离线收益结算弹窗、皮肤商店面板——那是表现层和 EP7 一起做以及真正拉动留存的推送通知本地/远程 push 提醒玩家回来那需要平台能力和后台属于发布阶段。但明天回来能拿什么、连签怎么算、离线怎么折算、皮肤怎么解锁——这套决定留存经济的算法已经建好并验证了。下一篇EP7表现层与手感juice——把前 6 集积累的纯逻辑接上动效、音频程序化合成、震动反馈让它从能跑的系统变成摸起来爽的游戏。工具funplay-unity-mcp开源工程本系列做出来的完整 Unity 工程已开源上一篇EP5 商城 内购