当前位置: 首页 > news >正文

Unity性能优化实战:从Stats面板到CPU/GPU瓶颈诊断

1. 项目概述:从Stats面板开始你的性能优化之旅

如果你是一名Unity开发者,无论你是刚入行的新手,还是已经摸爬滚打多年的老手,我相信你一定对“性能优化”这四个字又爱又恨。爱的是,一个流畅、稳定的游戏或应用能带来极佳的用户体验,是项目成功的关键;恨的是,性能问题往往像幽灵一样难以捉摸,你感觉哪里都正常,但帧率就是上不去,或者内存悄悄就爆了。很多时候,我们面对卡顿、掉帧、闪退,第一反应是去网上搜“Unity性能优化技巧”,然后对着几十条建议,一条条去试,耗时耗力,效果还不一定好。这就像医生看病,不先做检查,直接开药,风险很大。

今天,我们不谈那些零散的“技巧”,而是从一个最基础、最核心、但常常被忽视的工具入手——Unity编辑器自带的Stats面板。它就像是Unity项目的“性能仪表盘”,实时显示着CPU、GPU、渲染、内存等关键指标。很多开发者知道它的存在,但可能只是瞟一眼帧率(FPS),或者只在打包后遇到问题时才想起来看看。实际上,Stats面板是性能问题诊断的起点和基石。它能快速告诉你“哪里不对劲”,让你后续的优化工作有的放矢,而不是盲目地“猜”和“试”。

这个内容适合所有使用Unity进行开发的程序员、技术美术和项目负责人。无论你是在做移动端的轻量级小游戏,还是在开发PC端的大型项目,理解并善用Stats面板,都能让你在性能优化的道路上事半功倍。接下来,我将带你深入拆解Stats面板的每一个数据项,告诉你它们背后的含义、合理的范围,以及当数值异常时,你应该去项目的哪个角落寻找“病根”。

2. Stats面板全项深度解析与实战意义

打开Stats面板很简单,在Game视图的右上角,点击“Stats”按钮即可。弹出的面板里信息密密麻麻,我们把它分成几个核心板块来逐一击破。

2.1 图形渲染(Graphics)板块:帧时间的“解剖图”

这是最直观反映画面流畅度的区域。核心是一个堆叠柱状图,它把生成一帧画面所花费的时间(Frame Time)分解成了几个部分。

1. CPU: Main & Render Thread

  • 是什么CPU: Main代表主线程(游戏逻辑线程)的执行时间,处理你的Update、FixedUpdate、脚本逻辑、物理计算(如果没分线程)等。CPU: Render代表渲染线程准备渲染命令所花费的时间。
  • 为什么重要:这两者之和,可以粗略理解为“CPU等待GPU开始干活之前所花的时间”。如果这个值很高(比如超过10ms,目标60FPS下每帧约16.7ms),说明你的游戏逻辑或渲染指令准备太复杂,CPU成了瓶颈。
  • 经验之谈:在移动端,要特别关注CPU: Main。复杂的AI、大量的GameObject.Update调用、低效的算法(如未优化的寻路)都会导致这里暴涨。使用Profiler的CPU Usage模块可以进一步定位是哪个函数耗时。

2. GPU

  • 是什么:GPU执行所有渲染管线工作(顶点着色、片元着色、光栅化等)所花费的时间。
  • 为什么重要:这是纯粹的图形硬件负载。如果这个值很高,而CPU时间很低,说明瓶颈在GPU。你的画面太“费”了。
  • 实操要点:GPU时间高,通常意味着:渲染面数太多(批次过多或单个模型面数过高)、使用了过于复杂的Shader(特别是移动端的高精度计算、多重纹理采样)、过高的屏幕分辨率/后处理效果(如Bloom、SSAO)。这时需要借助Frame Debugger工具来查看具体绘制了什么。

3. Batches & Saved by batching

  • 是什么Batches是Unity实际提交给GPU的绘制调用次数。Saved by batching是通过动态合批或静态合批节省下来的批次数量。
  • 为什么重要降低Draw Call(在Unity中更准确的说法是减少Batches)是渲染优化的金科玉律。每一次Batch都伴随着CPU到GPU的一次通信开销。移动端建议将Batches控制在100以下,PC端根据项目复杂度可以放宽,但也是越低越好。
  • 深度解析:合批(Batching)是Unity减少批次的核心机制。静态合批(Static Batching)适用于不会移动的物体,在运行前合并网格,节省CPU但增加内存和包体。动态合批(Dynamic Batching)由Unity运行时自动为满足条件(顶点数少、使用相同材质等)的小型动态物体进行合批,有CPU开销。Saved by batching这个数字越高,说明你的合批策略越有效。如果这个数字是0,而Batches又很高,你就需要检查材质使用是否过于混乱,或者大量物体无法满足合批条件。

