第 20 章 项目蓝图:把前面的概念拼成你的塔防 / 大富翁

前面十九章,我们走了一条”溯源”的路:从现实里的自然现象(光怎么传播、声音怎么波动、物体在空间怎么定位),一直挖到 Unity 引擎里那些具体的旋钮(光照参数、采样率、坐标变换、组件组合)。我反复用”现实问题 → 工程师方案 → 落进引擎成了什么参数”这条线,就是想让你脑子里那张”概念地图”是连得起来的,而不是一堆散碎的术语。

但到了这一章,我得先问你一个扎心的问题:

你学这些,到底是为了什么?

不是为了去当图形学研究员,也不是为了背下所有组件的 API。你说过,你想”能设计效果、能动手做、能去面 Unity 岗”,而且你想做一个联机 + 登录 + 对接你自己的 Go 服务的策略游戏。那这一章,就是把前面十九章的”砖块”——坐标、网格、光照、雾、声音、组件、联机——摆到一张建筑图纸上,告诉你:它们分别砌在墙的哪个位置。

我把它叫”项目蓝图”。读完后,你该能回答三句话:我的游戏整体长什么样、每个系统用到了前面哪几章、我第一步该先碰什么。


20.1 你的终极目标是什么:一个”联机 + 登录 + 对接 Go 服务”的策略游戏

先跟你共个情。很多人学 Unity,一上来就焦虑:“我是不是得先精通着色器?得会写物理引擎?得懂渲染管线?” 我跟你直说——你不需要。

你不是要去和图形学博士抢饭碗。你的目标很务实,拆开就是三层:

  1. 能设计效果:看到”敌人死亡炸开一团火、血条跟着掉、背景乐渐弱”,你脑子里能反推出”这是 ParticleSystem + Slider + AudioSource 的协作”(呼应第 18 章心法)。这一步,前面章节已经给你铺好了。
  2. 能动手做:打开 Unity,能拼出可玩的一局——哪怕第一版是”方块敌人沿路走、点一下建座塔”。这一步,是本章路线图要解决的。
  3. 能去面 Unity 岗:面试时被问”你怎么实现一个联机塔防”,你能从架构讲到组件讲到网络消息,而不是只说”我用了 Netcode”。这一步,靠的是前面”懂设计意图”的训练。

而你真正的差异化优势,是第四层——你的 Go 后端。别人做联机游戏,卡在”服务器怎么写”;你写 HTTP handler、JWT、数据库 CRUD 是家常便饭。所以你的目标本质上是:

一个策略游戏,玩家能注册登录(你的 Go 管),进度和排行榜存你数据库(你的 Go 管),对局里能和别人联机(实时同步,Go 或网络库管),而游戏画面、手感、效果由 Unity 客户端呈现(前面学的全用上)。

这就对上了第 19 章那张架构图:你的 Go 是”指挥”,Unity 是”画师”。本章要做的,是把”画师 + 指挥”具体落成一份可执行的计划。

反问你一下:如果现在有人问你”你的游戏到底要解决玩家什么需求”,你能用一句话说清吗?“塔防”和”大富翁”是两个完全不一样的玩法——它们的节奏、联机需求、技术难点都不同。所以接下来,我先帮你把”你到底要做哪种”这个最容易含糊的问题掰清楚。


20.2 两种游戏模式的架构对比

你在原话里说”塔防游戏或者大富豪”。这里我先点出一个歧义,免得你后面钻牛角尖:

“大富豪”在中文圈里可能指两样东西。一是大富翁(Monopoly 类,掷骰子走棋盘、买地收租那种桌面游戏);二是大富豪(一种纸牌玩法,类似跑得快)。这两个虽然都叫”大富豪/大富翁”,但架构天差地别。

本文的做法是:按”回合制棋盘类”(即大富翁 / Monopoly 那种)来展开,同时把它和实时塔防做对比。为什么这么选?因为原话里”大富豪”和”塔防”并列,而塔防是经典的实时类,那么与之对照的”大富豪”最自然的理解就是一个回合制棋盘游戏——它和塔防恰好代表了”实时 vs 回合”这两种极端,对比着学,你的架构心智最完整。

