Unity性能优化之特效合并

Wesley13
• 阅读 1222

特效合并,意思是说将粒子所用的零碎图片,以shader为单位合并成一张图集,好处就是可以降低draw call。试想,合并前每个粒子使用一个material,而每一个material就要占用一个drawcall,而合并后多个粒子可以用同一个material,这样就降低了drawcall,提升了性能。

转载请注明出处:http://www.cnblogs.com/jietian331/p/8625078.html

合并工具的代码如下:

Unity性能优化之特效合并 Unity性能优化之特效合并

  1 using System;
  2 using System.Collections.Generic;
  3 using System.IO;
  4 using System.Linq;
  5 using System.Text;
  6 using UnityEditor;
  7 using UnityEngine;
  8 
  9 namespace AssetBundle
 10 {
 11     public class ParticleSystemCombiner : ScriptableObject
 12     {
 13         public const string
 14             AtlasFolder = "Assets/Cloth/Resources/ParticleSystemAtlas",
 15             ParticleAtlasPath = "Assets/Cloth/Resources/ParticleSystemAtlas/particle_atlas.prefab",
 16             SettingFilepath = "Assets/Editor/ParticleSystemCombinerSetting.csv";
 17 
 18         static string[] EffectObjFolders = new string[] 
 19         { 
 20             "Assets/Cloth/Resources/Effect/Cloth",
 21             "Assets/Cloth/Resources/Model/Equip",
 22         };
 23 
 24         static ParticleAtlases s_atlasesData;
 25         static List<string> s_materials;
 26         static Dictionary<string, int> s_texturesSize;
 27 
 28         static ParticleAtlases AtlasesData
 29         {
 30             get
 31             {
 32                 if (s_atlasesData == null)
 33                     s_atlasesData = AssetDatabase.LoadAssetAtPath<ParticleAtlases>(ParticleAtlasPath);
 34                 return s_atlasesData;
 35             }
 36         }
 37 
 38         static Dictionary<string, int> TexturesSize
 39         {
 40             get
 41             {
 42                 if (s_texturesSize == null)
 43                 {
 44                     s_texturesSize = new Dictionary<string, int>();
 45 
 46                     string[] lines = File.ReadAllLines(SettingFilepath);
 47                     bool decode = false;
 48                     foreach (var line in lines)
 49                     {
 50                         if (!decode)
 51                         {
 52                             if (line.StartsWith("# Texture Size"))
 53                             {
 54                                 decode = true;
 55                             }
 56                         }
 57                         else
 58                         {
 59                             if (line.StartsWith("#"))
 60                             {
 61                                 decode = false;
 62                             }
 63                         }
 64 
 65                         if (decode)
 66                         {
 67                             string[] words = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
 68                             if (words.Length > 1)
 69                             {
 70                                 string name = words[0];
 71                                 int size;
 72                                 int.TryParse(words[1], out size);
 73                                 if (size != 0)
 74                                     s_texturesSize[name] = size;
 75                             }
 76                         }
 77                     }
 78                 }
 79                 return s_texturesSize;
 80             }
 81         }
 82 
 83 
 84         static List<string> NotCombineTextures
 85         {
 86             get
 87             {
 88                 List<string> list = new List<string>();
 89                 string[] lines = File.ReadAllLines(SettingFilepath);
 90                 bool decode = false;
 91                 foreach (var line in lines)
 92                 {
 93                     if (!decode)
 94                     {
 95                         if (line.StartsWith("# Not Combine Textures"))
 96                         {
 97                             decode = true;
 98                         }
 99                     }
100                     else
101                     {
102                         if (line.StartsWith("#"))
103                         {
104                             decode = false;
105                         }
106                     }
107 
108                     if (decode)
109                     {
110                         string[] words = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
111                         if (words.Length > 0 && !string.IsNullOrEmpty(words[0]))
112                         {
113                             list.Add(words[0]);
114                         }
115                     }
116                 }
117                 return list;
118             }
119         }
120 
121         #region for build
122 
123         public static void ClearCache()
124         {
125             s_atlasesData = null;
126             s_materials = null;
127         }
128 
129         public static List<ParticleAtlases.TextureItem> GetParticlesUsedAtlas(GameObject effectObj)
130         {
131             List<ParticleAtlases.TextureItem> texturesData = new List<ParticleAtlases.TextureItem>();
132             ParticleSystem[] particles = effectObj.GetComponentsInChildren<ParticleSystem>(true);
133 
134             foreach (ParticleSystem ps in particles)
135             {
136                 ParticleSystemRenderer render = ps.GetComponent<ParticleSystemRenderer>();
137                 if (!render || !render.sharedMaterial)
138                 {
139                     Debug.LogWarning("Particle no material: " + ps.name);
140                     continue;
141                 }
142 
143                 Texture texture = render.sharedMaterial.mainTexture;
144                 if (ps.textureSheetAnimation.enabled || !texture)
145                     continue;
146 
147                 foreach (var atlasData in AtlasesData.Atlases)
148                 {
149                     foreach (var t in atlasData.Textures)
150                     {
151                         if (t.Name == texture.name)
152                         {
153                             texturesData.Add(t);
154                             break;
155                         }
156                     }
157                 }
158             }
159 
160             return texturesData;
161         }
162 
163         public static void ProcessEffectObj(GameObject obj)
164         {
165             ParticleSystem[] particles = obj.GetComponentsInChildren<ParticleSystem>(true);
166 
167             foreach (ParticleSystem ps in particles)
168             {
169                 ParticleSystemRenderer render = ps.GetComponent<ParticleSystemRenderer>();
170                 if (!render || !render.sharedMaterial)
171                 {
172                     Debug.LogWarning("Particle no material: " + ps.name);
173                     continue;
174                 }
175 
176                 Texture texture = render.sharedMaterial.mainTexture;
177                 if (ps.textureSheetAnimation.enabled || !texture)
178                     continue;
179 
180                 ParticleAtlases.Atlas target = AtlasesData.Atlases.FirstOrDefault(a => a.Textures.Any(t => t.Name == texture.name && t.ShaderName == render.sharedMaterial.shader.name));
181 
182                 if (target != null)
183                 {
184                     ParticleLoader loader = ps.GetComponent<ParticleLoader>();
185                     if (!loader)
186                         loader = ps.gameObject.AddComponent<ParticleLoader>();
187                     loader.TextureName = texture.name;
188                     loader.ShaderName = render.sharedMaterial.shader.name;
189                     render.sharedMaterial = null;
190 
191                     if (!ps.trails.enabled)
192                         render.trailMaterial = null;
193                 }
194             }
195 
196             EditorUtility.SetDirty(obj);
197             AssetDatabase.SaveAssets();
198         }
199 
200         public static List<string> GetAllMaterials()
201         {
202             if (s_materials != null)
203                 return s_materials;
204 
205             List<string> effects;
206             Dictionary<Shader, List<ParticleSystem>> dic = GetAllParticles(out effects);
207             s_materials = new List<string>();
208 
209             foreach (var pair in dic)
210             {
211                 foreach (ParticleSystem ps in pair.Value)
212                 {
213                     ParticleSystemRenderer r = ps.GetComponent<ParticleSystemRenderer>();
214                     string path = AssetDatabase.GetAssetPath(r.sharedMaterial);
215                     if (!s_materials.Contains(path))
216                         s_materials.Add(path);
217                 }
218             }
219 
220             return s_materials;
221         }
222 
223         #endregion
224 
225 
226         [MenuItem("BuildTool/AssetBundle/Combine Particle System")]
227         static void Init()
228         {
229             CombineAllEffectTextures();
230 
231             EditorUtility.DisplayDialog("finished", "All work finished.", "ok");
232         }
233 
234         static Dictionary<Shader, List<ParticleSystem>> GetAllParticles(out List<string> effectObjs)
235         {
236             // 获取所有的粒子
237             List<ParticleSystem> particlesList = new List<ParticleSystem>();
238             List<string> objPaths = new List<string>();
239             foreach (var effectObjFolder in EffectObjFolders)
240             {
241                 string[] paths = Directory.GetFiles(effectObjFolder, "*.prefab", SearchOption.AllDirectories);
242                 objPaths.AddRange(paths);
243             }
244             effectObjs = new List<string>();
245 
246             foreach (string path in objPaths)
247             {
248                 string pathFixed = path.Replace("\\", "/");
249                 effectObjs.Add(pathFixed);
250                 GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(pathFixed);
251                 ParticleSystem[] particles = obj.GetComponentsInChildren<ParticleSystem>(true);
252 
253                 foreach (ParticleSystem ps in particles)
254                 {
255                     ParticleSystemRenderer r = ps.GetComponent<ParticleSystemRenderer>();
256                     bool needCombine = !ps.textureSheetAnimation.enabled
257                         && r.sharedMaterial
258                         && r.sharedMaterial.shader.name != "Particles/Alpha Blended Premultiply"
259                         && r.sharedMaterial.mainTexture
260                         && r.sharedMaterial.mainTexture.width == r.sharedMaterial.mainTexture.height;
261                     if (needCombine)
262                         particlesList.Add(ps);
263                 }
264             }
265 
266             // 分类
267             Dictionary<Shader, List<ParticleSystem>> dic = new Dictionary<Shader, List<ParticleSystem>>();
268             foreach (ParticleSystem ps in particlesList)
269             {
270                 ParticleSystemRenderer r = ps.GetComponent<ParticleSystemRenderer>();
271                 var shader = r.sharedMaterial.shader;
272                 if (!dic.ContainsKey(shader))
273                     dic[shader] = new List<ParticleSystem>();
274                 dic[shader].Add(ps);
275             }
276 
277             return dic;
278         }
279 
280         public static List<string> CombineAllEffectTextures()
281         {
282             List<string> atlases = new List<string>();
283 
284             // 获取所有的粒子
285             List<string> effects;
286             Dictionary<Shader, List<ParticleSystem>> dic = GetAllParticles(out effects);
287 
288             // combine
289             Dictionary<Texture2D, Material> dictTextures = new Dictionary<Texture2D, Material>();
290             List<ParticleAtlases.Atlas> atlasesData = new List<ParticleAtlases.Atlas>();
291             List<string> notCombineTextures = NotCombineTextures;
292 
293             foreach (var pair in dic)
294             {
295                 // get textures
296                 dictTextures.Clear();
297                 foreach (ParticleSystem ps in pair.Value)
298                 {
299                     ParticleSystemRenderer r = ps.GetComponent<ParticleSystemRenderer>();
300                     Texture2D texture = (Texture2D)r.sharedMaterial.mainTexture;
301                     if (!notCombineTextures.Contains(texture.name) && !dictTextures.ContainsKey(texture))
302                         dictTextures.Add(texture, r.sharedMaterial);
303                 }
304 
305                 if (dictTextures.Count < 2)
306                     continue;
307 
308                 Texture2D[] texturesArray = dictTextures.Keys.ToArray();
309 
310                 // combine texture
311                 string atlasName = string.Format("ParticleAtlas_{0}", Path.GetFileNameWithoutExtension(pair.Key.name));
312                 string atlasPath = string.Format("{0}/{1}.png", AtlasFolder, atlasName);
313                 Uploader.CreateDirectory(atlasPath);
314                 Rect[] rects;
315                 Vector2[] textureSizes;
316                 Texture2D atlas = CombineTextures(texturesArray, atlasPath, out rects, out textureSizes);
317                 atlases.Add(atlasPath);
318 
319                 // create material
320                 string matPath = string.Format("{0}/{1}.mat", AtlasFolder, atlasName);
321                 Material mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
322                 if (mat == null)
323                 {
324                     mat = new Material(pair.Key);
325                     AssetDatabase.CreateAsset(mat, matPath);
326                 }
327                 mat.mainTexture = atlas;
328 
329                 // get config
330                 ParticleAtlases.TextureItem[] texturesData = new ParticleAtlases.TextureItem[texturesArray.Length];
331                 for (int i = 0; i < texturesArray.Length; i++)
332                 {
333                     Rect rect = rects[i];
334                     Texture2D texture2D = texturesArray[i];
335                     Vector2 textureSize = textureSizes[i];      // will resize temp texture, so cann't use texture2D.width
336                     int numTilesX = (int)(atlas.width / textureSize.x);
337                     int numTilesY = (int)(atlas.height / textureSize.y);
338                     int colIndex = (int)(rect.x * numTilesX);
339                     int rowIndex = (int)(numTilesY - 1 - rect.y * numTilesY);
340                     int index = rowIndex * numTilesX + colIndex;
341 
342                     // get color
343                     Material oldMat = dictTextures[texture2D];
344                     Color32 oldColor = oldMat.GetColor("_TintColor");
345                     string strColor = string.Format("{0}_{1}_{2}_{3}", oldColor.r, oldColor.g, oldColor.b, oldColor.a);
346 
347                     string shaderName = oldMat.shader.name;
348 
349                     int depth = oldMat.renderQueue;
350 
351                     texturesData[i] = new ParticleAtlases.TextureItem()
352                     {
353                         Color = oldColor,
354                         Depth = depth,
355                         Index = index,
356                         Name = texture2D.name,
357                         NumTilesX = numTilesX,
358                         NumTilesY = numTilesY,
359                         ShaderName = shaderName,
360                     };
361                 }
362 
363                 ParticleAtlases.Atlas atlasData = new ParticleAtlases.Atlas()
364                 {
365                     Material = mat,
366                     Textures = texturesData,
367                 };
368                 atlasesData.Add(atlasData);
369             }
370 
371             GameObject prefabObj = AssetDatabase.LoadAssetAtPath<GameObject>(ParticleAtlasPath);
372             if (!prefabObj)
373                 prefabObj = PrefabUtility.CreatePrefab(ParticleAtlasPath, new GameObject());
374             ParticleAtlases atlasesCom = prefabObj.GetComponent<ParticleAtlases>();
375             if (!atlasesCom)
376                 atlasesCom = prefabObj.AddComponent<ParticleAtlases>();
377             atlasesCom.Atlases = atlasesData.ToArray();
378             prefabObj.name = Path.GetFileNameWithoutExtension(ParticleAtlasPath);
379 
380             EditorUtility.SetDirty(prefabObj);
381             AssetDatabase.SaveAssets();
382 
383             Debug.Log("All cloth effect textures combine finished!");
384 
385             return atlases;
386         }
387 
388         static Texture2D CombineTextures(Texture2D[] textures, string path, out Rect[] rects, out Vector2[] textureSizes)
389         {
390             if (textures == null || textures.Length < 1)
391             {
392                 Debug.LogError("None textures");
393                 rects = null;
394                 textureSizes = null;
395                 return null;
396             }
397 
398             string tempFolderName = "_TempImages";
399             string tempFolder = "Assets/" + tempFolderName;
400             AssetDatabase.DeleteAsset(tempFolder);
401             AssetDatabase.CreateFolder("Assets", tempFolderName);
402 
403             List<string> newPaths = new List<string>();
404             var newTextures = new Texture2D[textures.Length];
405             textureSizes = new Vector2[textures.Length];
406 
407             // 将原来的图片复制一份出来
408             for (int i = 0; i < textures.Length; i++)
409             {
410                 string texPath = AssetDatabase.GetAssetPath(textures[i]);
411                 if (File.Exists(texPath))
412                 {
413                     string newPath = string.Format("{0}/{1}", tempFolder, Path.GetFileName(texPath));
414                     AssetDatabase.CopyAsset(texPath, newPath);
415                     newPaths.Add(newPath);
416                 }
417                 else
418                 {
419                     Debug.Log("File not exists: " + texPath);
420                 }
421             }
422 
423             // make it readable
424             for (int i = 0; i < newPaths.Count; i++)
425             {
426                 string newPath = newPaths[i];
427                 SetSourceTextureReadalbe(newPath);
428                 Texture2D t = AssetDatabase.LoadAssetAtPath<Texture2D>(newPath);
429                 textureSizes[i] = new Vector2(t.width, t.height);
430 
431                 // 去掉边缘的一个像素
432                 if (t.width > 4)
433                 {
434                     for (int j = 0; j < t.width; j++)
435                     {
436                         t.SetPixel(j, 0, new Color(0, 0, 0, 0));
437                         t.SetPixel(j, 1, new Color(0, 0, 0, 0));
438                         t.SetPixel(j, t.height - 1, new Color(0, 0, 0, 0));
439                         t.SetPixel(j, t.height - 2, new Color(0, 0, 0, 0));
440                     }
441                 }
442 
443                 if (t.height > 4)
444                 {
445                     for (int z = 0; z < t.height; z++)
446                     {
447                         t.SetPixel(0, z, new Color(0, 0, 0, 0));
448                         t.SetPixel(1, z, new Color(0, 0, 0, 0));
449                         t.SetPixel(t.width - 1, z, new Color(0, 0, 0, 0));
450                         t.SetPixel(t.width - 2, z, new Color(0, 0, 0, 0));
451                     }
452                 }
453 
454                 newTextures[i] = t;
455             }
456 
457             // pack
458             Texture2D atlas = new Texture2D(1, 1, TextureFormat.ARGB32, false);
459             rects = atlas.PackTextures(newTextures, 0, 4096, false);
460 
461             // save
462             byte[] bytes = atlas.EncodeToPNG();
463             File.WriteAllBytes(path, bytes);
464             AssetDatabase.Refresh();
465             AssetDatabase.SaveAssets();
466 
467             // setting
468             TextureCompresser.CompressRGBA(path);
469 
470             // 删除临时目录
471             AssetDatabase.DeleteAsset(tempFolder);
472 
473             AssetDatabase.Refresh();
474             AssetDatabase.SaveAssets();
475 
476             return AssetDatabase.LoadAssetAtPath<Texture2D>(path);
477         }
478 
479         static void SetSourceTextureReadalbe(string path)
480         {
481             string name = Path.GetFileNameWithoutExtension(path);
482             int maxSize;
483             TexturesSize.TryGetValue(name, out maxSize);
484             if (maxSize == 0)
485                 maxSize = 64;
486 
487             bool readable = true;
488             TextureImporterNPOTScale npotScale = TextureImporterNPOTScale.ToNearest;
489             TextureWrapMode wrapMode = TextureWrapMode.Clamp;
490             TextureImporterCompression compression = TextureImporterCompression.Uncompressed;
491 
492             bool changed = false;
493 
494             var importer = (TextureImporter)AssetImporter.GetAtPath(path);
495             TextureImporterSettings settings = new TextureImporterSettings();
496             importer.ReadTextureSettings(settings);
497 
498             settings.alphaIsTransparency = true;
499             settings.mipmapEnabled = false;
500 
501             if (settings.readable != readable)
502             {
503                 settings.readable = readable;
504                 changed = true;
505             }
506 
507             if (settings.npotScale != npotScale)
508             {
509                 settings.npotScale = npotScale;
510                 changed = true;
511             }
512 
513             if (settings.wrapMode != wrapMode)
514             {
515                 settings.wrapMode = wrapMode;
516                 changed = true;
517             }
518 
519             if (importer.maxTextureSize != maxSize)
520             {
521                 importer.maxTextureSize = maxSize;
522                 changed = true;
523             }
524 
525             if (importer.textureCompression != compression)
526             {
527                 importer.textureCompression = compression;
528                 changed = true;
529             }
530 
531             // set platform overriten as false
532             var androidSetting = importer.GetPlatformTextureSettings("Android");
533             var iosSetting = importer.GetPlatformTextureSettings("iPhone");
534             var pcSetting = importer.GetPlatformTextureSettings("Standalone");
535             if (androidSetting.overridden)
536             {
537                 androidSetting.overridden = false;
538                 changed = true;
539             }
540             if (iosSetting.overridden)
541             {
542                 iosSetting.overridden = false;
543                 changed = true;
544             }
545             if (pcSetting.overridden)
546             {
547                 pcSetting.overridden = false;
548                 changed = true;
549             }
550             importer.SetPlatformTextureSettings(androidSetting);
551             importer.SetPlatformTextureSettings(iosSetting);
552             importer.SetPlatformTextureSettings(pcSetting);
553 
554             if (changed)
555             {
556                 importer.SetTextureSettings(settings);
557                 AssetDatabase.ImportAsset(path);
558             }
559         }
560 
561     }
562 }

