Unity边玩边下限制下载速度技术实现

这篇具有很好参考价值的文章主要介绍了Unity边玩边下限制下载速度技术实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Unity提供了DownloadHandlerFile类来进行文件的下载,如果是那种网络比较好的宽带每秒下载速度可以达到20M以上,这样导致IO容易卡住。如果是进游戏前那种提前下载肯定没问题,但是边玩边下这种如果不限制下载速度那么游戏就不会那么流畅了。

Unity提供了DownloadHandlerScript类,开始我以为只要用FileStream自己来写一个比较小长度的Buffer就可以解决问题。如下代码所示,实际测试了一下ReveiveData会在一帧内回调多次导致write操作卡住IO,所以此思路只能作罢。

public class CustomDownloadHandler : DownloadHandlerScript
{
    FileStream fileStream;
    private int m_receiveLength = 0;
 
    ulong m_ContentLength;
    public CustomDownloadHandler(byte[] preallocatedBuffer) : base(preallocatedBuffer)
    {
        int size = preallocatedBuffer.Length;
        fileStream = new FileStream(Application.persistentDataPath + "/1.bundle", FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, size);
    }
    protected override bool ReceiveData(byte[] data, int dataLength)
    {
      
        Debug.Log(Time.frameCount + " " + dataLength); //1帧内需要写入大量数据导致IO卡住
        m_receiveLength += dataLength;
        fileStream.Write(data, 0, dataLength);
        return true;
    }
    //....略
}

既然Unity的API实现不了只能使用C#的API了。我们先达成一个共识,边玩边下同一时刻只能下载一个文件(游戏不卡顿优先,其次才是下载),所以缓冲Buffer可以分配一个静态的。假设最大的下载速度是1M/S 每秒30帧那么每帧Buffer的长度1024/30*1024。

每帧处理的Buffer字节数组已经确定,接着就是要开线程下载了。使用await Task.Run来开线程,它的好处是可以等子线程的下载任务结束在回到主线程,这样就可以把下载完成的事件抛出让逻辑层处理。下载过程中还需要考虑强制断开的问题,可以使用CancellationToken即可。

下载连接建立好以后就开始下载,启动一个while循环,为了避免IO的卡住,这里需要让线程sleep下来。最后就是上完整的代码了。

using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
 
 
public class DownloadHandler 
{
    public struct Result
    {
        public string error;
        public bool isHttpError => !string.IsNullOrEmpty(error);
    }
   
    static int DEFAULT_SLEEP_TIME = 33;
    static int DEFAULT_DOWNLOAD_SPEED = 1024;
    static byte[] DEFAULT_BUFFER = new byte[DEFAULT_DOWNLOAD_SPEED / 30 * 1024];
    static int DEFAULT_DOWNLOAD_TIMEOUT = 5;
 
 
    public event Action<Result> completed;
    public float Progress;
    public ulong DownloadedBytes;
    public bool IsDone;
 
    private string m_File;
    private string m_Url;
    private int m_SleepTime;
    private Result m_Result;
    private Stream m_Stream;
    private FileStream m_FileStream;
    private HttpWebRequest m_Request;
    private HttpWebResponse m_Response;
    private CancellationTokenSource m_Cts;
 
    /// <summary>
    /// 创建下载对象
    /// </summary>
    /// <param name="url">下载路径</param>
    /// <param name="file">保存路径</param>
    /// <param name="speed">每秒最大小速度,KB单位</param>
    public DownloadHandler(string url,string file,int speed)
    {
        m_File = file;
        m_Url = url;
        m_SleepTime = (int)(DEFAULT_SLEEP_TIME * Mathf.Max(1, (float)DEFAULT_DOWNLOAD_SPEED / speed));
    }
 