但你别纠结定义。 重点是:无论你最后做塔防还是大富翁,本章给你的架构心法是通用的。下面我把两种模式摆在一起比。

20.2.1 塔防(实时类):世界不停在变

塔防的核心循环是:怪物一波波从出生点沿固定路线走向你的基地,你在路边建塔、升级塔,塔自动攻击范围内敌人,敌人被清光或突破基地就分胜负。

它的几个特征直接决定了技术难度:

  • 节奏快、状态高频变:怪每秒都在移动,塔每秒可能开火,伤害每秒都在结算。世界状态在”实时流动”。
  • 需要状态同步:A 玩家屏幕上的怪走到 (3,0,5),B 玩家也得看到它在那。这正好连回第 19 章讲的状态同步——服务器定期把每个物体的位置/血量广播出去。
  • 延迟敏感:你打出的子弹,200ms 后才在队友屏幕生效,体感就”飘”。所以第 19.7 节埋的”插值/预测”在塔防里是绕不开的。
  • 敌人 AI 沿路走:怪要绕过地形走到基地,这用第 18.5 / 18.12 节的 NavMeshAgent(自动寻路)最合适——给场景烘焙一张”可走地图”,Agent 自动绕开障碍。

一句话:塔防是”实时 + 状态同步 + 服务器权威”的典型,技术面最全,也最能检验你前面学的表现层(光照/雾/摄像机/声音)和网络层(第 19 章)能不能咬合。

20.2.2 大富翁 / 大富豪(回合制棋盘类):一轮一动,世界大部分时间在”等”

大富翁的核心循环是:掷骰子 → 棋子走 N 格 → 落到某地块触发事件(买地 / 交税 / 进监狱)→ 可能和其他玩家交易 → 轮到下一家。

它的特征恰好和塔防相反:

  • 节奏慢、状态低频变:一回合才动一次,玩家大部分时间在”思考 / 等别人”。世界状态不是流动的,而是”跳变”的。
  • 对实时零要求:你根本不需要毫秒级同步。甚至像第 19 章参考答案说的,用最慢的 HTTP 轮询都够——客户端每隔一秒问服务器”现在轮到谁了、棋盘变成啥样了”,完全没问题,玩家毫无察觉。
  • 事件驱动 > 状态流:更适合”谁掷了骰子、走了几步、触发了什么地块”这种离散事件的同步,而不是”每帧位置”。
  • 棋盘是离散网格:不像塔防那种连续空间寻路,大富翁的”格子”是有限个(比如 40 格),状态就是一个数组 cells[40] 描述每块地属于谁、房价几级。这用你写 Go 最熟的”数组 + 结构体”就能建模,比 3D 寻路简单太多。

一句话:大富翁是”回合制 + 事件同步 + 甚至 HTTP 就够”的典型。联机难度远低于塔防,但对”游戏逻辑状态机”和”玩家间交互(交易/事件)“设计要求更高。

20.2.3 两种模式摆在一起的对照表

维度塔防(实时)大富翁(回合制棋盘)
世界状态连续流动,每帧在变离散跳变,回合间才变
联机核心状态同步(第 19.6)事件同步,HTTP 轮询都够
延迟敏感度高,需插值/预测极低,慢一点无所谓
空间模型连续 3D 坐标(第 2 章)离散格子数组(像棋盘索引)
AI / 移动NavMeshAgent 沿路(18.12)掷骰走固定格子数
你 Go 后端角色实时 WS + 账号数据账号数据 + 回合调度
适合练的技术全栈(表现+网络最全)游戏逻辑 + 轻量联机

关键认知:两种模式你都能用同一套”Unity 表现层 + Go 后端”吃下来,区别只在联机那一层用多重的方法。塔防逼你上 WebSocket + 状态同步;大富翁用 HTTP 轮询都绰绰有余。这直接影响了 20.4 的技术选型和 20.5 的路线图节奏。

