<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>微信小游戏 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E6%B8%B8%E6%88%8F/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Sat, 05 Sep 2020 21:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E6%B8%B8%E6%88%8F/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>《princes》：用 Cocos Creator 写一款餐厅经营放置小游戏</title>
      <link>https://ftmao.com/blog/posts/princes-restaurant-game-intro/</link>
      <pubDate>Sat, 05 Sep 2020 21:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/princes-restaurant-game-intro/</guid>
      <description>&lt;h1 id=&#34;princes用-cocos-creator-写一款餐厅经营放置小游戏&#34;&gt;《princes》：用 Cocos Creator 写一款餐厅经营放置小游戏&lt;/h1&gt;
&lt;p&gt;继《去赶海吧》之后，把更早做的一款餐厅经营小游戏也整理开源了，照例写篇文章介绍玩法和实现上的技术点。仓库名叫 princes（历史遗留命名，内容其实是餐厅），代码里还住着不少从其他项目移民过来的残留，这次一并&amp;quot;考古&amp;quot;出来。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开源地址：&lt;a href=&#34;https://gitee.com/soulnew/princes&#34;&gt;https://gitee.com/soulnew/princes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;技术栈：Cocos Creator 2.3.4 + JavaScript&lt;/li&gt;
&lt;li&gt;平台：微信小游戏（竖屏 720×1280）&lt;/li&gt;
&lt;li&gt;规模：39 个脚本，约 4200 行代码，单主场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;游戏核心玩法一句话概括：&lt;strong&gt;开一家餐厅，顾客自动上门排队等位、走到餐桌点餐、用餐完留下金币，你点击收金币，再用金币解锁更多食物、餐桌和顾客皮肤&lt;/strong&gt;——放置 + 点击收菜的经典经营循环。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一玩法介绍&#34;&gt;一、玩法介绍&lt;/h2&gt;
&lt;h3 id=&#34;11-核心循环&#34;&gt;1.1 核心循环&lt;/h3&gt;
&lt;p&gt;玩家几乎不直接操作角色，核心是&amp;quot;看店 + 点击收钱 + 花钱解锁&amp;quot;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[顾客自动上门&amp;lt;br/&amp;gt;每15秒补1个] --&amp;gt; B{有空餐桌?}
    B --&amp;gt;|有| C[A*寻路走到餐桌]
    B --&amp;gt;|没有| D[门口排队等位]
    D --&amp;gt;|有空桌释放| C
    C --&amp;gt; E[头顶弹气泡点单]
    E --&amp;gt; F[用餐5秒&amp;lt;br/&amp;gt;点击气泡可加速]
    F --&amp;gt; G[桌边产出一堆金币]
    G --&amp;gt; H[玩家点击拾取]
    H --&amp;gt; I[解锁新食物/餐桌/顾客]
    I --&amp;gt; A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个关键设计：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;餐桌是稀缺资源&lt;/strong&gt;：场上最多 10 个顾客（&lt;code&gt;maxPlayer = 10&lt;/code&gt;），没桌子的顾客会在门口按队列序号排开站位，有空桌释放时队首补位、全队前移；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;点单匹配&lt;/strong&gt;：每个顾客皮肤有自己的&amp;quot;可点菜单&amp;quot;（&lt;code&gt;foodList&lt;/code&gt;），随机点一道——这道菜已解锁就正常产金币，没解锁就显示&amp;quot;没有这道菜&amp;quot;的图标，等于告诉你&amp;quot;该去商店解锁了&amp;quot;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;巡游消费&lt;/strong&gt;：顾客吃完饭不直接走，而是从酒柜、古董花瓶、海滩桌等 6 类装饰家具里洗牌出一份&amp;quot;参观清单&amp;quot;，逐件走过去参观，每件停留几秒再留下一笔金币——买装饰家具不只是好看，是真的能多赚钱；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;邀请按钮&lt;/strong&gt;：等不及可以点&amp;quot;邀请&amp;quot;立刻来一位顾客，这个入口同时服务每日任务和成就。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-四屏地图与翻页&#34;&gt;1.2 四屏地图与翻页&lt;/h3&gt;
