2015-02-26 15:30:18 yxriyin 阅读数 2932

         如果你们美术不是很给力的话,是非常容易出现重复资源的情况。那么如果能剔除,当然是最好的了。

        先看下无场景的情况下apk大小:61.4M(这里有一个小插曲,本来是74M的,后来发现有一个jpg竟然占了13兆,仔细看了下发现是贴图采用了真彩色无压缩,所以还是使用压缩的先).

        然后加上所有场景文件,重新打包一个apk,大小变成:153M。

        然后用软件Duplicate Cleaner Free查看下重复文件。

       253个重复文件。

       那么接下来才是重点,怎么剔除呢,直接删掉肯定不行,因为引用这个文件的所有地方都会出错。那么就要按照下面的操作:

      选择一个原始模型,打开其他重复模型场景,将所有的模型改成原始模型,然后删除重复模型,确认后将原始模型拖入common。

       替换fbx比较麻烦,要一个个替换,但图片就相对简单了,只要直接设置就可以了。

       替换fbx的方法:先找到原始fbx,然后替换材质,可以先拖一个正确的到场景中,全部设置材质后,在break掉和prefab的关系,就不会红色了。

       然后比较恶心的是有些fbx替换后直接消失了,也没有红色的提示,这个就只能看你自己先截一张老的场景的图,然后和新的比较。

       public class FindMissing:ScriptableObject
{
    [MenuItem("Tools/FindMissing")]
    static void FindMissingMaterial()
    {
        Transform[] transforms = Selection.GetTransforms (SelectionMode.TopLevel | SelectionMode.OnlyUserModifiable);
        foreach(Transform transform in transforms)
        {
            MeshFilter mesh = transform.GetComponent<MeshFilter>();
            if(mesh != null && mesh.sharedMesh == null)
            {
                mesh.gameObject.SetActive(false);
            }

        }
    }
}

这个脚本可以把有问题的active设置为false.

function CreateObject() {
     var newObj : GameObject = new GameObject();
     newObj.transform.parent = target.transform;

     newObj.hideFlags = HideFlags.HideInHierarchy;
 }
这个脚本我怕弄不回来,就暂时不管了。

然后这真心是一个体力活,诶,下次一定要和美术说好规范。

我搞了一下午,还剩150个重复项,主要的问题还是因为我们场景确实很多,接近40个。

明天继续吧。

       又搞了一天,总算弄好了,不知道有没有漏掉的,不过没有关系,以后可以查漏补缺。看看新的apk大小吧:129M

减少了20+M,算是不错的成果了。


2015-11-25 21:59:35 wsc122181582 阅读数 234

影响性能的因素
  首先,我们得了解,影响游戏性能的因素哪些,才能对症下药。对于一个游戏来说,有两种主要的计算资源:CPU和GPU。它们会互相合作,来让我们的游戏可以在预期的帧率和分辨率下工作。CPU负责其中的帧率,GPU主要负责分辨率相关的一些东西。
  总结起来,主要的性能瓶颈在于:
1、CPU
  过多的Draw Calls
  复杂的脚本或者物理模拟
2、顶点处理
  过多的顶点
  过多的逐顶点计算
3、像素(Fragment)处理
  过多的fragment,overdraws
  过多的逐像素计算