反问:如果你做的是大富翁,但你非要上 WebSocket 做实时同步,是”更专业”还是”杀鸡用牛刀”?反过来,如果做塔防却图省事用 HTTP 轮询,会出什么具体问题?想清楚这个,你就真正懂了”架构要匹配玩法”这句话。


20.3 把每个游戏系统映射到你已经学的教程概念

这一节是全章的”字典中心”。我帮你把”一个策略游戏里会出现的系统”和”前面第几章的什么概念”一一对应。这样你动手时,遇到任何需求,都能立刻查到”该翻回哪一章”。

我以塔防为主轴列(大富翁可类比替换),因为每个系统都能对应上:

游戏系统用到的教程概念(第 X 章)落到什么组件/参数
怪 / 塔 / 玩家在世界的摆放第 2 章 坐标Transform 的 Position/Rotation/Scale(17.3)
怪的 3D 模型、形状第 4 章 网格MeshFilter(指定用哪个网格)+ MeshRenderer(18.2)
模型表面的皮(皮肤/金属感)第 5 章 纹理与材质Material 的贴图、金属度、平滑度(18.2)
场景的明暗、阴影第 6 章 光照Light 组件:Directional/Point/Spot、Intensity、Shadows(18.6)
玩家从哪看战场第 7 章 摄像机Camera:FOV、Near/Far Clip、正交/透视(18.7)
远处用雾藏边界、造氛围第 8 章 雾化场景 Fog 设置(连回 19.7 表现层)
背景音乐、音效第 9/10/11/12 章 音频AudioSource(发声)+ AudioListener(听,挂摄像机)(18.8)
物体能力的拼装第 17/18 章 组件组合优于继承:挂组件而非写死类(17.2、18.15)
敌人走路/攻击/死亡动画第 18.9 节 AnimatorAnimator + AnimationClip 状态机(18.9)
怪沿路走、绕开障碍第 18.12 节 NavMeshAgentNavMeshAgent + 烘焙 NavMesh(18.12)
塔开火的炮口火光、爆炸第 18.10 节 ParticleSystemParticleSystem 的 Emission/Shape/Color(18.10)
血条、金币、按钮界面第 18.11 节 Canvas/UICanvas + Slider/Text/Button(18.11)
子弹打中敌人第 18.13 节 碰撞回调Collider + OnTriggerEnter(18.13)
联机同步物体第 18.14 节 + 第 19 章NetworkObject / NetworkTransform(18.14),或你的 WS 消息(19.5)
登录 / 存进度 / 排行榜第 19 章 联机与登录UnityWebRequest → 你的 Go HTTP(19.3、19.4)

你可以把这张表当成”查书目录”。比如以后你想加”怪物死亡时背景乐渐弱”,你就知道:背景乐是第 9-12 章的 AudioSource(第 18.8 节),渐弱是调 AudioSource 的 Volume 参数,触发时机是敌人死亡事件(第 17.4 节的”事件/消息”通信)。没有一个需求是凭空来的,它都能回溯到你已经建好的概念体系。

大富翁模式怎么替换?棋盘格子 = 第 2 章坐标里”离散的格子索引”(不是连续 3D,但你照样用 Transform 摆每个格子的位置);掷骰子动画 = 第 18.9 节 Animator;地块买卖事件 = 第 19 章的 JSON 消息(但用 HTTP 轮询就够了);玩家棋子移动 = 简单的 Transform 插值(不用 NavMeshAgent,因为是直线跳格)。你看,同一套概念,换种拼法就是另一种游戏。

这张表背后还有一个元认知,值得你停下来想一想:它其实把”游戏系统”分成了两类,界限很清晰。