&lt;p&gt;餐厅不是一张单屏地图，而是四个房间：&lt;strong&gt;海滩（left1）、大厅（mid）、剧院（right1）、花园（down1）&lt;/strong&gt;。顾客在房间之间穿行时走&amp;quot;传送门&amp;quot;格子——比如大厅到海滩，先寻路到 &lt;code&gt;(0,11)&lt;/code&gt; 这个门，切换父节点到海滩场景节点，再从海滩的入口格 &lt;code&gt;(12,11)&lt;/code&gt; 继续寻路。&lt;/p&gt;
&lt;p&gt;相机不是跟随式的，而是&lt;strong&gt;手动拖拽翻页&lt;/strong&gt;：单轴锁定滑动，滑过屏宽 1/10 左右翻页、屏高 1/14 上下翻页。门口还有两个服务员（&lt;code&gt;waiterPlayer&lt;/code&gt;），有顾客进门就弹出欢迎气泡 5 秒——不做实际传菜逻辑，纯氛围组。&lt;/p&gt;
&lt;h3 id=&#34;13-成长线解锁三件套&#34;&gt;1.3 成长线：解锁三件套&lt;/h3&gt;
&lt;p&gt;金币花在三个商店面板上：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;商店&lt;/th&gt;
					&lt;th&gt;内容&lt;/th&gt;
					&lt;th&gt;解锁的意义&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;食物&lt;/td&gt;
					&lt;td&gt;54 道菜，价格 100~153 递增&lt;/td&gt;
					&lt;td&gt;顾客点单能匹配上，用餐才产金币&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;家具&lt;/td&gt;
					&lt;td&gt;大厅 6 组餐桌（每组 3 级升级）+ 各房间散桌 + 装饰家具&lt;/td&gt;
					&lt;td&gt;新餐桌 = 多一个接待座位；装饰家具 = 巡游产金币点&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;顾客&lt;/td&gt;
					&lt;td&gt;50 种皮肤&lt;/td&gt;
					&lt;td&gt;新皮肤进入上门顾客的随机池&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;顾客皮肤的名字很有梗：学士、水电工、警长、老板、小青龙、法师、熊猫、海贼、管道工、侦探、伯爵、外星人、厨神、牙医、流浪歌手、开膛手、屠夫、王少爷、腹肌男、特工K、机器人……基本是把能想到的梗都塞进去了。&lt;/p&gt;
&lt;h3 id=&#34;14-成就与每日任务&#34;&gt;1.4 成就与每日任务&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;15 条成就&lt;/strong&gt;分 5 种类型：解锁指定物品、解锁 N 种某类物品、服务 N 个顾客、累计金币（500/5000/10000/30000）、邀请 N 次，奖励统一 5000 金币；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每日任务&lt;/strong&gt;从 7 条里随机洗牌取 6 条（服务顾客 2 次、累计 800 金币、邀请 2 次、招待指定顾客 2 次等），按本地日期键 &lt;code&gt;年_月_日&lt;/code&gt; 翻日重置，还会随机指定&amp;quot;今天要招待某位顾客&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两个系统都有红点提醒，领取后自动检查链上下一档成就。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二技术亮点&#34;&gt;二、技术亮点&lt;/h2&gt;
&lt;p&gt;项目不大（4000 行出头），但有几个点是自己撸出来的轮子，值得展开。&lt;/p&gt;
&lt;h3 id=&#34;21-手写-a--传送门式跨屏寻路&#34;&gt;2.1 手写 A* + &amp;ldquo;传送门&amp;quot;式跨屏寻路&lt;/h3&gt;
&lt;p&gt;寻路没有用任何库，&lt;code&gt;AStar.js&lt;/code&gt;（217 行）是手写的标准实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;地图是 &lt;code&gt;position.json&lt;/code&gt; 里一个 &lt;strong&gt;21×13 的一维数组&lt;/strong&gt;（273 个元素，&lt;code&gt;0&lt;/code&gt; 可走，其他障碍），&lt;code&gt;screenManager&lt;/code&gt; 按屏幕尺寸算出格子边长，做格坐标到像素坐标的换算；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AStarStep&lt;/code&gt; 封装 &lt;code&gt;g/h/position/last&lt;/code&gt;，&lt;code&gt;f&lt;/code&gt; 是 &lt;code&gt;g+h&lt;/code&gt; 的 getter；启发函数用曼哈顿距离，八方向扩展，斜走 14、直走 10（经典的 √2×10 近似）；&lt;/li&gt;
&lt;li&gt;拿到格子路径后，把每一格转成 &lt;code&gt;cc.moveTo(1, 像素坐标)&lt;/code&gt; 拼成 &lt;code&gt;cc.sequence&lt;/code&gt; 播放——&lt;strong&gt;每格固定走 1 秒&lt;/strong&gt;，移动节奏完全由动画序列驱动，不需要 &lt;code&gt;update&lt;/code&gt; 里手动插值。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;跨屏是这套寻路最有意思的部分——四个房间不是一张大地图，而是各自独立的节点树，靠 6 对&amp;quot;门坐标&amp;quot;缝合：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[当前房间] --&amp;gt;|寻路到门格 mid_left1| B[门 0,11]
    B --&amp;gt;|切换父节点| C[目标房间]
    C --&amp;gt;|从入口格 left1_mid 继续寻路| D[最终目标]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;大地图拆成小地图 + 门对，每个房间的寻路空间始终只有 21×13，复杂度和内存都可控，是小工程里很实用的拆法。&lt;/p&gt;
