本文章所包含代码源自https://blog.youkuaiyun.com/lahmiley/article/details/83759753, 感谢@lahmiley提供解决方案
由本人整理修改后, 在本博客发布丶记录:
如有侵权, 请联系我删除
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine.Events;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.UI
{
/// <summary>
/// Emoji Text组件
///
/// 用法用例:
/// 图片 (NodeImage): <tag=image, id=90001/>
/// 按钮 (NodeButton): <tag=button, id=90001, callback=gameName&methodName&arg1&arg2&.../>
/// Emoji表情 (NodeEmoji): <tag=emoji, id=90001/>
/// 超链接 (NodeHyperlink): <tag=hyperlink, color=#ffoooo>超链接</tag>
/// 说明: tag必须在<...>中排首位, id指资源ID, callback指触发事件后回调(对象名&方法名&参数1&参数2&...), color指超链接文本颜色
///
/// 资源加载:
/// 图片丶按钮: 由AssetCacheMgr管理,传入id参数,返回Sprite纹理
/// Emoji表情: 由AssetCacheMgr管理,传入id参数,返回GameObject预制体
/// </summary>
[AddComponentMenu("UI/Text - EmojiText")]
public class EmojiText : Text
{
#if UNITY_EDITOR
[MenuItem("GameObject/UI/Text - EmojiText")]
static void Create()
{
var select_obj = Selection.activeGameObject;
//当前非资源文件
var asset_path = AssetDatabase.GetAssetPath(select_obj);
if (string.IsNullOrEmpty(asset_path))
{
//父节点
Transform parent = null;
//从当前节点向上遍历,寻找Canvas节点
var select = select_obj?.transform;
while (select != null)
{
if (select.GetComponent<Canvas>() != null)
{
parent = select_obj.transform;
break;
}
select = select.parent;
}
//从全局Find一个Canvas节点
parent = FindObjectOfType<Canvas>()?.transform;
//创建一个新的Canvas节点
if (parent == null)
{
var canvas_obj = new GameObject("Canvas");
canvas_obj.transform.SetParent(null);
var canvas = canvas_obj.AddComponent<Canvas>();
canvas_obj.AddComponent<CanvasScaler>();
canvas_obj.AddComponent<GraphicRaycaster>();
canvas.worldCamera = Camera.main;
canvas.renderMode = RenderMode.ScreenSpaceCamera;
parent = canvas_obj.transform;
//EventSystem
if (FindObjectOfType<EventSystems.EventSystem>() == null)
{
var system_obj = new GameObject("EventSystem");
system_obj.transform.SetParent(null);
system_obj.AddComponent<EventSystems.EventSystem>();
system_obj.AddComponent<EventSystems.StandaloneInputModule>();
}
}
//创建节点
var obj = new GameObject("EmojiText");
obj.AddComponent<EmojiText>();
obj.transform.SetParent(parent);
obj.transform.localScale = Vector3.one;
}
}
#endif
//匹配一个字符串
//例:<tag=image,id=90001/> <tag=button,id=90001/> <tag=emoji,id=90001/> <tag=hyperlink,color=#ffoooo>超链接</tag>
static readonly Regex regexTag = new Regex(@"<tag=(button|image|emoji|hyperlink){1}.+?(/>|</tag>){1}", RegexOptions.Singleline);
//对象缓存池
private List<NodeImage> imageCache = new List<NodeImage>();
private List<NodeEmoji> emojiCache = new List<NodeEmoji>();
private List<NodeButton> buttonCache = new List<NodeButton>();
private List<NodeHyperlink> hyperlinkCache = new List<NodeHyperlink>();
private Dictionary<int, TagData> tagDict;
//重新创建tag内容
private bool waitUpdateTag = false;
//目标内容信息
public string srcText { get; private set; }
private int srcSize = -1;
public override string text
{
set
{
srcText = value;
srcSize = fontSize;
waitUpdateTag = false;
base.text = Parse(value);
}
get
{
return base.text;
}
}
private void Update()
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
return;
}
#endif
if (font == null)
return;
if (srcSize != -1 && srcSize != fontSize)
{
srcSize = fontSize;
text = srcText;
return;
}
if (waitUpdateTag)
{
waitUpdateTag = false;
UpdateDatas();
}
}
protected override void OnDestroy()
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
return;
}
#endif
ClearCache();
}
protected override void OnPopulateMesh(VertexHelper helper)
{
if (font == null)
return;
if (tagDict != null)
{
foreach (TagData tagData in tagDict.Values)
{
tagData.SetValid(false);
}
}
//禁用纹理重建回调
m_DisableFontTextureRebuiltCallback = true;
var settings = GetGenerationSettings(rectTransform.rect.size);
cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);
//应用顶点偏移
IList<UIVertex> verts = cachedTextGenerator.verts;
float units_per_pixel = 1 / pixelsPerUnit;
//最后4个顶点独立为一行
int vert_count = verts.Count - 4;
if (vert_count > 0)
{
Vector2 rounding_offset = new Vector2(verts[0].position.x, verts[0].position.y) * units_per_pixel;
rounding_offset = PixelAdjustPoint(rounding_offset) - rounding_offset;
helper.Clear();
TagData hyperlink_tag = null;
var x_offset = rectTransform.sizeDelta.x * rectTransform.pivot.x;
var y_offset = rectTransform.sizeDelta.y * rectTransform.pivot.y;
Vector4 button_bound = new Vector4(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue);
var button_bound_init = false;
var have_rounding_offset = rounding_offset != Vector2.zero;
var last_char_min_y = float.MaxValue;
for (int i = 0; i < vert_count; ++i)
{
int temp_verts_index = i & 3;
m_TempVerts[temp_verts_index] = verts[i];
m_TempVerts[temp_verts_index].position *= units_per_pixel;
if (have_rounding_offset)
{
m_TempVerts[temp_verts_index].position.x += rounding_offset.x;
m_TempVerts[temp_verts_index].position.y += rounding_offset.y;
}
if (temp_verts_index == 3)
{
int index = Mathf.FloorToInt(i / 4);
if (hyperlink_tag != null)
{
CalculateHyperlinkBound(hyperlink_tag, ref button_bound, ref button_bound_init, ref last_char_min_y, x_offset, y_offset, index);
}
else if (tagDict != null && tagDict.ContainsKey(index))
{
TagData tag_data = tagDict[index];
tag_data.SetValid(true);
if (tag_data.type != TagType.Hyperlink)
{
float minX = float.MaxValue;
float minY = float.MaxValue;
for (int j = 0; j < 4; j++)
{
//清除占位符显示 也可以用<color=#00000000><quad></color>来隐藏
m_TempVerts[j].uv0 = Vector2.zero;
//获取占位符左下角坐标
if (m_TempVerts[j].position.x < minX) minX = m_TempVerts[j].position.x;
if (m_TempVerts[j].position.y < minY) minY = m_TempVerts[j].position.y;
}
tag_data.SetStartPosition(new Vector3(minX + x_offset, minY + y_offset, 0));
}
else if (tag_data.type == TagType.Hyperlink)
{
tag_data.ClearBound();
hyperlink_tag = tag_data;
if (IsTempVertexValid())
{
Vector4 charBound = GetCharBound();
button_bound = charBound;
last_char_min_y = charBound.y;
button_bound_init = true;
}
}
}
helper.AddUIVertexQuad(m_TempVerts);
}
}
}
//恢复回调
m_DisableFontTextureRebuiltCallback = false;
waitUpdateTag = true;
}
string Parse(string source_str)
{
//Clear Tag Data
tagDict?.Clear();
//Match
MatchCollection matchs = regexTag.Matches(source_str);
int count = matchs.Count;
if (count > 0)
{
tagDict = tagDict ?? new Dictionary<int, TagData>();
StringBuilder builder = new StringBuilder();
//解析字符串后的tag的首个匹配下标
int start_index = 0;
//sourceString上次匹配成功的结束下标
int last_match_end_index = 0;
for (int i = 0; i < count; i++)
{
Match match = matchs[i];
//Tag Before String / Tag标识之前的字符串
string tag_before = source_str.Substring(last_match_end_index, match.Index - last_match_end_index);
builder.Append(tag_before);
start_index += tag_before.Length;
//Tag Data
TagData tag_data = new TagData(match.Groups[0].Value, fontSize);
tag_data.startIndex = start_index;
tagDict.Add(start_index, tag_data);
builder.Append(tag_data.populateText);
start_index += tag_data.populateLength;
last_match_end_index = match.Index + match.Length;
//Last Tag/ 处理后面的文本
if (i == count - 1 && last_match_end_index < source_str.Length)
{
builder.Append(source_str.Substring(last_match_end_index, source_str.Length - last_match_end_index));
}
}
return builder.ToString();
}
return source_str;
}
void UpdateDatas()
{
ClearCache();
if (tagDict != null)
{
foreach (TagData tag_data in tagDict.Values)
{
if (!tag_data.valid)
{
continue;
}
switch (tag_data.type)
{
case TagType.Image:
{
NodeImage item = ObjectPoolMgr.GetInstance().ImagePool.Get(tag_data);
item.SetParent(transform);
item.SetData(tag_data);
imageCache.Add(item);
}
break;
case TagType.Emoji:
{
NodeEmoji item = ObjectPoolMgr.GetInstance().EmojiPool.Get(tag_data);
item.SetParent(transform);
item.SetData(tag_data);
emojiCache.Add(item);
}
break;
case TagType.Button:
{
NodeButton item = ObjectPoolMgr.GetInstance().ButtonPool.Get(tag_data);
item.SetParent(transform);
item.SetData(tag_data);
buttonCache.Add(item);
}
break;
case TagType.Hyperlink:
{
List<Vector4> boundList = tag_data.boundList;
if (boundList != null && boundList.Count > 0)
{
for (int i = 0; i < boundList.Count; i++)
{
NodeHyperlink item = ObjectPoolMgr.GetInstance().HyperlinkPool.Get(tag_data);
item.SetParent(transform);
item.SetData(tag_data);
item.SetBound(boundList[i]);
hyperlinkCache.Add(item);
}
}
}
break;
}
}
}
}
void ClearCache()
{
if (ObjectPoolMgr.Instance != null)
{
//Release All
ObjectPoolMgr.GetInstance().ImagePool.Release(imageCache);
ObjectPoolMgr.GetInstance().ButtonPool.Release(buttonCache);
ObjectPoolMgr.GetInstance().HyperlinkPool.Release(hyperlinkCache);
ObjectPoolMgr.GetInstance().EmojiPool.Release(emojiCache);
}
//Clear
imageCache.Clear();
buttonCache.Clear();
hyperlinkCache.Clear();
emojiCache.Clear();
}
readonly UIVertex[] m_TempVerts = new UIVertex[4];
bool IsTempVertexValid()
{
Vector3 vector0 = m_TempVerts[0].position;
Vector3 vector1 = m_TempVerts[1].position;
if (vector0.x == vector1.x && vector0.y == vector1.y)
{
return false;
}
return true;
}
Vector4 GetCharBound()
{
Vector4 result = new Vector4(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue);
for (int i = 0; i < 4; i++)
{
Vector3 position = m_TempVerts[i].position;
if (position.x < result.x) result.x = position.x;
if (position.y < result.y) result.y = position.y;
if (position.x > result.z) result.z = position.x;
if (position.y > result.w) result.w = position.y;
}
return result;
}
void CalculateHyperlinkBound(TagData hyperlink_tag, ref Vector4 button_bound, ref bool button_bound_init, ref float last_char_min_Y, float x_offset, float y_offset, int index)
{
if (IsTempVertexValid())
{
Vector4 charBound = GetCharBound();
if (button_bound_init)
{
//判断换行
if ((last_char_min_Y != float.MaxValue) && (last_char_min_Y > charBound.w))
{
button_bound.x += x_offset;
button_bound.y += y_offset;
button_bound.z += x_offset;
button_bound.w += y_offset;
hyperlink_tag.AddBound(button_bound);
last_char_min_Y = charBound.y;
button_bound = charBound;
}
else
{
last_char_min_Y = charBound.y;
if (charBound.x < button_bound.x) button_bound.x = charBound.x;
if (charBound.y < button_bound.y) button_bound.y = charBound.y;
if (charBound.z > button_bound.z) button_bound.z = charBound.z;
if (charBound.w > button_bound.w) button_bound.w = charBound.w;
}
}
else
{
button_bound = charBound;
last_char_min_Y = charBound.y;
button_bound_init = true;
}
}
if ((hyperlink_tag.endIndex - 1) == index)
{
if (button_bound_init)
{
button_bound.x += x_offset;
button_bound.y += y_offset;
button_bound.z += x_offset;
button_bound.w += y_offset;
hyperlink_tag.AddBound(button_bound); ;
}
button_bound_init = false;
hyperlink_tag = null;
}
}
}
#region 标签数据<对象>
internal enum TagType
{
Image = 1, //图标
Emoji = 2, //动态表情
Button = 3, //按钮
Hyperlink = 4, //超链接
}
internal class TagData
{
const float IMAGE_SCALE = 1.5f;
const float EMOJI_SCALE = 1.5f;
const float BUTTON_SCALE = 2f;
public TagType type { get; private set; }
public string id { get; private set; }
//填充文本
public string populateText { get; private set; }
//填充文本长度
public int populateLength { get; private set; }
//渲染的位置
public Vector3 startPosition { get; private set; }
public float width { get; private set; }
public float height { get; private set; }
public int fontSize { get; private set; }
//在显示范围内的
public bool valid { get; private set; } = false;
//按钮回调方法
public UnityAction callback { get; private set; } = null;
//用于处理Hyperlink类型的节点(startIndex/endIndex/boundList)
public int startIndex;
public int endIndex
{
get
{
return startIndex + populateLength;
}
}
public List<Vector4> boundList { get; private set; }
public TagData(string params_str, int size)
{
var param = new TagParams(params_str);
this.id = param.id;
this.fontSize = size;
this.type = param.type;
switch (this.type)
{
case TagType.Image:
populateText = string.Format("<quad Size={0}, Width={1}>", size.ToString(), IMAGE_SCALE.ToString());
width = size * IMAGE_SCALE;
height = size * IMAGE_SCALE;
break;
case TagType.Emoji:
populateText = string.Format("<quad Size={0}, Width={1}>", size.ToString(), EMOJI_SCALE.ToString());
width = size * EMOJI_SCALE;
height = size * EMOJI_SCALE;
break;
case TagType.Button:
populateText = string.Format("<quad Size={0}, Width={1}>", size.ToString(), BUTTON_SCALE.ToString());
width = size * BUTTON_SCALE;
height = size;
break;
case TagType.Hyperlink:
if (string.IsNullOrEmpty(param.contetnColor))
populateText = param.content;
else
populateText = string.Format("<color={1}>{0}</color>", param.content, param.contetnColor);
break;
}
this.populateLength = populateText?.Length ?? 0;
//Callback
if (!string.IsNullOrEmpty(param.gameName) && !string.IsNullOrEmpty(param.methodName))
{
string game_name = param.gameName, method_name = param.methodName;
string[] args = param.methodsAgrs;
if (args != null && args.Length > 0)
callback = () =>
{
var obj = GameObject.Find(game_name);
if (obj != null)
obj.SendMessage(method_name, args);
else
Debug.LogWarning(string.Format("Not Found GameObject:{0}\tId={1}", game_name, this.id));
};
else
callback = () =>
{
var obj = GameObject.Find(game_name);
if (obj != null)
obj.SendMessage(method_name);
else
Debug.LogWarning(string.Format("Not Found GameObject:{0}\tId={1}", game_name, this.id));
};
}
}
public void SetStartPosition(Vector3 position)
{
//偏移值 可以根据项目情况微调
float offset_y = (this.height - this.fontSize) * 0.5f;
position.Set(position.x, position.y - offset_y, position.z);
startPosition = position;
}
public void SetValid(bool valid)
{
this.valid = valid;
}
public void AddBound(Vector4 bound)
{
if (boundList == null)
{
boundList = new List<Vector4>();
}
boundList.Add(bound);
}
public void ClearBound()
{
boundList?.Clear();
}
public override string ToString()
{
return string.Format("type={0},id={1},populateText={2}({3}),width={4},height={5},fontSize={6},valid={7}",
type,
id,
populateText,
populateLength,
width,
height,
fontSize,
valid);
}
}
internal class TagParams
{
//基础属性
public string id;
public TagType type;
//Callback
public string gameName; //接受回调的对象名称
public string methodName; //回调方法名
public string[] methodsAgrs; //方法参数
//文本内容
public string contetnColor;
public string content;
//匹配条件
static Regex regexContent = new Regex(@">(.+?)</tag>", RegexOptions.Singleline);
static Regex regexHeader = new Regex(@"^<(.+?)(/>|>)", RegexOptions.Singleline);
public TagParams(string tag)
{
//Header
MatchCollection matchs = regexHeader.Matches(tag);
if (matchs.Count > 0)
{
var header = matchs[0].Groups[1].Value;
foreach (var field in header.Replace(" ", "").Split(','))
{
var arr = (field ?? "").Split('=');
if (arr != null && arr.Length == 2)
{
switch (arr[0]?.ToLower())
{
case "tag":
switch (arr[1]?.ToLower())
{
case "image":
this.type = TagType.Image;
break;
case "emoji":
this.type = TagType.Emoji;
break;
case "button":
this.type = TagType.Button;
break;
case "hyperlink":
this.type = TagType.Hyperlink;
break;
default:
Debug.LogWarning("未知识别类型");
break;
}
break;
case "id":
id = arr[1];
break;
case "color":
if (arr[1] != null && arr[1].Length == 7 && arr[1].StartsWith("#"))
{
contetnColor = arr[1];
}
break;
case "callback":
arr = (arr[1] ?? "").Split('&');
if (arr.Length >= 2)
{
gameName = arr[0];
methodName = arr[1];
if (arr.Length > 2)
{
methodsAgrs = new string[arr.Length - 2];
Array.Copy(arr, 2, methodsAgrs, 0, methodsAgrs.Length);
}
}
break;
}
}
}
}
//Content
matchs = regexContent.Matches(tag);
if (matchs.Count > 0)
{
content = matchs[0].Groups[1].Value;
}
}
}
#endregion
#region 资源缓存管理器/节点池管理器/节点池/节点对象
internal class AssetCacheMgr : MonoBehaviour
{
private static AssetCacheMgr instance;
//缓存池
private Dictionary<string, Sprite> dictImage = new Dictionary<string, Sprite>();
private Dictionary<string, Sprite> dictButton = new Dictionary<string, Sprite>();
private Dictionary<string, GameObject> dictEmoji = new Dictionary<string, GameObject>();
public static AssetCacheMgr Instance { get { return instance; } }
public static AssetCacheMgr GetInstance()
{
if (instance == null)
{
var obj = new GameObject("[CacheAssetMgr]");
DontDestroyOnLoad(obj);
instance = obj.AddComponent<AssetCacheMgr>();
}
return instance;
}
public Sprite LoadImage(string id)
{
id = id ?? string.Empty;
if (dictImage.ContainsKey(id))
{
return dictImage[id];
}
var sprite = Resources.Load<Sprite>(string.Format("Textures/Icons/{0}", id));
dictImage.Add(id, sprite);
return sprite;
}
public Sprite LoadButton(string id)
{
id = id ?? string.Empty;
if (dictButton.ContainsKey(id))
{
return dictButton[id];
}
var sprite = Resources.Load<Sprite>(string.Format("Emoji/Button/{0}", id));
dictButton.Add(id, sprite);
return sprite;
}
public GameObject LoadEmoji(string id)
{
id = id ?? string.Empty;
if (dictEmoji.ContainsKey(id))
{
return dictEmoji[id];
}
var prefab = Resources.Load<GameObject>(string.Format("Emoji/Face/Face_{0}", id));
dictEmoji.Add(id, prefab);
return prefab;
}
}
internal class ObjectPoolMgr : MonoBehaviour
{
private static ObjectPoolMgr instance;
public ObjectPool<NodeImage> ImagePool { get; private set; }
public ObjectPool<NodeButton> ButtonPool { get; private set; }
public ObjectPool<NodeEmoji> EmojiPool { get; private set; }
public ObjectPool<NodeHyperlink> HyperlinkPool { get; private set; }
private float tick = 0f;
public static ObjectPoolMgr Instance { get { return instance; } }
public static ObjectPoolMgr GetInstance()
{
if (instance == null)
{
var obj = new GameObject("[ObjectPoolMgr]");
DontDestroyOnLoad(obj);
instance = obj.AddComponent<ObjectPoolMgr>();
}
return instance;
}
void Awake()
{
InitPools();
}
void FixedUpdate()
{
tick -= Time.fixedDeltaTime;
if (tick < 0)
{
tick = 60f;
ImagePool.Tick();
ButtonPool.Tick();
EmojiPool.Tick();
HyperlinkPool.Tick();
}
}
void InitPools()
{
//Image
ImagePool = new ObjectPool<NodeImage>();
ImagePool.CreateEvent += (tag_data) =>
{
//"Emoji/Prefab/Image";
var obj = new GameObject("Image" + ImagePool.Count);
obj.AddComponent<Image>();
var node = new NodeImage(obj);
node.SetParent(transform);
return node;
};
ImagePool.ActiveEvent += item =>
{
item.Used = true;
item.SetActive(true);
item.SetParent(null);
};
ImagePool.ReleaseEvent += item =>
{
item.Used = false;
item.Release();
item.SetActive(false);
item.SetParent(transform);
};
//Button
ButtonPool = new ObjectPool<NodeButton>();
ButtonPool.CreateEvent += (tag_data) =>
{
//"Emoji/Prefab/Button";
var obj = new GameObject("Button" + ButtonPool.Count);
obj.AddComponent<Image>();
obj.AddComponent<Button>();
var node = new NodeButton(obj);
node.SetParent(transform);
return node;
};
ButtonPool.ActiveEvent += item =>
{
item.Used = true;
item.SetActive(true);
item.SetParent(null);
};
ButtonPool.ReleaseEvent += item =>
{
item.Used = false;
item.Release();
item.SetActive(false);
item.SetParent(transform);
};
//DummyImage
HyperlinkPool = new ObjectPool<NodeHyperlink>();
HyperlinkPool.CreateEvent += (tag_data) =>
{
//"Emoji/Prefab/DummyImage";
var obj = new GameObject("DummyImage" + HyperlinkPool.Count);
obj.AddComponent<Image>().color = new Color();
obj.AddComponent<Button>();
var node = new NodeHyperlink(obj);
node.SetParent(transform);
return node;
};
HyperlinkPool.ActiveEvent += item =>
{
item.Used = true;
item.SetActive(true);
item.SetParent(null);
};
HyperlinkPool.ReleaseEvent += item =>
{
item.Used = false;
item.Release();
item.SetActive(false);
item.SetParent(transform);
};
//Emoji
EmojiPool = new ObjectPool<NodeEmoji>(true);
EmojiPool.CreateEvent += (tag_data) =>
{
var prefab = AssetCacheMgr.GetInstance().LoadEmoji(tag_data.id);
var node = new NodeEmoji(prefab != null ? Instantiate(prefab) : new GameObject("NULL:" + tag_data.id));
node.SetParent(transform);
return node;
};
EmojiPool.ActiveEvent += item =>
{
item.Used = true;
item.SetActive(true);
item.SetParent(null);
};
EmojiPool.ReleaseEvent += item =>
{
item.Used = false;
item.Release();
item.SetActive(false);
item.SetParent(transform);
};
}
}
internal class ObjectPool<T> where T : Node
{
//事件接口
public event Func<TagData, T> CreateEvent;
public event Action<T> ReleaseEvent;
public event Action<T> ActiveEvent;
//计数器
public int Count { get { return _listPool.Count; } }
//节点池
private List<T> _listPool = null;
private Dictionary<string, List<T>> _dictPool = null;
public ObjectPool(bool use_dict_pool = false)
{
if (use_dict_pool)
_dictPool = new Dictionary<string, List<T>>();
else
_listPool = new List<T>();
}
private List<T> GetPool(string id)
{
if (_listPool != null)
{
return _listPool;
}
List<T> pool = null;
_dictPool.TryGetValue(id, out pool);
if (pool == null)
{
pool = new List<T>();
_dictPool.Add(id, pool);
}
return pool;
}
public void Release(T item)
{
if (item != null && !item.IsDestroy())
{
//回收事件
ReleaseEvent?.Invoke(item);
//处理列表
List<T> pool = GetPool(item.tagData.id);
if (!pool.Contains(item))
{
pool.Add(item);
}
}
}
public void Release(IEnumerable<T> lst)
{
foreach (var item in lst)
{
Release(item);
}
}
public T Get(TagData data)
{
T item = default(T);
List<T> pool = GetPool(data.id);
while (pool.Count > 0 && (item == null || item.IsDestroy()))
{
item = pool[0];
pool.RemoveAt(0);
}
//创建节点/创建事件
if (item == null || item.IsDestroy())
{
item = CreateEvent(data);
}
//Get事件
ActiveEvent?.Invoke(item);
return item;
}
public void Clear()
{
_listPool?.Clear();
_dictPool?.Clear();
}
public void Tick()
{
if (_listPool != null)
{
for (int i = _listPool.Count - 1; i >= 0; i--)
{
if (_listPool[i].IsDestroy())
_listPool.RemoveAt(i);
}
}
if (_dictPool != null)
{
foreach (var lst in _dictPool.Values)
{
for (int i = lst.Count - 1; i >= 0; i--)
{
if (lst[i].IsDestroy())
lst.RemoveAt(i);
}
}
}
}
}
internal class NodeButton : Node
{
public Image image { get; private set; }
public Button button { get; private set; }
public NodeButton(GameObject obj) : base(obj)
{
this.image = obj.GetComponent<Image>();
this.button = obj.GetComponent<Button>();
}
public override void SetData(TagData data)
{
base.SetData(data);
image.sprite = AssetCacheMgr.GetInstance().LoadButton(data.id);
button.onClick.RemoveAllListeners();
if (data.callback != null)
button.onClick.AddListener(data.callback);
}
public override void Release()
{
button.onClick.RemoveAllListeners();
}
}
internal class NodeHyperlink : Node
{
public Button button { get; private set; }
public NodeHyperlink(GameObject obj) : base(obj)
{
this.button = obj.GetComponent<Button>();
}
public override void SetData(TagData data)
{
base.SetData(data);
button.onClick.RemoveAllListeners();
if (data.callback != null)
button.onClick.AddListener(data.callback);
}
public void SetBound(Vector4 bound)
{
transform.sizeDelta = new Vector2(bound.z - bound.x, bound.w - bound.y);
transform.anchoredPosition3D = new Vector3(bound.x, bound.y, 0);
}
public override void Release()
{
button.onClick.RemoveAllListeners();
}
}
internal class NodeEmoji : Node
{
public NodeEmoji(GameObject obj) : base(obj)
{
}
}
internal class NodeImage : Node
{
private Image image;
public NodeImage(GameObject obj) : base(obj)
{
this.image = obj.GetComponent<Image>();
}
public override void SetData(TagData data)
{
base.SetData(data);
image.sprite = AssetCacheMgr.GetInstance().LoadImage(data.id);
}
}
internal class Node
{
public bool Used = false;
public TagData tagData { get; private set; }
public GameObject gameObject { get; private set; }
public RectTransform transform { get; private set; }
public Node(GameObject obj)
{
gameObject = obj;
transform = obj.GetComponent<RectTransform>();
}
public bool IsDestroy()
{
return gameObject == null;
}
public bool IsNull()
{
return gameObject == null || tagData == null;
}
public virtual void SetActive(bool active)
{
if (IsDestroy())
{
throw new System.Exception("这个对象已被销毁");
}
gameObject.SetActive(active);
}
public virtual void SetParent(Transform parent)
{
if (IsDestroy())
{
throw new System.Exception("这个对象已被销毁");
}
this.transform.SetParent(parent);
this.transform.localScale = Vector3.one;
this.transform.localRotation = Quaternion.identity;
this.transform.pivot = Vector2.zero;
this.transform.anchorMin = Vector2.zero;
this.transform.anchorMax = Vector2.zero;
}
public virtual void SetData(TagData data)
{
if (IsDestroy())
{
throw new System.Exception("这个对象已被销毁");
}
tagData = data;
transform.sizeDelta = new Vector2(data.width, data.height);
transform.anchoredPosition3D = data.startPosition;
}
public virtual void Release()
{
}
}
#endregion
}