Unity3d 周分享(19期 2019.6.22 )

选自过去1~2周 自己所看到外文内容:https://twitter.com/unity3d 和各种其他博客来源吧 

1、 #UnityTips您知道OnOpenAsset属性吗? 它允许您在项目窗口中双击资源时调用方法。 例如可以用来打开/编辑一些scriptableobject。

 

2、https://www.febucci.com/2018/11/unity-tips-collection/

  1. Multiply Inspector’s editing speed
  2. Built-in shaders
  3. MinMax Attribute
  4. Animation Curves
  5. Un-dock the Preview window
  6. Hierarchy Organization
  7. Particle System Playback time
  8. EditorOnly tag
  9. Assign a shader to a material upon creation
  10. Insert array element in the Inspector
  11. Show the content of multiple folders
  12. Customize Unity’s C# template
  13. Console Window Log Entries
  14. OnValidate
  15. Set Editor Presets as Default
  16. Change the Editor color when it’s in Play Mode
  17. Drag/select scenes in the build settings
  18. Fold the entire Hierarchy
  19. Rich text in Unity’s console
  20. Asset Store search in project window
  21. DisallowMultipleComponent & RequireComponent
  22. Graphy
  23. Drag multiple items in the inspector
  24. Unity Visual Search
  25. NaughtyAttributes
  26. Foldout
  27. Scene Icons
  28. ContextMenu
  29. Inspector Math Expressions
  30. SerializeField & HideInInspector
  31. FormerlySerializedAs
  32. AddComponentMenu
  33. MenuItem

 

 

3、根据你们这么多人的要求, 在@ Unity3d上制作了关于切割网格的教程

观看完整的教程: https://www.youtube.com/watch?v=1UsuZsaUUng&feature=youtu.be

 

4、您是否正在#unity3d中的代码中创建或修改网格? 我们可能会为此计划一些API改进,欢迎提供反馈。 请参阅google doc:Unity 2019.3 Mesh API Improvements https://docs.google.com/document/d/1I225X6jAxWN0cheDz_3gnhje3hWNMxTZq3FZQs5KqPc/edit#heading=h.vyksohcynwk5

和论坛帖子: https://forum.unity.com/threads/feedback-wanted-mesh-scripting-api-improvements.684670/

 

- 索引缓冲区Index buffers:手动指定整个索引缓冲区大小,从NativeArray设置完整或部分数据,手动设置子网格信息(拓扑,索引计数等)的能力。

- 顶点缓冲区Vertex buffers:能够手动指定顶点缓冲区布局(属性数据格式,流分割),从NativeArray设置完整或部分数据。

- 添加到现有SetFoobar网格API的Slice-like(“int start,int length”)重载。

- 添加到现有网格索引API的ushort重载,以避免索引数据的32位< - > 16位转换。

重要很重要的是 NativeArrays的重载目前非常受欢迎。

 

Problem: Unity中的Mesh脚本API是在Unity 1.5(即2006年!)中完成的,并且大部分保持不变。. 后来增加了一些 non-allocating List<T> 函数例如 (Mesh.SetVertices etc.),但整体API方法没有改变。

它主要存在性能问题,额外的内存复制,内存分配以及无法指定更多自定义数据格式:

  • 顶点数据访问基于各个属性 (positions, normals etc.), 即使内部网格可能存储它们也是交错的。
  • 无法查询或指定自定义顶点数据格式 (例如,对某些组件使用半精度float)
  • 索引缓冲区访问全部基于32位索引,即使对于使用16位索引缓冲区的网格也是如此。
  • 无法对顶点/索引缓冲区数据进行部分更新;每次调用都会替换整个数组。
  • 使用基于NativeArray <T>的数据没有很好的互操作性,这在C#Jobs / Burst / DOTS中很常见。

因此,在2019.3中,我们向Mesh类API添加了更多功能,在某种意义上,“更低级别”的API层更直接并且面向显式索引/顶点缓冲区布局和数据更新。

Let us know what you think about this! Here or on the forum thread.

 

Index Buffer API additions索引缓冲区API添加

(when: 本节中的所有内容都在进行中;尚未进行任何alpha版本)

指定索引缓冲区并更新它的数据:

void SetIndexBufferParams(int indexCount, IndexFormat format);
void SetIndexBufferData(
    NativeArray<T> data, int dataStart, int meshBufferStart, int count);

 

以上是用于设置索引缓冲区并使用数据填充/更新它。就像所有现有的Mesh API一样,这适用于索引缓冲区的系统内存表示,例如,如果你做Mesh.GetIndices,将通过SetIndexBufferData完成更新。

当然仅仅指定索引缓冲区是不够的,还必须设置网格“子网格submeshes”,其中基本上包含以下信息:: 边界框bounding box, 面拓扑face topology, 索引缓冲区范围index buffer range, 顶点缓冲区范围vertex buffer range. Mesh类已经包含subMeshCount(默认为1),除此之外:

struct SubMeshDescriptor
{
	AABB bounds;
	MeshTopology topology;
	int indexStart;
	int indexCount;
	int baseVertex;
	int firstVertex;
	int vertexCount;
};
void SetSubMesh(int index, SubMeshDescriptor desc);
SubMeshDescriptor GetSubMesh(int index);

通过上述组合,您可以完全控制索引缓冲区和网格子集信息。 SetIndexBufferParams可能(重新)为缓冲区分配内存,SetIndexBufferData更新它(或它的一部分),数据与内存中的索引缓冲区布局匹配,并且没有格式转换。 SetSubMesh可用于指定索引开始和计数,而无需调整大小或重新填充索引/顶点缓冲区。

 

Vertex Buffer API additions 顶点缓冲区API

(when: 本节中的所有内容都在进行中;尚未进行任何alpha版本)

需要指定顶点数据布局:存在哪些属性,它们的格式,通道数以及哪些属性进入哪些流(一个流中的所有内容都是经典的“交错interleaved”顶点布局;但Unity也几乎可以任意放置属性进入各种流配置)。

enum VertexAttributeFormat
{
	Float32, Float16,
	UNorm8, SNorm8,
	UNorm16, SNorm16,
	UInt8, SInt8,
	UInt16, SInt16,
	UInt32, SInt32,
};
struct VertexAttributeDescriptor
{
	VertexAttribute attribute;
	VertexAttributeFormat format;
	int dimension;
	int stream;
};
VertexAttributeDescriptor[] GetVertexAttributes();
bool                        HasVertexAttribute(VertexAttribute a);
VertexAttributeFormat       GetVertexAttributeFormat(VertexAttribute a);
int                         GetVertexAttributeDimension(VertexAttribute a);
 
bool SystemInfo.SupportsVertexAttributeFormat(VertexAttributeFormat format,
    int dimension);
 
void SetVertexBufferParams(int vertexCount,
    params VertexAttributeDescriptor[] attributes);

以上内容可用于查询网格数据布局(已经可能对GetNativeVertexBufferPtr有用),或者用于显式设置顶点缓冲区的大小和布局。请注意,未指定顶点数据属性的各个偏移量;目前在Unity中,它们总是被隐式计算为以预定义的顺序 (positions, normals, tangents, colors, texture coordinates, blend weights, blend indices)一个接一个地进行计算。

许多API /平台要求顶点组件的大小为4个字节的倍数,因此我们在SetVertexBufferParams级别强制执行此操作。例如: Float16 with dimension=3 is invalid (6 bytes), or UNorm8 with dimension=2 is invalid too.