第一类是”表现层系统”:怪长什么样(网格+材质)、场景亮不亮(光照)、远处雾不雾(雾化)、声音从哪来(音频)、玩家从哪看(摄像机)。这类系统的共同点是——它们不决定游戏规则,只决定玩家看到的样子。同一个塔防玩法,你可以把怪换成小猫、把光调成赛博朋克风,游戏规则一点没变,游戏还是那个游戏。这就是为什么前面第 4 到 12 章我反复强调”理解参数”——它们是你的”美术材料库”,你调的是观感,不是逻辑。

第二类是”逻辑层系统”:怪往哪走(寻路)、塔怎么打(碰撞回调)、血条数值(UI 背后变量)、联机状态(网络)。这类系统决定游戏怎么玩、谁输谁赢。它们往往和你写的自定义脚本、和你 Go 后端的计算绑在一起。

你动手时,养成一个习惯:改表现层,放心大胆调参数,不影响逻辑;改逻辑层,先在脑子里走一遍”服务器权威”那套(第 19 章),别让客户端偷偷算结果。 这个边界感,是你少踩坑的关键。

反问:上面这张表里,你觉得哪个系统是你”作为 Go 程序员最容易上手”的?又哪个是”纯游戏领域、你从零开始最该花时间”的?想清楚你的舒适区和攻坚区,路线图才有重点。


20.4 技术选型:务实,给新手明确建议

我知道你最怕”选择困难”——联机方案一堆,到底用哪个?我直接给你结论,再解释为什么。

20.4.1 联机方案对比

市面上主流两条路:

路线 A:Unity Netcode for GameObjects(官方网络库)

  • 是什么:Unity 官方出的联机框架,你给物体挂 NetworkObject + NetworkTransform(第 18.14 节),它帮你自动同步位置/旋转/变量。
  • 优点:状态同步开箱即用,不用自己写消息协议;和 Unity 组件体系无缝。
  • 缺点:实时逻辑它管,但你还是要一个服务器跑这个 Netcode 的 Server 端(可以用 Unity 的 Dedicated Server,或自建 relay 转发)。如果你坚持用 Go 当服务器,Netcode 默认不跟 Go 说话,要额外做桥接,反而麻烦。

路线 B:自己用 WebSocket 接你的 Go(推荐,契合你的背景)

  • 是什么:你在 Go 里用 gorilla/websocket 起一个 ws 服务(第 19.5 节写过雏形),Unity 端用 WebSocket 客户端库连上,你们自己定 JSON 消息协议({"type":"build_tower",...})。
  • 优点:你的 Go 后端几乎零新增成本——WebSocket 服务对你就是”又一个长连接 handler”,你写 goroutine 处理每连接、广播房间状态,和写普通 Go 服务一个脑子。服务器权威天然落在你最熟的领域。
  • 缺点:状态同步逻辑(谁动了、广播给谁、插值)要你自己写一点代码。但塔防/大富翁这种规模,这点代码量对你不算事。

我的明确建议:登录/数据层 100% 走你的 Go HTTP(你已会,零门槛);实时对局层,优先路线 B(WebSocket 接 Go)。原因就一条——你最大的优势是 Go 后端,把”指挥”牢牢攥在自己手里,比学一套新的 Netcode 服务器框架更顺、更可控、更好调试。Netcode 适合”不想写服务器”的纯客户端开发者,而你恰恰相反。

这里我多说一句,帮你避开一个”看起来更省事、实际更绕”的坑。有人会想:“Netcode 不是开箱即用状态同步吗,我直接用它的 Server 端不就完了,何必自己写 WebSocket?” 问题在于:Netcode 的 Server 端是跑在 Unity 运行时里的(要么用 Unity 专用服务器构建,要么用它的中继转发),它的”服务器逻辑”是 C# 写在 Unity 工程里的,和你那套已经写好的 Go 账号系统、数据库、JWT 是两套独立的世界。你要么把账号体系也搬进 Unity 服务端(重复造轮子,且你 Go 的积累废了),要么做两套系统之间的桥接(更麻烦)。而路线 B 让”指挥”留在你最熟的 Go 里,Unity 客户端只当”画师”——这才是对你的最短路径。你用 Go 写 WebSocket handler 的熟练度,远胜过你临时学一套 Unity 服务器框架。架构选型不是选”最流行”,是选”最契合你已有能力”。