4. Tris & Verts

  • 是什么Tris(三角形)和Verts(顶点)是当前帧渲染的图元总数。
  • 为什么重要:这是GPU负载的另一个重要指标。虽然现代GPU处理三角形能力很强,但在移动端或低端设备上,面数依然是硬约束。通常,移动端建议每帧渲染的三角形数量在10万到20万以下,具体取决于设备性能。
  • 注意事项:这个数字是累计值,即所有被渲染的物体的面数/顶点数之和。如果一个物体被渲染了多次(比如在反射探针、阴影中),它会被重复计算。因此,有时Tris数会远高于你场景中静态模型的总和,这是正常的,但也提示你可能存在过度渲染(如不必要的反射效果)。

2.2 内存(Memory)板块:资源管理的“晴雨表”

内存问题,尤其是移动端的内存溢出(OOM),是导致应用闪退的元凶之一。Stats面板提供了几个关键的内存指标。

1. Used Texture Memory / Used Mesh Memory

  • 是什么:当前使用的纹理内存和网格内存。
  • 为什么重要:纹理和网格是游戏内存占用的大头。这里显示的是GPU或系统内存中为它们分配的大小。你需要关注它是否在持续增长(内存泄漏),或者峰值是否超过了目标设备的安全线(例如,iOS上单个应用内存超过设备RAM的60%就非常危险)。
  • 排查技巧:如果纹理内存异常高,检查是否有纹理尺寸过大(2048x2048的RGBA32纹理一张就占16MB)、Mipmap未关闭(增加33%内存)、或者压缩格式不合适(在Android上用ASTC,iOS上用PVRTC或ASTC)。使用Unity Profiler的Memory模块可以查看具体的纹理资产列表。

2. Total Used Memory

  • 是什么:Unity引擎当前使用的总内存。这个值比单纯的托管堆(Managed Heap)要大,因为它包含了Native堆(引擎底层C++代码分配的内存,如纹理、网格、音频数据的Native部分)、GPU资源等。
  • 为什么重要:这是最需要盯住的“红线”指标。你需要根据目标平台(如iOS/Android)设定一个内存预算,并确保游戏运行时的Total Used Memory峰值低于这个预算。在Profiler的Memory模块中,你可以看到更详细的分解:System Used Memory(整个进程占用的系统内存)、Texture MemoryMesh MemoryAudio MemoryManaged Heap等。

3. GC (Garbage Collection)

  • 是什么:这不是一个实时数值,而是一个事件。当它发生时,会引发帧率卡顿。其根源在于托管堆内存分配
  • 为什么重要:在C#中,每次使用new关键字创建引用类型对象(如List, Dictionary, 字符串拼接等),都会在托管堆上分配内存。当这些对象不再被引用(变成垃圾),且托管堆需要更多空间时,Unity的Mono或IL2CPP运行时会触发垃圾回收(GC),这是一个“停止世界”(Stop-The-World)的操作,会阻塞主线程,导致明显的帧率下降。
  • 核心避坑指南
    • 避免在Update等每帧调用的函数中分配新对象。这是移动端性能的“头号杀手”。
    • 缓存常用组件和引用,不要每帧都GetComponent
    • 使用对象池(Object Pool)来复用频繁创建销毁的物体(如子弹、特效)。
    • 谨慎使用字符串操作,特别是string.Format+运算符在循环中,考虑使用StringBuilder
    • 在Profiler的CPU Usage模块中,关注GC Alloc列,它能精确告诉你每一帧分配了多少字节的托管内存,并定位到分配这些内存的代码行。

2.3 音频(Audio)板块:容易被忽略的“内存刺客”