使用NativeArray <T>源对每个流进行单独调用来更新顶点数据,其中T是必须与顶点数据布局匹配的结构(这可能意味着C#侧的结构可能需要StructLayout(顺序)属性,也许有Pack字段设置)。

void SetVertexBufferData(
    NativeArray<T> data, int dataStart, int meshBufferStart, int count,
    int stream=0);

与SetIndexBufferData一样,这会更新顶点缓冲区或其中一部分的系统内存表示,并且不会执行任何数据转换。

 

Existing old-style API additions and other small things现有的旧式API 和其他小东西

(when: 本节中的所有内容都在2019.3 alpha 4中)

为了保持一致性并且因为它被请求,我们还为现有的“旧”样式Mesh API添加了更多重载。

顶点数据设置器(SetVertices,SetNormals等)具有 “切片式 slice-style” API,其使用所提供的阵列的一部分或列表用于新的顶点数据。所有顶点属性 SetFoo(List<T>) API也得到:

  • SetFoo(T[])
  • SetFoo(T[], int start, int length)
  • SetFoo(List<T>, int start, int length)

类似地,索引数据设置器(SetTriangles和SetIndices)得到切片式重载:

  • SetIndices(int[] indices, int start, int length, …)
  • SetIndices(List<int> indices, int start, int length, …)

处理索引数据的现有函数在没有额外转换的情况下获得了超载以处理16位索引:

  • Add ushort[] and List<ushort> overloads to SetIndices
  • Add ushort[] and List<ushort> overloads to SetTriangles
  • Add List<ushort> overload to GetIndices

对Mesh API各个部分的脚本文档进行了调整,使其更加准确,详细,并在更多地方提及其他相关API。

 

Not Done Yet, But We Are Thinking About It尚未完成,但我们正在思考它

将网格缓冲区用于计算着色器会很好。也许是以下列方式之一:

  • 可选的标志,使网格缓冲区可以计算-writable/readable, 以及从网格中获取ComputeBuffer或GraphicsBuffer的方法,或者
  • 一种从用户提供的ComputeBuffer或GraphicsBuffer对象和一些额外信息创建 “外部external” Mesh的方法。类似于如何创建“外部”纹理。

 

直接NativeArray访问网格顶点/索引缓冲区,没有由SetVertexBufferData / SetIndexBufferData引起的memcpy。这可能与NativeArray Texture2D.GetRawTextureData的工作方式类似。

 

将“Flags”参数添加到SetVertexBufferParams / SetIndexBufferParams。可能包含以下标志:

  • “Dynamic buffer”, replacing Mesh.MarkDynamic
  • “Compute”, see above wrt Compute Shader usage
  • “Keep/convert previous data”, try to convert previously existing data

将“Flags”参数添加到SetVertexBufferData / SetIndexBufferData。可能包含以下标志:

  • “Update bounds”, optional automatic bounds calculation
  • “Check”, to validate if indices are not out of range (always done in old Mesh API)
  • “Update vertex ranges”, optional automatic SubMeshDescriptor vertex range calculation

 

 

5、

Unity 2018.3中的作业安全API

Unity 2019.1中的Job-Safe API

https://jacksondunstan.com/articles/5029

https://jacksondunstan.com/articles/5239

所以对于今天,我们现在有一个小的Unity编辑器脚本,它将为我们生成列表。这是它的作用:

1查找.dllUnity安装目录的Managed子目录中的所有文件

2打开它们作为 Assembly

3扫描所有方法和属性

4检查[NativeMethod]属性是否存在

5调用get给NativeMethodAttribute.IsThreadSafe

6输出所有匹配项Debug.Log和系统剪贴板

2018.3高达301作业安全的API,但很多人都private还是internal ,因此必须通过一个间接调用public API。总而言之,自Unity 2018.2以来,它只是九种方法和属性的扩展。但是,Unity越来越依赖于软件包来提供新功能,例如当前处于预览版的ECS。要使用此脚本检查任何其他库,只需更改paths变量以包含其目录。

Unity 2019.1.3f1运行相同的脚本会生成350个函数的列表:

值得注意的是,其中许多功能都是私有或内部功能。许多人甚至在他们的名字中都有“内部”这个词。虽然这些仍然可以通过反射在技术上调用,但更典型的方法是在公共API中调用某个函数,然后直接或间接地调用私有函数或内部函数。

找到如何调用这59个新的作业安全功能必须根据具体情况进行,但很容易看出引擎整体上变得更加安全。这对于Unity中高性能代码的未来来说是一件好事,特别是现在我们拥有专门用于C#作业的Burst编译器。

 

 

 

 

6、 ffmpeg stream raw video into unity Texture2D

ffmpeg将原始视频流转换为Unity的Texture2D

下载 https://ffmpeg.zeranoe.com/builds/: Architecture Windows 64-bit

客户端代码: https://github.com/unitycoder/ffmpegStreamToUnity

运行Unity项目 。

然后命令行执行 :

 

unity项目,它应该显示接收到的数据。 ffmpeg会捕获桌面的内容 流式传输到Unity材质texture2D

接下来的步骤

- TCP版本可能修复丢失帧/不同步问题

- 使用正确的容器mpegts左右,修复udp遗漏帧等问题,但难以解码mpeg4有效载荷数据

https://unitycoder.com/blog/2019/05/26/ffmpeg-stream-raw-video-into-unity-texture2d/

 

Unity 利用FFmpeg实现录屏、直播推流、音频视频格式转换、剪裁等功能

 

 

 

 

 

7、 为着色器或脚本启用HDR颜色选择器

我需要一些旧的Unlit / Color-shader的HDR颜色,而对于c#脚本,也发现你可以为着色器添加这个属性:

// BEFORE (no hdr color picker)

_Color ("Main Color", Color) = (1,1,1,1)

// AFTER (hdr color picker)

[HDR]_Color ("Main Color", Color) = (1,1,1,1)

为C#脚本:

// BEFORE

public Color shouldbeHDR = Color.white;

// AFTER

[ColorUsage(true, true)]

public Color shouldbeHDR = Color.white;

More info:

https://docs.unity3d.com/ScriptReference/ColorUsageAttribute.html

https://docs.unity3d.com/Manual/SL-Properties.html

还要确保您的相机启用了[x] HDR

 

 

 

 

 

 

8、一个帖子 : https://forum.unity.com/threads/draw-signal-strength-scrolling-graph-with-a-shader.682465/ 使用着色器绘制模拟电波信号强度滚动图...

基本思路:

- 使用CustomRenderTexture,双缓冲,以便它可以自己读取

- 将color32数组绘制到texture2D中

- 使用CustomRenderTexture中的texture2D作为底部像素行

- CustomRenderTexture通过读取下面的1个像素继续向上滚动

滚动Perlin噪声颜色(底部1像素行来自Texture2D)

https://github.com/unitycoder/ScrollingTexturePlotter

  • 使用 CustomRenderTexture 制作火特效

添加了火焰渐变颜色(通过将火焰计算移动到Alpha通道中)。

https://github.com/unitycoder/FireEffect

参考

 

 

 

9、 从Unity Build Files创建单个.EXE

使用Enigma Virtual Box

下载 : https://enigmaprotector.com/en/downloads.html

- 选择输入文件名:(这是Unity构建的.exe文件)

- 输入输出文件名:(这是输出单个exe文件)*这必须与原始.exe文件名相同,以便找到Data文件夹。

- 单击添加/添加文件夹递归..

- 浏览到您的Build文件夹,例如../Builds /在您的Unity项目中(Assets /文件夹旁边)

- 现在您可以测试您的单个exe文件

- 注意:从StreamingAssets加载数据单个exe应用程序中的/文件夹似乎也可以工作。

如果包含exe和data文件夹,请选择整个Builds文件夹

注: 另外, 测试此工具也有效,但似乎在运行exe时会打开控制台窗口..

https://github.com/dgiagio/warp#windows-1

TODO

- 检查enigma是否支持命令行参数,然后你可以使用一些编辑器脚本自动完成!

 

 

10、 Unity中角点检测的快速算法

将此(基本)算法转换为Unity c#https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_fast/py_fast.html

快速(加速段测试的功能)是一种从图像中检测角落特征的方法。

来源:*未优化

https://gist.github.com/unitycoder/7649f62a6eceeb761db7f0092d9c3df1

我的层次结构设置

 

 

 

11、 Unity中测试 Ray Marching

观看了The Coding Train的这一集后,在Unity中测试了Ray Marching 光线行进。

? https://thecodingtrain.com/CodingChal... ?? https://editor.p5js.org/codingtrain/s... ? Website: http://thecodingtrain.com/ ? Patreon: https://patreon.com/codingtrain ? Store: https://www.designbyhumans.com/shop/c... ? Books: https://www.amazon.com/shop/thecoding... ? Coding Challenges: https://www.youtube.com/playlist?list... ? Intro to Programming: https://www.youtube.com/playlist?list... ?? p5.js: https://p5js.org ? Processing: https://processing.org

源代码:https //gist.github.com/unitycoder/7120e492deba748dba3d4e1e424d3c2a

用法:

将相机设置为0,0,-10

投影:正交

然后将球体置于空游戏对象下,球体z值为0

 

 

 

 

12、从颜色数组创建渐变纹理

用于从颜色数组生成渐变Texture2D的小工具。(注意:最大颜色数为8,因为它在内部使用单位渐变(https://docs.unity3d.com/ScriptReference/Gradient.html

来源:

https //github.com/UnityCommunity/UnityLibrary/blob/master/Assets/Scripts/Texture/GradientTextureMaker.cs

示例用法脚本:(将脚本附加到某些3d对象)

https://gist.github.com/unitycoder / f7ea9019bb67b7042efbd08f0f4fa785

Unity脚本,根据输入`AnimationCurve`生成均匀分布的`Gradient`的多个变体。

https://github.com/5argon/GradientGenerator

 

 

 

13、 [Unity] [编辑器扩展]当从Inspector增加序列化数组的元素数量时,防止复制元素。

如下图正常情况下,新增加的元素内容是第一个元素的拷贝。

想在的需求是不希望这样!

public class ElementBehaviour : MonoBehaviour

{

}

public class Example : MonoBehaviour

{

[SerializeField]

private ElementBehaviour[] _elements;

}

[CustomPropertyDrawer(typeof(ElementBehaviour))]

public class ElementBehaviourDrawer : PropertyDrawer

{

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)

{

// 路径是_elements.Array.data [x]

var splitPath = property.propertyPath.Split('.');

var isArrayElement = splitPath[splitPath.Length - 2] == "Array";

if (isArrayElement && property.objectReferenceValue != null) {

// 获取数组索引

var arrayIndexStr = splitPath[splitPath.Length - 1].Replace("data[", "").Replace("]", "");

var arrayIndex = int.Parse(arrayIndexStr);

// 创建字符串_elements.Array.data [{0}]

var formatSplitPath = splitPath;

formatSplitPath[formatSplitPath.Length - 1] = "data[{0}]";

var formatPath = string.Join(".", formatSplitPath);

// 获取上一个元素和下一个元素

var previousElementPath = string.Format(formatPath, arrayIndex - 1);

var nextElementPath = string.Format(formatPath, arrayIndex + 1);

var previousElement = property.serializedObject.FindProperty(previousElementPath);

var nextElement = property.serializedObject.FindProperty(nextElementPath);

var isLastElement = nextElement == null;

// 如果有前一个元素,并且最后一个元素(刚添加的元素),以及前一个元素和引用一样,则删除引用

if (arrayIndex >= 1 && isLastElement && previousElement.objectReferenceValue == property.objectReferenceValue) {

property.objectReferenceValue = null;

}

}

// 通常绘制属性

using (new EditorGUI.PropertyScope(position, label, property)) {

EditorGUI.PropertyField(position, property);

}

}

public override float GetPropertyHeight(SerializedProperty property, GUIContent label)

{

return EditorGUIUtility.singleLineHeight;

}

}

当绘制元素时,它会检查数组元素,以及删除引用的过程(如果最后一个数组元素和前一个元素具有相同的引用)

 

 

 

 

14、 Unity中的代码文件默认是 带BOM 的 UTF-8

顺便说一下,换行代码根据OS而不同。

mac主要是LF,Windows是CR + LF (\r+ \n)。 Unity的换行代码是CR + LF。即使它是LF也可能没问题,但如果CR + LF文件和LF文件混合,则会出现不便,例如通过更改版本控制中的换行代码就会出现差异。 此外,如果多个人在不同Unity代码的环境中进行开发,则可以将换行代码混合在一个文件中。 这是一个重要问题,因为它会导致StackTrace行号不正确和编译警告。

可以保证模板是正确的格式 : C:\ Program Files(x86)\ Unity \ Editor \ Data \ Resources \ ScriptTemplates

 

可以后处理 生成新脚本时将文本编码转换为UTF-8 https://github.com/sharkattack51/GarageKit_for_Unity/blob/master/UnityProject/Assets/__ProjectName__/Editor/AssetPostprocessUTF8Encode.cs

获取此处使用的文件编码的部分作为参考。

http://dobon.net/vb/dotnet/string/detectcode.html

 

 

 

15、 [Unity]轻松可视化网格信息(UV,顶点颜色,法线等)

有时您想要可视化网格所具有的信息,例如UV,顶点颜色和法线。

我所要做的就是写一个着色器,但我把它变成了一个多功能的东西,因为每次写作都很麻烦。写一个着色器。

Shader "Hidden/MeshInfoViewer"
{
    Properties
    {
        [KeywordEnum(UV0, UV1, VertexColorRGB, VertexColorAlpha, WorldNormal)]_MeshInfo("Mesh Info", float)     = 0
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
           #pragma multi_compile _MESHINFO_UV0 _MESHINFO_UV1 _MESHINFO_VERTEXCOLORRGB _MESHINFO_VERTEXCOLORALPHA _MESHINFO_WORLDNORMAL
            
           #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex       : POSITION;
                float2 texcoord0    : TEXCOORD0;
                float2 texcoord1    : TEXCOORD1;
                float4 color        : COLOR;
                float4 normal       : NORMAL;
            };
            struct v2f
            {
                float4 vertex       : SV_POSITION;
                float2 uv0          : TEXCOORD0;
                float2 uv1          : TEXCOORD1;
                float4 color        : COLOR;
                float3 normal       : NORMAL;
            };
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex    = UnityObjectToClipPos(v.vertex);
                o.uv0       = v.texcoord0;
                o.uv1       = v.texcoord1;
                o.color     = v.color;
                o.normal    = UnityObjectToWorldNormal(v.normal);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
               #if _MESHINFO_UV0
                    return fixed4(i.uv0.xy, 0, 1);
               #elif _MESHINFO_UV1
                    return fixed4(i.uv1.xy, 0, 1);
               #elif _MESHINFO_VERTEXCOLORRGB
                    return fixed4(i.color.rgb, 1);
               #elif _MESHINFO_VERTEXCOLORALPHA
                    return fixed4(i.color.aaa, 1);
               #elif _MESHINFO_WORLDNORMAL
                    return fixed4(i.normal, 1);
               #endif
                return 1;
            }
            ENDCG
        }
    }
}