4、带宽
  尺寸很大且未压缩的纹理
  分辨率过高的framebuffer
  对于CPU来说,限制它的主要是游戏中的Draw Calls。那么什么是Draw Call呢?如果你学过OpenGL,那么你一定还记得在每次绘图前,我们都需要先准备好顶点数据(位置、法线、颜色、纹理坐标等),然后调用一系列API把它们放到GPU可以访问到的指定位置,最后,我们需要调用_glDraw*命令,来告诉GPU,“嘿,我把东西都准备好了,你个懒家伙赶紧出来干活(渲染)吧!”。而调用_glDraw*命令的时候,就是一次Draw Call。那么为什么Draw Call会成为性能瓶颈呢(而且是CPU的瓶颈)?上面说到过,我们想要绘制图像时,就一定需要调用Draw Call。例如,一个场景里有水有树,我们渲染水的时候使用的是一个material以及一个shader,但渲染树的时候就需要一个完全不同的material和shader,那么就需要CPU重新准备顶点数据、重新设置shader,而这种工作实际是非常耗时的。如果场景中,每一个物体都使用不同的material、不同的纹理,那么就会产生太多Draw Call,影响帧率,游戏性能就会下降。当然,这里说得很简单,更详细的请自行谷歌。其他CPU的性能瓶颈还有物理、布料模拟、粒子模拟等,都是计算量很大的操作。
  而对于GPU来说,它负责整个渲染流水线。它会从处理CPU传递过来的模型数据开始,进行Vertex Shader、Fragment Shader等一系列工作,最后输出屏幕上的每个像素。因此它的性能瓶颈可能和需要处理的顶点数目的、屏幕分辨率、显存等因素有关。总体包含了顶点和像素两方面的性能瓶颈。在像素处理中,最常见的性能瓶颈之一是overdraw。Overdraw指的是,我们可能对屏幕上的像素绘制了多次。


一、影响因素:

Draw Calls(合并纹理、批处理)

顶点处理(使用LOD、遮挡剔除)

像素处理(绘制顺序、透明物体、减少实时光照)

带宽(减小贴图,利用缩放)


二、静态对象优化:

1.贴图(纹理):减小大小,利用缩放

2.模型:删除多余组件

3.渲染:使用光照贴图,遮挡剔除


三、动态对象优化:

1.对象:频繁创建删除的对象,使用对象池(ObjectPool)

2.资源:动态加载

3.相同对象:使用


四、使用Profiler


五、内存

1.代码优化

2.贴图压缩

3.架构设计

切换场景使用loading加载页面

GUI生命周期管理



2017-09-12 00:36:24 wxlguitar 阅读数 259

减小包大小不仅可以使缩短用户下载时间,还可以节省流量,同时AppStore对包大小也有限制。

排查Resources目录

Resources目录中的所有资源,不管有没有被引用,都会被打进最终的包中。所以是重点优化目录。

压缩贴图

贴图是最占资空间的,非透明贴图比透明贴图更节省空间。除此之外,贴图的文件格式是不会影响最终包大小的,因为无论哪种图片格式,最终都会被Unity转成平台相关的贴图。所以把项目中的psd图片转成png是没有必要的。

最有效的方法是在Unity中设置图片的压缩程度,以及最大尺寸,调整到再降低画质就会被严重影响为止。

压缩模型

模型文件通常也比较占资源,最方便的方法就是在Unity中设置其压缩程度。
需要注意的是,这里设置压缩,只能减少本地模型文件的大小,但其在内存中占的大小并不会减少。

这里写图片描述

压缩动画

AnimationClip也可以被压缩,而压缩后不仅本地文件变小,同时内存占用量也会减少。由于压缩减少了关键帧,所以可能会导致动作形变等问题。

音频格式

小而短的音效,使用wav存储;背景音乐等比较长的,使用mp3存储。
同时音效文件,可以通过调整Quality,来减少大小。

代码库文件

避免使用一些重型的库,比如System.xml。可以使用Mono.Xml代替。

使用.Net 2.0 Subset

Net 2.0 Subset比.Net 2.0精简了不少代码,打APK对比,可以减少0.9M。

这里写图片描述

官方指引
https://docs.unity3d.com/560/Documentation/Manual/ReducingFilesize.html

2018-10-13 16:20:00 c1sdn19 阅读数 85

U3D 贴图通道分离后为什么能减小体积

原理上,分离与否,不会减小图片原始体积,还可能增大了。

RGBA32 分离后 = RGB24 + A8,这种情况下大小没变

