1) Questions about the use of Texture Streaming
2) the reason why the Unity 3D scene UI is interrupted and approved
3) Meaning of Asset Provider and Asset Bundle Provider
4) Addressables loads only the initial resources when updating resources
5) Tracing algorithm display problem
This is the 246th UWA technical knowledge sharing push. Today, we continue to select a number of issues related to development and Optimization for you. It is suggested to read for 10 minutes. After reading carefully, you will get results.
UWA Q & a community: answer.uwa4d.com
UWA QQ group 2: 793972859 (the original group is full)
Editor
Q: Does Unity 2019.2 Texture Streaming not take effect under Editor? According to the official introduction of Unity, you can Debug Texture Streaming in SceneView. But in fact, the switch will change to light blue / dark blue without any other changes. Does anyone encounter the same situation?
A: 1. You need to test the script to know how much memory is saved, which can't be seen in the scenario.
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;//The mandatory level is 2. In order to cooperate with the following test script } 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. In the game window, check which texture uses Streaming. In green, Streaming is used; Blue indicates that Streaming is not turned on; The red one is that Streaming is not used yet.
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(); } }
Corresponding 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 }
Thank the Tauren @ UWA Q & a community for providing answers
Rendering
Q: As shown in the following figure, using Sprite Renderer to make pictures and TextMeshPro to make text, and using the Unity frame debugger to check, it is found that three pictures in the first row are dynamically combined, three pictures in the second row are dynamically combined, and three pictures in the third row are dynamically combined, but the text cannot be combined. The reason is that dynamic batch processing is turned off in Player Settings or temporarily disabled in the current environment, To avoid Z-Fighting. Can all pictures be in one batch and all words in one batch?
A1: my method is to set Sorting Layer and Order in Layer:
SpriteRenderer -> Additional Settings;
TextMeshPro Text -> Extra Settings.
But this disadvantage is that the words will always be on the top of the figure, and there will be problems in the display when overlapping.
Thank little Ella @ UWA Q & a community for providing answers
A2: 1. For the drawing of ordinary Mesh objects, that is, Mesh drawn with Mesh Renderer: when dynamic batch is turned on, when two objects of the same Material are drawn in adjacent order, batch can be combined if other conditions for dynamic batch are met.
The text part of the title is not approved because dynamic approval is not opened. Look at the Frame Debugger. The rendering pipeline of the subject uses SRP. The way to start dynamic batch is to check Dynamic Batching under Advanced on the Inspector panel of RenderPipelineAsset.
As for the reason why the pictures are in batch, this is because the pictures are drawn by Sprite Renderer. Its grid is dynamically generated by Unity, and the batch behavior is also processed internally by Unity, which is not affected by whether Dynamic Batching is turned on or not.
2. The second is to control the rendering order, so that the objects that should be grouped are rendered in adjacent order. First draw all pictures, and then draw all words.
Method 1: set the Order in Layer as mentioned above. The Order in Layer of the picture is set to 0 and the text is 1.
Method 2: set the Render Queue of all pictures to 3000 and the Render Queue of all text to 3001.
The effect is as follows:
thank Prin@UWA The Q & a community provided answers
Addressable
Q: Does anyone know the meaning of Asset Provider and Asset Bundle Provider in Addressable 1.17.13?
A1: you can search the source code of Addressables. You can extend the default implementation to customize your own download and load processes.
[DisplayName("Assets from Bundles Provider")]
public class BundledAssetProvider : ResourceProviderBase
[DisplayName("AssetBundle Provider")]
public class AssetBundleProvider : ResourceProviderBase
thank jim@UWA The Q & a community provided answers
A2: This is used to customize the way of loading AssetBundle and the resources in AssetBundle. You can see addressable The version in assetprovider.cn The implementation of CS adds the method of decrypting AssetBundle in this version, which is another way to load AssetBundle.
thank Xuan@UWA The Q & a community provided answers
Addressable
Q: The problem is described as follows:
Usage scenario: I'm using Addressables to implement hot update without restarting the game.
Problem recurrence:
- Part1: the currently published resource is version A.
- Part2: then I released version b resources. After opening the game, the update has not started yet, so the loaded resources are version A. I start to check the Catalog and resource updates. When I detect that an update is needed, I release all the previously loaded handles and start the hot update. Load the resource again, which is version B. Restart the game and load resources again, which is also version B. (everything looks normal here)
- Part 3: I released the resources of version C. at this time, when I open the game, there is A problem. The resource I loaded is version A, not version B. (I think there is A problem in this step) I still release all the handles loaded before, start hot update and load resources again. C version resources can also be used.
- Part 4: later, I began to doubt whether it was Addressable. In the online state, I would automatically request Catalog (I have checked Disable Catalog Update on Startup). If a resource update is detected, the latest version of the resource will be used instead of the latest version after the hot update. So after my experiment, I turned off my resource server and it automatically used the latest version after hot update. After starting the resource server, the loaded resource will be the initial version. It will return to normal only after I hot update.
Expected effect: since I have updated the resource, under any circumstances, this resource should be the latest. Instead of inexplicably appearing in the initial version of resources.
Unity version: 2020.1.9f1c1
Platform: Windows
Related code: Test Addressables project
https://gitee.com/dreamCirno/addressable-hotfix-test/tree/master
A1: I don't know which version you are using. It is recommended to refer to the information in UWA Q & A:
https://answer.uwa4d.com/question/5e3b7fe686a39548919837e1
Or see if there is a problem with the calling order. I call in this order:
InitializeAsync->CheckForCatalogUpdates->UpdateCatalogs->GetDownloadSizeAsync->DownloadDependenciesAsync
thank jim@UWA The Q & a community provided answers
A2: from your description, it seems that your Catalog is not updated to version B.
Thank you, robot Huang@UWA The Q & a community provided answers
A3: refer to the following code:
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> ///Customize and specify the Label set to be updated /// </summary> private List<string> mKeys = new List<string>() { "default" }; public IEnumerator CheckUpdate() { bool isNeedUpdateCatalog = false; bool isNeedUpdateResources = false; // Initialize Addressable var initHandle = Addressables.InitializeAsync(); yield return initHandle; OnStatusTipHasChanged?.Invoke("Start checking for updates"); Debug.LogError("Start checking for updates"); // Check whether the local Catalog is the latest version var checkHandle = Addressables.CheckForCatalogUpdates(false); yield return checkHandle; if (checkHandle.Status == AsyncOperationStatus.Succeeded) { OnDownloadPercentHasChanged?.Invoke(1); OnStatusTipHasChanged?.Invoke("Directory check complete"); Debug.LogError("Directory check complete"); } List<string> catalogs = checkHandle.Result; if (catalogs != null && catalogs.Count > 0) { OnStatusTipHasChanged?.Invoke("A catalog update was detected"); Debug.LogError("Detected Catalogs Need update"); isNeedUpdateCatalog = true; } else { OnStatusTipHasChanged?.Invoke("It is detected that the directory is up to date"); Debug.LogError("Detected Catalogs It's up to date"); } var sizeHandle = Addressables.GetDownloadSizeAsync(mKeys); if (sizeHandle.Result > 0) { Debug.LogError("Update resource package detected"); OnStatusTipHasChanged?.Invoke("Update resource package detected"); isNeedUpdateResources = true; } else { Debug.LogError("No resource updates detected"); OnStatusTipHasChanged?.Invoke("No resource updates detected"); } OnStatusTipHasChanged?.Invoke("Ready for the next step"); if (isNeedUpdateCatalog || isNeedUpdateResources) { if (isNeedUpdateCatalog) { yield return UpdateCatalog(catalogs); } if (isNeedUpdateResources) { yield return UpdateResources(); } OnDownloadHasResult?.Invoke(true); } else { //StartGame(); Debug.LogError("Start the game"); } } 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("Preload successful"); } else { Debug.LogError("Preload failed"); } 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("Start update Catalogs"); yield return updateHandle; Addressables.Release(updateHandle); } private IEnumerator UpdateResources() { ReleaseCache(); // Update size > 0 indicates that updates need to be downloaded // Clean up old resources //var clearHandle = Addressables.ClearDependencyCacheAsync(mKeys, false); //yield return clearHandle; // Download resources to be updated var downloadHandle = Addressables.DownloadDependenciesAsync(mKeys, MergeMode.Union, false); Debug.LogError("Start updating resources"); while (!downloadHandle.IsDone) { DownloadStatus downloadStatus = downloadHandle.GetDownloadStatus(); OnDownloadPercentHasChanged?.Invoke(downloadStatus.Percent); OnStatusTipHasChanged?.Invoke($"Download progress: {downloadStatus.Percent * 100} %"); Debug.LogError($"Download progress: {downloadStatus.Percent * 100} %"); yield return null; } if (downloadHandle.Status == AsyncOperationStatus.Succeeded) { OnStatusTipHasChanged?.Invoke($"Download:{(downloadHandle.IsDone ? "complete" : "hang in the air")}"); Debug.LogError($"Download:{(downloadHandle.IsDone ? "complete" : "hang in the air")}"); } else { OnStatusTipHasChanged?.Invoke($"Failed to download update package"); Debug.LogError($"Failed to download update package, error content:{downloadHandle.OperationException.Message}"); } Addressables.Release(downloadHandle); } private void ReleaseCache() { try { Addressables.Release(handle); Debug.LogError("Resource released successfully"); } catch (Exception) { Debug.LogError("Failed to release resources"); } } }
Thank the questioner Li Chuandong @ UWA Q & a community for providing answers
Rendering
Q: Using the normal tracing algorithm, there is a die piercing phenomenon on some thin meshes. Is this an algorithm problem or a grid problem? How should it be solved?
Shader as shown in the figure:
A1: there are two doubtful points:
1. Do you want to Cull Front instead of Cull Back?
2. If the RenderTexture is rendered by yourself, is the accuracy of depth not enough?
Thank Wang Yu @ UWA Q & a community for providing answers
A2: it is an inherent problem caused by this method. The rendered Backfaces have a deep conflict with the original model, blocking the model and causing penetration problems.
Solution: one method is to set Z-offset for Backfaces to make the contour line buried in the adjacent face. Another method is to modify the normal of Backfaces expansion to flatten the contour lines.
Relevant reference links: https://blog.csdn.net/candycat1992/article/details/41120019
thank Xuan@UWA The Q & a community provided answers
The cover image comes from the Internet
That's all for today's sharing. Of course, life is boundless and knowledge is boundless. In the long development cycle, these questions you see may be just the tip of the iceberg. We have already prepared more technical topics on UWA Q & a website for you to explore and share. You who love progress are welcome to join us. Maybe your method can solve the urgent needs of others; And the "stone" of his mountain can also attack your "jade".
Official website: www.uwa4d.com
Official technology blog: blog.uwa4d.com
Official Q & a community: answer.uwa4d.com
UWA School: edu.uwa4d.com
Official technical QQ group: 793972859 (the original group is full)