&lt;h3 id=&#34;22-自研虚拟列表-listview&#34;&gt;2.2 自研虚拟列表 ListView&lt;/h3&gt;
&lt;p&gt;Cocos 2.x 官方没有现成的虚拟列表，商店、成就、每日任务的长列表就手写了一个（&lt;code&gt;ListView.js&lt;/code&gt;，380 行，项目内最大文件）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NodePool 复用&lt;/strong&gt;：划出视野的 item 回池，进入视野的从池里取或 instantiate；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;增量填充&lt;/strong&gt;：&lt;code&gt;_filledIds&lt;/code&gt; 记录当前树上已填充的索引，&lt;code&gt;update()&lt;/code&gt; 每帧用 content 的滚动偏移算起始 item 索引，变了才触发 &lt;code&gt;updateView&lt;/code&gt;，只回收越界的、只补缺失的；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adapter 模式&lt;/strong&gt;：&lt;code&gt;ListAdapter&lt;/code&gt; 持有数据和 item 脚本名，&lt;code&gt;updateView&lt;/code&gt; 里 &lt;code&gt;item.getComponent(componentName).setData(data)&lt;/code&gt;——Android ListView 的味道很浓，View 和数据彻底分离；&lt;/li&gt;
&lt;li&gt;支持多列网格（&lt;code&gt;oneLineItemNumber&lt;/code&gt;）、上拉/下拉回调、监听 &lt;code&gt;refreshScollView&lt;/code&gt; 事件做局部刷新。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;家具面板没用这个 ListView，而是手写了一个更简的裁剪：视野内 &lt;code&gt;opacity=255&lt;/code&gt;、视野外 &lt;code&gt;opacity=0&lt;/code&gt;。同一项目里两种方案并存，算是&amp;quot;够用就好&amp;quot;和&amp;quot;造个轮子&amp;quot;的对照组。&lt;/p&gt;
&lt;h3 id=&#34;23-餐桌分配一个微缩排队系统&#34;&gt;2.3 餐桌分配：一个微缩排队系统&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;realTimeData.js&lt;/code&gt; 里藏着一个小小的服务台排队模型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;activeFurnitureData&lt;/code&gt; 是&amp;quot;空闲桌池&amp;rdquo;，顾客来了随机抽一张（&lt;code&gt;splice&lt;/code&gt; 取出），用完 &lt;code&gt;push&lt;/code&gt; 回去；&lt;/li&gt;
&lt;li&gt;没桌时顾客进 &lt;code&gt;waitTableQuenue&lt;/code&gt;，按 &lt;code&gt;index/maxPlayer&lt;/code&gt; 比例在门口插值站位——队伍有多长，人就排多密；&lt;/li&gt;
&lt;li&gt;空桌释放时遍历等待队列，队首补位、全队回调前移。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;没有定时器轮询，全靠事件驱动，几十行代码把&amp;quot;等位&amp;quot;这个经营游戏的核心张力做出来了。&lt;/p&gt;
&lt;h3 id=&#34;24-运行时程序化动画两帧图集走天下&#34;&gt;2.4 运行时程序化动画：两帧图集走天下&lt;/h3&gt;
&lt;p&gt;所有角色（50 个顾客皮肤 + 2 个服务员）的走路动画都是&lt;strong&gt;运行时代码创建&lt;/strong&gt;的。&lt;code&gt;AnimationBase.js&lt;/code&gt; 只有 23 行：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clip&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;AnimationClip&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;createWithSpriteFrames&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;frames&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;clip&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;wrapMode&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cc&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;WrapMode&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Loop&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;美术只需要给每个皮肤画两帧（&lt;code&gt;icon&lt;/code&gt;/&lt;code&gt;icon2&lt;/code&gt;），代码动态建 clip 循环播放，&amp;ldquo;两帧鬼畜踏步&amp;quot;反而有一种魔性的喜感。50 套皮肤 = 100 帧图 + 一段 23 行的代码，动画资源维护成本为零。这个思路和《去赶海吧》的皮肤动画一脉相承。&lt;/p&gt;
&lt;h3 id=&#34;25-金币堆对象池--聚合拾取&#34;&gt;2.5 金币堆：对象池 + 聚合拾取&lt;/h3&gt;
&lt;p&gt;金币是这个游戏最高频的创建/销毁对象，处理上花了最多心思（git 历史里&amp;quot;金币堆积优化&amp;quot;&amp;ldquo;金币拾取动画效果修改&amp;quot;好几个 commit 都在打磨这块）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;金币走 &lt;code&gt;cc.NodePool&lt;/code&gt;（上限 50），生成时随机旋转、1 秒散落动画到桌边随机位置；&lt;/li&gt;
&lt;li&gt;同一格子的金币记入 &lt;code&gt;nearGroup&lt;/code&gt; &lt;strong&gt;按堆聚合&lt;/strong&gt;，面额累加进 &lt;code&gt;valueMap&lt;/code&gt;——十份饭钱堆成一坨，而不是十个独立节点；&lt;/li&gt;
&lt;li&gt;池满时 &lt;code&gt;getGoldByMoveNearby&lt;/code&gt; 从邻居堆&amp;quot;借&amp;quot;一个实体过来复用；&lt;/li&gt;
&lt;li&gt;点击一堆 → 整堆回收，合成一个 &amp;ldquo;+总额&amp;rdquo; 数字标签飞向屏幕左上角的 HUD，飞行时长按距离等比计算，落地才到账。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;性能和手感都照顾到了：节点数有上限，拾取有&amp;quot;整堆收进腰包&amp;quot;的爽感。&lt;/p&gt;
&lt;h3 id=&#34;26-事件驱动与成就三态流转&#34;&gt;2.6 事件驱动与成就三态流转&lt;/h3&gt;
&lt;p&gt;全局只有一个 50 行的事件中心（&lt;code&gt;eventuality.js&lt;/code&gt;，出自 Crockford《JavaScript 语言精粹》里的 &lt;code&gt;eventuality&lt;/code&gt; 函数），只有 &lt;code&gt;fire/on/removeListener&lt;/code&gt; 三个方法，所有跨模块通信——&lt;code&gt;loadover&lt;/code&gt;、&lt;code&gt;addGold&lt;/code&gt;、&lt;code&gt;unLock&lt;/code&gt;、&lt;code&gt;serverPlayer&lt;/code&gt;、&lt;code&gt;invite&lt;/code&gt;、&lt;code&gt;arriveAtGate&lt;/code&gt;、&lt;code&gt;showDialog&lt;/code&gt;——都走它。&lt;/p&gt;
&lt;p&gt;成就/每日任务系统（两个 Manager 合计 660 行）建立在这个事件中心上，数据按三态流转：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[unachieveMap&amp;lt;br/&amp;gt;未达成 按类型分桶] --&amp;gt;|事件计数达标| B[waitPayMap&amp;lt;br/&amp;gt;达成待领取]
    B --&amp;gt;|点击领取| C[已领取&amp;lt;br/&amp;gt;写入存档]
    C --&amp;gt;|自动检查| D[链上下一档成就]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;事件一来，只检查对应桶里的成就，不用全表扫描；领取后自动把下一档（服务 10 人 → 服务 50 人 → ……）挪进未达成桶继续监听。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三整体架构一览&#34;&gt;三、整体架构一览&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    subgraph 场景层
        L[loading.fire&amp;lt;br/&amp;gt;微信分包加载] --&amp;gt; G[game.fire&amp;lt;br/&amp;gt;主场景 103节点]
    end
    subgraph 入口与全局
        G --&amp;gt; GAME[game.js&amp;lt;br/&amp;gt;顾客生成/对象池]
        GL[global.js&amp;lt;br/&amp;gt;playerData/event/realTimeData]
        GAME --&amp;gt; GL
    end
    subgraph 玩法层
        GAME --&amp;gt; P[player.js&amp;lt;br/&amp;gt;顾客AI: 排队/寻路/点单/巡游]
        GAME --&amp;gt; W[waiterPlayer.js&amp;lt;br/&amp;gt;迎宾氛围组]
        GAME --&amp;gt; FM[FurnitureManager&amp;lt;br/&amp;gt;家具摆放/餐桌注册]
        GAME --&amp;gt; GM[GoldManager&amp;lt;br/&amp;gt;金币堆/拾取]
        P --&amp;gt; AS[AStar.js&amp;lt;br/&amp;gt;21×13格子寻路]
        P --&amp;gt; SM[screenManager&amp;lt;br/&amp;gt;格子坐标系/跨屏门]
    end
    subgraph 系统层
        AM[AchievementManager&amp;lt;br/&amp;gt;15条成就]
        DM[DailyTaskManager&amp;lt;br/&amp;gt;每日任务随机6条]
        LV[ListView&amp;lt;br/&amp;gt;自研虚拟列表]
        GUI[guide.js&amp;lt;br/&amp;gt;HUD与五个面板]
    end
    subgraph 数据层
        PD[player-data.js&amp;lt;br/&amp;gt;6份json配置+localStorage存档]
        RT[realTimeData.js&amp;lt;br/&amp;gt;餐桌队列/对象池/运行时状态]
    end
    GL --&amp;gt; AM
    GL --&amp;gt; DM
    GL --&amp;gt; PD
    GL --&amp;gt; RT
    GUI --&amp;gt; LV&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;数据分三层：&lt;code&gt;defines.js&lt;/code&gt; 常量表（配置路径 / 存档键 / 事件名）、&lt;code&gt;player-data.js&lt;/code&gt; 持久层（6 份 json 配置 + localStorage 存档，配置是 Excel 手填导出的）、&lt;code&gt;realTimeData.js&lt;/code&gt; 运行时状态（对象池、餐桌队列、zIndex 发号器）。首次进游戏送 400 金币，默认解锁每类第一个条目。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四找茬彩蛋&#34;&gt;四、找茬彩蛋&lt;/h2&gt;