1. Audio CPU / Audio DSP / Audio Stream

  • 是什么:分别表示音频处理占用的CPU时间、数字信号处理负载、以及音频流解码负载。
  • 为什么重要:音频通常不占主要CPU开销,但如果同时播放大量高采样率的音频源,或者使用了复杂的3D音效和混响效果,Audio CPU可能会升高。Audio Stream如果很高,说明你在播放未压缩的音频流(如.wav),这会占用大量内存和带宽。
  • 优化建议:对于背景音乐等长音频,使用压缩格式(如.mp3, .ogg (Vorbis)),并设置为流式加载(Streaming),避免一次性加载进内存。对于短音效,可以使用未压缩的.wav以获得更快加载速度,但要注意控制数量。在Audio Manager中合理设置最大实时音频源数量。

2.4 网络(Network)与物理(Physics)板块

这两个板块在非网络游戏或物理简单的游戏中可能不常关注,但一旦相关,就是性能问题的重灾区。

1. Network

  • 是什么:显示每秒发送和接收的字节数、消息数量等。
  • 为什么重要:对于网络游戏,这是监控网络流量和同步效率的窗口。过高的发送频率或过大的数据包会导致网络拥堵和延迟。
  • 优化方向:使用状态同步而非帧同步以减少数据量;对传输数据进行压缩和差分编码;提高发送频率的阈值。

2. Physics

  • 是什么:显示物理引擎的CPU耗时。注意,在Unity默认设置下,物理计算是在主线程或单独的物理线程中进行的。
  • 为什么重要:复杂的物理场景(大量刚体、碰撞体、关节、布料模拟)会消耗大量CPU资源。Physics.Process时间过长会直接拖慢主线程。
  • 深度优化
    • 简化碰撞体:用简单的几何体(Box, Sphere, Capsule)代替Mesh Collider。
    • 分层管理:通过Physics Layers和碰撞矩阵,避免不必要的物体间进行碰撞检测。
    • 使用触发器(Trigger)而非碰撞体(Collider)如果不需要物理反馈。
    • 静态物体标记为Static:对于永远不会移动的物体,标记为Static,物理引擎会对其进行优化。
    • 控制更新频率:对于非关键物理物体,可以尝试降低Fixed Timestep(Edit -> Project Settings -> Time),但要注意这可能影响物理精度。

3. 基于Stats数据的系统性性能排查流程

知道了每个指标的含义,我们如何将它们串联起来,形成一套高效的排查方法?下面是一个实战流程。

3.1 第一步:建立性能基线与目标

在开始优化前,你必须要知道“好”的标准是什么。

  1. 确定目标帧率(FPS):通常是30或60。对应的每帧目标时间就是1000ms / FPS(例如,60FPS对应16.7ms/帧)。
  2. 在目标设备(或相近性能的模拟环境)上运行游戏,进入一个你认为有代表性的、负载较重的场景(如主战场、繁华的城镇)。
  3. 观察Stats面板,记录下关键数据:
    • Frame Time (FPS):是否稳定在目标值附近?
    • CPU 和 GPU 时间:谁更高?谁是瓶颈?
    • Batches:是否在可接受范围?
    • Tris:是否超出设备承受能力?
    • Total Used Memory:峰值是多少?距离设备安全线有多远?
    • GC Alloc (需在Profiler中看):每帧分配量是否过高(例如超过几KB)?

这个记录就是你的“性能基线”。所有优化工作都围绕让这些指标达到或优于基线来展开。

3.2 第二步:识别瓶颈类型(CPU-Bound vs GPU-Bound)

这是最关键的一步,决定了你优化的主攻方向。

  • 情况A:CPU时间 (Main+Render) > GPU时间,且Frame Time超标

    • 诊断:CPU瓶颈。游戏逻辑或渲染准备太慢。
    • 下一步工具:立即打开Unity Profiler (Window -> Analysis -> Profiler),切换到CPU Usage模块。这里会以树状图形式显示主线程上所有函数的耗时。找到最顶部的“热点”函数,点击展开,定位到你的游戏代码。常见元凶:未优化的Update循环、复杂的AI逻辑、大量的Find/GetComponent调用、低效的算法(如List的线性查找)、过高的Draw Call(Batches)导致渲染线程压力大。
  • 情况B:GPU时间 > CPU时间,且Frame Time超标

    • 诊断:GPU瓶颈。画面渲染负载过重。
    • 下一步工具:打开Frame Debugger (Window -> Analysis -> Frame Debugger)。启用后,你可以逐绘制命令(Draw Call)地查看当前帧是如何渲染出来的。重点关注:
      • 为什么有这么多不连续的Batches?(通常是材质或Shader不同导致合批中断)
      • 是否有物体被重复渲染多次?(如渲染到阴影贴图、反射探针)
      • 是否有全屏的后处理效果?
    • 同时使用GPU Profiler(在Profiler窗口中添加GPU模块)或平台专属工具(如Android的Snapdragon Profiler, iOS的Xcode Instruments)。这些工具能告诉你GPU内部哪个阶段耗时最长(顶点处理、片元处理、带宽等)。
  • 情况C:CPU和GPU时间都不高,但Frame Time波动大,偶尔卡顿

    • 诊断:间歇性卡顿。罪魁祸首很可能是垃圾回收(GC)资源同步加载
    • 排查:在Profiler的CPU Usage模块中,寻找那些单帧耗时突然很高的帧,看其中是否有明显的GarbageCollect任务。同时,检查是否有在运行时同步加载大型资源(如Resources.Load、实例化一个包含大量网格/纹理的预制体)。

