开发注意事项
- 场景中不放置任何的物体,只放置启动脚本
- UI视图和其他预制体不挂载任何的脚本,保持纯净,使用时用代码挂载
- 所有的资源都使用AB包的形式进行存储和加载,舍弃Resource文件夹的使用
- UI的脚本不要自己创建,使用Tools/ui_gen中的编辑器拓展来生成
- Excel的解析工具代码在
单例模式
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 实现普通的单例模式
// where 限制模板的类型, new()指的是这个类型必须要能被实例化
public abstract class Singleton<T> where T : new() {
private static T _instance;
private static object mutex = new object();
public static T Instance {
get {
if (_instance == null) {
lock (mutex) { // 保证我们的单例,是线程安全的;
if (_instance == null) {
_instance = new T();
}
}
}
return _instance;
}
}
}
// Monobeavior: 声音, 网络
// Unity单例
public class UnitySingleton<T> : MonoBehaviour
where T : Component {
private static T _instance = null;
public static T Instance {
get {
if (_instance == null) {
_instance = FindObjectOfType(typeof(T)) as T;
if (_instance == null) {
GameObject obj = new GameObject();
_instance = (T)obj.AddComponent(typeof(T));
obj.hideFlags = HideFlags.DontSave;
// obj.hideFlags = HideFlags.HideAndDontSave;
obj.name = typeof(T).Name;
}
}
return _instance;
}
}
public virtual void Awake() {
DontDestroyOnLoad(this.gameObject);
if (_instance == null) {
_instance = this as T;
}
else {
GameObject.Destroy(this.gameObject);
}
}
}
事件管理
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EventMgr : UnitySingleton<EventMgr>
{
public delegate void event_handler(string event_name, object udata);
private Dictionary<string, event_handler> dic = new Dictionary<string, event_handler>();
public void init() {
}
public void AddListener(string event_name, event_handler h) {
if (this.dic.ContainsKey(event_name)) {
this.dic[event_name] += h;
}
else {
this.dic.Add(event_name, h);
}
}
public void RemoveListener(string event_name, event_handler h) {
if (!this.dic.ContainsKey(event_name)) {
return;
}
this.dic[event_name] -= h;
if (this.dic[event_name] == null) {
this.dic.Remove(event_name);
}
}
public void Emit(string event_name, object udata) {
if (!this.dic.ContainsKey(event_name)) {
return;
}
this.dic[event_name](event_name, udata);
}
}
资源管理(基于AssetBundle)
资源加载
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using AssetBundles;
public class ResMgr : UnitySingleton<ResMgr> {
public override void Awake() {
base.Awake();
this.gameObject.AddComponent<AssetBundleManager>();
}
public T GetAssetCache<T>(string name) where T : UnityEngine.Object
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
string path = AssetBundleUtility.PackagePathToAssetsPath(name);
// Debug.Log(path);
UnityEngine.Object target = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
return target as T;
}
#endif
return AssetBundleManager.Instance.GetAssetCache(name) as T;
}
public void LoadAssetBundleAsync(string assetbundleName, Action end_func)
{
this.StartCoroutine(this.IE_LoadAssetBundleAsync(assetbundleName, end_func));
}
public IEnumerator IE_LoadAssetBundleAsync(string assetbundleName, Action end_func)
{
var loader = AssetBundleManager.Instance.LoadAssetBundleAsync(assetbundleName);
yield return loader;
loader.Dispose();
end_func();
}
}
AB包管理
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// added by wsh @ 2017-12-21
/// 功能:assetbundle管理类,为外部提供统一的资源加载界面、协调Assetbundle各个子系统的运行
/// 注意:
/// 1、抛弃Resources目录的使用,官方建议:https://unity3d.com/cn/learn/tutorials/temas/best-practices/resources-folder?playlist=30089
/// 2、提供Editor和Simulate模式,前者不适用Assetbundle,直接加载资源,快速开发;后者使用Assetbundle,用本地服务器模拟资源更新
/// 3、场景不进行打包,场景资源打包为预设
/// 4、只提供异步接口,所有加载按异步进行
/// 5、采用LZMA压缩方式,性能瓶颈在Assetbundle加载上,ab加载异步,asset加载同步,ab加载后导出全部asset并卸载ab
/// 6、所有公共ab包(被多个ab包依赖)常驻内存,非公共包加载asset以后立刻卸载,被依赖的公共ab包会随着资源预加载自动加载并常驻内存
/// 7、随意卸载公共ab包可能导致内存资源重复,最好在切换场景时再手动清理不需要的公共ab包
/// 8、常驻包(公共ab包)引用计数不为0时手动清理无效,正在等待加载的所有ab包不能强行终止---一旦发起创建就一定要等操作结束,异步过程进行中清理无效
/// 9、切换场景时最好预加载所有可能使用到的资源,所有加载器用完以后记得Dispose回收,清理GC时注意先释放所有Asset缓存
/// 10、逻辑层所有Asset路径带文件类型后缀,且是AssetBundleConfig.ResourcesFolderName下的相对路径,注意:路径区分大小写
/// TODO:
/// 1、区分场景常驻包和全局公共包,切换场景时自动卸载场景公共包
/// 使用说明:
/// 1、由Asset路径获取AssetName、AssetBundleName:ParseAssetPathToNames
/// 2、设置常驻(公共)ab包:SetAssetBundleResident(assebundleName, true)---公共ab包已经自动设置常驻
/// 2、(预)加载资源:var loader = LoadAssetBundleAsync(assetbundleName),协程等待加载完毕后Dispose:loader.Dispose()
/// 3、加载Asset资源:var loader = LoadAssetAsync(assetPath, TextAsset),协程等待加载完毕后Dispose:loader.Dispose()
/// 4、离开场景清理所有Asset缓存:ClearAssetsCache(),UnloadUnusedAssetBundles(), Resources.UnloadUnusedAssets()
/// 5、离开场景清理必要的(公共)ab包:TryUnloadAssetBundle(),注意:这里只是尝试卸载,所有引用计数不为0的包(还正在加载)不会被清理
/// </summary>
namespace AssetBundles
{
public class AssetBundleManager : UnitySingleton<AssetBundleManager>
{
// 最大同时进行的ab创建数量
const int MAX_ASSETBUNDLE_CREATE_NUM = 5;
// manifest:提供依赖关系查找以及hash值比对
Manifest manifest = null;
// 资源路径相关的映射表
AssetsPathMapping assetsPathMapping = null;
// 常驻ab包:需要手动添加公共ab包进来,常驻包不会自动卸载(即使引用计数为0),引用计数为0时可以手动卸载
HashSet<string> assetbundleResident = new HashSet<string>();
// ab缓存包:所有目前已经加载的ab包,包括临时ab包与公共ab包
Dictionary<string, AssetBundle> assetbundlesCaching = new Dictionary<string, AssetBundle>();
// ab缓存包引用计数:卸载ab包时只有引用计数为0时才会真正执行卸载
Dictionary<string, int> assetbundleRefCount = new Dictionary<string, int>();
// asset缓存:给非公共ab包的asset提供逻辑层的复用
Dictionary<string, UnityEngine.Object> assetsCaching = new Dictionary<string, UnityEngine.Object>();
// 加载数据请求:正在prosessing或者等待prosessing的资源请求
Dictionary<string, ResourceWebRequester> webRequesting = new Dictionary<string, ResourceWebRequester>();
// 等待处理的资源请求
Queue<ResourceWebRequester> webRequesterQueue = new Queue<ResourceWebRequester>();
// 正在处理的资源请求
List<ResourceWebRequester> prosessingWebRequester = new List<ResourceWebRequester>();
// 逻辑层正在等待的ab加载异步句柄
List<AssetBundleAsyncLoader> prosessingAssetBundleAsyncLoader = new List<AssetBundleAsyncLoader>();
// 逻辑层正在等待的asset加载异步句柄
List<AssetAsyncLoader> prosessingAssetAsyncLoader = new List<AssetAsyncLoader>();
public static string ManifestBundleName
{
get;
set;
}
public IEnumerator Initialize()
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
yield break;
}
#endif
manifest = new Manifest();
assetsPathMapping = new AssetsPathMapping();
// 说明:同时请求资源可以提高加载速度
var manifestRequest = RequestAssetBundleAsync(manifest.AssetbundleName);
var pathMapRequest = RequestAssetBundleAsync(assetsPathMapping.AssetbundleName);
yield return manifestRequest;
var assetbundle = manifestRequest.assetbundle;
manifest.LoadFromAssetbundle(assetbundle);
assetbundle.Unload(false);
manifestRequest.Dispose();
yield return pathMapRequest;
assetbundle = pathMapRequest.assetbundle;
var mapContent = assetbundle.LoadAsset<TextAsset>(assetsPathMapping.AssetName);
if (mapContent != null)
{
assetsPathMapping.Initialize(mapContent.text);
}
assetbundle.Unload(true);
pathMapRequest.Dispose();
// 设置所有公共包为常驻包
var start = DateTime.Now;
var allAssetbundleNames = manifest.GetAllAssetBundleNames();
foreach (var curAssetbundleName in allAssetbundleNames)
{
if (string.IsNullOrEmpty(curAssetbundleName))
{
continue;
}
int count = 0;
foreach (var checkAssetbundle in allAssetbundleNames)
{
if (checkAssetbundle == curAssetbundleName || string.IsNullOrEmpty(checkAssetbundle))
{
continue;
}
var allDependencies = manifest.GetAllDependencies(checkAssetbundle);
if (Array.IndexOf(allDependencies, curAssetbundleName) >= 0)
{
count++;
if (count >= 2)
{
break;
}
}
}
if (count >= 2)
{
SetAssetBundleResident(curAssetbundleName, true);
}
}
Debug.Log(string.Format("AssetBundleResident Initialize use {0}ms", (DateTime.Now - start).Milliseconds));
yield break;
}
public IEnumerator Cleanup()
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
yield break;
}
#endif
// 等待所有请求完成
// 要是不等待Unity很多版本都有各种Bug
yield return new WaitUntil(() =>
{
return prosessingWebRequester.Count == 0;
});
yield return new WaitUntil(() =>
{
return prosessingAssetBundleAsyncLoader.Count == 0;
});
yield return new WaitUntil(() =>
{
return prosessingAssetAsyncLoader.Count == 0;
});
ClearAssetsCache();
foreach (var assetbunle in assetbundlesCaching.Values)
{
if (assetbunle != null)
{
assetbunle.Unload(false);
}
}
assetbundlesCaching.Clear();
assetbundleRefCount.Clear();
assetbundleResident.Clear();
yield break;
}
public Manifest curManifest
{
get
{
return manifest;
}
}
public string DownloadUrl
{
get
{
// return Setting.SERVER_RESOURCE_ADDR;
return null;
}
}
public void SetAssetBundleResident(string assetbundleName, bool resident)
{
Debug.Log("SetAssetBundleResident : " + assetbundleName + ", " + resident.ToString());
bool exist = assetbundleResident.Contains(assetbundleName);
if (resident && !exist)
{
assetbundleResident.Add(assetbundleName);
}
else if(!resident && exist)
{
assetbundleResident.Remove(assetbundleName);
}
}
public bool IsAssetBundleResident(string assebundleName)
{
return assetbundleResident.Contains(assebundleName);
}
public bool IsAssetBundleLoaded(string assetbundleName)
{
return assetbundlesCaching.ContainsKey(assetbundleName);
}
public AssetBundle GetAssetBundleCache(string assetbundleName)
{
AssetBundle target = null;
assetbundlesCaching.TryGetValue(assetbundleName, out target);
return target;
}
protected void RemoveAssetBundleCache(string assetbundleName)
{
assetbundlesCaching.Remove(assetbundleName);
}
protected void AddAssetBundleCache(string assetbundleName, AssetBundle assetbundle)
{
assetbundlesCaching[assetbundleName] = assetbundle;
}
public bool IsAssetLoaded(string assetName)
{
return assetsCaching.ContainsKey(assetName);
}
public UnityEngine.Object GetAssetCache(string assetName)
{
UnityEngine.Object target = null;
assetsCaching.TryGetValue(assetName, out target);
return target;
}
public void AddAssetCache(string assetName, UnityEngine.Object asset)
{
assetsCaching[assetName] = asset;
}
public void AddAssetbundleAssetsCache(string assetbundleName)
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
return;
}
#endif
if (!IsAssetBundleLoaded(assetbundleName))
{
Debug.LogError("Try to add assets cache from unloaded assetbundle : " + assetbundleName);
return;
}
var curAssetbundle = GetAssetBundleCache(assetbundleName);
var allAssetNames = assetsPathMapping.GetAllAssetNames(assetbundleName);
for (int i = 0; i < allAssetNames.Count; i++)
{
var assetName = allAssetNames[i];
if (IsAssetLoaded(assetName))
{
continue;
}
var assetPath = AssetBundleUtility.PackagePathToAssetsPath(assetName);
var asset = curAssetbundle == null ? null : curAssetbundle.LoadAsset(assetPath);
AddAssetCache(assetName, asset);
#if UNITY_EDITOR
// 说明:在Editor模拟时,Shader要重新指定
var go = asset as GameObject;
if (go != null)
{
var renderers = go.GetComponentsInChildren<Renderer>();
for (int j = 0; j < renderers.Length; j++)
{
var mat = renderers[j].sharedMaterial;
if (mat == null)
{
continue;
}
var shader = mat.shader;
if (shader != null)
{
var shaderName = shader.name;
mat.shader = Shader.Find(shaderName);
}
}
}
#endif
}
}
public void ClearAssetsCache()
{
assetsCaching.Clear();
}
public ResourceWebRequester GetAssetBundleAsyncCreater(string assetbundleName)
{
ResourceWebRequester creater = null;
webRequesting.TryGetValue(assetbundleName, out creater);
return creater;
}
protected int GetReferenceCount(string assetbundleName)
{
int count = 0;
assetbundleRefCount.TryGetValue(assetbundleName, out count);
return count;
}
protected int IncreaseReferenceCount(string assetbundleName)
{
int count = 0;
assetbundleRefCount.TryGetValue(assetbundleName, out count);
count++;
assetbundleRefCount[assetbundleName] = count;
return count;
}
protected int DecreaseReferenceCount(string assetbundleName)
{
int count = 0;
assetbundleRefCount.TryGetValue(assetbundleName, out count);
count--;
assetbundleRefCount[assetbundleName] = count;
return count;
}
protected bool CreateAssetBundleAsync(string assetbundleName)
{
if (IsAssetBundleLoaded(assetbundleName) || webRequesting.ContainsKey(assetbundleName))
{
return false;
}
var creater = ResourceWebRequester.Get();
var url = AssetBundleUtility.GetAssetBundleFileUrl(assetbundleName);
creater.Init(assetbundleName, url);
webRequesting.Add(assetbundleName, creater);
webRequesterQueue.Enqueue(creater);
// 创建器持有的引用:创建器对每个ab来说是全局唯一的
IncreaseReferenceCount(assetbundleName);
return true;
}
// 异步请求Assetbundle资源,AB是否缓存取决于是否设置为常驻包,Assets一律缓存,处理依赖
public BaseAssetBundleAsyncLoader LoadAssetBundleAsync(string assetbundleName)
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
return new EditorAssetBundleAsyncLoader(assetbundleName);
}
#endif
var loader = AssetBundleAsyncLoader.Get();
prosessingAssetBundleAsyncLoader.Add(loader);
if (manifest != null)
{
string[] dependancies = manifest.GetAllDependencies(assetbundleName);
for (int i = 0; i < dependancies.Length; i++)
{
var dependance = dependancies[i];
if (!string.IsNullOrEmpty(dependance) && dependance != assetbundleName)
{
CreateAssetBundleAsync(dependance);
// ab缓存对依赖持有的引用
IncreaseReferenceCount(dependance);
}
}
loader.Init(assetbundleName, dependancies);
}
else
{
loader.Init(assetbundleName, null);
}
CreateAssetBundleAsync(assetbundleName);
// 加载器持有的引用:同一个ab能同时存在多个加载器,等待ab创建器完成
IncreaseReferenceCount(assetbundleName);
return loader;
}
// 从服务器下载网页内容,需提供完整url
public ResourceWebRequester DownloadWebResourceAsync(string url)
{
var creater = ResourceWebRequester.Get();
creater.Init(url, url, true);
webRequesting.Add(url, creater);
webRequesterQueue.Enqueue(creater);
return creater;
}
// 从资源服务器下载非Assetbundle资源
public ResourceWebRequester DownloadAssetFileAsync(string filePath)
{
if (string.IsNullOrEmpty(DownloadUrl))
{
Debug.LogError("You should set download url first!!!");
return null;
}
var creater = ResourceWebRequester.Get();
var url = DownloadUrl + filePath;
creater.Init(filePath, url, true);
webRequesting.Add(filePath, creater);
webRequesterQueue.Enqueue(creater);
return creater;
}
// 从资源服务器下载Assetbundle资源,不缓存,无依赖
public ResourceWebRequester DownloadAssetBundleAsync(string filePath)
{
// 如果ResourceWebRequester升级到使用UnityWebRequester,那么下载AB和下载普通资源需要两个不同的DownLoadHandler
// 兼容升级的可能性,这里也做一下区分
return DownloadAssetFileAsync(filePath);
}
// 本地异步请求非Assetbundle资源
public ResourceWebRequester RequestAssetFileAsync(string filePath, bool streamingAssetsOnly = true)
{
var creater = ResourceWebRequester.Get();
string url = null;
if (streamingAssetsOnly)
{
url = AssetBundleUtility.GetStreamingAssetsFilePath(filePath);
}
else
{
url = AssetBundleUtility.GetAssetBundleFileUrl(filePath);
}
creater.Init(filePath, url, true);
webRequesting.Add(filePath, creater);
webRequesterQueue.Enqueue(creater);
return creater;
}
// 本地异步请求Assetbundle资源,不缓存,无依赖
public ResourceWebRequester RequestAssetBundleAsync(string assetbundleName)
{
var creater = ResourceWebRequester.Get();
var url = AssetBundleUtility.GetAssetBundleFileUrl(assetbundleName);
creater.Init(assetbundleName, url, true);
webRequesting.Add(assetbundleName, creater);
webRequesterQueue.Enqueue(creater);
return creater;
}
public void UnloadAssetBundleDependencies(string assetbundleName)
{
if (manifest != null)
{
string[] dependancies = manifest.GetAllDependencies(assetbundleName);
for (int i = 0; i < dependancies.Length; i++)
{
var dependance = dependancies[i];
if (!string.IsNullOrEmpty(dependance) && dependance != assetbundleName)
{
UnloadAssetBundle(dependance);
}
}
}
}
protected bool UnloadAssetBundle(string assetbundleName, bool unloadResident = false, bool unloadAllLoadedObjects = false)
{
int count = GetReferenceCount(assetbundleName);
if (count <= 0)
{
return false;
}
count = DecreaseReferenceCount(assetbundleName);
if (count > 0)
{
return false;
}
var assetbundle = GetAssetBundleCache(assetbundleName);
var isResident = IsAssetBundleResident(assetbundleName);
if (assetbundle != null)
{
if (!isResident || isResident && unloadResident)
{
assetbundle.Unload(unloadAllLoadedObjects);
RemoveAssetBundleCache(assetbundleName);
UnloadAssetBundleDependencies(assetbundleName);
return true;
}
}
return false;
}
public bool TryUnloadAssetBundle(string assetbundleName, bool unloadAllLoadedObjects = false)
{
int count = GetReferenceCount(assetbundleName);
if (count > 0)
{
return false;
}
return UnloadAssetBundle(assetbundleName, true, unloadAllLoadedObjects);
}
public void UnloadUnusedAssetBundles(bool unloadResident = false, bool unloadAllLoadedObjects = false)
{
int unloadCount = 0;
bool hasDoUnload = false;
do
{
hasDoUnload = false;
var iter = assetbundleRefCount.GetEnumerator();
while (iter.MoveNext())
{
var assetbundleName = iter.Current.Key;
var referenceCount = iter.Current.Value;
if (referenceCount <= 0)
{
var result = UnloadAssetBundle(assetbundleName, unloadResident, unloadAllLoadedObjects);
if (result)
{
unloadCount++;
hasDoUnload = true;
}
}
}
} while (hasDoUnload);
}
public bool MapAssetPath(string assetPath, out string assetbundleName, out string assetName)
{
return assetsPathMapping.MapAssetPath(assetPath, out assetbundleName, out assetName);
}
public BaseAssetAsyncLoader LoadAssetAsync(string assetPath, System.Type assetType)
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
string path = AssetBundleUtility.PackagePathToAssetsPath(assetPath);
UnityEngine.Object target = AssetDatabase.LoadAssetAtPath(path, assetType);
return new EditorAssetAsyncLoader(target);
}
#endif
string assetbundleName = null;
string assetName = null;
bool status = MapAssetPath(assetPath, out assetbundleName, out assetName);
if (!status)
{
Debug.LogError("No assetbundle at asset path :" + assetPath);
return null;
}
var loader = AssetAsyncLoader.Get();
prosessingAssetAsyncLoader.Add(loader);
if (IsAssetLoaded(assetName))
{
loader.Init(assetName, GetAssetCache(assetName));
return loader;
}
else
{
var assetbundleLoader = LoadAssetBundleAsync(assetbundleName);
loader.Init(assetName, assetbundleLoader);
return loader;
}
}
void Update()
{
OnProsessingWebRequester();
OnProsessingAssetBundleAsyncLoader();
OnProsessingAssetAsyncLoader();
}
void OnProsessingWebRequester()
{
for (int i = prosessingWebRequester.Count - 1; i >= 0; i--)
{
var creater = prosessingWebRequester[i];
creater.Update();
if (creater.IsDone())
{
prosessingWebRequester.RemoveAt(i);
webRequesting.Remove(creater.assetbundleName);
UnloadAssetBundle(creater.assetbundleName);
if (creater.noCache)
{
return;
}
// 说明:有错误也缓存下来,只不过资源为空
// 1、避免再次错误加载
// 2、如果不存下来加载器将无法判断什么时候结束
AddAssetBundleCache(creater.assetbundleName, creater.assetbundle);
creater.Dispose();
}
}
int slotCount = prosessingWebRequester.Count;
while (slotCount < MAX_ASSETBUNDLE_CREATE_NUM && webRequesterQueue.Count > 0)
{
var creater = webRequesterQueue.Dequeue();
creater.Start();
prosessingWebRequester.Add(creater);
slotCount++;
}
}
void OnProsessingAssetBundleAsyncLoader()
{
for (int i = prosessingAssetBundleAsyncLoader.Count - 1; i >= 0; i--)
{
var loader = prosessingAssetBundleAsyncLoader[i];
loader.Update();
if (loader.IsDone())
{
UnloadAssetBundle(loader.assetbundleName);
prosessingAssetBundleAsyncLoader.RemoveAt(i);
}
}
}
void OnProsessingAssetAsyncLoader()
{
for (int i = prosessingAssetAsyncLoader.Count - 1; i >= 0; i--)
{
var loader = prosessingAssetAsyncLoader[i];
loader.Update();
if (loader.IsDone())
{
prosessingAssetAsyncLoader.RemoveAt(i);
}
}
}
#if UNITY_EDITOR
public HashSet<string> GetAssetbundleResident()
{
return assetbundleResident;
}
public ICollection<string> GetAssetbundleCaching()
{
return assetbundlesCaching.Keys;
}
public Dictionary<string, ResourceWebRequester> GetWebRequesting()
{
return webRequesting;
}
public Queue<ResourceWebRequester> GetWebRequestQueue()
{
return webRequesterQueue;
}
public List<ResourceWebRequester> GetProsessingWebRequester()
{
return prosessingWebRequester;
}
public List<AssetBundleAsyncLoader> GetProsessingAssetBundleAsyncLoader()
{
return prosessingAssetBundleAsyncLoader;
}
public List<AssetAsyncLoader> GetProsessingAssetAsyncLoader()
{
return prosessingAssetAsyncLoader;
}
public string GetAssetBundleName(string assetName)
{
return assetsPathMapping.GetAssetBundleName(assetName);
}
public int GetAssetCachingCount()
{
return assetsCaching.Count;
}
public Dictionary<string, List<string>> GetAssetCaching()
{
var assetbundleDic = new Dictionary<string, List<string>>();
List<string> assetNameList = null;
var iter = assetsCaching.GetEnumerator();
while (iter.MoveNext())
{
var assetName = iter.Current.Key;
var assetbundleName = assetsPathMapping.GetAssetBundleName(assetName);
assetbundleDic.TryGetValue(assetbundleName, out assetNameList);
if (assetNameList == null)
{
assetNameList = new List<string>();
}
assetNameList.Add(assetName);
assetbundleDic[assetbundleName] = assetNameList;
}
return assetbundleDic;
}
public int GetAssetbundleRefrenceCount(string assetbundleName)
{
return GetReferenceCount(assetbundleName);
}
public int GetAssetbundleDependenciesCount(string assetbundleName)
{
string[] dependancies = manifest.GetAllDependencies(assetbundleName);
int count = 0;
for (int i = 0; i < dependancies.Length; i++)
{
var cur = dependancies[i];
if (!string.IsNullOrEmpty(cur) && cur != assetbundleName)
{
count++;
}
}
return count;
}
public List<string> GetAssetBundleRefrences(string assetbundleName)
{
List<string> refrences = new List<string>();
var cachingIter = assetbundlesCaching.GetEnumerator();
while (cachingIter.MoveNext())
{
var curAssetbundleName = cachingIter.Current.Key;
if (curAssetbundleName == assetbundleName)
{
continue;
}
string[] dependancies = manifest.GetAllDependencies(curAssetbundleName);
for (int i = 0; i < dependancies.Length; i++)
{
var dependance = dependancies[i];
if (dependance == assetbundleName)
{
refrences.Add(curAssetbundleName);
}
}
}
var requestingIter = webRequesting.GetEnumerator();
while (requestingIter.MoveNext())
{
var curAssetbundleName = requestingIter.Current.Key;
if (curAssetbundleName == assetbundleName)
{
continue;
}
string[] dependancies = manifest.GetAllDependencies(curAssetbundleName);
for (int i = 0; i < dependancies.Length; i++)
{
var dependance = dependancies[i];
if (dependance == assetbundleName)
{
refrences.Add(curAssetbundleName);
}
}
}
return refrences;
}
public List<string> GetWebRequesterRefrences(string assetbundleName)
{
List<string> refrences = new List<string>();
var iter = webRequesting.GetEnumerator();
while (iter.MoveNext())
{
var curAssetbundleName = iter.Current.Key;
var webRequster = iter.Current.Value;
if (curAssetbundleName == assetbundleName)
{
refrences.Add(webRequster.Sequence.ToString());
continue;
}
}
return refrences;
}
public List<string> GetAssetBundleLoaderRefrences(string assetbundleName)
{
List<string> refrences = new List<string>();
var iter = prosessingAssetBundleAsyncLoader.GetEnumerator();
while (iter.MoveNext())
{
var curAssetbundleName = iter.Current.assetbundleName;
var curLoader = iter.Current;
if (curAssetbundleName == assetbundleName)
{
refrences.Add(curLoader.Sequence.ToString());
}
}
return refrences;
}
#endif
}
}
AssetBundle编辑器扩展
Debug模块
show_fps
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class show_fps : MonoBehaviour {
// 固定的一个时间间隔
private float time_delta = 0.5f;
// Time.realtimeSinceStartup: 指的是我们当前从启动开始到现在运行的时间,单位(s)
private float prev_time = 0.0f; // 上一次统计FPS的时间;
private float fps = 0.0f; // 计算出来的FPS的值;
private int i_frames = 0; // 累计我们刷新的帧数;
// GUI显示;
private GUIStyle style;
void Awake() {
// 假设CPU 100% 工作的状态下FPS 300,
// 当你设置了这个以后,他就维持在60FPS左右,不会继续冲高;
// -1, 游戏引擎就会不段的刷新我们的画面,有多高,刷多高; 60FPS左右;
Application.targetFrameRate = 60;
}
// Use this for initialization
void Start () {
this.prev_time = Time.realtimeSinceStartup;
this.style = new GUIStyle();
this.style.fontSize = 15;
this.style.normal.textColor = new Color(255, 255, 255);
}
void OnGUI() {
GUI.Label(new Rect(0, Screen.height - 20, 200, 200), "FPS:" + this.fps.ToString("f2"), this.style);
}
// Update is called once per frame
// 每次游戏刷新的时候就会调用;
void Update () {
this.i_frames ++;
if (Time.realtimeSinceStartup >= this.prev_time + this.time_delta) {
this.fps = ((float)this.i_frames) / (Time.realtimeSinceStartup - this.prev_time);
this.prev_time = Time.realtimeSinceStartup;
this.i_frames = 0; // 重新累积我们的FPS
}
}
}
DebugManager(如果是编辑器模式,显示调试信息到界面)
#define USE_DEBUG
using UnityEngine;
using System;
using System.Collections.Generic;
#if RELEASE_BUILD
//发布的时候重载默认Debug
public static class Debug
{
public static void Log(object message) { }
public static void Log(object message, object context) { }
public static void LogError(object message) { }
public static void LogError(object message, object context) { }
public static void LogException(Exception exception) { }
public static void LogException(Exception exception, object context) { }
public static void LogWarning(object message) { }
public static void LogWarning(object message, object context) { }
public static void DrawLine(Vector3 start, Vector3 end) { }
public static void DrawLine(Vector3 start, Vector3 end, Color color) { }
}
#endif
//重载默认Debug
public static class Debugger
{
public static void Log(object message)
{
#if RELEASE_BUILD
#else
if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
DebugManager.Log(message.ToString());
else
UnityEngine.Debug.Log(message.ToString());
#endif
}
public static void LogError(object message)
{
#if RELEASE_BUILD
#else
if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
DebugManager.Log(message.ToString());
else
UnityEngine.Debug.LogError(message.ToString());
#endif
}
public static void LogWarning(object message)
{
#if RELEASE_BUILD
#else
if (Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
DebugManager.Log(message.ToString());
else
UnityEngine.Debug.LogWarning(message.ToString());
#endif
}
}
//[yaz]调试管理器
public class DebugManager : MonoBehaviour
{
// public bool DebugEffect = false;
static public bool DebugInfo = false;
static public Int32 DebugCount = 0;
//保存错误信息列表
static public List<string> errorInfoList = new List<string>();
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
public static void Switch()
{
DebugInfo = !DebugInfo;
if (DebugInfo)
{
++DebugCount;
errorInfoList.Clear();
errorInfoList.Add(DebugCount.ToString());
}
}
public static void Log(string str)
{
errorInfoList.Add(str);
}
//错误信息窗口
public Rect errorInfoWindowRect = new Rect(80, 20, 800, 2000);
void OnGUI()
{
GUILayout.Space(40);
if (GUILayout.Button("ShowError"))
{
DebugInfo = !DebugInfo;
}
else if (GUILayout.Button("Clear"))
{
errorInfoList.Clear();
}
//调试错误信息
if (DebugInfo)
{
errorInfoWindowRect = GUILayout.Window(1, errorInfoWindowRect, DebugErrorWindow, "Debug Error Window");
}
}
//错误信息显示窗口
private Vector2 errorInfoPos = new Vector2(0, 0);
void DebugErrorWindow(int windowID)
{
errorInfoPos = GUILayout.BeginScrollView(errorInfoPos, false, true, GUILayout.Width(800), GUILayout.Height(600));
GUILayout.Space(30);
GUILayout.BeginVertical();
foreach (string str in errorInfoList)
{
GUILayout.Label(str, GUILayout.Width(800));
}
//GUI.color = Color.white;
GUILayout.EndVertical();
GUILayout.EndScrollView();
}
}
声音管理模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SoundMgr : UnitySingleton<SoundMgr>
{
const int MAX_SOUNDS = 8;
private AudioSource music;
private AudioSource[] sounds = new AudioSource[MAX_SOUNDS];
private int music_muted;
private int sound_muted;
private float music_volume;
private float sound_volume;
private int now_soundid = 0;
public override void Awake () {
base.Awake();
this.music_muted = PlayerPrefs.GetInt("music_muted", 0);
this.sound_muted = PlayerPrefs.GetInt("sound_muted", 0);
this.music_volume = PlayerPrefs.GetFloat("music_volume", 1.0f);
this.sound_volume = PlayerPrefs.GetFloat("sound_volume", 1.0f);
this.music = this.gameObject.AddComponent<AudioSource>();
this.music.mute = (this.music_muted == 1);
this.music.volume = (this.music_volume);
for (int i = 0; i < MAX_SOUNDS; i++) {
this.sounds[i] = this.gameObject.AddComponent<AudioSource>();
this.sounds[i].mute = (this.sound_muted == 1);
this.sounds[i].volume = (this.sound_volume);
}
}
public void play_music(string url, bool loop = true)
{
AudioClip clip = ResMgr.Instance.GetAssetCache<AudioClip>(url) as AudioClip;
// AudioClip clip = (AudioClip)UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/AssetsPackage/Sounds/bgm_scene1.ogg", typeof(AudioClip));
this.music.clip = clip;
this.music.loop = loop;
this.music.Play();
}
public int play_sound(string url, bool loop = false)
{
int soundid = this.now_soundid;
AudioClip clip = (AudioClip)ResMgr.Instance.GetAssetCache<AudioClip>(url);
this.sounds[this.now_soundid].clip = clip;
this.sounds[this.now_soundid].loop = loop;
this.sounds[this.now_soundid].Play();
this.now_soundid ++;
this.now_soundid = (this.now_soundid >= this.sounds.Length) ? 0 : this.now_soundid;
return soundid;
}
public int play_one_shot(string url, bool loop = false)
{
int soundid = this.now_soundid;
AudioClip clip = (AudioClip)ResMgr.Instance.GetAssetCache<AudioClip>(url);
this.sounds[this.now_soundid].clip = clip;
this.sounds[this.now_soundid].loop = loop;
this.sounds[this.now_soundid].PlayOneShot(clip);
this.now_soundid++;
this.now_soundid = (this.now_soundid >= this.sounds.Length) ? 0 : this.now_soundid;
return soundid;
}
public void stop_sound(int soundid)
{
if (soundid < 0 || soundid >= this.sounds.Length) {
return;
}
this.sounds[soundid].Stop();
this.sounds[this.now_soundid].clip = null;
}
public void stop_music()
{
this.music.Stop();
this.music.clip = null;
}
public void stop_all_sounds()
{
for (int i = 0; i < this.sounds.Length; i++) {
this.sounds[i].Stop();
this.sounds[i].clip = null;
}
}
public void set_music_mute(bool mute)
{
if (mute == (this.music_muted == 1)) {
return;
}
this.music_muted = mute ? 1 : 0;
this.music.mute = mute;
PlayerPrefs.SetInt("music_muted", this.music_muted);
}
public void set_sound_mute(bool mute)
{
if (mute == (this.sound_muted == 1)) {
return;
}
this.sound_muted = (mute) ? 1 : 0;
for (int i = 0; i < MAX_SOUNDS; i++) {
this.sounds[i].mute = mute;
}
PlayerPrefs.SetInt("sound_muted", this.sound_muted);
}
public void set_music_volume(float value)
{
if (value < 0.0f || value > 1.0f) {
return;
}
this.music_volume = value;
this.music.volume = this.music_volume;
PlayerPrefs.SetFloat("music_volume", this.music_volume);
}
public void set_sound_volume(float value)
{
if (value < 0.0f || value > 1.0f) {
return;
}
this.sound_volume = value;
for (int i = 0; i < MAX_SOUNDS; i++) {
this.sounds[i].volume = value;
}
PlayerPrefs.SetFloat("sound_volume", this.sound_volume);
}
public bool musicMuted {
get {
return (this.music_muted == 1);
}
}
public bool soundMuted {
get {
return (this.sound_muted == 1);
}
}
public float musicVolume {
get {
return this.music_volume;
}
}
public float soundVolume {
get {
return this.sound_volume;
}
}
}
UI框架模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using System;
// 这是一个UI脚本的编写模板
public class UICtrl : MonoBehaviour {
public Dictionary<string, GameObject> view = new Dictionary<string, GameObject>();
private void load_all_object(GameObject root, string path) {
foreach (Transform tf in root.transform) {
if (this.view.ContainsKey(path + tf.gameObject.name)) {
// Debugger.LogWarning("Warning object is exist:" + path + tf.gameObject.name + "!");
continue;
}
this.view.Add(path + tf.gameObject.name, tf.gameObject);
load_all_object(tf.gameObject, path + tf.gameObject.name + "/");
}
}
public virtual void Awake() {
this.load_all_object(this.gameObject, "");
}
public void AddButtonListener(string view_name, UnityAction onclick) {
Button bt = this.view[view_name].GetComponent<Button>();
if (bt == null)
{
Debugger.LogWarning("UI_manager add_button_listener: not Button Component!");
return;
}
bt.onClick.AddListener(onclick);
}
public void AddSliderListener(string view_name, UnityAction<float> on_value_changle) {
Slider s = this.view[view_name].GetComponent<Slider>();
if (s == null)
{
Debugger.LogWarning("UI_manager add_slider_listener: not Slider Component!");
return;
}
s.onValueChanged.AddListener(on_value_changle);
}
}
// UI管理类
public class UIMgr : UnitySingleton<UIMgr> {
public GameObject canvas;
private Transform uiBloodRoot = null;
public override void Awake() {
base.Awake();
this.canvas = GameObject.Find("Canvas");
if (this.canvas == null) {
Debugger.LogError("UI manager load Canvas failed!!!!!");
}
this.uiBloodRoot = this.canvas.transform.Find("UIBloodRoot");
}
public UICtrl ShowUIView(string name) {
string path = "GUI/UI_Prefabs/" + name + ".prefab";
GameObject ui_prefab = (GameObject)ResMgr.Instance.GetAssetCache<GameObject>(path);
GameObject ui_view = GameObject.Instantiate(ui_prefab);
ui_view.name = ui_prefab.name;
ui_view.transform.SetParent(this.canvas.transform, false);
int lastIndex = name.LastIndexOf("/");
if (lastIndex > 0) {
name = name.Substring(lastIndex + 1);
}
Type type = Type.GetType(name + "_UICtrl");
UICtrl ctrl = (UICtrl)ui_view.AddComponent(type);
return ctrl;
}
public GameObject ShowSubView(string name, GameObject parent = null) {
string path = "GUI/Prefabs/" + name + ".prefab";
GameObject ui_prefab = (GameObject)ResMgr.Instance.GetAssetCache<GameObject>(path);
GameObject ui_view = GameObject.Instantiate(ui_prefab);
ui_view.name = ui_prefab.name;
if (parent == null) {
parent = this.canvas;
}
ui_view.transform.SetParent(parent.transform, false);
return ui_view;
}
public void RemoveUIView(string name) {
int lastIndex = name.LastIndexOf("/");
if (lastIndex > 0) {
name = name.Substring(lastIndex + 1);
}
Transform view = this.canvas.transform.Find(name);
if (view) {
GameObject.DestroyImmediate(view.gameObject);
}
}
public void RemoveAllViews() {
List<Transform> children = new List<Transform>();
foreach (Transform t in this.canvas.transform) {
if (t.gameObject.name == "UIBloodRoot") {
continue;
}
children.Add(t);
}
for (int i = 0; i < children.Count; i++) {
GameObject.DestroyImmediate(children[0].gameObject);
}
}
}
网络模块
网络连接管理,拆包封包
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
enum State{
Disconnect = 0,
Connecting = 1,
Connected = 2,
}
public class NetMgr : UnitySingleton<NetMgr>
{
// tcp
[SerializeField]
private string server_ip = "127.0.0.1";
[SerializeField]
private int port = 6080;
private Socket client_socket = null;
private bool is_connect = false;
private Thread recv_thread = null;
private const int RECV_LEN = 8192;
private byte[] recv_buf = new byte[RECV_LEN];
private int recved;
private byte[] long_pkg = null;
private int long_pkg_size = 0;
// end
private State state = State.Disconnect;
// 接收消息的队列
private Queue<cmd_msg> net_events = new Queue<cmd_msg>();
public delegate void net_message_handler(cmd_msg msg);
// 消息事件管理
private Dictionary<int, net_message_handler> event_listeners = new Dictionary<int, net_message_handler>();
void Start () {
this.state = State.Disconnect;
this.connect_to_server();
}
void OnDestroy() {
this.close();
}
void OnApplicaitonQuit() {
this.close();
}
void Update () {
// 如果失去连接,重新连接
if (this.state == State.Disconnect) {
this.connect_to_server();
return;
}
// 从队列中推出事件,调用对应的委托事件
lock (this.net_events) {
while (this.net_events.Count > 0) {
cmd_msg msg = this.net_events.Dequeue();
if (this.event_listeners.ContainsKey(msg.stype)) {
this.event_listeners[msg.stype](msg);
}
}
}
}
void on_conntect_timeout() {
}
void on_connect_error(string err) {
this.state = State.Disconnect;
}
void connect_to_server() {
if (this.state != State.Disconnect) {
return;
}
this.state = State.Connecting;
try {
this.client_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(this.server_ip);
IPEndPoint ipEndpoint = new IPEndPoint(ipAddress, this.port);
this.client_socket.BeginConnect(ipEndpoint, new AsyncCallback(this.on_connected), this.client_socket);
}
catch (System.Exception e) {
Debug.Log(e.ToString());
this.on_connect_error(e.ToString());
}
}
void on_recv_cmd(byte[] data, int start, int data_len) {
cmd_msg msg;
proto_man.unpack_cmd_msg(data, start, data_len, out msg);
if (msg != null) {
lock (this.net_events) { // recv thread
this.net_events.Enqueue(msg);
}
}
}
void on_recv_tcp_data() {
byte[] pkg_data = (this.long_pkg != null) ? this.long_pkg : this.recv_buf;
while (this.recved > 0) {
int pkg_size = 0;
int head_size = 0;
if (!tcp_packer.read_header(pkg_data, this.recved, out pkg_size, out head_size)) {
break;
}
if (this.recved < pkg_size) {
break;
}
int raw_data_start = head_size;
int raw_data_len = pkg_size - head_size;
on_recv_cmd(pkg_data, raw_data_start, raw_data_len);
if (this.recved > pkg_size) {
this.recv_buf = new byte[RECV_LEN];
Array.Copy(pkg_data, pkg_size, this.recv_buf, 0, this.recved - pkg_size);
pkg_data = this.recv_buf;
}
this.recved -= pkg_size;
if (this.recved == 0 && this.long_pkg != null) {
this.long_pkg = null;
this.long_pkg_size = 0;
}
}
}
void thread_recv_worker() {
if (this.is_connect == false) {
return;
}
while (true) {
if (!this.client_socket.Connected) {
break;
}
try
{
int recv_len = 0;
if (this.recved < RECV_LEN) {
recv_len = this.client_socket.Receive(this.recv_buf, this.recved, RECV_LEN - this.recved, SocketFlags.None);
}
else {
if (this.long_pkg == null) {
int pkg_size;
int head_size;
tcp_packer.read_header(this.recv_buf, this.recved, out pkg_size, out head_size);
this.long_pkg_size = pkg_size;
this.long_pkg = new byte[pkg_size];
Array.Copy(this.recv_buf, 0, this.long_pkg, 0, this.recved);
}
recv_len = this.client_socket.Receive(this.long_pkg, this.recved, this.long_pkg_size - this.recved, SocketFlags.None);
}
if (recv_len > 0) {
this.recved += recv_len;
this.on_recv_tcp_data();
}
}
catch (System.Exception e) {
Debug.Log(e.ToString());
if (this.client_socket != null && this.client_socket.Connected) {
if(this.client_socket.Connected) {
this.client_socket.Disconnect(true);
}
// this.client_socket.Shutdown(SocketShutdown.Both);
this.client_socket.Close();
}
this.client_socket = null;
this.is_connect = false;
this.state = State.Disconnect;
break;
}
}
Debugger.Log("exit recv thread");
}
void on_connected(IAsyncResult iar) {
try {
Socket client = (Socket)iar.AsyncState;
client.EndConnect(iar);
this.state = State.Connected;
this.is_connect = true;
this.recv_thread = new Thread(new ThreadStart(this.thread_recv_worker));
this.recv_thread.Start();
Debug.Log("connect to server success" + this.server_ip + ":" + this.port + "!");
}
catch (System.Exception e) {
Debug.Log(e.ToString());
this.on_connect_error(e.ToString());
this.is_connect = false;
}
}
void close() {
if (!this.is_connect) {
return;
}
this.is_connect = false;
this.state = State.Disconnect;
// abort recv thread
if (this.recv_thread != null) {
this.recv_thread.Interrupt();
this.recv_thread.Abort();
this.recv_thread = null;
}
// end
if (this.client_socket != null && this.client_socket.Connected) {
this.client_socket.Close();
this.client_socket = null;
}
}
private void on_send_data(IAsyncResult iar)
{
try
{
Socket client = (Socket)iar.AsyncState;
client.EndSend(iar);
}
catch (System.Exception e)
{
Debug.Log(e.ToString());
}
}
public void send_protobuf_cmd(int stype, int ctype, ProtoBuf.IExtensible body) {
byte[] cmd_data = proto_man.pack_protobuf_cmd(stype, ctype, body);
if (cmd_data == null) {
return;
}
byte[]tcp_pkg = tcp_packer.pack(cmd_data);
// end
this.client_socket.BeginSend(tcp_pkg, 0, tcp_pkg.Length, SocketFlags.None, new AsyncCallback(this.on_send_data), this.client_socket);
// this.client_socket.Send(tcp_pkg);
// end
}
public void send_json_cmd(int stype, int ctype, string json_body)
{
byte[] cmd_data = proto_man.pack_json_cmd(stype, ctype, json_body);
if (cmd_data == null) {
return;
}
byte[] tcp_pkg = tcp_packer.pack(cmd_data);
// end
this.client_socket.BeginSend(tcp_pkg, 0, tcp_pkg.Length, SocketFlags.None, new AsyncCallback(this.on_send_data), this.client_socket);
// end
}
public void add_service_listener(int stype, net_message_handler handler) {
if (this.event_listeners.ContainsKey(stype)) {
this.event_listeners[stype] += handler;
}
else {
this.event_listeners.Add(stype, handler);
}
}
public void remove_service_listener(int stype, net_message_handler handler) {
if (!this.event_listeners.ContainsKey(stype)) {
return;
}
this.event_listeners[stype] -= handler;
if (this.event_listeners[stype] == null) {
this.event_listeners.Remove(stype);
}
}
}
tcp封包函数
using System;
public class tcp_packer {
private const int HEADER_SIZE = 2;
public static byte[] pack(byte[] cmd_data) {
int len = cmd_data.Length;
if (len > 65535 - 2) {
return null;
}
int cmd_len = len + HEADER_SIZE;
byte[] cmd = new byte[cmd_len];
data_viewer.write_ushort_le(cmd, 0, (ushort)cmd_len);
data_viewer.write_bytes(cmd, HEADER_SIZE, cmd_data);
return cmd;
}
public static bool read_header(byte[] data, int data_len, out int pkg_size, out int head_size) {
pkg_size = 0;
head_size = 0;
if (data_len < 2) {
return false;
}
head_size = 2;
pkg_size = (data[0] | (data[1] << 8));
return true;
}
}
协议的封包拆包
using System;
using System.IO;
using System.Text;
using ProtoBuf;
public class cmd_msg {
public int stype;
public int ctype;
public byte[] body; // protobuf, utf8 string json byte;
}
public class proto_man {
private const int HEADER_SIZE = 8; // 2 stype, 2 ctype, 4utag, msg--> body;
private static byte[] protobuf_serializer(ProtoBuf.IExtensible data)
{
using (MemoryStream m = new MemoryStream())
{
byte[] buffer = null;
Serializer.Serialize(m, data);
m.Position = 0;
int length = (int)m.Length;
buffer = new byte[length];
m.Read(buffer, 0, length);
return buffer;
}
}
public static byte[] pack_protobuf_cmd(int stype, int ctype, ProtoBuf.IExtensible msg) {
int cmd_len = HEADER_SIZE;
byte[] cmd_body = null;
if (msg != null) {
cmd_body = protobuf_serializer(msg);
cmd_len += cmd_body.Length;
}
byte[] cmd = new byte[cmd_len];
// stype, ctype, utag(4保留), cmd_body
data_viewer.write_ushort_le(cmd, 0, (ushort)stype);
data_viewer.write_ushort_le(cmd, 2, (ushort)ctype);
if (cmd_body != null) {
data_viewer.write_bytes(cmd, HEADER_SIZE, cmd_body);
}
return cmd;
}
public static byte[] pack_json_cmd(int stype, int ctype, string json_msg) {
int cmd_len = HEADER_SIZE;
byte[] cmd_body = null;
if (json_msg.Length > 0) { // utf8
cmd_body = Encoding.UTF8.GetBytes(json_msg);
cmd_len += cmd_body.Length;
}
byte[] cmd = new byte[cmd_len];
// stype, ctype, utag(4保留), cmd_body
data_viewer.write_ushort_le(cmd, 0, (ushort)stype);
data_viewer.write_ushort_le(cmd, 2, (ushort)ctype);
if (cmd_body != null)
{
data_viewer.write_bytes(cmd, HEADER_SIZE, cmd_body);
}
return cmd;
}
public static bool unpack_cmd_msg(byte[] data, int start, int cmd_len, out cmd_msg msg) {
msg = new cmd_msg();
msg.stype = data_viewer.read_ushort_le(data, start);
msg.ctype = data_viewer.read_ushort_le(data, start + 2);
int body_len = cmd_len - HEADER_SIZE;
msg.body = new byte[body_len];
Array.Copy(data, start + HEADER_SIZE, msg.body, 0, body_len);
return true;
}
public static T protobuf_deserialize<T>(byte[] _data) {
using (MemoryStream m = new MemoryStream(_data))
{
return Serializer.Deserialize<T>(m);
}
}
}
读写数据
using System;
public class data_viewer {
public static void write_ushort_le(byte[] buf, int offset, ushort value) {
// value ---> byte[];
byte[] byte_value = BitConverter.GetBytes(value);
// 小尾,还是大尾?BitConvert 系统是小尾还是大尾;
if (!BitConverter.IsLittleEndian) {
Array.Reverse(byte_value);
}
Array.Copy(byte_value, 0, buf, offset, byte_value.Length);
}
public static void write_uint_le(byte[] buf, int offset, uint value) {
// value ---> byte[];
byte[] byte_value = BitConverter.GetBytes(value);
// 小尾,还是大尾?BitConvert 系统是小尾还是大尾;
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(byte_value);
}
Array.Copy(byte_value, 0, buf, offset, byte_value.Length);
}
public static void write_bytes(byte[] dst, int offset, byte[] value) {
Array.Copy(value, 0, dst, offset, value.Length);
}
public static ushort read_ushort_le(byte[] data, int offset) {
int ret = (data[offset] | (data[offset + 1] << 8));
return (ushort)ret;
}
}
异步请求资源(热更新模块,本地、远程通杀)
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// added by wsh @ 2017.12.22
/// 功能:资源异步请求,本地、远程通杀
/// 注意:
/// 1、Unity5.3官方建议用UnityWebRequest取代WWW:https://unity3d.com/cn/learn/tutorials/topics/best-practices/assetbundle-fundamentals?playlist=30089
/// 2、这里还是采用WWW,因为UnityWebRequest的Bug无数:
/// 1)Unity5.3.5:http://blog.csdn.net/st75033562/article/details/52411197
/// 2)Unity5.5:https://bitbucket.org/Unity-Technologies/assetbundledemo/pull-requests/25/feature-unitywebrequest/diff#comment-None
/// 3)还有各个版本发行说明中关于UnityWebRequest的修复,如Unity5.4.1(5.4全系列版本基本都有修复这个API的Bug):https://unity3d.com/cn/unity/whats-new/unity-5.4.1
/// 4)此外对于LZMA压缩,采用UnityWebRequest好处在于节省内存,性能上并不比WWW优越:https://docs.unity3d.com/530/Documentation/Manual/AssetBundleCompression.html
/// 3、LoadFromFile(Async)在Unity5.4以上支持streamingAsset目录加载资源,5.3.7和5.4.3以后支持LAMZ压缩,但是没法加载非Assetbundle资源
/// 4、另外,虽然LoadFromFile(Async)是加载ab最快的API,但是会延缓Asset加载的时间(读磁盘),如果ab尽量预加载,不考虑内存敏感问题,这个API意义就不大
/// </summary>
namespace AssetBundles
{
public class ResourceWebRequester : ResourceAsyncOperation
{
static Queue<ResourceWebRequester> pool = new Queue<ResourceWebRequester>();
static int sequence = 0;
// protected WWW www = null;
protected bool isOver = false;
UnityEngine.Networking.UnityWebRequest www_req = null;
public static ResourceWebRequester Get()
{
if (pool.Count > 0)
{
return pool.Dequeue();
}
else
{
return new ResourceWebRequester(++sequence);
}
}
public static void Recycle(ResourceWebRequester creater)
{
pool.Enqueue(creater);
}
public ResourceWebRequester(int sequence)
{
Sequence = sequence;
}
public void Init(string name, string url, bool noCache = false)
{
assetbundleName = name;
this.url = url;
this.noCache = noCache;
// www = null;
www_req = null;
isOver = false;
}
public int Sequence
{
get;
protected set;
}
public bool noCache
{
get;
protected set;
}
public string assetbundleName
{
get;
protected set;
}
public string url
{
get;
protected set;
}
public AssetBundle assetbundle
{
get
{
// return www.assetBundle;
return AssetBundle.LoadFromMemory(www_req.downloadHandler.data);
}
}
public byte[] bytes
{
get
{
// return www.bytes;
return www_req.downloadHandler.data;
}
}
public string text
{
get
{
// return www.text;
return www_req.downloadHandler.text;
}
}
public string error
{
get
{
// 注意:不能直接判空
// 详见:https://docs.unity3d.com/530/Documentation/ScriptReference/WWW-error.html
// return string.IsNullOrEmpty(www.error) ? null : www.error;
return string.IsNullOrEmpty(www_req.error) ? null : www_req.error;
}
}
public override bool IsDone()
{
return isOver;
}
public void Start()
{
// www = new WWW(url);
www_req = UnityEngine.Networking.UnityWebRequest.Get(url);
// if (www == null) {
if (www_req == null) {
Debug.LogError("New www failed!!!");
isOver = true;
}
else
{
Debug.Log("Downloading : " + url);
www_req.SendWebRequest();
}
}
public override float Progress()
{
if (isDone)
{
return 1.0f;
}
// return www != null ? www.progress : 0f;
return www_req != null ? www_req.downloadProgress : 0f;
}
public override void Update()
{
if (isDone)
{
return;
}
// isOver = www != null && (www.isDone || !string.IsNullOrEmpty(www.error));
isOver = www_req != null && (www_req.isDone || !string.IsNullOrEmpty(www_req.error));
if (!isOver)
{
return;
}
// if (www != null && !string.IsNullOrEmpty(www.error))
if (www_req != null && !string.IsNullOrEmpty(www_req.error))
{
// Debug.LogError(www.error);
Debug.LogError(www_req.error);
}
}
public override void Dispose()
{
/*if (www != null)
{
www.Dispose();
www = null;
}*/
if (www_req != null) {
www_req.Dispose();
www_req = null;
}
Recycle(this);
}
}
}
定时器模块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
class TimerNode
{
public TimerMgr.TimerHandler callback;
public float duration; // 定时器触发的时间间隔;
public float delay; // 第一次触发要隔多少时间;
public int repeat; // 你要触发的次数;
public float passedTime; // 这个Timer过去的时间;
public object param; // 用户要传的参数
public bool isRemoved; // 是否已经删除了
public int timerId; // 标识这个timer的唯一Id号;
}
public class TimerMgr : UnitySingleton<TimerMgr>
{
public delegate void TimerHandler(object param);
private int autoIncId = 1;
private Dictionary<int, TimerNode> timers = null;
private List<TimerNode> removeTimers = null;
private List<TimerNode> newAddTimers = null;
// 初始化的入口
public void Init()
{
this.timers = new Dictionary<int, TimerNode>();
this.autoIncId = 1;
this.removeTimers = new List<TimerNode>();
this.newAddTimers = new List<TimerNode>();
}
private void Update()
{
float dt = Time.deltaTime;
// 把新加进来的放入到我们的表里面来
for (int i = 0; i < this.newAddTimers.Count; i++) {
this.timers.Add(this.newAddTimers[i].timerId, this.newAddTimers[i]);
}
this.newAddTimers.Clear();
// end
foreach (TimerNode timer in this.timers.Values) {
if (timer.isRemoved) {
this.removeTimers.Add(timer);
continue;
}
timer.passedTime += dt;
if (timer.passedTime >= (timer.delay + timer.duration)) {
// 做一次触发
timer.callback(timer.param);
timer.repeat--;
timer.passedTime -= (timer.delay + timer.duration);
timer.delay = 0; // 很重要;
if (timer.repeat == 0)
{ // 触发次数结束,我们是不是要删除这个Timer;
timer.isRemoved = true;
this.removeTimers.Add(timer);
}
// end
}
}
// 结束以后,清理掉要删除的Timer;
for (int i = 0; i < this.removeTimers.Count; i++) {
this.timers.Remove(this.removeTimers[i].timerId);
}
this.removeTimers.Clear();
// end
}
public int ScheduleOnce(TimerHandler func, float delay)
{
return this.Schedule(func, 1, 0, delay);
}
public int ScheduleOnce(TimerHandler func, object param, float delay)
{
return this.Schedule(func, param, 1, delay);
}
// [repeat < 0 or repeat == 0 表示的是无限触发]
public int Schedule(TimerHandler func, int repeat, float duration, float delay = 0.0f)
{
return Schedule(func, null, repeat, duration, delay);
}
// [repeat < 0 or repeat == 0 表示的是无限触发]
public int Schedule(TimerHandler func, object param, int repeat, float duration, float delay = 0.0f)
{
TimerNode timer = new TimerNode();
timer.callback = func;
timer.param = param;
timer.repeat = repeat;
timer.duration = duration;
timer.delay = delay;
timer.passedTime = timer.duration;
timer.isRemoved = false;
timer.timerId = this.autoIncId;
this.autoIncId++;
// this.timers.Add(timer.timerId, timer);
this.newAddTimers.Add(timer);
return timer.timerId;
}
public void Unschedule(int timerId)
{
if (!this.timers.ContainsKey(timerId)) {
return;
}
TimerNode timer = this.timers[timerId];
timer.isRemoved = true;
}
}
Excel表格处理模块
把Excel处理成CSV文件
Protobuf协议生成
Utils工具模块
检查版本
using UnityEngine;
/// <summary>
/// added by wsh @ 2018.01.04
/// 功能:构建相关配置和通用函数
/// </summary>
public class BuildUtils
{
public const string PackageNameFileName = "package_name.bytes";
public const string AppVersionFileName = "app_version.bytes";
public const string ResVersionFileName = "res_version.bytes";
public const string NoticeVersionFileName = "notice_version.bytes";
public const string AssetBundlesSizeFileName = "assetbundls_size.bytes";
public const string UpdateNoticeFileName = "update_notice.bytes";
public static bool CheckIsNewVersion(string sourceVersion, string targetVersion)
{
string[] sVerList = sourceVersion.Split('.');
string[] tVerList = targetVersion.Split('.');
if (sVerList.Length >= 3 && tVerList.Length >= 3)
{
try
{
int sV0 = int.Parse(sVerList[0]);
int sV1 = int.Parse(sVerList[1]);
int sV2 = int.Parse(sVerList[2]);
int tV0 = int.Parse(tVerList[0]);
int tV1 = int.Parse(tVerList[1]);
int tV2 = int.Parse(tVerList[2]);
if (tV0 > sV0)
{
return true;
}
else if (tV0 < sV0)
{
return false;
}
if (tV1 > sV1)
{
return true;
}
else if (tV1 < sV1)
{
return false;
}
if (tV2 > sV2)
{
return true;
}
else
{
return false;
}
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("parse version error. clientversion: {0} serverversion: {1}\n {2}\n{3}", sourceVersion, targetVersion, ex.Message, ex.StackTrace));
return false;
}
}
return false;
}
}
数据转换
using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
public class DataUtils
{
public static void CopyBytes(byte[] copyTo, int offsetTo, byte[] copyFrom, int offsetFrom, int count)
{
Array.Copy(copyFrom, offsetFrom, copyTo, offsetTo, count);
}
public static byte[] StringToBytes(string str)
{
return System.Text.Encoding.Default.GetBytes(str);
}
public static byte[] StringToUTFBytes(string str)
{
return System.Text.Encoding.UTF8.GetBytes(str);
}
public static string BytesToString(byte[] bytes)
{
return System.Text.Encoding.Default.GetString(bytes).Trim();
}
public static Hashtable HttpGetInfo(string info)
{
if (string.IsNullOrEmpty(info))
{
return null;
}
Hashtable table = new Hashtable();
string[] paramList = info.Split('&');
for (int i = 0; i < paramList.Length; i++)
{
string[] keyAndValue = paramList[i].Split('=');
if (keyAndValue.Length >= 2)
{
if (!table.ContainsKey(keyAndValue[0]))
{
table.Add(keyAndValue[0], keyAndValue[1]);
}
}
}
return table;
}
}
文件和路径操作
using UnityEngine;
using System.IO;
using System.Linq;
using System;
using System.Collections.Generic;
/// <summary>
/// added by wsh @ 2017.12.25
/// 功能:通用静态方法
/// </summary>
public class GameUtility
{
public const string AssetsFolderName = "Assets";
public static string FormatToUnityPath(string path)
{
return path.Replace("\\", "/");
}
public static string FormatToSysFilePath(string path)
{
return path.Replace("/", "\\");
}
public static string FullPathToAssetPath(string full_path)
{
full_path = FormatToUnityPath(full_path);
if (!full_path.StartsWith(Application.dataPath))
{
return null;
}
string ret_path = full_path.Replace(Application.dataPath, "");
return AssetsFolderName + ret_path;
}
public static string GetFileExtension(string path)
{
return Path.GetExtension(path).ToLower();
}
public static string[] GetSpecifyFilesInFolder(string path, string[] extensions = null, bool exclude = false)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
if (extensions == null)
{
return Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
}
else if (exclude)
{
return Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
.Where(f => !extensions.Contains(GetFileExtension(f))).ToArray();
}
else
{
return Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
.Where(f => extensions.Contains(GetFileExtension(f))).ToArray();
}
}
public static string[] GetSpecifyFilesInFolder(string path, string pattern)
{
if (string.IsNullOrEmpty(path))
{
return null;
}
return Directory.GetFiles(path, pattern, SearchOption.AllDirectories);
}
public static string[] GetAllFilesInFolder(string path)
{
return GetSpecifyFilesInFolder(path);
}
public static string[] GetAllDirsInFolder(string path)
{
return Directory.GetDirectories(path, "*", SearchOption.AllDirectories);
}
public static void CheckFileAndCreateDirWhenNeeded(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return;
}
FileInfo file_info = new FileInfo(filePath);
DirectoryInfo dir_info = file_info.Directory;
if (!dir_info.Exists)
{
Directory.CreateDirectory(dir_info.FullName);
}
}
public static void CheckDirAndCreateWhenNeeded(string folderPath)
{
if (string.IsNullOrEmpty(folderPath))
{
return;
}
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
}
public static bool SafeWriteAllBytes(string outFile, byte[] outBytes)
{
try
{
if (string.IsNullOrEmpty(outFile))
{
return false;
}
CheckFileAndCreateDirWhenNeeded(outFile);
if (File.Exists(outFile))
{
File.SetAttributes(outFile, FileAttributes.Normal);
}
File.WriteAllBytes(outFile, outBytes);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeWriteAllBytes failed! path = {0} with err = {1}", outFile, ex.Message));
return false;
}
}
public static bool SafeWriteAllLines(string outFile, string[] outLines)
{
try
{
if (string.IsNullOrEmpty(outFile))
{
return false;
}
CheckFileAndCreateDirWhenNeeded(outFile);
if (File.Exists(outFile))
{
File.SetAttributes(outFile, FileAttributes.Normal);
}
File.WriteAllLines(outFile, outLines);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeWriteAllLines failed! path = {0} with err = {1}", outFile, ex.Message));
return false;
}
}
public static bool SafeWriteAllText(string outFile, string text)
{
try
{
if (string.IsNullOrEmpty(outFile))
{
return false;
}
CheckFileAndCreateDirWhenNeeded(outFile);
if (File.Exists(outFile))
{
File.SetAttributes(outFile, FileAttributes.Normal);
}
File.WriteAllText(outFile, text);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeWriteAllText failed! path = {0} with err = {1}", outFile, ex.Message));
return false;
}
}
public static byte[] SafeReadAllBytes(string inFile)
{
try
{
if (string.IsNullOrEmpty(inFile))
{
return null;
}
if (!File.Exists(inFile))
{
return null;
}
File.SetAttributes(inFile, FileAttributes.Normal);
return File.ReadAllBytes(inFile);
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeReadAllBytes failed! path = {0} with err = {1}", inFile, ex.Message));
return null;
}
}
public static string[] SafeReadAllLines(string inFile)
{
try
{
if (string.IsNullOrEmpty(inFile))
{
return null;
}
if (!File.Exists(inFile))
{
return null;
}
File.SetAttributes(inFile, FileAttributes.Normal);
return File.ReadAllLines(inFile);
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeReadAllLines failed! path = {0} with err = {1}", inFile, ex.Message));
return null;
}
}
public static string SafeReadAllText(string inFile)
{
try
{
if (string.IsNullOrEmpty(inFile))
{
return null;
}
if (!File.Exists(inFile))
{
return null;
}
File.SetAttributes(inFile, FileAttributes.Normal);
return File.ReadAllText(inFile);
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeReadAllText failed! path = {0} with err = {1}", inFile, ex.Message));
return null;
}
}
public static void DeleteDirectory(string dirPath)
{
string[] files = Directory.GetFiles(dirPath);
string[] dirs = Directory.GetDirectories(dirPath);
foreach (string file in files)
{
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
foreach (string dir in dirs)
{
DeleteDirectory(dir);
}
Directory.Delete(dirPath, false);
}
public static bool SafeClearDir(string folderPath)
{
try
{
if (string.IsNullOrEmpty(folderPath))
{
return true;
}
if (Directory.Exists(folderPath))
{
DeleteDirectory(folderPath);
}
Directory.CreateDirectory(folderPath);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeClearDir failed! path = {0} with err = {1}", folderPath, ex.Message));
return false;
}
}
public static bool SafeDeleteDir(string folderPath)
{
try
{
if (string.IsNullOrEmpty(folderPath))
{
return true;
}
if (Directory.Exists(folderPath))
{
DeleteDirectory(folderPath);
}
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeDeleteDir failed! path = {0} with err: {1}", folderPath, ex.Message));
return false;
}
}
public static bool SafeDeleteFile(string filePath)
{
try
{
if (string.IsNullOrEmpty(filePath))
{
return true;
}
if (!File.Exists(filePath))
{
return true;
}
File.SetAttributes(filePath, FileAttributes.Normal);
File.Delete(filePath);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeDeleteFile failed! path = {0} with err: {1}", filePath, ex.Message));
return false;
}
}
public static bool SafeRenameFile(string sourceFileName, string destFileName)
{
try
{
if (string.IsNullOrEmpty(sourceFileName))
{
return false;
}
if (!File.Exists(sourceFileName))
{
return true;
}
File.SetAttributes(sourceFileName, FileAttributes.Normal);
File.Move(sourceFileName, destFileName);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeRenameFile failed! path = {0} with err: {1}", sourceFileName, ex.Message));
return false;
}
}
public static bool SafeCopyFile(string fromFile, string toFile)
{
try
{
if (string.IsNullOrEmpty(fromFile))
{
return false;
}
if (!File.Exists(fromFile))
{
return false;
}
CheckFileAndCreateDirWhenNeeded(toFile);
if (File.Exists(toFile))
{
File.SetAttributes(toFile, FileAttributes.Normal);
}
File.Copy(fromFile, toFile, true);
return true;
}
catch (System.Exception ex)
{
Debug.LogError(string.Format("SafeCopyFile failed! formFile = {0}, toFile = {1}, with err = {2}",
fromFile, toFile, ex.Message));
return false;
}
}
}
#if UNITY_EDITOR
public static class GameUtilityExporter
{
public static List<Type> LuaCallCSharp = new List<Type>(){
typeof(GameUtility),
};
}
#endif
其他通用
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using UnityEngine;
public class utils {
public static string rand_str(int len) {
byte[] b = new byte[4];
new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
System.Random r = new System.Random(System.BitConverter.ToInt32(b, 0));
string str = null;
str += "0123456789";
str += "abcdefghijklmnopqrstuvwxyz";
str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string s = null;
for (int i = 0; i < len; i++)
{
s += str.Substring(r.Next(0, str.Length - 1), 1);
}
return s;
}
public static string md5(string str) {
string cl = str;
StringBuilder md5_builder = new StringBuilder();
MD5 md5 = MD5.Create();//实例化一个md5对像
// 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择
byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl));
// 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得
for (int i = 0; i < s.Length; i++)
{
// 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符
md5_builder.Append(s[i].ToString("X2"));
//pwd = pwd + s[i].ToString("X");
}
return md5_builder.ToString();
}
public static void DestroyAllChildren(Transform parent) {
int count = parent.childCount;
for (int i = 0; i < count; i++) {
Transform child = parent.GetChild(0);
GameObject.DestroyImmediate(child.gameObject);
}
}
}
启动脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using AssetBundles;
using GameChannel;
public class GameLanch : UnitySingleton<GameLanch> {
public bool isUseSocket = false;
public override void Awake() {
// 初始化游戏框架
this.gameObject.AddComponent<TimerMgr>().Init();
this.gameObject.AddComponent<ResMgr>();
this.gameObject.AddComponent<SoundMgr>();
this.gameObject.AddComponent<EventMgr>();
EventMgr.Instance.init();
this.gameObject.AddComponent<UIMgr>();
if (this.isUseSocket) {
this.gameObject.AddComponent<NetMgr>();
}
#if RELEASE_BUILD
#else
this.gameObject.AddComponent<show_fps>();
if (Application.platform == RuntimePlatform.Android ||
Application.platform == RuntimePlatform.IPhonePlayer)
{
this.gameObject.AddComponent<DebugManager>();
}
#endif
// end
this.gameObject.AddComponent<GameApp>().Init();
}
IEnumerator InitPackageName()
{
#if UNITY_EDITOR
if (AssetBundleConfig.IsEditorMode)
{
yield break;
}
#endif
var packageNameRequest = AssetBundleManager.Instance.RequestAssetFileAsync(BuildUtils.PackageNameFileName);
yield return packageNameRequest; // 中断当前协程,直到请求结束;
var packageName = packageNameRequest.text;
packageNameRequest.Dispose(); // 释放请求;
AssetBundleManager.ManifestBundleName = packageName; // 包名字;
ChannelManager.Instance.Init(packageName);
Debug.Log(string.Format("packageName = {0}", packageName));
yield break;
}
IEnumerator CheckAndDownload() {
yield break;
}
IEnumerator GameStart() {
var start = DateTime.Now;
yield return InitPackageName();
Debug.Log(string.Format("InitPackageName use {0}ms", (DateTime.Now - start).Milliseconds));
// 启动资源管理模块
start = DateTime.Now;
yield return AssetBundleManager.Instance.Initialize();
Debug.Log(string.Format("AssetBundleManager Initialize use {0}ms", (DateTime.Now - start).Milliseconds));
// 启动检测更新
yield return CheckAndDownload();
// end
GameApp.Instance.EnterGame();
yield break;
}
void Start() {
this.StartCoroutine(this.GameStart());
}
}