    //开始下载
    public void StartDownload()
    {
        Download();
    }
    //停止正在下载中的文件
    public void Dispose()
    {
        m_Cts?.Cancel();
        Close();
    }
 
 
    async void Download()
    {
        m_Cts = new CancellationTokenSource();
        CancellationToken token = m_Cts.Token;
        m_Result = default(Result);
        DownloadedBytes = 0;
        IsDone = false;
        await Task.Run(() =>
        {
            try
            {
                m_Request = (HttpWebRequest)WebRequest.Create(m_Url);
                m_Response = (HttpWebResponse)m_Request.GetResponse();
                long content = m_Response.ContentLength;
                m_Stream = m_Response.GetResponseStream();
                m_Stream.ReadTimeout = DEFAULT_DOWNLOAD_TIMEOUT*1000;
                m_FileStream = new FileStream(m_File, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, DEFAULT_BUFFER.Length);
                int read = 0;
                while (!token.IsCancellationRequested &&
                    (read = m_Stream.Read(DEFAULT_BUFFER, 0, DEFAULT_BUFFER.Length)) > 0)
                {
                    DownloadedBytes += (ulong)read;
                    m_FileStream.Write(DEFAULT_BUFFER, 0, read);
                    Thread.Sleep(m_SleepTime);
                }
            }
            catch (WebException ex)
            {
                m_Result.error = ex.ToString();
            }
            finally
            {
                Close();
            }
 
        }, token);
 
        try
        {
            if (!token.IsCancellationRequested)
            {
                IsDone = true;
                completed?.Invoke(m_Result);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.ToString());
        }
        
    }
 
    void Close()
    {
        m_FileStream?.Dispose();
        m_Stream?.Dispose();
        m_Response?.Dispose();
        m_Cts?.Dispose();
        m_Cts = null;
        m_FileStream = null;
        m_Stream = null;
        m_Response = null;
        m_Request = null;
    }
 
}

启动下载调用的代码,这里可以监听下载完成的事件以及错误信息。

if (GUILayout.Button("<size=200>下载 </size>"))
        {
            string url = "https://xxxxxxx.bundle";
            string file = Application.persistentDataPath + "/1.bundle";
            float t = Time.time;
            downloadHandler = new DownloadHandler(url, file, 1024);//1024表示每秒下载1M,还可以传512或者256让下载速度继续往下降
            downloadHandler.StartDownload();
            downloadHandler.completed += (info) =>
            {
                if (info.isHttpError)
                {
                    Debug.LogError(info.error);
                }
                else
                {
                    finishTime = Time.time - t;
 
                    Debug.LogError("fininsh " + finishTime);
                }
 
            };
        }

下载过程中取消下载

if (GUILayout.Button("<size=200>取消下载 </size>"))
        {
            downloadHandler?.Dispose();
        }

注意如果是下载file://开头的本地文件, 需要在代码中将HttpWebRequest和HttpWebResponse换成FileWebRequest和FileWebResponse其他地方都完全一样。

m_Request = (HttpWebRequest)WebRequest.Create(m_Url);
m_Response = (HttpWebResponse)m_Request.GetResponse();
 

最后在总结一下资源下载。目前根据我们的经验会将下载分成两部分,一部分是启动下载,另一部分是边玩边下。

先说启动下载,它需要尽可能的快,一般这种下载展示就是一个普通的下载进度条,它并不要求高帧率,需要尽最快速度下载完毕。针对这种下载类型可以直接使用unity的DownloadHandlerFile,但是在面对小文件(几K几十K大小)的时候下载速度是非常慢的,因为针对每个文件需要单独建立http的链接,这些都需要额外开销。反而如果是大文件(百M以上大小),每秒下载好几十M都是可以的。

在针对下载小文件慢的问题上其实是可以增加同时下载的数量的,比如同时下载的资源大小不超过一个阀值就继续开下载队列,目前我项目最大开了30个下载队列,动态根据当前下载文件的小灵活变更数量,尽可能保证下载速度足够快。

其次就是边玩边下了,它和启动下载有个本质区别,边玩边下是不能影响用户游戏体验的,如果用户觉得游戏卡住很可能一开始就流失了。也就是说宁可下载的慢也不能下载太快影响操作体验,所以就有了这篇文章的限速。

另外Unity提供的几个下载的类都在这类,核心都是在C++中完成的。

unity downloadhandlerfile,unity,unity,游戏引擎

 文章来源地址https://www.toymoban.com/news/detail-797815.html