3.3 第三步:实施针对性优化与验证

根据第二步的定位,采取具体措施:

针对CPU瓶颈(逻辑/渲染线程):

  1. 脚本优化:优化热点函数算法;缓存组件引用;避免在Update中使用FindGetComponent;使用CoroutineInvokeRepeating替代高频的Update检查。
  2. 降低Draw Call
    • 合并材质:尽可能让多个物体共享同一个材质球。使用纹理图集(Texture Atlas)将多个小纹理合并成一张大图。
    • 利用合批:将静态物体标记为Static(启用Static Batching);确保动态合批的条件(顶点数、相同材质等)被满足。
    • 使用GPU Instancing:对于大量相同的物体(如草、树、子弹),使用支持GPU Instancing的Shader,可以极大降低Draw Call。
  3. 优化物理:如前所述,简化碰撞体,使用图层过滤。

针对GPU瓶颈:

  1. 减少过度绘制(Overdraw)
    • 使用Occlusion Culling(遮挡剔除),避免渲染被挡在相机后面的物体。
    • 合理安排物体渲染顺序,不透明物体从前往后画(利用深度测试提前丢弃片元),半透明物体从后往前画。
    • 在移动端,谨慎使用全屏后处理。如果要用,选择性能开销小的,或自己编写简化版本的Shader。
  2. 简化Shader
    • 移动端使用简单的、支持Forward Rendering的Shader,如Unity内置的Mobile系列或Unlit系列。
    • 减少Shader中的纹理采样次数、复杂数学运算(如sin,pow)和条件判断。
    • 使用Shader LOD(Level of Detail),为远处物体使用更简单的Shader变体。
  3. 优化模型与纹理
    • 使用合理的模型LOD(Level of Detail),远处显示低模。
    • 压缩纹理尺寸,使用合适的压缩格式(ASTC, ETC2, PVRTC)。
    • 关闭不需要的纹理通道的Mipmap。

针对内存与GC问题:

  1. 监控与预算:为纹理、网格、音频、总内存设定严格的预算,并在开发过程中持续监控Stats和Profiler中的Memory模块。
  2. 杜绝每帧托管内存分配:这是最立竿见影的优化。使用对象池,重用集合(如List.Clear()后复用而非new List()),避免在频繁调用的函数中创建临时对象。
  3. 异步加载资源:使用AddressablesAssetBundle的异步加载接口,避免主线程卡顿。

每做一项优化,都要回到目标设备上,重新运行场景,对比Stats面板和Profiler中的数据,验证优化是否有效。性能优化是一个迭代和权衡的过程。

4. 高级技巧与Profiler联动分析

Stats面板给出了宏观指标,而Unity Profiler提供了微观的、代码级别的洞察。两者结合,才能完成从“发现问题”到“定位代码”的闭环。

4.1 使用Profiler深挖CPU热点

当Stats显示CPU时间过高时:

  1. 在Profiler的CPU Usage模块,确保记录模式是“Deep Profile”(深度分析)。这会记录所有函数调用,开销较大,但信息最全。对于发布版本或真机调试,可以使用“Editor”连接真机进行采样分析。
  2. 在时间线视图中,找到耗时最高的那一帧,点击它。
  3. 在下面的层次视图(Hierarchy)中,按照“Total”或“Self”时间排序。“Self”时间是指该函数自身代码的耗时,不包括它调用的子函数。通常优化“Self”时间高的函数收益最大。
  4. 展开函数,你会看到具体的代码行耗时。例如,你可能会发现一个foreach循环在遍历一个巨大的List,或者一个字符串操作在频繁分配内存。

注意:深度分析(Deep Profile)会极大影响运行速度,仅用于在编辑器内定位关键问题。对于真机性能测试,应使用采样分析。

4.2 内存分析的完整流程

Stats的Total Used Memory告诉你水位高了,但不知道是哪里漏水。Profiler的Memory模块是你的“探漏仪”。

  1. 在Profiler中切换到Memory模块。
  2. 点击“Take Sample”(获取样本)。稍等片刻,你会看到一个详细的内存快照。
  3. 重点关注:
    • Managed Heap:托管堆内存。如果这里异常大,点击“Open in Memory Profiler”(需要安装Memory Profiler包),它可以帮你追踪是哪些C#对象占用了内存,以及是谁引用了它们,这对于查找内存泄漏至关重要。
    • Assets:这里列出了所有加载到内存中的纹理、网格、音频等资源。按大小排序,很容易找到那些“内存大户”。检查它们是否被合理使用,是否有重复加载,或者尺寸是否过大。
    • Not Saved / Scene Memory:区分了来自Assets的资源内存和场景中动态创建对象的内存。

4.3 真机调试与远程连接

编辑器下的性能表现和真机(尤其是移动设备)往往天差地别。真机测试是性能优化的必经之路

  1. 构建Development Build:在Build Settings中勾选“Development Build”和“Autoconnect Profiler”(对于Android,可能还需要勾选“Enable Deep Profiling Support”)。
  2. 连接设备:通过USB连接Android设备或iOS设备。
  3. 在Unity编辑器中,打开Profiler窗口,在Active Profiler下拉菜单中选择你的设备(通常以设备IP地址或名称显示)。
  4. 在手机上运行游戏,现在你可以在编辑器的Profiler中实时看到手机上的性能数据了!Stats面板的数据也会同步更新为真机数据。

这个过程可能会遇到连接问题,确保设备与电脑在同一网络,或者USB调试已正确开启。真机数据才是最真实、最可靠的优化依据。

5. 常见性能问题速查与实战案例

结合Stats面板的异常表现,我们可以快速联想到一些常见问题及其排查方向。下面这个表格可以作为你的“性能诊断备忘录”:

Stats面板异常现象可能的原因下一步排查工具/方向
FPS低,CPU (Main) 时间极高1. 复杂脚本逻辑(AI、寻路)
2. 大量GameObject.Update调用
3. 每帧进行昂贵的计算(物理射线、路径点计算)
4. 频繁的GC Alloc引发GC停顿
Profiler CPU Usage:定位耗时最高的函数。检查GC Alloc列。
FPS低,CPU (Render) 时间极高1. Draw Call (Batches) 过多
2. 动态合批开销大(顶点数超限的物体)
3. 复杂的UI重建(Canvas)
Frame Debugger:查看Draw Call数量和中断原因。
Profiler:查看Canvas.SendWillRenderCanvases耗时。
FPS低,GPU时间极高1. 渲染面数(Tris)过多
2. 过度绘制严重
3. 复杂的片元Shader(高精度计算、多重纹理、后处理)
4. 分辨率过高(特别是UI渲染分辨率)
Frame Debugger:查看渲染顺序和Overdraw。
GPU Profiler:分析GPU流水线瓶颈。
降低画质设置,看FPS是否大幅提升。
Batches数量异常高1. 使用了大量不同的材质球
2. 物体无法合批(动态物体顶点数超限、缩放不一致、含有实时灯光等)
3. UI元素过多,每个Graphic都是一个Batch
Frame Debugger:逐条查看Draw Call,看材质变化点。
检查材质属性(如颜色、纹理)是否通过脚本每帧修改(这会打断合批)。
Total Used Memory持续增长1. 资源泄漏(加载的AssetBundle、实例化的对象未销毁)
2. 动态加载资源未卸载
3. 托管堆内存因GC不及时而膨胀
Profiler Memory:定期Take Sample,对比Managed HeapAssets的变化。使用Memory Profiler追踪对象引用。
频繁的帧率卡顿(Spike)1. 垃圾回收(GC)
2. 同步加载大型资源(Resources.Load)
3. 磁盘I/O(读取文件)
4. 复杂的瞬时计算(如A*寻路开始)
Profiler CPU Usage:在卡顿帧寻找GarbageCollect或长的同步加载调用栈。
使用异步加载(Addressables.LoadAssetAsync)。

实战案例分享:一个UI界面的性能陷阱