&lt;p&gt;和《去赶海吧》一样，代码里的瑕疵原样保留，欢迎找茬：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;&lt;em&gt;A&lt;/em&gt; 代价函数传错参&lt;/em&gt;*（&lt;code&gt;AStar.js:87&lt;/code&gt;）：算邻居格移动代价时，&lt;code&gt;_costToMove(borderPosition, finish)&lt;/code&gt; 把终点当成上一个节点传了进去，导致大量格子被按斜走 14 计费、g 值普遍虚高。好在曼哈顿 h 也偏小，错误互相稀释，路还是能找对，只是已经不是标准 A* 了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全项目调了一个不存在的方法&lt;/strong&gt;：事件中心只实现了 &lt;code&gt;removeListener&lt;/code&gt;，但十几处 &lt;code&gt;onDestroy&lt;/code&gt; 里都在调 &lt;code&gt;global.event.off(...)&lt;/code&gt;——调用即 TypeError，只是主场景从不销毁所以没炸过。就算有 &lt;code&gt;off&lt;/code&gt;，传进去的也是新建箭头函数，永远匹配不上原监听器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;死代码雷区&lt;/strong&gt;（&lt;code&gt;utils/pathSearch.js&lt;/code&gt;）：另一套从未被实例化的 A*，里面有 &lt;code&gt;this.year = &#39;2002&#39;&lt;/code&gt; 调试残留、原型方法引用闭包变量（调用即 ReferenceError）、严格模式下裸赋值，是从网上教程抄来没跑通的草稿。&lt;code&gt;paopao.js&lt;/code&gt; 和 &lt;code&gt;food.js&lt;/code&gt; 则是从泡泡龙/三消项目移民来的——&lt;code&gt;food.js&lt;/code&gt; 的 &lt;code&gt;onLoad&lt;/code&gt; 会调一个不存在的方法，挂上节点必崩，所以它谁也没挂。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拼写宇宙&lt;/strong&gt;：&lt;code&gt;createPalyer&lt;/code&gt;、&lt;code&gt;walktoTabel&lt;/code&gt;、&lt;code&gt;distence&lt;/code&gt;、&lt;code&gt;memuPanel&lt;/code&gt; 全文通行；最惨的是 &lt;code&gt;awarddiamand&lt;/code&gt;（成就表）和 &lt;code&gt;awarddiamond&lt;/code&gt;（家具表）两种拼法并存，&lt;code&gt;AchievementItem.getAward&lt;/code&gt; 只认前者，家具表里的钻石奖励永远读不到。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每日任务污染成就存档&lt;/strong&gt;（&lt;code&gt;DailyTaskManager.js:220&lt;/code&gt;）：每日服务计数误写进了成就用的总计数键；&lt;code&gt;payAward&lt;/code&gt; 的 switch 里还有两个永远进不去的 &lt;code&gt;undefined&lt;/code&gt; case。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外 &lt;code&gt;defines.js&lt;/code&gt; 里挂着 9 个根本不存在的配置路径（&lt;code&gt;engineerNameConfig&lt;/code&gt;……&amp;ldquo;工程师&amp;quot;暴露了这是从另一个项目复制来的），food.json 里 54 道菜的名字是&amp;quot;食物1&amp;quot;到&amp;quot;食物54&amp;rdquo;——明显的占位数值没来得及填。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五写在最后&#34;&gt;五、写在最后&lt;/h2&gt;
&lt;p&gt;从 git 历史能还原出开发顺序：跨屏寻路 → 桌子排队 → 成就系统 → 每日任务 → 钻石 → 金币特效与堆积优化 → 层级修正——先把核心玩法立住，再一层层包外围系统，最后打磨手感。&lt;/p&gt;
&lt;p&gt;这是一个典型的个人开发者&amp;quot;麻雀型&amp;quot;项目：4000 行代码里同时住着写得相当讲究的虚拟列表、排队模型、跨屏寻路，和成堆的复制粘贴残留、拼写错误与失效代码。亮点和找茬各占一半，反而更适合做&amp;quot;真实项目代码考古&amp;quot;的样本——毕竟真实项目从来不是教科书。&lt;/p&gt;
&lt;p&gt;源码在 &lt;a href=&#34;https://gitee.com/soulnew/princes&#34;&gt;https://gitee.com/soulnew/princes&lt;/a&gt;，欢迎 Star，也欢迎提 Issue 交流。&lt;/p&gt;
</description>
    </item>
    <item>
      <title>《去赶海吧》：用 Cocos Creator 写一款「大鱼吃小鱼」io 微信小游戏</title>
      <link>https://ftmao.com/blog/posts/fishpick-cocos-io-game-intro/</link>
      <pubDate>Wed, 26 Feb 2020 14:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/fishpick-cocos-io-game-intro/</guid>
      <description>&lt;h1 id=&#34;去赶海吧用-cocos-creator-写一款大鱼吃小鱼io-微信小游戏&#34;&gt;《去赶海吧》：用 Cocos Creator 写一款「大鱼吃小鱼」io 微信小游戏&lt;/h1&gt;