这个过程很简单。

我为每个要显示的信息定义一个关键字并制作变体。

怎么用

至于如何使用,只需从Inspector设置材质的模式。

http://light11.hatenadiary.com/entry/2018/12/03/224659

 

 

16、 [Unity] [编辑器扩展]在场景视图中显示窗口

这是一种在场景视图中显示IMGUI窗口的方法。

using UnityEngine;
using UnityEditor;
using UnityEngine.SceneManagement;
public class SampleSceneEditWindow {
    private static Rect _windowSize = new Rect(8, 24, 300, 100);
    private static bool _enabled = false;
    // 加载脚本时调用
    [InitializeOnLoadMethod]
    private static void SampleSceneEdit(){
        SceneView.onSceneGUIDelegate += OnSceneGUI;
    }
    private static void OnSceneGUI(SceneView sceneView){
        if (!GetIsTargetScene()) {
            return;
        }
        GUILayout.Window(1, _windowSize, DrawWindow, "Sample Window");
    }
    public static void DrawWindow(int id)
    {
        EditorGUILayout.LabelField("Sample");
    }
    private static bool GetIsTargetScene()
    {
        return SceneManager.GetActiveScene().name.Contains("sample_");
    }
}

 

 

17、 [Unity] [编辑器扩展]获取特定资产的AssetImporter

