从统一android的内部存储无缝加载图像?

Posted

技术标签:

【中文标题】从统一android的内部存储无缝加载图像?【英文标题】:Load images seamlessly from internal storage in unity android? 【发布时间】:2022-01-01 04:41:35 【问题描述】:

我一直在使用 Unity 游戏引擎为 android 设备开发一款益智游戏,但我一直面临从内部存储加载图像的问题。游戏有400-500个关卡。在运行时根据需要下载,并存储在内部存储中。加载这些图像时会出现问题。从内部存储加载图像非常昂贵。下面是负责下载或加载图片的函数,

public IEnumerator DownloadOrLoadImage(string link, string dirName, int index = -1)

    if (!loadFromResources)
    
        //Extract Link
        int startPos = link.LastIndexOf("https://drive.google.com/file/d/") + "https://drive.google.com/file/d/".Length;
        int length = link.IndexOf("/view?usp=sharing") - startPos;
        if (length >= 1)
        
            string ID = link.Substring(startPos, length);

            link = "https://drive.google.com/uc?export=download&id=" + ID;

            if (File.Exists(Application.persistentDataPath + "/.cache/" + ID))
            
                if (index != -1 && index == LevelMenuLoader.instance.totalJsonCount)
                
                    LevelMenuLoader.instance.AfterMenuDownloadSequence();
                
                print("Loading from the device");

                UnityWebRequest request = UnityWebRequestTexture.GetTexture("file://" + Application.persistentDataPath + "/.cache/" + ID);
                yield return request.SendWebRequest();

                if (request.result != UnityWebRequest.Result.Success)
                
                    Debug.Log(request.error);
                
                else
                
                    byte[] bytes = ((DownloadHandlerTexture)request.downloadHandler).data;

                    if (bytes.Length > 1)
                        this.GetComponent<RawImage>().texture = ((DownloadHandlerTexture)request.downloadHandler).texture;
                

                request.downloadHandler.Dispose();
                request.Dispose();
                isImageDownloaded = true;
            
            else
            
                LevelMenuLoader.instance.AfterMenuDownloadSequence();
                print("Downloading from the web");


                using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(link))
                
                    yield return request.SendWebRequest();

                    if (request.isNetworkError || request.isHttpError)
                        Debug.Log(request.error);
                    else
                    
                        this.GetComponent<RawImage>().texture = ((DownloadHandlerTexture)request.downloadHandler).texture;

                        byte[] bytes = ((DownloadHandlerTexture)request.downloadHandler).data;
                        if (bytes.Length > 1)
                        
                            File.WriteAllBytes(Application.persistentDataPath + "/.cache/" + ID, bytes);
                            isImageDownloaded = true;
                        

                        request.downloadHandler.Dispose();
                        request.Dispose();
                    
                
            
        
    
    else
    
        //      Debug.Log("Loaded from resources");
        this.GetComponent<RawImage>().texture = Resources.Load<Texture>("Levels/" + dirName + "/" + this.GetComponent<LevelInformation>().m_LevelID);
    

很抱歉它这么长,但它只是在检查文件是否存在,从存储中加载图像或下载它。我发现这是迄今为止最有效且延迟最少的方法。加载 300 多张图片时,非常令人头疼,加载图像需要 3-4 秒,并且在某些设备中,由于某些设备的内存使用限制,它根本不会加载。如果有人可以帮助或指导我以一种非常无缝且加载图像更快的方式优化代码,那将非常有帮助。

PS:我已经为滚动视图尝试了对象池,所以我只加载需要的图像并在它们在屏幕上不可见时处理它们。但这是不切实际的,因为加载图像会产生一点延迟,而且速度不如需要的快。任何建议都会很棒。

如果我违反了任何准则或我的格式不好,我很抱歉。请告诉我,以便我更正。

谢谢。

【问题讨论】:

您必须牺牲加载时间或内存。否则,您是否尝试为查看大量图像的屏幕创建较小的缩略图(当下载到设备或从服务器时,或者动态创建,尽管我怀疑这不可行,因为您已经有加载时间问题)? 嗨,@LukeVo 感谢您的回复。我不介意加载时间,但在内存中加载 400 多张图像会产生很多问题(内存不是很多),但有些设备会出现内存使用量突然激增的限制,这会阻止游戏加载图像。它在三星设备和 ios 中受到高度关注。因此,游戏不会超过加载。缩略图在 512x512 中非常小,每个大约 100kb。这是我猜想的最小的缩略图。这些图像加载问题仅适用于滚动视图中需要这 400 多张图像的菜单。 我以前是开发游戏的,现在不知道是不是不一样了,但是100kB的图片文件在内存中的占用空间不一样。在内存中它总是位图(即每个像素 3 或 4 个字节)。 512x512 相当大!我记得当时(10 年前)我们只能加载大约 2048x2048 的纹理并将所有内容压缩到该限制内。 我明白了。有什么解决方法吗?因为我想低于 512x512 会导致质量很差。我们对图像进行了最大程度的优化,以保持较小尺寸的质量。 为什么需要加载全部 300 张图片?用户无法一次查看所有内容。假设我假设您有一个菜单屏幕,因此用户可以选择一个级别,以及一个实际上需要大好图像才能玩的屏幕。菜单屏幕,例如,您加载 30 个 50x50 的缩略图,这将非常快并且不会占用大量内存。然后到播放屏幕,你实际上加载了 512x512 的文件。 【参考方案1】:

或许可以解决

在开始时预加载它们,然后向它们展示播放器 该方法主要用于战地等计算类游戏。例如,粒子和武器在开始时加载,当你需要它们时,它们不会随你生成,而是传送给你以获得更好的性能。在您的情况下,预先加载所有图像应该是一个很好的解决方案,这样它就会顺利运行(只要您没有内置不合理的调试日志或其他不必要的计算过程)

【讨论】:

嗨@somewhat32,感谢您的解决方案。这就是我现在所做的,以避免以后加载。而且,它在统一编辑器上对我来说就像一个魅力。但是一些移动设备有某种限制。它们可以防止应用程序和游戏立即使用 ram。特别是在三星和IOS。这是一种奇怪的行为,在某些设备上,游戏就像一个魅力,但加载需要很长时间,在某些设备上,它只是无法通过加载屏幕。 google play 上有一个游戏叫艺术拼图,和我的机制一样,但是做的很完美。 啊,我看到你在制作一个艺术拼图游戏,我还以为你在谷歌驱动器上制作了自己的画廊。无论如何,我认为他们将图像放入游戏文件中,就像他们一次发布所有图像一样。如果您降低分辨率,您可以在大约 900 mb 的图像使用量下获得大约 300 个级别,但如果有机会,您可以回收图像。但即使是千兆字节也不足以成为一款出色的游戏。如果您找不到任何解决 ram 问题的方法,我建议您发布游戏并实现所有这些图像,不要使用外部来源。 一个优点是播放器不必连接到互联网。 嗨,是的,我们有几乎相同的概念,唯一的不同是我们的是拼图游戏。他们没有将所有图像都放入游戏文件中。他们的下载大小非常简洁,大约 50 兆字节左右。他们确实会实时下载它们,因为他们自己有大约 500 多个级别(即大约 1500 多个图像)。我们使用实时下载的麻烦的原因是为了节省来自 Play 商店的玩家的下载大小,他们不必等待下载完成。 我们对游戏进行了编码,让玩家无需等待任何内容完成。我们在游戏中有 10 个普通关卡,在他们完成这 10 个关卡之前,应该按顺序排列其他资产。他们可以立即开始游戏,游戏将在后台管理所有下载。游戏也可以离线玩。

以上是关于从统一android的内部存储无缝加载图像?的主要内容,如果未能解决你的问题,请参考以下文章

Android:如何将位图写入内部存储

Glide 无法从内部存储加载文件

在 ImageView 中显示图像并将其保存到 Android 上的内部存储中

Android从内部存储读取文件非常慢

显示来自 SD 卡或内部存储的图像以查看寻呼机 android

如何将张量流图像分类数据存储在Android应用程序的内部存储中?