&lt;p&gt;最近把自己做的一款微信小游戏整理开源了，写篇文章介绍一下玩法和实现上的一些技术点，希望能给同样在做休闲小游戏的朋友一些参考。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开源地址：&lt;a href=&#34;https://gitee.com/soulnew/fishPick&#34;&gt;https://gitee.com/soulnew/fishPick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;技术栈：Cocos Creator 2.3.4 + TypeScript&lt;/li&gt;
&lt;li&gt;平台：微信小游戏&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;游戏叫《去赶海吧》，核心玩法一句话概括：&lt;strong&gt;摇杆控制赶海人在海里吃海鲜攒贝壳，分数即体型，大鱼吃小鱼，活到最后吃鸡&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一玩法介绍&#34;&gt;一、玩法介绍&lt;/h2&gt;
&lt;h3 id=&#34;11-核心循环&#34;&gt;1.1 核心循环&lt;/h3&gt;
&lt;p&gt;玩家通过全屏虚拟摇杆控制角色游动，摄像机始终跟随主角。地图上持续刷新各种海洋生物，吃到就得分（贝壳），分数直接决定体型和战斗力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;两个玩家相遇，分高者吃掉分低者&lt;/strong&gt;——这是 io 类游戏的经典规则；&lt;/li&gt;
&lt;li&gt;被吃的玩家死亡后会&lt;strong&gt;爆出金币&lt;/strong&gt;（一半的分数拆成最多 10 条金币鱼散布在周围），形成&amp;quot;击杀后抢遗产&amp;quot;的爽点；&lt;/li&gt;
&lt;li&gt;分数变成负数会直接死亡，所以海里的&amp;quot;毒蛇&amp;quot;（-2 分）和&amp;quot;海胆&amp;quot;（-1 分）是要躲着走的。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[摇杆控制游动] --&amp;gt; B[吃海鲜攒贝壳]
    B --&amp;gt; C{遇到其他玩家?}
    C --&amp;gt;|我分高| D[吃掉对方]
    C --&amp;gt;|我分低| E[触发逃跑 AI / 被吃]
    D --&amp;gt; F[对方爆金币]
    F --&amp;gt; B
    B --&amp;gt; G[实时排行榜 Top5]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;12-鱼的种类与概率设计&#34;&gt;1.2 鱼的种类与概率设计&lt;/h3&gt;