ParticleSystemCombiner

加载的代码:

Unity性能优化之特效合并 Unity性能优化之特效合并

1 using UnityEngine;
2 
3 public partial class ParticleLoader : MonoBehaviour
4 {
5     public string TextureName;
6     public string ShaderName;
7 }

ParticleLoader

Unity性能优化之特效合并 Unity性能优化之特效合并

  1 using Common;
  2 using UnityEngine;
  3 
  4 public partial class ParticleLoader : MonoBehaviour
  5 {
  6     static ParticleAtlases s_atlases;
  7 
  8 
  9     // 加载图集
 10     public static void Initialize()
 11     {
 12         BundleLoader.Singleton.LoadAssetBundle("atlases/particlesystematlas.u", r =>
 13         {
 14             GameObject[] objs = r.Bundle.LoadAllAssets<GameObject>();
 15             s_atlases = objs[0].GetComponent<ParticleAtlases>();
 16         });
 17     }
 18 
 19     void OnEnable()
 20     {
 21         Load();
 22     }
 23 
 24     void Load()
 25     {
 26         ParticleSystemRenderer renderer = GetComponent<ParticleSystemRenderer>();
 27         if (!renderer)
 28             return;
 29 
 30         ParticleAtlases.Atlas targetAtlas = null;
 31         ParticleAtlases.TextureItem targetTextureConfig = null;
 32 
 33         // 找对应的配置
 34         for (int i = 0; i < s_atlases.Atlases.Length; i++)
 35         {
 36             ParticleAtlases.Atlas atlasData = s_atlases.Atlases[i];
 37             for (int j = 0; j < atlasData.Textures.Length; j++)
 38             {
 39                 ParticleAtlases.TextureItem textureData = atlasData.Textures[j];
 40                 if (textureData.Name == this.TextureName && textureData.ShaderName == this.ShaderName)
 41                 {
 42                     targetAtlas = atlasData;
 43                     targetTextureConfig = textureData;
 44                     break;
 45                 }
 46             }
 47 
 48             if (targetAtlas != null)
 49                 break;
 50         }
 51 
 52         if (targetAtlas != null && targetTextureConfig != null)
 53         {
 54             ParticleSystem ps = GetComponent<ParticleSystem>();
 55 
 56             renderer.sharedMaterial = targetAtlas.Material;             // 渲染
 57 
 58             SetTextureSheet(ps, targetTextureConfig);                   // 设置格子
 59 
 60             SetStartColor(ps, renderer, targetTextureConfig.Color);     // 设置颜色
 61 
 62             SetDepth(renderer, targetTextureConfig);                    // 排序
 63         }
 64     }
 65 
 66     // 设置格子
 67     void SetTextureSheet(ParticleSystem ps, ParticleAtlases.TextureItem targetTextureConfig)
 68     {
 69         var tsa = ps.textureSheetAnimation;
 70         float curveConstant = (float)targetTextureConfig.Index / targetTextureConfig.NumTilesX / targetTextureConfig.NumTilesY;
 71         tsa.enabled = true;
 72         tsa.numTilesX = targetTextureConfig.NumTilesX;
 73         tsa.numTilesY = targetTextureConfig.NumTilesY;
 74         tsa.animation = ParticleSystemAnimationType.WholeSheet;
 75         tsa.startFrame = new ParticleSystem.MinMaxCurve(0);
 76         tsa.frameOverTime = new ParticleSystem.MinMaxCurve(curveConstant);
 77         tsa.cycleCount = 1;
 78     }
 79 
 80     // 设置颜色
 81     void SetStartColor(ParticleSystem ps, ParticleSystemRenderer renderer, Color matColor)
 82     {
 83         var main = ps.main;
 84         switch (main.startColor.mode)
 85         {
 86             case ParticleSystemGradientMode.Color:
 87             case ParticleSystemGradientMode.Gradient:
 88             case ParticleSystemGradientMode.RandomColor:
 89             case ParticleSystemGradientMode.TwoGradients:
 90                 var targetColor = main.startColor.color * matColor;
 91                 main.startColor = new UnityEngine.ParticleSystem.MinMaxGradient(targetColor);
 92                 break;
 93 
 94             case ParticleSystemGradientMode.TwoColors:
 95                 var colorMin = main.startColor.colorMin * matColor;
 96                 var colorMax = main.startColor.colorMax * matColor;
 97                 main.startColor = new UnityEngine.ParticleSystem.MinMaxGradient(colorMin, colorMax);
 98                 break;
 99 
100             default:
101                 Debug.LogError("Unknown mode: " + main.startColor.mode);
102                 break;
103         }
104 
105         renderer.sharedMaterial.SetColor("_TintColor", Color.white);
106     }
107 
108     // 排序
109     void SetDepth(ParticleSystemRenderer renderer, ParticleAtlases.TextureItem targetTextureConfig)
110     {
111         int depth = targetTextureConfig.Depth;
112         if (depth > 0 && renderer.sharedMaterial.renderQueue < depth)
113             renderer.sharedMaterial.renderQueue = depth;
114     }
115 
116 }

ParticleLoader

 合并后的图集如下:

Unity性能优化之特效合并

 合并后的粒子如下:

Unity性能优化之特效合并

效果如下:

Unity性能优化之特效合并

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这