1)Texture Streaming 的应用疑难
2)Unity 3D 场景 UI 被打断合批的起因
3)Asset Provider 和 Asset Bundle Provider 的意义
4)Addressables 更新资源时只加载最初始资源
5)描边算法显示问题
这是第 246 篇 UWA 技术常识分享的推送。明天咱们持续为大家精选了若干和开发、优化相干的问题,倡议浏览工夫 10 分钟,认真读完必有播种。
UWA 问答社区:answer.uwa4d.com
UWA QQ 群 2:793972859(原群已满员)
Editor
Q:Unity 2019.2 Texture Streaming 在 Editor 下不失效吗?依照 Unity 官网介绍,能够在 SceneView 外面 Debug Texture Streaming。然而实际上切换过来会变成浅蓝色 / 深蓝色,并没有其余任何变动。有人遇到雷同的状况吗?
A:1. 须要测试脚本能力晓得节俭了多少内存,场景是看不出来的。
using System; using UnityEditor; using UnityEngine; public class ShowTextureStreamingSummary : MonoBehaviour { public float X = 10; public float Y = 20; public int MemoryBudgetPercentThreshold = 80; public int TextureStreamingPercentThreshold = 50; private ulong HighestDesiredTextureMemory; private Rect TextRect; public void Start() { HighestDesiredTextureMemory = 0; Texture.streamingTextureDiscardUnusedMips = false; QualitySettings.masterTextureLimit = 2;// 强制等级为 2,为了配合上面的测试脚本 } public string HumanReadableSize(ulong size) {return string.Format("{0:0.0}M", (float)size / (float)(1024 * 1024)); } void ShowText(string text) { float yInc = GUI.skin.font.lineHeight; GUI.Label(TextRect, text); TextRect.y += yInc; } public void OnGUI() {TextRect = new Rect(X, Y, Screen.width - X, Screen.height - Y); GUI.color = Color.red; if (!SystemInfo.supportsMipStreaming) ShowText("Texture streaming unsupported"); if (!QualitySettings.streamingMipmapsActive) ShowText("Texture streaming disabled"); else if (QualitySettings.streamingMipmapsMemoryBudget == 0) ShowText("No texture streaming budget"); else if (Texture.totalTextureMemory == 0) ShowText("No texture memory needed"); else { // Reduced highest memory usage if (Texture.desiredTextureMemory > HighestDesiredTextureMemory) HighestDesiredTextureMemory = Texture.desiredTextureMemory; // Show stats ulong budget = (ulong)(1024 * 1024 * QualitySettings.streamingMipmapsMemoryBudget); float percentUsed = (float)(100 * Texture.desiredTextureMemory) / (float)budget; ShowText(string.Format("Memory budget utilisation {0:0.0}% of {1} texture budget", percentUsed, HumanReadableSize(budget))); if (HighestDesiredTextureMemory > budget) { ulong memoryExcess = HighestDesiredTextureMemory - budget; ShowText(string.Format("Memory exceeds budget by {0}", HumanReadableSize(memoryExcess))); } else { ulong memorySaving = Texture.totalTextureMemory - HighestDesiredTextureMemory; float percentReduction = (float)(100 * HighestDesiredTextureMemory) / (float)Texture.totalTextureMemory; ShowText(string.Format("Memory saving at least {0} with streaming enabled (at {1:0.0}% of original {2}) - ignoring caching", HumanReadableSize(memorySaving), percentReduction, HumanReadableSize(Texture.totalTextureMemory))); } // Advice section #if UNITY_EDITOR ShowText("Run in standalone app for accurate figures. When run in Play Mode the stats are biased by editor textures"); #endif if (percentUsed < (float)MemoryBudgetPercentThreshold) ShowText(string.Format("Reduce the Memory Budget closer to {0}", HumanReadableSize(Texture.desiredTextureMemory))); else if (Texture.desiredTextureMemory > budget) ShowText(string.Format("Raise Memory Budget above {0}", HumanReadableSize(Texture.desiredTextureMemory))); float percentStreaming = (float)(100 * (Texture.totalTextureMemory - Texture.nonStreamingTextureMemory)) / (float)Texture.totalTextureMemory; if (percentStreaming < (float)TextureStreamingPercentThreshold) ShowText(string.Format("Mark more textures streaming to improve savings ({0:0.0}% texture memory marked as streaming)", percentStreaming)); if (!Texture.streamingTextureDiscardUnusedMips) ShowText("Consider turning on Texture.streamingTextureDiscardUnusedMips to analyse without cached textures"); ShowText(string.Format("desiredTextureMemory {0}", HumanReadableSize(Texture.desiredTextureMemory))); ShowText(string.Format("nonStreamingTextureMemory {0}", HumanReadableSize(Texture.nonStreamingTextureMemory))); ShowText(string.Format("totalTextureMemory {0}", HumanReadableSize(Texture.totalTextureMemory))); } } }
2.Game 窗口下,查看哪位纹理应用了 Streaming。显示绿的是应用了 Streaming;蓝色的是未开启 Streaming;红色的是还没应用 Streaming。
using UnityEngine; public class TextureStreamingDebug : MonoBehaviour { public Camera m_MainCamera; public Shader m_ReplacementShader; public bool m_ActivateDebugShader; private bool m_DebugShaderActive = false; private RenderingPath originalRenderingPath; void Start() { // Grab camera from self if none set if (!m_MainCamera) m_MainCamera = GetComponent<Camera>(); if (m_MainCamera) originalRenderingPath = m_MainCamera.renderingPath; } void Update() {if (!m_MainCamera || !m_ReplacementShader) return; #if UNITY_STANDALONE_WIN || UNITY_EDITOR if (Input.GetKeyDown(KeyCode.Space)) m_ActivateDebugShader ^= true; #else if (Input.GetButtonDown("Fire1")) m_ActivateDebugShader ^= true; #endif if (m_ActivateDebugShader != m_DebugShaderActive) {if (m_ActivateDebugShader) { m_MainCamera.renderingPath = RenderingPath.Forward; m_MainCamera.SetReplacementShader(m_ReplacementShader, "RenderType"); } else { m_MainCamera.renderingPath = originalRenderingPath; m_MainCamera.ResetReplacementShader();} m_DebugShaderActive = m_ActivateDebugShader; } if (m_DebugShaderActive) Texture.SetStreamingTextureMaterialDebugProperties();} }
对应 Shader:
Shader "Hidden/Scene View Show Texture Streaming" { Properties {_MainTex ("", 2D) ="white" {} _Control ("Control (RGBA)", 2D) = "red" {} _Splat3 ("Layer 3 (A)", 2D) = "white" {} _Splat2 ("Layer 2 (B)", 2D) = "white" {} _Splat1 ("Layer 1 (G)", 2D) = "white" {} _Splat0 ("Layer 0 (R)", 2D) = "white" {} _BaseMap ("", 2D) ="white" {} _Cutoff ("Cutoff", float) = 0.5 } CGINCLUDE // Common code used by most of the things below #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; }; uniform float4 _MainTex_ST; uniform float4 _MainTex_TexelSize; uniform float4 _MainTex_MipInfo; UNITY_DECLARE_TEX2D(_MainTex); UNITY_DECLARE_TEX2D(_SceneViewMipcolorsTexture); uint GetMipCount(Texture2D tex) {#if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12) || defined(SHADER_API_D3D11_9X) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_PSSL) #define MIP_COUNT_SUPPORTED 1 #endif #if (defined(SHADER_API_OPENGL) || defined(SHADER_API_VULKAN)) && !defined(SHADER_STAGE_COMPUTE) // OpenGL only supports textureSize for width, height, depth // textureQueryLevels (GL_ARB_texture_query_levels) needs OpenGL 4.3 or above and doesn't compile in compute shaders // tex.GetDimensions converted to textureQueryLevels #define MIP_COUNT_SUPPORTED 1 #endif // Metal doesn't support high enough OpenGL version #if defined(MIP_COUNT_SUPPORTED) uint mipLevel, width, height, mipCount; mipLevel = width = height = mipCount = 0; tex.GetDimensions(mipLevel, width, height, mipCount); return mipCount; #else return 0; #endif } float4 GetStreamingMipColor(uint mipCount, float4 mipInfo) {// alpha is amount to blend with source color (0.0 = use original, 1.0 = use new color) // mipInfo : // x = quality setings minStreamingMipLevel // y = original mip count for texture // z = desired on screen mip level // w = loaded mip level uint originalTextureMipCount = uint(mipInfo.y); // If material/shader mip info (original mip level) has not been set it’s either not a streamed texture // or no renderer is updating it if (originalTextureMipCount == 0) return float4(0.0, 0.0, 1.0, 0.5); uint desiredMipLevel = uint(mipInfo.z); uint mipCountDesired = uint(originalTextureMipCount)-uint(desiredMipLevel); if (mipCount == 0) { // Can't calculate, use the passed value mipCount = originalTextureMipCount - uint(mipInfo.w); } if (mipCount < mipCountDesired) {// red tones when not at the desired mip level (reduction due to budget). Brighter is further from original, alpha 0 when at desired float ratioToDesired = float(mipCount) / float(mipCountDesired); return float4(1.0, 0.0, 0.0, 1.0 - ratioToDesired); } else if (mipCount >= originalTextureMipCount) {// original color when at (or beyond) original mip count return float4(1.0, 1.0, 1.0, 0.0); } else { // green tones when not at the original mip level. Brighter is closer to original, alpha 0 when at original float ratioToOriginal = float(mipCount) / float(originalTextureMipCount); return float4(0.0, 1.0, 0.0, 1.0 - ratioToOriginal); } } float3 GetDebugStreamingMipColorBlended(float3 originalColor, Texture2D tex, float4 mipInfo) {uint mipCount = GetMipCount(tex); float4 mipColor = GetStreamingMipColor(mipCount, mipInfo); return lerp(originalColor, mipColor.rgb, mipColor.a); } v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); return o; } fixed4 frag(v2f i) : COLOR {fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv); half4 res; res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo); res.a = col.a; return res; } struct v2fGrass { float4 pos : SV_POSITION; fixed4 color : COLOR; float2 uv : TEXCOORD0; }; fixed4 fragGrass(v2fGrass i) : COLOR {fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv); half4 res; res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo); res.a = col.a * i.color.a; return res; } ENDCG SubShader {Tags { "ForceSupported" = "True" "RenderType"="Opaque"} Pass { CGPROGRAM // As both normal opaque shaders and terrain splat shaders // have "Opaque" render type, we need to do some voodoo // to make both work. #pragma vertex vertWTerrain #pragma fragment fragWTerrain #pragma target 2.0 #pragma exclude_renderers gles struct v2fterr { float4 pos : SV_POSITION; float2 uvnormal : TEXCOORD0; float4 uv[3] : TEXCOORD2; float nonterrain : TEXCOORD5; }; uniform float4 _Splat0_ST,_Splat1_ST,_Splat2_ST,_Splat3_ST,_Splat4_ST; uniform float4 _Splat0_TexelSize,_Splat1_TexelSize,_Splat2_TexelSize,_Splat3_TexelSize,_Splat4_TexelSize; uniform float4 _BaseMap_TexelSize; v2fterr vertWTerrain(appdata_base v) { v2fterr o; o.pos = UnityObjectToClipPos(v.vertex); // assume it's not a terrain if _Splat0_TexelSize is not set up. float nonterrain = _Splat0_TexelSize.z==0.0 ? 1:0; // collapse/don't draw terrain's add pass in this mode, since it looks really bad if first pass // and add pass blink depending on which gets drawn first with this replacement shader // TODO: make it display mips properly even for two-pass terrains. o.pos *= _MainTex_TexelSize.z==0.0 && _Splat0_TexelSize.z!=0.0 ? 0 : 1; // normal texture UV o.uvnormal = TRANSFORM_TEX(v.texcoord,_MainTex); // terrain splat UVs float2 baseUV = v.texcoord.xy; o.uv[0].xy = baseUV; o.uv[0].zw = half2(0,0); o.uv[1].xy = TRANSFORM_TEX (baseUV, _Splat0); o.uv[1].zw = TRANSFORM_TEX (baseUV, _Splat1); o.uv[2].xy = TRANSFORM_TEX (baseUV, _Splat2); o.uv[2].zw = TRANSFORM_TEX (baseUV, _Splat3); o.nonterrain = nonterrain; return o; } UNITY_DECLARE_TEX2D(_Control); UNITY_DECLARE_TEX2D(_Splat0); UNITY_DECLARE_TEX2D(_Splat1); UNITY_DECLARE_TEX2D(_Splat2); UNITY_DECLARE_TEX2D(_Splat3); UNITY_DECLARE_TEX2D(_BaseMap); fixed4 fragWTerrain(v2fterr i) : COLOR { // sample regular texture fixed4 colnormal = UNITY_SAMPLE_TEX2D(_MainTex, i.uvnormal); // sample splatmaps half4 splat_control = UNITY_SAMPLE_TEX2D(_Control, i.uv[0].xy); half3 splat_color = splat_control.r * UNITY_SAMPLE_TEX2D(_Splat0, i.uv[1].xy).rgb; splat_color += splat_control.g * UNITY_SAMPLE_TEX2D(_Splat1, i.uv[1].zw).rgb; splat_color += splat_control.b * UNITY_SAMPLE_TEX2D(_Splat2, i.uv[2].xy).rgb; splat_color += splat_control.a * UNITY_SAMPLE_TEX2D(_Splat3, i.uv[2].zw).rgb; // lerp between normal and splatmaps half3 col = lerp(splat_color, colnormal.rgb, (half)i.nonterrain); half4 res; // TODO: Take splat mips into account res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo); res.a = colnormal.a; return res; } ENDCG } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="Transparent"} Pass { Cull Off CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles ENDCG } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="TransparentCutout"} Pass {AlphaTest Greater [_Cutoff] CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles ENDCG } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="TreeBark"} Pass { CGPROGRAM #pragma vertex vertTreeBark #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles #include "UnityCG.cginc" #include "UnityBuiltin3xTreeLibrary.cginc" v2f vertTreeBark (appdata_full v) { v2f o; TreeVertBark(v); o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } ENDCG } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="TreeLeaf"} Pass { CGPROGRAM #pragma vertex vertTreeLeaf #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles #include "UnityCG.cginc" #include "UnityBuiltin3xTreeLibrary.cginc" v2f vertTreeLeaf (appdata_full v) { v2f o; TreeVertLeaf (v); o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } ENDCG AlphaTest GEqual [_Cutoff] } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="TreeOpaque"} Pass { CGPROGRAM #pragma vertex vertTree #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles #include "TerrainEngine.cginc" struct appdata { float4 vertex : POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; }; v2f vertTree(appdata v) { v2f o; TerrainAnimateTree(v.vertex, v.color.w); o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } ENDCG } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="TreeTransparentCutout"} Pass { Cull Off CGPROGRAM #pragma vertex vertTree #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles #include "TerrainEngine.cginc" struct appdata { float4 vertex : POSITION; fixed4 color : COLOR; float4 texcoord : TEXCOORD0; }; v2f vertTree(appdata v) { v2f o; TerrainAnimateTree(v.vertex, v.color.w); o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } ENDCG AlphaTest GEqual [_Cutoff] } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="TreeBillboard"} Pass { Cull Off ZWrite Off CGPROGRAM #pragma vertex vertTree #pragma fragment frag #pragma target 2.0 #pragma exclude_renderers gles #include "TerrainEngine.cginc" v2f vertTree (appdata_tree_billboard v) { v2f o; TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y); o.pos = UnityObjectToClipPos(v.vertex); o.uv.x = v.texcoord.x; o.uv.y = v.texcoord.y > 0; return o; } ENDCG SetTexture [_MainTex] {combine primary, texture} } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="GrassBillboard"} Pass { Cull Off CGPROGRAM #pragma vertex vertGrass #pragma fragment fragGrass #pragma target 2.0 #pragma exclude_renderers gles #include "TerrainEngine.cginc" v2fGrass vertGrass (appdata_full v) { v2fGrass o; WavingGrassBillboardVert (v); o.color = v.color; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } ENDCG AlphaTest Greater [_Cutoff] } } SubShader {Tags { "ForceSupported" = "True" "RenderType"="Grass"} Pass { Cull Off CGPROGRAM #pragma vertex vertGrass #pragma fragment fragGrass #pragma target 2.0 #pragma exclude_renderers gles #include "TerrainEngine.cginc" v2fGrass vertGrass (appdata_full v) { v2fGrass o; WavingGrassVert (v); o.color = v.color; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; return o; } ENDCG AlphaTest Greater [_Cutoff] } } Fallback Off }
感激牛头人不服 @UWA 问答社区提供了答复
Rendering
Q:如下图,应用 Sprite Renderer 做图片和 TextMeshPro 做文字,应用 Unity 帧调试器查看,发现第一排三个图片动静合批了,第二排三个图片动静合批了,第三排三个图片动静合批了,然而文字都合不了批,起因是动静批处理在 Player Settings 中被敞开或在以后环境中被临时禁用,以防止 Z -Fighting。能不能所有图片一个批次,所有文字一个批次?
A1:我的办法是设置 Sorting Layer 和 Order in Layer:
SpriteRenderer -> Additional Settings;
TextMeshPro Text -> Extra Settings。但这样的毛病是字永远会在图的下面,重叠的时候显示会有问题。
感激小埃拉 @UWA 问答社区提供了答复
A2:1. 对于一般的 Mesh 物体的绘制,也就是应用 Mesh Renderer 绘制的 Mesh:在开启了动静合批的状况下,应用雷同 Material 的两个物体,以相邻程序绘制时,如果满足其余动静合批的条件,即可合批。
题主的文字局部没有合批,是因为没有开启动静合批。看 Frame Debugger,题主的渲染管线应用的是 SRP,开启动静合批的形式是在 RenderPipelineAsset 的 Inspector 面板的 Advanced 下勾选 Dynamic Batching。
至于说为什么图片呈现了合批景象,这是因为图片应用 Sprite Renderer 来绘制,其网格是 Unity 动静生成的,合批的行为也是 Unity 外部解决的,不受 Dynamic Batching 开启与否的影响。
2. 其次是管制渲染程序,使应该合批的物体以相邻程序渲染,先绘制所有图片,再绘制所有文字。
办法一:依照楼上说的,设置 Order in Layer,图片的 Order in Layer 设置为 0,文字为 1。
办法二:将所有图片的 Render Queue 都设置为 3000,将所有文字的 Render Queue 都设置为 3001。
成果如下:
感激 Prin@UWA 问答社区提供了答复
Addressable
Q:请问有没有人晓得 Addressable 1.17.13 中 Asset Provider 和 Asset Bundle Provider 这两个选项的意义?
A1:能够搜寻下 Addressables 的源码。能够扩大默认的实现,来自定义本人的下载和加载流程。
[DisplayName(“Assets from Bundles Provider”)]
public class BundledAssetProvider : ResourceProviderBase
[DisplayName(“AssetBundle Provider”)]
public class AssetBundleProvider : ResourceProviderBase
感激 jim@UWA 问答社区提供了答复
A2:这个是用来自定义加载 AssetBundle 和 AssetBundle 外面的资源的形式,题主能够看下 Addressables.CN 版本外面的 AssetBundleProvider.cs 的实现,在这个版本外面增加了解密 AssetBundle 的办法,算是另外一种加载 AssetBundle 的形式。
感激 Xuan@UWA 问答社区提供了答复
Addressable
Q:问题形容如下:
应用场景:我正通过 Addressables 实现无需重启游戏的热更新。
问题复现:
- Part1:以后公布的资源为 A 版本。
- Part2:而后我公布了 B 版本的资源,关上游戏后,以后还未开始更新,所以加载的资源为 A 版本。我开始查看 Catalog 和资源更新,检测到须要更新后,则开释之前加载的所有 Handle,开始热更新。再次加载资源,此时为 B 版本。重启游戏,再次加载资源也是 B 版本。(这里所有看起来失常)
- Part3:我公布了 C 版本的资源,此时关上游戏,就呈现问题了。我加载的资源是 A 版本,而不是 B 版本。(这一步,我认为有问题)我还是紧接着开释之前加载的所有 Handle,开始热更新,再次加载资源。C 版本的资源也能够被应用。
- Part4:我起初开始狐疑是否是 Addressable 在联网状态下,主动会去申请 Catalog(我曾经勾选 Disable Catalog Update on Startup)。如果检测到有资源更新,就会应用最初始版本,而不是热更新后的最新版本资源。于是通过我的试验,我把我的资源服务器关掉,它就主动应用热更新后的最新版本。而启动资源服务器后,加载的资源就会是最初始版本,只有在我热更新后,才恢复正常。
冀望成果:我既然曾经更新资源,不论是什么状况下,这个资源就应该是最新的。而不是莫名其妙地呈现了最初始版本的资源。
Unity 版本:2020.1.9f1c1
平台:Windows
相干代码:测试 Addressables 我的项目
https://gitee.com/dreamCirno/…
A1:不晓得你用的是哪个版本,倡议参考 UWA 问答中的信息:
https://answer.uwa4d.com/ques…或者看看调用的程序是不是有问题,我是依照这个顺序调用的:
InitializeAsync->CheckForCatalogUpdates->UpdateCatalogs->GetDownloadSizeAsync->DownloadDependenciesAsync
感激 jim@UWA 问答社区提供了答复
A2:从你的形容上看,感觉你这个如同是没更新到 B 版本的 Catalog 导致的。
感激 Robot.Huang@UWA 问答社区提供了答复
A3:参考如下代码:
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using UnityEngine.ResourceManagement.ResourceProviders; using UnityEngine.UI; using static UnityEngine.AddressableAssets.Addressables; public class GameLaunch : MonoBehaviour { public Action<string> OnStatusTipHasChanged; public Action<float> OnDownloadPercentHasChanged; public Action<bool> OnDownloadHasResult; AsyncOperationHandle<GameObject> handle; GameObject obj; /// <summary> /// 自定义指定待更新检测的 Label 汇合 /// </summary> private List<string> mKeys = new List<string>() { "default"}; public IEnumerator CheckUpdate() { bool isNeedUpdateCatalog = false; bool isNeedUpdateResources = false; // 初始化 Addressable var initHandle = Addressables.InitializeAsync(); yield return initHandle; OnStatusTipHasChanged?.Invoke("开始查看更新"); Debug.LogError("开始查看更新"); // 查看本地 Catalog 是否为最新版本 var checkHandle = Addressables.CheckForCatalogUpdates(false); yield return checkHandle; if (checkHandle.Status == AsyncOperationStatus.Succeeded) {OnDownloadPercentHasChanged?.Invoke(1); OnStatusTipHasChanged?.Invoke("目录查看实现"); Debug.LogError("目录查看实现"); } List<string> catalogs = checkHandle.Result; if (catalogs != null && catalogs.Count > 0) {OnStatusTipHasChanged?.Invoke("检测到目录须要更新"); Debug.LogError("检测到 Catalogs 须要更新"); isNeedUpdateCatalog = true; } else {OnStatusTipHasChanged?.Invoke("检测到目录已是最新"); Debug.LogError("检测到 Catalogs 已是最新"); } var sizeHandle = Addressables.GetDownloadSizeAsync(mKeys); if (sizeHandle.Result > 0) {Debug.LogError("检测到有更新资源包"); OnStatusTipHasChanged?.Invoke("检测到有更新资源包"); isNeedUpdateResources = true; } else {Debug.LogError("检测到没有资源更新"); OnStatusTipHasChanged?.Invoke("检测到没有资源更新"); } OnStatusTipHasChanged?.Invoke("筹备进行下一步"); if (isNeedUpdateCatalog || isNeedUpdateResources) {if (isNeedUpdateCatalog) {yield return UpdateCatalog(catalogs); } if (isNeedUpdateResources) {yield return UpdateResources(); } OnDownloadHasResult?.Invoke(true); } else {//StartGame(); Debug.LogError("开始游戏"); } } private void Update() {if (Input.GetKeyDown(KeyCode.C)) {StartCoroutine(CheckUpdate()); } if (Input.GetKeyDown(KeyCode.L)) {handle = Addressables.LoadAssetAsync<GameObject>("Image"); handle.Completed += param => {if (param.Status == AsyncOperationStatus.Succeeded) {Debug.LogError("预加载胜利"); } else {Debug.LogError("预加载失败"); } obj = param.Result; }; } if (Input.GetKeyDown(KeyCode.R)) {ReleaseCache(); } if (Input.GetKeyDown(KeyCode.Space)) {Instantiate(obj, new Vector2(UnityEngine.Random.Range(0, 400), UnityEngine.Random.Range(0, 400)), Quaternion.identity, GameObject.Find("Canvas").transform); } } private IEnumerator UpdateCatalog(List<string> catalogs) {var updateHandle = Addressables.UpdateCatalogs(catalogs, false); Debug.LogError("开始更新 Catalogs"); yield return updateHandle; Addressables.Release(updateHandle); } private IEnumerator UpdateResources() {ReleaseCache(); // 需更新大小 > 0,示意须要下载更新 // 清理旧资源 //var clearHandle = Addressables.ClearDependencyCacheAsync(mKeys, false); //yield return clearHandle; // 下载待更新资源 var downloadHandle = Addressables.DownloadDependenciesAsync(mKeys, MergeMode.Union, false); Debug.LogError("开始更新资源"); while (!downloadHandle.IsDone) {DownloadStatus downloadStatus = downloadHandle.GetDownloadStatus(); OnDownloadPercentHasChanged?.Invoke(downloadStatus.Percent); OnStatusTipHasChanged?.Invoke($"下载进度: {downloadStatus.Percent * 100} %"); Debug.LogError($"下载进度: {downloadStatus.Percent * 100} %"); yield return null; } if (downloadHandle.Status == AsyncOperationStatus.Succeeded) {OnStatusTipHasChanged?.Invoke($"下载状况:{(downloadHandle.IsDone ?" 实现 ":" 未实现 ")}"); Debug.LogError($"下载状况:{(downloadHandle.IsDone ?" 实现 ":" 未实现 ")}"); } else {OnStatusTipHasChanged?.Invoke($"下载更新包失败"); Debug.LogError($"下载更新包失败,谬误内容:{downloadHandle.OperationException.Message}"); } Addressables.Release(downloadHandle); } private void ReleaseCache() { try {Addressables.Release(handle); Debug.LogError("开释资源胜利"); } catch (Exception) {Debug.LogError("开释资源失败"); } } }
感激题主李传东 @UWA 问答社区提供了答复
Rendering
Q:应用法线描边算法,在一些比拟薄的网格上有穿模景象。这是算法问题还是网格问题?应该如何解决?
Shader 如图:
A1:比拟狐疑两点:
1. 是否要 Cull Front 而不是 Cull Back?
2. 如果是本人渲染的 RenderTexture,是不是深度的精度给的不太够?
感激王宇 @UWA 问答社区提供了答复
A2:是这种办法会产生的固有问题,渲染的 Backfaces 与原有模型产生深度抵触,遮挡模型,造成穿透问题。
解决办法:一种办法是给 Backfaces 设置 Z -offset,让轮廓线湮没到邻近的面里。另一种办法是批改 Backfaces 扩张的法线,使轮廓线(Backfaces)扁平化。
相干参考链接:https://blog.csdn.net/candyca…
感激 Xuan@UWA 问答社区提供了答复
封面图来源于网络
明天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题兴许都只是冰山一角,咱们早已在 UWA 问答网站上筹备了更多的技术话题等你一起来摸索和分享。欢送酷爱提高的你退出,兴许你的办法恰能解他人的当务之急;而他山之“石”,也能攻你之“玉”。
官网:www.uwa4d.com
官网技术博客:blog.uwa4d.com
官网问答社区:answer.uwa4d.com
UWA 学堂:edu.uwa4d.com
官网技术 QQ 群:793972859(原群已满员)