&lt;p&gt;海里的鱼不只是&amp;quot;分&amp;quot;，还混入了道具和陷阱，让取舍更有策略性：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;类型&lt;/th&gt;
					&lt;th&gt;效果&lt;/th&gt;
					&lt;th&gt;概率（实现值）&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;加速道具&lt;/td&gt;
					&lt;td&gt;速度 ×1.5，持续 10 秒&lt;/td&gt;
					&lt;td&gt;5%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;护盾道具&lt;/td&gt;
					&lt;td&gt;免疫被吃 / 免疫负分鱼，10 秒&lt;/td&gt;
					&lt;td&gt;5%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;鱼、虾、蟹&lt;/td&gt;
					&lt;td&gt;+1 分&lt;/td&gt;
					&lt;td&gt;约 35%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;水母、海星&lt;/td&gt;
					&lt;td&gt;+2 分&lt;/td&gt;
					&lt;td&gt;20%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;乌龟&lt;/td&gt;
					&lt;td&gt;+3 分&lt;/td&gt;
					&lt;td&gt;10%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;海豚&lt;/td&gt;
					&lt;td&gt;+4 分&lt;/td&gt;
					&lt;td&gt;5%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;毒蛇&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;-2 分&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;10%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;海胆&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;-1 分&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;10%&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;负分鱼 10 秒后会自动消失，道具的剩余时间通过节点透明度渐变来提示，玩家一眼就能看懂状态。&lt;/p&gt;
