summaryrefslogtreecommitdiff
path: root/Assets/Scripts
diff options
context:
space:
mode:
Diffstat (limited to 'Assets/Scripts')
-rw-r--r--Assets/Scripts/Editor.meta8
-rw-r--r--Assets/Scripts/Editor/BuildCustomPipeline.cs16
-rw-r--r--Assets/Scripts/Editor/BuildCustomPipeline.cs.meta11
-rw-r--r--Assets/Scripts/Editor/BundleHelper.cs82
-rw-r--r--Assets/Scripts/Editor/BundleHelper.cs.meta11
-rw-r--r--Assets/Scripts/FileUtil.cs57
-rw-r--r--Assets/Scripts/FileUtil.cs.meta11
-rw-r--r--Assets/Scripts/PathHelper.cs41
-rw-r--r--Assets/Scripts/PathHelper.cs.meta11
-rw-r--r--Assets/Scripts/ResManager.cs273
-rw-r--r--Assets/Scripts/ResManager.cs.meta11
-rw-r--r--Assets/Scripts/Test.cs56
-rw-r--r--Assets/Scripts/Test.cs.meta11
13 files changed, 599 insertions, 0 deletions
diff --git a/Assets/Scripts/Editor.meta b/Assets/Scripts/Editor.meta
new file mode 100644
index 0000000..b1cf477
--- /dev/null
+++ b/Assets/Scripts/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 22e71af268e34ac4f86477c2ec704ddf
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Editor/BuildCustomPipeline.cs b/Assets/Scripts/Editor/BuildCustomPipeline.cs
new file mode 100644
index 0000000..3c91409
--- /dev/null
+++ b/Assets/Scripts/Editor/BuildCustomPipeline.cs
@@ -0,0 +1,16 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.Build;
+using UnityEditor.Build.Reporting;
+
+public class BuildCustomPipeline : IPreprocessBuildWithReport
+{
+ public int callbackOrder { get { return 0; } }
+ public void OnPreprocessBuild(BuildReport report)
+ {
+ // BundleHelper.BuildBundles();
+ }
+
+}
diff --git a/Assets/Scripts/Editor/BuildCustomPipeline.cs.meta b/Assets/Scripts/Editor/BuildCustomPipeline.cs.meta
new file mode 100644
index 0000000..5230b90
--- /dev/null
+++ b/Assets/Scripts/Editor/BuildCustomPipeline.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 900a82c1f64da4b488ea055108308070
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Editor/BundleHelper.cs b/Assets/Scripts/Editor/BundleHelper.cs
new file mode 100644
index 0000000..631d542
--- /dev/null
+++ b/Assets/Scripts/Editor/BundleHelper.cs
@@ -0,0 +1,82 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Linq;
+using System.IO;
+using UnityEngine;
+using UnityEditor;
+
+public class BundleHelper
+{
+
+ static string TempPath = Application.temporaryCachePath + "/PackData";
+ static string TargetPath = Application.dataPath + "/StreamingAssets/PackData"; // or Application.streamingAssetsPath
+ static string MainBundleName = "PackData";
+ static string MainBundle = TargetPath + "/PackData";
+ static string ManifestFile = Application.dataPath + "/Resources/bundles.manifest";
+
+ [MenuItem("Build/Build bundles")]
+ public static void BuildBundles()
+ {
+ if(!Directory.Exists(TempPath))
+ Directory.CreateDirectory(TempPath);
+ BuildAssetBundleOptions options = BuildAssetBundleOptions.None;
+ options |= BuildAssetBundleOptions.ChunkBasedCompression;
+ options |= BuildAssetBundleOptions.DeterministicAssetBundle;
+ options |= BuildAssetBundleOptions.ForceRebuildAssetBundle;
+ options |= BuildAssetBundleOptions.StrictMode;
+#if UNITY_IOS
+ BuildPipeline.BuildAssetBundles(TempPath, options, BuildTarget.iOS);
+#elif UNITY_ANDROID
+ BuildPipeline.BuildAssetBundles(TempPath, options, BuildTarget.Android);
+#else
+ BuildPipeline.BuildAssetBundles(TempPath, options, BuildTarget.StandaloneWindows);
+#endif
+ Debug.Log("Bundle生成结束");
+ // Move to StreamingAssets
+ if (!Directory.Exists(TargetPath))
+ Directory.CreateDirectory(TargetPath);
+ string[] files = Directory.GetFiles(TempPath, "*.*");
+ string dirPath = null;
+ foreach(string file in files)
+ {
+ if (!file.EndsWith(".manifest") && !file.EndsWith(".meta"))
+ {
+ dirPath = file.Replace('\\', '/');
+ dirPath = dirPath.Replace(TempPath, TargetPath);
+ if (File.Exists(dirPath))
+ File.Delete(dirPath);
+ File.Move(file, dirPath);
+ }
+ }
+ // 将bundle的md5写入bundles.manifest文件
+ AssetBundle mainBundle = AssetBundle.LoadFromFile(MainBundle);
+ if (mainBundle != null)
+ {
+ AssetBundleManifest manifest = mainBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
+ if (manifest != null)
+ {
+ if (File.Exists(ManifestFile))
+ File.Delete(ManifestFile);
+ using (StreamWriter file = new System.IO.StreamWriter(ManifestFile, true))
+ {
+ string[] bundles = manifest.GetAllAssetBundles();
+ List<string> _bundles = bundles.ToList<string>();
+ _bundles.Add(MainBundleName);
+ foreach (string bundle in _bundles)
+ {
+ string path = TargetPath + "/" + bundle ;
+ if (!File.Exists(path))
+ continue;
+ long size = new FileInfo(path).Length;
+ byte[] data = File.ReadAllBytes(path);
+ string hash = FileUtil.GetMD5Hash(data);
+ file.WriteLine(bundle + "," + size + "," + hash);
+ }
+ };
+ }
+ mainBundle.Unload(true);
+ }
+ }
+
+}
diff --git a/Assets/Scripts/Editor/BundleHelper.cs.meta b/Assets/Scripts/Editor/BundleHelper.cs.meta
new file mode 100644
index 0000000..673f75d
--- /dev/null
+++ b/Assets/Scripts/Editor/BundleHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9eb05f681b7bd80448b2f488711dad41
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/FileUtil.cs b/Assets/Scripts/FileUtil.cs
new file mode 100644
index 0000000..b0e9b8c
--- /dev/null
+++ b/Assets/Scripts/FileUtil.cs
@@ -0,0 +1,57 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.IO;
+using System;
+using UnityEngine;
+
+public class FileUtil {
+
+ public static string GetMD5Hash(byte[] data)
+ {
+ string strHash = "";
+ using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
+ {
+ byte[] bytesHash = md5.ComputeHash(data);
+ strHash = BitConverter.ToString(bytesHash);
+ strHash = strHash.Replace("-", "");
+ }
+ return strHash.ToLower();
+ }
+
+ public static string GetMD5Hash(string pathName)
+ {
+ if (!File.Exists(pathName))
+ {
+ Debug.LogError("GetMD5Hash Error, file not exist: " + pathName);
+ return "";
+ }
+ string strResult = "";
+ string strHash = "";
+
+ byte[] bytesHash;
+
+ FileStream fs = null;
+ using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
+ {
+ try
+ {
+ fs = new FileStream(pathName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+
+ bytesHash = md5.ComputeHash(fs);
+ fs.Close();
+ strHash = BitConverter.ToString(bytesHash);
+ strHash = strHash.Replace("-", "");
+
+ strResult = strHash;
+ }
+ catch (System.Exception ex)
+ {
+ Debug.LogError("read md5 file error :" + pathName + " e: " + ex.ToString());
+ }
+ }
+
+ return strResult.ToLower();
+ }
+
+}
diff --git a/Assets/Scripts/FileUtil.cs.meta b/Assets/Scripts/FileUtil.cs.meta
new file mode 100644
index 0000000..aeb1552
--- /dev/null
+++ b/Assets/Scripts/FileUtil.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0481cdb792bfaac469dd9cd32f24af3a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/PathHelper.cs b/Assets/Scripts/PathHelper.cs
new file mode 100644
index 0000000..7c23dd6
--- /dev/null
+++ b/Assets/Scripts/PathHelper.cs
@@ -0,0 +1,41 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class PathHelper{
+ // 用于bundle读取的streaming路径
+ // 不需要加file://协议
+ public static string GetStreamingFullFilePath(string filePath)
+ {
+#if UNITY_ANDROID
+ return Application.dataPath + "!assets/" + filePath;
+#else
+ return Application.streamingAssetsPath + "/" + filePath;
+#endif
+ }
+
+ // 用于www读取的streaming路径
+ // 需要加file协议
+ public static string GetStreamingWWWFullFilePath(string filePath)
+ {
+#if UNITY_ANDROID
+ return Application.streamingAssetsPath + "/" + filePath;
+#else
+ return "file://" + Application.streamingAssetsPath + "/" + filePath;
+#endif
+ }
+
+ public static string GetPersistentFullFilePath(string filePath)
+ {
+ return Application.persistentDataPath + "/" + filePath;
+ }
+
+#if UNITY_IOS
+// 将文件从自动上传iCloud中排除
+public static void ExcludeFileFromiCloud(string filePath)
+{
+ string full = GetPersistentFullFilePath(filePath);
+ UnityEngine.iOS.Device.SetNoBackupFlag(full);
+}
+#endif
+}
diff --git a/Assets/Scripts/PathHelper.cs.meta b/Assets/Scripts/PathHelper.cs.meta
new file mode 100644
index 0000000..f13fefb
--- /dev/null
+++ b/Assets/Scripts/PathHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f866276720b96643a5cec178754bb40
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/ResManager.cs b/Assets/Scripts/ResManager.cs
new file mode 100644
index 0000000..ba47f8e
--- /dev/null
+++ b/Assets/Scripts/ResManager.cs
@@ -0,0 +1,273 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.IO;
+using UnityEngine;
+using UnityEngine.UI;
+using MEC;
+
+public class ResManager {
+
+ private struct BundleInfo{
+ public long size;
+ public string hash;
+ }
+
+ public static ResManager Instance
+ {
+ get
+ {
+ if (_instance == null)
+ _instance = new ResManager();
+ return _instance;
+ }
+ }
+ static ResManager _instance;
+
+ Test test;
+ string Content;
+
+ static string BundleRoot = "PackData";
+ static string ManifestBundleName = "PackData";
+ static string ManifestFileResName = "bundles"; // under Resources/bundles.manifest
+ static string ManifesteRemotePath = "bundles.manifest";
+#if UNITY_STANDALONE_WIN
+ static string cdnAddress = "http://localhost/bundles/"; // PC
+#elif UNITY_ANDROID
+ static string cdnAddress = "http://10.0.2.2/bundles/"; // AndroidEmu
+#endif
+
+ Dictionary<string, AssetBundle> CachedBundles = new Dictionary<string, AssetBundle>();
+ Dictionary<string, string> FileMapping = new Dictionary<string, string>();
+ AssetBundleManifest Manifest;
+
+#region 更新相关
+ // 本地streamingAssetsPath下的bundles数据,从固化的bundles.manifest中读取
+ Dictionary<string, BundleInfo> localBundles = new Dictionary<string, BundleInfo>();
+ // CDN上的bundles数据
+ Dictionary<string, BundleInfo> remoteBundles = new Dictionary<string, BundleInfo>();
+#endregion
+
+ public void Init(Test test)
+ {
+ this.test = test;
+ LoadLocalBundlesInfo();
+ Timing.Instance.RunCoroutineOnInstance(DownloadManifest());
+ }
+
+ void Print(string content , bool alert = false)
+ {
+ test.Print(content, alert);
+ }
+
+ public string ResContent(string path)
+ {
+ TextAsset text = Resources.Load(path) as TextAsset;
+ if (text == null)
+ return null;
+ return text.text;
+ }
+
+ public void ReadFileMapping()
+ {
+ AssetBundle bundle = LoadBundle("bundle00.ab"); // 不可以省略后缀
+ if (bundle == null)
+ {
+ string path = PathHelper.GetStreamingFullFilePath(BundleRoot + "/bundle00.ab");
+ Print("Bundle 为空, path = " + path, true);
+ return;
+ }
+ TextAsset text = bundle.LoadAsset<TextAsset>("fileMapping");
+ if (text == null)
+ return;
+ string content = text.text;
+ content = content.Replace("\r", string.Empty);
+ string[] lines = content.Split('\n');
+ Print("Load FileMapping:");
+ foreach (string line in lines)
+ {
+ string[] part = line.Split(',');
+ FileMapping.Add(part[0], part[1]);
+ Print(part[0] + " " + part[1]);
+ }
+ }
+
+ /// <summary>
+ /// 刚开始进入游戏的时候要写入.manifest,用来后续对比更新
+ /// </summary>
+ public void LoadManifest()
+ {
+ AssetBundle bundle = LoadBundle(ManifestBundleName);
+ if (bundle == null)
+ {
+ Print("没有Manifest", true);
+ return;
+ }
+ Manifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
+ if (Manifest == null)
+ {
+ Print("AssetBundleManifest", true);
+ return;
+ }
+ Print("All bundles:");
+ string[] bundleNames = Manifest.GetAllAssetBundles();
+ foreach (var bundleName in bundleNames)
+ {
+ Print(bundleName);
+ }
+ }
+
+ public AssetBundle LoadBundle(string name)
+ {
+ AssetBundle bundle;
+ if (CachedBundles.TryGetValue(name, out bundle))
+ {
+ return bundle;
+ }
+ string path = PathHelper.GetPersistentFullFilePath(BundleRoot + "/" + name);
+ Print(path);
+ if (!File.Exists(path))
+ {
+ Print("不在persistent里");
+ path = PathHelper.GetStreamingFullFilePath(BundleRoot + "/" + name);
+ }
+ bundle = AssetBundle.LoadFromFile(path);
+ if(bundle == null)
+ Print("是空的");
+ CachedBundles.Add(name, bundle);
+ return bundle;
+ }
+
+ public string GetBundleName(string assetName)
+ {
+ string bundleName;
+ FileMapping.TryGetValue(assetName, out bundleName);
+ return bundleName;
+ }
+
+ public string BundleContent(string resName)
+ {
+ string bundleName = GetBundleName(resName);
+ if (bundleName == null)
+ return null;
+ AssetBundle bundle = LoadBundle(bundleName);
+ if (bundle == null)
+ return null;
+ TextAsset text = bundle.LoadAsset<TextAsset>(resName);
+ if (text == null)
+ return null;
+ return text.text;
+ }
+
+ /// <summary>
+ /// 根据Resources/bundles.manifest读取本地bundle数据
+ /// </summary>
+ private void LoadLocalBundlesInfo()
+ {
+ TextAsset manifest = Resources.Load<TextAsset>(ManifestFileResName);
+ if(manifest == null)
+ {
+ Debug.LogError("本地Resources目录下没有bundles.manifest");
+ return;
+ }
+ string[] bundles = manifest.text.Replace("\r", string.Empty).Split('\n');
+ string[] info;
+ foreach(string bundle in bundles)
+ {
+ info = bundle.Split(',');
+ if (info.Length != 3)
+ continue;
+ BundleInfo bi = new BundleInfo();
+ bi.size = long.Parse(info[1]);
+ bi.hash = info[2];
+ localBundles.Add(info[0], bi);
+ }
+ }
+
+ IEnumerator<float> DownloadManifest()
+ {
+ WWW www = new WWW(cdnAddress + ManifesteRemotePath);
+ while(!www.isDone)
+ {
+ yield return Timing.WaitForSeconds(0.01f);
+ }
+ string content = www.text;
+ string[] bundles = content.Replace("\r", string.Empty).Split('\n');
+ string[] info;
+ foreach (string bundle in bundles)
+ {
+ info = bundle.Split(',');
+ if (info.Length != 3)
+ continue;
+ BundleInfo bi = new BundleInfo();
+ bi.size = long.Parse(info[1]);
+ bi.hash = info[2];
+ remoteBundles.Add(info[0], bi);
+ }
+ // Download bundles
+ Timing.Instance.RunCoroutineOnInstance(DownloadBundles());
+ }
+
+ IEnumerator<float> DownloadBundles()
+ {
+ List<string> downloadBundles = new List<string>();
+ foreach(var b in remoteBundles)
+ {
+ string bundleName = b.Key;
+ long size = b.Value.size;
+ string hash = b.Value.hash;
+ BundleInfo localInfo;
+ //1. 剔除首包中就有的
+ if (localBundles.TryGetValue(bundleName, out localInfo) && localInfo.hash == hash)
+ continue;
+ //2. 剔除上次小版本更新下载下来的
+ string persistentPath = PathHelper.GetPersistentFullFilePath(BundleRoot + "/" + bundleName);
+ if(File.Exists(persistentPath))
+ {
+ long lastsize = new FileInfo(persistentPath).Length;
+ if(lastsize == size)
+ {
+ string lasthash = FileUtil.GetMD5Hash(persistentPath);
+ if (lasthash == "")
+ {
+ Debug.LogError("计算md5出错");
+ }
+ else if (lasthash == hash)
+ continue;
+ }
+ }
+ downloadBundles.Add(bundleName);
+ }
+ //3. 下载
+ foreach (string bundle in downloadBundles)
+ {
+ WWW www = new WWW(cdnAddress + bundle);
+ while(!www.isDone)
+ {
+ yield return Timing.WaitForSeconds(0.01f);
+ }
+ // save to persistentDataPath
+ Print("->下载" + bundle + "完毕");
+ byte[] data = www.bytes;
+ string folder = PathHelper.GetPersistentFullFilePath(BundleRoot);
+ if (!Directory.Exists(folder))
+ Directory.CreateDirectory(folder);
+ File.WriteAllBytes(folder + "/" + bundle, data);
+ }
+
+ Print("Bundle 下载完毕,共下载" + downloadBundles.Count + "个");
+ Print("------------------------------------------------------------------------");
+ // test test
+ ResManager.Instance.ReadFileMapping();
+ ResManager.Instance.LoadManifest();
+ // Done
+ OnDownloaded();
+ }
+
+ void OnDownloaded()
+ {
+ if (test != null)
+ test.Testing();
+ }
+
+}
diff --git a/Assets/Scripts/ResManager.cs.meta b/Assets/Scripts/ResManager.cs.meta
new file mode 100644
index 0000000..0e58ce6
--- /dev/null
+++ b/Assets/Scripts/ResManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4014a0d4dcc96c744acda2162e08e6cd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Test.cs b/Assets/Scripts/Test.cs
new file mode 100644
index 0000000..da32140
--- /dev/null
+++ b/Assets/Scripts/Test.cs
@@ -0,0 +1,56 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.IO;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class Test : MonoBehaviour {
+
+ public Text text;
+ string Content;
+
+ public void Print(string content, bool alert = false)
+ {
+ if (content == null)
+ content = "空";
+ if(alert)
+ Content += "<Color=#ff0000ff>";
+ Content += content + "\n";
+ if(alert)
+ Content += "</Color>";
+ text.text = Content;
+ }
+
+ private void Awake()
+ {
+ ResManager.Instance.Init(this);
+ }
+
+ public void Testing()
+ {
+ Print("----------------------------------------------------------------------------------------");
+ Print("Application.dataPath=" + Application.dataPath);
+ Print("Application.streamingAssetsPath=" + Application.streamingAssetsPath);
+ Print("Application.persistentDataPath=" + Application.persistentDataPath);
+ Print("Application.temporaryCachePath=" + Application.temporaryCachePath);
+ Print("----------------------------------------------------------------------------------------");
+ Print("读取Assets/Resources/res1.txt , 内容是");
+ Print(ResManager.Instance.ResContent("res1"));
+ Print("读取Assets/Resources/SubFolder/res1.txt , 内容是");
+ Print(ResManager.Instance.ResContent("SubFolder/res1"));
+ Print("读取Assets/Art/Resources/res5.txt , 内容是");
+ Print(ResManager.Instance.ResContent("res5"));
+ Print("----------------------------------------------------------------------------------------");
+ // AssetBundle.LoadAsset()可以省略后缀
+ Print("读取Assets/Files/file1.txt , 内容是");
+ Print(ResManager.Instance.BundleContent("file1.txt"));
+ Print(ResManager.Instance.BundleContent("file1"));
+ Print("读取Assets/Files/file1.abc, 内容是");
+ Print(ResManager.Instance.BundleContent("file1.abc"));
+ Print("读取Assets/Files/file1.bcd, 内容是");
+ Print(ResManager.Instance.BundleContent("file1.bcd"));
+ Print("说明进Bundle的资源不含后缀的资源名字不能冲突", true);
+ }
+
+}
diff --git a/Assets/Scripts/Test.cs.meta b/Assets/Scripts/Test.cs.meta
new file mode 100644
index 0000000..9c5d14a
--- /dev/null
+++ b/Assets/Scripts/Test.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 97aee000e53f1464bbd45e96b6ea0f9a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: