using System; using System.Collections.Generic; using System.IO; using UnityEngine; using FairyGUI.Utils; #if UNITY_EDITOR using UnityEditor; #endif namespace FairyGUI { /// /// A UI Package contains a description file and some texture,sound assets. /// public class UIPackage { /// /// Package id. It is generated by the Editor, or set by customId. /// public string id { get; private set; } /// /// Package name. /// public string name { get; private set; } /// /// The path relative to the resources folder. /// public string assetPath { get; private set; } /// /// /// /// public delegate void CreateObjectCallback(GObject result); List _items; Dictionary _itemsById; Dictionary _itemsByName; string _customId; bool _resourceMode; class AtlasSprite { public PackageItem atlas; public Rect rect = new Rect(); public bool rotated; } Dictionary _sprites; static Dictionary _packageInstById = new Dictionary(); static Dictionary _packageInstByName = new Dictionary(); static List _packageList = new List(); internal static int _constructing; public const string URL_PREFIX = "ui://"; #if UNITY_EDITOR && !PACK List _assetMap = new List(); #endif public UIPackage() { _items = new List(); _itemsById = new Dictionary(); _itemsByName = new Dictionary(); _sprites = new Dictionary(); } /// /// Return a UIPackage with a certain id. /// /// ID of the package. /// UIPackage public static UIPackage GetById(string id) { UIPackage pkg; if (_packageInstById.TryGetValue(id, out pkg)) return pkg; else return null; } /// /// Return a UIPackage with a certain name. /// /// Name of the package. /// UIPackage public static UIPackage GetByName(string name) { UIPackage pkg; if (_packageInstByName.TryGetValue(name, out pkg)) return pkg; else return null; } UnityEngine.Object _loadFunc(string name, string extension, System.Type type, out DestroyMethod destroyMethod) { destroyMethod = DestroyMethod.Unload; UnityEngine.Object obj = null; var path = name + extension; if (_resourceMode) { obj = Resources.Load(name, type); } else { obj = taurus.unity.ResourcesManager.LoadObject(path, type); if (obj == null) { obj = Resources.Load(name, type); } } #if UNITY_EDITOR && !PACK if (obj != null) { _assetMap.Add(path); } #endif return obj; } /// /// Add a UI package from a path relative to Unity Resources path. /// /// Path relative to Unity Resources path. /// UIPackage public static UIPackage AddPackage(string descFilePath) { return AddPackage(descFilePath, false); } /// /// 使用自定义的加载方式载入一个包。 /// /// 包资源路径。 /// 载入函数 /// public static UIPackage AddPackage(string assetPath, bool _resourceMode) { if (_packageInstById.ContainsKey(assetPath)) return _packageInstById[assetPath]; DestroyMethod dm; UIPackage pkg = new UIPackage(); pkg._resourceMode = _resourceMode; TextAsset asset = (TextAsset)pkg._loadFunc(assetPath + "_fui", ".bytes", typeof(TextAsset), out dm); if (asset == null) { if (Application.isPlaying) throw new Exception("FairyGUI: Cannot load ui package in '" + assetPath + "'"); else Debug.LogWarning("FairyGUI: Cannot load ui package in '" + assetPath + "'"); } ByteBuffer buffer = new ByteBuffer(asset.bytes); pkg.assetPath = assetPath; if (!pkg.LoadPackage(buffer, assetPath, assetPath)) return null; _packageInstById[pkg.id] = pkg; _packageInstByName[pkg.name] = pkg; _packageInstById[assetPath] = pkg; _packageList.Add(pkg); return pkg; } /// /// Remove a package. All resources in this package will be disposed. /// /// /// public static void RemovePackage(string packageIdOrName) { UIPackage pkg = null; if (!_packageInstById.TryGetValue(packageIdOrName, out pkg)) { if (!_packageInstByName.TryGetValue(packageIdOrName, out pkg)) { //Debugger.LogError("FairyGUI: '" + packageIdOrName + "' is not a valid package id or name."); return; } } pkg.Dispose(); #if UNITY_EDITOR && !PACK foreach (string path in pkg._assetMap) { taurus.unity.ResourcesManager.UnLoad(path); } pkg._assetMap.Clear(); #else taurus.unity.ResourcesManager.UnLoad(packageIdOrName + "_fui.bytes"); #endif _packageInstById.Remove(pkg.id); if (pkg._customId != null) _packageInstById.Remove(pkg._customId); if (pkg.assetPath != null) _packageInstById.Remove(pkg.assetPath); _packageInstByName.Remove(pkg.name); _packageList.Remove(pkg); } /// /// /// public static void RemoveAllPackages() { if (_packageInstById.Count > 0) { UIPackage[] pkgs = _packageList.ToArray(); foreach (UIPackage pkg in pkgs) { pkg.Dispose(); } } _packageList.Clear(); _packageInstById.Clear(); _packageInstByName.Clear(); } /// /// /// /// public static List GetPackages() { return _packageList; } /// /// Create a UI object. /// /// Package name. /// Resource name. /// A UI object. public static GObject CreateObject(string pkgName, string resName) { UIPackage pkg = GetByName(pkgName); if (pkg != null) return pkg.CreateObject(resName); else return null; } /// /// Create a UI object. /// /// Package name. /// Resource name. /// Custom implementation of this object. /// A UI object. public static GObject CreateObject(string pkgName, string resName, System.Type userClass) { UIPackage pkg = GetByName(pkgName); if (pkg != null) return pkg.CreateObject(resName, userClass); else return null; } /// /// Create a UI object. /// /// Resource url. /// A UI object. public static GObject CreateObjectFromURL(string url) { PackageItem pi = GetItemByURL(url); if (pi != null) return pi.owner.CreateObject(pi, null); else return null; } /// /// Create a UI object. /// /// Resource url. /// Custom implementation of this object. /// A UI object. public static GObject CreateObjectFromURL(string url, System.Type userClass) { PackageItem pi = GetItemByURL(url); if (pi != null) return pi.owner.CreateObject(pi, userClass); else return null; } public static void CreateObjectAsync(string pkgName, string resName, CreateObjectCallback callback) { UIPackage pkg = GetByName(pkgName); if (pkg != null) pkg.CreateObjectAsync(resName, callback); else Debug.LogError("FairyGUI: package not found - " + pkgName); } public static void CreateObjectFromURL(string url, CreateObjectCallback callback) { PackageItem pi = GetItemByURL(url); if (pi != null) AsyncCreationHelper.CreateObject(pi, callback); else Debug.LogError("FairyGUI: resource not found - " + url); } /// /// Get a asset with a certain name. /// /// Package name. /// Resource name. /// If resource is atlas, returns NTexture; If resource is sound, returns AudioClip. public static object GetItemAsset(string pkgName, string resName) { UIPackage pkg = GetByName(pkgName); if (pkg != null) return pkg.GetItemAsset(resName); else return null; } /// /// Get a asset with a certain name. /// /// Resource url. /// If resource is atlas, returns NTexture; If resource is sound, returns AudioClip. public static object GetItemAssetByURL(string url) { PackageItem item = GetItemByURL(url); if (item == null) return null; return item.owner.GetItemAsset(item); } /// /// Get url of an item in package. /// /// Package name. /// Resource name. /// Url. public static string GetItemURL(string pkgName, string resName) { UIPackage pkg = GetByName(pkgName); if (pkg == null) return null; PackageItem pi; if (!pkg._itemsByName.TryGetValue(resName, out pi)) return null; return URL_PREFIX + pkg.id + pi.id; } public static PackageItem GetItemByURL(string url) { if (url == null) return null; int pos1 = url.IndexOf("//"); if (pos1 == -1) return null; int pos2 = url.IndexOf('/', pos1 + 2); if (pos2 == -1) { if (url.Length > 13) { string pkgId = url.Substring(5, 8); UIPackage pkg = GetById(pkgId); if (pkg != null) { string srcId = url.Substring(13); return pkg.GetItem(srcId); } } } else { string pkgName = url.Substring(pos1 + 2, pos2 - pos1 - 2); UIPackage pkg = GetByName(pkgName); if (pkg != null) { string srcName = url.Substring(pos2 + 1); return pkg.GetItemByName(srcName); } } return null; } /// /// 将'ui://包名/组件名'转换为以内部id表达的url格式。如果传入的url本身就是内部id格式,则直接返回。 /// 同时这个方法还带格式检测,如果传入不正确的url,会返回null。 /// /// /// public static string NormalizeURL(string url) { if (url == null) return null; int pos1 = url.IndexOf("//"); if (pos1 == -1) return null; int pos2 = url.IndexOf('/', pos1 + 2); if (pos2 == -1) return url; else { string pkgName = url.Substring(pos1 + 2, pos2 - pos1 - 2); string srcName = url.Substring(pos2 + 1); return GetItemURL(pkgName, srcName); } } /// /// Set strings source. /// /// public static void SetStringsSource(XML source) { TranslationHelper.LoadFromXML(source); } /// /// Set a custom id for package, then you can use it in GetById. /// public string customId { get { return _customId; } set { if (_customId != null) _packageInstById.Remove(_customId); _customId = value; if (_customId != null) _packageInstById[_customId] = this; } } bool LoadPackage(ByteBuffer buffer, string packageSource, string assetNamePrefix) { if (buffer.ReadUint() != 0x46475549) { if (Application.isPlaying) throw new Exception("FairyGUI: old package format found in '" + packageSource + "'"); else { Debug.LogWarning("FairyGUI: old package format found in '" + packageSource + "'"); return false; } } buffer.version = buffer.ReadInt(); buffer.ReadBool(); //compressed id = buffer.ReadString(); name = buffer.ReadString(); if (_packageInstById.ContainsKey(id) && name != _packageInstById[id].name) { throw new Exception("FairyGUI: Package id conflicts, '" + name + "' and '" + _packageInstById[id].name + "'"); } buffer.Skip(20); int indexTablePos = buffer.position; int cnt; buffer.Seek(indexTablePos, 4); cnt = buffer.ReadInt(); string[] stringTable = new string[cnt]; for (int i = 0; i < cnt; i++) stringTable[i] = buffer.ReadString(); buffer.stringTable = stringTable; buffer.Seek(indexTablePos, 1); PackageItem pi; if (assetNamePrefix == null) assetNamePrefix = string.Empty; else if (assetNamePrefix.Length > 0) assetNamePrefix = assetNamePrefix + "_"; cnt = buffer.ReadShort(); for (int i = 0; i < cnt; i++) { int nextPos = buffer.ReadInt(); nextPos += buffer.position; pi = new PackageItem(); pi.owner = this; pi.type = (PackageItemType)buffer.ReadByte(); pi.id = buffer.ReadS(); pi.name = buffer.ReadS(); buffer.ReadS(); //path pi.file = buffer.ReadS(); pi.exported = buffer.ReadBool(); pi.width = buffer.ReadInt(); pi.height = buffer.ReadInt(); switch (pi.type) { case PackageItemType.Image: { pi.objectType = ObjectType.Image; int scaleOption = buffer.ReadByte(); if (scaleOption == 1) { Rect rect = new Rect(); rect.x = buffer.ReadInt(); rect.y = buffer.ReadInt(); rect.width = buffer.ReadInt(); rect.height = buffer.ReadInt(); pi.scale9Grid = rect; pi.tileGridIndice = buffer.ReadInt(); } else if (scaleOption == 2) pi.scaleByTile = true; buffer.ReadBool(); //smoothing break; } case PackageItemType.MovieClip: { buffer.ReadBool(); //smoothing pi.objectType = ObjectType.MovieClip; pi.rawData = buffer.ReadBuffer(); break; } case PackageItemType.Font: { pi.rawData = buffer.ReadBuffer(); break; } case PackageItemType.Component: { int extension = buffer.ReadByte(); if (extension > 0) pi.objectType = (ObjectType)extension; else pi.objectType = ObjectType.Component; pi.rawData = buffer.ReadBuffer(); UIObjectFactory.ResolvePackageItemExtension(pi); break; } case PackageItemType.Atlas: case PackageItemType.Sound: case PackageItemType.Misc: { pi.file = assetNamePrefix + pi.file; break; } } _items.Add(pi); _itemsById[pi.id] = pi; if (pi.name != null) _itemsByName[pi.name] = pi; buffer.position = nextPos; } buffer.Seek(indexTablePos, 2); cnt = buffer.ReadShort(); for (int i = 0; i < cnt; i++) { int nextPos = buffer.ReadShort(); nextPos += buffer.position; string itemId = buffer.ReadS(); pi = _itemsById[buffer.ReadS()]; AtlasSprite sprite = new AtlasSprite(); sprite.atlas = pi; sprite.rect.x = buffer.ReadInt(); sprite.rect.y = buffer.ReadInt(); sprite.rect.width = buffer.ReadInt(); sprite.rect.height = buffer.ReadInt(); sprite.rotated = buffer.ReadBool(); _sprites[itemId] = sprite; buffer.position = nextPos; } if (buffer.Seek(indexTablePos, 3)) { cnt = buffer.ReadShort(); for (int i = 0; i < cnt; i++) { int nextPos = buffer.ReadInt(); nextPos += buffer.position; if (_itemsById.TryGetValue(buffer.ReadS(), out pi)) { if (pi.type == PackageItemType.Image) { pi.pixelHitTestData = new PixelHitTestData(); pi.pixelHitTestData.Load(buffer); } } buffer.position = nextPos; } } if (!Application.isPlaying) _items.Sort(ComparePackageItem); return true; } static int ComparePackageItem(PackageItem p1, PackageItem p2) { if (p1.name != null && p2.name != null) return p1.name.CompareTo(p2.name); else return 0; } /// /// /// public void LoadAllAssets() { int cnt = _items.Count; for (int i = 0; i < cnt; i++) GetItemAsset(_items[i]); } /// /// /// public void UnloadAssets() { int cnt = _items.Count; for (int i = 0; i < cnt; i++) { PackageItem pi = _items[i]; if (pi.type == PackageItemType.Atlas) { if (pi.texture != null) pi.texture.Unload(); } else if (pi.type == PackageItemType.Sound) { if (pi.audioClip != null) pi.audioClip.Unload(); } } } /// /// /// public void ReloadAssets() { int cnt = _items.Count; for (int i = 0; i < cnt; i++) { PackageItem pi = _items[i]; if (pi.type == PackageItemType.Atlas) { if (pi.texture != null && pi.texture.nativeTexture == null) LoadAtlas(pi); } else if (pi.type == PackageItemType.Sound) { if (pi.audioClip != null && pi.audioClip.nativeClip == null) LoadSound(pi); } } } void Dispose() { int cnt = _items.Count; for (int i = 0; i < cnt; i++) { PackageItem pi = _items[i]; if (pi.type == PackageItemType.Atlas) { if (pi.texture != null) { pi.texture.Dispose(); pi.texture = null; } } else if (pi.type == PackageItemType.Sound) { if (pi.audioClip != null) { pi.audioClip.Unload(); pi.audioClip = null; } } } _items.Clear(); } /// /// /// /// /// public GObject CreateObject(string resName) { PackageItem pi; if (!_itemsByName.TryGetValue(resName, out pi)) { Debug.LogError("FairyGUI: resource not found - " + resName + " in " + this.name); return null; } return CreateObject(pi, null); } /// /// /// /// /// /// public GObject CreateObject(string resName, System.Type userClass) { PackageItem pi; if (!_itemsByName.TryGetValue(resName, out pi)) { Debug.LogError("FairyGUI: resource not found - " + resName + " in " + this.name); return null; } return CreateObject(pi, userClass); } public void CreateObjectAsync(string resName, CreateObjectCallback callback) { PackageItem pi; if (!_itemsByName.TryGetValue(resName, out pi)) { Debug.LogError("FairyGUI: resource not found - " + resName + " in " + this.name); return; } AsyncCreationHelper.CreateObject(pi, callback); } GObject CreateObject(PackageItem item, System.Type userClass) { Stats.LatestObjectCreation = 0; Stats.LatestGraphicsCreation = 0; GetItemAsset(item); GObject g = null; if (item.type == PackageItemType.Component) { if (userClass != null) g = (GComponent)Activator.CreateInstance(userClass); else g = UIObjectFactory.NewObject(item); } else g = UIObjectFactory.NewObject(item); if (g == null) return null; _constructing++; g.packageItem = item; g.ConstructFromResource(); _constructing--; return g; } /// /// /// /// /// public object GetItemAsset(string resName) { PackageItem pi; if (!_itemsByName.TryGetValue(resName, out pi)) { Debug.LogError("FairyGUI: Resource not found - " + resName + " in " + this.name); return null; } return GetItemAsset(pi); } public List GetItems() { return _items; } public PackageItem GetItem(string itemId) { PackageItem pi; if (_itemsById.TryGetValue(itemId, out pi)) return pi; else return null; } public PackageItem GetItemByName(string itemName) { PackageItem pi; if (_itemsByName.TryGetValue(itemName, out pi)) return pi; else return null; } public object GetItemAsset(PackageItem item) { switch (item.type) { case PackageItemType.Image: if (item.texture == null) LoadImage(item); return item.texture; case PackageItemType.Atlas: if (item.texture == null) LoadAtlas(item); return item.texture; case PackageItemType.Sound: if (item.audioClip == null) LoadSound(item); return item.audioClip; case PackageItemType.Font: if (item.bitmapFont == null) LoadFont(item); return item.bitmapFont; case PackageItemType.MovieClip: if (item.frames == null) LoadMovieClip(item); return item.frames; case PackageItemType.Component: return item.rawData; case PackageItemType.Misc: return LoadBinary(item); default: return null; } } void LoadAtlas(PackageItem item) { string ext = Path.GetExtension(item.file); string fileName = item.file.Substring(0, item.file.Length - ext.Length); Texture tex = null; Texture alphaTex = null; DestroyMethod dm; tex = (Texture)_loadFunc(fileName, ext, typeof(Texture), out dm); if (tex == null) Debug.LogWarning("FairyGUI: texture '" + item.file + "' not found in " + this.name); else if (!(tex is Texture2D)) { Debug.LogWarning("FairyGUI: settings for '" + item.file + "' is wrong! Correct values are: (Texture Type=Default, Texture Shape=2D)"); tex = null; } else { if (((Texture2D)tex).mipmapCount > 1) Debug.LogWarning("FairyGUI: settings for '" + item.file + "' is wrong! Correct values are: (Generate Mip Maps=unchecked)"); } if (tex != null) { fileName = fileName + "!a"; alphaTex = (Texture2D)_loadFunc(fileName, ext, typeof(Texture2D), out dm); } if (tex == null) { tex = NTexture.CreateEmptyTexture(); dm = DestroyMethod.Destroy; } if (item.texture == null) { item.texture = new NTexture(tex, alphaTex, (float)tex.width / item.width, (float)tex.height / item.height); item.texture.destroyMethod = dm; } else { item.texture.Reload(tex, alphaTex); item.texture.destroyMethod = dm; } } void LoadImage(PackageItem item) { AtlasSprite sprite; if (_sprites.TryGetValue(item.id, out sprite)) item.texture = new NTexture((NTexture)GetItemAsset(sprite.atlas), sprite.rect, sprite.rotated); else item.texture = NTexture.Empty; } void LoadSound(PackageItem item) { string ext = Path.GetExtension(item.file); string fileName = item.file.Substring(0, item.file.Length - ext.Length); AudioClip audioClip = null; DestroyMethod dm; audioClip = (AudioClip)_loadFunc(fileName, ext, typeof(AudioClip), out dm); if (item.audioClip == null) item.audioClip = new NAudioClip(audioClip); else item.audioClip.Reload(audioClip); item.audioClip.destroyMethod = dm; } byte[] LoadBinary(PackageItem item) { string ext = Path.GetExtension(item.file); string fileName = item.file.Substring(0, item.file.Length - ext.Length); DestroyMethod dm; object ret = _loadFunc(fileName, ext, typeof(TextAsset), out dm); if (ret == null) return null; if (ret is byte[]) return (byte[])ret; else return ((TextAsset)ret).bytes; } void LoadMovieClip(PackageItem item) { ByteBuffer buffer = item.rawData; buffer.Seek(0, 0); item.interval = buffer.ReadInt() / 1000f; item.swing = buffer.ReadBool(); item.repeatDelay = buffer.ReadInt() / 1000f; buffer.Seek(0, 1); int frameCount = buffer.ReadShort(); item.frames = new MovieClip.Frame[frameCount]; string spriteId; MovieClip.Frame frame; AtlasSprite sprite; Rect frameRect = new Rect(); for (int i = 0; i < frameCount; i++) { int nextPos = buffer.ReadShort(); nextPos += buffer.position; frame = new MovieClip.Frame(); frameRect.x = buffer.ReadInt(); frameRect.y = buffer.ReadInt(); frameRect.width = buffer.ReadInt(); frameRect.height = buffer.ReadInt(); frame.addDelay = buffer.ReadInt() / 1000f; spriteId = buffer.ReadS(); if (spriteId != null && _sprites.TryGetValue(spriteId, out sprite)) { frame.texture = new NTexture((NTexture)GetItemAsset(sprite.atlas), sprite.rect, sprite.rotated, new Vector2(item.width, item.height), frameRect.position); } item.frames[i] = frame; buffer.position = nextPos; } } void LoadFont(PackageItem item) { BitmapFont font = new BitmapFont(item); item.bitmapFont = font; ByteBuffer buffer = item.rawData; buffer.Seek(0, 0); bool ttf = buffer.ReadBool(); font.canTint = buffer.ReadBool(); font.resizable = buffer.ReadBool(); font.hasChannel = buffer.ReadBool(); int fontSize = buffer.ReadInt(); int xadvance = buffer.ReadInt(); int lineHeight = buffer.ReadInt(); float texScaleX = 1; float texScaleY = 1; NTexture mainTexture = null; AtlasSprite mainSprite = null; if (ttf && _sprites.TryGetValue(item.id, out mainSprite)) { mainTexture = (NTexture)GetItemAsset(mainSprite.atlas); texScaleX = mainTexture.root.uvRect.width / mainTexture.width; texScaleY = mainTexture.root.uvRect.height / mainTexture.height; } buffer.Seek(0, 1); BitmapFont.BMGlyph bg; int cnt = buffer.ReadInt(); for (int i = 0; i < cnt; i++) { int nextPos = buffer.ReadShort(); nextPos += buffer.position; bg = new BitmapFont.BMGlyph(); char ch = buffer.ReadChar(); font.AddChar(ch, bg); string img = buffer.ReadS(); int bx = buffer.ReadInt(); int by = buffer.ReadInt(); bg.offsetX = buffer.ReadInt(); bg.offsetY = buffer.ReadInt(); bg.width = buffer.ReadInt(); bg.height = buffer.ReadInt(); bg.advance = buffer.ReadInt(); bg.channel = buffer.ReadByte(); //The texture channel where the character image is found (1 = blue, 2 = green, 4 = red, 8 = alpha). if (bg.channel == 1) bg.channel = 2; else if (bg.channel == 2) bg.channel = 1; else if (bg.channel == 3) bg.channel = 0; else if (bg.channel == 8) bg.channel = 3; if (ttf) { if (mainSprite.rotated) { bg.uv[0] = new Vector2((float)(by + bg.height + mainSprite.rect.x) * texScaleX, 1 - (float)(mainSprite.rect.yMax - bx) * texScaleY); bg.uv[1] = new Vector2(bg.uv[0].x - (float)bg.height * texScaleX, bg.uv[0].y); bg.uv[2] = new Vector2(bg.uv[1].x, bg.uv[0].y + (float)bg.width * texScaleY); bg.uv[3] = new Vector2(bg.uv[0].x, bg.uv[2].y); } else { bg.uv[0] = new Vector2((float)(bx + mainSprite.rect.x) * texScaleX, 1 - (float)(by + bg.height + mainSprite.rect.y) * texScaleY); bg.uv[1] = new Vector2(bg.uv[0].x, bg.uv[0].y + (float)bg.height * texScaleY); bg.uv[2] = new Vector2(bg.uv[0].x + (float)bg.width * texScaleX, bg.uv[1].y); bg.uv[3] = new Vector2(bg.uv[2].x, bg.uv[0].y); } bg.lineHeight = lineHeight; } else { PackageItem charImg; if (_itemsById.TryGetValue(img, out charImg)) { GetItemAsset(charImg); charImg.texture.GetUV(bg.uv); bg.width = charImg.texture.width; bg.height = charImg.texture.height; if (mainTexture == null) mainTexture = charImg.texture.root; } if (fontSize == 0) fontSize = bg.height; if (bg.advance == 0) { if (xadvance == 0) bg.advance = bg.offsetX + bg.width; else bg.advance = xadvance; } bg.lineHeight = bg.offsetY < 0 ? bg.height : (bg.offsetY + bg.height); if (bg.lineHeight < font.size) bg.lineHeight = font.size; } buffer.position = nextPos; } font.size = fontSize; font.mainTexture = mainTexture; if (!font.hasChannel) font.shader = ShaderConfig.imageShader; } } }