https://docs.unity3d.com/ScriptReference/AssetImporter.html

这样做的方法是将资产路径传递给AssetImporter.GetAtPath() 。

Texture2D texture;

var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(texture)) as TextureImporter;

类型是以下三种之一。

  • ModelImporter
  • TextureImporter
  • AudioImporter

基本用法:PreProcess和PostProcess

PreProcess方法。

  • OnPreprocessModel
  • OnPreprocessAudio
  • OnPreprocessAnimation
  • OnPreprocessSpeedTree
  • OnPreprocessTexture

Post也以各种方式定义。

  • OnPostprocessModel
  • OnPostprocessAudio
  • OnPostprocessSprites
  • OnPostprocessSpeedTree
  • OnPostprocessGameObjectWithUserProperties
  • OnPostprocessAssetbundleNameChanged
  • OnPostprocessTexture

这些Processor在以下时间进行。

  • 首先导入
  • Reimport时
  • 重命名时
  • 从Inspector更改设置并Apply时

导入所有资源后调用OnPostprocessAllAssets()方法。

此外,它晚于PostprocessXXX系列。

例如,如果选择并重新导入两个纹理,则在调用OnPostprocessTexture两次后将调用一次OnPostprocessAllAssets。

 

GetPostprocessOrder()决定处理顺序 ,此值似乎不会影响OnPostprocessAllAssets()。

如果创建多个继承AssetPostprocessor的类,则可以通过定义GetPostprocessOrder()来确定处理顺序。

值按升序处理。

public class AssetPostProcessorTest : AssetPostprocessor {
    public override int GetPostprocessOrder()
    {
        return 1;
    }
    private void OnPreprocessTexture()
    {
        Debug.Log(GetPostprocessOrder());
    }
    private void OnPostprocessTexture(Texture2D tex)
    {
        Debug.Log(GetPostprocessOrder());
    }
}
public class AssetPostProcessorTest2 : AssetPostprocessor {
    public override int GetPostprocessOrder()
    {
        return 2;
    }
    private void OnPreprocessTexture()
    {
        Debug.Log(GetPostprocessOrder());
    }
    private void OnPostprocessTexture(Texture2D tex)
    {
        Debug.Log(GetPostprocessOrder());
    }
}

您可以使用GetVersion()来定义版本。使用 GetVersion()进行版本控制

当版本更改时,该后处理器的处理将在所有资产上运行。

public class AssetPostProcessorTest : AssetPostprocessor {
    public override uint GetVersion()
    {
        return 1;
    }
    private void OnPreprocessTexture()
    {
        Debug.Log(assetPath);
    }
}

顺便说一下,似乎从Unity2018添加了OnPreprocessAsset()。

 

 

 

 

 

 

18、 【YAML】YAML的基本语法

序列(数组)
- a
- b
- c
     结果由json表示如下:
[
  "a", 
  "b", 
  "c"
]
映射(字典)
A: a
B: b
C: c
对应Json如下
{
  "A": "a", 
  "B": "b", 
  "C": "c"
}
嵌套序列
- a
- b
-
 - c
 - d
对应Json如下
[
  "a", 
  "b", 
  [
    "c", 
    "d"
  ]
]
嵌套映射
A: a
B: b
C:
 1: c-1
 2: c-2
对应Json如下
{
  "A": "a", 
  "B": "b", 
  "C": {
    "1": "c-1", 
    "2": "c-2"
  }
}
映射序列
- A: a
  B: b
- C: c
  D: d
对应Json如下
[
  {
    "A": "a", 
    "B": "b"
  }, 
  {
    "C": "c", 
    "D": "d"
  }
]
序列映射
A: 
 - a-1
 - a-2
 - a-3
B:
 - b-1
 - b-2
对应Json如下
{
  "A": [
    "a-1", 
    "a-2", 
    "a-3"
  ], 
  "B": [
    "b-1", 
    "b-2"
  ]
}

包含换行符的字符串
A: |
 line1
 line2
 line3
对应Json如下
{
  "A": "line1\nline2\nline3"
}
锚定别名
您可以使用&[Anchor Name]注册锚点,如下所示,并使用* [Anchor Name]引用它。
- &test a
- b
- c
- *test
对应Json如下
[
  "a", 
  "b", 
  "c", 
  "a"
]
锚也附加到序列和映射。
- a
- b
- &c
 - c-1
 - c-2
- *c
你可以用它来做这样的事情。
players: 
 - &profile1
  id: 1
  name: taro
  age: 21
 - &profile2
  id: 2
  name: hanako
  age: 18
currentPlayer : *profile1
对应Json如下
{
  "players": [
    {
      "age": 21, 
      "id": 1, 
      "name": "taro"
    }, 
    {
      "age": 18, 
      "id": 2, 
      "name": "hanako"
    }
  ], 
  "currentPlayer": {
    "age": 21, 
    "id": 1, 
    "name": "taro"
  }
}
还有一种称为流式的书写方法
[A, B, C: c]
对应Json如下
[
  "A", 
  "B", 
  {
    "C": "c"
  }
]
  • 序列(数组)

- a - b - c

结果由json表示如下:

[ "a", "b", "c" ]

  • 映射(字典)

A: a B: b C: c

对应Json如下

{ "A": "a", "B": "b", "C": "c" }

  • 嵌套序列

- a - b - - c - d

对应Json如下

[ "a", "b", [ "c", "d" ] ]

  • 嵌套映射

A: a B: b C: 1: c-1 2: c-2

对应Json如下

{ "A": "a", "B": "b", "C": { "1": "c-1", "2": "c-2" } }

  • 映射序列

- A: a B: b - C: c D: d

对应Json如下

[ { "A": "a", "B": "b" }, { "C": "c", "D": "d" } ]

  • 序列映射

A: - a-1 - a-2 - a-3 B: - b-1 - b-2

对应Json如下

{ "A": [ "a-1", "a-2", "a-3" ], "B": [ "b-1", "b-2" ] }

在YAML中,类型是自动确定的。

例子

类型判定

1 / 1,000

int

0.1

float

true・false / yes・no

bool

"text" / 'text'

string

~ / null

null

2019-01-01

日期