顺便提一个务实的折中:如果未来你真的想用 Netcode 的同步便利,又想保住 Go 后端,可以走”Netcode 客户端 + 自建 Go relay(转发服务器)“——让 Netcode 管客户端之间的状态插值,Go 只做房间调度和消息中转。但对 MVP 阶段,我仍建议纯路线 B,简单直接。

一句话:MVP 阶段,先做单机可玩,再加登录,再加联机。 别一上来就”我要联机”,那是给自己加难度。下面路线图细说。

20.4.2 登录对接 Go:复用第 19 章流程,你端几乎零新增成本

第 19.3、19.4 节已经把流程讲透了:Unity 用 UnityWebRequest 发 POST /login,你的 Go 校验发 token,客户端存 token,后续请求带 Authorization 头,你的中间件解出 userId。

对你而言,Go 端这套东西你闭着眼都写过——gin 的 POST /login handler、JWT 生成、数据库查用户,全是存量技能。Unity 端只是把”浏览器 fetch”换成”UnityWebRequest”,逻辑一字不差。所以登录这层对你不是”学习成本”,是”复制粘贴 + 改字段”。

20.4.3 状态存储:存档 / 排行榜走 Go + 数据库(你熟)

玩家进度(塔防的第几关、金币数)、排行榜(谁分数高),本质是结构化数据 + 持久化。这正好是你写 Go 的强项:用你熟悉的数据库(Postgres / MySQL / Redis 都行),定义几张表,写几个 CRUD handler。Unity 端在合适时机(过关后、对局结束时)发 HTTP 把数据交给 Go 存起来,下次登录再拉回来。

关键认知:游戏里”会变的、要算的”逻辑(怪在哪、谁死了)归实时层;“要记住的、要排名的”数据归存储层。两层职责分开,别混。这和你后端”有状态服务 vs 数据库”的划分一个道理。

反问:如果有人建议”存档也走 WebSocket 实时通道一起发”,你会怎么回他?提示:存档和实时对局,对”可靠性”和”时效”的要求一样吗?(想想你 Go 里为什么把”重要落库”和”高频消息”分开处理。)


20.5 MVP 路线图:分阶段,每阶段碰哪些 Unity 知识点

“MVP”是”最小可行产品”(Minimum Viable Product)——先做一个能跑起来的简陋版本,再逐步加料。我把它拆成四个阶段,每个阶段都标出”要碰的 Unity 知识点”和”对应教程章节”。你照着这个顺序走,既不至于一上来被联机劝退,也不会学到一半发现前面基础没打牢。

阶段一:单机塔防原型(先别想联机)

目标:屏幕上,方块敌人从出生点沿路走向基地,你点地面建一座塔,塔自动打怪,怪有血条、被打死。

要碰的 Unity 知识点 + 对应章节:

  • 场景搭建、摆放物体 → 第 2 章坐标(Transform 的 Position)
  • 用方块/简单模型当怪和塔 → 第 4 章网格 + 第 5 章材质(哪怕只用默认方块,也理解 MeshFilter/MeshRenderer)
  • 场景打光让东西看得见 → 第 6 章光照(一个 Directional 光就够)
  • 摄像机看向战场 → 第 7 章摄像机(正交或透视俯视都行)
  • 远处加雾藏边界 → 第 8 章雾化(顺手体验,造氛围)
  • 敌人死亡音效、背景乐 → 第 9-12 章音频(AudioSource + Listener)
  • 怪沿路走 → 第 18.12 节 NavMeshAgent(烘焙可走区域)
  • 塔检测范围内敌人、开火 → 第 18.13 节碰撞回调(Trigger 检测)+ 自定义脚本
  • 血条 UI → 第 18.11 节 Canvas + Slider
  • 拼装这一切 → 第 17 章组件(敌人和塔各挂哪些组件,复习 18.15)

