加载外部图片的三种方式

一、 WWW 加载
首先,获取当前工程同目录下的“MapImages”文件夹路径,然后获取每张图片的全部路径,并将路径存到列表中。
 /// <summary>
    /// 获取当前物体应该读取的地形贴图的文件的路径
    /// </summary>
    /// <returns></returns>
    private string GetFilePath()
    {
        string[] strTempPath = Application.dataPath.Split('/');
        string strPath = string.Empty;
        //去掉后面两个,获取跟工程相同目录的路径,如“E:/ZXB/MyProject/Asset/",去掉后面两个就得到和工程同级的目录为:“E:/ZXB”
        for (int i = 0; i < strTempPath.Length - 2; i++)
        {
            strPath += strTempPath[i] + "/";
        }
        //加上整个地形贴图的文件命
        strPath += TERRAINMAP_FILENAME + "/";
        //最后加上当前文件夹的名称,最后的文件夹名称和当前物体的名称一致
        strPath += gameObject.name + "/";

        return strPath;
    }
    /// <summary>
    /// 获取所有地图贴图文件路径
    /// </summary>
    /// <param name="info"></param>
    private void GetAllFile(FileSystemInfo info)
    {
        if (!info.Exists)
        {
            Debug.Log("该路径不存在!");
            return;
        }
        DirectoryInfo dir = info as DirectoryInfo;
        if (null == dir)
        {
            Debug.Log("该目录不存在!");
            return;
        }
        FileSystemInfo[] si = dir.GetFileSystemInfos();
        for (int i = 0; i < si.Length; i++)
        {
            FileInfo fi = si[i] as FileInfo;
            if (null != fi && IsImage(fi.Extension))
            {
                listStrFileName.Add(fi.FullName);
            }
            else
            {

            }
        }
    }

然后根据路径列表使用WWW逐个加载图片
 /// <summary>
    /// 根据文件路径加载图片
    /// </summary>
    private IEnumerator GetAllTexture()
    {
        curFilePath = GetFilePath();
        DirectoryInfo tempInfo = new DirectoryInfo(curFilePath);
        GetAllFile(tempInfo);
        foreach (string item in listStrFileName)
        {
            WWW www = new WWW("file://" + item);
            yield return www;
            //先得到最后的图片文件名
            string[] tempAllFileName = item.Split(Path.DirectorySeparatorChar);
            //去掉后缀
            string tempStrKey = tempAllFileName[tempAllFileName.Length - 1];
            tempStrKey = tempStrKey.Substring(0, tempStrKey.Length - 4).Trim();
            if (null != tempStrKey && !dicTextures.ContainsKey(tempStrKey))
            {
                dicTextures.Add(tempStrKey, www.texture);
            }
            else
            {
                Debug.LogError("图片文件名为空或者有相同的文件名!");
                continue;
            }
            if (dicSubTerrainMat.ContainsKey(tempStrKey))
            {
                dicSubTerrainMat[tempStrKey].SetTexture("_MainTex", www.texture);
            }
            else
            {
                Debug.LogError("文件名" + tempStrKey + "在材质列表中没有找到对应的材质名!");
            }
        }
        isLoadAllTexture = true;
        Debug.Log("Load All Terrain Texture!");
    }
效果图如图所示:一开始显示的是默认模糊处理的贴图,在加载的过程中帧率非常低,造成卡顿。

二、 IO 文件流加载

        首先,同样的是加载文件路径。然后开启另外一个线程将图片的字节流加载到字典中。
 /// <summary>
    /// 根据文件路径读取图片并转换成byte
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static byte[] ReadPictureByPath(string path)
    {
        FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
        fileStream.Seek(0, SeekOrigin.Begin);
        byte[] binary = new byte[fileStream.Length];
        fileStream.Read(binary, 0, (int)fileStream.Length);
        fileStream.Close();
        fileStream.Dispose();
        fileStream = null;
        return binary;
    }
    然后,加载整个文件列表里的图片到字典缓存中。
    /// <summary>
    /// 根据文件路径加载图片
    /// </summary>
    private void GetAllTerrainTex()
    {
        DirectoryInfo tempInfo = new DirectoryInfo(curFilePath);
        GetAllFile(tempInfo);
        foreach (string item in listStrFileName)
        {
            byte[] tempImageBuffer = ReadPictureByPath(item);
            if (null == tempImageBuffer)
            {
                Debug.Log("读取路径" + item + "图片失败!");
            }
            string[] tempAllFileName = item.Split(Path.DirectorySeparatorChar);
            //去掉后缀
            string tempStrKey = tempAllFileName[tempAllFileName.Length - 1];
            tempStrKey = tempStrKey.Substring(0, tempStrKey.Length - 4).Trim();
            if (null != tempStrKey && !dicImageBuffer.ContainsKey(tempStrKey))
            {
                dicImageBuffer.Add(tempStrKey, tempImageBuffer);
            }
            else
            {
                Debug.LogError("图片文件名为空或者有相同的文件名!");
                continue;
            }
        }
        isLoadAllTexture = true;
        Debug.Log("Load All Terrain Texture!");
    }