2020-01-01 00:00:00 +00:00

时间戳

 

  • 包含换行符的字符串

A: | line1 line2 line3

对应Json如下

{ "A": "line1\nline2\nline3" }

  • 锚定别名

您可以使用&[Anchor Name]注册锚点,如下所示,并使用* [Anchor Name]引用它。

- &test a - b - c - *test

对应Json如下

[ "a", "b", "c", "a" ]

锚也附加到序列和映射。

- a - b - &c - c-1 - c-2 - *c

你可以用它来做这样的事情。

players: - &profile1 id: 1 name: taro age: 21 - &profile2 id: 2 name: hanako age: 18 currentPlayer : *profile1

对应Json如下

{ "players": [ { "age": 21, "id": 1, "name": "taro" }, { "age": 18, "id": 2, "name": "hanako" } ], "currentPlayer": { "age": 21, "id": 1, "name": "taro" } }

  • 还有一种称为流式的书写方法

[A, B, C: c]

对应Json如下

[ "A", "B", { "C": "c" } ]

  • 一个易于试用的网站

以下网站便于轻松尝试YAML语法。它将YAML解析为json和python。

http://yaml-online-parser.appspot.com/

http://ben-kiki.org/ypaste/

 

 

【Unity】打开Prefab或Scene YAML并直接编辑

包括可能是 ScriptableObject 对象, 在Unity中直接修改原始文件可能更方便, 比如字符串替换一下。 省去了写代码逻辑。 比如在 VS Code这种文本编辑器中批量处理就很方便。

Unity2018.3.6, 尝试在文本编辑器中打开一个空的GameObject 。

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4903041867623032639
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 4433608277066839807}
  m_Layer: 0
  m_Name: GameObject (1)
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &4433608277066839807
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 4903041867623032639}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: -0.21204466, y: 0.30996525, z: -0.86567664}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_Children: []
  m_Father: {fileID: 0}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

前两行是标准形式, 每个都有。

组成Prefab 的每个类(GameObject或组件)的信息由 --- 分隔。

     --- !u!1 &4903041867623032639
     GameObject:

这之后的数字!u!是类ID。下一行的字符串是类名。

这些定义如下。顺便说一句,我所有的类都是114:MonoBehaviour。

此外,&之后的数字是表示此Prefab中的此GameObject或组件的任意ID。

比如可以修改 localScale 的值:

  m_LocalScale: {x: 2, y: 2, z: 2}

添加一个如下组件:

public class Example : MonoBehaviour
{
    [SerializeField]
    private int _example;
}

YAML 变为如下:

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4903041867623032639
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 4433608277066839807}
  - component: {fileID: 5697825174949230319}
  m_Layer: 0
  m_Name: GameObject
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &4433608277066839807
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 4903041867623032639}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: -0.21204466, y: 0.30996525, z: -0.86567664}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_Children: []
  m_Father: {fileID: 0}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &5697825174949230319
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 4903041867623032639}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 041e1384a28babc438abff5eaa04bfe5, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
  _example: 0

换句话说,您似乎可以通过执行此过程来附加组件

因此,我将尝试附加与试验相同的组件

首先,在原来基础上 将先前添加的组件部分复制到底部。仅更改ID。

  - component: {fileID: 5697825174949230320}
--- !u!114 &5697825174949230320
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 4903041867623032639}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 041e1384a28babc438abff5eaa04bfe5, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
  _example: 0

知道如何添加了, 那么移除自然就很简单, 你可以试一试 ~~

与此同时,让我们看看除了预制件之外的YAML。

Scene的YAML。 它看起来很复杂,因为有很多设置项,但格式与Prefab相同。 自己看吧 ~~~

接下来,我们来看看ScriptableObject 的YAML。

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 37c88262b3fdee44da78122d1afd7476, type: 3}
  m_Name: ScriptableObject
  m_EditorClassIdentifier: 
  _example: 0

这不是GameObject,它比Prefab简单。格式与Prefab相同。

 

Text-Based Scene Files

 

 

 

19、 [Unity] [编辑器扩展] 在组件中设置HideFlags的问题, 它在成为预制件时消失

在组件上设置HideFlags可以在Inspector中隐藏它。(因为它是UnityEngine.Object的一个属性,它可以在组件之外使用)

现在假设您使用它来防止组件从Inspector中可见。

 

解决方案是在MonoBehaviour的OnValidate()中设置hideFlags。

using UnityEngine;
public class Example : MonoBehaviour
{
    private void OnValidate()
    {
        hideFlags |= HideFlags.HideInInspector;     // | 运算 ,添加了标志而不是被覆盖。
    }
} 

虽然不显示, 但是依然存在, 也会保存到Prefab中:

可以在此处理预制生成/更新的回调,因为它们可以按如下方式进行处理。

using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public class OnPrefabInstanceUpdated
{
    static OnPrefabInstanceUpdated()
    {
        PrefabUtility.prefabInstanceUpdated += OnPrefabInstanceUpdate;
    }
    private static void OnPrefabInstanceUpdate(GameObject instance)
    {
        // 预制件实例的组件
        var instanceComponents = instance.GetComponentsInChildren<MonoBehaviour>();
        foreach (var instanceComponent in instanceComponents) {
            // 预制组件
            var prefabComponent = PrefabUtility.GetCorrespondingObjectFromOriginalSource(instanceComponent);
            // 此时,instanceComponent的hideFlags已更改为None,因此下面的代码没有意义
            prefabComponent.hideFlags = instanceComponent.hideFlags;
            // 即使以这种方式,它也没有被反映出来
            if (instanceComponent.GetType() == typeof(Example) || instanceComponent.GetType().IsSubclassOf(typeof(Example))) {
                prefabComponent.hideFlags |= HideFlags.HideInInspector;
            }
        }
    }
}

但是,如评论中所述,此方法效果不佳。

HideFlags 果然很有意思 ~~~

 

 

20、 [Unity]在TextMesh Pro中使用Mobile 版本着色器

针对移动平台,肯定是推荐优先使用这些~

 

21、 使用反射调用BuildAndRun

[MenuItem("ReflectionTest/BuildAndRun")]
public static void BuildAndRun() 
{   
    // 方法名称可能会根据Unity版本而改变...
    var reflectionMethodName = "CallBuildMethods";
    var reflectionFlags =  BindingFlags.NonPublic | BindingFlags.Static;

    var buildAndRun = typeof(BuildPlayerWindow).GetMethod(reflectionMethodName, reflectionFlags);

    if(buildAndRun != null){
        // 注意UnityEngine.Object的错误。
        buildAndRun.Invoke(null, new System.Object[]{ true, BuildOptions.AutoRunPlayer | BuildOptions.StrictMode });
    }
}

 

 

22、 首先GPU Profiler并不是所有图形API和显卡都能支持的,如果不支持面板中会提示。还有如果打开了 Graphics Jobs (Experimental) 选项,GPU Profiler将会被关闭。

首先来看看各平台GPU Profiler所支持的图形API和显卡。

 

 

23、

Project Tiny C# Preview available Tiny Project的 C#版本现在可以使用了, 之前是Typescript版本的~~~

https://forum.unity.com/threads/project-tiny-c-preview-available.688969/?from=timeline

Unity’s Data-Oriented Tech Stack (DOTS)

我们非常高兴能与您分享使用C#作为编程语言的新Project Tiny的第一个预览版。我们一直在努力改变大多数为Project Tiny提供支持的基础技术,以响应您的反馈,并使其更接近Unity生态系统。此预览与Unity的面向数据的技术堆栈(DOTS)完全集成,并为为小型和大型用例提供高级功能奠定了基础。