本阶段产出:一个纯单机、能玩一局的塔防。这一步不碰任何网络,纯粹练”Unity 表现层 + 游戏逻辑”。这是你信心的地基。

我为什么把这个阶段说成”地基”,值得展开讲一句。你作为后端程序员,习惯”逻辑对了就对了”,但游戏的难点往往不在逻辑,而在手感与呈现:怪走得自不自然(NavMeshAgent 参数)、塔开火有没有反馈(ParticleSystem + AudioSource)、血条跟不跟手(UI 绑定)。这些”软”的东西,单机阶段就能全部试出来。等你阶段三接联机时,如果”怪走得卡”,你立刻知道是单机阶段的移动脚本问题,而不是网络问题——因为单机时它已经走得很顺了。先让单机完美,联机只是”把本地的状态换成服务器给的状态”,排查面被压缩到最小。 这也是为什么我反复说”复杂度分层叠加”不是偷懒,是工程上的必然。

阶段二:加登录,对接你的 Go

目标:游戏启动有登录界面,注册/登录成功后才能进游戏;过关后进度存到你 Go 后端,下次登录拉回。

要碰的知识点 + 对应章节:

  • Unity 发 HTTP → 第 19.3、19.4 节(UnityWebRequest / async + HttpClient,注意异步别卡主线程)
  • 登录 UI → 第 18.11 节 Canvas/UI(输入框、按钮)
  • 你的 Go 端 → 第 19.2、19.3 节的 handler(你写,几乎零新增成本)
  • token 存储 → 第 19.8 节坑点(内存持有 + 过期刷新,别存明文密码)

本阶段产出:游戏有了”身份”。这一步你主要写 Go,Unity 端只加一个登录界面和几个请求。

阶段三:加联机同步(多玩家看到同一局)

目标:两个客户端连上你的 Go WebSocket,能看到”同一局”——A 建的塔 B 也看见,A 打的怪 B 也掉血。

要碰的知识点 + 对应章节:

  • WebSocket 接 Go → 第 19.5 节(Go 端 ws handler + Unity 端 ws 客户端)
  • 物体网络同步 → 第 18.14 节 Network 思路(若用 Netcode);若走路线 B,则你自己定 JSON 消息 build_tower / sync_hp 同步 Transform 和血量
  • 服务器权威 → 第 19.6 节状态同步(服务器算、客户端画)
  • 插值/预测 → 第 19.7 节反问埋的点(缓解延迟,进阶可做)

本阶段产出:游戏”联机了”。这是最难的一跃,但因为前两个阶段把表现层和登录都铺好了,这里你只需专注”状态怎么在玩家间传”。

阶段四:房间 / 匹配 / 排行榜

目标:玩家能创建/加入房间,匹配陌生人,对局结束看排行榜。

要碰的知识点 + 对应章节:

  • 房间管理 → 第 19 章 Go 后端(房间状态、连接池,你写)
  • 排行榜展示 → Unity UI(第 18.11 节)+ 你 Go 的排行榜接口(阶段二延伸)
  • 匹配逻辑 → 你 Go 的并发与状态管理(你熟,goroutine 管房间)

本阶段产出:一个完整可上线的联机策略游戏。到这步,你已经具备面 Unity 岗时”我能从零做联机游戏”的底气。

重要心法:永远先做单机(阶段一),再登录(阶段二),再联机(阶段三)。不要反过来。因为联机问题(延迟、一致)会”淹没”你本该先掌握的游戏基础(移动、碰撞、UI)。就像你学 Go,也是先写单机 CLI,再学 net/http,而不是一上来就写分布式——复杂度要一层层叠加。

反问:为什么我强调”阶段一绝对不要碰联机”?如果你一上来就联机,当”怪没同步”这个 bug 出现时,你分得清是”我的移动脚本写错了”还是”网络消息没发对”吗?分不清,就会在两层之间反复横跳、越改越乱。


20.6 拆解练习:从”需求”反推”组件 + 参数 + 网络消息”

