956 lines
29 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.Globalization;
using System.IO;
using System.Text;
using AssetManagement;
using Games.GlobeDefine;
using GCGame.Table;
using Module.Log;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// Author: Blastom
/// 通用工具类型代码
/// </summary>
public static class CommonUtility
{
/// <summary>
/// 客户端转到服务器位置精度的倍数
/// </summary>
public const float clientToServerPos = 10000f;
/// <summary>
/// 服务器转到客户端位置的精度倍数
/// </summary>
public const float serverToClientPos = 1f / clientToServerPos;
private const int _maxJumpRecursive = 10;
// 默认每次跳跃动画花费的时间
private const float _jumpAnimTime = 1f;
private static float _lastGetUnityTime;
private static int _lastGetTime;
/// <summary>
/// 数组Find方法
/// </summary>
public static T Find<T>(this T[] array, Predicate<T> condition)
{
var index = array.FindIndex(condition);
return index < 0 ? default(T) : array[index];
}
/// <summary>
/// 数组FindIndex方法
/// </summary>
public static int FindIndex<T>(this T[] array, Predicate<T> condition)
{
var index = -1;
for (var i = 0; i < array.Length; i++)
if (condition(array[i]))
{
index = i;
break;
}
return index;
}
public static string RemoveExtension(this string assetPath)
{
var i = assetPath.LastIndexOf('.');
if (i > 0)
assetPath = assetPath.Remove(i);
return assetPath;
}
/// <summary>
/// 数组Contain方法
/// </summary>
public static bool Contain<T>(this T[] array, T item)
{
var result = false;
for (var i = 0; i < array.Length; i++)
if (array[i].Equals(item))
{
result = true;
break;
}
return result;
}
public static string GetHierarchyName(this Transform transform)
{
var builder = new StringBuilder();
builder.Append(transform.gameObject.name);
transform = transform.parent;
while (transform)
{
builder.Insert(0, transform.gameObject.name + "/");
transform = transform.parent;
}
return builder.ToString();
}
public static void ToCopyClip(string text)
{
var textEditor = new TextEditor { text = text };
textEditor.SelectAll();
textEditor.Copy();
}
/// <summary>
/// 将用Vector2表示的方向转换为服务器弧度值
/// </summary>
public static int DirToServerRadian(Vector2 axis)
{
return DirToServerRadian(axis.x, axis.y);
}
/// <summary>
/// 将用x和z表示的方向转换为服务器弧度值
/// </summary>
public static int DirToServerRadian(float x, float z)
{
var dirRadian = Mathf.Atan2(z, x);
if (dirRadian < 0)
dirRadian += 2 * Mathf.PI;
return Mathf.FloorToInt(dirRadian * 100f);
}
/// <summary>
/// 将服务器弧度值转换为客户端旋转方向
/// </summary>
public static Quaternion ServerRadianToQuaternion(int dirRadian)
{
var radian = Mathf.PI * 0.5f - dirRadian * 0.01f;
return Quaternion.Euler(0f, radian * Mathf.Rad2Deg, 0f);
}
/// <summary>
/// 确保物体拥有组件T
/// </summary>
public static T EnsureComponent<T>(this GameObject gameObject) where T : Component
{
var component = gameObject.GetComponent<T>();
if (component == null)
component = gameObject.AddComponent<T>();
return component;
}
/// <summary>
/// 检查物体是否拥有组件
/// </summary>
public static bool HasComponent<T>(this GameObject gameObject) where T : Component
{
var component = gameObject.GetComponent<T>();
return component != null;
}
/// <summary>
/// 检查物体是否拥有组件
/// </summary>
public static bool HasComponent<T>(this Component item) where T : Component
{
return HasComponent<T>(item.gameObject);
}
/// <summary>
/// 删除GameObject名称后面的Clone
/// </summary>
public static void TrimCloneInName(this GameObject gameObject)
{
const string clone = "(Clone)";
var name = gameObject.name;
if (name.EndsWith(clone))
{
name = name.Remove(name.Length - clone.Length);
gameObject.name = name;
}
}
/// <summary>
/// 获得平方数
/// </summary>
public static float ToSquare(this float source)
{
return source * source;
}
/// <summary>
/// 整数转换为掩码数值
/// </summary>
public static int ToFlag(this int i)
{
return 1 << i;
}
/// <summary>
/// 千分秒整数时间转化为浮点时间
/// </summary>
public static float ToClientTime(this int time)
{
return time * 0.001f;
}
/// <summary>
/// 检查一个整数是否包含一个标志位
/// </summary>
public static bool ContainFlag(this int source, int flag)
{
return flag > 0 && (source & flag) == flag;
}
/// <summary>
/// 将一个整数位替换为新的数值
/// </summary>
public static int ReplaceFlag(this int source, int flag, bool isOn)
{
return isOn ? source | flag : source & ~flag;
}
/// <summary>
/// 复制RectTransform参数
/// </summary>
public static void CopyTo(this RectTransform source, RectTransform target)
{
target.pivot = source.pivot;
target.anchorMin = source.anchorMin;
target.anchorMax = source.anchorMax;
target.anchoredPosition = source.anchoredPosition;
target.sizeDelta = source.sizeDelta;
target.SetParent(source.parent, false);
}
/// <summary>
/// 高精度输出Vector3数值的方法
/// </summary>
public static string ToPreciseString(this Vector3 source)
{
return string.Format("({0}, {1}, {2})", source.x, source.y, source.z);
}
/// <summary>
/// 高精度输出Vector2数值的方法
/// </summary>
public static string ToPreciseString(this Vector2 source)
{
return string.Format("({0}, {1})", source.x, source.y);
}
/// <summary>
/// 按照Root偏移一个本地Offset之后的世界位置
/// </summary>
/// <param name="root">原始坐标</param>
/// <param name="offset">偏移数值</param>
/// <returns>偏移后世界坐标</returns>
public static Vector3 RootOffsetPosition(Transform root, Vector3 offset)
{
return root.localToWorldMatrix.MultiplyPoint3x4(offset);
}
public static AnimationCurve GetAnimationCurveById(int shakeId)
{
AnimationCurve result = null;
if (shakeId > 0)
{
var keyFrameList = TableManager.GetAnimationCurveByID(shakeId);
if (keyFrameList != null && keyFrameList.Count > 0)
{
result = new AnimationCurve();
for (var i = 0; i < keyFrameList.Count; i++)
{
var data = keyFrameList[i];
var keyFrame =
new Keyframe(data.Time, data.Value, data.InTangent, data.OutTangent)
{
tangentMode = data.TangentMode
};
result.AddKey(keyFrame);
}
result.preWrapMode = (WrapMode)keyFrameList[0].PreWrapMode;
result.postWrapMode = (WrapMode)keyFrameList[0].PostWrapMode;
}
}
#if UNITY_EDITOR
if (result == null)
LogModule.ErrorLog(string.Format("无法获得id为{0}的动画曲线!", shakeId));
#endif
return result;
}
/// <summary>
/// 将一个数值校正到循环范围内
/// </summary>
public static float LoopFloat(float source, float minValue, float maxValue)
{
var cycle = maxValue - minValue;
var multiplier = Mathf.Floor((source - minValue) / cycle);
return source - multiplier * cycle;
}
/// <summary>
/// 设置自己和子节点到某个layer
/// </summary>
public static void SetLayerRecursively(this GameObject gameObject, string layerName)
{
var layer = LayerMask.NameToLayer(layerName);
SetLayerRecursively(gameObject, layer);
}
/// <summary>
/// 设置自己和子节点到某个layer
/// </summary>
public static void SetLayerRecursively(this GameObject gameObject, int layer)
{
SetLayerRecursively(gameObject.transform, layer);
}
private static void SetLayerRecursively(Transform transform, int layer)
{
transform.gameObject.layer = layer;
foreach (Transform child in transform)
SetLayersRecursively(child, layer);
}
public static Transform FindIgnoreCase(this Transform root, string name)
{
Transform result = null;
foreach (Transform child in root)
if (string.Equals(child.gameObject.name, name, StringComparison.CurrentCultureIgnoreCase))
{
result = child;
break;
}
return result;
}
/// <summary>
/// 将物体转换为目标类型转换失败输出错误和物体路径用于代替项目中使用as转换屏蔽错误的情况。
/// </summary>
public static TTarget TryCastToType<TTarget>(this MonoBehaviour source) where TTarget : MonoBehaviour
{
var target = source as TTarget;
if (target == null)
LogModule.ErrorLog(string.Format("无法将组件{0}转换为{1}, 所在路径{2}", source.GetType(), typeof(TTarget),
source.transform.GetHierarchyName()));
return target;
}
/// <summary>
/// 试图获得一个数据表,获得失败输出错误,用于代替项目中获得空数据后跳过的情况
/// </summary>
public static TTarget TryGetTable<TTarget>(int index, Func<int, TTarget> func) where TTarget : class
{
var target = func(index);
if (target == null)
LogModule.ErrorLog(string.Format("无法获得id = {0},类型 = {1}的数据表!", index, typeof(TTarget)));
return target;
}
// 临时使用的Resources.Load接口 - 后期考虑改为同步读取预加载的AssetBundle
public static Material LoadSharedMaterial(string name)
{
return LoadFromResources<Material>(name);
}
public static bool IsCharacterShader(this Shader shader)
{
//return shader.name.StartsWith(_characterShaderHeading, StringComparison.CurrentCultureIgnoreCase);
// 2019.04.08 过滤粒子
// 之前有不过滤Shader名的情况无法确认具体需要哪一些组合
// 注particle 有particle/ 和 particles/ 等多种组合搞正则表达式Loose End会更多
return !shader.name.Contains("particle", StringComparison.CurrentCultureIgnoreCase);
}
/// <summary>
/// 技能是否是一个以地面为目标的技能
/// </summary>
public static bool IsTargetGround(this Tab_SkillEx skillEx)
{
var selector = GetSkillSelector(skillEx);
if (selector == null)
return false;
return selector.RangCenter > 0 || selector.RangType > 2;
}
/// <summary>
/// 技能是否是一个冲锋技能
/// </summary>
public static bool IsMoveSkill(this Tab_SkillEx skillEx)
{
return GetMoveSkillImpact(skillEx) != null;
}
/// <summary>
/// 获得技能的冲锋模块Impact
/// </summary>
public static Tab_Impact GetMoveSkillImpact(this Tab_SkillEx skillEx)
{
Tab_Impact result = null;
var impactCount = skillEx.getImpactIdCount();
for (var i = 0; i < impactCount; i++)
{
var impactId = skillEx.GetImpactIdbyIndex(i);
if (impactId >= 0)
{
var impact = TableManager.GetImpactByID(impactId);
if (GlobeVar.moveSkillLogicIds.Contain(impact.LogicID))
{
result = impact;
break;
}
}
}
return result;
}
/// <summary>
/// 技能是否存在下一段。注:普攻不是这个逻辑。
/// </summary>
public static bool HasNextCombo(this Tab_SkillEx skillEx)
{
//var hasNext = false;
//if (skillEx.NextStageSkillId >= 0)
//{
// var nextSkill = GameManager.gameManager.PlayerDataPool.GetOwnSkillInfo(skillEx.NextStageSkillId);
// if (nextSkill != null)
// hasNext = nextSkill.IsCooldownFinish();
//}
//return hasNext;
return skillEx.NextStageSkillId >= 0 &&
GameManager.gameManager.PlayerDataPool.GetOwnSkillInfo(skillEx.NextStageSkillId) != null;
}
/// <summary>
/// 通过SkillEx获得SkillSelector
/// </summary>
public static Tab_SkillTargetSelector GetSkillSelector(this Tab_SkillEx skillEx)
{
Tab_SkillTargetSelector result = null;
var selectorId = skillEx.GetSelectorIdbyIndex(0);
if (selectorId > -1)
result = TryGetTable(selectorId, a => TableManager.GetSkillTargetSelectorByID(a));
return result;
}
/// <summary>
/// 配置Root以及其以下的GameObject的layer
/// </summary>
public static void SetLayersRecursively(Transform root, int layer)
{
var transforms = root.GetComponentsInChildren<Transform>(true);
for (var i = 0; i < transforms.Length; i++)
transforms[i].gameObject.layer = layer;
}
/// <summary>
/// 防止值重复赋值引起的重绘
/// </summary>
/// <param name="text"></param>
/// <param name="val"></param>
/// <returns></returns>
public static Text EnsureVal(this Text text, string val)
{
if (!text.text.Equals(val))
text.text = val;
return text;
}
/// <summary>
/// 支持增加StringComparison标签的Contain函数
/// </summary>
public static bool Contains(this string source, string target, StringComparison comparison)
{
return !string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target) &&
source.IndexOf(target, comparison) >= 0;
}
public static Vector3 InsertY(this Vector2 source, float y = 0f)
{
return new Vector3(source.x, y, source.y);
}
public static Vector2 RemoveY(this Vector3 source)
{
return new Vector2(source.x, source.z);
}
/// <summary>
/// 获得当前的时间描述
/// </summary>
public static string GetCurrentTimeString(bool showInterval = true)
{
var time = GetCurrentTimeInMilliseconds();
var result = time.ToString();
if (showInterval)
result = string.Format("({0} - {1})", result, time - _lastGetTime);
_lastGetTime = time;
return result;
}
public static string GetCurrentUnityTimeString(bool showInterval = true)
{
var result = showInterval
? string.Format("({0} - {1})", Time.unscaledTime, Time.unscaledTime - _lastGetUnityTime)
: Time.unscaledTime.ToString(CultureInfo.InvariantCulture);
_lastGetUnityTime = Time.unscaledTime;
return result;
}
/// <summary>
/// 获得当前时间的毫秒数值
/// </summary>
private static int GetCurrentTimeInMilliseconds()
{
return (int)(DateTime.Now - DateTime.Today).TotalMilliseconds;
}
public static Vector3 ParseVector3(string source)
{
var result = Vector3.zero;
var segments = source.Split(';');
result.x = float.Parse(segments[0]);
result.y = float.Parse(segments[1]);
result.z = float.Parse(segments[2]);
return result;
}
/// <summary>
/// 服务器位置坐标转换为客户端位置坐标
/// </summary>
public static float ToClientPos(this int source)
{
return source * serverToClientPos;
}
/// <summary>
/// 服务器位置坐标转换为客户端位置坐标
/// </summary>
public static Vector2 ToClientPos(this Vector2 source)
{
return source * serverToClientPos;
}
/// <summary>
/// 服务器位置坐标转换为客户端位置坐标
/// </summary>
public static Vector3 ToClientPos(this Vector3 source)
{
return source * serverToClientPos;
}
/// <summary>
/// 客户端坐标位置转换为服务器使用的整数坐标
/// </summary>
public static int ToServerPos(this float source)
{
return Mathf.FloorToInt(source * clientToServerPos);
}
/// <summary>
/// 检查一个资源路径是否为项目资源路径
/// </summary>
public static bool IsCommonAssetPath(this string assetPath)
{
return !string.IsNullOrEmpty(assetPath) && assetPath.StartsWith(AssetConst.nonInternalHeader);
}
public static T GetMaxValue<T>(params T[] values) where T : IComparable
{
if (values.Length > 0)
{
var result = values[0];
for (var i = 1; i < values.Length; i++)
if (result.CompareTo(values[i]) < 0)
result = values[i];
return result;
}
return default(T);
}
public static bool IsAutoCombatCopyScene(int fubenId)
{
var fubenData = TableManager.GetFubenByID(fubenId);
return IsAutoCombatCopyScene(fubenData);
}
public static bool IsAutoCombatCopyScene(Tab_Fuben fubenData)
{
return FubenAutoType(fubenData) == Games.GlobeDefine.FubenAutoType.auto;
}
public static int FubenAutoType()
{
var fubenId = GlobeVar.INVALID_ID;
var cache = GameManager.gameManager.PlayerDataPool.EnterSceneCache;
if (cache != null)
fubenId = cache.EnterCopySceneID;
if (fubenId <= GlobeVar.INVALID_ID)
fubenId = GameManager.gameManager.RunningScene;
return FubenAutoType(fubenId);
}
public static int FubenAutoType(int fubenId)
{
var fubenData = TableManager.GetFubenByID(fubenId);
return FubenAutoType(fubenData);
}
public static int FubenAutoType(Tab_Fuben fubenData)
{
return fubenData == null ? 0 : fubenData.AutoType;
}
public static bool IsCopyScene(this Tab_SceneClass sceneClassData)
{
return sceneClassData.SceneID != (int)GameDefine_Globe.SCENE_DEFINE.SCENE_GUILD &&
sceneClassData.Type == (int)GameDefine_Globe.SCENE_TYPE.SCENETYPE_COPYSCENE;
}
public static Tab_Jump GetFinialJumpData(this Tab_Jump jumpData)
{
var firstId = jumpData.JumpId;
for (var i = 0; i < _maxJumpRecursive; i++)
if (jumpData.NextJumpId > GlobeVar.INVALID_ID)
{
var temp = TableManager.GetJumpByID(jumpData.NextJumpId);
if (temp == null)
{
LogModule.ErrorLog(string.Format("无法Jump{0}的NextJump{1}", jumpData.JumpId, jumpData.NextJumpId));
break;
}
jumpData = temp;
}
else
{
break;
}
if (jumpData.NextJumpId > -1)
LogModule.ErrorLog(string.Format("跳跃点{0}迭代超过{1}次,检查是否配表问题,或者提高代码中迭代上限!", firstId, _maxJumpRecursive));
return jumpData;
}
public static Tab_Jump GetFinialJumpData(this Tab_Jump jumpData, out float jumpTime)
{
jumpTime = 0f;
var firstId = jumpData.JumpId;
for (var i = 0; i < _maxJumpRecursive; i++)
{
jumpTime += jumpData.Duration.ToClientTime();
if (jumpData.NextJumpId > GlobeVar.INVALID_ID)
{
var temp = TableManager.GetJumpByID(jumpData.NextJumpId);
if (temp == null)
{
LogModule.ErrorLog(string.Format("无法Jump{0}的NextJump{1}", jumpData.JumpId, jumpData.NextJumpId));
break;
}
jumpData = temp;
}
else
{
break;
}
}
if (jumpData.NextJumpId > -1)
LogModule.ErrorLog(string.Format("跳跃点{0}迭代超过{1}次,检查是否配表问题,或者提高代码中迭代上限!", firstId, _maxJumpRecursive));
// 如果不是瞬间传送,则增加最后一段的落地动画时间
if (jumpTime > 0f)
jumpTime += _jumpAnimTime;
return jumpData;
}
public static Tab_SkillEx GetSkillMaxLevelByBaseId(int baseId)
{
Tab_SkillEx result = null;
var maxLevel = int.MinValue;
var skillExDict = TableManager.GetSkillEx();
foreach (var value in skillExDict.Values)
{
var skillEx = value;
if (skillEx.BaseId == baseId && skillEx.Level > maxLevel)
{
result = skillEx;
maxLevel = skillEx.Level;
}
}
return result;
}
// 注C#不支持对泛型限制为枚举
public static T GetMaxEnum<T>() where T : struct, IComparable
{
var values = (T[])Enum.GetValues(typeof(T));
return GetMaxValue(values);
}
/// <summary>
/// 计算射线同水平面的交点
/// </summary>
public static Vector3 RayToHorizontalPlane(Ray ray, float y)
{
if (ray.direction.y == 0f)
{
LogModule.ErrorLog("水平射线无法同水平面相交!");
return ray.origin;
}
var a = (y - ray.origin.y) / ray.direction.y;
return ray.origin + ray.direction * a;
}
/// <summary>
/// 从路径上获得一段距离后的点
/// </summary>
public static Vector3 GetPointOnPath(this NavMeshPath path, Vector3 startPos, float distance)
{
var corners = path.corners;
if (corners.Length > 1)
for (var i = 1; i < corners.Length; i++)
{
var length = Vector3.Distance(corners[i], startPos);
if (length >= distance)
{
startPos = Vector3.Lerp(startPos, corners[i], distance / length);
break;
}
startPos = corners[i];
distance -= length;
}
return startPos;
}
/// <summary>
/// 对一个数组进行冒泡排序
/// </summary>
public static void SortArray<T>(this T[] array, Comparison<T> comparison)
{
for (var i = 0; i < array.Length; i++)
for (var j = i + 1; j < array.Length; j++)
if (comparison(array[i], array[j]) > 0)
{
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
/// <summary>
/// 获得路径总长度
/// </summary>
public static float GetTotalDistance(this NavMeshPath path)
{
var length = 0f;
var corners = path.corners;
if (corners.Length > 1)
for (var i = 1; i < corners.Length; i++)
length += Vector3.Distance(corners[i], corners[i - 1]);
return length;
}
public static T Max<T>(params T[] items) where T : IComparable
{
if (items.Length > 0)
{
var result = items[0];
for (var i = 1; i < items.Length; i++)
if (items[i].CompareTo(result) > 0)
result = items[i];
return result;
}
return default(T);
}
public static TV GetItem<T, TV>(this List<TV> list, T id, ListGetMode mode) where TV : ListItemBase<T>, new()
{
TV result = null;
for (var i = 0; i < list.Count; i++)
if (list[i].id.Equals(id))
{
result = list[i];
if (mode == ListGetMode.remove)
list.RemoveAt(i);
break;
}
if (mode == ListGetMode.create && result == null)
{
result = new TV { id = id };
list.Add(result);
}
return result;
}
public static int GetIndex<T, TV>(this List<TV> list, T id) where TV : ListItemBase<T>, new()
{
var result = -1;
for (var i = 0; i < list.Count; i++)
if (list[i].id.Equals(id))
{
result = i;
break;
}
return result;
}
#region
#if UNITY_EDITOR
private static List<string> _resourcePaths;
#endif
public static T LoadFromResources<T>(string name) where T : Object
{
T result = null;
name = Path.GetFileNameWithoutExtension(name);
#if UNITY_EDITOR
if (AssetUpdateManager.useResources)
{
if (_resourcePaths == null)
{
var assetPaths = AssetDatabase.GetAllAssetPaths();
_resourcePaths = new List<string>();
for (var i = 0; i < assetPaths.Length; i++)
{
var assetPath = assetPaths[i];
if (assetPath.StartsWith("Assets/Project/GameRes"))
{
var extension = Path.GetExtension(assetPath);
if (!string.IsNullOrEmpty(extension))
_resourcePaths.Add(assetPath);
}
}
}
var assetHeader = ObjectTypeToGameResPath(typeof(T));
for (var i = 0; i < _resourcePaths.Count; i++)
if (_resourcePaths[i].StartsWith(assetHeader) &&
Path.GetFileNameWithoutExtension(_resourcePaths[i]) == name)
{
result = AssetDatabase.LoadAssetAtPath<T>(_resourcePaths[i]);
if (result != null)
break;
}
return result;
}
#endif
var bundleName = LoadAssetBundle.FixBundleName(LoadAssetBundle.BUNDLE_PATH_GAMERES);
if (AssetManager.instance == null)
{
throw new Exception("AssetManager.instance is null");
}
return AssetManager.instance.LoadAssetSync<T>(bundleName, name);
}
#if UNITY_EDITOR
public static string ObjectTypeToGameResPath(Type itemType)
{
var typeName = itemType.ToString();
var dotIndex = typeName.LastIndexOf('.');
if (dotIndex >= 0)
typeName = typeName.Substring(dotIndex + 1);
return "Assets/Project/GameRes".Open(typeName);
}
#endif
// public static void LoadGameObjectInstance(string bundleName, string assetName, LoadBundleAssetCallback<GameObject> callBack, bool archive)
// {
//#if UNITY_EDITOR
// if (AssetBundleManager.SimulateAssetBundleInEditor)
// {
// var result = EditorTestUtility.LoadGameObject(assetName);
// callBack(assetName, result, null);
// return;
// }
//#endif
// LoadAssetBundle.Instance.LoadGameObject(bundleName, assetName, callBack, null, archive);
// }
public static void LoadAssetInstance(string bundleName, string assetName,
LoadBundleAssetCallback<GameObject> callBack, bool archive)
{
#if UNITY_EDITOR
if (AssetUpdateManager.useResources)
{
var result = EditorTestUtility.LoadGameObject(assetName);
callBack(assetName, result, null);
return;
}
#endif
// 使用LoadUI接口统一处理所有类型的加载LoadGameObject会将bundleName额外加上assetName
LoadAssetBundle.Instance.LoadUI(bundleName, assetName, callBack, null, archive);
}
#endregion
}
/// <summary>
/// 列表用键值对KeyValue是struct不易用于存在判断
/// </summary>
public class MyTuple<T1, T2>
{
public T1 first;
public T2 second;
public MyTuple()
{
}
public MyTuple(T1 first, T2 second)
{
this.first = first;
this.second = second;
}
}
#region
// 不想再重复处理列表匹配、添加、删除之类的事情了,新的列表元素都继承匹配方法,直接用扩展统一处理
public abstract class ListItemBase<T>
{
public T id;
}
/// <summary>
/// 列表获取方法,用来让频繁访问的列表,避开匿名委托,又能用单个访问方法处理多种需求的工具
/// </summary>
public enum ListGetMode
{
get,
create,
remove
}
#endregion