使用Unity 2019.2.0b3或更高版本,您可以立即通过软件包管理器安装“Project Tiny Preview - 0.15.3”。有关详细信息,请参阅下面的“入门”。

 

 

 

 

 

24、 中国官方翻译:

https://connect.unity.com/p/zai-bian-ji-qi-gong-ju-zhong-kuai-su-ti-qu-lei-xing-shu-xing

 

UnityEditor.TypeCache API,用于在编辑器工具中快速提取类型属性

https://forum.unity.com/threads/unityeditor-typecache-api-for-fast-extraction-of-type-attributes-in-the-editor-tooling.687682/?utm_source=twitter&utm_medium=social&utm_campaign=engine_global_generalpromo_2019-06-04_2019-2-beta&utm_content=forum+thread

您正在开发编辑器实用程序或包吗? Unity 2019.2 beta公开了一种用于类型提取的新API,以减少工具初始化并进入播放模式时间。

为什么会出现性能问题

在考虑优化进入Play模式时,我们发现从加载的程序集中提取类型需要相当长的时间。类型提取在编辑器模块内部广泛使用,在外部由包和用户代码使用,以扩展编辑功能。累积效果取决于项目,并且可以为域重新加载时间贡献300-600毫秒(如果系统具有延迟初始化,则可以更多)。在新的Mono运行时,由于时间的原因,时间显着增加Type.IsSubclassOf性能回归,最长可达1300毫秒。

性能问题源于这样一个事实:代码通常从当前域中提取所有类型,然后迭代所有类型进行昂贵的检查。时间根据游戏类型的数量线性增加(通常为30-60K)。

解决方案

缓存类型信息允许我们打破由域中的类型的迭代引起的O(N)复杂性。在本机级别,我们已经有了加速结构,它们在加载所有程序集后填充并包含缓存类型数据,例如方法和类属性以及接口实现程序。在内部,这些结构通过UnityEditor.EditorAssemblies API公开,以利用快速缓存。遗憾的是,API未公开发布,并且不支持重要的SubclassesOf用例。

对于2019.2,我们优化并扩展了本机缓存,并将其作为公共UnityEditor.TypeCache API公开。它可以非常快速地提取信息,允许迭代我们感兴趣的较少数量的类型(10-100)。这大大减少了通过编辑器工具获取类型所需的时间。

具体示例代码看论坛 :::::

 

 

 

25、

NEW 2D LIGHTS IN UNITY 2019.2 (Tutorial)

https://forum.unity.com/threads/experimental-2d-lights-and-shader-graph-support-in-lwrp.683623/

https://www.youtube.com/watch?v=ZJvCphxCGJU

我们在LWRP的实验命名空间中发布了一个2D渲染器。

此版本包括:

  • 2D灯
  • 着色器图中的点亮和未点亮的Sprite Masternode
  • Pixel Perfect相机组件
    • 这将是Pixel Perfect前进的新家。独立包仍然会收到错误修复,但只会在此处添加新功能。
    • 这是与Pixel Perfect软件包中的工作流程相同的组件。主要变化是Pixel Perfect现在与LWRP中的2D渲染器兼容。

样本

在此处获取2D渲染器示例:

https//github.com/Unity-Technologies/2d-renderer-samples

文档: https://docs.unity3d.com/Packages/com.unity.render-pipelines.lightweight@6.7/manual/2d-index.html

 

 

 

26、

Unity Roadmap - Q2 - 2019.pdf 最新版本 :

正如一些人提出的那样,最新更新的#Unity路线图 第二季是https://www.dropbox.com/s/bj8l1s7meiw00dj/Roadmap%20-%20Q2%20-%202019.pdf?dl=0 ... #gamedev - 如果你看到的话,自GDC以来只有一些小的更新。

https://www.dropbox.com/s/bj8l1s7meiw00dj/Roadmap%20-%20Q2%20-%202019.pdf?dl=0

推荐看一下 , 推荐看一下 , 推荐看一下 ,

 

 

 

 

27、 [Unity]在使用adb shell am start启动Android应用程序时分析指定参数的“Uni Android Intent”函数已发布到GitHub

https://github.com/baba-s/uni-android-intent

Unity 2018.3.11f1

adb shell am start ^
    -n com.baba_s.uniandroidintent/com.unity3d.player.UnityPlayerActivity ^
    --ei i 123 ^
    --el l 456 ^
    -e s ABC ^
    --ez b true ^
    --eia ia 111,223,343 ^
    --ela la 444,555,666 ^
    --esa sa AAA,BBB,CCC

如果您使用adb shell am start启动带有参数的Android应用程序

// int 类型值获取
Debug.Log( UniAndroidIntent.GetInt( "i" ) );
// long 类型值获取
Debug.Log( UniAndroidIntent.GetLong( "l" ) );
// string 类型值获取
Debug.Log( UniAndroidIntent.GetString( "s" ) );
// bool 类型值获取
Debug.Log( UniAndroidIntent.GetBool( "b" ) );
// int 数组类型值获取
foreach ( var n in UniAndroidIntent.GetIntArray( "ia" ) )
{
    Debug.Log( n );
}
// long 数组类型值获取
foreach ( var n in UniAndroidIntent.GetLongArray( "la" ) )
{
    Debug.Log( n );
}
// string 数组类型值获取
foreach ( var n in UniAndroidIntent.GetStringArray( "sa" ) )
{
    Debug.Log( n );
}

您可以使用此类代码解析和使用参数

如何为adb shell指定参数

类型

指定方法

int

--ei [参数名称] [参数值]

long

--el [参数名称] [参数值]

string

-e [参数名称] [参数值]

bool

--ez [参数名称] [参数值]

int 数组

--eia [参数名称] [数组的值(以逗号分隔)]

long 数组

--ela [参数名称] [数组的值(以逗号分隔)]

string 数组

--esa [参数名称] [数组的值(以逗号分隔)]

用途:::

例如, 在使用Jenkins等自动构建应用程序并 进一步执行测试之后,通过在Android设备上自动安装应用程序 来检查构建的应用程序是否正常工作

https://developer.android.com/reference/android/content/Intent

 

 

 

 