&lt;h3 id=&#34;13-四种游戏模式&#34;&gt;1.3 四种游戏模式&lt;/h3&gt;
&lt;p&gt;主菜单提供四个入口，共用同一套核心机制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无尽模式&lt;/strong&gt;：死了可以用激励视频复活，AI 每 5 秒重生补齐，刷分为主；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;挑战模式&lt;/strong&gt;：&amp;ldquo;吃鸡&amp;quot;玩法——剩余玩家数低于阈值后进入 10 秒倒计时，活到最后获胜；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;贪吃蛇模式&lt;/strong&gt;：吃到的鱼变成尾巴跟随身后，撞到别人的尾巴即死；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;人挤人模式&lt;/strong&gt;：群体 Steering Behaviors 的实验玩法，20 个跟随者 + 1 个领队挤作一团。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;挑战模式和贪吃蛇模式开局前有一个&lt;strong&gt;伪联机匹配面板&lt;/strong&gt;：&amp;ldquo;搜索附近玩家…&amp;ldquo;的省略号动画，12 个头像从 288 张头像图集中不重复随机抽取、按 0~6 秒随机延迟逐个亮起——营造真人匹配的感觉，实际对手全是 AI。这是小团队没有联机服务器时的一个低成本体验方案。&lt;/p&gt;
&lt;h3 id=&#34;14-ai-设计&#34;&gt;1.4 AI 设计&lt;/h3&gt;
&lt;p&gt;AI 不是无头苍蝇，每个 AI 出生时会随机分到三种性格之一：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;进攻型&lt;/strong&gt;：主动出击追杀人；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;吃鱼型&lt;/strong&gt;：闷头吃海鲜发育；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;闲逛型&lt;/strong&gt;：随机游走为主。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;配合两个细节参数避免行为抖动：被追时有 2 秒&amp;quot;逃跑冷却&amp;rdquo;（不会在追/逃之间反复横跳），追击最长持续 7 秒（避免死追不放）。AI 找鱼也不搜全图，而是在自己屏幕范围内找&lt;strong&gt;鱼最多的格子&lt;/strong&gt;再过去吃——这个格子划分和碰撞检测用的是同一套空间网格，下面会讲。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二技术亮点&#34;&gt;二、技术亮点&lt;/h2&gt;
&lt;p&gt;整个项目是单场景架构（主场景约 400 个节点），28 个 TypeScript 脚本，代码量不大，但性能优化和 AI 上的几个点值得展开说说。&lt;/p&gt;
&lt;h3 id=&#34;21-空间哈希网格把碰撞检测从-on-降为局部查询&#34;&gt;2.1 空间哈希网格：把碰撞检测从 O(n) 降为局部查询&lt;/h3&gt;
&lt;p&gt;地图上同时存在几百条鱼，如果每帧让每条鱼和所有玩家做碰撞检测，微信小游戏的低端机上肯定扛不住。做法是把地图划分为 &lt;strong&gt;20×20 的网格&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    A[鱼出生/移动] --&amp;gt; B[按坐标计算所在格子&amp;lt;br/&amp;gt;key = x_y]
    B --&amp;gt; C[挂到对应桶 BlockManager]
    D[碰撞检测] --&amp;gt; E[只查玩家 AABB 覆盖的格子&amp;lt;br/&amp;gt;getCoveredBlock]
    E --&amp;gt; F[与桶内鱼做精确检测]
    C -.鱼移动跨格.-&amp;gt; G[updatePosofFish 迁移桶]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;碰撞检测时只查询玩家包围盒覆盖的几个格子里的鱼，而不是全图扫描；&lt;/li&gt;
&lt;li&gt;鱼移动跨格时从一个桶迁移到另一个桶；&lt;/li&gt;
&lt;li&gt;这套网格还顺手解决了另外两个需求：&lt;strong&gt;视野裁剪&lt;/strong&gt;（视野外格子里的鱼直接隐藏）和上面提到的 &lt;strong&gt;AI 找鱼最密集区域&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再配合&lt;strong&gt;降频采样&lt;/strong&gt;——每 4 帧才做一次全量碰撞检测——CPU 占用就控制得很好了。&lt;/p&gt;
&lt;h3 id=&#34;22-对象池向-gc-抖动说不&#34;&gt;2.2 对象池：向 GC 抖动说不&lt;/h3&gt;
&lt;p&gt;鱼、金币、飘分数字这些高频创建销毁的节点全部走对象池：&lt;code&gt;Map&amp;lt;string, Node[]&amp;gt;&lt;/code&gt; 按预制名分桶，全局上限 500。小游戏平台上 GC 卡顿是卡顿的主要来源之一，对象池是性价比最高的优化。&lt;/p&gt;
&lt;h3 id=&#34;23-steering-behaviors教科书式的群体行为实现&#34;&gt;2.3 Steering Behaviors：教科书式的群体行为实现&lt;/h3&gt;
&lt;p&gt;&amp;ldquo;人挤人&amp;quot;模式的背后是把 Reynolds 那套经典的 Steering Behaviors 完整实现了一遍（&lt;code&gt;Gamer.ts&lt;/code&gt;，约 400 行）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    subgraph Steering Behaviors
        A[Seek 追踪] --&amp;gt; H[AccumulateForce&amp;lt;br/&amp;gt;力累加 + 截断]
        B[Flee 逃跑] --&amp;gt; H
        C[Arrive 减速到达] --&amp;gt; H
        D[Wander 随机游走] --&amp;gt; H
        E[Alignment / Cohesion / Separation&amp;lt;br/&amp;gt;群体三规则] --&amp;gt; H
        F[Pursuit 带预测提前量的追击] --&amp;gt; H
        H --&amp;gt; I[EnforceNonPenetrationConstraint&amp;lt;br/&amp;gt;防穿透约束]
        I --&amp;gt; J[最终速度/朝向]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;其中 Wander 用的是&amp;quot;jitter + 圆半径 + 投影距离&amp;quot;的经典做法，Pursuit 会根据目标速度算提前量。所有行为输出的力先累加、再按 max_force 截断，最后加一个防穿透的分离约束——这套结构在后面的项目里基本可以原样搬走。&lt;/p&gt;
