2025-06-07 17:43:34 +08:00

1693 lines
58 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}