我曾遇到一个案例:游戏内打开一个角色背包界面时,FPS从60骤降到20。Stats面板显示:Batches暴增到500+,CPU (Render)时间飙升

  1. 初步分析:Batches过高,显然是渲染问题,且与UI相关。
  2. 使用Frame Debugger:打开背包界面,启用Frame Debugger。发现几百个Draw Call都在渲染UI元素(Image, Text)。原因是,这个背包有100个格子,每个格子是一个独立的UI元素(Image+Text),且每个格子的背景图(Image)都引用了同一个Sprite,但文字(Text)内容不同。
  3. 问题根源:在Unity的UGUI系统中,每个Graphic组件(Image, Text, RawImage)默认都会产生至少一个Draw Call。即使它们使用相同的材质(如Atlas),但如果它们的顶点数据(如文字内容改变导致网格重建)不同,或者Mask、RectMask2D等组件的影响,也会导致合批失败,产生大量独立的Batch。
  4. 解决方案
    • 合批优化:确保所有使用同一图集的UI元素在层级上连续,且中间没有被其他材质或特效打断。
    • 减少元素数量:对于超长列表,使用循环列表(Recycling List)虚拟化技术,只实例化屏幕内可见的少数几个UI项,通过数据绑定来更新内容。这是解决UI性能问题的终极方案。
    • 简化UI结构:避免过深的UI嵌套和过多的Mask组件。
    • 使用TextMeshPro:对于大量动态文本,TextMeshPro (TMP) 在性能和效果上通常优于传统的UI Text。
  5. 优化后:采用循环列表,将同时存在的UI格子减少到10个左右。再次打开背包,Batches降至50以内,FPS恢复到55+。

这个案例告诉我们,Stats面板的异常指标(高Batches)是指引我们方向的灯塔,而Frame Debugger和Profiler则是我们深入问题腹地的显微镜和解剖刀。性能优化没有银弹,但有了正确的工具和方法论,我们就能将复杂的问题层层分解,最终找到并解决那个关键的瓶颈。

http://www.cnnetsun.cn/news/3508766.html

相关文章:

  • 批量视频截图工具2.0:三种截帧模式详解与高效批量处理实践
  • AM/FM信号调制度测量与解调系统设计
  • HTTP基础认证与浏览器登录框机制详解
  • java运算符和变量,scanner
  • WPS AI写文档效率翻倍:实测7类高频场景,92%用户忽略的5个隐藏技巧
  • CC35xx通用定时器GPT深度解析:从PWM生成到输入捕获的嵌入式实战
  • STM32串口通信基础与HAL库实现
  • ARM GIC中断路由机制与GICD_IROUTER寄存器配置详解
  • 安卓网络请求框架android-async-http详解与优化
  • Android面试核心知识体系与高频问题解析
  • TI ISP寄存器深度解析:CBUFF与CSI1B模块配置实战指南
  • 会计与大数据专业大学四年最好的规划
  • ChatGPT Work实战:如何把一次性对话改造成可复用的任务工作流
  • 深入解析OMAP4460时钟与电源管理:寄存器配置与低功耗设计实战
  • JavaScript 异常捕获完全指南
  • Ubuntu 22.04编译AOSP Android 13完整指南
  • Wallpaper Engine资源逆向全解析:3步掌握PKG解密与TEX纹理转换技术
  • Winform多线程UI操作简化与优化实践
  • 【mimo-硬件损伤-预编码】加权融合预编码 —— 从“单项冠军”到“全能战队”
  • Unity与VS2013/2015调试集成:原理、配置与实战排错指南
  • CANoe CAPL中Checksum与Counter的实现与应用
  • WAIC 2026|范式展出全栈 AI 底座与产业落地成果,构筑 AI 2.0 Token 工厂
  • 多模型网关 Token 账单对不上:流式响应、重试与团队成本归因的实现
  • ShaderGraph沃罗诺伊噪声:从原理到实战,打造程序化纹理与材质
  • 安卓网络请求框架android-async-http详解与优化实践
  • ShaderGraph三角波节点深度解析:从数学原理到UI动效与游戏特效实战
  • ASP.NET Core Identity高级定制与安全实践
  • 支持JavaScript基础语法和数据类型,以及数组、集合、字典、对象操作。 支持require引用模块。 二、安装 1 install-package AScript 2 install-pac
  • Python新手入门:环境搭建与基础语法详解
  • 博客导航系统设计:提升老文章阅读量的技术方案