&lt;h3 id=&#34;24-屏外敌人指示器io-游戏的标配-ux&#34;&gt;2.4 屏外敌人指示器：io 游戏的标配 UX&lt;/h3&gt;
&lt;p&gt;强敌在屏幕外时，玩家需要感知威胁方向。实现方式是给每个屏外 AI 分配一个&lt;strong&gt;钉在屏幕边缘的箭头 + 分数标签&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用 &lt;code&gt;atan2&lt;/code&gt; 计算敌人相对主角的方向角；&lt;/li&gt;
&lt;li&gt;按屏幕对角线角度判断箭头该落在上下左右哪条边上；&lt;/li&gt;
&lt;li&gt;求射线与屏幕边界的交点作为箭头位置；&lt;/li&gt;
&lt;li&gt;8 种随机颜色区分不同敌人。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;代码不长，但体验提升非常明显——被高分玩家接近时能提前看到箭头逼近，紧张感就出来了。&lt;/p&gt;
&lt;h3 id=&#34;25-运行时程序化动画&#34;&gt;2.5 运行时程序化动画&lt;/h3&gt;
&lt;p&gt;美术资源只有每张皮肤 2 帧的游动图，动画是运行时用 &lt;code&gt;cc.AnimationClip.createWithSpriteFrames&lt;/code&gt; &lt;strong&gt;动态创建&lt;/strong&gt;的 2 帧循环。换皮肤只需要换图集里的起始帧索引（100/102/104…），不用为每套皮肤手工做 Animation 资源。6 套皮肤 = 12 帧图 + 一段代码，维护成本几乎为零。&lt;/p&gt;
&lt;h3 id=&#34;26-事件总线与模块解耦&#34;&gt;2.6 事件总线与模块解耦&lt;/h3&gt;
&lt;p&gt;全局只有一个 50 行的事件总线（on/off/fire）加一个聚合单例，音乐、广告、面板切换、碰撞结果全部走事件通信。单场景多面板的结构下，这种轻量解耦比引入框架更实用。&lt;/p&gt;
&lt;h3 id=&#34;27-微信小游戏生态全套集成&#34;&gt;2.7 微信小游戏生态全套集成&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;分包加载&lt;/strong&gt;：字体、头像、音频、图集拆成 4 个分包，loading 场景按包显示进度；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;广告变现&lt;/strong&gt;：Banner + 激励视频复活；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;社交&lt;/strong&gt;：&lt;code&gt;wx.showShareMenu&lt;/code&gt; 分享、好友排行榜（开放数据域 + &lt;code&gt;wx.postMessage&lt;/code&gt; 通信）、&lt;code&gt;wx.login&lt;/code&gt; 登录；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;轻量后端&lt;/strong&gt;：服务器只负责登录、存分数、拉排行三个接口，玩法完全单机，部署成本极低。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三整体架构一览&#34;&gt;三、整体架构一览&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    subgraph 场景层
        L[loading.fire&amp;lt;br/&amp;gt;分包加载 + 剧情动画] --&amp;gt; F[FirstPannel.fire&amp;lt;br/&amp;gt;单场景多面板]
    end
    subgraph 玩法层
        F --&amp;gt; GM[gameManager&amp;lt;br/&amp;gt;刷鱼/模式管理/降频碰撞]
        GM --&amp;gt; P[Player / BasePlayer&amp;lt;br/&amp;gt;得分/体型/道具/死亡爆币]
        GM --&amp;gt; Fish[Fish&amp;lt;br/&amp;gt;概率产出]
        GM --&amp;gt; Snake[贪吃蛇尾巴 lerp 跟随]
    end
    subgraph 数据与系统层
        GM --&amp;gt; BM[BlockManager&amp;lt;br/&amp;gt;20×20 空间网格]
        GM --&amp;gt; CM[CollideManager]
        GM --&amp;gt; PM[PoolManager&amp;lt;br/&amp;gt;对象池上限500]
        GM --&amp;gt; SM[ScoreManager&amp;lt;br/&amp;gt;实时排行]
        GM --&amp;gt; EV[CustomEventListener&amp;lt;br/&amp;gt;事件总线]
    end
    subgraph 平台层
        EV --&amp;gt; WX[分包/广告/分享/好友排行/登录]
        EV --&amp;gt; SRV[轻量后端&amp;lt;br/&amp;gt;login / saveScore / getRank]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;四写在最后&#34;&gt;四、写在最后&lt;/h2&gt;
&lt;p&gt;这个项目验证了一个想法：&lt;strong&gt;没有联机服务器，用&amp;quot;伪匹配 + 性格化 AI&amp;quot;也能撑起 io 游戏的核心体验&lt;/strong&gt;。代码里当然也留下了一些遗憾，比如数值全部硬编码没有配置表、&lt;code&gt;BasePlayer.scoreChange()&lt;/code&gt; 里有一条永远执行不到的 else-if 分支（&lt;code&gt;score&amp;gt;=10&lt;/code&gt; 先命中，后面的大体型缩放被挡住了）——这些都在代码里原样保留着，权当给读者的&amp;quot;找茬彩蛋&amp;rdquo;。&lt;/p&gt;
&lt;p&gt;源码在 &lt;a href=&#34;https://gitee.com/soulnew/fishPick&#34;&gt;https://gitee.com/soulnew/fishPick&lt;/a&gt;，欢迎 Star，也欢迎提 Issue 交流。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
