1693 lines
58 KiB
C#
Raw Normal View History

2025-06-07 17:43:34 +08:00
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using FairyGUI.Utils;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace FairyGUI
{
/// <summary>
/// A UI Package contains a description file and some texture, sound assets.
/// </summary>
public class UIPackage
{
/// <summary>
/// Unload UIAssetBundle by FairyGUI system.
/// if use AssetBundlesManager set value to false
/// then unload UIAssetBundle by AssetBundlesManager
/// </summary>
public static bool unloadBundleByFGUI = true;
/// <summary>
/// The event is triggered when all reference to this package item dropped.
/// </summary>
public static event Action<PackageItem> onReleaseResource;
/// <summary>
/// Package id. It is generated by the Editor.
/// </summary>
public string id { get; private set; }
/// <summary>
/// Package name.
/// </summary>
public string name { get; private set; }
/// <summary>
/// Use this callback to provide resources to the package.
/// </summary>
/// <param name="name">Resource name without extension.</param>
/// <param name="extension">Resource extension. e.g. '.png' '.wav'</param>
/// <param name="type">Resource type. e.g. 'Texture' 'AudioClip'</param>
/// <param name="destroyMethod">How to destroy this resource.</param>
/// <returns></returns>
public delegate object LoadResource(string name, string extension, System.Type type, out DestroyMethod destroyMethod);
/// <summary>
/// A async load resource callback. After loaded, you should call item.owner.SetItemAsset.
/// </summary>
/// <param name="name">Resource name without extension.</param>
/// <param name="extension">Resource extension. e.g. '.png' '.wav'</param>
/// <param name="type">Resource type. e.g. 'Texture' 'AudioClip'</param>
/// <param name="item">Resource item object.</param>
/// <returns></returns>
public delegate void LoadResourceAsync(string name, string extension, System.Type type, PackageItem item);
/// <summary>
///
/// </summary>
/// <param name="result"></param>
public delegate void CreateObjectCallback(GObject result);
List<PackageItem> _items;
Dictionary<string, PackageItem> _itemsById;
Dictionary<string, PackageItem> _itemsByName;
Dictionary<string, string>[] _dependencies;
string _assetPath;
string[] _branches;
internal int _branchIndex;
AssetBundle _resBundle;
string _customId;
bool _fromBundle;
LoadResource _loadFunc;
LoadResourceAsync _loadAsyncFunc;
class AtlasSprite
{
public PackageItem atlas;
public Rect rect = new Rect();
public Vector2 offset = new Vector2();
public Vector2 originalSize = new Vector2();
public bool rotated;
}
Dictionary<string, AtlasSprite> _sprites;
static Dictionary<string, UIPackage> _packageInstById = new Dictionary<string, UIPackage>();
static Dictionary<string, UIPackage> _packageInstByName = new Dictionary<string, UIPackage>();
static List<UIPackage> _packageList = new List<UIPackage>();
static string _branch;
static Dictionary<string, string> _vars = new Dictionary<string, string>();
internal static int _constructing;
public const string URL_PREFIX = "ui://";
#if UNITY_EDITOR
static LoadResource _loadFromAssetsPath = (string name, string extension, System.Type type, out DestroyMethod destroyMethod) =>
{
destroyMethod = DestroyMethod.Unload;
return AssetDatabase.LoadAssetAtPath(name + extension, type);
};
#endif
static LoadResource _loadFromResourcesPath = (string name, string extension, System.Type type, out DestroyMethod destroyMethod) =>
{
destroyMethod = DestroyMethod.Unload;
return Resources.Load(name, type);
};
public UIPackage()
{
_items = new List<PackageItem>();
_itemsById = new Dictionary<string, PackageItem>();
_itemsByName = new Dictionary<string, PackageItem>();
_sprites = new Dictionary<string, AtlasSprite>();
_branchIndex = -1;
}
/// <summary>
///
/// </summary>
public static string branch
{
get { return _branch; }
set
{
_branch = value;
bool empty = string.IsNullOrEmpty(_branch);
var iter = _packageInstById.GetEnumerator();
while (iter.MoveNext())
{
UIPackage pkg = iter.Current.Value;
if (empty)
pkg._branchIndex = -1;
else if (pkg._branches != null)
pkg._branchIndex = Array.IndexOf(pkg._branches, value);
}
iter.Dispose();
}
}
/// <summary>
///
/// </summary>
public static string GetVar(string key)
{
string value;
if (_vars.TryGetValue(key, out value))
return value;
else
return null;
}
/// <summary>
///
/// </summary>
public static void SetVar(string key, string value)
{
if (value == null)
_vars.Remove(key);
else
_vars[key] = value;
}
/// <summary>
/// Return a UIPackage with a certain id.
/// </summary>
/// <param name="id">ID of the package.</param>
/// <returns>UIPackage</returns>
public static UIPackage GetById(string id)
{
UIPackage pkg;
if (_packageInstById.TryGetValue(id, out pkg))
return pkg;
else
return null;
}
/// <summary>
/// Return a UIPackage with a certain name.
/// </summary>
/// <param name="name">Name of the package.</param>
/// <returns>UIPackage</returns>
public static UIPackage GetByName(string name)
{
UIPackage pkg;
if (_packageInstByName.TryGetValue(name, out pkg))
return pkg;
else
return null;
}
/// <summary>
/// Add a UI package from assetbundle.
/// </summary>
/// <param name="bundle">A assetbundle.</param>
/// <returns>UIPackage</returns>
public static UIPackage AddPackage(AssetBundle bundle)
{
return AddPackage(bundle, bundle, null);
}
/// <summary>
/// Add a UI package from two assetbundles. desc and res can be same.
/// </summary>
/// <param name="desc">A assetbunble contains description file.</param>
/// <param name="res">A assetbundle contains resources.</param>
/// <returns>UIPackage</returns>
public static UIPackage AddPackage(AssetBundle desc, AssetBundle res)
{
return AddPackage(desc, res, null);
}
/// <summary>
/// Add a UI package from two assetbundles with a optional main asset name.
/// </summary>
/// <param name="desc">A assetbunble contains description file.</param>
/// <param name="res">A assetbundle contains resources.</param>
/// <param name="mainAssetName">Main asset name. e.g. Basics_fui</param>
/// <returns>UIPackage</returns>
public static UIPackage AddPackage(AssetBundle desc, AssetBundle res, string mainAssetName)
{
byte[] source = null;
if (!string.IsNullOrEmpty(mainAssetName))
{
TextAsset ta = desc.LoadAsset<TextAsset>(mainAssetName);
if (ta != null)
source = ta.bytes;
}
else
{
string[] names = desc.GetAllAssetNames();
string searchPattern = "_fui";
foreach (string n in names)
{
if (n.IndexOf(searchPattern) != -1)
{
TextAsset ta = desc.LoadAsset<TextAsset>(n);
if (ta != null)
{
source = ta.bytes;
mainAssetName = Path.GetFileNameWithoutExtension(n);
break;
}
}
}
}
if (source == null)
throw new Exception("FairyGUI: no package found in this bundle.");
if (unloadBundleByFGUI && desc != res)
desc.Unload(true);
ByteBuffer buffer = new ByteBuffer(source);
UIPackage pkg = new UIPackage();
pkg._resBundle = res;
pkg._fromBundle = true;
int pos = mainAssetName.IndexOf("_fui");
if (pos != -1)
mainAssetName = mainAssetName.Substring(0, pos);
if (!pkg.LoadPackage(buffer, mainAssetName))
return null;
_packageInstById[pkg.id] = pkg;
_packageInstByName[pkg.name] = pkg;
_packageList.Add(pkg);
return pkg;
}
/// <summary>
/// Add a UI package from a path relative to Unity Resources path.
/// </summary>
/// <param name="descFilePath">Path relative to Unity Resources path.</param>
/// <returns>UIPackage</returns>
public static UIPackage AddPackage(string descFilePath)
{
if (descFilePath.StartsWith("Assets/"))
{
#if UNITY_EDITOR
return AddPackage(descFilePath, _loadFromAssetsPath);
#else
Debug.LogWarning("FairyGUI: failed to load package in '" + descFilePath + "'");
return null;
#endif
}
else
return AddPackage(descFilePath, _loadFromResourcesPath);
}
/// <summary>
/// 使用自定义的加载方式载入一个包。
/// </summary>
/// <param name="assetPath">包资源路径。</param>
/// <param name="loadFunc">载入函数</param>
/// <returns></returns>
public static UIPackage AddPackage(string assetPath, LoadResource loadFunc)
{
if (_packageInstById.ContainsKey(assetPath))
return _packageInstById[assetPath];
DestroyMethod dm;
TextAsset asset = (TextAsset)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);
UIPackage pkg = new UIPackage();
pkg._loadFunc = loadFunc;
pkg._assetPath = assetPath;
if (!pkg.LoadPackage(buffer, assetPath))
return null;
_packageInstById[pkg.id] = pkg;
_packageInstByName[pkg.name] = pkg;
_packageInstById[assetPath] = pkg;
_packageList.Add(pkg);
return pkg;
}
/// <summary>
/// Load Package by custom load method.
/// </summary>
/// <param name="descData">Description file data</param>
/// <param name="assetNamePrefix">Prefix of the resource file name. The file name would be in format of 'assetNamePrefix_resFileName'. It can be empty.</param>
/// <param name="loadFunc">Load method</param>
/// <returns></returns>
public static UIPackage AddPackage(byte[] descData, string assetNamePrefix, LoadResource loadFunc)
{
ByteBuffer buffer = new ByteBuffer(descData);
UIPackage pkg = new UIPackage();
pkg._loadFunc = loadFunc;
if (!pkg.LoadPackage(buffer, assetNamePrefix))
return null;
_packageInstById[pkg.id] = pkg;
_packageInstByName[pkg.name] = pkg;
_packageList.Add(pkg);
return pkg;
}
/// <summary>
/// Load Package async by custom load method.
/// </summary>
/// <param name="descData">Description file data</param>
/// <param name="assetNamePrefix">refix of the resource file name. The file name would be in format of 'assetNamePrefix_resFileName'. It can be empty.</param>
/// <param name="loadFunc">Load method</param>
/// <returns></returns>
public static UIPackage AddPackage(byte[] descData, string assetNamePrefix, LoadResourceAsync loadFunc)
{
ByteBuffer buffer = new ByteBuffer(descData);
UIPackage pkg = new UIPackage();
pkg._loadAsyncFunc = loadFunc;
if (!pkg.LoadPackage(buffer, assetNamePrefix))
return null;
_packageInstById[pkg.id] = pkg;
_packageInstByName[pkg.name] = pkg;
_packageList.Add(pkg);
return pkg;
}
/// <summary>
/// Remove a package. All resources in this package will be disposed.
/// </summary>
/// <param name="packageIdOrName"></param>
public static void RemovePackage(string packageIdOrName)
{
UIPackage pkg = null;
if (!_packageInstById.TryGetValue(packageIdOrName, out pkg))
{
if (!_packageInstByName.TryGetValue(packageIdOrName, out pkg))
throw new Exception("FairyGUI: '" + packageIdOrName + "' is not a valid package id or name.");
}
pkg.Dispose();
_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);
}
/// <summary>
///
/// </summary>
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();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public static List<UIPackage> GetPackages()
{
return _packageList;
}
/// <summary>
/// Create a UI object.
/// </summary>
/// <param name="pkgName">Package name.</param>
/// <param name="resName">Resource name.</param>
/// <returns>A UI object.</returns>
public static GObject CreateObject(string pkgName, string resName)
{
UIPackage pkg = GetByName(pkgName);
if (pkg != null)
return pkg.CreateObject(resName);
else
return null;
}
/// <summary>
/// Create a UI object.
/// </summary>
/// <param name="pkgName">Package name.</param>
/// <param name="resName">Resource name.</param>
/// <param name="userClass">Custom implementation of this object.</param>
/// <returns>A UI object.</returns>
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;
}
/// <summary>
/// Create a UI object.
/// </summary>
/// <param name="url">Resource url.</param>
/// <returns>A UI object.</returns>
public static GObject CreateObjectFromURL(string url)
{
PackageItem pi = GetItemByURL(url);
if (pi != null)
return pi.owner.CreateObject(pi, null);
else
return null;
}
/// <summary>
/// Create a UI object.
/// </summary>
/// <param name="url">Resource url.</param>
/// <param name="userClass">Custom implementation of this object.</param>
/// <returns>A UI object.</returns>
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);
}
/// <summary>
/// Get a asset with a certain name.
/// </summary>
/// <param name="pkgName">Package name.</param>
/// <param name="resName">Resource name.</param>
/// <returns>If resource is atlas, returns NTexture; If resource is sound, returns AudioClip.</returns>
public static object GetItemAsset(string pkgName, string resName)
{
UIPackage pkg = GetByName(pkgName);
if (pkg != null)
return pkg.GetItemAsset(resName);
else
return null;
}
/// <summary>
/// Get a asset with a certain name.
/// </summary>
/// <param name="url">Resource url.</param>
/// <returns>If resource is atlas, returns NTexture; If resource is sound, returns AudioClip.</returns>
public static object GetItemAssetByURL(string url)
{
PackageItem item = GetItemByURL(url);
if (item == null)
return null;
return item.owner.GetItemAsset(item);
}
/// <summary>
/// Get url of an item in package.
/// </summary>
/// <param name="pkgName">Package name.</param>
/// <param name="resName">Resource name.</param>
/// <returns>Url.</returns>
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;
}
/// <summary>
/// 将'ui://包名/组件名'转换为以内部id表达的url格式。如果传入的url本身就是内部id格式则直接返回。
/// 同时这个方法还带格式检测如果传入不正确的url会返回null。
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
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);
}
}
/// <summary>
/// Set strings source.
/// </summary>
/// <param name="source"></param>
public static void SetStringsSource(XML source)
{
TranslationHelper.LoadFromXML(source);
}
/// <summary>
///
/// </summary>
public string assetPath
{
get { return _assetPath; }
}
/// <summary>
/// Set a custom id for package, then you can use it in GetById.
/// </summary>
public string customId
{
get { return _customId; }
set
{
if (_customId != null)
_packageInstById.Remove(_customId);
_customId = value;
if (_customId != null)
_packageInstById[_customId] = this;
}
}
/// <summary>
///
/// </summary>
public AssetBundle resBundle
{
get { return _resBundle; }
}
/// <summary>
/// 获得本包依赖的包的id列表
/// </summary>
public Dictionary<string, string>[] dependencies
{
get { return _dependencies; }
}
bool LoadPackage(ByteBuffer buffer, string assetNamePrefix)
{
if (buffer.ReadUint() != 0x46475549)
{
if (Application.isPlaying)
throw new Exception("FairyGUI: old package format found in '" + assetNamePrefix + "'");
else
{
Debug.LogWarning("FairyGUI: old package format found in '" + assetNamePrefix + "'");
return false;
}
}
buffer.version = buffer.ReadInt();
bool ver2 = buffer.version >= 2;
buffer.ReadBool(); //compressed
id = buffer.ReadString();
name = buffer.ReadString();
UIPackage existingPkg;
if (_packageInstById.TryGetValue(id, out existingPkg))
{
if (name != existingPkg.name)
Debug.LogWarning("FairyGUI: Package conflicts, '" + name + "' and '" + existingPkg.name + "'");
#if UNITY_EDITOR
//maybe multiple pkgs in different folder, pefer the one in resources
if (Application.isEditor)
{
if (existingPkg._loadFunc == _loadFromAssetsPath) //old one is outside resources path
existingPkg.Dispose(); //replace the existing
else if (existingPkg._loadFunc == _loadFromResourcesPath && _loadFunc == _loadFromResourcesPath
&& _assetPath.Length < existingPkg._assetPath.Length) //both in resources path, pefer short path
existingPkg.Dispose(); //replace the existing
else //keep the existing
return false;
}
#endif
}
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;
if (buffer.Seek(indexTablePos, 5))
{
cnt = buffer.ReadInt();
for (int i = 0; i < cnt; i++)
{
int index = buffer.ReadUshort();
int len = buffer.ReadInt();
stringTable[index] = buffer.ReadString(len);
}
}
buffer.Seek(indexTablePos, 0);
cnt = buffer.ReadShort();
_dependencies = new Dictionary<string, string>[cnt];
for (int i = 0; i < cnt; i++)
{
Dictionary<string, string> kv = new Dictionary<string, string>();
kv.Add("id", buffer.ReadS());
kv.Add("name", buffer.ReadS());
_dependencies[i] = kv;
}
bool branchIncluded = false;
if (ver2)
{
cnt = buffer.ReadShort();
if (cnt > 0)
{
_branches = buffer.ReadSArray(cnt);
if (!string.IsNullOrEmpty(_branch))
_branchIndex = Array.IndexOf(_branches, _branch);
}
branchIncluded = cnt > 0;
}
buffer.Seek(indexTablePos, 1);
PackageItem pi;
string assetPath;
if (assetNamePrefix.Length > 0)
{
assetPath = Path.GetDirectoryName(assetNamePrefix);
if (assetPath.Length > 0)
assetPath += "/";
assetNamePrefix = assetNamePrefix + "_";
}
else
assetPath = string.Empty;
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;
}
case PackageItemType.Spine:
case PackageItemType.DragoneBones:
{
pi.file = assetPath + pi.file;
pi.skeletonAnchor.x = buffer.ReadFloat();
pi.skeletonAnchor.y = buffer.ReadFloat();
pi.skeletonLoaders = new HashSet<GLoader3D>();
break;
}
}
if (ver2)
{
string str = buffer.ReadS();//branch
if (str != null)
pi.name = str + "/" + pi.name;
int branchCnt = buffer.ReadByte();
if (branchCnt > 0)
{
if (branchIncluded)
pi.branches = buffer.ReadSArray(branchCnt);
else
_itemsById[buffer.ReadS()] = pi;
}
int highResCnt = buffer.ReadByte();
if (highResCnt > 0)
pi.highResolution = buffer.ReadSArray(highResCnt);
}
_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.ReadUshort();
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();
if (ver2 && buffer.ReadBool())
{
sprite.offset.x = buffer.ReadInt();
sprite.offset.y = buffer.ReadInt();
sprite.originalSize.x = buffer.ReadInt();
sprite.originalSize.y = buffer.ReadInt();
}
else if (sprite.rotated)
{
sprite.originalSize.x = sprite.rect.height;
sprite.originalSize.y = sprite.rect.width;
}
else
{
sprite.originalSize.x = sprite.rect.width;
sprite.originalSize.y = sprite.rect.height;
}
_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;
}
/// <summary>
///
/// </summary>
public void LoadAllAssets()
{
int cnt = _items.Count;
for (int i = 0; i < cnt; i++)
GetItemAsset(_items[i]);
}
/// <summary>
///
/// </summary>
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();
}
}
if (unloadBundleByFGUI &&
_resBundle != null)
{
_resBundle.Unload(true);
_resBundle = null;
}
}
/// <summary>
///
/// </summary>
public void ReloadAssets()
{
if (_fromBundle)
throw new Exception("FairyGUI: new bundle must be passed to this function");
ReloadAssets(null);
}
/// <summary>
///
/// </summary>
public void ReloadAssets(AssetBundle resBundle)
{
_resBundle = resBundle;
_fromBundle = _resBundle != null;
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();
if (unloadBundleByFGUI &&
_resBundle != null)
{
_resBundle.Unload(true);
_resBundle = null;
}
}
/// <summary>
///
/// </summary>
/// <param name="resName"></param>
/// <returns></returns>
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);
}
/// <summary>
///
/// </summary>
/// <param name="resName"></param>
/// <param name="userClass"></param>
/// <returns></returns>
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 = UIObjectFactory.NewObject(item, userClass);
if (g == null)
return null;
_constructing++;
g.ConstructFromResource();
_constructing--;
return g;
}
/// <summary>
///
/// </summary>
/// <param name="resName"></param>
/// <returns></returns>
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<PackageItem> 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);
case PackageItemType.Spine:
if (item.skeletonAsset == null)
LoadSpine(item);
return item.skeletonAsset;
case PackageItemType.DragoneBones:
if (item.skeletonAsset == null)
LoadDragonBones(item);
return item.skeletonAsset;
default:
return null;
}
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <param name="asset"></param>
/// <param name="destroyMethod"></param>
public void SetItemAsset(PackageItem item, object asset, DestroyMethod destroyMethod)
{
switch (item.type)
{
case PackageItemType.Atlas:
if (item.texture == null)
item.texture = new NTexture(null, new Rect(0, 0, item.width, item.height));
item.texture.Reload((Texture)asset, null);
item.texture.destroyMethod = destroyMethod;
break;
case PackageItemType.Sound:
if (item.audioClip == null)
item.audioClip = new NAudioClip(null);
item.audioClip.Reload((AudioClip)asset);
item.audioClip.destroyMethod = destroyMethod;
break;
case PackageItemType.Spine:
#if FAIRYGUI_SPINE
item.skeletonAsset = (Spine.Unity.SkeletonDataAsset)asset;
foreach (var gLoader3D in item.skeletonLoaders)
{
if(!gLoader3D.isDisposed)
gLoader3D.SetSpine((Spine.Unity.SkeletonDataAsset) item.skeletonAsset);
}
item.skeletonLoaders.Clear();
#endif
break;
case PackageItemType.DragoneBones:
#if FAIRYGUI_DRAGONBONES
item.skeletonAsset = (DragonBones.UnityDragonBonesData)asset;
#endif
break;
}
}
void LoadAtlas(PackageItem item)
{
string ext = Path.GetExtension(item.file);
string fileName = item.file.Substring(0, item.file.Length - ext.Length);
if (_loadAsyncFunc != null)
{
_loadAsyncFunc(fileName, ext, typeof(Texture), item);
if (item.texture == null)
item.texture = new NTexture(null, new Rect(0, 0, item.width, item.height));
item.texture.destroyMethod = DestroyMethod.None;
}
else
{
Texture tex = null;
Texture alphaTex = null;
DestroyMethod dm;
if (_fromBundle)
{
if (_resBundle != null)
tex = _resBundle.LoadAsset<Texture>(fileName);
else
Debug.LogWarning("FairyGUI: bundle already unloaded.");
dm = DestroyMethod.None;
}
else
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 FAIRYGUI_USE_ALPHA_TEXTURE
if (tex != null)
{
fileName = fileName + "!a";
if (_fromBundle)
{
if (_resBundle != null)
alphaTex = _resBundle.LoadAsset<Texture2D>(fileName);
}
else
alphaTex = (Texture2D)_loadFunc(fileName, ext, typeof(Texture2D), out dm);
}
#endif
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.onRelease += (NTexture t) =>
{
if (onReleaseResource != null)
onReleaseResource(item);
};
}
else
item.texture.Reload(tex, alphaTex);
item.texture.destroyMethod = dm;
}
}
void LoadImage(PackageItem item)
{
AtlasSprite sprite;
if (_sprites.TryGetValue(item.id, out sprite))
{
NTexture atlas = (NTexture)GetItemAsset(sprite.atlas);
if (atlas.width == sprite.rect.width && atlas.height == sprite.rect.height)
item.texture = atlas;
else
item.texture = new NTexture(atlas, sprite.rect, sprite.rotated, sprite.originalSize, sprite.offset);
}
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);
if (_loadAsyncFunc != null)
{
_loadAsyncFunc(fileName, ext, typeof(AudioClip), item);
if (item.audioClip == null)
item.audioClip = new NAudioClip(null);
item.audioClip.destroyMethod = DestroyMethod.None;
}
else
{
AudioClip audioClip = null;
DestroyMethod dm;
if (_fromBundle)
{
if (_resBundle != null)
audioClip = _resBundle.LoadAsset<AudioClip>(fileName);
dm = DestroyMethod.None;
}
else
{
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);
TextAsset ta;
if (_resBundle != null)
{
ta = _resBundle.LoadAsset<TextAsset>(fileName);
if (ta != null)
return ta.bytes;
else
return null;
}
else
{
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.ReadUshort();
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();
font.name = URL_PREFIX + this.id + item.id;
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;
int bgX;
int bgY;
int bgWidth;
int bgHeight;
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.ReadUshort();
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();
bgX = buffer.ReadInt();
bgY = buffer.ReadInt();
bgWidth = buffer.ReadInt();
bgHeight = 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, 15-all).
if (bg.channel == 1)
bg.channel = 2;
else if (bg.channel == 2)
bg.channel = 1;
else if (bg.channel == 4)
bg.channel = 0;
else if (bg.channel == 8)
bg.channel = 3;
if (ttf)
{
if (mainSprite.rotated)
{
bg.uv[0] = new Vector2((float)(by + bgHeight + mainSprite.rect.x) * texScaleX,
1 - (float)(mainSprite.rect.yMax - bx) * texScaleY);
bg.uv[1] = new Vector2(bg.uv[0].x - (float)bgHeight * texScaleX, bg.uv[0].y);
bg.uv[2] = new Vector2(bg.uv[1].x, bg.uv[0].y + (float)bgWidth * 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 + bgHeight + mainSprite.rect.y) * texScaleY);
bg.uv[1] = new Vector2(bg.uv[0].x, bg.uv[0].y + (float)bgHeight * texScaleY);
bg.uv[2] = new Vector2(bg.uv[0].x + (float)bgWidth * texScaleX, bg.uv[1].y);
bg.uv[3] = new Vector2(bg.uv[2].x, bg.uv[0].y);
}
bg.lineHeight = lineHeight;
bg.x = bgX;
bg.y = bgY;
bg.width = bgWidth;
bg.height = bgHeight;
}
else
{
PackageItem charImg;
if (_itemsById.TryGetValue(img, out charImg))
{
charImg = charImg.getBranch();
bgWidth = charImg.width;
bgHeight = charImg.height;
charImg = charImg.getHighResolution();
GetItemAsset(charImg);
charImg.texture.GetUV(bg.uv);
texScaleX = (float)bgWidth / charImg.width;
texScaleY = (float)bgHeight / charImg.height;
bg.x = bgX + charImg.texture.offset.x * texScaleX;
bg.y = bgY + charImg.texture.offset.y * texScaleY;
bg.width = charImg.texture.width * texScaleX;
bg.height = charImg.texture.height * texScaleY;
if (mainTexture == null)
mainTexture = charImg.texture.root;
}
if (fontSize == 0)
fontSize = bgHeight;
if (bg.advance == 0)
{
if (xadvance == 0)
bg.advance = bgX + bgWidth;
else
bg.advance = xadvance;
}
bg.lineHeight = bgY < 0 ? bgHeight : (bgY + bgHeight);
if (bg.lineHeight < fontSize)
bg.lineHeight = fontSize;
}
buffer.position = nextPos;
}
font.size = fontSize;
font.mainTexture = mainTexture;
if (!font.hasChannel)
font.shader = ShaderConfig.imageShader;
}
void LoadSpine(PackageItem item)
{
string ext = Path.GetExtension(item.file);
string fileName = item.file.Substring(0, item.file.Length - ext.Length);
int index = fileName.LastIndexOf(".skel");
if (index > 0)
fileName = fileName.Substring(0, index);
#if FAIRYGUI_SPINE
if (_loadAsyncFunc != null)
{
_loadAsyncFunc(fileName + "_SkeletonData", ".asset", typeof(Spine.Unity.SkeletonDataAsset), item);
}
else
{
Spine.Unity.SkeletonDataAsset asset;
if (_resBundle != null)
asset = _resBundle.LoadAsset<Spine.Unity.SkeletonDataAsset>(fileName);
else
{
DestroyMethod dm;
asset = (Spine.Unity.SkeletonDataAsset)_loadFunc(fileName + "_SkeletonData", ".asset", typeof(Spine.Unity.SkeletonDataAsset), out dm);
}
if (asset == null)
Debug.LogWarning("FairyGUI: Failed to load " + fileName);
item.skeletonAsset = asset;
}
#else
Debug.LogWarning("To enable Spine support, add script define symbol: FAIRYGUI_SPINE");
#endif
}
void LoadDragonBones(PackageItem item)
{
#if FAIRYGUI_DRAGONBONES
string ext = Path.GetExtension(item.file);
string fileName = item.file.Substring(0, item.file.Length - ext.Length);
int index = fileName.LastIndexOf("_ske");
if (index > 0)
fileName = fileName.Substring(0, index);
index = fileName.LastIndexOf(".dbbin");
if (index > 0)
fileName = fileName.Substring(0, index);
DragonBones.UnityDragonBonesData asset;
if (_resBundle != null)
asset = _resBundle.LoadAsset<DragonBones.UnityDragonBonesData>(fileName + "_Data");
else
{
DestroyMethod dm;
asset = (DragonBones.UnityDragonBonesData)_loadFunc(fileName + "_Data", ".asset", typeof(DragonBones.UnityDragonBonesData), out dm);
}
if (asset != null)
{
foreach (var atlas in asset.textureAtlas)
{
if (atlas.material == null)
{
atlas.material = new Material(ShaderConfig.GetShader(ShaderConfig.imageShader));
atlas.material.mainTexture = atlas.texture;
}
}
item.skeletonAsset = DragonBones.UnityFactory.factory.LoadData(asset);
}
else
Debug.LogWarning("FairyGUI: Failed to load " + fileName);
#else
Debug.LogWarning("To enable DragonBones support, add script define symbol: FAIRYGUI_DRAGONBONES");
#endif
}
#if UNITY_2019_3_OR_NEWER
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void InitializeOnLoad()
{
RemoveAllPackages();
UIPackage.branch = null;
}
#endif
}
}