但压缩后就不一样了,因为RGBA32整张图的压缩过程中,每个像素是否可以压缩受制于R,G,B,A四个分量同时影响。

而分离后,RGB只受R,G,B三个量影响,A仅受A自己影响,很明显会带来更多的压缩可能性。

因此,当图片尺寸较大时,可以通过ALPHA通道分离方法来提高图片压缩率

posted on 2018-10-13 16:20 时空观察者9号 阅读(...) 评论(...) 编辑 收藏

2015-03-07 11:46:16 lyh916 阅读数 4758

参考链接:

http://blog.csdn.net/u010153703/article/details/45502895

http://blog.csdn.net/candycat1992/article/details/42127811

http://www.cnblogs.com/joeshifu/p/5489906.html


这是本篇文章的主角:



0.

贴图压缩:
Android平台使用ETC1格式压缩,但它不支持含Alpha通道的图片压缩。
所以一般把RGB和ALPHA分离出来,rgb值从RGB图获取,a值从Alpha图获取。
随着OPENGL ES 3.0的发布,ETC2也出了,支持Alpha通道,但就目前的市场,支持的机型还比较少,所以可以不用考虑。


IOS平台使用PVRTC格式压缩,它支持含Alpha通道的图片压缩。


贴图设置:

宽高为2的n次方
Read/Write Enabled不勾
Generate Mip Maps不勾



1.首先先理解一下Texture2D.GetPixels()这个方法,它会返回纹理中所有像素的颜色值,其中有一个参数是miplevel,通过调节下图中右上方那个滑动按钮即可看到不同miplevel下的样子。

miplevel为0时(默认),像素数目最多,最清晰:


miplevel为3时,有点像素风的感觉:


那么miplevel不同时,真正的差别究竟是什么呢,测试一下:

using UnityEngine;
using System.Collections;

public class TestTexture : MonoBehaviour {

    //需要先将TextureType设置为Advanced,然后设置Read/Write Enabled为true
    //如果图片宽高不为2的n次方,建议将NonPowerOf2设置为非None的选项,方便测试
    public Texture2D texture2D;
    private Color[] colors;

	void Start () 
    {
        //api:public Color[] GetPixels(int miplevel = 0);
        //以一张512x512的图为例,输出为:
        //262144,262144,65536,16384
        //结论:miplevel每增加1,则宽高同时除以2
        colors = texture2D.GetPixels();
        Debug.Log(colors.Length);
        colors = texture2D.GetPixels(0);
        Debug.Log(colors.Length);
        colors = texture2D.GetPixels(1);
        Debug.Log(colors.Length);
        colors = texture2D.GetPixels(2);
        Debug.Log(colors.Length);
	}

}

miplevel的取值范围:以一张512x512的图为例(512,即2的9次方),那么取值就是0到9。

那么知道miplevel之后,再说一下Generate Mip Maps,下图分别是勾选和不勾选的区别。勾选后,会生成各种不同miplevel值的纹理,当纹理距离摄像机较远时,会自动使用先前生成的纹理,优点是减少运算量,缺点是增加内存占用;不勾选,则相反。

  


2.那么回到正题,使用脚本批量处理,分离通道:

using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// 选择文件夹或者选择单个多个图片,然后右键即可
/// </summary>
public class SeperateChannel {

    private static List<string> allFiles;
    private static bool mipmap = false;

