<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Ank_Blogging</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2025-05-27T12:07:46.000Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>Ank</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>合批和UI图集</title>
    <link href="http://example.com/2025/05/25/%E5%90%88%E6%89%B9%E5%92%8CUI%E5%9B%BE%E9%9B%86/"/>
    <id>http://example.com/2025/05/25/%E5%90%88%E6%89%B9%E5%92%8CUI%E5%9B%BE%E9%9B%86/</id>
    <published>2025-05-25T09:37:08.000Z</published>
    <updated>2025-05-27T12:07:46.000Z</updated>
    
    <content type="html"><![CDATA[<ul><li>项目文件 “MoveSnake”</li></ul><blockquote><p>tips:打图集类似合批，UI 渲染以 Canvas 为单位,每个 canvas 带一个批次，然后根据 canvas 下的内容进行分批渲染，一个图集中的 Image 为一个批次，不在一个图集中或不在一个 canvas 下为另一个批次</p></blockquote><blockquote><p>脚本打包方式，找行为树教学</p></blockquote><p><a href="https://blog.csdn.net/ww1351646544/article/details/139678759?spm=1001.2014.3001.5506">合批</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;ul&gt;
&lt;li&gt;项目文件 “MoveSnake”&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;tips:打图集类似合批，UI 渲染以 Canvas 为单位,每个 canvas 带一个批次，然后根据 canvas 下的内容进行分批渲染，一个图集中的 Image 为一个批</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Unity调试</title>
    <link href="http://example.com/2025/05/16/Unity%E8%B0%83%E8%AF%95/"/>
    <id>http://example.com/2025/05/16/Unity%E8%B0%83%E8%AF%95/</id>
    <published>2025-05-16T02:30:42.000Z</published>
    <updated>2025-05-25T09:39:06.000Z</updated>
    
    
    
    
    
    <category term="Unity" scheme="http://example.com/tags/Unity/"/>
    
  </entry>
  
  <entry>
    <title>网格，纹理，着色器和材质</title>
    <link href="http://example.com/2024/08/08/%E7%BD%91%E6%A0%BC%EF%BC%8C%E7%BA%B9%E7%90%86%EF%BC%8C%E7%9D%80%E8%89%B2%E5%99%A8%E5%92%8C%E6%9D%90%E8%B4%A8/"/>
    <id>http://example.com/2024/08/08/%E7%BD%91%E6%A0%BC%EF%BC%8C%E7%BA%B9%E7%90%86%EF%BC%8C%E7%9D%80%E8%89%B2%E5%99%A8%E5%92%8C%E6%9D%90%E8%B4%A8/</id>
    <published>2024-08-08T07:42:02.000Z</published>
    <updated>2024-08-19T01:43:56.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="网格"><a href="#网格" class="headerlink" title="网格"></a>网格</h1><h1 id="纹理"><a href="#纹理" class="headerlink" title="纹理"></a>纹理</h1><h1 id="材质"><a href="#材质" class="headerlink" title="材质"></a>材质</h1><h1 id="着色器"><a href="#着色器" class="headerlink" title="着色器"></a>着色器</h1>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;网格&quot;&gt;&lt;a href=&quot;#网格&quot; class=&quot;headerlink&quot; title=&quot;网格&quot;&gt;&lt;/a&gt;网格&lt;/h1&gt;&lt;h1 id=&quot;纹理&quot;&gt;&lt;a href=&quot;#纹理&quot; class=&quot;headerlink&quot; title=&quot;纹理&quot;&gt;&lt;/a&gt;纹理&lt;/h1&gt;&lt;h1 id</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Unity渲染管线</title>
    <link href="http://example.com/2024/08/01/Unity%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF/"/>
    <id>http://example.com/2024/08/01/Unity%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF/</id>
    <published>2024-08-01T08:03:52.000Z</published>
    <updated>2024-08-08T11:14:24.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="渲染管线简介"><a href="#渲染管线简介" class="headerlink" title="渲染管线简介"></a>渲染管线简介</h1><p>渲染管线是计算机图形学中用于将 3D 模型转换为 2D 图像的一系列处理步骤。这个过程从读取 3D 模型的数据开始，一直到将最终的像素颜色输出到屏幕上结束。概括来说，这些操作如下：</p><ul><li>剔除</li><li>渲染</li><li>后期处理</li></ul><p>这里把他们详细的分为几个阶段：</p><ol><li><p><strong>应用阶段（Application Stage）</strong></p><ul><li>在这一阶段，开发者通过编程控制模型的变换、材质和光照设置。</li><li>这里通常涉及到模型、视图和投影矩阵的设置，用于控制物体在世界中的位置、方向和大小，以及摄像机的位置和视角。</li></ul></li><li><p><strong>顶点着色（Vertex Shading）</strong></p><ul><li>顶点着色器接收原始顶点数据，执行一系列数学运算，如模型变换、光照计算等，来准备顶点供下一步处理。</li><li>这一步骤也常用于处理动画、皮肤和骨骼绑定。</li></ul></li><li><p><strong>光栅化（Rasterization）</strong></p><ul><li>光栅化阶段将 3D 空间中的多边形转换为 2D 屏幕上的像素。</li><li>这个过程中，每个多边形被分解成像素，然后这些像素被着色。</li></ul></li><li><p><strong>片段着色（Fragment Shading）</strong></p><ul><li>片段着色器（也称为像素着色器）为每个像素计算最终颜色，包括光照、纹理贴图、阴影和透明度等效果。</li><li>这一步骤负责产生最终的视觉细节，如高光、反射和折射。</li></ul></li><li><p><strong>混合（Blending）</strong></p><ul><li>当多个片段落在同一像素上时，需要决定如何结合它们的颜色。</li><li>混合可以用来处理透明物体，使它们看起来自然地与其他物体叠加在一起。</li></ul></li><li><p><strong>后处理（Post-Processing）</strong></p><ul><li>后处理效果包括色彩校正、模糊、锐化、景深、镜头眩光等，这些效果通常是在整个图像上应用的。</li><li>这些效果可以增强最终图像的质量和艺术感。</li></ul></li><li><p><strong>显示（Display）</strong></p><ul><li>最终，所有处理过的像素被输出到屏幕上，完成从 3D 模型到可见图像的转换。</li></ul></li></ol><p>不同的渲染管线具有不同的功能和性能特征，并且适用于不同的游戏、应用程序和平台。</p><p>将项目从一个渲染管线切换到另一个渲染管线可能很困难，因为不同的渲染管线使用不同的着色器输出，并且可能没有相同的特性。因此，必须要了解 Unity 提供的不同渲染管线，以便可以在开发早期为项目做出正确决定。<a href="https://docs.unity3d.com/cn/2021.3/Manual/render-pipelines-feature-comparison.html">渲染管线特征比较</a></p><p>Unity 提供以下渲染管线：</p><ul><li><strong>内置渲染管线</strong>是 Unity 的默认渲染管线。这是通用的渲染管线，其自定义选项有限。</li><li><strong>通用渲染管线 (URP)</strong> 是一种可快速轻松自定义的可编程渲染管线，允许您在-各种平台上创建优化的图形。</li><li><strong>高清渲染管线 (HDRP)</strong> 是一种可编程渲染管线，可让您在高端平台上创建出色的高保真图形。</li><li>可以使用 Unity 的可编程渲染管线 API 来创建自己的<strong>自定义渲染管线</strong>。</li></ul><h1 id="内置渲染管线"><a href="#内置渲染管线" class="headerlink" title="内置渲染管线"></a>内置渲染管线</h1><p>Unity 的内置渲染管线是 Unity 较陈旧的渲染管线。它并非基于可编程渲染管线。内置渲染管线专注于提供广泛兼容性和性能优化，适用于大多数平台，包括移动设备、桌面和游戏主机。</p><h2 id="渲染路径"><a href="#渲染路径" class="headerlink" title="渲染路径"></a>渲染路径</h2><p>Unity 的内置渲染管线（Built-in Render Pipeline）提供了几种不同的渲染路径，每种路径针对不同的场景和需求进行了优化。Unity 提供了几种渲染路径包括<strong>前向渲染（Forward Rendering）、延迟渲染（Deferred Rendering）、旧版延迟 (Legacy Deferred)、旧版顶点光照</strong>。现在内建渲染管线的渲染路径主要是：<strong>前向渲染路径</strong>和<strong>延迟渲染路径</strong>。<a href="https://docs.unity3d.com/cn/2021.3/Manual/RenderingPaths.html">渲染路径比较</a></p><h3 id="前向渲染（Forward-Rendering）"><a href="#前向渲染（Forward-Rendering）" class="headerlink" title="前向渲染（Forward Rendering）"></a>前向渲染（Forward Rendering）</h3><h4 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h4><p>前向渲染是 Unity 中最常见的渲染路径，也是最简单的。它为每个光源生成一个渲染 Pass，这意味着如果场景中有多个光源，每个光源都会有自己的 Pass，每个 Pass 都会对可见的几何体进行渲染。</p><h4 id="渲染流程概述"><a href="#渲染流程概述" class="headerlink" title="渲染流程概述"></a>渲染流程概述</h4><ol><li><strong>初始化</strong>：清空帧缓冲，包括颜色缓冲和深度缓冲。</li><li><strong>顶点着色</strong>：使用顶点着色器处理每个顶点，计算变换后的顶点位置、法线和其他属性，如纹理坐标。</li><li><strong>片段处理</strong>：对于场景中的每一个光源，进行以下步骤：<ul><li>将每个三角形栅格化为像素。</li><li>对于每个像素，使用片段着色器计算光照效果，包括光照强度、颜色和阴影。</li><li>将计算得到的颜色值和深度值写入颜色缓冲和深度缓冲，进行深度测试以确定像素是否应该被绘制。</li></ul></li><li><strong>后处理</strong>：应用任何额外的后处理效果，如模糊、色调映射或抗锯齿。</li><li><strong>显示</strong>：将最终的颜色缓冲输出到屏幕上。</li></ol><h4 id="实现详细信息"><a href="#实现详细信息" class="headerlink" title="实现详细信息"></a>实现详细信息</h4><p>在前向渲染中，场景中一些（最多 4 个）最亮的灯光将进行逐像素渲染，然后最多 4 个点光源进行逐顶点光照，其他的灯光则使用球谐函数(Spherical Harmonics)进行计算。光源是否为每像素光源根据以下原则而定：</p><ul><li>Render Mode 设置为 Not Important 的光源始终为每顶点或 SH 光源。</li><li>最亮的方向光始终为每像素光源。</li><li>Render Mode 设置为 Important 的光源始终为每像素光源。</li><li>如果上面的灯光数量还没达到 QualitySetting 中设置的逐像素灯光的上限时，则其他灯光以亮度优先的方式优先作为逐像素灯光使用。</li></ul><p>每个对象的渲染按如下方式进行：</p><ul><li>基础通道应用一个每像素方向光和所有每顶点&#x2F;SH 光源。</li><li>其他每像素光源在额外的通道中渲染（每个光源对应一个通道）。</li></ul><p><strong>Base Pass</strong></p><p>基础通道使用一个每像素方向光和所有 SH&#x2F;每顶点光源来渲染对象。此通道还会添加着色器中的所有光照贴图、环境光照和发射光照。在此通道中渲染的方向光可以具有阴影。请注意，光照贴图的对象不会从 SH 光源获得光照。</p><p>请注意，在着色器中使用“OnlyDirectional”通道标志时，前向基础通道仅渲染主方向光、环境光&#x2F;光照探针和光照贴图（SH 和顶点光源不包括在通道数据中）。</p><p><strong>Additional Passes</strong></p><p>对于影响此对象的每个额外的每像素光源，需要额外的渲染通道。默认情况下，这些通道中的光源没有阴影（因此在结果中，前向渲染支持一个带阴影的方向光），除非使用 multi_compile_fwdadd_fullshadows 变体快捷方式。</p><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><p>简单易懂，易于调试和优化，适合光源数量较少的场景。</p><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><p>采用前向渲染方式渲染实时光源会非常消耗资源,当场景中光源数量较多时，会导致大量的渲染 Pass，从而降低渲染效率,可以设置每像素渲染的光源数量,其余光源会以较低保真度渲染。</p><h4 id="性能注意事项"><a href="#性能注意事项" class="headerlink" title="性能注意事项"></a>性能注意事项</h4><ol><li>应使用光照贴图实现静态对象的光照，而不是每帧计算其光照。每顶点动态光照可能会为顶点变换增加显著的工作量，因此尽量避免多个光源照射单个对象的情况。</li><li>避免组合距离足够远而需要受到不同像素光照影响的网格。通常情况下，为渲染组合对象而必须创建的 pass 数为每个单独对象的 pass 数之和，因此进行网格组合并不会获得任何好处。</li><li>在渲染过程中应该更具光源是否重要选择合适的 Render Mode 设置</li></ol><h4 id="Spherical-Harmonics-lights"><a href="#Spherical-Harmonics-lights" class="headerlink" title="Spherical Harmonics lights"></a>Spherical Harmonics lights</h4><p>球谐函数(Spherical Harmonics) 灯光的渲染速度非常快。 它们在 CPU 上的开销很小，实际上对 GPU 来说是无额外开销的（也就是说，base pass 总是计算 SH 光照；但由于 SH 光照的工作方式，无论多少 SH 光照，成本都是完全相同的）。 SH 灯光的缺点： - 由于 SH 是在逐顶点计算的，不是逐像素，这也意味着它们不支持灯光 Cookies 或法线贴图。 - SH 照明的频率非常低。 使用 SH 灯无法实现锐利的照明过渡。它们也只影响漫反射照明。 - SH 照明不是局部的； 靠近某个表面的点或点 SH 灯会“看起来不对”。</p><p>总而言之，SH 灯通常对于小的动态物体来说已经足够了。</p><h3 id="延迟渲染（Deferred-Rendering）"><a href="#延迟渲染（Deferred-Rendering）" class="headerlink" title="延迟渲染（Deferred Rendering）"></a>延迟渲染（Deferred Rendering）</h3><h4 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h4><p>延迟渲染是一种高效的渲染技术，特别适合处理大量光源的场景。在第一阶段，它只渲染几何体的深度、法线和颜色信息到所谓的 G-Buffer 中。在第二阶段，它从 G-Buffer 中读取信息，并为每个光源计算光照效果。</p><h4 id="流程概述"><a href="#流程概述" class="headerlink" title="流程概述"></a>流程概述</h4><ol><li><strong>初始化</strong>：清空帧缓冲，包括 G-Buffer（几何缓存）和深度缓冲。</li><li><strong>几何遍历</strong>：仅使用顶点着色器和片段着色器处理几何体，但不计算光照。<ul><li>将顶点数据转换到屏幕空间，并将颜色、法线、深度等信息写入 G-Buffer。</li></ul></li><li><strong>光照遍历</strong>：<ul><li>对于场景中的每个光源，使用 G-Buffer 中的信息计算光照效果。</li><li>将光照结果累加到累积缓冲中。</li></ul></li><li><strong>后处理</strong>：应用后处理效果，如抗锯齿、色调映射等。</li><li><strong>显示</strong>：将最终的累积缓冲输出到屏幕上。</li></ol><h4 id="实现详细信息-1"><a href="#实现详细信息-1" class="headerlink" title="实现详细信息"></a>实现详细信息</h4><p>在延迟渲染完成后，再使用前向渲染路径渲染那些不支持延迟着色的 Shader 对象。 在 G-buffer 中渲染目标（RT0-RT4）的默认布局如下，数据类型存储在每个渲染目标的不同通道中：</p><ul><li>RT0, ARGB32 格式：漫反射颜色（RGB）, 遮挡（A）。</li><li>RT1, ARGB32 格式：高光颜色（RGB）, 粗糙度(A)。</li><li>RT2, ARGB2101010 格式：世界空间下的法线，未被使用（A）。</li><li>RT3, ARGB2101010(非 HDR)或 ARGBHalf(HDR 每个通道 16bits)格式：自发光+光照+lightmap+反射探针+深度缓冲和模板缓冲。</li><li>深度+模板缓冲区。</li></ul><p>所以默认的 G-buffer 布局每个像素是 160bits&#x2F;pixel(非 HDR)或 192bits&#x2F;pixel(HDR)。 如果对于混合光照使用了 Shadowmask 或 Distance Shadowmask 模式,那么第 5 个渲染目标（RT）将被使用：</p><ul><li>RT4, ARGB32 格式：灯光遮挡值（RGBA）</li></ul><p>因此 G-buffer 布局大小将增至每个 192bits&#x2F;pixel(非 HDR)或 224bits&#x2F;pixel(HDR)。</p><p>如果硬件不支持五个并发渲染目标，则使用阴影遮罩的对象将回退到前向渲染路径。 当摄像机不使用 HDR 时，发射+光照缓冲区 (RT3) 采用对数编码，因此提供的动态范围高于 ARGB32 纹理通常可能提供的范围。</p><p>请注意，当摄像机使用 HDR 渲染时，不会为发射 + 光照缓冲区 (RT3) 创建单独的渲染目标；而是将摄像机渲染到的渲染目标（即传递给图像效果的渲染目标）用作 RT3。</p><h4 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h4><p>可以高效处理大量光源，减少渲染 Pass 的数量，提高性能。光照的处理开销与灯光照亮的像素数成正比。光照的开销是由场景中的光量大小决定的不管它照亮了多少游戏对象。</p><h4 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h4><p>需要额外的内存来存储 G-Buffer,需要 GPU 支持,不适用于半透明对象、正交投影、硬件抗锯齿的场景（可以同通过后处理解决-边缘查找做模糊）。此外，它也不支持网格渲染器 (Mesh Renderer) 的接受阴影 (Receive Shadows) 标志，并且仅在有限程度上支持剔除遮罩。最多只能使用四个剔除遮罩。</p><h4 id="性能注意事项-1"><a href="#性能注意事项-1" class="headerlink" title="性能注意事项"></a>性能注意事项</h4><p>在延迟渲染中实时灯光的性能消耗跟这个灯光所照明的像素是成正比的，不依赖于场景的复杂度。所以小的 Point 光源或 Spot 光源的性能消耗是非常低的，如果照明对象是被其他对象部分或完全遮挡的那么它的性能开销将更小。</p><p>当然，带阴影的灯光是比不带阴影的灯光更耗的。在延迟着色中，对于每个投射阴影的灯光，阴影投射对象还是需要被渲染一次或多次。</p><h4 id="G-缓冲区通道"><a href="#G-缓冲区通道" class="headerlink" title="G 缓冲区通道"></a>G 缓冲区通道</h4><p>G 缓冲区通道将每个游戏对象渲染一次。漫射和镜面反射颜色、表面平滑度、世界空间法线和发射+环境+反射+光照贴图都将渲染到 G 缓冲区纹理中。G 缓冲区纹理设置为全局着色器属性供着色器以后访问（_CameraGBufferTexture0 .._CameraGBufferTexture3 指定）。</p><h4 id="光照通道"><a href="#光照通道" class="headerlink" title="光照通道"></a>光照通道</h4><p>光照 pass 根据 G-Buffer 和深度来计算光照。光照是在屏幕空间内计算的，因此处理所需的时间与场景复杂性无关。光照将添加到发射缓冲区。</p><p>不穿过相机近平面的点光源和聚光灯被渲染为 3D 形状，并启用了 Z 缓冲区针对场景的测试。 这使得部分或完全遮挡的点光源和聚光灯的渲染成本非常低。穿过近平面的方向光和点或聚光灯被渲染为全屏四边形。</p><p>如果光源启用了阴影，那么也会在此通道中渲染并应用阴影。请注意，阴影并非是“无成本”的；需要渲染阴影投射物，并且必须应用更复杂的光照着色器。</p><p>唯一可用的光照模型是标准 (Standard) 光照模型。如果需要不同的模型，可修改光照 pass 着色器，方法是将内置着色器中的 Internal-DeferredShading.shader 文件的修改版本放入“Assets”文件夹中名为“Resources”的文件夹内。然后打开 Graphics 设置（菜单：Edit &gt; Project Settings，然后单击 Graphics 类别）。将“Deferred”下拉选单改为“Custom Shader”。然后，更改当前使用的着色器对应的着色器 (Shader) 选项。</p><h2 id="使用-CommandBuffer-来扩展内置渲染管线"><a href="#使用-CommandBuffer-来扩展内置渲染管线" class="headerlink" title="使用 CommandBuffer 来扩展内置渲染管线"></a>使用 CommandBuffer 来扩展内置渲染管线</h2><blockquote><p>&#x2F;&#x2F;TODO<br><a href="https://docs.unity3d.com/cn/2021.3/Manual/GraphicsCommandBuffers.html">使用 CommandBuffer 来扩展内置渲染管线</a></p></blockquote><h2 id="内置渲染管线的硬件要求"><a href="#内置渲染管线的硬件要求" class="headerlink" title="内置渲染管线的硬件要求"></a>内置渲染管线的硬件要求</h2><p>详细信息参考文档<a href="https://docs.unity3d.com/cn/2021.3/Manual/RenderTech-HardwareRequirements.html">内置渲染管线的硬件要求</a></p><h1 id="SRP"><a href="#SRP" class="headerlink" title="SRP"></a>SRP</h1><p>Unity 的可编程渲染管线 (Scriptable Render Pipeline, SRP) 是一项可以通过 C# 脚本来控制渲染的功能。SRP 技术可以强化通用渲染管线 (URP) 和高清渲染管线 (HDRP)。</p><p>可编程渲染管线是一个轻量级 API 层，允许使用 C# 脚本来调度和配置渲染命令。Unity 将这些命令传递给它的低级图形架构，后者随后将指令发送给图形 API。URP 和 HDRP 是基于 SRP 技术开发的，我们也可以基于 SRP 自定义渲染管线</p><h2 id="渲染管线实例和渲染管线资源"><a href="#渲染管线实例和渲染管线资源" class="headerlink" title="渲染管线实例和渲染管线资源"></a>渲染管线实例和渲染管线资源</h2><p>每个基于 SRP 的渲染管线都有两个关键的自定义元素：</p><ul><li><strong>渲染管线实例</strong> 这是定义渲染管线功能的类的实例。它的脚本继承自 RenderPipeline 并覆盖其 Render() 方法。</li><li><strong>渲染管线资源</strong> 这是 Unity 项目中的一项资源，用于存储有关所使用的渲染管线实例以及如何对其进行配置的数据。它的脚本继承自 RenderPipelineAsset 并覆盖其 CreatePipeline() 方法。</li></ul><h2 id="ScriptableRenderContext"><a href="#ScriptableRenderContext" class="headerlink" title="ScriptableRenderContext"></a>ScriptableRenderContext</h2><p>ScriptableRenderContext 是一个类，用作渲染管线中的自定义 C# 代码与 Unity 的低级图形代码之间的接口。</p><h2 id="入口点和回调"><a href="#入口点和回调" class="headerlink" title="入口点和回调"></a>入口点和回调</h2><p>使用 SRP 时，使用它们可让 Unity 在特定时间调用您的 C# 代码。</p><ul><li><strong>RenderPipeline.Render</strong> 是 SRP 的主要入口点。Unity 会自动调用此方法。如果要编写自定义渲染管线，这就是开始编写代码的地方。</li><li><strong>RenderPipelineManager</strong> 类有以下事件可供您订阅，以便您可以在渲染循环中的特定点执行自定义代码：<ul><li>beginFrameRendering (将产生 GC 使用 beginContextRendering 代替)</li><li>endFrameRendering (将产生 GC 使用 endContextRendering 代替)</li><li>beginContextRendering</li><li>endContextRendering</li><li>beginCameraRendering</li><li>endCameraRendering</li></ul></li></ul><h2 id="在-SRP-中调度和执行渲染命令"><a href="#在-SRP-中调度和执行渲染命令" class="headerlink" title="在 SRP 中调度和执行渲染命令"></a>在 SRP 中调度和执行渲染命令</h2><p>在 SRP 中，应使用 C# 脚本来配置和调度渲染命令。然后，需要告诉 Unity 的低级图形架构执行这些命令，此过程会将指令发送到图形 API。</p><p>主要做法是对 ScriptableRenderContext 进行 API 调用，不过也可以立即执行 CommandBuffers。</p><h3 id="使用-ScriptableRenderContext-API"><a href="#使用-ScriptableRenderContext-API" class="headerlink" title="使用 ScriptableRenderContext API"></a>使用 ScriptableRenderContext API</h3><p>在 SRP 中，ScriptableRenderContext 类用作 C# 渲染管线代码与 Unity 的低级图形代码之间的接口。SRP 使用延迟执行的方式来实现渲染；需要使用 ScriptableRenderContext 来构建渲染命令列表，然后告诉 Unity 执行这些命令。Unity 的低级图形架构随后将指令发送到图形 API。</p><p>要调度渲染命令，可以：</p><ul><li>使用 ScriptableRenderContext.ExecuteCommandBuffer 将 CommandBuffers 传递到 ScriptableRenderContext</li><li>对可编程渲染上下文进行直接 API 调用（例如 ScriptableRenderContext.Cull 或 ScriptableRenderContext.DrawRenderers）</li></ul><p>当安排好命令后，通过调用 ScriptableRenderContext.Submit 方法告诉 Unity 去执行你的命令。注意：不论你是使用的 CommandBuffer 还是直接调用的 API, 在 ScriptableRenderContext 中都同样的，并且也只有调用了 Submit 才会去执行。</p><p>以下示例代码演示如何使用 CommandBuffer 来调度和执行命令以清除当前渲染目标。</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipeline</span> : <span class="title">RenderPipeline</span></span><br><span class="line">&#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">ExampleRenderPipeline</span>()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">Render</span>(<span class="params">ScriptableRenderContext context, Camera[] cameras</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建并调度命令以清除当前渲染目标</span></span><br><span class="line">        <span class="keyword">var</span> cmd = <span class="keyword">new</span> CommandBuffer();</span><br><span class="line">        cmd.ClearRenderTarget(<span class="literal">true</span>, <span class="literal">true</span>, Color.red);</span><br><span class="line">        context.ExecuteCommandBuffer(cmd);</span><br><span class="line">        cmd.Release();</span><br><span class="line"></span><br><span class="line">         <span class="comment">// 告诉Unity执行命令</span></span><br><span class="line">        context.Submit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="立即执行-CommandBuffers"><a href="#立即执行-CommandBuffers" class="headerlink" title="立即执行 CommandBuffers"></a>立即执行 CommandBuffers</h3><p>可通过调用 Graphics.ExecuteCommandBuffer 来立即执行 CommandBuffers，而不使用 ScriptableRenderContext。对该 API 的调用发生在渲染管线之外。</p><h1 id="自定义渲染管线"><a href="#自定义渲染管线" class="headerlink" title="自定义渲染管线"></a>自定义渲染管线</h1><p><a href="https://docs.unity3d.com/cn/2021.3/Manual/srp-custom.html">创建自定义渲染管线</a></p><p>Unity 提供两种根据 SRP 预构建的渲染管线：高清渲染管线 (HDRP) 和通用渲染管线 (URP)。HDRP 和 URP 提供广泛的定制选项；但是，如果需要更多地控制渲染管线，可以基于 SRP 创建自己的自定义渲染管线。</p><h2 id="基于-SRP-创建自定义渲染管线"><a href="#基于-SRP-创建自定义渲染管线" class="headerlink" title="基于 SRP 创建自定义渲染管线"></a>基于 SRP 创建自定义渲染管线</h2><h3 id="创建新项目并安装自定义渲染管线所需的包"><a href="#创建新项目并安装自定义渲染管线所需的包" class="headerlink" title="创建新项目并安装自定义渲染管线所需的包"></a>创建新项目并安装自定义渲染管线所需的包</h3><p>自定义渲染需要使用 SRP Core 包来创建。SRP Core 是 Unity 创建的包，其中包含可复用代码来帮助您创建自己的渲染管线，包括用于与平台特定的图形 API 结合使用的样板代码、用于常见渲染操作的实用函数以及供 URP 和 HDRP 使用的着色器库。有关 SRP Core 的更多信息，请参阅 <a href="https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@17.0/manual/index.html">SRP Core 包文档</a>。</p><ol><li><p>创建一个新的 Unity 项目</p></li><li><p>在<a href="https://github.com/Unity-Technologies/Graphics">SRP 源码仓库</a>中下载对应 Unity 版本的 SRP 包。</p></li><li><p>将下载的包按序放到工程中</p><ul><li>com.unity.render-pipelines.core : SRP 的核心包</li><li>com.unity.render-pipelines.shadergraph ：（可选）一个可视化的 shader 编辑器</li><li>com.unity.render-pipelines.visualeffectgraph ：（可选）视觉效果图像编辑器</li></ul></li></ol><h3 id="创建自定义版本的-URP-或-HDRP"><a href="#创建自定义版本的-URP-或-HDRP" class="headerlink" title="创建自定义版本的 URP 或 HDRP"></a>创建自定义版本的 URP 或 HDRP</h3><p>通用渲染管线 (URP) 和高清渲染管线 (HDRP) 提供广泛的自定义选项，可帮助您获得所需的图形和性能。但是，如果您希望获得更多控制权，可为这些渲染管线之一创建自定义版本，并修改源代码。分别需要安装以下包体：</p><p><strong>URP：</strong></p><ul><li>com.unity.render-pipelines.shadergraph</li><li>com.unity.render-pipelines.core</li><li>com.unity.render-pipelines.universal</li></ul><p><strong>HDRP:</strong></p><ul><li>com.unity.render-pipelines.core</li><li>com.unity.render-pipelines.shadergraph</li><li>com.unity.render-pipelines.high-defintion</li></ul><h2 id="在自定义渲染管线中创建渲染管线资源和渲染管线实例"><a href="#在自定义渲染管线中创建渲染管线资源和渲染管线实例" class="headerlink" title="在自定义渲染管线中创建渲染管线资源和渲染管线实例"></a>在自定义渲染管线中创建渲染管线资源和渲染管线实例</h2><p>如果创建基于 SRP 的自定义渲染管线，项目必须包含：</p><ul><li>一个继承自 RenderPipelineAsset 并覆盖其 CreatePipeline() 方法的脚本。此脚本用于定义渲染管线资源。</li><li>一个继承自 RenderPipeline 并覆盖其 Render() 方法的脚本。此脚本定义渲染管线实例，是编写自定义渲染代码的地方。</li><li>一个从 RenderPipelineAsset 脚本创建的渲染管线资源。此资源充当渲染管线实例的工厂类。</li></ul><p>因为这些元素非常紧密相关，所以应该同时创建它们。</p><h3 id="创建基本渲染管线资源和渲染管线实例"><a href="#创建基本渲染管线资源和渲染管线实例" class="headerlink" title="创建基本渲染管线资源和渲染管线实例"></a>创建基本渲染管线资源和渲染管线实例</h3><p>示例代码如下：</p><p><strong>渲染管线资源脚本</strong></p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line">[<span class="meta">CreateAssetMenu(menuName = <span class="string">&quot;Rendering/ExampleRenderPipelineAsset&quot;</span>)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipelineAsset</span> : <span class="title">RenderPipelineAsset</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// Unity 在渲染第一帧之前调用此方法。</span></span><br><span class="line">    <span class="comment">// 如果渲染管线资源上的设置改变，Unity 将销毁当前的渲染管线实例，并在渲染下一帧之前再次调用此方法。</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> RenderPipeline <span class="title">CreatePipeline</span>()</span> &#123;</span><br><span class="line">        <span class="comment">// 实例化此自定义 SRP 用于渲染的渲染管线。</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ExampleRenderPipelineInstance();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>渲染管线实例脚本</strong></p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipelineInstance</span> : <span class="title">RenderPipeline</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ExampleRenderPipelineInstance</span>()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">Render</span> (<span class="params">ScriptableRenderContext context, Camera[] cameras</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// 可以在此处编写自定义渲染代码。渲染主入口</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>渲染管线资源</strong></p><p><img src="/../img/RenderPipeline/CreatePipelineAsset.png" alt="CreatePipelineAsset"></p><p>创建出如下资源文件</p><p><img src="/../img/RenderPipeline/RenderPipelineAsset.png" alt="RenderPipelineAsset"></p><h3 id="创建可配置的渲染管线资源和实例"><a href="#创建可配置的渲染管线资源和实例" class="headerlink" title="创建可配置的渲染管线资源和实例"></a>创建可配置的渲染管线资源和实例</h3><p>默认情况下，渲染管线资源存储关于用于渲染的渲染管线实例以及默认材质和着色器的信息。 在 RenderPipelineAsset 脚本中，可以扩展您的渲染管线资源，以便它存储额外的数据，并且可以在项目中拥有多个具有不同配置的不同渲染管线资源。例如，可以使用渲染管线资源来保存每个不同硬件层的配置数据。 高清渲染管线 (HDRP) 和通用渲染管线 (URP) 包括这方面的示例。</p><p>以下示例展示了如何创建一个 RenderPipelineAsset 脚本，该脚本定义一个带有公共数据的渲染管线资源，可以在 Inspector 窗口为每个实例设置这些数据，以及在其构造函数中接收渲染管线资源并使用这些数据。</p><p>实例代码如下：</p><p><strong>渲染资源脚本</strong></p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line">[<span class="meta">CreateAssetMenu(menuName = <span class="string">&quot;Rendering/ExampleRenderPipelineAsset&quot;</span>)</span>]</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipelineAsset</span> : <span class="title">RenderPipelineAsset</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 可以在 Inspector 中为每个渲染管线资源定义数据</span></span><br><span class="line">    <span class="keyword">public</span> Color exampleColor;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">string</span> exampleString;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> RenderPipeline <span class="title">CreatePipeline</span>()</span> &#123;</span><br><span class="line">        <span class="comment">// 实例化此自定义 SRP 用于渲染的渲染管线，然后传递对此渲染管线资源的引用。</span></span><br><span class="line">        <span class="comment">// 然后，渲染管线实例可以访问上方定义的配置数据。</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ExampleRenderPipelineInstance(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>渲染实例脚本</strong></p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipelineInstance</span> : <span class="title">RenderPipeline</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 使用此变量来引用传递给构造函数的渲染管线资源</span></span><br><span class="line">    <span class="keyword">private</span> ExampleRenderPipelineAsset renderPipelineAsset;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ExampleRenderPipelineInstance</span>(<span class="params">ExampleRenderPipelineAsset asset</span>)</span> &#123;</span><br><span class="line">        renderPipelineAsset = asset;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">Render</span>(<span class="params">ScriptableRenderContext context, Camera[] cameras</span>)</span> &#123;</span><br><span class="line">        <span class="comment">// 这是使用渲染管线资源数据的示例。</span></span><br><span class="line">        Debug.Log(renderPipelineAsset.exampleString);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>渲染管线资源</strong></p><p>创建出如下资源文件</p><p><img src="/../img/RenderPipeline/RenderPipelineAsset2.png" alt="RenderPipelineAsset2"></p><h2 id="在自定义渲染管线中创建简单渲染循环"><a href="#在自定义渲染管线中创建简单渲染循环" class="headerlink" title="在自定义渲染管线中创建简单渲染循环"></a>在自定义渲染管线中创建简单渲染循环</h2><p>渲染循环是在单个帧中发生的所有渲染操作的术语。此节代码示例说明使用 SRP 的基础概念。你能使用这些信息去构建自己的自定可编程渲染管线，也可以用于理解 SRP 是如何工作的。</p><h3 id="准备项目"><a href="#准备项目" class="headerlink" title="准备项目"></a>准备项目</h3><p>步骤如下所示：</p><ol><li>创建与 SRP 兼容的着色器。</li><li>创建一个或多个要渲染的游戏对象。</li><li>创建自定义 SRP 的基本结构。</li><li>可选：如果计划扩展简单自定义 SRP 以添加更复杂的功能，需要安装 SRP Core 包。SRP Core 包中包含 SRP Core 着色器库（可以用于使着色器与 SRP Batcher 兼容）以及用于常见操作的实用程序函数。有关更多信息，请参阅 <a href="https://docs.unity3d.com/Packages/com.unity.render-pipelines.core@17.0/manual/index.html">SRP Core 包文档</a>。</li></ol><h3 id="创建-Shader"><a href="#创建-Shader" class="headerlink" title="创建 Shader"></a>创建 Shader</h3><p>在可编程渲染管线中，使用 LightMode 通道标签确定如何绘制几何体。有关通道标签的更多信息，请参阅 ShaderLab：<a href="https://docs.unity3d.com/cn/2021.3/Manual/SL-PassTags.html">向通道分配标签</a>。</p><p>此任务演示如何创建非常简单的无光照 Shader 对象，其 LightMode 通道标签值为 ExampleLightModeTag。</p><p>新建 Shader 名为 SimpleUnlitColor,代码如下：</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">// 这定义一个与自定义可编程渲染管线兼容的简单无光照 Shader 对象。</span><br><span class="line">// 它应用硬编码颜色，并演示 LightMode 通道标签的使用。</span><br><span class="line">// 它不与 SRP Batcher 兼容。</span><br><span class="line"></span><br><span class="line">Shader &quot;Examples/SimpleUnlitColor&quot;</span><br><span class="line">&#123;</span><br><span class="line">    SubShader</span><br><span class="line">    &#123;</span><br><span class="line">        Pass</span><br><span class="line">        &#123;</span><br><span class="line">            // LightMode 通道标签的值必须与 ScriptableRenderContext.DrawRenderers 中的 ShaderTagId 匹配</span><br><span class="line">            Tags &#123; &quot;LightMode&quot; = &quot;ExampleLightModeTag&quot;&#125;</span><br><span class="line"></span><br><span class="line">            HLSLPROGRAM</span><br><span class="line">            #pragma vertex vert</span><br><span class="line">            #pragma fragment frag</span><br><span class="line"></span><br><span class="line">    float4x4 unity_MatrixVP;</span><br><span class="line">            float4x4 unity_ObjectToWorld;</span><br><span class="line"></span><br><span class="line">            struct Attributes</span><br><span class="line">            &#123;</span><br><span class="line">                float4 positionOS   : POSITION;</span><br><span class="line">            &#125;;</span><br><span class="line"></span><br><span class="line">            struct Varyings</span><br><span class="line">            &#123;</span><br><span class="line">                float4 positionCS : SV_POSITION;</span><br><span class="line">            &#125;;</span><br><span class="line"></span><br><span class="line">            Varyings vert (Attributes IN)</span><br><span class="line">            &#123;</span><br><span class="line">                Varyings OUT;</span><br><span class="line">                float4 worldPos = mul(unity_ObjectToWorld, IN.positionOS);</span><br><span class="line">                OUT.positionCS = mul(unity_MatrixVP, worldPos);</span><br><span class="line">                return OUT;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            float4 frag (Varyings IN) : SV_TARGET</span><br><span class="line">            &#123;</span><br><span class="line">                return float4(0.5,1,0.5,1);</span><br><span class="line">            &#125;</span><br><span class="line">            ENDHLSL</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="创建渲染对象"><a href="#创建渲染对象" class="headerlink" title="创建渲染对象"></a>创建渲染对象</h3><p>创建一个东西进行渲染,使用材质附带 Shader 作用于该物体。</p><p><img src="/../img/RenderPipeline/SimpleAssets.png" alt="SimpleAsset"></p><h3 id="创建自定义-SRP-的基本结构"><a href="#创建自定义-SRP-的基本结构" class="headerlink" title="创建自定义 SRP 的基本结构"></a>创建自定义 SRP 的基本结构</h3><p>前面章节所描述的步骤：</p><ol><li>创建渲染管线资源和渲染管线实例脚本</li><li>将当前创建的管线资源设置为激活的渲染管线</li></ol><h3 id="创建渲染循环"><a href="#创建渲染循环" class="headerlink" title="创建渲染循环"></a>创建渲染循环</h3><p>在简单渲染循环中，基本操作有：</p><ul><li>清除渲染目标，移除在最后一帧期间绘制的几何体。</li><li>剔除，过滤掉对摄像机不可见的几何体。</li><li>绘制，向 GPU 告知要绘制的几何体以及如何进行绘制。</li></ul><h4 id="清除"><a href="#清除" class="headerlink" title="清除"></a>清除</h4><p>清除意味着移除在最后一帧期间绘制的内容。渲染目标通常是屏幕；但是，也可以渲染到纹理以创建“画中画”效果。</p><p>要清除可编程渲染管线中的渲染目标，请执行以下操作：</p><ol><li>使用 Clear 命令配置 CommandBuffer。</li><li>将 CommandBuffer 添加到 ScriptableRenderContext 上的命令队列；为此，请调用 ScriptableRenderContext.ExecuteCommandBuffer。</li><li>指示图形 API 执行 ScriptableRenderContext 上的命令队列；为此，请调用 ScriptableRenderContext.Submit。</li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipeline</span> : <span class="title">RenderPipeline</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ExampleRenderPipeline</span>()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">Render</span> (<span class="params">ScriptableRenderContext context, Camera[] cameras</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> cmd = <span class="keyword">new</span> CommandBuffer();</span><br><span class="line">        cmd.ClearRenderTarget(<span class="literal">true</span>, <span class="literal">true</span>, Color.black);</span><br><span class="line">        context.ExecuteCommandBuffer(cmd);</span><br><span class="line">        cmd.Release();</span><br><span class="line"></span><br><span class="line">        context.Submit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="剔除"><a href="#剔除" class="headerlink" title="剔除"></a>剔除</h4><p>剔除是过滤掉对摄像机不可见的几何体的过程。</p><p>要在可编程渲染管线中进行剔除，请执行以下操作：</p><ul><li>使用有关摄像机的数据填充 ScriptableCullingParameters 结构；为此，请调用 Camera.TryGetCullingParameters。</li><li>可选：手动更新 ScriptableCullingParameters 结构的值。</li><li>调用 ScriptableRenderContext.Cull，并将结果存储在一个 CullingResults 结构中。</li></ul><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipeline</span> : <span class="title">RenderPipeline</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ExampleRenderPipeline</span>()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">Render</span> (<span class="params">ScriptableRenderContext context, Camera[] cameras</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> cmd = <span class="keyword">new</span> CommandBuffer();</span><br><span class="line">        cmd.ClearRenderTarget(<span class="literal">true</span>, <span class="literal">true</span>, Color.black);</span><br><span class="line">        context.ExecuteCommandBuffer(cmd);</span><br><span class="line">        cmd.Release();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历所有相机</span></span><br><span class="line">        <span class="keyword">foreach</span> (Camera camera <span class="keyword">in</span> cameras)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 获取剔除参数</span></span><br><span class="line">            camera.TryGetCullingParameters(<span class="keyword">out</span> <span class="keyword">var</span> cullingParameters);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 剔除并存储剔除结果</span></span><br><span class="line">            <span class="keyword">var</span> cullingResults = context.Cull(<span class="keyword">ref</span> cullingParameters);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通知图像API执行命令</span></span><br><span class="line">        context.Submit();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="绘制"><a href="#绘制" class="headerlink" title="绘制"></a>绘制</h4><p>绘图是指示图形 API 以给定设置绘制一组给定几何图形的过程。在 SRP 中的绘制应有以下步骤：</p><ol><li>执行剔除（上一节描述的），并存储结果在 CullingResults 结构体中。</li><li>创建并且配置 FilteringSetting 结构体，此结构描述了如果去过滤剔除结果。</li><li>创建并配置 DrawingSettings 结构体， 此结构描述哪些几何体将被绘制以及如何绘制它。</li><li>可以选步骤，默认 Unity 基于 Shader 对象设置渲染状态(深度，蒙版和模板等)。如果你想对一些或所有打算绘制的几何对象覆写这些渲染状态，你能使用 RenderStateBlock 结构体去做这件事情。</li><li>调用 ScriptableRenderContext.DrawRenderers，并传递上面创建的结构体对象。Unity 将根据上面的设置的信息进行绘制。</li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> UnityEngine.Rendering;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ExampleRenderPipeline</span> : <span class="title">RenderPipeline</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">ExampleRenderPipeline</span>()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">override</span> <span class="keyword">void</span> <span class="title">Render</span> (<span class="params">ScriptableRenderContext context, Camera[] cameras</span>)</span> &#123;</span><br><span class="line">        <span class="keyword">var</span> cmd = <span class="keyword">new</span> CommandBuffer();</span><br><span class="line">        cmd.ClearRenderTarget(<span class="literal">true</span>, <span class="literal">true</span>, Color.black);</span><br><span class="line">        context.ExecuteCommandBuffer(cmd);</span><br><span class="line">        cmd.Release();</span><br><span class="line"></span><br><span class="line">       <span class="comment">// 遍历所有相机</span></span><br><span class="line">        <span class="keyword">foreach</span> (Camera camera <span class="keyword">in</span> cameras)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">// 获取剔除参数</span></span><br><span class="line">            camera.TryGetCullingParameters(<span class="keyword">out</span> <span class="keyword">var</span> cullingParameters);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 剔除并存储剔除结果</span></span><br><span class="line">            <span class="keyword">var</span> cullingResults = context.Cull(<span class="keyword">ref</span> cullingParameters);</span><br><span class="line"></span><br><span class="line"><span class="comment">//设置视图、投影和剪切平面全局着色器变量。</span></span><br><span class="line">            context.SetupCameraProperties(camera);</span><br><span class="line"></span><br><span class="line"><span class="comment">//根据 LightMode Pass 标签值，告诉Unity要绘制哪个几何图形</span></span><br><span class="line">            ShaderTagId shaderTagId = <span class="keyword">new</span> ShaderTagId(<span class="string">&quot;ExampleLightModeTag&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 告诉Unity，基于当前相机该如何去排序</span></span><br><span class="line">            <span class="keyword">var</span> sortingSettings = <span class="keyword">new</span> SortingSettings(camera);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建用于描述需要绘制的几何体，以及如何绘制他们</span></span><br><span class="line">            DrawingSettings drawingSettings = <span class="keyword">new</span> DrawingSettings(shaderTagId, sortingSettings);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 告诉Unity如何去过滤裁剪的结果，进一步指定要绘制的几何体</span></span><br><span class="line"><span class="comment">// 使用FilteringSettings.defaultValue指定不进行过滤</span></span><br><span class="line">            FilteringSettings filteringSettings = FilteringSettings.defaultValue;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 基于前面的设置绘制几何体</span></span><br><span class="line">            context.DrawRenderers(cullingResults, <span class="keyword">ref</span> drawingSettings, <span class="keyword">ref</span> filteringSettings);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 安排天空盒子的绘制</span></span><br><span class="line">            <span class="keyword">if</span> (camera.clearFlags == CameraClearFlags.Skybox &amp;&amp; RenderSettings.skybox != <span class="literal">null</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                context.DrawSkybox(camera);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 提交执行</span></span><br><span class="line">            context.Submit();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h4><p>生成资源文件<br><img src="/../img/RenderPipeline/Ex3Assets.png" alt="Ex3Assets"></p><p>在 ProjectSettings 中启用当前管线资源<br><img src="/../img/RenderPipeline/Ex3Settings.png" alt="Ex3Settings"></p><p>观察 Scene 视图,渲染结果如下:<br><img src="/../img/RenderPipeline/Result.png" alt="Result"></p><h1 id="URP"><a href="#URP" class="headerlink" title="URP"></a>URP</h1><p><a href="https://docs.unity3d.com/cn/Packages/com.unity.render-pipelines.universal@12.1/manual/index.html">通用渲染管线</a></p><h1 id="HDRP"><a href="#HDRP" class="headerlink" title="HDRP"></a>HDRP</h1><p><a href="https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.0/manual/index.html">高清渲染管线</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;渲染管线简介&quot;&gt;&lt;a href=&quot;#渲染管线简介&quot; class=&quot;headerlink&quot; title=&quot;渲染管线简介&quot;&gt;&lt;/a&gt;渲染管线简介&lt;/h1&gt;&lt;p&gt;渲染管线是计算机图形学中用于将 3D 模型转换为 2D 图像的一系列处理步骤。这个过程从读取 3D 模型的数</summary>
      
    
    
    
    
    <category term="Unity" scheme="http://example.com/tags/Unity/"/>
    
  </entry>
  
  <entry>
    <title>UGUI基础知识</title>
    <link href="http://example.com/2024/07/30/UGUI%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    <id>http://example.com/2024/07/30/UGUI%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/</id>
    <published>2024-07-30T02:14:26.000Z</published>
    <updated>2024-08-01T07:46:26.000Z</updated>
    
    <content type="html"><![CDATA[<p>Unity UI（用户界面）是 Unity 引擎中用于创建游戏和应用中的交互式界面的系统。它允许开发者设计、构建和管理 2D 和 3D 游戏内的菜单、HUD（抬头显示）、按钮、滑块、文本框等元素。</p><h1 id="Unity-UI-的主要组成部分"><a href="#Unity-UI-的主要组成部分" class="headerlink" title="Unity UI 的主要组成部分"></a>Unity UI 的主要组成部分</h1><h2 id="Canvas（画布）"><a href="#Canvas（画布）" class="headerlink" title="Canvas（画布）"></a>Canvas（画布）</h2><p>Canvas 是所有 UI 元素的容器。你可以有多个 Canvas，每个 Canvas 可以有自己的渲染模式（Screen Space-Overlay, Screen Space-Camera 或 World Space）和相机设置。画布 使用 <strong>EventSystem</strong> 对象来协助消息系统。</p><h3 id="元素绘制顺序"><a href="#元素绘制顺序" class="headerlink" title="元素绘制顺序"></a>元素绘制顺序</h3><p>画布中的 UI 元素按照它们在 Hierarchy 中显示的顺序绘制。如果两个 UI 元素重叠，则后一个元素将显示在前一个元素之上。</p><p>要更改元素的显示顺序，只需在 Hierarchy 中拖动元素进行重新排序。也可以通过在变换组件上使用以下方法从脚本控制顺序：SetAsFirstSibling、SetAsLastSibling 和 SetSiblingIndex。</p><h3 id="渲染模式"><a href="#渲染模式" class="headerlink" title="渲染模式"></a>渲染模式</h3><ul><li>Screen Space-Overlay：UI 元素直接渲染到屏幕之上，不受到任何相机的影响。</li><li>Screen Space-Camera：UI 元素相对于特定的相机进行渲染。</li><li>World Space：UI 元素作为场景中的 3D 对象存在，可以被任何相机渲染。</li></ul><h3 id="组件参数"><a href="#组件参数" class="headerlink" title="组件参数"></a>组件参数</h3><h4 id="Canvas"><a href="#Canvas" class="headerlink" title="Canvas"></a>Canvas</h4><p><img src="/../img/UGUIBasic/Canvas.png" alt="Canvas"></p><ul><li>Pixel Perfect（仅限 Screen Space 模式） 是否应该无锯齿精确渲染 UI？</li><li>Render Camera（仅限 Screen Space - Camera 模式） 应该将 UI 渲染到的摄像机。</li><li>Plane Distance（仅限 Screen Space - Camera 模式） UI 平面在摄像机前方的距离。</li><li>Event Camera（仅限 World Space 模式） 用于处理 UI 事件的摄像机。</li></ul><h4 id="Canvas-Scaler"><a href="#Canvas-Scaler" class="headerlink" title="Canvas Scaler"></a>Canvas Scaler</h4><p><img src="/../img/UGUIBasic/CanvasScaler.png" alt="CanvasScaler"></p><ul><li>UI Scale Mode 确定画布中的 UI 元素的缩放方式。<ul><li>Constant Pixel Size UI 元素都保持相同的像素大小。</li><li>Scale With Screen Size 跟随屏幕大小缩放。</li><li>Constant Physical Size UI 元素都保持相同的物理大小。</li></ul></li></ul><ol><li><p><strong>Constant Pixel Size</strong><br><img src="/../img/UGUIBasic/Constant%20Pixel%20Size.png" alt="ConstantPixelSize"></p><ul><li>Scale Factor 按此系数缩放画布中的所有 UI 元素。</li><li>Reference Pixels Per Unit 如果精灵具有此“Pixels Per Unit”设置，则精灵中的每个像素将覆盖 UI 中的一个单位。</li></ul></li><li><p><strong>Scale With Screen Size</strong><br><img src="/../img/UGUIBasic/Scale%20With%20Screen%20Size.png" alt="ScaleWithScreenSize"></p><ul><li>Reference Resolution UI 布局设计的目标分辨率。如果屏幕分辨率较大，则 UI 会放大，如果较小，则 UI 会缩小。</li><li>Screen Match Mode 在当前分辨率的宽高比不适应参考分辨率时，用于缩放画布区域的模式。<ul><li>Match Width or Height 以宽度、高度或二者的某种平均值作为参考来缩放画布区域。</li><li>Expand 水平或垂直扩展画布区域，使画布不会小于参考。</li><li>Shrink 水平或垂直裁剪画布区域，使画布不会大于参考。</li></ul></li><li>Match 确定是否以宽度、高度或二者的某种平均值作为参考进行缩放。</li><li>Reference Pixels Per Unit 如果精灵具有此“Pixels Per Unit”设置，则精灵中的每个像素将覆盖 UI 中的一个单位。</li></ul></li><li><p><strong>Constant Physical Size</strong><br><img src="/../img/UGUIBasic/Constant%20Physical%20Size.png" alt="ConstantPhysicalSize"></p></li></ol><h4 id="Canvas-Group"><a href="#Canvas-Group" class="headerlink" title="Canvas Group"></a>Canvas Group</h4><p><img src="/../img/UGUIBasic/CanvasGroup.png" alt="CanvasGroup"></p><ul><li>Alpha 此组中的 UI 元素的不透明度。该值介于 0 和 1 之间，其中 0 表示完全透明，1 表示完全不透明。注意，UI 元素也会保留自己的透明度，因此画布组的 Alpha 值将与各个元素的 Alpha 值彼此相乘。</li><li>Interactable 确定此组件是否接受输入。当设置为 false 时，禁用交互。</li><li>Block Raycasts 此组件是否作为射线投射的碰撞体？需要在连接到画布的图形射线投射器上调用 RayCast 函数。这<em>不适用</em>于 Physics.Raycast。</li><li>Ignore Parent Groups 此组还会受到游戏对象层级视图中更上层的画布组 (Canvas Group) 组件中的设置所影响，还是会忽略并因此覆盖这些设置？</li></ul><p><strong>画布组的典型用途为：</strong></p><ul><li>通过在窗口的游戏对象上添加画布组并控制其 Alpha 属性来淡入或淡出整个窗口。</li><li>通过将画布组添加到父游戏对象并将其 Interactable 属性设置为 false 来使整组控件不可交互（“灰显”）。</li><li>通过在 UI 元素或其某个父元素上放置画布组 (Canvas Group) 组件并将其 Block Raycasts 属性设置为 false 来使一个或多个 UI 元素不阻止鼠标事件。</li></ul><h4 id="Canvas-Renderer-画布渲染器"><a href="#Canvas-Renderer-画布渲染器" class="headerlink" title="Canvas Renderer(画布渲染器)"></a>Canvas Renderer(画布渲染器)</h4><p>**画布渲染器 (Canvas Renderer)**组件用于渲染画布中包含的图形 UI 对象。</p><h2 id="UI-控件"><a href="#UI-控件" class="headerlink" title="UI 控件"></a>UI 控件</h2><p>Unity UI 提供了一系列预定义的控件，如 Button（按钮）、Text（文本）、Image（图像）、Slider（滑动条）、Toggle（切换开关）、Scrollbar（滚动条）、ScrollView(滚动视图)、InputFiled(输入框)、Dropdown (下拉框)等。</p><h2 id="Layout（布局）"><a href="#Layout（布局）" class="headerlink" title="Layout（布局）"></a>Layout（布局）</h2><p>Unity UI 支持多种布局方式，如 Horizontal Layout Group（水平布局组）、Vertical Layout Group（垂直布局组）、Grid Layout Group（网格布局组），帮助自动排列 UI 元素。</p><p>在 Content 上添加 Content Size Fitter 和 Grid Layout Group,配置好参数就可以实现 ScrollView 的动态添加 Item.</p><h2 id="Mask（遮罩）"><a href="#Mask（遮罩）" class="headerlink" title="Mask（遮罩）"></a>Mask（遮罩）</h2><p>Mask 可以用来限制 UI 元素的显示区域。<br><img src="/../img/UGUIBasic/Mask.png" alt="Mask"></p><ul><li>Show Mask Graphic: 在子对象上使用 Alpha 绘制遮罩（父）对象的图形</li></ul><p>遮罩的常见用法是使用面板 (Panel) 对象作为“框架”显示大型图像的一小部分。为实现此目的，可先将图像设置为面板对象的子项。应调整图像的位置，使应该可见的区域直接位于面板区域的后面。然后，将一个遮罩组件添加到面板。面板外面的子图像区域将变得不可见，因为这些区域被面板的形状所掩盖。</p><p><strong>RectMask2D</strong><br>RectMask2D 是一个类似于<strong>遮罩 (Mask)</strong> 控件的遮罩控件。遮罩将子元素限制为父元素的矩形。与标准的遮罩控件不同，这种控件有一些限制，但也有许多性能优势。</p><h2 id="Event-System（事件系统）"><a href="#Event-System（事件系统）" class="headerlink" title="Event System（事件系统）"></a>Event System（事件系统）</h2><p>用于处理用户输入，如鼠标点击或触摸屏幕，以及发送事件到 UI 元素。</p><h2 id="Raycast-Target（射线目标）"><a href="#Raycast-Target（射线目标）" class="headerlink" title="Raycast Target（射线目标）"></a>Raycast Target（射线目标）</h2><p>UI 元素是否接受射线投射的属性，这对于响应用户的输入很重要。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Unity UI（用户界面）是 Unity 引擎中用于创建游戏和应用中的交互式界面的系统。它允许开发者设计、构建和管理 2D 和 3D 游戏内的菜单、HUD（抬头显示）、按钮、滑块、文本框等元素。&lt;/p&gt;
&lt;h1 id=&quot;Unity-UI-的主要组成部分&quot;&gt;&lt;a href=</summary>
      
    
    
    
    
    <category term="Unity" scheme="http://example.com/tags/Unity/"/>
    
  </entry>
  
  <entry>
    <title>ScreenToWorldPoint从屏幕坐标系转换到世界坐标系</title>
    <link href="http://example.com/2024/07/19/ScreenToWorldPoint%E4%BB%8E%E5%B1%8F%E5%B9%95%E5%9D%90%E6%A0%87%E7%B3%BB%E8%BD%AC%E6%8D%A2%E5%88%B0%E4%B8%96%E7%95%8C%E5%9D%90%E6%A0%87%E7%B3%BB/"/>
    <id>http://example.com/2024/07/19/ScreenToWorldPoint%E4%BB%8E%E5%B1%8F%E5%B9%95%E5%9D%90%E6%A0%87%E7%B3%BB%E8%BD%AC%E6%8D%A2%E5%88%B0%E4%B8%96%E7%95%8C%E5%9D%90%E6%A0%87%E7%B3%BB/</id>
    <published>2024-07-19T04:14:26.000Z</published>
    <updated>2024-07-19T05:19:20.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="最终代码"><a href="#最终代码" class="headerlink" title="最终代码"></a>最终代码</h1><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnMouseDrag</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    Vector3 mousePos = Input.mousePosition;</span><br><span class="line">    mousePos.z = camera.WorldToScreenPoint(transform.position).z;</span><br><span class="line">    Vector3 pos = Camera.main.ScreenToWorldPoint(mousePos);</span><br><span class="line">    transform.position = pos;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h1><p>测试鼠标事件 OnMouseDrag()时,想让鼠标拖着游戏物体移动,最开始使用代码</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnMouseDrag</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>发现物体不见了,检查发现 Cube 位置变为了(0,1,-10)<br><img src="/../img/ScreenToWorldPoint%E5%B1%8F%E5%B9%95%E5%9D%90%E6%A0%87%E8%BD%AC%E4%B8%96%E7%95%8C%E5%9D%90%E6%A0%87/CubeTrs.png" alt="CubeTrs"></p><p>分别输出 Input.mousePosition 和转换后的 Camera.main.ScreenToWorldPoint(Input.mousePosition),发现前者正常显示指针的屏幕坐标,后者始终为(0,1,-10)</p><h1 id="分析原因"><a href="#分析原因" class="headerlink" title="分析原因"></a>分析原因</h1><p>关于 ScreenToWorldPoint 的使用说明：</p><ol><li><p>如果是 2D，Camera.main.ScreenToWorldPoint（Input.mousePosition），那么转换后的坐标，直接用没有问题，原因是 2D 的 Z 轴默认是 0，所以转后没有问题</p></li><li><p>如果是 3D，Camera.main.ScreenToWorldPoint（Input.mousePosition），那么转换后的坐标是不正确的，因为此时的摄像机是投影摄像机，n 多个面，所以必须指定要转换哪个面上的坐标，即 z 距离所对应的面：Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z))，这样转换后的坐标才是正确的！</p></li></ol><h1 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h1><ol><li>修改代码为</li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line">Vector3 mousePos = Input.mousePosition;</span><br><span class="line">transform.position = Camera.main.ScreenToWorldPoint(<span class="keyword">new</span> Vector3(mousePos.x, mousePos.y, transform.position.z));</span><br></pre></td></tr></table></figure><ol start="2"><li>发现物体一直在移动,因为每次转换后的 z 轴坐标都在变化,所以锁一下 Z 轴</li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line">Vector3 mousePos = Input.mousePosition;</span><br><span class="line">Vector3 pos = Camera.main.ScreenToWorldPoint(<span class="keyword">new</span> Vector3(mousePos.x, mousePos.y, transform.position.z));</span><br><span class="line">transform.position = <span class="keyword">new</span> Vector3(pos.x, pos.y, transform.position.z);</span><br></pre></td></tr></table></figure><ol start="3"><li>运行发现物体的运动 XY 轴是反向的.</li></ol><p>既然 Cube 要使用屏幕坐标,那就将 Cube 的屏幕坐标的 Z 轴赋值给指针屏幕坐标的 Z 轴</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line">Vector3 mousePos = Input.mousePosition;</span><br><span class="line">mousePos.z = camera.WorldToScreenPoint(transform.position).z;</span><br><span class="line">Vector3 pos = Camera.main.ScreenToWorldPoint(mousePos);</span><br><span class="line">transform.position = pos;</span><br></pre></td></tr></table></figure><p>现在 Cube 就可以跟随指针移动了</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://blog.csdn.net/HarvestHarvest/article/details/112726968">unity 屏幕坐标转世界坐标 ScreenToWorldPoint</a></p><p><a href="https://blog.csdn.net/weixin_44045614/article/details/108489738">从屏幕坐标系转换到世界坐标系</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;最终代码&quot;&gt;&lt;a href=&quot;#最终代码&quot; class=&quot;headerlink&quot; title=&quot;最终代码&quot;&gt;&lt;/a&gt;最终代码&lt;/h1&gt;&lt;figure class=&quot;highlight c#&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;sp</summary>
      
    
    
    
    
  </entry>
  
  <entry>
    <title>Unity输入系统基础</title>
    <link href="http://example.com/2024/07/18/Unity%E8%BE%93%E5%85%A5%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80/"/>
    <id>http://example.com/2024/07/18/Unity%E8%BE%93%E5%85%A5%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80/</id>
    <published>2024-07-18T01:27:44.000Z</published>
    <updated>2025-05-16T02:31:04.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h1><p>Unity 的输入系统是用于处理用户输入的核心模块，它使开发者能够捕获和响应来自键盘、鼠标、触摸屏、游戏手柄等各种输入设备的输入事件。Unity 的输入系统经历了几个重大版本的演变，目前主要有两种输入系统：经典输入系统和新的输入系统（Input System package）。</p><h2 id="经典输入系统"><a href="#经典输入系统" class="headerlink" title="经典输入系统"></a>经典输入系统</h2><p>经典输入系统是 Unity 早期版本中内置的输入系统，它通过一系列的公共变量和函数来提供输入处理。这些变量和函数可以在脚本中直接使用，例如：</p><ul><li>Input.GetAxis：获取轴的值，常用于处理模拟输入，如游戏手柄的摇杆或键盘的方向键。</li><li>Input.GetButton：检查按钮是否被按下，适用于数字输入，如键盘按键或手柄按钮。</li><li>Input.GetMouseButton 和 Input.GetMouseButtonDown：分别用于检测鼠标按钮是否被按下或是否被按下了一帧。</li><li>Input.mousePosition：获取鼠标的屏幕坐标。</li><li>Input.touchCount 和 Input.GetTouch：用于处理触摸屏输入。</li></ul><p>经典输入系统的优点是简单易用，但是它的功能相对有限，对于复杂的输入处理和跨平台的支持不够灵活。</p><h2 id="新的输入系统（Input-System-package）"><a href="#新的输入系统（Input-System-package）" class="headerlink" title="新的输入系统（Input System package）"></a>新的输入系统（Input System package）</h2><p>新的输入系统是一个独立的 Unity Package，提供了更强大和灵活的输入处理能力。它允许开发者定义自定义的输入动作、绑定和处理程序，可以更精细地控制输入行为。新输入系统的主要特点包括：</p><ul><li>可配置性：输入动作和绑定可以在编辑器中通过图形界面配置，无需编写代码。</li><li>跨平台支持：提供了广泛的设备和平台支持，可以很容易地处理不同设备和平台的输入差异。</li><li>高级功能：支持复合输入、延迟输入处理、输入过滤和变换等高级功能。</li><li>脚本控制：提供了丰富的 API，允许开发者在脚本中控制输入行为，如监听特定的输入事件、调整输入映射等。</li></ul><p>新输入系统的核心概念包括：</p><ul><li>Input Actions：表示一个具体的输入事件，如跳跃或射击。</li><li>Input Bindings：定义了如何从硬件输入（如按键、触控点）映射到 Input Actions。</li><li>Input Controls：代表了具体的硬件输入源，如键盘上的某个键、手柄的某个按钮。</li></ul><p>新输入系统的设计目的是为了提供更现代化、更灵活的输入处理框架，以适应各种游戏和应用的需求，尤其是在处理复杂的输入场景和跨平台开发时。</p><h1 id="鼠标输入"><a href="#鼠标输入" class="headerlink" title="鼠标输入"></a>鼠标输入</h1><p>在 Unity 中，鼠标输入主要包括以下几种类型：</p><h2 id="鼠标按钮"><a href="#鼠标按钮" class="headerlink" title="鼠标按钮"></a>鼠标按钮</h2><ul><li>GetMouseButtonDown(int button): 检测鼠标按钮是否刚刚按下。</li><li>GetMouseButton(int button): 检测鼠标按钮是否被按下。</li><li>GetMouseButtonUp(int button): 检测鼠标按钮是否刚刚释放。</li></ul><p>其中 button 参数可以是 0（左键）、1（右键）、2（中间键），或者更高的数值，具体取决于鼠标有多少个额外的按钮。一般在 Update 函数中每帧对按键进行检测</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Update</span>()</span></span><br><span class="line">  &#123;</span><br><span class="line">      <span class="keyword">if</span>(Input.GetMouseButton(<span class="number">0</span>))</span><br><span class="line">      &#123;</span><br><span class="line">          Debug.Log(<span class="string">&quot;GetMouseButton&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span>(Input.GetMouseButtonDown(<span class="number">0</span>))</span><br><span class="line">      &#123;</span><br><span class="line">          Debug.Log(<span class="string">&quot;Down&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span>(Input.GetMouseButtonUp(<span class="number">0</span>))</span><br><span class="line">      &#123;</span><br><span class="line">          Debug.Log(<span class="string">&quot;Up&quot;</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>鼠标左键点击一次屏幕,控制台输出<br><img src="/../img/UnityInputSystemBlog/MouseInput/GetMouseButton.png" alt="GetMouseButton"></p><h2 id="鼠标位置"><a href="#鼠标位置" class="headerlink" title="鼠标位置"></a>鼠标位置</h2><ul><li>mousePosition: 返回鼠标在屏幕坐标系中的位置，左下角为(0,0)，右上角为(Screen.width, Screen.height)。</li></ul><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Update</span>()</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span>(Input.GetMouseButtonDown(<span class="number">1</span>))</span><br><span class="line">      &#123;</span><br><span class="line">          Debug.Log(Input.mousePosition);</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><p>屏幕中点击鼠标右键,控制台输出<br><img src="/../img/UnityInputSystemBlog/MouseInput/MousePosition.png" alt="MousePosition"></p><h2 id="鼠标滚动"><a href="#鼠标滚动" class="headerlink" title="鼠标滚动"></a>鼠标滚动</h2><ul><li>GetAxis(“Mouse ScrollWheel”): 获取鼠标滚轮的滚动量，正数表示向上滚动，负数表示向下滚动。</li></ul><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="built_in">float</span> mouseScrollWheel = Input.GetAxis(<span class="string">&quot;Mouse ScrollWheel&quot;</span>);</span><br><span class="line">Debug.Log(mouseScrollWheel);</span><br></pre></td></tr></table></figure><p>在窗口中滚动鼠标滚轮,控制台输出<br><img src="/../img/UnityInputSystemBlog/MouseInput/ScrollWheel.png" alt="ScrollWheel"></p><h2 id="鼠标移动"><a href="#鼠标移动" class="headerlink" title="鼠标移动"></a>鼠标移动</h2><ul><li>GetAxis(“Mouse X”) 和 GetAxis(“Mouse Y”): 分别返回鼠标水平和垂直方向上的移动量。</li></ul><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Update</span>()</span>&#123;</span><br><span class="line">  <span class="built_in">float</span> mouseX = Input.GetAxis(<span class="string">&quot;Mouse X&quot;</span>);</span><br><span class="line">  <span class="built_in">float</span> mouseY = Input.GetAxis(<span class="string">&quot;Mouse Y&quot;</span>);</span><br><span class="line">  Debug.Log(<span class="string">&quot;X:&quot;</span> + mouseX + <span class="string">&quot;  Y:&quot;</span> + mouseY);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>移动鼠标，控制台输出<br><img src="/../img/UnityInputSystemBlog/MouseInput/MouseXY.png" alt="MouseXY"></p><h2 id="鼠标事件"><a href="#鼠标事件" class="headerlink" title="鼠标事件"></a>鼠标事件</h2><p>对于 UI 元素，Unity 的 EventSystem 提供了对鼠标<a href="https://docs.unity3d.com/cn/2023.2/Manual/SupportedEvents.html">事件的支持</a>.</p><ul><li>IPointerEnterHandler - OnPointerEnter - 当指针进入对象时调用</li><li>IPointerExitHandler - OnPointerExit - 当指针退出对象时调用</li><li>IPointerDownHandler - OnPointerDown - 在对象上按下指针时调用</li><li>IPointerUpHandler - OnPointerUp - 松开指针时调用（在指针正在点-击的游戏对象上调用）</li><li>IPointerClickHandler - OnPointerClick - 在同一对象上按下再松开指针时调用</li><li>IBeginDragHandler - OnBeginDrag - 即将开始拖动时在拖动对象上调用</li><li>IDragHandler - OnDrag - 发生拖动时在拖动对象上调用</li><li>IEndDragHandler - OnEndDrag - 拖动完成时在拖动对象上调用</li></ul><p>对于非 UI 元素，可以使用物理碰撞和射线投射技术来检测鼠标与 3D 世界中的物体的交互。<br>常用 API 有:</p><ul><li>OnMouseDown 在物体上按下鼠标</li><li>OnMouseUp 在物体上抬起鼠标</li><li>OnMouseDrag 在物体上拖动鼠标,每帧调用</li><li>OnMouseEnter 鼠标进入物体</li><li>OnMouseExit 鼠标移出物体</li><li>OnMouseOver 鼠标位于物体上,每帧调用</li></ul><h1 id="键盘输入"><a href="#键盘输入" class="headerlink" title="键盘输入"></a>键盘输入</h1><p>在 Unity 中，键盘输入是游戏和应用程序中常见的用户交互方式之一。Unity 提供了多种方法来读取和处理键盘输入，这些方法可以分为两大类：基于事件的输入和基于轴的输入。</p><h2 id="基于事件的输入"><a href="#基于事件的输入" class="headerlink" title="基于事件的输入"></a>基于事件的输入</h2><p>基于事件的输入用于检测键盘上的按键状态，主要包括以下几种方法：</p><ul><li>GetKey：检测按键是否一直被按下。</li><li>GetKeyDown：检测按键是否在当前帧首次按下。</li><li>GetKeyUp：检测按键是否在当前帧首次释放。</li></ul><p>这些方法接受一个字符或按键名称作为参数，返回一个布尔值，表示按键的状态。如：</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Update</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">   <span class="keyword">if</span>(Input.GetKey(KeyCode.A))</span><br><span class="line">   &#123;</span><br><span class="line">     Debug.Log(<span class="string">&quot;A&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span>(Input.GetKeyDown(KeyCode.A))</span><br><span class="line">   &#123;</span><br><span class="line">     Debug.Log(<span class="string">&quot;Down&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">   <span class="keyword">if</span>(Input.GetKeyUp(KeyCode.A))</span><br><span class="line">   &#123;</span><br><span class="line">     Debug.Log(<span class="string">&quot;UP&quot;</span>);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>点击一次 A 键,控制台输出<br><img src="/../img/UnityInputSystemBlog/KeyboardInput/GetKeyLog.png" alt="GetKey"></p><p>还有三个 API 用于事件输入</p><ul><li>GetButton</li><li>GetButtonDown</li><li>GetButtonUp</li></ul><p>这三个 API 的用法和之前的基本一样，只是传入的参数只能是 InputManager 中定义的轴键。以 InputManager 自带的 jump 为例:<br><img src="/../img/UnityInputSystemBlog/KeyboardInput/JumpButton.png" alt="JumpButton"><br>代码格式:</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span>(Input.GetButtonDown(<span class="string">&quot;Jump&quot;</span>))</span><br><span class="line">&#123;</span><br><span class="line">    Debug.Log(<span class="string">&quot;Jump&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>运行时按 InputManager 中设置的 space(空格键),控制台输出:<br><img src="/../img/UnityInputSystemBlog/KeyboardInput/JumpLog.png" alt="JumpLog"><br>如果传入的 Button Name 在 InputManager 中没有注册,运行游戏时会报错</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (Input.GetButtonDown(<span class="string">&quot;space&quot;</span>))</span><br><span class="line">   &#123;</span><br><span class="line">       Debug.Log(<span class="string">&quot;space&quot;</span>);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>错误信息:<br><img src="/../img/UnityInputSystemBlog/KeyboardInput/ErrorLog.png" alt="ErrorLog"></p><h2 id="基于轴的输入"><a href="#基于轴的输入" class="headerlink" title="基于轴的输入"></a>基于轴的输入</h2><p>基于轴的输入用于处理类似游戏手柄的模拟输入，但也可以用于键盘。Unity 为常用的键盘方向键定义了轴，例如：</p><ul><li>Horizontal：处理左右方向键或 A&#x2F;D 键。</li><li>Vertical：处理上下方向键或 W&#x2F;S 键。</li></ul><p>这些轴的值范围一般在-1 到 1 之间，其中-1 表示按键完全向左或向下，1 表示完全向右或向上。</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="built_in">float</span> horizontal = Input.GetAxis(<span class="string">&quot;Horizontal&quot;</span>);</span><br><span class="line"><span class="built_in">float</span> vertical = Input.GetAxis(<span class="string">&quot;Vertical&quot;</span>);</span><br><span class="line">Debug.Log(<span class="string">&quot;h = &quot;</span> + horizontal + <span class="string">&quot;,v = &quot;</span> + vertical);</span><br></pre></td></tr></table></figure><p><img src="/../img/UnityInputSystemBlog/KeyboardInput/GetAxis.png" alt="GetAxis"></p><h1 id="Touch-输入"><a href="#Touch-输入" class="headerlink" title="Touch 输入"></a>Touch 输入</h1><p>在 Unity 中，处理触摸输入对于开发移动平台（如 iOS 和 Android）的游戏和应用程序至关重要。Unity 提供了对多点触摸的支持，允许你检测和响应来自触摸屏的输入事件。触摸输入在 Unity 中主要通过 Input.touches 数组来访问，这个数组包含了所有当前正在触摸屏幕的手指的信息。</p><h2 id="Input-touches-数组"><a href="#Input-touches-数组" class="headerlink" title="Input.touches 数组"></a>Input.touches 数组</h2><p>Input.touches 是一个 Touch 类型的数组，包含了所有正在触摸屏幕的手指信息。每个 Touch 对象都有以下属性和方法：</p><ul><li>fingerId: 触摸的唯一索引。</li><li>position：手指在屏幕上的位置（以像素为单位）。</li><li>deltaPosition：自上次帧以来手指移动的距离。</li><li>tapCount：连续的点击次数，对于快速多次点击同一位置的情况。</li><li>phase：触摸事件的阶段，可以是 Began（开始）、Moved（移动）、Stationary（静止）、Ended（结束）或 Cancelled（取消）。</li></ul><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Update</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">   <span class="keyword">if</span>(Input.touchCount&gt;<span class="number">0</span>)</span><br><span class="line">      <span class="keyword">foreach</span> (<span class="keyword">var</span> touch <span class="keyword">in</span> Input.touches)</span><br><span class="line">      &#123;</span><br><span class="line">            <span class="keyword">if</span> (touch.phase == TouchPhase.Began)</span><br><span class="line">               print(touch.position);</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用打包工具将场景打包成 Apk,勾选调试选项</p><p><img src="/../img/UnityInputSystemBlog/TouchInput/Build.png" alt="Build"></p><p>在手机上运行程序,看到 Unity 控制台显示监听信息,然后点击手机屏幕,控制台输出</p><p><img src="/../img/UnityInputSystemBlog/TouchInput/TouchCount.png" alt="TouchCount"></p><h2 id="使用-Touch-输入拖动物体"><a href="#使用-Touch-输入拖动物体" class="headerlink" title="使用 Touch 输入拖动物体"></a>使用 Touch 输入拖动物体</h2><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Update</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">   <span class="keyword">if</span>(Input.touchCount&gt;<span class="number">0</span>)</span><br><span class="line">      <span class="keyword">foreach</span> (<span class="keyword">var</span> touch <span class="keyword">in</span> Input.touches)</span><br><span class="line">      &#123;</span><br><span class="line">            <span class="keyword">if</span> (touch.phase == TouchPhase.Moved)</span><br><span class="line">            &#123;</span><br><span class="line">               Vector3 touchPos = touch.position;</span><br><span class="line">               touchPos.z = Camera.main.WorldToScreenPoint(transform.position).z;</span><br><span class="line">               Vector3 pos = Camera.main.ScreenToWorldPoint(touchPos);</span><br><span class="line">               transform.position = pos;</span><br><span class="line">            &#125;</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="鼠标事件模拟单个-touch"><a href="#鼠标事件模拟单个-touch" class="headerlink" title="鼠标事件模拟单个 touch"></a>鼠标事件模拟单个 touch</h2><p>鼠标事件也可以用来模拟单个 touch 输入,在脚本中的 Input.mousePosition 也是单个手指点击的位置,所以 Unity 提供的鼠标事件也可以用来模拟输入,API:</p><ul><li>OnMouseDown 在物体上按下鼠标</li><li>OnMouseUp 在物体上抬起鼠标</li><li>OnMouseDrag 在物体上拖动鼠标,每帧调用</li><li>OnMouseEnter 鼠标进入物体</li><li>OnMouseExit 鼠标移出物体</li><li>OnMouseOver 鼠标位于物体上,每帧调用</li></ul><p>所以拖动物体可以用以下方法,而且无论在编辑器下还是打包到手机都可以触发事件</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnMouseDrag</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">   Vector3 mousePos = Input.mousePosition;<span class="comment">//或者检测touch获取touch.position</span></span><br><span class="line">   mousePos.z = Camera.main.WorldToScreenPoint(transform.position).z;</span><br><span class="line">   Vector3 pos = Camera.main.ScreenToWorldPoint(mousePos);</span><br><span class="line">   transform.position = pos;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是 GetMouseButtonDown 等方法就模拟不了手机输入,也就是方法不会在点击屏幕时触发</p><h2 id="触摸事件和鼠标事件对比"><a href="#触摸事件和鼠标事件对比" class="headerlink" title="触摸事件和鼠标事件对比"></a>触摸事件和鼠标事件对比</h2><p>在 Unity 中，处理触摸事件（Touch Events）、鼠标事件（Mouse Events）有着不同的应用场景和特性，它们分别针对不同的输入设备和用户交互方式。下面是对其的对比：</p><p>触摸事件（Touch Events）</p><ul><li>适用平台：主要针对移动设备（如智能手机和平板电脑）和触摸屏。</li><li>输入设备：触摸屏。</li><li>事件类型：通过 Input.touches 数组获取，包含了 Touch 对象，每个对象描述了一个触摸点的状态。</li><li>多点触摸：支持同时处理多个触摸点，适用于手势识别，如捏合缩放、多指滑动等。</li></ul><p>鼠标事件（Mouse Events）</p><ul><li>适用平台：桌面操作系统，如 Windows、macOS。</li><li>输入设备：鼠标。</li><li>事件类型：鼠标事件（例如“OnMouseDown”、“OnMouseDrag”和“OnMouseUp”）是 Unity 的内置事件函数，用于处理 Unity 编辑器内以及某些情况下独立应用程序中的鼠标交互。这些事件与具有 Collider 组件的游戏对象相关联，通常用于 Unity 编辑器中的测试和开发目的。</li><li>单点定位：相对于触摸事件，鼠标事件通常涉及单一输入点，即鼠标的位置。</li></ul><p>在实际开发中，选择使用哪一种输入事件处理方式，主要取决于你的目标平台、游戏类型和用户交互设计。例如，对于一款移动平台的跑酷游戏，你可能主要依赖触摸事件来处理玩家的滑动和轻触操作；而对于一款桌面平台的策略游戏，你可能更多地使用鼠标事件来处理选择和拖拽操作。</p><h1 id="陀螺仪输入"><a href="#陀螺仪输入" class="headerlink" title="陀螺仪输入"></a>陀螺仪输入</h1><p>Unity 中的陀螺仪输入主要用于处理来自带有陀螺仪传感器的设备的运动数据，如智能手机和平板电脑。陀螺仪传感器能够测量设备绕三个轴的角速度，这对于实现基于运动的控制和增强现实（AR）应用非常有用。在 Unity 中，陀螺仪输入可以通过 Input.gyro 属性来访问。</p><h2 id="Input-gyro-属性"><a href="#Input-gyro-属性" class="headerlink" title="Input.gyro 属性"></a>Input.gyro 属性</h2><p>Input.gyro 是一个 Gyroscope 类型的对象，提供了以下主要属性和方法：</p><ul><li>enabled: 设置或检索该陀螺仪的启用状态。</li><li>attitude: 返回设备的姿态（即在空间中的方向）。</li><li>rotationRate: 类似于 angularVelocity，但表示的是旋转速率，通常用于 AR 应用。</li><li>rotationRateUnbiased: 与 rotationRate 类似，但去除了偏置，更准确地反映了实际的旋转速率。</li><li>updateInterval: 返回或设置陀螺仪数据的更新间隔，单位为秒。</li><li>userAcceleration: 返回用户给予设备的加速度。</li></ul><h2 id="检查设备支持"><a href="#检查设备支持" class="headerlink" title="检查设备支持"></a>检查设备支持</h2><p>首先，你需要检查设备是否支持陀螺仪功能。</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Start</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (SystemInfo.supportsGyroscope)</span><br><span class="line">    &#123;</span><br><span class="line">        Debug.Log(<span class="string">&quot;Gyroscope is supported.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    &#123;</span><br><span class="line">        Debug.Log(<span class="string">&quot;Gyroscope is not supported.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="启用陀螺仪"><a href="#启用陀螺仪" class="headerlink" title="启用陀螺仪"></a>启用陀螺仪</h2><p>在开始读取陀螺仪数据之前，必须先启用它</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (Input.gyro != <span class="literal">null</span> &amp;&amp; !Input.gyro.enabled)</span><br><span class="line">&#123;</span><br><span class="line">    Input.gyro.enabled = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="获取陀螺仪数据"><a href="#获取陀螺仪数据" class="headerlink" title="获取陀螺仪数据"></a>获取陀螺仪数据</h2><p>一旦陀螺仪被启用，可以通过以下方式获取数据：</p><p>旋转速率：Input.gyro.rotationRate, 返回一个 Vector3，表示设备在 X、Y、Z 轴上的角速度。<br>未校正的旋转速度：Input.gyro.rotationRateUnbiased,这将不考虑任何偏移或校准。<br>姿态：Input.gyro.attitude，这是一个 Quaternion，代表了设备相对于世界坐标系的姿态。</p><h2 id="更新频率"><a href="#更新频率" class="headerlink" title="更新频率"></a>更新频率</h2><p>陀螺仪的更新频率可能会影响性能和精度。可以通过 Input.gyro.updateInterval 属性来设置更新间隔（以秒为单位），默认值通常是 0.02 秒。</p><h2 id="示例代码"><a href="#示例代码" class="headerlink" title="示例代码"></a>示例代码</h2><p>通过陀螺仪实现旋转手机移动相机视角</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">FixedUpdate</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">   <span class="keyword">if</span> (Input.gyro.enabled)</span><br><span class="line">   &#123;</span><br><span class="line">      <span class="comment">//1.使用attitude,效果不好,没优化</span></span><br><span class="line">      <span class="comment">// Quaternion gyroData = Input.gyro.attitude;</span></span><br><span class="line">      <span class="comment">// Quaternion gyroQuaternion =Quaternion.Euler( gyroData.eulerAngles );</span></span><br><span class="line">      <span class="comment">// trs.rotation = new Quaternion( -gyroQuaternion.x,-gyroQuaternion.y,gyroQuaternion.z,gyroQuaternion.w);</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">//2.使用rotationRate</span></span><br><span class="line">      <span class="built_in">float</span> rotSpeed = <span class="number">10</span>;</span><br><span class="line">      Vector3 gyroRot = Input.gyro.rotationRate;</span><br><span class="line">      trs.transform.rotation = Quaternion.Euler(<span class="keyword">new</span> Vector3(-gyroRot.x,-gyroRot.y, gyroRot.z)*Time.deltaTime*rotSpeed +trs.rotation.eulerAngles);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意，陀螺仪数据可能会受到漂移的影响，因此在长时间运行的应用中，可能需要实现一些校准机制来处理这种问题。此外，陀螺仪的使用也会增加电池消耗，所以优化和测试是非常重要的。</p><h1 id="EasyTouch"><a href="#EasyTouch" class="headerlink" title="EasyTouch"></a>EasyTouch</h1><p>EasyTouch 是一个非常流行且功能丰富的 Unity 插件，主要用于处理触摸和手势输入，特别适合于移动平台的游戏开发。它提供了高度定制化的触摸输入处理，使得开发者可以轻松地在游戏内实现各种触摸操作，如点击、滑动、双击、长按、缩放、旋转等。</p><h2 id="EasyTouch-的特点"><a href="#EasyTouch-的特点" class="headerlink" title="EasyTouch 的特点"></a>EasyTouch 的特点</h2><ol><li>广泛的触摸事件支持：EasyTouch 支持多种触摸事件，包括但不限于点击、长按、拖动、双击、多点触摸、缩放、旋转等。</li><li>虚拟控制器：它允许创建虚拟按钮、摇杆和其他控制元件，这些元件可以在屏幕上任意位置放置，非常适合需要复杂控制方案的游戏。</li><li>手势识别：EasyTouch 具有智能的手势识别功能，可以识别复杂的用户输入，如滑动手势的方向和速度，这在许多游戏中是必不可少的。</li><li>事件系统：插件内置了一套事件系统，可以方便地将触摸事件与游戏逻辑连接起来，无需复杂的编程。</li><li>自定义和可扩展：EasyTouch 允许开发者自定义和扩展其功能，可以添加自己的触摸事件处理器，以适应特定的游戏需求。</li><li>编辑器工具：它提供了一个强大的编辑器工具，可以直观地配置触摸事件和虚拟控制器，减少代码工作量。</li><li>性能优化：EasyTouch 设计时考虑了性能，确保即使在高负载下也能保持良好的响应性和游戏流畅度。</li></ol><h2 id="使用-EasyTouch-的基本步骤"><a href="#使用-EasyTouch-的基本步骤" class="headerlink" title="使用 EasyTouch 的基本步骤"></a>使用 EasyTouch 的基本步骤</h2><ol><li>导入插件：从 Unity Asset Store 下载并导入 EasyTouch 插件到 Unity 项目中。</li><li>创建 EasyTouch 控件：在场景中创建一个 EasyTouch 控件,里面可以对 EasyTouch 的参数进行设置<br><img src="/../img/UnityInputSystemBlog/EasyTouch/ImprotEasyTouch.png" alt="ImportEasyTouch"></li><li>配置触摸事件：EasyTouch 提供多种使用方式包括使用脚本 API,使用 Quick Component 以及使用 EasyTouch Control</li><li>编写脚本：在游戏对象的脚本中，你可以订阅 EasyTouch Manager 发布的事件，从而根据不同的触摸事件执行相应的游戏逻辑。</li><li>测试和调试：使用 Unity 编辑器或在目标设备上测试触摸事件，确保一切按预期工作。</li></ol><h2 id="Easy-Touch-Script-组件"><a href="#Easy-Touch-Script-组件" class="headerlink" title="Easy Touch(Script) 组件"></a>Easy Touch(Script) 组件</h2><p>创建的 Easy Touch 带有一个同名的组件,结构如下：<br><img src="/../img/UnityInputSystemBlog/EasyTouch/EasyTouchCom.png" alt="EasyTouchCom"></p><ul><li>Enable Easy Touch : 是否启用 EasyTouch</li><li>Enable Unity Remote : 是否启用 UnityRemote</li></ul><h3 id="GUI-compatibility"><a href="#GUI-compatibility" class="headerlink" title="GUI compatibility"></a>GUI compatibility</h3><p>GUI Compatibility（GUI 兼容性）设置是为了确保 EasyTouch 与 Unity 的旧版 GUI 系统（不是 UGUI，即 Unity 的 User Interface 系统）兼容。在 Unity 早期版本中，GUI 系统是用于创建用户界面的主要方式，但后来被新的 UGUI 系统所取代。当启用此选项时，EasyTouch 会在 GUI 事件发生时禁用手势识别，以防止与 GUI 控件的交互冲突。这是因为旧的 GUI 系统使用鼠标事件，而 EasyTouch 则使用自己的事件系统，两者之间可能存在冲突。<br><img src="/../img/UnityInputSystemBlog/EasyTouch/GUI%20com.png" alt="GUIcom"></p><ul><li>Enable Unity UI detection：是否启用 UI 检测，取消勾选 EasyTouch 就取消对 UI 的控制</li><li>Unity UI Compatibility：与 UGUI 的兼容性</li><li>Auto update picked Unity UI：是否每一帧都检测 touch 下的物体是否符合要求。(比如滑动,如果滑动出了物体,指针下就没有 picked object 就会结束执行)</li></ul><p><strong>示例:</strong></p><p>新建两个 Image</p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/GUIcom2.png" alt="GUIcom2"></p><p>在红色 UI 上挂载脚本如下:</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">UICom</span>: <span class="title">MonoBehaviour</span>, <span class="title">IDragHandler</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">OnDrag</span>(<span class="params">PointerEventData eventData</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        transform.position = Input.mousePosition;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在蓝色 UI 上新建组件 Quick Drag:</p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/GUIcom3.png" alt="GUIcom3"></p><p>当关闭 Enable Unity UI detection 时蓝色的 image 拖动功能失效,启动该选项时无论 Unity UI compatibility 是否启用运行时都会将其禁用,然后蓝色 image 也可以实现拖动了</p><h3 id="Automatic-selection"><a href="#Automatic-selection" class="headerlink" title="Automatic selection"></a>Automatic selection</h3><p>Automatic Selection（自动选择）是一个功能，用于自动检测屏幕上的可交互对象。当启用此选项时，EasyTouch 会尝试根据触摸位置自动选择最接近的 UI 元素或者游戏对象。这对于实现点击、拖动等交互非常有用，可以减少手动编写选择逻辑的工作量。</p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/Automatic%20selection.png" alt="Automatic selection"></p><ul><li>Enable auto-select：自动选择。开启时，可通过 EasyTouch 选择物体执行命令</li><li>Picked Layers 3D：能够选取的层级</li><li>Enable 2D collider：自动选择的 2D 层</li><li>Add Camera：多相机的可选取。默认关联 MainCamera 。若添加某摄像机，位于其视口底下的物体也可被选取</li></ul><h3 id="General-gesture-properties"><a href="#General-gesture-properties" class="headerlink" title="General gesture properties"></a>General gesture properties</h3><p>General Gesture Properties（通用手势属性）包含了一系列影响所有手势类型的基础设置。这里可以设置一些全局的手势行为.</p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/General.png" alt="General"></p><ul><li>Priority to : 点击和滑动的优先级(主要针对比较模糊触碰)</li><li>Stationary tolerance：静止精度，当手指在屏幕上时，如果滑动距离小于 15 即认为未滑动</li><li>Long tap time：触发长按事件的最短间隔</li><li>Double tap detection：双击事件触发的两次点击间隔，即点击两次时间间隔设置或者采用系统默认双击设置</li><li>Swipe tolerance：滑动灵敏度</li><li>always sent swipe event：即使误触（在灵敏度以下），也会发送事件</li></ul><h3 id="Two-fingers-gesture-properties、"><a href="#Two-fingers-gesture-properties、" class="headerlink" title="Two fingers gesture properties、"></a>Two fingers gesture properties、</h3><p>Two Fingers Gesture Properties（双指手势属性）专门针对需要两个手指的手势，如捏合（Pinch）、旋转（Rotate）和双指拖动（Two Finger Drag）。在这个部分，可以调整这些特定手势的参数</p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/Two%20fingers.png" alt="TwoFingers"></p><ul><li>Pick method：触发方式,Finger,只有两指都在物体上,才触发事件,Average 是当两指连线位于物体上，便会触发</li><li>Enable swipe &amp; drag：是否允许滑动或拖拽</li><li>Enable Pinch：是否允许缩放</li><li>Enable twist：是否允许旋转</li></ul><h3 id="Second-finger-simulation"><a href="#Second-finger-simulation" class="headerlink" title="Second finger simulation"></a>Second finger simulation</h3><p>Second Finger Simulation（第二根手指模拟）是一个高级功能，允许在只有一根手指触摸屏幕的情况下模拟第二根手指的行为。这在某些情况下可能是有用的，比如正在测试需要两指操作的功能，但目前只有单指可用。通过这个设置，可以定义在单指触控时如何模拟第二根手指的位置和行为，以便于开发和调试。</p><h2 id="三种使用方式"><a href="#三种使用方式" class="headerlink" title="三种使用方式"></a>三种使用方式</h2><h3 id="使用-API"><a href="#使用-API" class="headerlink" title="使用 API"></a>使用 API</h3><ol><li>EasyTouch4.x,需要通过绑定事件的方式</li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnEnable</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    EasyTouch.On_Drag += OnDrag;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnDisable</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    EasyTouch.On_Drag -= OnDrag;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnDestroy</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    EasyTouch.On_Drag -= OnDrag;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnDrag</span>(<span class="params">Gesture gesture</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    Debug.Log(<span class="string">&quot;OnDrag&quot;</span>+gesture.actionTime);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>EasyTouch5.x,不需要绑定事件,使用 Gesture 获取当前手指状态</li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">Update</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    Gesture curGesture = EasyTouch.current;</span><br><span class="line">    <span class="keyword">if</span>(curGesture!=<span class="literal">null</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        OpinionAction(curGesture);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OpinionAction</span>(<span class="params">Gesture gesture</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">switch</span> (gesture.type)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">case</span> EasyTouch.EvtType.On_TouchStart:</span><br><span class="line">            OnTouchStart(gesture);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> EasyTouch.EvtType.On_TouchUp:</span><br><span class="line">            OnTouchUp(gesture);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">case</span> EasyTouch.EvtType.On_Drag:</span><br><span class="line">            OnDrag(gesture);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="comment">//更多状态</span></span><br><span class="line">        <span class="literal">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">OnDrag</span>(<span class="params">Gesture gesture</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    Debug.Log(<span class="string">&quot;OnDrag&quot;</span> + gesture.actionTime);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment"><span class="doctag">///</span>其他方法</span></span><br></pre></td></tr></table></figure><ol start="3"><li>其他常用 API</li></ol><p><img src="/../img/UnityInputSystemBlog/EasyTouch/GetureVariables.png" alt="GestureVariables"></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/GestureFunc.png" alt="GesturesFunc"></p><p>更多到包中的 EasyTouch.pdf 查看</p><h3 id="Quick-Component"><a href="#Quick-Component" class="headerlink" title="Quick Component"></a>Quick Component</h3><ol><li><p><strong>Quick Drag</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickDrag.png" alt="QuickDrag"></p><ul><li>Allow on the axis：允许拖动的轴</li><li>Allow pick over UI element：是否允许在 UI 元素底下拖拽</li><li>Stop drag on collision enter：碰撞时停止拖拽</li><li>Reset physics on drag: 拖拽时禁用刚体</li></ul></li><li><p><strong>Quick Enter-Over-Exit</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickEOE.png" alt="QuickEOE"></p><ul><li>Allow multi-touches：允许多指操作</li></ul></li><li><p><strong>Quick LongTap</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickLongTap.png" alt="QuickLongTap"></p><ul><li>2 fingers gesture：需要两个手指长按才能触发</li><li>Action triggering: 触发事件的时机分别是(Start,In progress,End)</li></ul></li><li><p><strong>Quick Pinch</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickPinch.png" alt="QuickPinch"></p><ul><li>Gesture over me：手势必须位于具有 collider 的物体上(或者两个手指的连线在物体身上)</li><li>Triggering： 触发时机,包括(In Progress,End)</li><li>Pinch direction: 缩放方向,包括(All,Pinch In,Pinch Out)</li><li>Enable simple action: 需要 Triggering 设置为 In Progress,用来设置双指实现的效果(缩放，移动，旋转)<ul><li>Action : 控制行为</li><li>Affected axes: 控制的轴向</li><li>Sensibility: 灵敏度</li><li>Inverse axis: 是否反转轴</li></ul></li></ul></li><li><p><strong>Quick Swipe</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickSwipe.png" alt="QuickSwipe"></p><ul><li>Allow swipe start over me：是否允许从物体上开始滑动</li></ul><p>其他参数类似 Quick Pinch</p></li><li><p><strong>Quick Tap</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickTap.png" alt="Quick"></p><ul><li>Action triggering：点击方式,包括单击双击</li></ul></li><li><p><strong>Quick Touch</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickTouch.png" alt="QuickTouch"></p><ul><li>Action triggering：触摸方式,包括开始,按下和抬起</li></ul></li><li><p><strong>Quick Twist</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/QuickTwist.png" alt="QuickTwist"></p><p>参数类似 Quick Pinch</p></li><li><p><strong>Easy Touch Trigger</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/QuickComponent/Trigger.png" alt="Trigger"></p><ul><li>Testing on：作用对象是 UI 还是 3D 物体</li><li>Only if on me：只有点击到当前物体时才会触发事件</li><li>All the time，or other object：点击到其他物体，或者指定的物体时，也可触发</li><li>Other receiver：其他接收者。即：当该物体触发事件后，要求 Receiver 做 Receiver 身上的 Method</li></ul></li></ol><h3 id="EasyTouch-Controls"><a href="#EasyTouch-Controls" class="headerlink" title="EasyTouch Controls"></a>EasyTouch Controls</h3><p>Hierarchy 窗口空白处右键添加 Controls<br><img src="/../img/UnityInputSystemBlog/EasyTouch/Controls/NewContorls.png" alt="NewControl"></p><h4 id="InputManager"><a href="#InputManager" class="headerlink" title="InputManager"></a>InputManager</h4><p>InputManager 是 EasyTouch 控制器的一个核心概念，它用于管理虚拟控制器的输入。可以创建多个 InputManager 实例，并为每个实例配置不同的控制器类型。每个 InputManager 都可以拥有一个或多个虚拟控制器，这些控制器可以在不同的游戏场景或不同的控制方案中使用。</p><h4 id="Joystick"><a href="#Joystick" class="headerlink" title="Joystick"></a>Joystick</h4><p>Joystick（摇杆）是一种常用的虚拟控制器，用于模拟传统游戏手柄上的摇杆。它通常由两个部分组成：底座（Base）和 Thumb（拇指触摸部分）。玩家可以通过触摸并移动 Thumb 来控制游戏中的角色或摄像机等。</p><p>底座：是 Joystick 的固定部分，玩家不能直接与其互动。<br>Thumb：玩家可以通过触摸并移动 Thumb 来控制游戏中的角色或摄像机等。<br>在 EasyTouch 中，你可以配置 Joystick 的各种属性，如：</p><ol><li><p><strong>主参数</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/Controls/JoystickMain.png" alt="JoystickMain"></p><ul><li>Activated : 是否激活</li><li>Visible : 是否可见,不可见也就不能使用</li><li>Use Fixed Update : 是否加入帧更新,适用物理控制</li><li>Unregister at disabling time : 禁用时取消已经注册的事件</li></ul></li><li><p><strong>Position &amp; Size</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/Controls/JoystickPos&Size.png" alt="JoystickPos&amp;Size"></p><ul><li>Type:摇杆的类型,包括 Static 和 Dynamic,静态遥感位置固定,动态摇杆生成在第一次点击屏幕的位置,动态摇杆可以控制可生成区域,也可自定义区域(使用 Controls 中的 Area)</li><li>Anchor:锚点设置,本质上就是 UI 元素,<strong>建议不要直接使用 Rect Transform 设置,存在某种 Bug,可以先使用提供的设置调到大概位置,再修改 Anchor 为 User Define,再在 Scene 中微调</strong></li><li>No offset thumb: 是否开启遥感偏移,关闭时鼠标点哪里,遥感就那个位置跟随,开启后遥感自动将位置移到鼠标对应遥感中心</li><li>No return of the thumb: 是否放回遥感位置,即松开时摇杆是否归位</li><li>Background size:背景图片大小</li><li>Thumb size: 遥感大小</li><li>Radius based on: 半径基于宽度还是高度,或者自定义</li></ul></li><li><p><strong>Axes properties</strong></p><p><img src="/../img/UnityInputSystemBlog/EasyTouch/Controls/JoystickAxes.png" alt="JoystickAxes"></p><ul><li>Turn &amp; Move direction Action：开启可以直接以第三人称控制移动和旋转</li><li>Horizontal axis: 轴输入名称,惟一</li><li>General setting<ul><li>React on : 触发方式点击或按下</li><li>Dead zone method: 死区控制方式,传统或线性</li><li>Dead length:死去范围</li><li>Inverted axis:反转轴</li><li>On&#x2F;Off Threshold:开&#x2F;关阈值,该值用于确定轴第一次被认为向下的阈值。使用此值，如果你想管理你的轴模式开&#x2F;关，或基础上的直接行动向下轴。</li><li>Speed: 控制的速度</li></ul></li><li>Direction action(optional)<ul><li>Auto link on tag : 根据 tag 自动连接</li></ul></li></ul></li></ol><h4 id="Other-Controls"><a href="#Other-Controls" class="headerlink" title="Other Controls"></a>Other Controls</h4><p>其他控件如 D-Pad,Button,TouchPad,Area 参数上都和 Joystick 相差不大,详情可在文档<strong>EasyTouchControls_UserDocumentation.pdf</strong>中查看.</p><h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h3><p>EasyTouch 插件大大简化了触摸输入的处理，使开发者能够更加专注于游戏的核心玩法和用户体验，而不是陷入底层触摸事件的细节处理中。如果你正在开发一款需要复杂触摸控制的移动游戏，EasyTouch 绝对是一个值得考虑的强大工具。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;输入&quot;&gt;&lt;a href=&quot;#输入&quot; class=&quot;headerlink&quot; title=&quot;输入&quot;&gt;&lt;/a&gt;输入&lt;/h1&gt;&lt;p&gt;Unity 的输入系统是用于处理用户输入的核心模块，它使开发者能够捕获和响应来自键盘、鼠标、触摸屏、游戏手柄等各种输入设备的输入事件。Uni</summary>
      
    
    
    
    <category term="unity基础" scheme="http://example.com/categories/unity%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="unity" scheme="http://example.com/tags/unity/"/>
    
  </entry>
  
  <entry>
    <title>Unity 资源管理基础知识</title>
    <link href="http://example.com/2024/07/06/Unity-%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    <id>http://example.com/2024/07/06/Unity-%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/</id>
    <published>2024-07-06T11:08:12.000Z</published>
    <updated>2024-07-30T02:13:26.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="各类型资源以及导入面板相关参数"><a href="#各类型资源以及导入面板相关参数" class="headerlink" title="各类型资源以及导入面板相关参数"></a>各类型资源以及导入面板相关参数</h1><p>Unity 是一款强大的跨平台游戏开发引擎，它支持多种类型的资源，这些资源可以被用于构建游戏场景、动画、交互以及音效等。以下是一些常见的 Unity 资源类型及其用途：</p><h2 id="纹理和图像-Textures-and-Images"><a href="#纹理和图像-Textures-and-Images" class="headerlink" title="纹理和图像 (Textures and Images)"></a>纹理和图像 (Textures and Images)</h2><p>包括 BMP, TIF, TGA, JPG, PNG, PSD 等格式，用于材质、UI 元素或贴图。<br>Inspector 面板:<br><img src="/../img/UnityInspector/TextureIns.png" alt="TextureIns"></p><p><strong>Texture Type</strong></p><ol><li><p><strong>Default:</strong> 这是用于所有纹理的最常用设置。此选项可用于访问大多数纹理导入属性。</p></li><li><p><strong>Normal map:</strong> 选择此选项可将颜色通道转换为适合实时法线贴图的格式。</p></li><li><p><strong>Editor GUI and Legacy GUI:</strong> 在任何 HUD 或 GUI 控件上使用纹理.</p></li><li><p><strong>Sprite(2D and UI):</strong> 在 2D 游戏中使用该纹理作为精灵.</p></li><li><p><strong>Cursor:</strong> 将纹理用作自定义游标.</p></li><li><p><strong>Cookie:</strong> 此选项可通过基本参数来设置纹理，从而将其用于场景光源的剪影.</p></li><li><p><strong>光照贴图(Light map):</strong> 允许将纹理编码为特定格式（RGBM 或 dLDR，具体取决于平台）并通过后期处理步骤对纹理数据进行处理（推拉式扩张通道）</p></li><li><p><strong>Single Channel:</strong> 纹理中只需要一个通道</p></li></ol><p>各属性解释来自<a href="https://docs.unity3d.com/cn/2021.3/Manual/TextureTypes.html#Default">unity 手册</a><br><img src="/../img/UnityInspector/DefaultUintyDoc.png" alt="Default"></p><p>子选择参考<a href="%22https://docs.unity3d.com/cn/2021.3/Manual/class-TextureImporter.html#AlphaSrc%22">unity 手册</a></p><h2 id="模型-Models"><a href="#模型-Models" class="headerlink" title="模型 (Models)"></a>模型 (Models)</h2><p>.fbx, .obj, .dae 等格式的 3D 模型，用于游戏中的物体和角色。</p><ol><li><p>Model</p><p>一个 3D 模型可表示角色、建筑物或家具。在这些情况下，Unity 从单个模型文件创建多个资源。在 Project 窗口中，主导入对象是模型<strong>预制件</strong>。通常，该模型预制件还会引用多个<strong>网格</strong>对象。<br><img src="/../img/UnityInspector/ModelIns.png" alt="ModelIns"></p><ul><li>场景 (Scene) 级别的属性，比如，是否导入光源和摄像机以及使用何种缩放因子。<br>详细参数参考<a href="https://docs.unity3d.com/cn/2021.3/Manual/FBXImporter-Model.html">unity 手册</a><br><img src="/../img/UnityInspector/ModelScene.png" alt="ModelScene"></li><li>特定于网格 (Meshes) 的属性。<br>详细参数参考<a href="https://docs.unity3d.com/cn/2021.3/Manual/FBXImporter-Model.html">unity 手册</a><br><img src="/../img/UnityInspector/ModelMesh.png" alt="ModelMesh"></li><li>几何体 (Geometry) 相关属性，用于处理拓扑、UV 和法线。<br>详细参数参考<a href="https://docs.unity3d.com/cn/2021.3/Manual/FBXImporter-Model.html">unity 手册</a><br><img src="/../img/UnityInspector/ModelGeometry.png" alt="ModelGeometry"></li></ul></li><li><p>Rig</p><p>一个骨架 (Rig)（有时称为 skeleton（骨骼框架）<em>）包含一组以层级视图排列的变形体 (deformer)，这些变形体在 3D 建模应用程序（如 Autodesk® 3ds Max® 或 Autodesk® Maya®）中创建的一个或多个模型上对网格（有时称为</em>皮肤<em>）进行动画化。对于__人形 (Humanoid)</em> 和<strong>通用 (Generic)（非人形）模型，Unity 会创建 Avatar</strong> 来使导入的骨架与 Unity 游戏对象 (GameObject) 协调。</p><table><thead><tr><th>Animation Type</th><th align="left">功能</th></tr></thead><tbody><tr><td>None</td><td align="left">没有动画</td></tr><tr><td>Legacy</td><td align="left">旧版动画</td></tr><tr><td>Generic</td><td align="left">通用模型(非人形)</td></tr><tr><td>Humanoid</td><td align="left">人形模型</td></tr></tbody></table><p><a href="https://docs.unity3d.com/cn/2021.3/Manual/FBXImporter-Rig.html">参考手册</a></p></li><li><p>Animation</p><p>可以定义一组帧上发生的任何不同姿势（例如行走、奔跑甚至空闲状态（从一只脚移动到另一只脚））的序列作为动画剪辑。可以对任何具有相同骨架的模型重用剪辑。通常，单个文件包含多个不同的动作，可将每个动作定义为特定的<strong>动画剪辑</strong>。<a href="https://docs.unity3d.com/cn/2021.3/Manual/class-AnimationClip.html">参数详解</a></p></li><li><p>Materials</p><p>可以提取材质和纹理或将它们嵌入模型中。还可以调整材质在模型中的贴图方式。</p></li></ol><h2 id="音频-Audio"><a href="#音频-Audio" class="headerlink" title="音频 (Audio)"></a>音频 (Audio)</h2><p>包括 WAV, MP3, OGG 等格式的声音文件，用于背景音乐和音效。<br><a href="https://docs.unity3d.com/cn/2021.3/Manual/class-AudioClip.html">面板参数解释</a></p><h2 id="视频-Video"><a href="#视频-Video" class="headerlink" title="视频 (Video)"></a>视频 (Video)</h2><p>视频文件可以用于过场动画或 UI 元素，例如.mp4 或.webm 格式。<a href="https://docs.unity3d.com/cn/2021.3/Manual/class-VideoClip.html">视频参数解释</a>.</p><p>使用 VideoPlayer 组件控制播放视频，<a href="https://docs.unity3d.com/cn/2021.3/Manual/class-VideoPlayer.html">组件参数解释</a></p><h2 id="脚本-Scripts"><a href="#脚本-Scripts" class="headerlink" title="脚本 (Scripts)"></a>脚本 (Scripts)</h2><p>C#或 JavaScript 编写的代码文件，用于控制游戏逻辑和行为。</p><h2 id="材质-Materials"><a href="#材质-Materials" class="headerlink" title="材质 (Materials)"></a>材质 (Materials)</h2><p>包含着色器设置和纹理引用，用于渲染物体表面。</p><h2 id="动画-Animations"><a href="#动画-Animations" class="headerlink" title="动画 (Animations)"></a>动画 (Animations)</h2><p>通过 Unity 的动画系统创建的动画剪辑，用于角色或对象的动作。<br><a href="https://docs.unity3d.com/cn/2021.3/Manual/class-AnimationClip.html">参数详解</a></p><h2 id="预制件-Prefabs"><a href="#预制件-Prefabs" class="headerlink" title="预制件 (Prefabs)"></a>预制件 (Prefabs)</h2><p>预先设置好的游戏对象实例，可以多次重复使用。</p><h2 id="粒子系统-Particle-Systems"><a href="#粒子系统-Particle-Systems" class="headerlink" title="粒子系统 (Particle Systems)"></a>粒子系统 (Particle Systems)</h2><p>用于创建动态效果如火焰、烟雾或火花。<br><a href="https://docs.unity3d.com/cn/2021.3/Manual/ParticleSystemModules.html">粒子系统模块</a></p><h2 id="网格-Meshes"><a href="#网格-Meshes" class="headerlink" title="网格 (Meshes)"></a>网格 (Meshes)</h2><p>定义模型的几何形状，通常与模型文件一起导入。</p><h2 id="着色器-Shaders"><a href="#着色器-Shaders" class="headerlink" title="着色器 (Shaders)"></a>着色器 (Shaders)</h2><p>控制如何渲染物体的光照和颜色，可以是内置的也可以是自定义的。</p><h2 id="字体-Fonts"><a href="#字体-Fonts" class="headerlink" title="字体 (Fonts)"></a>字体 (Fonts)</h2><p>用于 UI 文本显示，可以是位图或动态字体。<br><a href="https://docs.unity3d.com/cn/2021.3/Manual/class-Font.html">Font assets</a></p><h2 id="场景-Scenes"><a href="#场景-Scenes" class="headerlink" title="场景 (Scenes)"></a>场景 (Scenes)</h2><p>游戏世界的布局，包含游戏对象和相机。</p><h2 id="物理材质-Physics-Materials"><a href="#物理材质-Physics-Materials" class="headerlink" title="物理材质 (Physics Materials)"></a>物理材质 (Physics Materials)</h2><p>控制物理碰撞的摩擦力和弹力。<br><a href="https://docs.unity3d.com/cn/2021.3/Manual/class-PhysicMaterial.html">Physic Mat</a></p><h2 id="天空盒-Skyboxes"><a href="#天空盒-Skyboxes" class="headerlink" title="天空盒 (Skyboxes)"></a>天空盒 (Skyboxes)</h2><p>通常是六面立方体贴图，用于模拟远处的天空和环境。</p><h2 id="光照贴图-Lightmaps"><a href="#光照贴图-Lightmaps" class="headerlink" title="光照贴图 (Lightmaps)"></a>光照贴图 (Lightmaps)</h2><p>预计算的光照数据，用于提高渲染效率。</p><h2 id="动画控制器-Animator-Controllers"><a href="#动画控制器-Animator-Controllers" class="headerlink" title="动画控制器 (Animator Controllers)"></a>动画控制器 (Animator Controllers)</h2><p>用于管理复杂的动画状态和过渡。</p><h2 id="输入映射-Input-Axes"><a href="#输入映射-Input-Axes" class="headerlink" title="输入映射 (Input Axes)"></a>输入映射 (Input Axes)</h2><p>定义玩家输入的映射，如移动和跳跃。</p><h2 id="资源文件-Resource-Files"><a href="#资源文件-Resource-Files" class="headerlink" title="资源文件 (Resource Files)"></a>资源文件 (Resource Files)</h2><p>可以是 JSON、XML 或其他格式的数据文件，用于存储游戏数据。</p><h1 id="AssetBundle-打包和加载"><a href="#AssetBundle-打包和加载" class="headerlink" title="AssetBundle 打包和加载"></a>AssetBundle 打包和加载</h1><p>AssetBundles 是 Unity 引擎中用于资源管理的一种机制，主要用于在运行时从外部加载游戏资源，如纹理、模型、音频文件等。这种机制允许开发者将游戏资源打包成独立的文件，然后在游戏运行时按需下载和加载这些文件，从而实现资源的动态管理和优化。</p><h2 id="分配资源"><a href="#分配资源" class="headerlink" title="分配资源"></a>分配资源</h2><p>选择可打包资源，即如纹理、模型、音频文件、预制件、场景等，进入 inspector 窗口<br>在 inspector 窗口中选择资源分配的 AssetBundle 包名，可以以文件夹形式</p><p><img src="/../img/UnityInspector/AssetBundle.png" alt="AssetBundle"></p><p>第一个参数为包名，第二个参数为后缀</p><h2 id="构建-AssetBundle"><a href="#构建-AssetBundle" class="headerlink" title="构建 AssetBundle"></a>构建 AssetBundle</h2><p>在 Assets 文件夹中创建一个名为 <strong>Editor 的文件夹</strong>，并将包含以下内容的脚本放在该文件夹中：</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEditor;</span><br><span class="line"><span class="keyword">using</span> System.IO;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CreateAssetBundles</span></span><br><span class="line">&#123;</span><br><span class="line">    [<span class="meta">MenuItem(<span class="string">&quot;Assets/Build AssetBundles&quot;</span>)</span>]</span><br><span class="line">    <span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">BuildAllAssetBundles</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">string</span> assetBundleDirectory = <span class="string">&quot;Assets/AssetBundles&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span>(!Directory.Exists(assetBundleDirectory))</span><br><span class="line">        &#123;</span><br><span class="line">            Directory.CreateDirectory(assetBundleDirectory);</span><br><span class="line">        &#125;</span><br><span class="line">        BuildPipeline.BuildAssetBundles(assetBundleDirectory,</span><br><span class="line">                                        BuildAssetBundleOptions.None,</span><br><span class="line">                                        BuildTarget.StandaloneWindows);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>BuildAssetBundles()参数解释：</p><table><thead><tr><th align="left">参数</th><th align="left">解释</th></tr></thead><tbody><tr><td align="left">outputPath</td><td align="left">输出路径</td></tr><tr><td align="left">assetBundleOptions</td><td align="left">构建选项</td></tr><tr><td align="left">BuildTarget</td><td align="left">目标平台</td></tr><tr><td align="left">常用构建选项：</td><td align="left"></td></tr></tbody></table><ol><li>None:默认构建选项，使用 LZMA 压缩方式</li><li>UncompressedAssetBundle:不压缩</li><li>ChunkBasedCompression: 按块级压缩,即使用 LZ4 压缩方式</li></ol><h2 id="加载-AssetBundle"><a href="#加载-AssetBundle" class="headerlink" title="加载 AssetBundle"></a>加载 AssetBundle</h2><p>在使用 AB 包中的资源时，需要先将 AB 包加载出来，以下包含三种常用的加载方式，可以选择使用同步加载或者异步加载，异步加载需要使用协程</p><ol><li>AssetBundle.LoadFromMemoryAsync</li></ol><p>此函数采用包含 AssetBundle 数据的字节数组。也可以根据需要传递 CRC 值。如果捆绑包采用的是 LZMA 压缩方式，将在加载时解压缩 AssetBundle。LZ4 压缩包则会以压缩状态加载。</p><p>以下是如何使用此方法的一个示例：</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">using</span> UnityEngine;</span><br><span class="line"><span class="keyword">using</span> System.Collections;</span><br><span class="line"><span class="keyword">using</span> System.IO;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Example</span> : <span class="title">MonoBehaviour</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="function">IEnumerator <span class="title">LoadFromMemoryAsync</span>(<span class="params"><span class="built_in">string</span> path</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); <span class="comment">//这里是读的文件的字节信息</span></span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">return</span> createRequest;</span><br><span class="line">        AssetBundle bundle = createRequest.assetBundle;</span><br><span class="line">        <span class="keyword">var</span> prefab = bundle.LoadAsset&lt;GameObject&gt;(<span class="string">&quot;MyObject&quot;</span>);</span><br><span class="line">        Instantiate(prefab);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>AssetBundle.LoadFromFile</li></ol><p>从本地存储中加载未压缩的捆绑包时，此 API 非常高效。如果捆绑包未压缩或采用了数据块 (LZ4) 压缩方式，LoadFromFile 将直接从磁盘加载捆绑包。使用此方法加载完全压缩的 (LZMA) 捆绑包将首先解压缩捆绑包，然后再将其加载到内存中。</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Start</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">var</span> myLoadedAssetBundle = AssetBundle ab = AssetBundle.LoadFromFile(<span class="string">&quot;AssetBundles/PC/tt&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (myLoadedAssetBundle == <span class="literal">null</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            Debug.Log(<span class="string">&quot;Failed to load AssetBundle!&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">var</span> prefab = myLoadedAssetBundle.LoadAsset&lt;GameObject&gt;(<span class="string">&quot;MyObject&quot;</span>);</span><br><span class="line">        Instantiate(prefab);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p><strong>异步加载示例</strong></p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function">IEnumerator <span class="title">LoadAssetBundlesAsync</span>(<span class="params"><span class="built_in">string</span> abName,<span class="built_in">string</span> resName</span>)</span></span><br><span class="line">    &#123;</span><br><span class="line">        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(<span class="string">&quot;AssetBundles/PC/&quot;</span> + abName);</span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">return</span> abcr;</span><br><span class="line">        AssetBundleRequest abr = abcr.assetBundle.LoadAssetAsync(resName, <span class="keyword">typeof</span>(Sprite));</span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">return</span> abr;</span><br><span class="line">        image.sprite = abr.asset <span class="keyword">as</span> Sprite;</span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>UnityWebRequestAssetBundle 的 DownloadHandlerAssetBundle （Unity 5.3 或更高版本）</li></ol><p>UnityWebRequestAssetBundle 有一个特定 API 调用来处理 AssetBundle。首先，需要使用 UnityWebRequestAssetBundle.GetAssetBundle 来创建 Web 请求。返回请求后，请将请求对象传递给 DownloadHandlerAssetBundle.GetContent(UnityWebRequestAssetBundle)。GetContent 调用将返回 AssetBundle 对象。</p><p>下载捆绑包后，还可以在 DownloadHandlerAssetBundle 类上使用 assetBundle 属性，从而以 AssetBundle.LoadFromFile 的效率加载 AssetBundle。</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Start</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//加载服务器ab包</span></span><br><span class="line">        StartCoroutine(LoadAssetBundle());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">IEnumerator <span class="title">LoadAssetBundle</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">string</span> uri = <span class="string">@&quot;File:///E:\God\Unity\Unity_Study_Demo\AssetBundles\PC\tt&quot;</span>;</span><br><span class="line">        UnityWebRequest requestAssetBundle = UnityWebRequestAssetBundle.GetAssetBundle(uri, <span class="number">0</span>);</span><br><span class="line">        <span class="keyword">yield</span> <span class="keyword">return</span> requestAssetBundle.Send();</span><br><span class="line">        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(requestAssetBundle);</span><br><span class="line">        cube = bundle.LoadAsset&lt;GameObject&gt;(<span class="string">&quot;Cube&quot;</span>);</span><br><span class="line">        Instantiate(cube,Vector3.zero,Quaternion.identity);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><ol start="4"><li><p>依赖关系</p><p><strong>ab 包依赖关系:</strong><br>在一个 ab 包中的资源依赖其他 ab 包中的某个资源，比如 ab1 中的 model 使用了 ab2 中的 material,<br>使用时如果只加载 ab1 然后使用资源 model，会出现依赖的 material 丢失的情况<br>解决：1.依赖文件放在同一包下。2.加载 ab1 时同时加载 ab2(不用指定，ab2 加载出来后依赖它的资源会自动使用)</p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">region</span> 加载cube的依赖资源，加载AssetBundle清单</span></span><br><span class="line">     <span class="comment">//明确依赖的ab包</span></span><br><span class="line">     <span class="comment">//AssetBundle ab2 = AssetBundle.LoadFromFile(&quot;AssetBundles/PC/material&quot;);</span></span><br><span class="line">     <span class="comment">//问题： 一个包中的资源可能会依赖许多其他包中的资源</span></span><br><span class="line">     <span class="comment">//解决： 使用主包寻找依赖关系批量加载(主包就是构建ab包时自动生成的与文件夹名同名的包，内部记载了各包之间的关系)</span></span><br><span class="line">     <span class="comment">//依赖包的关键知识：利用主包 获取依赖信息</span></span><br><span class="line">     <span class="comment">//1.加载主包</span></span><br><span class="line">     AssetBundle abMain = AssetBundle.LoadFromFile(<span class="string">&quot;AssetBundles/PC/PC&quot;</span>);</span><br><span class="line">     <span class="comment">//2.加载主包中的固定文件</span></span><br><span class="line">     AssetBundleManifest abManifest = abMain.LoadAsset(<span class="string">&quot;AssetBundleManifest&quot;</span>, <span class="keyword">typeof</span>(AssetBundleManifest)) <span class="keyword">as</span> AssetBundleManifest;</span><br><span class="line">     <span class="comment">//3.从固定文件中获取依赖信息(名字)</span></span><br><span class="line">     <span class="built_in">string</span>[] strs = abManifest.GetAllDependencies(<span class="string">&quot;tt&quot;</span>);</span><br><span class="line">     <span class="comment">//4.加载依赖包</span></span><br><span class="line">     <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; strs.Length; i++)</span><br><span class="line">     &#123;</span><br><span class="line">         <span class="comment">//print(strs[i]);</span></span><br><span class="line">         AssetBundle.LoadFromFile(<span class="string">&quot;AssetBundles/PC/&quot;</span>+strs[i]);</span><br><span class="line">     &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endregion</span></span></span><br></pre></td></tr></table></figure></li></ol><h2 id="加载资源"><a href="#加载资源" class="headerlink" title="加载资源"></a>加载资源</h2><p>加载资源也包含同步加载和异步加载，可以加载包中的一个资源，也可以加载包中的所有资源</p><p>通用方式：</p><blockquote><p>T objectFromBundle &#x3D; bundleObject.LoadAsset<T>(assetName);<br>T objectFromBundle &#x3D; bundleObject.LoadAllAssets<T>();</p></blockquote><p>由于 Lua 中不支持泛型,使用:</p><blockquote><p>Object cube &#x3D; ab.LoadAsset(“Cube”, typeof(GameObject));<br>Object[] objects &#x3D; ab.LoadAllAssets(typeof(GameObject));</p></blockquote><p>还有一种需要资源名称,但是可能会取到 ab 包中同名的不同类型文件</p><blockquote><p>GameObject obj &#x3D; ab.LoadAsset<GameObject>(“RPGHeroHP”);<br>Object[] objects &#x3D; ab.LoadAllAssets();</p></blockquote><h2 id="卸载-AssetBundle"><a href="#卸载-AssetBundle" class="headerlink" title="卸载 AssetBundle"></a>卸载 AssetBundle</h2><p>何时加载和卸载 AssetBundle 非常重要。不正确地卸载 AssetBundle 会导致在内存中复制对象或其他不良情况，例如缺少纹理。</p><p>关于 AssetBundle 管理的最需要了解的是何时调用 AssetBundle.Unload(bool) , 或者 AssetBundle.UnloadAsync(bool) , 以及应该将 true 还是 false 传递到函数调用中。Unload 是一个非静态函数，可用于卸载 AssetBundle。此 API 会卸载正在调用的 AssetBundle 的标头信息。该参数指示是否还要卸载通过此 AssetBundle 实例化的所有对象。</p><p>通常，使用 AssetBundle.Unload(false) 不会获得理想的情况。大多数项目应该使用 AssetBundle.Unload(true) 并采用一种方法来确保对象不会重复。两种常用方法是：</p><ul><li><p>在应用程序生命周期中具有明确定义的卸载瞬态 AssetBundle 的时间点，例如在关卡之间或在加载屏幕期间。</p></li><li><p>维护单个对象的<strong>引用计数</strong>，仅当未使用所有组成对象时才卸载 AssetBundle。这允许应用程序卸载和重新加载单个对象，而无需复制内存。</p></li></ul><p>还提供一个静态方法来卸载所有 AB 包</p><blockquote><p>&#x2F;&#x2F;卸载全部 ab 包，参数为 true 时表示连同 ab 包生成的资源一起卸载，false 只卸载包，资源保留</p></blockquote><blockquote><p>AssetBundle.UnloadAllAssetBundles(true);</p></blockquote><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="AssetBundleBrowser"><a href="#AssetBundleBrowser" class="headerlink" title="AssetBundleBrowser"></a>AssetBundleBrowser</h3><p>GitHub 上下载<a href="https://github.com/Unity-Technologies/AssetBundles-Browser">AssetBundleBrowser</a></p><p>或者在项目 PackageManager 中选择选择 Add package from git URL,输入 URL:</p><blockquote><p><a href="https://github.com/Unity-Technologies/AssetBundles-Browser.git">https://github.com/Unity-Technologies/AssetBundles-Browser.git</a></p></blockquote><p>安装成功后就可以在 Windows-&gt;AssetBundleBrowser 中打开面板<br><img src="/../img/UnityInspector/AssetBundleBrowser.png" alt="AssetBundleBrowser"></p><p>面板中可以看到以及分类好的包体资源以及相关信息<br>确认无误后就可以在 Build 面板中进行包体构建<br><img src="/../img/UnityInspector/AssetBundleBrowserBuild.png" alt="AssetBundleBrowserBuild"><br>主要参数包括构建平台,输出路径,是否清空文件夹在构建,复制到 StreamingAssets 文件夹下以及压缩方式等。更多细节可以<a href="https://docs.unity3d.com/Packages/com.unity.assetbundlebrowser@1.7/manual/index.html">官网查看</a></p><h3 id="生成的-AB-包文件"><a href="#生成的-AB-包文件" class="headerlink" title="生成的 AB 包文件"></a>生成的 AB 包文件</h3><p><img src="/../img/UnityInspector/AssetBundleFiles.png" alt="AssetBundleFiles"></p><ol><li><p><strong>AssetBundle 文件</strong></p><p>其中包含在运行时为了加载资源而需要加载的内容。<br>AssetBundle 文件是一个存档，在内部包含多个文件。此存档的结构根据它是 AssetBundle 还是场景 AssetBundle 可能会略有不同。以下是普通 AssetBundle 的结构：<br><img src="/../img/UnityInspector/AssetBundleStruct.png" alt="AssetBundleStruct"><br>场景 AssetBundle 与普通 AssetBundle 的不同之处在于，它针对场景及其内容的串流加载进行了优化。</p></li><li><p><strong>清单文件</strong></p><p>对于生成的每个包(包括附加的 Manifest bundle)，都会生成一个相关的清单文件。<br>清单文件的扩展名为.manifest，可以用任何文本编辑器打开。<br>它包含诸如循环冗余校验(CRC)数据和 bundle 的依赖项数据之类的信息。</p></li></ol><h3 id="AssetBundle-压缩方式"><a href="#AssetBundle-压缩方式" class="headerlink" title="AssetBundle 压缩方式"></a>AssetBundle 压缩方式</h3><p><strong>压缩类型：</strong></p><ol><li>LZ4：这是 Unity 默认使用的压缩方法，提供快速的压缩和解压速度，同时具有较好的压缩比。LZ4 非常适合用于游戏资源，因为它能在压缩和解压速度与压缩比率之间取得良好的平衡。使用哪个资源就解压哪个资源，读取快，内存略大。</li><li>LZMA：提供更高的压缩比率，但压缩和解压速度较慢。如果你的 AssetBundles 非常大，并且下载速度不是主要瓶颈，可以考虑使用 LZMA。使用一个资源需要解压整个数据流，读取慢，内存小。</li><li>No Compression：不使用任何压缩。虽然这会导致更大的文件大小，但在某些情况下可能更优，比如对于一些小文件或者已经高度压缩的资源（如 PNG 图像）。不用解压，读取快，内存大</li></ol><p><strong>压缩策略</strong></p><ol><li>按需压缩：并非所有的资源都需要压缩。例如，一些小的资源文件可能在压缩后的大小变化不大，甚至可能因为额外的压缩和解压开销而变慢。因此，识别哪些资源应该被压缩，哪些不应该，是很重要的。</li><li>预热压缩：在游戏启动时预加载一些关键的 AssetBundles，可以避免玩家在游戏过程中遇到长时间的加载等待。这种预加载通常使用 LZ4 压缩，因为它能够快速解压。</li></ol><h3 id="分组策略"><a href="#分组策略" class="headerlink" title="分组策略"></a>分组策略</h3><p>因为在 unity 中给一个资源配置了 AssetBundle,系统会自动将该资源依赖的其他资源附加到当前 AB 包中,只是不会表现出来.如果现在有另一个资源也依赖这个资源,打包时在这个包中也会自动添加一个依赖资源，这就导致了资源重复，从而导致浪费内存。所以需要合理的分组策略将资源打包成不同的包，比如将共同依赖的资源打包成一个独立的包，那么依赖它的包就不会自动再打包一次了。</p><p><strong>常用分组策略:</strong></p><ol><li><p>逻辑实体分组</p><p>逻辑实体分组是指根据资源所代表的项目功能部分将资源分配给 AssetBundle。这包括各种不同部分，比如用户界面、角色、环境以及在应用程序整个生命周期中可能经常出现的任何其他内容。</p></li><li><p>类型分组</p><p>根据此策略，可以将相似类型的资源（例如音频轨道或语言本地化文件）分配到单个 AssetBundle。</p></li><li><p>并发内容分组</p><p>并发内容分组是指将需要同时加载和使用的资源捆绑在一起。可以将这些类型的捆绑包用于基于关卡的游戏（其中每个关卡包含完全独特的角色、纹理、音乐等）。有时可能希望确保其中一个 AssetBundle 中的资源与该捆绑包中的其余资源同时使用。依赖于并发内容分组捆绑包中的单个资源会导致加载时间显著增加。您将被迫下载该单个资源的整个捆绑包。</p></li></ol><p>无论遵循何种策略，下面这些额外提示都有助于掌控全局：</p><ul><li>将频繁更新的对象与很少更改的对象拆分到不同的 AssetBundle 中</li><li>将可能同时加载的对象分到一组。例如模型及其纹理和动画</li><li>如果发现多个 AssetBundle 中的多个对象依赖于另一个完全不同的 AssetBundle 中的单个资源，请将依赖项移动到单独的 AssetBundle。如果多个 AssetBundle 引用其他 AssetBundle 中的同一组资源，一种有价值的做法可能是将这些依赖项拉入一个共享 AssetBundle 来减少重复。</li><li>如果不可能同时加载两组对象（例如标清资源和高清资源），请确保它们位于各自的 AssetBundle 中。</li><li>如果经常同时加载的 AssetBundle 少于 50%，可以考虑拆分 AssetBundle</li><li>考虑将多个小型的（少于 5 到 10 个资源）但经常同时加载内容的 AssetBundle 组合在一起</li><li>如果一组对象只是同一对象的不同版本，请考虑使用 AssetBundle 变体</li></ul><h3 id="安全性和完整性"><a href="#安全性和完整性" class="headerlink" title="安全性和完整性"></a>安全性和完整性</h3><ol><li>采用安全协议下载</li><li>CRC 校验和</li><li>用户生成的内容</li></ol><h1 id="Addressable-简单使用"><a href="#Addressable-简单使用" class="headerlink" title="Addressable 简单使用"></a>Addressable 简单使用</h1><p>Unity 的 Addressables 系统是一种先进的资源管理和加载解决方案，旨在提供更加灵活和高效的资源处理方式。Addressables 系统克服了传统 AssetBundles 的一些限制，提供了更强大的功能和更好的性能，特别是在处理大量资源和复杂依赖关系时。</p><h2 id="准备资源"><a href="#准备资源" class="headerlink" title="准备资源"></a>准备资源</h2><ol><li><p>下载 Addressables 插件</p><p>在 Package Manager 中找到 Addressables 插件进行安装</p></li><li><p>创建默认 Setting</p><p>打开 Windows-&gt;Asset Management-&gt;Addressable -&gt;Group<br><img src="/../img/UnityInspector/Addressables/OpenWindows.png" alt="OpenWindows"><br>点击 Create Addressables Settings 后会在 Asset 文件夹下自动生成一个 AddressableAssetsData 文件夹，里面包含了对 Addressable 的配置文件等</p></li></ol><h2 id="构建资源"><a href="#构建资源" class="headerlink" title="构建资源"></a>构建资源</h2><p>使用 Addressables 系统提供的构建工具，将资源打包成适合运行时加载的格式。</p><p>点击可打包资源，勾选顶部的 Addressable 选项或拖动资源到 Addressables Group 窗口中对资源进行打包<br><img src="/../img/UnityInspector/Addressables/SelectObj.png" alt="SelectObj"></p><ol><li>新建 Group</li></ol><p>在 Addressable Group 窗口上方 New-&gt; Packed Asset 或者在窗口空白处右击鼠标点击 Create New Group -&gt; Packed Asset<br>每次新建一个 Group,在 Assets&#x2F;AddressableAssetsData&#x2F;AssetGroups&#x2F;Schemas 目录下会生成一个对应的配置文件<br><img src="/../img/UnityInspector/Addressables/CreateGroup.png" alt="CreateGroup"></p><ol start="2"><li>Group</li></ol><p>Group 是可寻址系统的主要组织单位,要控制 Unity 在内容构建期间处理资源的方式，请将可寻址对象组织成组，并根据需要为每个组分配不同的设置。</p><ol start="3"><li>Label</li></ol><p>标签在可寻址系统中有多种用途，包括：</p><ul><li>可以使用一个或多个标签作为键来标识要在运行时加载的资产。</li><li>可以根据分配的标签将组中的资产打包到 AssetBundle 中。</li><li>可以使用“组”窗口的筛选框中的标签来帮助查找已标记的资产</li></ul><ol start="4"><li><strong>资源打包</strong></li></ol><p>资源配置完成后点击 Addressable-&gt;Build-&gt;New Build-&gt;Default Build Script 即可构建 bundle 包</p><p>默认情况下系统会将每一个 Group 打包成一个 bundle 文件,但是根据需要可以修改配置文件,将文件单独打包,或者按标签打包<br><img src="/../img/UnityInspector/Addressables/BundleMode.png" alt="BundleMode"></p><p>默认情况下 bundle 包都是保存在本地的,也可以通过配置文件修改位置<br><img src="/../img/UnityInspector/Addressables/BuildPath.png" alt="BuildPath"></p><ol start="5"><li><strong>修改 LoadPath&amp;BuildPath</strong></li></ol><p>在 Addressable Group 窗口中 Tools-&gt;Window-&gt;Profiles 或者在 Unity 中的 Windows-&gt;Asset Management-&gt;Addressables-&gt;Profiles<br><img src="/../img/UnityInspector/Addressables/Profiles.png" alt="Profiles"></p><h2 id="加载资源-1"><a href="#加载资源-1" class="headerlink" title="加载资源"></a>加载资源</h2><p>在运行时，使用 Addressables API 来异步加载资源。这可以是按需加载单个资源，或者加载一组相关的资源。</p><h3 id="加载资源方式"><a href="#加载资源方式" class="headerlink" title="加载资源方式"></a>加载资源方式</h3><ol><li><strong>通过 Addressables Name 加载</strong></li></ol><p>Addressable 提供了许多加载资源的方式,详情见<a href="https://docs.unity3d.com/Packages/com.unity.addressables@2.2/api/UnityEngine.AddressableAssets.Addressables.LoadAssetsAsync.html">Addressable API</a></p><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Start</span>()</span></span><br><span class="line">    &#123;</span><br><span class="line">        Addressables.LoadAssetAsync&lt;GameObject&gt;(<span class="string">&quot;RPGHeroHP&quot;</span>).Completed += (handle) =&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (handle.Status == AsyncOperationStatus.Succeeded)</span><br><span class="line">                Instantiate(handle.Result);</span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="comment">//批量加载</span></span><br><span class="line">        Addressables.LoadAssetsAsync&lt;Object&gt;(<span class="string">&quot;Cube&quot;</span>, (handle) =&gt; &#123; print(<span class="string">&quot;批量加载Cube&quot;</span>+handle.GetType()); &#125;);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>AsyncOperationHandle 中包含了资源的加载信息</p><p>如果是需要直接用来实例化的游戏对象,Addressable 还提供了另一个方法</p><blockquote><p>Addressables.InstantiateAsync(“RPGHeroHP”);</p></blockquote><ol start="2"><li><strong>通过 AssetReference 加载</strong></li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> AssetReference assetReference;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Start</span>()</span></span><br><span class="line">&#123;</span><br><span class="line">    AsyncOperationHandle hadle = assetReference.LoadAssetAsync&lt;GameObject&gt;();</span><br><span class="line">    hadle.Completed += (h) =&gt; &#123; print(h.Result); &#125;;</span><br><span class="line">    <span class="comment">//直接实例化游戏物体</span></span><br><span class="line">    assetReference.InstantiateAsync();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li><strong>通过 Label 加载</strong></li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> AssetLabelReference labelReference;<span class="comment">//在Inspector面板中可以设置</span></span><br><span class="line"><span class="comment">//面板设置label加载</span></span><br><span class="line">Addressables.LoadAssetsAsync&lt;GameObject&gt;(labelReference, (obj) =&gt;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 每加载完一个资源，就回调一次</span></span><br><span class="line">    Debug.Log(<span class="string">&quot;加载了一个资源： &quot;</span> + obj.name);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//输入label加载</span></span><br><span class="line">Addressables.LoadAssetAsync&lt;GameObject&gt;(<span class="string">&quot;Prefab&quot;</span>).Completed += (handle) =&gt; &#123; Instantiate(handle.Result, Vector3.zero, Quaternion.identity); &#125;;</span><br><span class="line"><span class="comment">//多个Label</span></span><br><span class="line">List&lt;<span class="built_in">string</span>&gt; keys = <span class="keyword">new</span> List&lt;<span class="built_in">string</span>&gt;() &#123; <span class="string">&quot;Prefab&quot;</span>, <span class="string">&quot;Cube&quot;</span> &#125;;</span><br><span class="line">Addressables.LoadAssetsAsync&lt;Object&gt;(keys, (handle) =&gt; &#123; print(<span class="string">&quot;Object: &quot;</span> + handle.name); &#125;, Addressables.MergeMode.Union, <span class="literal">false</span>);</span><br><span class="line"><span class="comment">//Addressables.MergeMode参数：</span></span><br><span class="line"><span class="comment">//None = 0,        默认</span></span><br><span class="line"><span class="comment">//UseFirst = 0,    使用第一个Label</span></span><br><span class="line"><span class="comment">//Union = 1,       匹配任意Label即可</span></span><br><span class="line"><span class="comment">//Intersection = 2 需要匹配全部Label</span></span><br></pre></td></tr></table></figure><ol start="4"><li><strong>加载场景</strong></li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line"><span class="comment">//加载场景</span></span><br><span class="line">Addressables.LoadSceneAsync(<span class="string">&quot;Level 2&quot;</span>, LoadSceneMode.Additive).Completed += (handle) =&gt;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (handle.Status == AsyncOperationStatus.Succeeded)</span><br><span class="line">    &#123;</span><br><span class="line">        print(<span class="string">&quot;success&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><ol start="5"><li><strong>加载资源路径</strong></li></ol><figure class="highlight c#"><table><tr><td class="code"><pre><span class="line">Addressables.LoadResourceLocationsAsync(assetReference, <span class="keyword">typeof</span>(GameObject)).Completed +=(handle)=&gt;</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span> (handle.Status == AsyncOperationStatus.Succeeded)</span><br><span class="line">    &#123;</span><br><span class="line">        Debug.Log(<span class="string">$&quot;Successfully loaded resource locations&quot;</span>);</span><br><span class="line">        <span class="keyword">foreach</span> (IResourceLocation location <span class="keyword">in</span> handle.Result)</span><br><span class="line">        &#123;</span><br><span class="line">            print(location);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Addressables-三个加载模式"><a href="#Addressables-三个加载模式" class="headerlink" title="Addressables 三个加载模式"></a>Addressables 三个加载模式</h3><ol><li><p>Use Asset Database (fastest)</p><p>这种模式是最快的加载模式，因为它直接使用 Unity 编辑器中的 AssetDatabase 来加载资源。在这种模式下，资源不会被打包成 AssetBundles，而是以原始的形式存储在磁盘上。因此，资源的加载速度非常快，因为不需要解包或解压缩的过程。 这个模式最适合在开发阶段使用，因为它是最快的，可以提供即时的反馈，帮助开发者迅速迭代和测试。然而，由于资源没有被打包，这种模式不适合最终的构建或部署，因为它会导致资源的大小和数量直接暴露给用户，容易被破解或修改。</p></li><li><p>Simulate Groups (advanced)</p><p>这种模式模拟了 AssetBundles 的行为，但资源实际上并未被打包。它提供了比 Use Asset Database 更高级的功能，如资源分组和依赖管理，同时仍然保持较快的加载速度。 在 Simulate Groups 模式下，Addressables 系统会像处理 AssetBundles 一样处理资源，包括处理资源的依赖关系，但所有这些操作都在不实际打包资源的情况下进行。这使得它成为开发和调试阶段的理想选择，因为你可以在接近生产环境的条件下测试资源的加载和管理，而无需等待打包过程。</p></li><li><p>Use Existing Build (requires built groups)</p><p>这是最接近实际生产环境的加载模式，因为它要求资源已经被打包成 AssetBundles。在 Use Existing Build 模式下，Addressables 系统将从已构建的 AssetBundles 中加载资源，这通常是最终构建和部署时所采用的方式。 这种模式提供了最好的性能和安全性，因为资源被打包并压缩，减少了存储空间和下载时间。同时，由于资源被加密，它也提供了更好的保护，防止资源被轻易访问或修改。</p></li></ol><p>选择加载模式</p><p>选择哪种加载模式取决于你的项目阶段和需求。在开发和测试阶段，通常会选择 Use Asset Database 或 Simulate Groups 模式，因为它们提供了更快的迭代速度。而在最终构建和部署阶段，应该使用 Use Existing Build 模式，以确保资源的安全性和最佳的性能。</p><h2 id="管理资源生命周期"><a href="#管理资源生命周期" class="headerlink" title="管理资源生命周期"></a>管理资源生命周期</h2><p>Addressables 提供了 API 来管理资源的生命周期，包括缓存和卸载资源</p><p><strong>资源释放</strong><br>调用 API</p><blockquote><p>Addressables.Release(handle);</p></blockquote><p>如果是 AssetReference,则用</p><blockquote><p>assetReference.ReleaseAsset();</p></blockquote><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://docs.unity3d.com/cn/2021.3/Manual/AssetWorkflow.html">unity 手册资源工作流程</a></p><p><a href="https://docs.unity3d.com/Packages/com.unity.addressables@2.2/manual/index.html">com.unity.addressable 手册</a></p><p><a href="https://blog.csdn.net/linxinfa/article/details/122390621">Unity Addressables 资源管理方式用起来太爽了，资源打包、加载、热更变得如此轻松</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;各类型资源以及导入面板相关参数&quot;&gt;&lt;a href=&quot;#各类型资源以及导入面板相关参数&quot; class=&quot;headerlink&quot; title=&quot;各类型资源以及导入面板相关参数&quot;&gt;&lt;/a&gt;各类型资源以及导入面板相关参数&lt;/h1&gt;&lt;p&gt;Unity 是一款强大的跨平台游戏开</summary>
      
    
    
    
    <category term="unity基础" scheme="http://example.com/categories/unity%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="unity" scheme="http://example.com/tags/unity/"/>
    
  </entry>
  
  <entry>
    <title>搭建自己的博客</title>
    <link href="http://example.com/2024/07/01/%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/"/>
    <id>http://example.com/2024/07/01/%E6%90%AD%E5%BB%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%8D%9A%E5%AE%A2/</id>
    <published>2024-07-01T09:15:17.000Z</published>
    <updated>2025-08-06T13:34:32.659Z</updated>
    
    <content type="html"><![CDATA[<h1 id="使用-Hexo-搭建博客"><a href="#使用-Hexo-搭建博客" class="headerlink" title="使用 Hexo 搭建博客"></a>使用 Hexo 搭建博客</h1><h2 id="什么是-Hexo"><a href="#什么是-Hexo" class="headerlink" title="什么是 Hexo"></a>什么是 Hexo</h2><p>Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown（或其他标记语言）解析文章，在几秒内，即可利用靓丽的主题生成静态网页。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><h3 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h3><p>Node.js (Node.js 版本需不低于 10.13，建议使用 Node.js 12.0 及以上版本)</p><p>Git</p><h3 id="更新-Node-js"><a href="#更新-Node-js" class="headerlink" title="更新 Node.js"></a>更新 Node.js</h3><ol><li>查看当前版本<br><br>npm -v</li><li>更新到最新版<br><br>npm install npm@latest -g</li></ol><h3 id="安装-Hexo"><a href="#安装-Hexo" class="headerlink" title="安装 Hexo"></a>安装 Hexo</h3><h4 id="打开-cmd-输入指令"><a href="#打开-cmd-输入指令" class="headerlink" title="打开 cmd 输入指令"></a>打开 cmd 输入指令</h4><blockquote><p>$ npm install -g hexo-cli<br>$ npm install -g <a href="mailto:&#104;&#101;&#120;&#x6f;&#45;&#99;&#108;&#105;&#64;&#52;&#46;&#51;&#x2e;&#x32;">&#104;&#101;&#120;&#x6f;&#45;&#99;&#108;&#105;&#64;&#52;&#46;&#51;&#x2e;&#x32;</a><br>安装记得打开管理员权限，不然可能安装失败</p></blockquote><h4 id="查看版本"><a href="#查看版本" class="headerlink" title="查看版本"></a>查看版本</h4><blockquote><p>$ hexo v<br>需要安装对应版本的npm和hexo才能正常使用，<a href="https://hexo.io/zh-cn/docs/">对比文档</a></p></blockquote><h4 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h4><blockquote><p>$ hexo init</p></blockquote><h4 id="安装主题"><a href="#安装主题" class="headerlink" title="安装主题"></a>安装主题</h4><p>1.在 <a href="https://hexo.io/themes/">主题网站</a> 选择主题并跳转到 github <br><br>2.clone <a href="https://github.com/jerryc127/hexo-theme-butterfly.git">仓库地址</a> 这里选择”hexo-theme-butterfly”</p><h4 id="配置主题"><a href="#配置主题" class="headerlink" title="配置主题"></a>配置主题</h4><p>打开根目录下的 _config.yml,找到 theme 修改为主题文件名</p><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Extensions</span></span><br><span class="line"><span class="comment">## Plugins: https://hexo.io/plugins/</span></span><br><span class="line"><span class="comment">## Themes: https://hexo.io/themes/</span></span><br><span class="line"><span class="attr">theme:</span> <span class="string">hexo-theme-butterfly</span></span><br></pre></td></tr></table></figure><p><a href="https://blog.csdn.net/mjh1667002013/article/details/129290903">butterfly 主题个性化设置</a></p><h4 id="部署-hexo"><a href="#部署-hexo" class="headerlink" title="部署 hexo"></a>部署 hexo</h4><blockquote><p>$ hexo g</p></blockquote><h4 id="启动-hexo-本地化预览"><a href="#启动-hexo-本地化预览" class="headerlink" title="启动 hexo 本地化预览"></a>启动 hexo 本地化预览</h4><blockquote><p>$ hexo s</p></blockquote><h2 id="部署到-github"><a href="#部署到-github" class="headerlink" title="部署到 github"></a>部署到 github</h2><ol><li>github 创建仓库,后缀.github.io</li><li>配置 SSH 详情见 <a href="https://developer.aliyun.com/article/1218122">https://developer.aliyun.com/article/1218122</a></li><li>修改_config.yml 文件如下:</li></ol><figure class="highlight yml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Deployment</span></span><br><span class="line"><span class="comment">## Docs: https://hexo.io/docs/one-command-deployment</span></span><br><span class="line"><span class="attr">deploy:</span></span><br><span class="line"><span class="attr">type:</span> <span class="string">git</span></span><br><span class="line"><span class="attr">repo:</span> <span class="string">git@github.com:Ankk7/Ankk7.github.io.git</span></span><br><span class="line"><span class="attr">branch:</span> <span class="string">main</span></span><br></pre></td></tr></table></figure><ol start="4"><li>根目录下安装 deploy-git ，也就是部署的命令,这样才能用命令部署到 GitHub<blockquote><p>npm install hexo-deployer-git –save</p></blockquote></li><li>依次执行<blockquote><p>hexo c #清除缓存文件 db.json 和已生成的静态文件 public</p><p>hexo g #生成网站静态文件到默认设置的 public 文件夹(hexo generate 的缩写)</p><p>hexo d #自动生成网站静态文件，并部署到设定的仓库(hexo deploy 的缩写)</p></blockquote></li></ol><h2 id="常用指令"><a href="#常用指令" class="headerlink" title="常用指令"></a>常用指令</h2><blockquote><p>hexo s #启动本地服务<br>hexo new [文件名] #新建文件<br>hexo c #清除缓存文件<br>hexo g #生成网站静态文件到默认设置的 public 文件夹<br>hexo d #生成部署</p></blockquote><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><h3 id="更改主题后启动服务器在本地页面上显示各种信息如：extends-includes-layout-pug-block-content-include-includes-recent-posts-pug-include"><a href="#更改主题后启动服务器在本地页面上显示各种信息如：extends-includes-layout-pug-block-content-include-includes-recent-posts-pug-include" class="headerlink" title="更改主题后启动服务器在本地页面上显示各种信息如：extends includes&#x2F;layout.pug block content include includes&#x2F;recent-posts.pug include"></a>更改主题后启动服务器在本地页面上显示各种信息如：extends includes&#x2F;layout.pug block content include includes&#x2F;recent-posts.pug include</h3><p>解决办法：<br>npm install –save hexo-renderer-jade hexo-generator-feed hexo-generator-sitemap hexo-browsersync hexo-generator-archive</p><h3 id="部署到-github-上后在-google-上无法访问，在-edge-上可以访问"><a href="#部署到-github-上后在-google-上无法访问，在-edge-上可以访问" class="headerlink" title="部署到 github 上后在 google 上无法访问，在 edge 上可以访问"></a>部署到 github 上后在 google 上无法访问，在 edge 上可以访问</h3><h1 id="迁移博客"><a href="#迁移博客" class="headerlink" title="迁移博客"></a>迁移博客</h1><h2 id="安装前置环境"><a href="#安装前置环境" class="headerlink" title="安装前置环境"></a>安装前置环境</h2><p>同上<a href="#%E5%AE%89%E8%A3%85">安装</a>前置的npm和hexo支持</p><h2 id="导入博客源文件"><a href="#导入博客源文件" class="headerlink" title="导入博客源文件"></a>导入博客源文件</h2><p>大致文件目录如下</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">my-blog/</span><br><span class="line">├── _config.yml       # Hexo 主配置文件</span><br><span class="line">├── package.json      # 依赖列表</span><br><span class="line">├── scaffolds/        # 模板文件</span><br><span class="line">├── source/           # 文章和资源（最重要！）</span><br><span class="line">│   ├── _posts/       # 所有文章（.md 文件）</span><br><span class="line">│   ├── _drafts/      # 草稿（可选）</span><br><span class="line">│   └── images/       # 图片等资源（可选）</span><br><span class="line">├── themes/           # 主题文件夹（如 next、butterfly）</span><br><span class="line">└── .gitignore        # Git 忽略文件</span><br></pre></td></tr></table></figure><p>检查package.json文件中的版本和依赖是否正确</p><h2 id="下载依赖"><a href="#下载依赖" class="headerlink" title="下载依赖"></a>下载依赖</h2><p>进入博客根目录下安装依赖</p><blockquote><p>npm install -force</p></blockquote><p>再安装hexo，知道能正确输出hexo版本</p><h2 id="导入主题"><a href="#导入主题" class="headerlink" title="导入主题"></a>导入主题</h2><p>进入主题所在文件夹</p><blockquote><p>blog\themes\hexo-theme-butterfly</p></blockquote><p>执行：</p><blockquote><p>git clone <a href="https://github.com/theme-next/hexo-theme-next">https://github.com/theme-next/hexo-theme-next</a> themes&#x2F;next</p></blockquote><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>如果遇到部分报错如</p><blockquote><p>ERROR Plugin load failed: hexo-browsersync</p></blockquote><p>是因为依赖加载错误，可以将依赖卸载后重新下载</p><blockquote><p>npm uninstall hexo-browsersync –save</p></blockquote><blockquote><p>npm install hexo-browsersync@latest –save</p></blockquote><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://hexo.io/zh-cn/">Hexo</a><br><a href="https://juejin.cn/post/7161634586824212488">如何更新 NodeJs 到最新版本？</a><br><a href="https://sspai.com/post/62441">hexo 本地化部署</a><br><a href="https://blog.csdn.net/qq_51714990/article/details/127267185">Hexo 更换主题后，启动页面报错</a><br><a href="https://developer.aliyun.com/article/1218122">将 hexo 部署到 GitHub 上</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;使用-Hexo-搭建博客&quot;&gt;&lt;a href=&quot;#使用-Hexo-搭建博客&quot; class=&quot;headerlink&quot; title=&quot;使用 Hexo 搭建博客&quot;&gt;&lt;/a&gt;使用 Hexo 搭建博客&lt;/h1&gt;&lt;h2 id=&quot;什么是-Hexo&quot;&gt;&lt;a href=&quot;#什么是-H</summary>
      
    
    
    
    <category term="博客" scheme="http://example.com/categories/%E5%8D%9A%E5%AE%A2/"/>
    
    
    <category term="博客" scheme="http://example.com/tags/%E5%8D%9A%E5%AE%A2/"/>
    
  </entry>
  
</feed>
