游戏蛮牛学习群(纯技术交流,不闲聊):959392658
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

首页   >   博客   >   游戏小猪

小猪日记,初识Unity中的AssetBundle --->(三) 热度 1

2018-3-7 17:33
0 个评论 | 阅读 436 | 收藏 | 举报
书接上回。前边已经实现了一键打包。我之所以使用AB,是因为我们想要在不重新下载APP的情况下实现资源的更新或者代码的热更新。不管是什么,只要是想要更新的,都会以AB包的形式呈现,想要实现更新,当然需要一个远程的服务器了,但是我并没有,那只好本地模拟;但是自己动手去搭建一个http文件服务器,显然是很费事的,网上有现成的模拟器,那么我就直接拿来用了,这里也分享给大家     链接:https://pan.baidu.com/s/1J5suybtgBQhOgAL6MkG_QA 密码:ogzm
 

运行后就是这个界面了,把打好的文件夹拷贝到桌面一分,直接拖进来就可以了
 
这样就得到了一个HTTP的下载连接。这里贴一个简单的HTTP下载代码,我并没有用WWW去下载

[code]csharpcode:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Net;
using System;
 
public class DownLoaderEnum
{
    public string url;
    public string path;
    public Action action;
    public DownLoaderEnum(string URL, string PATH,Action ACTION)
    {
        url = URL;
        path = PATH;
        action = ACTION;
    }
}
 
 
public class HttpUtil  {
 
    static HttpUtil instance;
 
    public static HttpUtil Instance
    {
        get
        {
            if (instance == null)
                instance = new HttpUtil();
            return instance;
        }
    }
 
 
    public bool is_DownLoadOver = false;
 