    [MenuItem("Assets/Seperate Channel")]
    static void Seperate()
    {
        string path = GetFullPath(Selection.objects[0]);
        allFiles = new List<string>();

        //如果选择的是文件夹,就获取文件夹下的所有文件
        //否则就获取选择的文件
        if (!path.Contains(".")) GetAllFiles(path);
        else GetAllFiles();
        allFiles.RemoveAll((s) => { return s.Contains("meta"); });

        string[] suffix = new string[] { ".psd", ".tga", ".png", ".jpg", ".bmp", ".tif", ".gif" };
        string[] convert = new string[] { "_RGB", "_Alpha" };//经处理后的纹理会带有这些字样
        for (int i = 0; i < allFiles.Count; i++)
        {
            bool needHandle = false;

            //经过两个for循环筛选出要处理的纹理
            for (int j = 0; j < suffix.Length; j++)
            {
                if (allFiles[i].EndsWith(suffix[j]))
                {
                    needHandle = true; 
                    break;
                }
            }

            for (int j = 0; j < convert.Length; j++)
            {
                if (allFiles[i].Contains(convert[j]))
                {
                    needHandle = false;
                    break;
                }
            }

            if (!needHandle) continue;
            Debug.Log(allFiles[i]);


            allFiles[i] = GetAssetPath(allFiles[i]);
            TextureImporter ti = TextureImporter.GetAtPath(allFiles[i]) as TextureImporter;
            ti.isReadable = true;

            Texture2D sourceTex = AssetDatabase.LoadAssetAtPath<Texture2D>(allFiles[i]);
            Color[] sourceColor = sourceTex.GetPixels();

            Texture2D rgbTex = new Texture2D(sourceTex.width, sourceTex.height, TextureFormat.RGB24, mipmap);
            rgbTex.SetPixels(sourceColor);
            WriteTexture(rgbTex, allFiles[i], true);

            Texture2D alphaTex = new Texture2D(sourceTex.width, sourceTex.height, TextureFormat.RGB24, mipmap);
            Color[] c = new Color[sourceColor.Length];
            for (int j = 0; j < c.Length; j++)
            {
                c[j] = new Color(sourceColor[j].a, 0, 0);
            }
            alphaTex.SetPixels(c);
            WriteTexture(alphaTex, allFiles[i], false);
        }

        AssetDatabase.Refresh();
        Debug.Log("分离完毕");
    }

    //获取完整的路径(用于递归遍历)
    public static string GetFullPath(Object assetObj)
    {
        string path = AssetDatabase.GetAssetPath(assetObj);
        path = Application.dataPath + path;
        path = path.Replace("AssetsAssets", "Assets");
        return path;
    }

    //获取以Asset开头的路径
    public static string GetAssetPath(string fullPath)
    {
        fullPath = fullPath.Substring(fullPath.IndexOf("Assets"));
        return fullPath;
    }

    //获取选择的所有文件
    public static void GetAllFiles()
    {
        for (int i = 0; i < Selection.objects.Length; i++)
        {
            string s = GetFullPath(Selection.objects[i]);
            allFiles.Add(s);
        }
    }

    //递归获取一个根目录下的所有文件  
    public static void GetAllFiles(string dir)
    {
        string[] files = Directory.GetFiles(dir);
        foreach (var item in files)
        {
            allFiles.Add(item);
        }

        string[] dirs = Directory.GetDirectories(dir);
        foreach (var item in dirs)
        {
            GetAllFiles(item);
        }
    }

    //写入
    public static void WriteTexture(Texture2D tex, string assetPath, bool rgb)
    {
        string path = Application.dataPath + assetPath;
        path = path.Replace("AssetsAssets", "Assets");
        string suffix = path.Substring(path.IndexOf("."));

        if (rgb) path = path.Replace(suffix, "_RGB" + suffix);
        else path = path.Replace(suffix, "_Alpha" + suffix);
        Debug.Log(path);

        tex.Apply();
        byte[] bytes = tex.EncodeToPNG();
        File.WriteAllBytes(path, bytes);
        AssetDatabase.Refresh();

        //重新导入
        path = GetAssetPath(path);
        TextureImporter ti = TextureImporter.GetAtPath(path) as TextureImporter;
        ti.isReadable = false;
        ti.textureFormat = TextureImporterFormat.AutomaticCompressed;
        AssetDatabase.ImportAsset(path);
    }

}




谈谈Unity模型优化

阅读数 866

性能优化新思路

阅读数 524

【Unity】NGUI优化

阅读数 583

没有更多推荐了,返回首页