尧图精选

UE4架构核心:GameInstance与GameMode生命周期、网络复制与实战避坑指南

🕒 发布时间:2026/10/2 13:24:38 📁 来源:尧图网络
1. 开局先搞清楚这两个类到底管什么先讲个我自己的经历。有一次接手一个联机合作项目玩家进游戏大厅之后要选角色、选难度、带道具然后开始一局比赛打完回大厅继续下一局。当时项目里有个老哥把玩家选的角色和道具直接存在关卡里的某个Actor身上结果每次换关卡数据全没了联机时服务器和客户端的数据还对不上一堆玩家反馈“我明明选了A角色进游戏变成B了”。后来排查了半天问题就出在数据放错了地方。这就是GameInstance和GameMode最核心的职责区分问题。简单说GameInstance管的是“整个游戏进程”的事GameMode管的是“当前这一局”的事。数据放错地方轻则逻辑混乱重则联机直接翻车。这个话题展开讲能聊的东西其实非常多。GameInstance和GameMode是UE4里两个最常用但又最容易被低估的类。很多人学了蓝图之后就往Character里塞各种逻辑结果项目越做越乱。实际上搞懂这两个类的定位、生命周期、网络行为以及它们之间的配合方式你的项目架构会清晰一大截。这篇文章我把自己踩过的坑和总结的经验全写出来涉及生命周期、网络复制、全局数据管理、规则控制还有外接设备映射和查询/物理模拟器的边界问题尽量把每个点都说透。1.1 先理解两者的定位差异很多人上来就背概念GameInstance是全局的GameMode是每局的。听起来很简单但一写代码就分不清了。我用一句话总结GameInstance是你游戏程序的“进程级”对象GameMode是你“当前关卡”的“规则集”。什么叫“进程级”就是你的游戏从启动到退出整个进程存在期间它都在。不管你怎么切换关卡、加载地图、进菜单、回大厅GameInstance始终活着它持有的数据不会因为关卡切换而丢失。什么叫“规则集”GameMode定义了当前地图怎么玩玩家怎么出生、有多少人、能不能暂停、用什么PlayerController、用什么Pawn、胜利条件是什么。关卡一切换GameMode就销毁了换成新关卡的GameMode。如果你把“玩家选了哪个角色”放在GameMode里玩家从大厅进战斗地图GameMode一换数据就没了。如果你把“全局音量设置”放在GameMode里每次进新关卡音量就重置了玩家肯定骂娘。理解了这层定位差异你再去思考数据放哪正确率至少提升80%。1.2 GameInstance的定位整个游戏的“持久内存”GameInstance活在引擎启动到引擎关闭这段时间里跨关卡、跨地图、跨一切加载流程。它是UE4里少有的“全局单例”性质的类所有玩家、所有关卡、所有UI都能访问它。所以它天然适合做这几类事情全局配置数据画面设置、音量、语言、按键绑定跨关卡传递的玩家数据玩家ID、角色选择、道具栏、金币数全局服务和管理器网络会话管理器、存档管理器、音频管理器、外部设备映射管理器大厅与关卡之间的中间数据缓存比如匹配成功后把匹配结果存到GameInstance再切地图但GameInstance有一个很关键的坑它不参与网络复制。服务器上的GameInstance和客户端上的GameInstance是两个完全独立的对象彼此之间不会自动同步任何数据。这一点太多人踩坑了。有人把玩家血量上限存在GameInstance里想着全局统一好管理结果联机时服务器和客户端各存各的数据完全对不上。正确做法是GameInstance里存“局外数据”局内实时数据走Actor复制或GameMode的服务器权威逻辑。1.3 GameMode的定位单局比赛的“裁判规则”GameMode只在服务器上存在客户端没有GameMode。它是纯服务器权威类负责制定规则、裁决胜负、管理系统流程。GameMode在关卡加载时创建关卡卸载时销毁生命周期绑定在单个关卡上。它适合做这些事情设置默认Pawn、PlayerController、PlayerState、HUD控制玩家出生点、复活逻辑、初始道具管理比赛状态准备中、进行中、结束判定胜负条件、计分规则生成AI、刷怪、控制比赛节奏GameMode没有复制函数它不需要复制因为它的逻辑只跑在服务器上。客户端那边通过PlayerController、PlayerState、GameState来间接感知游戏规则。这里有一个很容易搞混的概念GameMode和GameState的区别。GameMode是服务器上的规则制定者GameState是服务器上状态的中继广播器。GameMode定了“这局谁赢了”GameState负责把这个结果同步给所有客户端。所以GameState可以复制GameMode不行。2. 实战核心数据放哪、规则放哪两者怎么配合现在进入真正的实战环节。光知道定位还不够你得知道具体怎么用才能把项目做得清爽、稳定、不翻车。这一章我把GameInstance和GameMode最常见的配合场景拆开讲每一步都配上思路和原因。2.1 GameInstance全局数据管理的实用套路先讲GameInstance里最常用的数据管理方式。我这里不扯那些复杂的框架就说一个最实在的落地做法把GameInstance当作你整个游戏的“客户端数据总线”。所有需要在关卡之间传递、保留的数据统一通过GameInstance读写。比如一个典型的游戏流程玩家打开游戏进入主菜单选择“开始游戏”进入角色选择界面选好角色后进入战斗关卡打完战斗回到主菜单或者进入结算界面。这个过程里玩家选的角色、难度设置、携带的道具、当前金币数全部都应该存在GameInstance里。我一般的做法是在GameInstance里定义一组变量用蓝图和C都行然后暴露Get/Set接口或者直接用引用。注意这里有一个关键点GameInstance本身在蓝图中很容易访问直接Get GameInstance节点就能拿到然后Cast成你的自定义GameInstance子类。但蓝图里频繁访问GameInstanceCast一次就要浪费一点点性能而且代码很啰嗦。我更推荐的做法是做一个静态函数或者蓝图函数库内部封装好对GameInstance的访问这样外部调用就一行代码干净利落。C写法大致是UCLASS() class UMyGameInstance : public UGameInstance { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, Category SaveData) void SetSelectedCharacter(int32 NewCharIndex); UFUNCTION(BlueprintPure, Category SaveData) int32 GetSelectedCharacter() const; private: UPROPERTY() int32 SelectedCharacterIndex -1; };然后外部调用UMyGameInstance* MyGI CastUMyGameInstance(UGameplayStatics::GetGameInstance(this)); if (MyGI) { MyGI-SetSelectedCharacter(2); }用蓝图的话套路也差不多但我会额外封装一个“全局管理器接口”把那些频繁访问的逻辑统一收敛避免蓝图节点连线连成一锅粥。再强调一次GameInstance里不要存局内实时数据。比如玩家当前的HP、子弹数量、buff剩余时间这些数据属于局内状态应该由Actor或GameMode管理通过复制同步到客户端。GameInstance只做“跨局数据”和“全局配置”的存储。2.2 GameMode规则控制的实用套路GameMode的核心价值是把游戏的“玩法规则”集中放在一处管理。一个清晰的项目GameMode里的逻辑应该像一本规则说明书而不是一锅逻辑大杂烩。实战里我习惯把GameMode拆成几个重点区域玩家连接和出生管理回合/比赛状态机胜负判断与结算出生点与复活逻辑拿一个最基础的“死亡竞赛”模式举例GameMode里要处理的东西大致有玩家加入时调用RestartPlayer选择出生点并生成Pawn玩家死亡后延迟几秒再调用RestartPlayer击杀数达到目标后结束比赛并宣布赢家这些逻辑全部集中在GameMode里客户端那边通过监听GameState的变化来响应界面更新。具体到蓝图/代码GameMode最常用的重写接口包括InitGame游戏开始时的初始配置常用于读取地图配置、生成规则参数PostLogin玩家登录后的处理新手最容易漏掉这个RestartPlayer重写这里可以自定义玩家的出生逻辑Logout玩家退出时清理数据HandleStartingNewPlayer新玩家加入时初始化这里面PostLogin和Logout是特别容易被忽略的。如果你需要在玩家加入时给他一个初始道具、绑定一个初始UI或者玩家退出时保存数据这两个接口是你的首选。代码示意void AMyGameMode::PostLogin(APlayerController* NewPlayer) { Super::PostLogin(NewPlayer); // 玩家登录后给玩家设置初始数据 // 例如NewPlayer-ServerSetSelectedCharacter(GI-GetSelectedCharacter()); } void AMyGameMode::Logout(AController* Exiting) { Super::Logout(Exiting); // 玩家退出时保存玩家数据或广播退出事件 }这里面有个细节值得多说一句PostLogin只在服务器上执行所以你在里面可以放心地操作服务器权威数据比如给玩家生成专属的PlayerState并初始化数据。客户端那边无法直接调GameMode但可以通过PlayerController的RPC来请求服务器执行某些操作。2.3 两者配合的经典模式大厅进战斗的数据交接现在我们做一个最常见的实战场景大厅选择角色进入战斗地图战斗地图的GameMode根据玩家的选择来生成Pawn。第一步玩家在大厅里选择角色这个选择存到GameInstance。第二步服务器进入战斗地图加载对应的GameMode。第三步GameMode在PostLogin或RestartPlayer时去GameInstance里读取玩家选择然后生成对应角色。这个链路里有两个关键点第一个关键点服务器和客户端对GameInstance的读取目标不同。服务器上的GameInstance是服务器版本的客户端上是客户端版本的。如果是单机游戏两者是同一个程序没问题。但如果是联机游戏客户端的GameInstance数据不会自动同步到服务器。所以如果是联机游戏玩家在大厅选择了什么角色客户端要把这个信息通过RPC发送到服务器服务器写入自己的某个变量然后再由服务器的GameMode来读取和生成。我见过不少项目在这里翻车客户端自以为“我已经把角色选择存到GameInstance了”联机时服务器根本不知道生成出来全是默认角色。正确做法是客户端把选择结果通过RPC传给服务器服务器把它存在PlayerState或服务器端的GameInstance变量里等GameMode需要时再读取。第二个关键点数据读取的时机。GameMode的PostLogin触发时玩家已经在服务器上登录了但Pawn可能还没生成。如果你需要根据玩家选择来生成Pawn应该在RestartPlayer里处理或者在PostLogin里先保存数据等生成Pawn时使用。这里的套路我总结成一句话GameInstance负责“带数据过关卡”GameMode负责“按数据造Pawn”。中间的桥梁是服务器端的会话数据或PlayerState。理解了这句话你就能应对绝大多数类似场景。3. 生命周期与网络复制最容易翻车的地方生命周期和网络复制是GameInstance与GameMode最大的深水区。很多开发者单机玩得溜一上联机就各种诡异Bug十有八九是这两块没吃透。这一章我把生命周期、网络模型以及它们对外接设备映射和物理模拟器查询的影响一起讲清楚。3.1 GameInstance的生命周期从启动到退出GameInstance的创建时机大约是引擎初始化之后、第一个关卡加载之前。它销毁的时机是游戏进程关闭、引擎开始销毁所有对象时。在这个生命周期里有几个需要特别注意的节点游戏刚启动时GameInstance最先创建然后是默认地图加载每次切换地图GameInstance会收到一个通知比如NotifyPreClientTravel、NotifyPostLoadMap等游戏退出时GameInstance最后销毁所有数据不可再访问这意味着如果你有一些数据需要在“切换到新关卡时做一次重新初始化”你得监听地图加载事件而不能依赖GameInstance的重建。我在项目中就经常遇到这种情况玩家从大厅进入战斗关卡战斗关卡的GameMode里需要读取大厅里选择的地图参数。这个参数存在GameInstance里没问题但GameMode创建的时候GameInstance是否已经准备好数据大多数情况下是准备好的因为GameInstance早就活着了数据也早就写进去了。但要注意一个反向的场景玩家直接启动战斗关卡比如从编辑器直接Play、或者通过命令行启动这时候GameInstance虽然活着但它里面没有任何大厅阶段的数据如果你直接读取未初始化变量就会出现空数据或默认值。我的建议是在GameInstance里给所有“关卡切换需要的数据”设置一个默认值并且在读取时做校验发现是非法值就回退默认。别假设数据一定存在。3.2 GameMode的生命周期从加载到卸载GameMode的生命周期绑定在一个关卡上。具体来说关卡开始加载时UWorld根据地图配置创建新的GameMode实例关卡卸载时GameMode销毁。这里有一个关键点UWorld和GameMode的关系。一个UWorld对应一个关卡GameMode是UWorld的下级对象。当你用OpenLevel切换地图时旧UWorld销毁、新UWorld创建、GameMode也随之重建。GameMode有几个生命周期事件值得关注InitGame关卡初始化时调用适合读地图配置StartPlay游戏开始逻辑所有Actor已经生成完毕PostLogin玩家登录后调用BeginPlayGameMode本身也是ActorBeginPlay也会触发这里踩坑最多的是InitGame和BeginPlay的执行顺序。InitGame在所有Actor生成之前调用BeginPlay在Actor生成之后调用。如果你在InitGame里访问场景中的Actor就会扑空。我一般在GameMode里这样安排InitGame里只做配置读取BeginPlay里做规则初始化RestartPlayer里做具体的玩家创建PostLogin里做玩家数据绑定。这样职责清晰也不会因为顺序问题踩坑。3.3 网络模式下谁在谁不在网络模式下GameInstance和GameMode的存在性有巨大差异很多人一联机就懵了因为这个差异。GameInstance在服务器和每个客户端上都有一个实例它们彼此独立不通信、不复制。客户端A的GameInstance和服务器上的GameInstance完全是两个东西改其中任何一个都不会影响另一个。GameMode只在服务器上存在客户端上没有GameMode实例。客户端试图调用GetGameMode时拿到的是空引用。这一点在蓝图中尤其容易被忽略很多人直接拖一个Get GameMode节点结果客户端上一直返回null还以为是Bug。那么客户端靠什么获取规则状态答案是GameState。GameState在服务器上创建并且会自动复制到所有客户端客户端可以安全地读取GameState数据。所以你的架构思路应该是服务器上的规则修改走GameMode状态广播和客户端读取走GameState跨关卡持久数据走GameInstance玩家个体数据走PlayerState这套模型理解了网络相关的很多坑都能提前避开。4. 热词实战外接设备映射与查询/物理模拟器的边界这一章专门聊两个比较新而且容易混淆的话题。一个是外接设备映射另一个是查询与物理模拟器的区别。它们表面上看起来和GameInstance、GameMode关系不大但实际上不管实体外设还是角色控制最终都要回到这两个类来管理生命周期和数据存储。4.1 外接设备映射放在哪一层才合理所谓外接设备映射简单说就是把外部设备的输入事件映射成游戏内的逻辑动作。比如方向盘、踏板、飞行摇杆、跳舞毯、甚至自定义的串口按钮板都需要通过映射层转成游戏可识别的输入指令。为什么要单独聊这个因为我见过太多项目把设备映射逻辑塞进Character或PlayerController里导致换关卡时映射失灵、联机时设备数据只在本地生效没人处理同步。正确思路是把外接设备的“配置数据”和设备本身的“运行时数据”分开。外接设备的配置数据比如“方向盘转角映射到多大转向幅度”“自定义按键对应的游戏功能”这种与关卡无关的全局配置适合放在GameInstance里。因为设备配置应该在整个游戏进程中保持一致不管你在菜单还是战斗中方向盘的死区设置不应该因换图而重置。设备映射的运行时状态比如当前设备连接的端口、实时输入值、校准状态这些数据如果是局内使用的放在PlayerController或GameMode管理的管理器里更合理。特别是联机游戏每个客户端的物理外设数据不应该直接写入服务器权威逻辑而应该通过客户端输入上报给服务器。举个例子一个支持方向盘外设的赛车游戏。方向盘的转角数据每帧在客户端被读取这个数据属于本地输入直接作用于本地Pawn的控制逻辑就行但如果游戏需要服务器做防作弊校验或回放记录客户端就需要定期上报转角数据到服务器由服务器信任或校验后再广播给其他客户端。这个过程中设备映射的配置从GameInstance读取设备实时数据在客户端本地进行处理需要同步的数据再走PlayerController的RPC上报。三个层面各司其职系统才能稳定可靠。这样的分层还有一个好处切换关卡时GameInstance里的设备配置不丢玩家不需要重新校准设备。而局内设备运行时状态由GameMode或PlayerController重新初始化避免旧关卡的脏数据带入新关卡。4.2 查询与物理模拟器的区别在实战中怎么体现另一个容易踩坑的概念是“查询”和“物理模拟器”的区别。听着好像都是引擎内部的东西但在做实际功能时选错了会直接影响性能表现和联机同步。查询Query在UE4里通常指的是物理查询比如射线检测、碰撞通道查询、Overlap检测等。它的特点是立即执行、返回结果、不改变世界状态。查询只回答“这里有没有东西、碰到了什么”它不会推动任何物理对象。物理模拟器Simulation则是指真正的物理模拟比如刚体运动、碰撞响应、力的作用。它是一帧一帧演算出来的结果受质量、速度、约束条件等参数影响结果带有连续时间特性。为什么在讲GameInstance和GameMode时要提这个区别因为查询和物理模拟在游戏架构中的归属和使用方式完全不同。查询适合用于“服务器权威的规则判定”。比如GameMode要判断玩家的攻击有没有打中敌人服务器可以发出一次射线检测物理查询看射线是否命中目标。这个操作成本低、结果明确、便于服务器裁决。物理模拟适合用于“表现和交互”。比如角色被击飞、物体被炸飞这种连续物理效果一般交给物理引擎去模拟。服务器可以同步物理模拟的关键事件但不可能每帧把所有物理体状态全部同步到客户端否则带宽直接爆炸。实战中经常出现的问题是什么有人把“查询”当“模拟”用比如在客户端上直接模拟了物理击飞效果然后试图把这个模拟结果同步给服务器。结果就是客户端表现和服务器判定完全不一致玩家看到自己击飞了敌人服务器判定根本没打中。反过来也有人把“模拟”当“查询”用比如频繁地生成临时物理体去验证碰撞路径这会导致性能开销爆炸。正确的做法是短时间的判定用查询长时间的物理表现用模拟两者分开管理。在GameInstance和GameMode这套架构里查询逻辑一般放在GameMode或者服务器端的PlayerController里因为查询结果是服务器裁决依据需要服务器权威物理模拟表现一般放在Pawn或Actor的组件里由物理引擎驱动通过RPC和复制把重要事件同步出去。GameInstance负责提供全局配置比如设备的输入映射表、物理灵敏度设置但这些配置也要落到具体的Pawn或GameMode逻辑中去执行。4.3 实战示例外接设备映射 查询判定的联动我们以一个带外接踏板和方向盘的赛车游戏为例走一遍完整链路。第一步设备映射配置存在GameInstance里。玩家在设置界面校准方向盘转角、踏板行程、按键映射这些数据通过SaveGame持久化到本地运行时由GameInstance统一管理。第二步客户端本地读取设备输入。打到车辆控制层把方向盘的实时角度、踏板行程读出来作为本车辆的输入信号。第三步本地输入映射到Pawn的移动逻辑。Pawn根据输入驱动车辆模型运动这个过程可以使用物理模拟器来实现车辆的动态运行给玩家真实的手感和反馈。但请注意车辆模拟在客户端本地运行时的结果不能直接作为服务器的判定数据。第四步查询判定放在服务器或本地可信层完成。如果是一个竞速游戏游戏需要判断“车辆是否压过检查点”这个可以用物理查询或区域判定来实现结果由服务器确认并广播给所有客户端。如果服务器确认检查点通过GameMode更新比分。第五步GameMode作为中央裁决记录每一辆车的圈数和检查点状态。比赛结束时GameMode统一判定胜负、结算数据并通过GameState广播到所有客户端。这个例子里GameInstance管设备配置和持久数据Pawn管物理模拟和手感反馈GameMode管理查询判定和比赛规则。每一层只做自己该做的事互相之间通过清晰的接口衔接。如果有人把方向盘映射放在GameMode里那么客户端切个关卡配置就全丢了。如果有人把车辆物理模拟结果直接当成服务器判定依据那么不同设备、不同帧率、不同网络延迟下的结果会有很大差异比赛公平性无法保证。这些坑我在实际项目中都见过区分开之后项目稳定了很多。5. 避坑指南常见问题与排查技巧实录聊完架构和实战这一章专门整理一份“翻车现场实录”。每一类问题都是我或身边朋友在真实项目中踩过的我把现象、原因、解决方案全部分享给你方便你直接对照排查。5.1 数据存错地方关卡切换后丢失这是最常见的一个问题。现象是玩家在大厅选了一堆东西进入战斗关卡后发现所有初始化数据消失。原因是数据存到了GameMode或其他关卡级对象里切换地图时对象被销毁了。排查思路先确认数据读写对象是不是GameInstance在GameInstance里加日志查看关卡切换时数据是否被意外重置检查是不是有代码在关卡加载时对GameInstance做了清空操作解决方案就是规范数据存放位置跨关卡数据一律走GameInstance。5.2 客户端访问GameMode返回空联机项目里客户端上调用GetGameMode得到空引用然后就崩溃或逻辑异常。原因是GameMode只在服务器上存在这是引擎设计使然不是Bug。排查思路检查调用者是否在客户端确认客户端是否真的通过RPC请求服务器转发用IsLocalController或HasAuthority来判断执行环境解决方案是客户端需要规则数据时通过GameState读取或通过PlayerController向服务器发RPC请求而不是直接访问GameMode。5.3 GameInstance不参与复制联机数据不同步联机时服务器改了GameInstance数据客户端完全无感知或者各自存各自的值。原因是GameInstance不复制、不同步每端独立。排查思路检查数据是否真的需要同步如果必须同步考虑改用PlayerState、GameState或复制Actor如果只是本地UI的全局设置不需要同步那继续用GameInstance没问题解决方案是分清“全局本地数据”和“全局同步数据”跨玩家一致的数据用GameState/PlayerState纯本地配置用GameInstance。5.4 外接设备在换关卡后映射失效玩家在菜单界面校准好方向盘进入比赛后方向盘输出变得异常。原因是设备映射配置存在了关卡内对象里关卡切换时被销毁。排查思路检查设备配置数据的存放位置确认校准结果的持久化时机看Pawn或PlayerController里是否有独立的映射加载逻辑解决方案是把设备配置持久化到GameInstance或SaveGame把运行时映射加载逻辑放在Pawn初始化或PlayerController初始化时读取这样每次进入关卡都会重新应用同一套映射配置。5.5 游戏流程顺序导致的数据覆盖这里分享一个我在项目中遇到过的真实问题。玩家在大厅选择一个角色后通过“开始游戏”按钮进入战斗关卡。战斗关卡的GameMode在PostLogin时从GameInstance读取角色数据但我当时在GameInstance里做了一个初始化操作会在关卡加载时把角色数据重置为默认值结果就是GameMode每次读到的都是默认值。看起来又是一个“数据没传过去”的问题但仔细排查后发现数据其实传过去了只是GameInstance在加载新地图时还做了数据覆盖操作。排查方法很简单在GameInstance的各个生命周期函数里打印日志看数据被清空的时间点即可。这个问题让我养成了一个习惯在GameInstance里专门写一个DebugPrintAllData函数方便随时查看全局数据的状态。尤其在联机排查时这个函数能帮你快速判断是数据没写进去、被覆盖了还是根本没从客户端发到服务器。5.6 速查表GameInstance与GameMode对比为了方便你日常开发时快速决策我把两者的核心差异整理成一张表维度GameInstanceGameMode生命周期整个游戏进程单个关卡是否跨关卡是否服务器/客户端每端独立存在仅服务器存在数据复制不复制不复制逻辑不广播适合存储全局配置、跨关卡数据当前比赛规则、流程状态典型用法存档管理、设备映射配置、全局管理器出生管理、胜负判定、关卡规则客户端访问每端都有可直接访问客户端不可访问需走GameState/RPC典型错误存局内实时数据存跨关卡全局数据这张表你直接抄走用就行。每次不确定数据该放哪时对着这个表看一眼基本不会错。5.7 联机项目架构清单最后分享一个我在联机项目里使用的架构检查清单供你搭建项目时参考跨关卡不做同步的数据放GameInstance跨关卡需要同步的数据放PlayerState或SaveGame单局规则放GameMode单局状态广播放GameState玩家个体局内数据放PlayerState局内实时表现数据放Actor组件并通过复制同步外接设备配置放GameInstance或SaveGame外接设备实时输入在客户端处理需要同步时走RPC物理模拟效果放Pawn组件关键事件通过RPC/复制广播物理查询判定放在服务器权威层保证公平性和一致性这套清单帮我避掉了大量联机同步问题尤其是外接设备的跨关卡映射配置、查询和物理模拟的职责分离这两块按这个方式划分后项目结构清晰很多排查问题时也快得多。我在实际项目里见过太多人把GameInstance当存储箱、把GameMode当万能工具类最后项目越做越乱。其实引擎把这些类设计出来边界是很清晰的你只要尊重它的边界它就能把你照顾得很好。反过来说你随意越界使用就会得到一堆莫名其妙的问题。GameInstance和GameMode的配合没什么高深玄机想清楚“数据归谁管、规则归谁定、状态归谁传”这三件事你的游戏架构就已经赢了一大半。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →