GPU优化(1)

目录

[用Stats 面板来判断"卡顿"发生在哪一层、由什么因素引起,再针对性优化?](#用Stats 面板来判断“卡顿”发生在哪一层、由什么因素引起,再针对性优化?)

[如何用绘制调用(DrawCall)批处理和帧调试器来降低 CPU 渲染开销、提升帧率?](#如何用绘制调用(DrawCall)批处理和帧调试器来降低 CPU 渲染开销、提升帧率?)

为什么要做"绘制调用批处理"

四种绘制调用批处理技术

[SRP 批处理器(SRP Batcher)](#SRP 批处理器(SRP Batcher))

[什么是SRP 批处理器(SRP Batcher)?](#什么是SRP 批处理器(SRP Batcher)?)

它是什么做到的呢?

[SRP Batcher 的本质?](#SRP Batcher 的本质?)

那为什么不把传统"扔掉"?

[GPU 实例化(GPU Instancing)](#GPU 实例化(GPU Instancing))

什么是?

["勾选 Enable Instancing"到底干了啥?](#"勾选 Enable Instancing"到底干了啥?)

[原理:那 1000 棵树怎么"各长各样"还一次画完?](#原理:那 1000 棵树怎么"各长各样"还一次画完?)

静态批处理,动态批处理

[静态批处理(Static Batching)](#静态批处理(Static Batching))

[动态批处理(Dynamic Batching)](#动态批处理(Dynamic Batching))

优化填充率,降低过度绘制(Overdraw)

剔除(Culling)

[剔除类型一:视锥剔除(Frustum Culling)](#剔除类型一:视锥剔除(Frustum Culling))

[剔除类型二:遮挡剔除(Occlusion Culling)](#剔除类型二:遮挡剔除(Occlusion Culling))


用Stats 面板来判断"卡顿"发生在哪一层、由什么因素引起,再针对性优化?

数据项 含义 优化用途
FPS 每秒帧率 总体检看流畅度。低于目标帧率(如 60/30)再往下细分原因:是主线程慢、渲染线程慢,还是卡在 GC / 物理 / 场景加载。
CPU Main 单帧主线程耗时(含编辑器窗口更新) 判断是否是 CPU 瓶颈。偏高说明脚本逻辑、物理、动画、GC 压力太大;可优化:减少每帧计算、用 Job System / Burst 把计算搬到后台线程、对象池减少 GC。
CPU Render 渲染线程耗时 判断是否是 GPU/渲染瓶颈。偏高说明渲染复杂度过高,需降低 DrawCall、减少 Overdraw、降低分辨率 / 使用 Mipmap、压缩纹理。
Batches 合并后的 DrawCall 数量 核心优化指标。DrawCall 多 → CPU 开销大。可优化:合并网格(静态批处理)、材质合批、Atlas 图集、Instancing、降低粒子/UI 的 DrawCall。
Tris / Verts 渲染的三角形和顶点数 评估 几何负载。过高说明模型/场景太复杂,可降低面数、LOD、简化骨骼动画,或检查是否有超大网格。
SetPass calls 切换着色器 Pass 的次数 比 Batch 更细的 材质/Shader 切换开销 。每一次切换有额外 CPU 成本。优化方向:减少材质种类(按 Pass 分组合批)、共享材质、减少多光源/多 Pass 效果。

常见思路:

  1. 先看 FPS:确定有性能问题。
  2. 再看 CPU Main vs CPU Render
    • CPU 高 → 逻辑层(脚本/Job System/GC/物理);
    • Render 高 → 渲染层(DrawCall/Overdraw/分辨率)。
  3. 然后看 Batches / SetPass calls / Tris:定位具体渲染瓶颈点。

如何用绘制调用(DrawCall)批处理和帧调试器来降低 CPU 渲染开销、提升帧率?

为什么要做"绘制调用批处理"

渲染一个游戏对象时,Unity 要朝图形 API(OpenGL / Vulkan / Direct3D 等)发绘制调用(Draw Call)。每发一次,CPU 都要做不少准备工作:准备顶点数据、绑定纹理、绑定着色器、设置渲染状态......所以:

  • 绘制调用本身有成本
  • 两次绘制调用之间如果切换了材质/着色器,还会多一次"SetPass"(渲染状态切换) ,而 SetPass 往往是 CPU 开销里最贵的操作

结论:

数量越少、状态切换越少 → CPU 渲染耗时越低

  • PC / 主机:硬件强,能扛很多 Draw Call,但仍值得优化
  • 移动端:Draw Call 优化几乎是刚需

官方判断标准(经验值):PC 端 Draw Call 建议 <1000,移动端 <100,VR <50。

四种绘制调用批处理技术

Unity 会用多种技术把"很多对象"合并成"很少的批(Batch)"。

SRP 批处理器(SRP Batcher)

如果项目使用 HDRP 或 URP 管线,在管线资源文件的Advanced(高级)选项内开启 SRP Batcher。

使用兼容 Shader 时,SRP 批处理器可以减少绘制调用之间的 GPU 状态准备工作,让材质数据常驻 GPU 显存,显著缩短 CPU 渲染耗时。 尽量减少着色器变体数量、精简关键字,进一步提升 SRP 批处理效果。

什么是 SRP 批处理器(SRP Batcher)**

SRP Batcher 是 URP/HDRP 里的一个渲染优化:它让材质数据长期留在 GPU 显存里,同一 Shader 的物体连续绘制时不用反复上传数据,从而把 Draw Call 之间的 CPU 切换开销降到几乎为零。它省的是 CPU,不是 Draw Call 数量。

注意:

它只优化 CPU → GPU 的提交开销

如果瓶颈在 GPU(像素填充率、Overdraw、三角形数),SRP Batcher 几乎帮不上忙。

关键词:材质常驻显存 + 不重新上传 + 省 CPU

它是什么做到的呢?

传统 Built-in**(每材质都要完整走一遍)**:

物体A: 收集 → 打包 → 上传GPU → 绑定 → Draw

物体B: 收集 → 打包 → 上传GPU → 绑定 → Draw ← 全!部!重!做!

SRP Batcher 把流程拆成两个阶段------初始化只做一次,每帧渲染极简:

阶段 初始化(启动时,只执行一次)

把材质A、B、C... 的所有属性

→ 各自打包成一个 CBuffer

→ 一次性上传到 GPU 显存,长期驻留

→ GPU 显存里变成: 材质A块材质B块材质C块...

从此材质数据住在显存里不动了

阶段 每帧渲染(A、B、C 共用同一套极简流程)

物体A: 更新「每对象矩阵」 → 指→材质A块 → Draw
物体B: 更新「每对象矩阵」 → 指→材质B块 → Draw ← 只改了个指针!
物体C: 更新「每对象矩阵」 → 指→材质C块 → Draw

那一步 指→材质块 是什么

每个材质在显存里有自己固定的 CBuffer 块。渲染物体时,CPU 只需要告诉 GPU:

"这次用第 X 号材质块 ,矩阵用这个对象的矩阵" → 然后 Draw

几个限定一个都不能少:

  • 只优化 CPU 提交,不优化 GPU 着色/像素
  • 不减少 Draw Call 数量,只减少 Draw Call 之间的 SetPass
  • 合批边界 = 同一 Shader 变体(关键字必须一致)
  • 材质参数不同没关系(数据各住各的显存块,切换只改指针)
SRP Batcher 的本质?

GPU 的常量缓冲区(CBuffer)本来就该是"持久"的------CPU 把一帧内不变的数据一次传上去,反复用。

Unity 传统路径的问题是:每帧、每个材质都临时打包+上传 CBuffer,切换材质就重做一遍,CPU 累死。

SRP Batcher 干的事就是"把 CBuffer 用回了它本来的正确用法":

复制代码
初始化: 每种材质 → 一个 CBuffer → 一次性上传 → 常驻显存
每帧:   更新每对象矩阵 → 指一下用哪块材质 → Draw

所以 SRP Batcher 的本质是:

一种"按 Shader 变体分组、材质数据 GPU 常驻、每对象数据单独更新"的渲染循环实现。

把"临时上传"改成"持久化 + 指针切换"

那为什么不把传统"扔掉"?

因为它覆盖不了这些情况

这是核心。SRP Batcher 有硬性限制,不满足就自动退回"标准 SRP 代码路径"()

场景 传统循环 SRP Batcher
粒子系统 正常工作 不支持(粒子/地形细节走另一套渲染路径)
蒙皮网格 Skinned Mesh 正常工作 官方文档说法不一致(见下文坑点)
用了 MaterialPropertyBlock 正常工作 直接退出(MPB 打破持久缓冲)
Shader 不兼容(CBuffer 不规范) 正常渲染 退回标准路径
GPU Instancing 更优的场景 --- SRP Batcher 反而更慢,得主动关掉它
极低端机 / 显存极紧 无额外显存占用 常驻 CBuffer 多吃显存,Unity 警告过"低端设备关掉反而更快"

GPU 实例化(GPU Instancing)

什么是?

大量完全相同的物体(网格、材质一致,例如建筑、树木、草丛),启用 GPU 实例化。该技术依靠图形硬件完成批合并。 开启方式:在项目窗口选中材质,检视面板勾选 Enable Instancing(启用实例化)

GPU Instancing 要求的"相同",只是指 Mesh + 材质(Shader)相同 。至于每个实例的位置、旋转、缩放、颜色 ------这些可以完全不同

回忆传统痛点:每个物体一次 Draw Call → CPU 累死

GPU Instancing :CPU 只发 一次命令:"GPU 你把这 1 个 Mesh 画 1000 次"

"勾选 Enable Instancing"到底干了啥?

材质 Inspector 勾选 Enable Instancing​ 后,Unity 会:

  1. 编译 Shader 时 加一个 INSTANCING_ON 关键字分支

  2. 自动用 UNITY_INSTANCING_BUFFER 等宏,把"每实例属性"塞进一个专门的 Instance CBuffer

  3. 运行时:当你用 Graphics.DrawMeshInstancedSRP 内部调度时,一次 Draw Call 画完所有实例

    // Shader 里的关键宏(Unity 自动处理,你通常不用手写)
    UNITY_INSTANCING_BUFFER_START(Props)
    UNITY_DEFINE_INSTANCED_PROP(float4, _Color) // 每棵树自己的颜色
    UNITY_INSTANCING_BUFFER_END(Props)

想给每棵树不同颜色/位置? ​ 用 MaterialPropertyBlock 设置 unity_InstanceID 对应的数据即可------但注意,用了 MPB 会让 SRP Batcher 失效

原理:那 1000 棵树怎么"各长各样"还一次画完?

关键就是 per-instance 数据缓冲 (前面 Shader 里那个 INSTANCE_ID):

CPU 端:

  • 准备 1 个 Mesh + 1 个材质(共享)

  • 准备一块"实例数据缓冲",存 1000 条记录:

树1: 位置A, 颜色红, 大小1.2

树2: 位置B, 颜色绿, 大小0.8

...

  • 发 1 次 DrawInstanced 命令 + 指向这块缓冲

GPU 端:

对每个实例 i:

读 缓冲i → 拿到自己的位置/颜色/大小

用同一套 Shader,画出"不一样的树"

hader 里长这样(Unity 自动处理,你通常只管加属性):

复制代码
UNITY_INSTANCING_BUFFER_START(Props)
    UNITY_DEFINE_INSTANCED_PROP(float4, _Color)   // 每棵树自己的颜色
    UNITY_DEFINE_INSTANCED_PROP(float3, _Offset)  // 每棵树自己的偏移
UNITY_INSTANCING_BUFFER_END(Props)

// 顶点着色器里用 instanceID 取值
float3 offset = UNITY_ACCESS_INSTANCED_PROP(Props, _Offset);
复制代码
核心思想:GPU 跑同一份 Shader 代码,但通过 instanceID去查各自的数据——所以"代码相同、数据不同",画面就各不一样。

静态批处理,动态批处理

都在"物理上减少 Draw Call",但合并的时机和地点完全不同

共同点:都是为了"减少 Draw Call"

不管静态还是动态,目标一致------把多个小物体合并成一次 Draw Call

区别在于:"合并"这件事,谁来做、什么时候做、在哪做

静态批处理(Static Batching)

"永不移动的几何体、共用同一材质 → Unity 在打包阶段合并成一张大 Mesh → 运行时一次 Draw Call 画完。"

核心:合并放在"打包时",且顶点矩阵提前算好

这是它最关键的特质。普通渲染时,每个物体的顶点要每帧乘一次模型矩阵 (从本地坐标→世界坐标),才能画到正确位置。而静态批处理因为物体永不移动

打包时就一次性把"乘好矩阵的最终顶点"算出来、存进那张合并大 Mesh。 ​ 运行时直接拿来画,再也不用每帧算矩阵

静态批处理对Mesh也没有要求

开启方式

  1. 编辑器 :Inspector 勾选 Batching Static(标记"我永不移动")
  2. 运行时StaticBatchingUtility.Combine() ------ 比如你程序化生成了一个静止关卡,生成完调用它手动合并

为什么"效率更高、但更耗内存"

说明
效率高 合并 + 顶点变换都在打包期做完,运行时零 CPU 变换,纯一次 Draw Call
更耗内存 多了一份"合并后的大网格"(顶点已经展开到世界空间,可能比原来大);且每个静态 GameObject 仍保留一份数据便于编辑/剔除
动态批处理(Dynamic Batching)

"针对小型网格,Unity 在 CPU 每帧完成顶点变换合并,一次性绘制。"

Unity 在运行时每帧由 CPU 把同材质的小网」的顶点实时合并成一个临时 Mesh,然后用一次 Draw Call 画完。

动态批处理要求"同材质",但对 Mesh 没有"必须完全相同"的硬性要求 (不像 Instancing 那样要求 Mesh 一模一样)

核心:合并放在"每帧的 CPU 上",临时做

因为物体会移动 ,没法提前算好顶点 → 只能每帧由 CPU 实时合并。

为什么有严格限制(原文的 300 / 900)

合并本身要 CPU 遍历顶点。如果 Mesh 太大,CPU 忙着合并反而比直接画还慢​ ------ 所以设了硬门槛:

限制 数值 原因
单个 Mesh 顶点数 ≤ 300 顶点多了合并开销大
总顶点属性 ≤ 900 位置+法线+UV... 属性多了搬运量大
必须同材质 不同材质没法一次 Draw

只有"大量低多边形小网格"才划算 (碎片、草、粒子等)。物体太少或太大,合并收益 < CPU 开销,得不偿失

本质:

抛开 Unity,回到图形学底层:

Draw Call 的开销主要来自"CPU 提交 + 状态切换"。动态批处理的思路是:既然多个小物体的材质相同,那我把它们的顶点在 CPU 端先拼成一个大 Mesh,GPU 就只需一次 Draw Call。

它本质上是一种 **"用 CPU 换 Draw Call 数量"**​ 的权衡:

  • 赚的:Draw Call 变少 → CPU 提交次数下降
  • 花的:CPU 每帧要遍历、变换、合并顶点 → 有开销

所以才有那个300 顶点 / 900 属性 的硬限制------合并本身的 CPU 成本不能大于省下来的 Draw Call 收益,否则就得不偿失。这就是"动态批处理"存在的根本逻辑。

一些注意事项总结:

材质 (Material) 的参数,分两种:【材质全局参数】、【实例化实例参数】

1、普通材质(非 Instancing)

材质对象本身,存了颜色、贴图、粗糙度等所有参数。

  • 如果你做两个 Material:

    • MatA:颜色红色

    • MatB:颜色蓝色 这是两个不同的 Material 实例

静态批处理、动态批处理:不允许,无法合并,会产生 2 个 DrawCall。

也就是说:普通情况下,颜色属于材质的一部分,改颜色 = 材质不一样


2、GPU Instancing 特殊机制

GPU Instancing 允许:同一个 Material 资源,给每个实例单独传颜色

关键:

  • Material 本体不变,Shader 开启 Enable Instancing

  • 颜色不存到 Material 里面,而是作为Instance(实例)数据,CPU 每帧传给 GPU。

  • 所有实例共用同一个 Material 对象,只是每个实例有自己的颜色参数。

此时:Mesh 相同 + 同一个 Material,就可以触发 GPU Instancing;每个实例颜色可以不一样。

重点: 不能是两个不同 Material(一个红一个蓝); 必须同一个 Material,颜色由实例缓冲区传入着色器。

写 Shader 的区别

复制代码
// 普通属性,属于材质,所有实例共用
_Color ("Color", Color) = (1,1,1,1)

// 实例化属性,每个实例独立
[PerInstance] _InstanceColor("Instance Color", Color) = (1,1,1,1)
  • _Color:改它,所有实例一起变色;不同实例不能独立。

  • [PerInstance] _InstanceColor:实例级参数,每个物体可以有自己颜色,不改变原始 Material

场景:100 个一模一样的方块,想要每个方块颜色不一样。

  1. 方案 A:新建 100 个 Material,每个 Material 设置不同颜色。
    • 此时:每个物体材质不一样,GPU Instancing 不生效,会 100 个 DrawCall。
  2. 方案 B:只用1 个 Material ,Shader 写[PerInstance] _InstanceColor
    • CPU 给每个实例传入自己的颜色。
    • Mesh 相同、Material 相同 → 触发 GPU Instancing,1 个 DrawCall
技术 支持管线 Mesh 要求 Material 材质实例要求 实例独立位置旋转缩放 实例独立颜色 / 参数 减少 DrawCall
静态批处理 Static 内置 / URP/HDRP 可以不同 Mesh 必须同一个 Material 实例 运行时不能修改 transform,变换烘焙进顶点 不支持,只能顶点色
动态批处理 Dynamic 仅内置管线 可以不同 Mesh 必须同一个 Material 实例 允许移动,CPU 每帧变换顶点 不支持,只能顶点色
GPU Instancing 内置 / URP/HDRP 必须同一个 Mesh 资源 必须同一个 Material 实例 完全独立,GPU 侧变换 支持,靠[PerInstance]实例参数
SRP Batcher URP/HDRP 无限制,可以任意 Mesh 无限制,可以不同 Material 实例 完全自由 材质各自独立 否,只优化 CPU 状态

遵循下面简单规则,最大化批处理效果:

  • 场景尽量减少纹理数量。纹理越少,独立材质数量越少,更容易合并批;尽可能使用纹理图集。
  • 光照贴图烘焙时,图集尺寸在合理范围内尽量开大。光照贴图数量越少,材质状态切换越少,但需要留意内存占用。
  • 避免无意实例化复制材质。脚本中访问 Renderer.material 会复制一份全新材质实例,直接破坏原有批合并。 如果需要读取批内物体材质,请改用 Renderer.sharedMaterial。
  • 优化过程中,借助 Profiler 性能分析器或者渲染统计面板,对比静态批、动态批数量与总绘制调用数量。 更多细节查阅官方绘制调用批处理文档。

优化填充率,降低过度绘制(Overdraw)

填充率(Fill rate):GPU 每秒能够向屏幕输出的像素总量。 如果游戏受填充率瓶颈限制,代表每帧需要绘制的像素总量超出了 GPU 处理能力。

同一个像素位置被多次重复绘制,称为过度绘制(Overdraw)。 过度绘制会消耗填充率,额外占用显存带宽。

造成过度绘制常见原因:

  • 不透明 / 透明几何体互相重叠
  • 复杂着色器,包含多个渲染 Pass
  • 未优化的粒子特效
  • UI 元素互相堆叠

没有万能方案可以彻底解决过度绘制,只能尽量降低它带来的负面影响。优先从上面几项入手调试验证,减小开销。

剔除(Culling)

***遮挡剔除(Occlusion culling)*会把被其他物体完全遮挡住的游戏对象禁用渲染。避免 CPU、GPU 耗费资源去渲染摄像机永远看不到的物体。

剔除是按摄像机独立执行的;如果同时启用多台摄像机,剔除带来的性能开销会明显增大。

Unity 包含两类剔除:视锥剔除(frustum culling)遮挡剔除(occlusion culling)视锥剔除由每台摄像机自动执行,不在摄像机视锥范围内的物体不会参与渲染,以此优化性能。

你可以通过 Camera.layerCullDistances 手动设置每层的剔除距离。 可以让小型物体比默认远裁剪平面更早被剔除。

将游戏对象划分到不同层级,通过 layerCullDistances 数组,给 32 个层级分别设置小于远裁剪平面的距离;填 0 代表沿用默认远裁剪平面。

***Unity 的剔除执行顺序:*先按层级剔除,只保留该摄像机启用层级上的物体;再执行视锥剔除,移除视锥之外的对象。 视锥剔除以多 Job 任务形式运行,充分利用工作线程。 每层的剔除检测本身开销很低,本质只是位掩码运算。但当游戏对象数量极其庞大时,累计开销仍然不可忽视。 如果项目出现该瓶颈,可以自行实现区块管理系统,把大世界划分为多个 "区块",摄像机视锥外的区块直接整体关闭,减轻 Unity 层级 / 视锥剔除系统压力。

遮挡剔除:摄像机完全看不见的物体直接从渲染列表移除。 物体被其他物体挡住时,默认依然会参与渲染消耗资源;开启遮挡剔除可以避免这种无效计算。 举个例子:房门关闭,摄像机看不到隔壁房间,就不需要渲染隔壁房间内容。

开启遮挡剔除可以大幅提升性能,但代价是占用更多磁盘空间、CPU 耗时与内存。 Unity 会在打包阶段烘焙遮挡数据;场景加载时再把这份数据从磁盘读入内存。

视锥剔除是自动生效的;而遮挡剔除属于预烘焙功能 。 只需把物体标记为静态遮挡物(Occluders)或被遮挡物(Occludees),再打开窗口: Window > Rendering > Occlusion Culling 完成烘焙。

这段讲的是 Unity 渲染管线的第一道优化关卡------剔除(Culling) 。核心就一句:"摄像机看不到的物体,干脆别送去渲染,CPU/GPU 的资源全省了。

它是按每台摄像机独立执行 的------所以多台摄像机 = 剔除开销成倍

剔除类型一:视锥剔除(Frustum Culling)

是什么

摄像机视锥(可见的那个金字塔区域)之外的物体,直接不渲染。

这是每台摄像机自动执行的,你不用做任何事,开箱即用。

手动优化Camera.layerCullDistances

可以手动控制"每层多远被剔除"

// 给 32 个层级分别设"小于远裁剪平面"的剔除距离

camera.layerCullDistances = new float32 {

0 = 50f, // Layer 0 (Default) 超过 50 单位就剔除

8 = 30f, // Layer 8 (草/小物件) 超过 30 单位剔除

// ... 填 0 = 沿用默认远裁剪面

};

执行顺序(原文这段很关键)

第1步:按层级剔除 → 只保留"该摄像机启用层"上的物体(位掩码运算,极快)
第2步:视锥剔除 → 移除视锥外的对象(多 Job 任务,用工作线程并行)

"每层的剔除检测开销很低(只是位掩码运算),但对象极其庞大时累计不可忽视"------意思是:

  • 普通项目:不用管,自动的够用
  • 超大世界(海量对象) :剔除本身成了瓶颈 → 自己实现**"区块管理"**(把世界切块,视锥外的整块直接关掉)

剔除类型二:遮挡剔除(Occlusion Culling)

视锥剔除只管"在不在视锥内",不管"被挡住没"。遮挡剔除管的是:物体在视锥内、但被别的物体完全挡住了 → 也别渲染。

视锥剔除: 墙后面 → 在视锥内 → 照渲染 (浪费)

遮挡剔除: 墙后面 → 被挡住 → 不渲染 (省了)

怎么开启(原文步骤)

复制代码
1. 把物体标记为:

| 选项 | 含义 |
| Occluder Static​ | 遮挡物​ ------ 我能挡住别人(墙、门、大建筑) |

Occludee Static 被遮挡物​ ------ 我被挡住就不渲染(家具、道具)
复制代码
复制代码
2. 打开窗口: Window > Rendering > Occlusion Culling

3. 点击烘焙 (Bake)
   → Unity 在打包阶段烘焙遮挡数据
   → 运行时场景加载时把数据读进内存

代价(别只看好处)

维度 代价
磁盘空间 烘焙的遮挡数据占包体
内存 运行时加载进内存
CPU 运行时查询遮挡数据有耗时

所以不是"无脑开" :小场景、遮挡关系简单的,开了反而亏(烘焙数据 + 查询开销 > 省下的渲染)。大场景、有明确遮挡结构(房间、走廊、建筑)的才划算。

"剔除顺序"的完整流程

复制代码
相关推荐
淡海水1 小时前
11-03-Unity-List-T-和Dictionary-TKey-TValue-的性能调优实战
算法·unity·c#·list·dictionary
狂人开飞机2 小时前
27、实战物理益智游戏
游戏·游戏引擎·godot
一孤程13 小时前
游戏测试专题第四篇:游戏性能测试实战-帧率/内存/发热全覆盖
游戏·测试
小小数媒成员18 小时前
如何优化代码适应托管内存?
游戏·unity·游戏引擎
humors22118 小时前
支付宝游戏灵画师简易步骤整理
游戏·技巧·手游·攻略·灵画师
河南花仙子科技18 小时前
企业定制小游戏助力品牌软性传播
大数据·科技·游戏·小程序
liulilittle1 天前
为什么采用全局管理缓存及状态:麻将客户端状态管理
服务器·网络·游戏·客户端·异步·mahjong·麻将
狂人开飞机1 天前
21、游戏架构设计模式
游戏·游戏引擎·godot
bj_bluewei_tech2 天前
拯救者游戏本维修卡 logo 无限重启,确认显卡虚焊故障
游戏·电脑