    /// <summary>
    /// http下载文件
    /// </summary>
    /// <param name="url">下载文件地址</param>
    /// <param name="path">文件存放地址,包含文件名</param>
    /// <returns></returns>
    public  void HttpDownloader(object down)
    {
        is_DownLoadOver = false;
        if (!Directory.Exists((down as DownLoaderEnum).path))
            Directory.CreateDirectory((down as DownLoaderEnum).path);
        string tempPath = System.IO.Path.GetDirectoryName((down as DownLoaderEnum).path) + @"\temp";
        System.IO.Directory.CreateDirectory(tempPath);  //创建临时文件目录
        string tempFile = tempPath + @"\" + System.IO.Path.GetFileName((down as DownLoaderEnum).path) + ".temp"; //临时文件
 
        if (File.Exists(tempFile))
        {
            File.Delete(tempFile);    //存在则删除
        }
        try
        {
            FileStream fs = new FileStream(tempFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
            // 设置参数
            HttpWebRequest request = WebRequest.Create((down as DownLoaderEnum).url) as HttpWebRequest;
            //发送请求并获取相应回应数据
            HttpWebResponse response = request.GetResponse() as HttpWebResponse;
            //直到request.GetResponse()程序才开始向目标网页发送Post请求
            Stream responseStream = response.GetResponseStream();
            //创建本地文件写入流
            //Stream stream = new FileStream(tempFile, FileMode.Create);
            byte[] bArr = new byte[1024];
            int size = responseStream.Read(bArr, 0, bArr.Length);
            while (size > 0)
            {
                //stream.Write(bArr, 0, size);
                fs.Write(bArr, 0, size);
                size = responseStream.Read(bArr, 0, bArr.Length);
            }
            //stream.Close();
            fs.Close();
            responseStream.Close();
            string suffixName = (down as DownLoaderEnum).url;
            int su = suffixName.LastIndexOf('/');
            suffixName = (down as DownLoaderEnum).path + suffixName.Substring(su);
            // Debug.LogError(suffixName);
            if (File.Exists(suffixName))
                File.Delete(suffixName);
            System.IO.File.Move(tempFile, suffixName);
            // return true;
            Debug.LogError("下载完成");
            is_DownLoadOver = true;
        }
        catch (Exception ex)
        {
            Debug.LogError("错误==>>" + ex.Message);
            //return false;
        }
    }
}

?这样就能实现一个简单的下载了,然后把它放进一个单独的线程中去执行;
        想要实现更新,我们肯定是要进行一个版本对比的,只有版本有变动的AB包,我们才会去从服务器下载,而没有发生变动的,还从本地加载;如果你已经打过包了,你会发现,每个AB包同级目录中,还多了个文件 XXX.manifest  ,这个文件是为了方便我们肉眼能看到的manifest文件,用小绿本就可以打开,
 

打开后 AssetFileHash  ,就是这个AB包的hash值。如果资源没有发生过变动,重新打包,这个哈希值是不会变动的;我把cube放大了一点,再重新打包,发现;材质和贴图的哈希值没有变化,而cube的哈希值发生了变化;
       这个哈希值可以在manifest文件中得到  ,也可以用C#中自带的方法得到,或者得到他的MD5值。原理都是一样,如果资源没有发生变化,那么这个值不会变,如果发生变化,相应的值也会发生变化;每次游戏启动时,通过对比他们的值,我们确定是否需要去下载新的资源;
       可能有人会想,我直接拉取服务器上的manifest文件和本地的manifest文件,然后将他们两个的信息进行对比,然后在按照哈希值,去进行资源的更新,这样不是很省事么;但是这样是不行的,因为manifest只能唯一,并不能同时加载两个。所以只能是自己手动制作一个配置文件了;配置文件一般我妹做成XML形式或者json形式。但是个人非常讨厌XML,相反,json用起来有一种如丝般的顺滑;unity中为我们自带了json的序列化工具。再要序列化的类上,需要加上序列化标签,就可以实现嵌套和数组;
      

[code]csharpcode:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class JsonUtil
{
 
    static JsonUtil instance;
 
    public static JsonUtil Instance
    {
        get
        {
            if (instance == null)
                instance = new JsonUtil();
            return instance;
        }
    }
 
    public void Init()
    {
   
              
    }
    /// <summary>
    /// 将类转换成json字符串
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="t"></param>
    /// <returns></returns>
    public string ObjectToJson<T>(T t)
    {
        string json = JsonUtility.ToJson(t);
        return json;
    }
    /// <summary>
    /// json字符串,反序列化成对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="json"></param>
    /// <returns></returns>
    public T JsonToObject<T>(string json)
    {
        T t = JsonUtility.FromJson<T>(json);
        return t;
    }
}


[code]csharpcode:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
[System.Serializable]
public class AllBundleInfo  {
 