最后一节,我手把手示范第 18.16 节那套”效果 → 组件 → 参数”心法,怎么用在真实需求上。这是你面试和实战都最值钱的能力。

给定需求(你原话的变体):

“怪物走到基地就扣我血;玩家建塔要花钱;用第三人称视角看整个战场。”

别急着写代码。我们按”说人话 → 拆效果 → 查图鉴 → 定参数 → 定消息”五步走。

第一步:说人话(把需求拆成可执行的子项)

  • 子项 A:怪物从出生点移动到基地。
  • 子项 B:怪物碰到基地,基地扣血(玩家失血)。
  • 子项 C:玩家点地面建塔,花掉 100 金币。
  • 子项 D:摄像机在玩家/战场后方,第三人称俯视。

第二步:拆效果 → 查图鉴(连回前面章节)

  • 子项 A(怪移动):怪沿路走 → 第 18.12 节 NavMeshAgent(烘焙可走区域,Agent 自动绕障到基地)。走路动作 → 第 18.9 节 Animator(播 Walk 片段)。怪的模样 → 第 4 章网格 + 第 5 章材质(MeshFilter + MeshRenderer)。
  • 子项 B(扣血):基地要能被”碰到” → 第 18.13 节 Collider(基地挂一个 Is Trigger 的触发区)。怪进入触发区 → 脚本 OnTriggerEnter 里调”基地血量 -1”。基地血条 → 第 18.11 节 Canvas + Slider。扣血音效 → 第 18.8 节 AudioSource。
  • 子项 C(花钱建塔):点地面 → 第 18.11 节 UI 按钮/点击射线检测(Raycast 打到第 2 章坐标的地面点)。花钱 → 自定义脚本里 gold -= 100。生成塔 → 第 17 章组件组合:Instantiate 一个”塔预制体”(塔 = Transform + Mesh + BoxCollider + TowerAttack 脚本 + AudioSource + ParticleSystem,复习 18.15)。
  • 子项 D(第三人称视角):第 7 章 Camera,摆在战场斜上方,Projection 用透视,FOV 调成能看到全场(比如 50-60 度),Near/Far Clip 设好别裁掉远处。

第三步:定参数(举几个典型旋钮)

  • NavMeshAgent:Speed=3(怪走得快慢)、Stopping Distance=1(到基地多远算”到了”)。
  • 基地 Trigger Collider:Is Trigger=true(不挡路,只感应)。
  • 基地 Slider:maxValue=100,value 跟随 baseHP 变量。
  • Camera:Field of View=55、Far Clip Plane=200(雾在第 8 章设好,远处自然淡出)。
  • 建塔花费:脚本里 public int towerCost = 100;。

第四步:定网络消息(联机时怎么传)

走到阶段三,这些”本地事件”要变成”服务器权威下的消息”。按第 19.5 节的自定协议:

  • 怪移动:{"type":"enemy_move","enemyId":7,"x":3.0,"y":0,"z":5.0}(服务器算位置,广播给所有客户端画)。
  • 基地扣血:{"type":"base_damage","baseId":1,"hp":99}(服务器在怪进触发区时算,广播新血量,客户端更新 Slider)。
  • 建塔:{"type":"build_tower","playerId":42,"x":5,"z":2,"goldLeft":400}(客户端发”我想建”,服务器校验金币≥100、位置空,扣钱后广播”建塔成功”,所有客户端 Instantiate 塔预制体)。
  • 视角:纯本地,不用同步(每个玩家自己看自己的摄像机,第 18.7 节联机注意里提过)。

你看到了吗——同一个需求,被拆成了”表现层组件(Camera/Light/Particle/UI)+ 逻辑层脚本(扣血/花钱)+ 网络层消息(联机时广播)“三层。这正是第 19.7 节那张”指挥 vs 画师”图的落地:网络消息是”指挥”(决定世界状态),组件是”画师”(把状态呈现)。而你作为 Go 程序员,写那几个 {"type":...} 的 handler,和写普通 Go API 没两样。