28、 [Unity]可以从Inspector更改着色器的剔除模式

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 0 // ★
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {
            Cull [_Cull] // ★
            CGPROGRAM
。。。。。。。

切换后就可以看到效果。

  1. 指定[Enum(UnityEngine.Rendering.CullMode)]作为属性
  2. 指定 Cull [_Cull]

 

 

 

29、 [Unity]如何在着色器的Inspector中显示和使用开关

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
        [Toggle(IS_RED)] _IsRed("Is Red", Float) = 0 // ★
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            #pragma shader_feature _ IS_RED // ★

            #include "UnityCG.cginc"
。。。。。。。。。。。。。。。

1、指定Toggle为属性

2、使用#pragma shader_feature指定关键字

3、在#if中使用关键字

 

 

30、

怎么为角色创建 LodGroup ?

https://answers.unity.com/questions/323575/creating-lod-group-for-character.html

 

 MySoldier (GameObject with LOD Group)

    Soldier_LOD0 (with Animation)

       Pelvis

          other bones

       Soldier (SkinnedMeshRenderer)

    Soldier_LOD1 (with Animation)

       Pelvis

          other bones

       Soldier (SkinnedMeshRenderer)

 

结果是网格将在距离处切换,但除非我将动画应用于Soldier_LOD0和Soldier_LOD1,否则不会进行动画处理。

有没有人对Olly在几个LOD上制作动画角色的步骤有什么好运?

我怀疑fbx需要包含单个骨架和多个蒙皮网格,但是如果有人对如何从包含单个蒙皮网格的多个fbx设置它们有任何建议,那么我真的很感激如何实现这一点的一些信息。

https://forum.unity.com/threads/level-of-detail-management-lod.7265/

如果Lod 只是切换Mesh !!!

它所做的只是改变Mesh用于显示一个角色。游戏对象根本不会改变或切换。

所以动画应该正常工作,脚本应该继续运行等等; 它所做的就是改变使用哪个网格。您只需更改MeshFilter组件中使用的网格即可。

var highLod : Mesh;
var lowLod : Mesh;
var distance = 10.0;
function Update ()
{
    var campos = Camera.main.transform.position;
    var meshFilter : MeshFilter = GetComponent(MeshFilter);
    if( (transform.position - campos).sqrMagnitude <
        distance * distance )
    {
        // use high LOD
        if( meshFilter.sharedMesh != highLod )
            meshFilter.sharedMesh = highLod;
    }
    else
    {
        // use low LOD
        if( meshFilter.sharedMesh != lowLod )
            meshFilter.sharedMesh = lowLod;
    }
}

https://docs.unity3d.com/2019.1/Documentation/Manual/LevelOfDetail.html

https://docs.unity3d.com/2019.1/Documentation/Manual/class-LODGroup.html

 

 

 

 

31、[ReSharper]关于如何使用ReSharper的文章摘要(75)

 

 

 

32、 Unity 分析GPU ~~

Snapdragon简介

由于GPU的详细指标无法在Unity的Profiler上进行, 。在iOS上,您将使用Instruments来测量绘图负载。

对于Android可以使用 Snapdragon Profiler - Qualcomm 您需要在Qualcomm上注册才能下载。

它通过USB连接由Snapdragon处理器驱动的Android设备。Snapdragon Profiler允许开发人员分析CPU,GPU,DSP,内存,电源,散热和网络数据,以便他们找到并修复性能瓶颈。

特点和好处

  • 实时视图可以轻松关联时间轴上的系统资源使用情况
    • 分析CPU,GPU,DSP *,内存,电源,散热和网络数据指标
    • 从22个类别中的150多种不同硬件性能计数器中进行选择
  • 跟踪捕获模式允许您在时间线上可视化内核和系统事件,以分析CPU,GPU和DSP上的低级系统事件。
    • 查看CPU调度和GPU阶段数据,以查看应用程序花费时间的位置
  • Snapshot Capture ***模式允许您从任何OpenGL ES应用程序捕获和调试渲染帧
    • 逐步并重放渲染帧绘制逐个调用调用
    • 查看和编辑着色器并在设备上预览结果
    • 查看和调试像素历史记录
  • 捕获并查看每个绘制调用的GPU指标

GPU API:OpenGL ES 3.1,OpenCL 2.1和Vulkan 1.0 **

*需要Snapdragon 820(或更高版本)处理器

**需要Android N(或带有支持Vulkan的图形驱动程序的Android 6.0设备)

***需要Snapdragon 805(或更高版本)处理器和Android 6.0(或更高版本)

https://youtu.be/9wxxTl_Su7U

 

 

RenderDoc非常好

https://docs.unity3d.com/2018.3/Documentation/Manual/RenderDocIntegration.html

捕获非常简单。

安装RenderDoc后,在Unity的GameView上播放之前加载RenderDoc。

你所要做的就是捕捉按钮Pochiru

在PixelHistory中,您可以看到屏幕上点数的填充历史

您可以看到Mesh内容的值。

 

 

 

33、 与ETC1相比,ETC2在颜色部分得到改善的故事

https://enrike3.hatenablog.com/entry/2018/04/21/210901

我在 谈到压缩纹理, 源自DXT1的初始压缩纹理的族谱将4x4 = 16px视为一个块,并且

每块使用64位颜色(RGB),即64/16 = 4bpp(每像素位)。

在这种情况下,由于不存在α,因此每个块也添加64位(4bpp)的alpha-

不透明图像4 bpp-具有透明度8bpp的图像是DXT2~5和ETC2的配置。

由于PVRTC也表示4bpp的alpha值,因此具有透明度的块具有严重的颜色质量。

OPTPiX博客上有一篇很好的评论,关于

什么算法在RGBpp8 (24bpp)的4bpp的严格约束下压缩,即压缩比为1/6 。

ETC改进

ETC1的优点是它可以在亮度表中产生白色和黑色,因此

每个块有三种或更多颜色具有不同倾向(不能用代表性颜色的混合表示)的堵塞问题,这是DXT1的弱点,如果是白色或黑色,即使颜色增加也应该是好的。块感比DXT1软,因为存在子块划分。

在另一方面,在情况下,在博客OPTPiX不同色调ETC1的弱点是津市对角线,

这是压缩纹理的一个薄弱点,因为它甚至不通过划分子块在垂直顺利,甚至通过将横向色伸出的很这很明显。

如果你看一下ETC1,我想你可以看到Unity左臂周围的颜色突出和右臂的袖子也是。

简单的DXT1并不是特别弱,但ETC1还不够好。

 

ETC2正在坚定地研究ETC1的弱点。

增加了三种没有子块划分的新模式,ETC1中弱的图像得到了显着改善。

详细信息在以下文档中详述。这不仅仅是为了获得透明度。

ETC2: Texture Compression using Invalid Combinations https://www.semanticscholar.org/paper/ETC2%3A-texture-compression-using-invalid-Str%C3%B6m-Pettersson/3099e5c603c1c7c7ef735d3ebfe4c3032691fd30

与BC7和ASTC等每块128位的新一代压缩相比,质量似乎有点下降,但是到2018年它仍然可以广泛使用并且质量非常好,因此ETC2可能不是一个好的格式我想。

 

 

 

34、在不使用UnityEngine.UI的情况下在Canvas上绘图

看Unity UGUI的开源代码,UnityEngine.UI.dll的内容在Bitbuckethttps://bitbucket.org/Unity-Technologies/ui中。每个人都可以阅读它, 会看到是有Dirty脏标记(顶点,材质,Layout三个脏标记吧),Unity 会在 Canvas.willRenderCanvases事件中检查脏标记,如果有就会重新绘制合批等等操作。

 

这个只是为了更好的理解UGUI

UnityEngine.UI.dll是一个用C#编写的托管dll。

另一方面,Canvas,CanvasGroup,CanvasRenderer等是本机组件,命名空间也属UnityEngine。

Canvas没有对UnityEngine.UI.dll的依赖,如果你想在Canvas上绘图,只需使用CanvasRenderer即可。

using UnityEngine;
[RequireComponent(typeof(CanvasRenderer))]
public class RawImageTest : MonoBehaviour
{
    [SerializeField]
    private Texture2D _texture = null;
    public Texture2D Texture
    {
        get { return _texture; }
        set
        {
            if (_texture != value)
            {
                _renderRequired = true;
                _texture = value;
            }
        }
    }
    private CanvasRenderer _canvasRenderer = null;
    public CanvasRenderer CanvasRenderer
    {
        get { return _canvasRenderer != null ? _canvasRenderer : (_canvasRenderer = GetComponent<CanvasRenderer>()); }
    }
    void Start()
    {
        Canvas.willRenderCanvases += Canvas_willRenderCanvases;
    }
    private void OnDestroy()
    {
        Canvas.willRenderCanvases -= Canvas_willRenderCanvases;
    }
    private void Canvas_willRenderCanvases()
    {
        if (!_renderRequired)
            return;
        _renderRequired = false;
        const float size = 200;
        var mesh = new Mesh();
        mesh.vertices = new Vector3[]
        {
            new Vector3(-size, -size),
            new Vector3(-size,  size),
            new Vector3( size,  size),
            new Vector3( size, -size),
        };
        mesh.uv = new Vector2[]
        {
            new Vector2(0, 0),
            new Vector2(0, 1),
            new Vector2(1, 1),
            new Vector2(1, 0),
        };
        mesh.triangles = new int[] { 0, 1, 2, 2, 3, 0 };
        var renderer = this.CanvasRenderer;
        renderer.SetMesh(mesh);
        renderer.materialCount = 1; //请注意,如果您忘记了这一点,将无法绘制
        renderer.SetMaterial(Canvas.GetDefaultCanvasMaterial(), 0);
        renderer.SetTexture(this.Texture);
        renderer.SetColor(Color.white);
    }
    private bool _renderRequired = false;
#if UNITY_EDITOR
    private void OnValidate()
    {
        _renderRequired = true;
        if (!Application.isPlaying)
        {
            Canvas_willRenderCanvases();
        }
    }
#endif
}

此代码的重要部分是绘图时序,它使用Canvas.willRenderCanvases事件。这个事件是在LateUpdate之后触发。

Canvas.willRenderCanvases + = Canvas_willRenderCanvases;

您需要做的就是在CanvasRenderer中填写您需要在此事件中绘制的内容。

var renderer = this.CanvasRenderer;
renderer.SetMesh(mesh);
renderer.materialCount = 1; 
renderer.SetMaterial(Canvas.GetDefaultCanvasMaterial(), 0);
renderer.SetTexture(this.Texture);
renderer.SetColor(Color.white);

Canvas.willRenderCanvases 在每一帧调用。

Canvas与Unity的DynamicBatching 合批不同, 但是批处理条件类似,因此如果您知道相同的材质和参数匹配, 由于CanvasRenderer扮演类似于MaterialPropertyBlock的角色,因此以下方法可以与MaterialPropertyBlock相同的方式使用。但是,无法指定着色器参数名称

  • CanvasRenderer.SetTexture ※_MainTex
  • CanvasRenderer.SetAlphaTexture ※_AlphaTex
  • CanvasRenderer.SetColor ※适用于_Color
  • CanvasRenderer.SetAlpha ※仅8适用于_Color 的Alpha

如果这些参数完全匹配,它将被正确批处理。

 

CanvasUpdateRegistry 和 ICanvasElement

我认为UnityEngine.UI.CanvasUpdateRegistryhttps://docs.unity3d.com/ja/current/ScriptReference/UI.CanvasUpdateRegistry.html类在理解uGUI时非常重要。

刚刚提到 Canvas.willRenderCanvases事件分为布局和渲染两部分。

uGUI具有VerticalLayoutGroup等布局组件。

如果VerticalLayoutGroup 在绘图组件将Mesh发送到CanvasRenderer后调整布局,则1帧绘制变得奇怪。因此,必须始终在绘制之前完成布局。有很多方法可以实现这一点,例如提供另一个布局事件,保证在Canvas .willRenderCanvases事件之前触发,但是uGUI 选择将Canvas .willRenderCanvases切割成两个。

  • 需要重新布局的UGUI组件使用CanvasUpdateRegistry注册自己,并使用CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild
  • 需要重新绘制的UGUI组件使用CanvasUpdateRegistry注册CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild
  • CanvasUpdateRegistry将布局指令发送到Canvas.willRenderCanvases时刻为布局注册的组件。 布局完成后,将重绘指令发送到注册为重绘的组件
  • 在CanvasUpdateRegistry一侧取消了布局和重绘的注册,因此它将在注册的框架中执行一次。

ICanvasElement

可以使用CanvasUpdateRegistry注册任何实现UnityEngine.UI.ICanvasElement接口的类。不是MonoBehaviour,可以在普通的C#类中使用。

(*但是,当使用普通类时,似乎大多数都采用MonoBehaviour的形式,以免被transform和IsDestroyed的实现所困扰)

uGUI的组件基于实现ICanvasElement 的基类UnityEngine.UI.Graphic类,并且布局组件也随时包含在UnityEngine.UI.LayoutRebuilder类中。

ICanvasElement实现中最重要的一点是ICanvasElement.Rebuild方法。这是布局和重绘的主体。您可以通过查看参数传递的UnityEngine.UI.CanvasUpdate枚举类型来查看是否为布局调用了当前的Rebuild 。如您所见,此枚举有三个布局成员和两个绘图成员。

应用一下

using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(CanvasRenderer))]
public class RawImageTest : MonoBehaviour, ICanvasElement
{
    [SerializeField]
    private Texture2D _texture = null;
    public Texture2D Texture
    {
        get { return _texture; }
        set
        {
            if (_texture != value)
            {
                _texture = value;
                SetDirty();
            }
        }
    }
    private CanvasRenderer _canvasRenderer = null;
    public CanvasRenderer CanvasRenderer
    {
        get { return _canvasRenderer != null ? _canvasRenderer : (_canvasRenderer = GetComponent<CanvasRenderer>()); }
    }
    void OnEnable()
    {
        SetDirty();
    }
    private void Start()
    {
        StartCoroutine(DelayRegister());
    }
    //有一个IsDestroyed返回true的时间,所以我推迟了一点并注册了它。
    private System.Collections.IEnumerator DelayRegister()
    {
        yield return null;
        SetDirty();
    }
    private void SetDirty()
    {
        CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
        Debug.Log("SetDirty");
    }
    private void Canvas_willRenderCanvases()
    {
        const float size = 200;
        var mesh = new Mesh();
        mesh.vertices = new Vector3[]
        {
            new Vector3(-size, -size),
            new Vector3(-size,  size),
            new Vector3( size,  size),
            new Vector3( size, -size),
        };
        mesh.uv = new Vector2[]
        {
            new Vector2(0, 0),
            new Vector2(0, 1),
            new Vector2(1, 1),
            new Vector2(1, 0),
        };
        mesh.triangles = new int[] { 0, 1, 2, 2, 3, 0 };
        var renderer = this.CanvasRenderer;
        renderer.SetMesh(mesh);
        renderer.materialCount = 1; //请注意,如果您忘记了这一点,将无法绘制
        renderer.SetMaterial(Canvas.GetDefaultCanvasMaterial(), 0);
        renderer.SetTexture(this.Texture);
        renderer.SetColor(Color.white);
    }
    #region ICanvasElement
    void ICanvasElement.Rebuild(CanvasUpdate executing)
    {
        Debug.LogFormat("Rebuild {0}", executing);
        if (executing == CanvasUpdate.PreRender)
            Canvas_willRenderCanvases();
    }
    public void LayoutComplete() { }
    public void GraphicUpdateComplete() { }
    public bool IsDestroyed()
    {
        var result = this == null;
        Debug.LogFormat("IsDestroyed {0}", result);
        return result;
    }
    #endregion
#if UNITY_EDITOR
    private void OnValidate()
    {
        SetDirty();
        if (!Application.isPlaying)
        {
            Canvas_willRenderCanvases();
        }
    }
#endif
}

您不再需要直接订阅Canvas .willRenderCanvases,因此不再需要使用_renderRequired标志来保护每个帧的处理。

https://enrike3.hatenablog.com/entry/2018/03/10/160041

https://enrike3.hatenablog.com/entry/2018/03/10/190715

 

 

 

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:Age of Ai 设计师:meimeiellie 返回首页

打赏作者

u010019717

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值