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
///
/// Author: Blastom
/// 通用工具类型代码
///
public static class CommonUtility
{
///
/// 客户端转到服务器位置精度的倍数
///
public const float clientToServerPos = 10000f;
///
/// 服务器转到客户端位置的精度倍数
///
public const float serverToClientPos = 1f / clientToServerPos;
private const int _maxJumpRecursive = 10;
// 默认每次跳跃动画花费的时间
private const float _jumpAnimTime = 1f;
private static float _lastGetUnityTime;
private static int _lastGetTime;
///
/// 数组Find方法
///
public static T Find(this T[] array, Predicate condition)
{
var index = array.FindIndex(condition);
return index < 0 ? default(T) : array[index];
}
///
/// 数组FindIndex方法
///
public static int FindIndex(this T[] array, Predicate 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;
}
///
/// 数组Contain方法
///
public static bool Contain(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();
}
///
/// 将用Vector2表示的方向,转换为服务器弧度值
///
public static int DirToServerRadian(Vector2 axis)
{
return DirToServerRadian(axis.x, axis.y);
}
///
/// 将用x和z表示的方向,转换为服务器弧度值
///
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);
}
///
/// 将服务器弧度值转换为客户端旋转方向
///
public static Quaternion ServerRadianToQuaternion(int dirRadian)
{
var radian = Mathf.PI * 0.5f - dirRadian * 0.01f;
return Quaternion.Euler(0f, radian * Mathf.Rad2Deg, 0f);
}
///
/// 确保物体拥有组件T
///
public static T EnsureComponent(this GameObject gameObject) where T : Component
{
var component = gameObject.GetComponent();
if (component == null)
component = gameObject.AddComponent();
return component;
}
///
/// 检查物体是否拥有组件
///
public static bool HasComponent(this GameObject gameObject) where T : Component
{
var component = gameObject.GetComponent();
return component != null;
}
///
/// 检查物体是否拥有组件
///
public static bool HasComponent(this Component item) where T : Component
{
return HasComponent(item.gameObject);
}
///
/// 删除GameObject名称后面的(Clone)
///
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;
}
}
///
/// 获得平方数
///
public static float ToSquare(this float source)
{
return source * source;
}
///
/// 整数转换为掩码数值
///
public static int ToFlag(this int i)
{
return 1 << i;
}
///
/// 千分秒整数时间转化为浮点时间
///
public static float ToClientTime(this int time)
{
return time * 0.001f;
}
///
/// 检查一个整数是否包含一个标志位
///
public static bool ContainFlag(this int source, int flag)
{
return flag > 0 && (source & flag) == flag;
}
///
/// 将一个整数位替换为新的数值
///
public static int ReplaceFlag(this int source, int flag, bool isOn)
{
return isOn ? source | flag : source & ~flag;
}
///
/// 复制RectTransform参数
///
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);
}
///
/// 高精度输出Vector3数值的方法
///
public static string ToPreciseString(this Vector3 source)
{
return string.Format("({0}, {1}, {2})", source.x, source.y, source.z);
}
///
/// 高精度输出Vector2数值的方法
///
public static string ToPreciseString(this Vector2 source)
{
return string.Format("({0}, {1})", source.x, source.y);
}
///
/// 按照Root偏移一个本地Offset之后的世界位置
///
/// 原始坐标
/// 偏移数值
/// 偏移后世界坐标
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;
}
///
/// 将一个数值校正到循环范围内
///
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;
}
///
/// 设置自己和子节点到某个layer
///
public static void SetLayerRecursively(this GameObject gameObject, string layerName)
{
var layer = LayerMask.NameToLayer(layerName);
SetLayerRecursively(gameObject, layer);
}
///
/// 设置自己和子节点到某个layer
///
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;
}
///
/// 将物体转换为目标类型,转换失败输出错误和物体路径,用于代替项目中使用as转换屏蔽错误的情况。
///
public static TTarget TryCastToType(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;
}
///
/// 试图获得一个数据表,获得失败输出错误,用于代替项目中获得空数据后跳过的情况
///
public static TTarget TryGetTable(int index, Func 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(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);
}
///
/// 技能是否是一个以地面为目标的技能
///
public static bool IsTargetGround(this Tab_SkillEx skillEx)
{
var selector = GetSkillSelector(skillEx);
if (selector == null)
return false;
return selector.RangCenter > 0 || selector.RangType > 2;
}
///
/// 技能是否是一个冲锋技能
///
public static bool IsMoveSkill(this Tab_SkillEx skillEx)
{
return GetMoveSkillImpact(skillEx) != null;
}
///
/// 获得技能的冲锋模块Impact
///
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;
}
///
/// 技能是否存在下一段。注:普攻不是这个逻辑。
///
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;
}
///
/// 通过SkillEx获得SkillSelector
///
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;
}
///
/// 配置Root以及其以下的GameObject的layer
///
public static void SetLayersRecursively(Transform root, int layer)
{
var transforms = root.GetComponentsInChildren(true);
for (var i = 0; i < transforms.Length; i++)
transforms[i].gameObject.layer = layer;
}
///
/// 防止值重复赋值引起的重绘
///
///
///
///
public static Text EnsureVal(this Text text, string val)
{
if (!text.text.Equals(val))
text.text = val;
return text;
}
///
/// 支持增加StringComparison标签的Contain函数
///
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);
}
///
/// 获得当前的时间描述
///
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;
}
///
/// 获得当前时间的毫秒数值
///
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;
}
///
/// 服务器位置坐标转换为客户端位置坐标
///
public static float ToClientPos(this int source)
{
return source * serverToClientPos;
}
///
/// 服务器位置坐标转换为客户端位置坐标
///
public static Vector2 ToClientPos(this Vector2 source)
{
return source * serverToClientPos;
}
///
/// 服务器位置坐标转换为客户端位置坐标
///
public static Vector3 ToClientPos(this Vector3 source)
{
return source * serverToClientPos;
}
///
/// 客户端坐标位置转换为服务器使用的整数坐标
///
public static int ToServerPos(this float source)
{
return Mathf.FloorToInt(source * clientToServerPos);
}
///
/// 检查一个资源路径是否为项目资源路径
///
public static bool IsCommonAssetPath(this string assetPath)
{
return !string.IsNullOrEmpty(assetPath) && assetPath.StartsWith(AssetConst.nonInternalHeader);
}
public static T GetMaxValue(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() where T : struct, IComparable
{
var values = (T[])Enum.GetValues(typeof(T));
return GetMaxValue(values);
}
///
/// 计算射线同水平面的交点
///
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;
}
///
/// 从路径上获得一段距离后的点
///
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;
}
///
/// 对一个数组进行冒泡排序
///
public static void SortArray(this T[] array, Comparison 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;
}
}
///
/// 获得路径总长度
///
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(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(this List list, T id, ListGetMode mode) where TV : ListItemBase, 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(this List list, T id) where TV : ListItemBase, 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 _resourcePaths;
#endif
public static T LoadFromResources(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();
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(_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(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 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 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
}
///
/// 列表用键值对,KeyValue是struct不易用于存在判断
///
public class MyTuple
{
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
{
public T id;
}
///
/// 列表获取方法,用来让频繁访问的列表,避开匿名委托,又能用单个访问方法处理多种需求的工具
///
public enum ListGetMode
{
get,
create,
remove
}
#endregion