反问收尾:如果”基地扣血”这个逻辑你图省事写在客户端(怪一碰基地,客户端自己 -1 血),联机时会出什么问题?提示:回到第 19.1 节那三个麻烦(延迟/丢包/一致性)和第 19.8 节坑点 3(客户端算血量会被改内存作弊)。想通这个,你就真正把”服务器权威”从概念变成了肌肉记忆。


到这,十九章的概念你都串起来了:从坐标、网格、光照、雾、声音,到组件组合、内置图鉴,再到联机和你的 Go 后端。这张”项目蓝图”不是终点,而是你动手时的地图。打开 Unity,从阶段一那个”方块怪沿路走”开始——你前面学的每一章,都会在那一刻派上用场。

【思考题】

  1. 回合制棋盘类(大富翁)用 HTTP 轮询同步,和用 WebSocket 实时同步,哪种更合适?为什么?反过来,塔防若图省事用 HTTP 轮询,会出现什么具体问题?
  2. 本章路线图为什么强调”先做单机、再加登录、最后联机”,而不是一上来就做联机?如果从联机开始,遇到”怪没同步”的 bug 时你会面临什么困境?
  3. 在 20.6 的拆解练习里,“基地扣血”如果写在客户端(怪碰基地,客户端自己 -1),联机时会出哪两个问题?正确的做法应该让谁来计算这次扣血?
  4. 你作为 Go 程序员,做这个联机策略游戏时,哪一层(账号数据 / 实时同步 / 游戏逻辑表现)对你最熟、哪一层最该花时间学?结合前面章节说说你的判断。

【参考答案】

  1. 大富翁用 HTTP 轮询更合适:它是回合制,一回合才动一次,对实时零要求,客户端每隔一秒问”轮到谁了、棋盘啥样”完全够,玩家无感;上 WebSocket 反而是杀鸡用牛刀,还多写一套长连接维护。塔防若用 HTTP 轮询:怪每秒都在动,轮询间隔(哪怕 100ms)也会让其他玩家看到”怪一跳一跳”地瞬移,且 HTTP 一问一答、服务器无法主动推”怪移到这了”,状态严重滞后、手感崩溃。所以塔防必须 WebSocket/状态同步,大富翁用 HTTP 就够——架构要匹配玩法。
  2. 因为复杂度要分层叠加。单机阶段你只需搞懂移动、碰撞、UI、组件这些”表现层 + 游戏逻辑”,地基打牢;登录阶段主要写 Go,Unity 只加界面和请求;联机阶段才专注”状态怎么在玩家间传”。若一上来联机,当”怪没同步”出现时,你分不清是移动脚本写错(逻辑层)还是网络消息没发对(网络层),两层问题搅在一起,越改越乱。先单机再联机,是把”已定位的单一问题”逐个击破。
  3. 两个问题:一致性问题——A 客户端自己 -1,B 客户端没收到,两人看到不同血量,违反第 19.1 节”多份状态要一致”;作弊问题——玩家改本地内存让基地”永远不扣”,第 19.8 节坑点 3 说的”客户端算血量被改内存作弊”就发生了。正确做法:让**服务器(你的 Go)**在收到”怪进入基地触发区”时统一计算扣血、广播新血量,客户端只负责把服务器给的数值画到 Slider 上。
  4. 最熟的是账号数据层(HTTP handler、JWT、数据库 CRUD,第 19 章几乎零新增成本)和实时同步的 Go 端(WebSocket 服务、goroutine 广播,你并发直觉直接用)。最该花时间学的是游戏逻辑与表现层(Unity 组件组合、C# 脚本、Animator/NavMeshAgent/ParticleSystem、UI Canvas,第 4-18 章)——这是纯游戏领域、你从零起步的部分。判断:把 Go 后端当”已会”的杠杆,把学习和练习时间集中砸在 Unity 客户端的表现与逻辑上,再用联机把两者接起来,就是最短路径。