    public List<SingleBundleInfo> BundleInfoList;
 
}
 
 
[System.Serializable]
public class SingleBundleInfo
{
    public string bundleName;
    public string bundleMD5;
}


这样就可以实现嵌套;
   这里我先实现了一个manifest文件的md5值 的获取,因为其他的我们还可以在各自的manifest文件中看到他的哈希值,但是manifest文件却我发看到,至于获取MD5值什么的,网上也是有很多的,我这里直接从网上COPY过来了,这是COPY过来的原文

[code]csharpcode:

/// <summary>
  /// 提供用于计算指定文件哈希值的方法
  /// <example>例如计算文件的MD5值:
  /// <code>
  ///   String hashMd5=HashHelper.ComputeMD5("MyFile.txt");
  /// </code>
  /// </example>
  /// <example>例如计算文件的CRC32值:
  /// <code>
  ///   String hashCrc32 = HashHelper.ComputeCRC32("MyFile.txt");
  /// </code>
  /// </example>
  /// <example>例如计算文件的SHA1值:
  /// <code>
  ///   String hashSha1 =HashHelper.ComputeSHA1("MyFile.txt");
  /// </code>
  /// </example>
  /// </summary>
  public sealed class HashHelper
  {
      /// <summary>
      ///  计算指定文件的MD5值
      /// </summary>
      /// <param name="fileName">指定文件的完全限定名称</param>
      /// <returns>返回值的字符串形式</returns>
      public static String ComputeMD5(String fileName)
      {
          String hashMD5 = String.Empty;
          //检查文件是否存在,如果文件存在则进行计算,否则返回空值
          if (System.IO.File.Exists(fileName))
          {
              using (System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
              {
                  //计算文件的MD5值
                  System.Security.Cryptography.MD5 calculator=System.Security.Cryptography.MD5.Create();
                  Byte[] buffer = calculator.ComputeHash(fs);
                  calculator.Clear();
                  //将字节数组转换成十六进制的字符串形式
                  StringBuilder stringBuilder = new StringBuilder();
                  for (int i = 0; i < buffer.Length; i++)
                  {
                      stringBuilder.Append(buffer[i].ToString("x2"));
                  }
                 hashMD5= stringBuilder.ToString();
              }//关闭文件流
          }//结束计算
          return hashMD5;
      }//ComputeMD5
      /// <summary>
      ///  计算指定文件的CRC32值
      /// </summary>
      /// <param name="fileName">指定文件的完全限定名称</param>
      /// <returns>返回值的字符串形式</returns>
      public static String ComputeCRC32(String fileName)
      {
          String hashCRC32 = String.Empty;
          //检查文件是否存在,如果文件存在则进行计算,否则返回空值
          if (System.IO.File.Exists(fileName))
          {
              using (System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
              {
              //计算文件的CSC32值
              Crc32 calculator = new Crc32();
              Byte[] buffer = calculator.ComputeHash(fs);
              calculator.Clear();
              //将字节数组转换成十六进制的字符串形式
              StringBuilder stringBuilder = new StringBuilder();
              for (int i = 0; i < buffer.Length; i++)
              {
                  stringBuilder.Append(buffer[i].ToString("x2"));
              }
              hashCRC32 = stringBuilder.ToString();
              }//关闭文件流
          }
          return hashCRC32;
      }//ComputeCRC32
      /// <summary>
      ///  计算指定文件的SHA1值
      /// </summary>
      /// <param name="fileName">指定文件的完全限定名称</param>
      /// <returns>返回值的字符串形式</returns>
      public static String ComputeSHA1(String fileName)
      {
          String hashSHA1 = String.Empty;
          //检查文件是否存在,如果文件存在则进行计算,否则返回空值
          if (System.IO.File.Exists(fileName))
          {
              using (System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
              {
                  //计算文件的SHA1值
                  System.Security.Cryptography.SHA1 calculator = System.Security.Cryptography.SHA1.Create();
                  Byte[] buffer = calculator.ComputeHash(fs);
                  calculator.Clear();
                  //将字节数组转换成十六进制的字符串形式
                  StringBuilder stringBuilder = new StringBuilder();
                  for (int i = 0; i < buffer.Length; i++)
                  {
                      stringBuilder.Append(buffer[i].ToString("x2"));
                  }
                  hashSHA1 = stringBuilder.ToString();
              }//关闭文件流
          }
          return hashSHA1;
      }//ComputeSHA1
  }//end class: HashHelper
   /// <summary>
   /// 提供 CRC32 算法的实现
   /// </summary>
   public class Crc32 : System.Security.Cryptography.HashAlgorithm
   {
       public const UInt32 DefaultPolynomial = 0xedb88320;
       public const UInt32 DefaultSeed = 0xffffffff;
       private UInt32 hash;
       private UInt32 seed;
       private UInt32[] table;
       private static UInt32[] defaultTable;
       public Crc32()
       {
           table = InitializeTable(DefaultPolynomial);
           seed = DefaultSeed;
           Initialize();
       }
       public Crc32(UInt32 polynomial, UInt32 seed)
       {
           table = InitializeTable(polynomial);
           this.seed = seed;
           Initialize();
       }
       public override void Initialize()
       {
           hash = seed;
       }
       protected override void HashCore(byte[] buffer, int start, int length)
       {
           hash = CalculateHash(table, hash, buffer, start, length);
       }
       protected override byte[] HashFinal()
       {
           byte[] hashBuffer = UInt32ToBigEndianBytes(~hash);
           this.HashValue = hashBuffer;
           return hashBuffer;
       }
       public static UInt32 Compute(byte[] buffer)
       {
           return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
       }
       public static UInt32 Compute(UInt32 seed, byte[] buffer)
       {
           return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
       }
       public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
       {
           return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
       }
       private static UInt32[] InitializeTable(UInt32 polynomial)
       {
           if (polynomial == DefaultPolynomial && defaultTable != null)
           {
               return defaultTable;
           }
           UInt32[] createTable = new UInt32[256];
           for (int i = 0; i < 256; i++)
           {
               UInt32 entry = (UInt32)i;
               for (int j = 0; j < 8; j++)
               {
                   if ((entry & 1) == 1)
                       entry = (entry >> 1) ^ polynomial;
                   else
                       entry = entry >> 1;
               }
               createTable[i] = entry;
           }
           if (polynomial == DefaultPolynomial)
           {
               defaultTable = createTable;
           }
           return createTable;
       }
       private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
       {
           UInt32 crc = seed;
           for (int i = start; i < size; i++)
           {
               unchecked
               {
                   crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
               }
           }
           return crc;
       }
       private byte[] UInt32ToBigEndianBytes(UInt32 x)
       {
           return new byte[] { (byte)((x >> 24) & 0xff), (byte)((x >> 16) & 0xff), (byte)((x >> 8) & 0xff), (byte)(x & 0xff) };
       }
   }//end class: Crc32

[code]csharpcode:

/// <summary>
  /// 提供用于计算指定文件哈希值的方法
  /// <example>例如计算文件的MD5值:
  /// <code>
  ///   String hashMd5=HashHelper.ComputeMD5("MyFile.txt");
  /// </code>
  /// </example>
  /// <example>例如计算文件的CRC32值:
  /// <code>
  ///   String hashCrc32 = HashHelper.ComputeCRC32("MyFile.txt");
  /// </code>
  /// </example>
  /// <example>例如计算文件的SHA1值:
  /// <code>
  ///   String hashSha1 =HashHelper.ComputeSHA1("MyFile.txt");
  /// </code>
  /// </example>
  /// </summary>
  public sealed class HashHelper
  {
      /// <summary>
      ///  计算指定文件的MD5值
      /// </summary>
      /// <param name="fileName">指定文件的完全限定名称</param>
      /// <returns>返回值的字符串形式</returns>
      public static String ComputeMD5(String fileName)
      {
          String hashMD5 = String.Empty;
          //检查文件是否存在,如果文件存在则进行计算,否则返回空值
          if (System.IO.File.Exists(fileName))
          {
              using (System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
              {
                  //计算文件的MD5值
                  System.Security.Cryptography.MD5 calculator=System.Security.Cryptography.MD5.Create();
                  Byte[] buffer = calculator.ComputeHash(fs);
                  calculator.Clear();
                  //将字节数组转换成十六进制的字符串形式
                  StringBuilder stringBuilder = new StringBuilder();
                  for (int i = 0; i < buffer.Length; i++)
                  {
                      stringBuilder.Append(buffer[i].ToString("x2"));
                  }
                 hashMD5= stringBuilder.ToString();
              }//关闭文件流
          }//结束计算
          return hashMD5;
      }//ComputeMD5
      /// <summary>
      ///  计算指定文件的CRC32值
      /// </summary>
      /// <param name="fileName">指定文件的完全限定名称</param>
      /// <returns>返回值的字符串形式</returns>
      public static String ComputeCRC32(String fileName)
      {
          String hashCRC32 = String.Empty;
          //检查文件是否存在,如果文件存在则进行计算,否则返回空值
          if (System.IO.File.Exists(fileName))
          {
              using (System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
              {
              //计算文件的CSC32值
              Crc32 calculator = new Crc32();
              Byte[] buffer = calculator.ComputeHash(fs);
              calculator.Clear();
              //将字节数组转换成十六进制的字符串形式
              StringBuilder stringBuilder = new StringBuilder();
              for (int i = 0; i < buffer.Length; i++)
              {
                  stringBuilder.Append(buffer[i].ToString("x2"));
              }
              hashCRC32 = stringBuilder.ToString();
              }//关闭文件流
          }
          return hashCRC32;
      }//ComputeCRC32
      /// <summary>
      ///  计算指定文件的SHA1值
      /// </summary>
      /// <param name="fileName">指定文件的完全限定名称</param>
      /// <returns>返回值的字符串形式</returns>
      public static String ComputeSHA1(String fileName)
      {
          String hashSHA1 = String.Empty;
          //检查文件是否存在,如果文件存在则进行计算,否则返回空值
          if (System.IO.File.Exists(fileName))
          {
              using (System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
              {
                  //计算文件的SHA1值
                  System.Security.Cryptography.SHA1 calculator = System.Security.Cryptography.SHA1.Create();
                  Byte[] buffer = calculator.ComputeHash(fs);
                  calculator.Clear();
                  //将字节数组转换成十六进制的字符串形式
                  StringBuilder stringBuilder = new StringBuilder();
                  for (int i = 0; i < buffer.Length; i++)
                  {
                      stringBuilder.Append(buffer[i].ToString("x2"));
                  }
                  hashSHA1 = stringBuilder.ToString();
              }//关闭文件流
          }
          return hashSHA1;
      }//ComputeSHA1
  }//end class: HashHelper
   /// <summary>
   /// 提供 CRC32 算法的实现
   /// </summary>
   public class Crc32 : System.Security.Cryptography.HashAlgorithm
   {
       public const UInt32 DefaultPolynomial = 0xedb88320;
       public const UInt32 DefaultSeed = 0xffffffff;
       private UInt32 hash;
       private UInt32 seed;
       private UInt32[] table;
       private static UInt32[] defaultTable;
       public Crc32()
       {
           table = InitializeTable(DefaultPolynomial);
           seed = DefaultSeed;
           Initialize();
       }
       public Crc32(UInt32 polynomial, UInt32 seed)
       {
           table = InitializeTable(polynomial);
           this.seed = seed;
           Initialize();
       }
       public override void Initialize()
       {
           hash = seed;
       }
       protected override void HashCore(byte[] buffer, int start, int length)
       {
           hash = CalculateHash(table, hash, buffer, start, length);
       }
       protected override byte[] HashFinal()
       {
           byte[] hashBuffer = UInt32ToBigEndianBytes(~hash);
           this.HashValue = hashBuffer;
           return hashBuffer;
       }
       public static UInt32 Compute(byte[] buffer)
       {
           return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
       }
       public static UInt32 Compute(UInt32 seed, byte[] buffer)
       {
           return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
       }
       public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
       {
           return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
       }
       private static UInt32[] InitializeTable(UInt32 polynomial)
       {
           if (polynomial == DefaultPolynomial && defaultTable != null)
           {
               return defaultTable;
           }
           UInt32[] createTable = new UInt32[256];
           for (int i = 0; i < 256; i++)
           {
               UInt32 entry = (UInt32)i;
               for (int j = 0; j < 8; j++)
               {
                   if ((entry & 1) == 1)
                       entry = (entry >> 1) ^ polynomial;
                   else
                       entry = entry >> 1;
               }
               createTable[i] = entry;
           }
           if (polynomial == DefaultPolynomial)
           {
               defaultTable = createTable;
           }
           return createTable;
       }
       private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
       {
           UInt32 crc = seed;
           for (int i = start; i < size; i++)
           {
               unchecked
               {
                   crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
               }
           }
           return crc;
       }
       private byte[] UInt32ToBigEndianBytes(UInt32 x)
       {
           return new byte[] { (byte)((x >> 24) & 0xff), (byte)((x >> 16) & 0xff), (byte)((x >> 8) & 0xff), (byte)(x & 0xff) };
       }
   }//end class: Crc32



1 0

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册帐号

阅读排行

评论排行

推荐博客

最新博客

返回顶部