最终的加载效果图如图所示:同样会导致帧率很低造成卡顿,但是这种方式有一个好处是可以先将图片使用其他线程加载到缓存中,造成卡顿是因为将字节流转成Texture2D
 
Texture2D tempTex = new Texture2D(TERRAIN_MAP_DI, TERRAIN_MAP_DI);
             tempTex.LoadImage(item.Value);

这里的地形贴图都很大,就算单张图片压缩到 4M 左右,使用 LoadImage 方法还是会卡顿。而这个方法又只能在主线程里使用,也即不能在新开的加载线程里使用。 Unity 对这类的类都限制了,只能在主线程使用。
三、 Assetbundle 打包再加载
    首先,要将贴图导入到 Unity 工程里面,然后对一个文件夹里的贴图进行打包。 .0 之后的打包方式比较简单,如图所示:
 
先选中要打包的贴图,在 Inspectors 面板下有个 AssetBundle ,这里新建你要打包成集合的名字,我以文件夹的名字新建了一个标签“ 40_73.4_36.69237_77.61875” 。选中文件夹里其他图片,将它们都设置成这个标签。这样就可以将整个文件夹里的图片打包成一个集合,方便一次性加载。

        打包的代码和简单,可以在 Editor 文件下,创建一个编辑脚本,代码如下:
[MenuItem("Example/Build Asset Bundles")]
    static void BuildABs()
    {
        // Put the bundles in a folder called "ABs" within the Assets folder.
        BuildPipeline.BuildAssetBundles("Assets/Assetbundle", BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }

对比以前的方式,新的打包方法确实简化方便了很多。接下来将打包好的Assetbundle文件夹放到工程的同级目录下,加载Assetbuudle的代码如下:
/// <summary>
    ///异步加载地形的贴图
    /// </summary>
    /// <returns></returns>
    private IEnumerator SetAllTexAsy()
    {
        yield return null;
        var bundleLoadRequest = AssetBundle.LoadFromFileAsync(curFilePath);
        yield return bundleLoadRequest;
        var myLoadedAssetBundle = bundleLoadRequest.assetBundle;
        if (myLoadedAssetBundle == null)
        {
            Debug.Log("Failed to load AssetBundle!");
            yield break;
        }
        foreach (var item in dicSubTerrainMat)
        {
            var assetLoadRequest = myLoadedAssetBundle.LoadAssetAsync<Texture2D>(item.Key);
            yield return assetLoadRequest;

            Texture2D tempTex = assetLoadRequest.asset as Texture2D;
            if (null == tempTex)
            {
                Debug.Log(gameObject.name + "Assetbundle里没有对应" + item.Key + "的贴图");
            }
            else
            {
                item.Value.SetTexture("_MainTex", tempTex);
            }
        }
        myLoadedAssetBundle.Unload(false);
    }
加载得到集合包,然后根据每个图片的文件名得到具体的图片。最终的效果图如图所示:
可以看到帧率几乎不受任何影响,但是会在加载到显示的过程中停留2秒时间左右,对于程序的卡顿,这个时间的停滞还是可以接受的。
四、总结
虽然,第三种方式在加载的时候比较流畅,不会影响到主线程的执行。但是,打包的过程也是一个很繁琐,且需要细致认真的工作。总的来说第三种方式最好的,前面两种方式应该用在其他方面,或许以后就能用的上。

猜你喜欢

转载自blog.csdn.net/lei_7103/article/details/78019647