到了这里,关于Unity边玩边下限制下载速度技术实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 路由器限制下载速度、路由器禁止迅雷下载、路由器下载速度设置的方法

    在公司局域网中,员工上班时间经常利用公司的网络下载一些文件,通常情况下网管员也不会禁止,毕竟工作中或多或少会需要从网上下载文件。但是,员工为了提高下载速度而经常采用一些P2P下载软件(如BT、电驴、迅雷等),这些P2P软件由于边下载、边上传,因此可以大

    2024年02月08日
    浏览(32)
  • 如何设置无线路由器限速以限制看电影下载东西影响速度

    随着无线路由器组建的Wifi无线网络变得普及流行,不少企业以及家庭都安装有无线路由器,以方便一大家群体的智能手机、笔记本、平板电脑、台式电脑等设备满足上网需求,不过很多时候大家会觉得上网很慢,主要是因为当有一个或者多个用户在看电影或者下载东西的时候

    2024年02月07日
    浏览(37)
  • tplink路由器限制p2p软件下载速度的方法

    P2P软件在下载(download)的同时,也在为其他用户提供上传(upload),所以不会随着用户数的增加而降低下载速度。 TP-LINK宽带路由器封端口限速设置与P2P P2P全称peer-to-peer,可定义为:以非集中方式使用分布式资源来完成关键任务的一类系统和应用。定义中有两点解释一下:

    2024年02月06日
    浏览(38)
  • 双路由器实现宽带叠加提高pc端下载速度

    无需双wan口路由器,无需安装虚拟机,完全依靠路由器实现两个宽带的叠加,提高pc端下载速度。 三个普通路由器,实现三网叠加。 工具 d-link dir629无线路由器(A路由器) tp-link 402m宽带路由器(B路由器) 广电4M宽带,电信端口,网线进户 联通10M宽带,光纤进户,联通提供的

    2024年02月06日
    浏览(28)
  • python实现超级马里奥,回忆童年+边学边玩

    完整代码和素材戳我 主代码 main setup tools 运行成果 好了,被忘了在GitHub里面点star喔。 关于Python技术储备 学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后给大家分享一份全套的 Python 学习资料,希望提供给想学习 Python 的小伙伴们一

    2024年02月04日
    浏览(39)
  • 永久免费内网穿透不限制速度

    市面上的免费内网穿透大都有格式各样的限制,什么限制流量啊,每个月要签到打卡啊,还有更改域名地址等,只有神卓互联内网穿透是永久免费没有限制的,白嫖也可以。 这篇文章分享了3个方案,按照性能和综合指标排序,依次是 目录 方案一:使用IBCS虚拟专线 方案二:

    2023年04月09日
    浏览(28)
  • cisco路由器限制速度的方法

    1、设置ACL access-list 102 permit ip host 192.168.10.2 any access-list 102 permit ip any host 192.168.10.2 access-list 103 permit ip host 192.168.10.3 any access-list 103 permit ip any host 192.168.10.3 2、定义类 class-map match-all 102 match access-group 102 class-map match-all 103 match access-group 103 3、将类加入策略中 policy-map xiansu cl

    2024年02月05日
    浏览(28)
  • 路由器限制网络速度怎么设置?路由器限制网速设置方法(图文详解)

    随着网络带宽的提高,我们路由器承载的用户数越来越多,虽然表面上带宽是提升上去了,但实际上随着用户数的增加,我们的带宽明显上是降低了。今天,晓月就和大家分享一下怎么通过路由器限制网络速度,达到均衡上网。   方法/步骤 首先,我们在浏览器的链接栏输入

    2024年02月07日
    浏览(37)
  • 【100个 Unity实用技能】 | InputField输入框组件实现输入限制,只能输入中文或特殊字符等

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2023年04月27日
    浏览(32)
  • ChatGPT突然上线APP!iPhone可用、速度更快,GPT-4用量限制疑似取消

    新建了一个网站 ChatGPT人工智能中文站 - ChatGPT人工智能中文站http://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT免费镜像站 OpenAIChatGPT正式推出iOS应用程序的官方公告突然发布。 立即在苹果商店的免费列表中排名第二,在效率列表中排名第一。 (第一是拼多多海外版)

    2024年02月06日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包