This commit is contained in:
PC-20230316NUNE\Administrator
2024-10-18 20:35:04 +08:00
parent 31db916fec
commit 425f2eabea
721 changed files with 77521 additions and 22559 deletions

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5b22c58989b9453f804027e8b5abe655
timeCreated: 1710383267

View File

@@ -0,0 +1,11 @@
using System;
namespace GAS.General
{
[Serializable]
public class JsonData
{
public string Type;
public string Data;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7a7cb94b9dba4808bbaa27c7ddfe6f37
timeCreated: 1709455860

View File

@@ -0,0 +1,27 @@
using System;
namespace GAS.General
{
public class ObservableValue<T>
{
private T _value;
public ObservableValue(T initialValue)
{
_value = initialValue;
}
public T Value
{
get => _value;
set
{
var oldValue = _value;
_value = value;
OnValueChanged?.Invoke(oldValue, value);
}
}
public event Action<T, T> OnValueChanged;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 49847b3f12774f21ba8dbb0970c7dcb6
timeCreated: 1702438524

View File

@@ -0,0 +1,184 @@
namespace GAS.General
{
public static class GASTextDefine
{
public const string TITLE_SETTING = "设置";
public const string TITLE_PATHS = "路径";
public const string TITLE_BASE_INFO = "基本信息";
public const string TITLE_DESCRIPTION = "描述";
#region GASSettingAsset
public const string TIP_CREATE_GEN_AscUtilCode =
"<color=white><size=15>生成ASC拓展类之前一定要保证AbilityAttributeSet的集合工具类已经生成。因为ASC拓展类依赖于此</size></color>";
public const string TIP_CREATE_FOLDERS =
"<color=white><size=15>如果你修改了EX-GAS的配置Asset路径,请点击这个按钮来确保所有子文件夹和基础配置文件正确生成。</size></color>";
public const string LABEL_OF_CodeGeneratePath = "脚本生成路径";
public const string LABEL_OF_GASConfigAssetPath = "配置文件Asset路径";
public const string BUTTON_CheckAllPathFolderExist = " 检查子目录和基础配置";
public const string BUTTON_GenerateAscExtensionCode = " 生成AbilitySystemComponentExtension类脚本";
#endregion
#region Tag
public const string BUTTON_ExpandAllTag = "展开全部";
public const string BUTTON_CollapseAllTag = "折叠全部";
public const string BUTTON_AddTag = "添加Tag";
public const string BUTTON_RemoveTag = "移除Tag";
public const string BUTTON_GenTagCode = "生成TagLib";
#endregion
#region Attribute
public const string TIP_Warning_EmptyAttribute =
"<size=13><color=yellow><color=orange>Attribute名</color>不准为<color=red><b>空</b></color>! " +
"Please check!</color></size>";
public const string BUTTON_GenerateAttributeCollection = " 生成AttrLib";
public const string TIP_Warning_DuplicatedAttribute =
"<size=13><color=yellow>The <color=orange>Attribute名</color> 禁止 <color=red><b>重复</b></color>!\n" +
"重复的Attributes名:<size=15><b><color=white> {0} </color></b></size>.</color></size>";
#endregion
#region AttributeSet
public const string ERROR_DuplicatedAttribute = "<size=16><b>存在重复Attribute</b></size>";
public const string ERROR_Empty = "<size=16><b>AttributeSet至少要有一个Attribute</b></size>";
public const string ERROR_EmptyName = "<size=16><b>AttributeSet名不可以为空</b></size>";
public const string ERROR_InElements = "<size=16><b><color=orange>请先修复AttributeSet的提示错误!</color></b></size>";
public const string ERROR_DuplicatedAttributeSet = "<size=16><b><color=orange>存在重复AttributeSet!\n" +
"<color=white> -> {0}</color></color></b></size>";
public const string BUTTON_GenerateAttributeSetCode = " 生成AttrSetLib";
#endregion
#region GameplayEffect
public const string TIP_BASE_INFO="仅用于描述,方便理解。";
public const string TIP_GE_POLICY="Instant=瞬时Duration=持续性Infinite=永久";
public const string LABLE_GE_NAME = "效果名称";
public const string LABLE_GE_DESCRIPTION = "效果描述";
public const string TITLE_GE_POLICY="Gameplay Effect实施策略";
public const string LABLE_GE_POLICY = "时限策略";
public const string LABLE_GE_DURATION = "持续时间";
public const string LABLE_GE_INTERVAL = "间隔时间";
public const string LABLE_GE_EXEC = "间隔效果";
public const string LABLE_GE_SnapshotPolicy = "快照策略";
public const string TITLE_GE_GrantedAbilities = "授予能力(Ability)";
public const string TITLE_GE_MOD = "修改器Modifier";
public const string TITLE_GE_TAG = "标签Tag";
public const string TITLE_GE_CUE = "提示Cue";
public const string TITLE_GE_TAG_AssetTags = "AssetTags - 该[游戏效果]自身的标签";
public const string TIP_GE_TAG_AssetTags = "AssetTags: 标签用于描述[游戏效果]自身的特定属性,包括但不限于伤害、治疗、控制等效果类型。\n这些标签有助于区分和定义[游戏效果]的作用和表现。\n可配合RemoveGameplayEffectsWithTags食用。";
public const string TITLE_GE_TAG_GrantedTags = "GrantedTags - 授予目标单位的标签";
public const string TIP_GE_TAG_GrantedTags = "GrantedTags: 当[游戏效果]生效时,标签会被添加到目标单位上,并在[游戏效果]失效时移除。\n该标签对即时型Instant[游戏效果]的无效。";
public const string TITLE_GE_TAG_ApplicationRequiredTags = "ApplicationRequiredTags - 应用该[游戏效果]的【全部】前提";
public const string TIP_GE_TAG_ApplicationRequiredTags = "ApplicationRequiredTags: [游戏效果]的目标单位必须具备【所有】这些标签才能应用于目标单位。\n如果想表达【任一】标签不可作用于目标应该使用ApplicationImmunityTags标签。";
public const string TITLE_GE_TAG_OngoingRequiredTags = "OngoingRequiredTags - 激活该[游戏效果]的【全部】前提";
public const string TIP_GE_TAG_OngoingRequiredTags = "OngoingRequiredTags: [游戏效果]的目标单位必须具备【全部】这些标签,否则该效果不会触发。\n一旦[游戏效果]被施加,如果目标单位在效果持续期间标签发生变化,导致不再具备【全部】这些标签,效果将失效;反之,如果满足条件,效果将被激活。\n该标签对即时型Instant[游戏效果]的无效。";
public const string TITLE_GE_TAG_RemoveGameplayEffectsWithTags = "RemoveGameplayEffectsWithTags - 移除具有【任一】标签的[游戏效果]";
public const string TIP_GE_TAG_RemoveGameplayEffectsWithTags = "RemoveGameplayEffectsWithTags: [游戏效果]的目标单位当前持有的所有[游戏效果]中其AssetTags或GrantedTags中具有【任一】这些标签的[游戏效果]将被移除。";
public const string TITLE_GE_TAG_ApplicationImmunityTags = "ApplicationImmunityTags - 无法应用于具有【任一】标签的目标";
public const string TIP_GE_TAG_ApplicationImmunityTags = "ApplicationImmunityTags: 该[游戏效果]无法作用于拥有【任一】这些标签的目标单位。";
public const string TITLE_GE_CUE_CueOnExecute = "CueOnExecute - 执行时触发";
public const string TITLE_GE_CUE_CueDurational = "CueDurational - 存在时持续触发";
public const string TITLE_GE_CUE_CueOnAdd = "CueOnAdd - 添加时触发";
public const string TITLE_GE_CUE_CueOnRemove = "CueOnRemove - 移除时触发";
public const string TITLE_GE_CUE_CueOnActivate = "CueOnActivate - 激活时触发";
public const string TITLE_GE_CUE_CueOnDeactivate = "CueOnDeactivate - 失活时触发";
public const string LABEL_GRANT_ABILITY = "授予能力";
public const string LABEL_GRANT_ABILITY_LEVEL = "能力等级";
public const string LABEL_GRANT_ABILITY_ACTIVATION_POLICY = "激活策略";
public const string LABEL_GRANT_ABILITY_DEACTIVATION_POLICY = "失活策略";
public const string LABEL_GRANT_ABILITY_REMOVE_POLICY = "移除策略";
public const string TIP_GRANT_ABILITY_ACTIVATION_POLICY = "None = 不激活需要用户手动调用ASC相关接口激活; " +
"WhenAdded = 添加时就激活;" +
"SyncWithEffect = 同步GEGE激活时激活";
public const string TIP_GRANT_ABILITY_DEACTIVATION_POLICY = "None = 无相关取消激活逻辑, 需要用户调用ASC取消激活; " +
"SyncWithEffect = 同步GEGE失活时取消激活";
public const string TIP_GRANT_ABILITY_REMOVE_POLICY = "None = 不移除能力;" +
"SyncWithEffect = 同步GEGE移除时移除" +
"WhenEnd = 能力结束时,移除自身;" +
"WhenCancel = 能力取消时,移除自身;" +
"WhenCancelOrEnd = 能力取消或结束时,移除自身";
public const string TITLE_GE_STACKING = "堆叠配置";
public const string LABEL_GE_STACKING_CODENAME = "堆叠GE识别码";
public const string LABEL_GE_STACKING_TYPE = "堆叠类型";
public const string LABEL_GE_STACKING_COUNT = "堆叠上限";
public const string LABEL_GE_STACKING_DURATION_REFRESH_POLICY = "持续时间刷新策略";
public const string LABEL_GE_STACKING_PERIOD_RESET_POLICY = "周期重置策略";
public const string LABEL_GE_STACKING_EXPIRATION_POLICY = "持续时间结束策略";
public const string LABEL_GE_STACKING_DENY_OVERFLOW_APPLICATION = "溢出的GE不生效";
public const string LABEL_GE_STACKING_CLEAR_STACK_ON_OVERFLOW = "溢出时清空堆叠";
public const string LABEL_GE_STACKING_CLEAR_OVERFLOW_EFFECTS = "溢出时触发的GE";
#endregion
#region Ability
public const string ABILITY_BASEINFO="基本信息";
public const string TIP_UNAME =
"<size=12><b><color=white><color=orange>U-Name非常重要!</color>" +
"GAS会使用U-Name作为Ability的标识符。" +
"所以你必须保证U-Name的唯一性。" +
"别担心生成AbilityLib时工具会提醒你这一点。</color></b></size>";
public const string ABILITY_CD_TIME="冷却时长";
public const string ABILITY_EFFECT_CD="冷却效果";
public const string ABILITY_EFFECT_COST="消耗效果";
public const string ABILITY_MANUAL_ENDABILITY = "手动结束能力";
public const string BUTTON_CHECK_TIMELINE_ABILITY = "查看/编辑能力时间轴";
public const string ABILITY_PLAY_RATE = "播放速率";
#endregion
#region ASC
public const string TIP_ASC_BASEINFO="基本信息只用于描述这个ASC方便策划阅读理解该ASC。";
public const string ASC_BASE_TAG="固有标签";
public const string ASC_BASE_ABILITY="固有能力";
public const string ASC_AttributeSet="属性集";
#endregion
#region Watcher
public const string TIP_WATCHER = "该窗口用于监视GAS运行状态,建议在调试GAS的角色能力效果时打开该窗口。";
public const string TIP_WATCHER_OnlyForGameRunning =
"<size=20><b><color=yellow>监视器只在游戏运行时可用.</color></b></size>";
#endregion
#region Gameplay Cue
public const string CUE_ANIMATION_PATH = "动画机相对路径";
public const string CUE_ANIMATION_INCLUDE_CHILDREN = "包括子节点";
public const string CUE_ANIMATION_INCLUDE_CHILDREN_ANIMATOR_TIP = "在自身及孩子节点中查找动画机, 当你的动画机路径不能完全确定时(例如: 不同的皮肤节点不一致), 可勾选此项";
public const string CUE_ANIMATION_STATE = "Animation State名";
public const string CUE_ANIMATION_PATH_TIP = "为空表示物体根节点,示例:'A'=根节点下名为'A'的子物体,'A/B'='A'节点下名为'B'的子物体";
public const string CUE_SOUND_EFFECT = "音效源";
public const string CUE_ATTACH_TO_OWNER = "是否附加到Owner";
public const string CUE_VFX_PREFAB = "特效预制体";
public const string CUE_VFX_OFFSET = "特效偏移";
public const string CUE_VFX_ROTATION = "特效旋转";
public const string CUE_VFX_SCALE = "特效缩放";
public const string CUE_VFX_ACTIVE_WHEN_ADDED = "是否在添加时就被激活";
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 40ecb12e788648c49235e3b85f466825
timeCreated: 1706946276

View File

@@ -0,0 +1,44 @@
using System;
using UnityEngine;
namespace GAS.General
{
public class GASTimer
{
// TODO 矫正时间差(服务器客户端时间差/暂停游戏导致的时间差)
static int _deltaTime;
public static long Timestamp() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() + _deltaTime;
public static long TimestampSeconds() => Timestamp() / 1000;
private static int _currentFrameCount;
public static int CurrentFrameCount => _currentFrameCount;
public static void UpdateCurrentFrameCount()
{
_currentFrameCount = Mathf.FloorToInt((Timestamp() - _startTimestamp) / 1000f * FrameRate);
}
private static long _startTimestamp;
public static long StartTimestamp => _startTimestamp;
public static void InitStartTimestamp()
{
_startTimestamp = Timestamp();
}
private static long _pauseTimestamp;
public static void Pause()
{
_pauseTimestamp = Timestamp();
}
public static void Unpause()
{
_deltaTime -= (int)(Timestamp() - _pauseTimestamp);
}
private static int _frameRate = 60;
public static int FrameRate => _frameRate;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9378cf08054e430f8fd59ca809378c8a
timeCreated: 1702545659

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ecd3bc3b1a964d4bb6a2fd56126fbefc
timeCreated: 1709716270

View File

@@ -0,0 +1,506 @@
using UnityEngine;
namespace GAS.General
{
public static class DebugExtension
{
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DebugBox(Vector2 center, Vector2 size, float angle, Color color, float showTime)
{
var halfSize = size * 0.5f;
var p1 = new Vector2(center.x - halfSize.x, center.y - halfSize.y);
var p2 = new Vector2(center.x + halfSize.x, center.y - halfSize.y);
var p3 = new Vector2(center.x + halfSize.x, center.y + halfSize.y);
var p4 = new Vector2(center.x - halfSize.x, center.y + halfSize.y);
// p1 绕center旋转angle角度
p1 = RotatePoint(center, p1, angle);
p2 = RotatePoint(center, p2, angle);
p3 = RotatePoint(center, p3, angle);
p4 = RotatePoint(center, p4, angle);
DrawLine(p1, p2, color, showTime);
DrawLine(p2, p3, color, showTime);
DrawLine(p3, p4, color, showTime);
DrawLine(p4, p1, color, showTime);
}
public static Vector2 RotatePoint(Vector2 center, Vector2 point, float angle)
{
var cos = Mathf.Cos(angle * Mathf.Deg2Rad);
var sin = Mathf.Sin(angle * Mathf.Deg2Rad);
var x = cos * (point.x - center.x) - sin * (point.y - center.y) + center.x;
var y = sin * (point.x - center.x) + cos * (point.y - center.y) + center.y;
return new Vector2(x, y);
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DebugDrawCircle(Vector2 center, float radius, Color color, float showTime, float segments = 120)
{
var step = 360f / segments;
var from = center + new Vector2(radius, 0);
for (var i = 0; i < segments; i++)
{
var to = center + new Vector2(radius * Mathf.Cos((i + 1) * step * Mathf.Deg2Rad),
radius * Mathf.Sin((i + 1) * step * Mathf.Deg2Rad));
DrawLine(from, to, color, showTime);
from = to;
}
}
/// <summary>
/// 绘制一个圆形
/// </summary>
/// <param name="position">位置, 圆心</param>
/// <param name="rotation">旋转</param>
/// <param name="radius">半径</param>
/// <param name="segments">分段数(建议与角度适配, 如每10°分一段: segments = Mathf.CeilToInt(angle / 10))</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawCircle(in Vector3 position, in Quaternion rotation, float radius, int segments = 36,
in Color? color = null, float duration = 0f)
{
DrawArc(position, rotation, radius, 360, segments, color, duration);
}
public static void DrawCircle(in Vector3 position, in Vector3 forward, float radius, int segments = 36,
in Color? color = null, float duration = 0f)
{
DrawCircle(position, Quaternion.LookRotation(forward), radius, segments, color, duration);
}
/// <summary>
/// 绘制一个圆弧
/// </summary>
/// <param name="position">位置</param>
/// <param name="rotation">旋转</param>
/// <param name="radius">半径</param>
/// <param name="angle">角度</param>
/// <param name="segments">分段数(建议与角度适配, 如每10°分一段: segments = Mathf.CeilToInt(angle / 10))(建议与角度适配, 如每10°分一段: segments = Mathf.CeilToInt(angle / 10))</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawArc(in Vector3 position, in Quaternion rotation, float radius, float angle, int segments = 12,
in Color? color = null, float duration = 0f)
{
if (angle <= 0) return;
if (radius <= 0) return;
if (segments <= 0) return;
var angleStep = angle / segments;
var lastPoint = position + rotation * (Vector3.right * radius);
for (int i = 1; i <= segments; i++)
{
var currentAngle = i * angleStep;
var nextPoint = position + rotation * (Quaternion.Euler(0, currentAngle, 0) * Vector3.right * radius);
DrawLine(lastPoint, nextPoint, color, duration);
lastPoint = nextPoint;
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawArc(in Vector3 position, in Vector3 forward, float radius, float angle, int segments = 12,
in Color? color = null, float duration = 0f)
{
DrawArc(position, Quaternion.LookRotation(forward), radius, angle, segments, color, duration);
}
/// <summary>
/// 绘制一个圆环
/// </summary>
/// <param name="position">位置, 圆心</param>
/// <param name="rotation">旋转</param>
/// <param name="outerRadius">外圈半径</param>
/// <param name="innerRadius">内圈半径</param>
/// <param name="segments">分段数(建议与角度适配, 如每10°分一段: segments = Mathf.CeilToInt(angle / 10))</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawRing(in Vector3 position, in Quaternion rotation, float outerRadius, float innerRadius, int segments = 36,
in Color? color = null, float duration = 0f)
{
if (outerRadius <= 0) return;
if (segments <= 0) return;
innerRadius = Mathf.Clamp(innerRadius, 0, outerRadius);
// 计算圆的每个点的位置
var angleStep = 360f / segments;
var lastOuterPoint = position + rotation * (Vector3.right * outerRadius);
var lastInnerPoint = position + rotation * (Vector3.right * innerRadius);
for (int i = 1; i <= segments; i++)
{
var angle = i * angleStep;
var nextOuterPoint = position + rotation * (Quaternion.Euler(0, angle, 0) * Vector3.right * outerRadius);
var nextInnerPoint = position + rotation * (Quaternion.Euler(0, angle, 0) * Vector3.right * innerRadius);
DrawLine(lastOuterPoint, nextOuterPoint, color, duration);
DrawLine(lastInnerPoint, nextInnerPoint, color, duration);
DrawLine(nextOuterPoint, nextInnerPoint, color, duration);
lastOuterPoint = nextOuterPoint;
lastInnerPoint = nextInnerPoint;
}
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawRing(in Vector3 position, in Vector3 forward, float outerRadius, float innerRadius, int segments = 36,
in Color? color = null, float duration = 0f)
{
DrawRing(position, Quaternion.LookRotation(forward), outerRadius, innerRadius, segments, color, duration);
}
/// <summary>
/// 绘制一个矩形
/// </summary>
/// <param name="position">位置, 矩形中心点</param>
/// <param name="rotation">旋转</param>
/// <param name="size">矩形的宽长, 宽:左右(垂直于朝向), 长:前后(与朝向一致)</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawRectangle(in Vector3 position, in Quaternion rotation, in Vector2 size,
in Color? color = null, float duration = 0f)
{
// 计算矩形的四个角点在局部坐标系中的位置
var halfSize = new Vector3(size.x * 0.5f, 0, size.y * 0.5f);
var corners = new Vector3[4];
corners[0] = new Vector3(-halfSize.x, 0, -halfSize.z); // 左下角
corners[1] = new Vector3(halfSize.x, 0, -halfSize.z); // 右下角
corners[2] = new Vector3(halfSize.x, 0, halfSize.z); // 右上角
corners[3] = new Vector3(-halfSize.x, 0, halfSize.z); // 左上角
// 旋转角点并转换到世界坐标系
for (int i = 0; i < corners.Length; i++)
{
corners[i] = position + rotation * corners[i];
}
// 绘制矩形的四条边
DrawLine(corners[0], corners[1], color, duration); // 底边
DrawLine(corners[1], corners[2], color, duration); // 右边
DrawLine(corners[2], corners[3], color, duration); // 顶边
DrawLine(corners[3], corners[0], color, duration); // 左边
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawRectangle(in Vector3 position, in Vector3 forward, in Vector2 size,
in Color? color = null, float duration = 0f)
{
DrawRectangle(position, Quaternion.LookRotation(forward), size, color, duration);
}
/// <summary>
/// 基于位置和朝向, 在其前方绘制一个矩形(如: 基于技能释放者前方的矩形攻击范围)
/// </summary>
/// <param name="position">位置</param>
/// <param name="rotation">旋转</param>
/// <param name="size">矩形的宽长(宽:左右, 长:前后)</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawFrontRectangle(in Vector3 position, in Quaternion rotation, in Vector2 size,
in Color? color = null, float duration = 0f)
{
// 计算矩形的四个角点在局部坐标系中的位置
var halfX = size.x * 0.5f;
var corners = new Vector3[4];
corners[0] = new Vector3(-halfX, 0, 0); // 左下角
corners[1] = new Vector3(halfX, 0, 0); // 右下角
corners[2] = new Vector3(halfX, 0, size.y); // 右上角
corners[3] = new Vector3(-halfX, 0, size.y); // 左上角
// 旋转角点并转换到世界坐标系
for (int i = 0; i < corners.Length; i++)
{
corners[i] = position + rotation * corners[i];
}
// 绘制矩形的四条边
DrawLine(corners[0], corners[1], color, duration); // 底边
DrawLine(corners[1], corners[2], color, duration); // 右边
DrawLine(corners[2], corners[3], color, duration); // 顶边
DrawLine(corners[3], corners[0], color, duration); // 左边
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawFrontRectangle(in Vector3 position, in Vector3 forward, in Vector2 size,
in Color? color = null, float duration = 0f)
{
DrawFrontRectangle(position, Quaternion.LookRotation(forward), size, color, duration);
}
/// <summary>
/// 绘制一个立方体
/// </summary>
/// <param name="position">立方体中心点</param>
/// <param name="rotation">立方体的朝向</param>
/// <param name="size">立方体的尺寸</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawCube(in Vector3 position, in Quaternion rotation, in Vector3 size,
in Color? color = null, float duration = 0f)
{
// 计算立方体的八个顶点在局部坐标系中的位置
var halfSize = size * 0.5f;
var vertices = new Vector3[8];
// 下底面四个顶点
vertices[0] = new Vector3(-halfSize.x, -halfSize.y, -halfSize.z);
vertices[1] = new Vector3(halfSize.x, -halfSize.y, -halfSize.z);
vertices[2] = new Vector3(halfSize.x, -halfSize.y, halfSize.z);
vertices[3] = new Vector3(-halfSize.x, -halfSize.y, halfSize.z);
// 上顶面四个顶点
vertices[4] = new Vector3(-halfSize.x, halfSize.y, -halfSize.z);
vertices[5] = new Vector3(halfSize.x, halfSize.y, -halfSize.z);
vertices[6] = new Vector3(halfSize.x, halfSize.y, halfSize.z);
vertices[7] = new Vector3(-halfSize.x, halfSize.y, halfSize.z);
// 旋转顶点并转换到世界坐标系
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = position + rotation * vertices[i];
}
// 绘制立方体的十二条边
DrawLine(vertices[0], vertices[1], color, duration); // 底面边
DrawLine(vertices[1], vertices[2], color, duration);
DrawLine(vertices[2], vertices[3], color, duration);
DrawLine(vertices[3], vertices[0], color, duration);
DrawLine(vertices[4], vertices[5], color, duration); // 顶面边
DrawLine(vertices[5], vertices[6], color, duration);
DrawLine(vertices[6], vertices[7], color, duration);
DrawLine(vertices[7], vertices[4], color, duration);
DrawLine(vertices[0], vertices[4], color, duration); // 竖向边
DrawLine(vertices[1], vertices[5], color, duration);
DrawLine(vertices[2], vertices[6], color, duration);
DrawLine(vertices[3], vertices[7], color, duration);
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawCube(in Vector3 position, in Vector3 forward, in Vector3 size,
in Color? color = null, float duration = 0f)
{
DrawCube(position, Quaternion.LookRotation(forward), size, color, duration);
}
/// <summary>
/// 绘制一个圆柱体
/// </summary>
/// <param name="position"></param>
/// <param name="rotation">朝向, 以圆柱的侧面为forward/right, 上下面为up</param>
/// <param name="radius"></param>
/// <param name="height"></param>
/// <param name="segments"> </param>
/// <param name="color"></param>
/// <param name="duration"></param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawCylinder(in Vector3 position, in Quaternion rotation, float radius, float height, int segments = 12,
in Color? color = null, float duration = 0f)
{
var offset = rotation * Vector3.up * (height * 0.5f);
var topCenter = position + offset;
var bottomCenter = position - offset;
DrawCylinder(topCenter, bottomCenter, radius, segments, color, duration);
}
/// <summary>
/// 绘制一个圆柱体
/// </summary>
/// <param name="position"></param>
/// <param name="forward">朝向, forward是圆柱体的侧面(其实对于圆柱体而言forward/right都一样)</param>
/// <param name="radius"></param>
/// <param name="height"></param>
/// <param name="segments"></param>
/// <param name="color"></param>
/// <param name="duration"></param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawCylinder(in Vector3 position, in Vector3 forward, float radius, float height, int segments = 12,
in Color? color = null, float duration = 0f)
{
DrawCylinder(position, Quaternion.LookRotation(forward), radius, height, segments, in color, duration);
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawCylinder(in Vector3 topCenter, in Vector3 bottomCenter, float radius, int segments = 12,
in Color? color = null, float duration = 0f)
{
if (radius <= 0) return;
if (segments <= 0) return;
if (Vector3.Distance(topCenter, bottomCenter) <= Mathf.Epsilon) return;
var direction = (topCenter - bottomCenter).normalized;
// 计算一个垂直于direction的向量作为forward
var forward = Vector3.Cross(direction, Vector3.up).normalized;
// 如果forward和direction平行即direction和Vector3.up共线则选择一个不同的方向作为forward
if (forward == Vector3.zero)
{
forward = Vector3.Cross(direction, Vector3.right).normalized;
}
var rotation = Quaternion.LookRotation(forward, direction);
var angleStep = 360f / segments;
var offset = rotation * (Vector3.right * radius);
var lastTopPoint = topCenter + offset;
var lastBottomPoint = bottomCenter + offset;
for (int i = 1; i <= segments; i++)
{
var currentAngle = i * angleStep;
offset = rotation * (Quaternion.Euler(0, currentAngle, 0) * (Vector3.right * radius));
var nextTopPoint = topCenter + offset;
var nextBottomPoint = bottomCenter + offset;
DrawLine(lastTopPoint, nextTopPoint, color, duration);
DrawLine(lastBottomPoint, nextBottomPoint, color, duration);
DrawLine(nextTopPoint, nextBottomPoint, color, duration);
lastTopPoint = nextTopPoint;
lastBottomPoint = nextBottomPoint;
}
}
/// <summary>
/// 绘制一个扇形
/// </summary>
/// <param name="position">位置, 扇形所在圆的圆心</param>
/// <param name="rotation">旋转</param>
/// <param name="radius">扇形的半径</param>
/// <param name="angle">扇形的角度(度)</param>
/// <param name="segments">分段数(建议与角度适配, 如每10°分一段: segments = Mathf.CeilToInt(angle / 10))</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawSector(Vector3 position, Quaternion rotation, float radius, float angle, int segments = 12,
Color? color = null, float duration = 0f)
{
DrawAnnularSector(position, rotation, radius, 0, angle, segments, color, duration);
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawSector(in Vector3 position, in Vector3 forward, float radius, float angle, int segments = 12,
in Color? color = null, float duration = 0f)
{
DrawSector(position, Quaternion.LookRotation(forward), radius, angle, segments, color, duration);
}
/// <summary>
/// 绘制一个环形扇区
/// </summary>
/// <param name="position">位置, 扇形所在圆的圆心</param>
/// <param name="rotation">旋转</param>
/// <param name="outerRadius">扇形的外半径</param>
/// <param name="innerRadius">扇形的内半径, 为0时为标准扇形</param>
/// <param name="angle">扇形的角度(度)</param>
/// <param name="segments">分段数(建议与角度适配, 如每10°分一段: segments = Mathf.CeilToInt(angle / 10))</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawAnnularSector(in Vector3 position, in Quaternion rotation, float outerRadius, float innerRadius, float angle, int segments = 12,
in Color? color = null, float duration = 0f)
{
if (outerRadius <= 0) return;
if (segments <= 0) return;
if (angle <= 0) return;
innerRadius = Mathf.Clamp(innerRadius, 0, outerRadius);
var angleStep = angle / segments;
var currentAngle = -angle * 0.5f;
var outerPoints = new Vector3[segments + 1];
var innerPoints = new Vector3[segments + 1];
// 计算内外扇形的点
for (var i = 0; i <= segments; i++)
{
var currentDirection = rotation * Quaternion.Euler(0, currentAngle, 0) * Vector3.forward;
outerPoints[i] = position + currentDirection * outerRadius;
innerPoints[i] = position + currentDirection * innerRadius;
currentAngle += angleStep;
}
// 绘制内外扇形的边缘
for (var i = 0; i <= segments; i++)
{
// 绘制内扇形的边缘
if (i > 0)
{
DrawLine(innerPoints[i - 1], innerPoints[i], color, duration);
DrawLine(outerPoints[i - 1], outerPoints[i], color, duration);
}
// 绘制从中心到内外扇形点的线段
DrawLine(innerPoints[i], outerPoints[i], color, duration);
}
// 绘制内扇形的两侧边缘
DrawLine(position, innerPoints[0], color, duration);
DrawLine(position, innerPoints[segments], color, duration);
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawAnnularSector(in Vector3 position, in Vector3 forward, float outerRadius, float innerRadius, float angle, int segments = 12, in Color? color = null, float duration = 0f)
{
DrawAnnularSector(position, Quaternion.LookRotation(forward), outerRadius, innerRadius, angle, segments, color, duration);
}
/// <summary>
/// 绘制一条线段
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawLine(in Vector3 start, in Vector3 end, in Color? color = null, float duration = 0f)
{
Debug.DrawLine(start, end, color ?? Color.cyan, duration);
}
/// <summary>
/// 绘制一个箭头
/// </summary>
/// <param name="origin"></param>
/// <param name="destination"></param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawArrow(in Vector3 origin, in Vector3 destination, in Color? color = null, float duration = 0f)
{
DrawLine(origin, destination, color, duration);
var direction = destination - origin;
var right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + 30, 0) * Vector3.forward;
var left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - 30, 0) * Vector3.forward;
DrawLine(destination, destination + right, color, duration);
DrawLine(destination, destination + left, color, duration);
}
/// <summary>
/// 绘制一个箭头
/// </summary>
/// <param name="origin"></param>
/// <param name="rotation"></param>
/// <param name="length">长度</param>
/// <param name="color">颜色</param>
/// <param name="duration">绘制时长(0为1帧)</param>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawArrow(in Vector3 origin, in Quaternion rotation, float length, in Color? color = null, float duration = 0f)
{
var direction = rotation * Vector3.forward;
var destination = origin + direction * length;
DrawArrow(origin, destination, color, duration);
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void DrawArrow(in Vector3 origin, in Vector3 direction, float length, in Color? color = null, float duration = 0f)
{
var destination = origin + direction.normalized * length;
DrawArrow(origin, destination, color, duration);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 517b2cdf2c8042bda08d8f5d0d4d8995
timeCreated: 1709821901

View File

@@ -0,0 +1,90 @@
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Animations;
#endif
namespace GAS.General
{
public static class GASAnimatorUtil
{
/// <summary>
/// Only For Editor
/// </summary>
/// <param name="animator"></param>
/// <param name="layerIndex"></param>
/// <returns></returns>
public static Dictionary<string, AnimationClip> GetAllAnimationState(this Animator animator, int layerIndex = 0)
{
#pragma warning disable 162
#if UNITY_EDITOR
var result = new Dictionary<string, AnimationClip>();
var runtimeController = animator.runtimeAnimatorController;
if (runtimeController == null)
{
Debug.LogError("RuntimeAnimatorController must not be null.");
return null;
}
if (animator.runtimeAnimatorController is AnimatorOverrideController)
{
var overrideController =
AssetDatabase.LoadAssetAtPath<AnimatorOverrideController>(
AssetDatabase.GetAssetPath(runtimeController));
if (overrideController == null)
{
Debug.LogErrorFormat("AnimatorOverrideController must not be null.");
return null;
}
var controller =
AssetDatabase.LoadAssetAtPath<AnimatorController>(
AssetDatabase.GetAssetPath(overrideController.runtimeAnimatorController));
var overrides = new List<KeyValuePair<AnimationClip, AnimationClip>>();
overrideController.GetOverrides(overrides);
// 获取 Layer 的状态机
var stateMachine = controller.layers[layerIndex].stateMachine;
// 遍历所有状态并打印名称
foreach (var state in stateMachine.states)
{
if (state.state.motion is AnimationClip clip)
{
foreach (var pair in overrides)
{
if (pair.Key.name == clip.name)
{
result.Add(state.state.name, pair.Value);
break;
}
}
if (!result.ContainsKey(state.state.name)) result.Add(state.state.name, clip);
}
}
}
else
{
var controller =
AssetDatabase.LoadAssetAtPath<AnimatorController>(AssetDatabase.GetAssetPath(runtimeController));
if (controller == null)
{
Debug.LogErrorFormat("AnimatorController must not be null.");
return null;
}
// 获取第一个 Layer 的状态机
var stateMachine = controller.layers[layerIndex].stateMachine;
// 遍历所有状态并打印名称
foreach (var state in stateMachine.states)
result.Add(state.state.name, state.state.motion as AnimationClip);
}
return result;
#endif
return null;
#pragma warning restore 162
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 709355e72df34ede8668a186cad79f12
timeCreated: 1709716282

View File

@@ -0,0 +1,13 @@
using System.ComponentModel;
namespace System.Runtime.CompilerServices
{
/// <summary>
/// 让Unity支持init setter
/// <see href="https://docs.unity.cn/cn/2022.3/Manual/CSharpCompiler.html">unity文档</see>。
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public class IsExternalInit
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ac5d19bb315743de9f460aae665ccd42
timeCreated: 1723619186

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6f50d4142b94bc3b37233ff4806abf6
timeCreated: 1723102748

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using UnityEngine;
using UnityEngine.Profiling;
namespace GAS.General
{
public class ArrayPool<T>
{
private readonly int _maxCapacity;
private readonly ConcurrentDictionary<int, Pool> _items = new();
public ArrayPool(int maxCapacity = 1024)
{
if (maxCapacity <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxCapacity), "maxCapacity must be greater than 0");
}
_maxCapacity = maxCapacity;
}
public T[] Fetch(int length)
{
return _items.TryGetValue(length, out var pool) ? pool.Get() : new T[length];
}
public bool Recycle(T[] obj)
{
if (obj == null)
{
#if UNITY_EDITOR
Debug.LogWarning("ArrayPool<T>.Recycle: obj is null");
#endif
}
else
{
if (!_items.TryGetValue(obj.Length, out var queue))
{
Profiler.BeginSample("ArrayPoolEx<T>.CreateInstance");
queue = _items[obj.Length] = new Pool(obj.Length, _maxCapacity);
Profiler.EndSample();
}
return queue.Return(obj);
}
return false;
}
private class Pool
{
private readonly int _arrayLength;
private readonly int _maxCapacity;
private int _numItems;
private readonly ConcurrentQueue<T[]> _items = new();
private T[] _fastItem;
public Pool(int arrayLength, int maxCapacity = 128)
{
if (_arrayLength < 0)
{
throw new ArgumentOutOfRangeException(nameof(arrayLength), "arrayLength must be greater than or equal to 0");
}
if (maxCapacity <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxCapacity), "maxCapacity must be greater than 0");
}
_arrayLength = arrayLength;
_maxCapacity = maxCapacity;
}
public T[] Get()
{
var item = _fastItem;
if (item == null || Interlocked.CompareExchange(ref _fastItem, null, item) != item)
{
if (_items.TryDequeue(out item))
{
Interlocked.Decrement(ref _numItems);
}
else
{
Profiler.BeginSample("ArrayPool<T>.Pool<TT>.CreateInstance");
item = new T[_arrayLength];
Profiler.EndSample();
}
}
return item;
}
public bool Return(T[] obj)
{
if (obj == null)
{
return false;
}
if (obj.Length != _arrayLength)
{
Debug.LogWarning($"Array length({obj.Length}) not match({_arrayLength})");
return false;
}
if (_fastItem != null || Interlocked.CompareExchange(ref _fastItem, obj, null) != null)
{
if (Interlocked.Increment(ref _numItems) <= _maxCapacity)
{
_items.Enqueue(obj);
return true;
}
Interlocked.Decrement(ref _numItems);
#if UNITY_EDITOR
Debug.LogWarning($"ArrayPool<{typeof(T).FullName}>.Return: Exceed max capacity({_maxCapacity}), consider increase max capacity.");
#endif
}
return false;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ce03b164d35f457b9ce6ff277db4e735
timeCreated: 1723102741

View File

@@ -0,0 +1,152 @@
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Threading;
using UnityEngine;
using UnityEngine.Profiling;
namespace GAS.General
{
public interface IPool
{
bool IsFromPool { get; set; }
}
public class JexGasObjectPool
{
private static JexGasObjectPool _singleton;
public static JexGasObjectPool Instance => _singleton ??= new JexGasObjectPool();
private readonly ConcurrentDictionary<Type, Pool> _objPool = new();
private readonly Func<Type, Pool> _addPoolFunc = type => new Pool(type, 1024);
public static void Awake()
{
_singleton = null;
_singleton = new JexGasObjectPool();
}
public static void Destroy()
{
_singleton = null;
}
public T Fetch<T>() where T : class
{
var type = typeof(T);
var obj = Fetch(type);
return obj as T;
}
public object Fetch(Type type, bool isFromPool = true)
{
object obj;
if (!isFromPool)
{
Profiler.BeginSample("ObjectPool.CreateInstance");
obj = Activator.CreateInstance(type);
Profiler.EndSample();
}
else
{
var pool = GetPool(type);
obj = pool.Get();
if (obj is IPool p)
{
p.IsFromPool = true;
}
}
return obj;
}
public void Recycle(object obj)
{
if (obj is IPool p)
{
if (!p.IsFromPool)
{
return;
}
// 防止多次入池
p.IsFromPool = false;
}
var type = obj.GetType();
var pool = GetPool(type);
pool.Return(obj);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Pool GetPool(Type type)
{
return _objPool.GetOrAdd(type, _addPoolFunc);
}
/// <summary>
/// 线程安全的无锁对象池
/// </summary>
private class Pool
{
private readonly Type _objectType;
private readonly int _maxCapacity;
private int _numItems;
private readonly ConcurrentQueue<object> _items = new();
private object _fastItem;
public Pool(Type objectType, int maxCapacity)
{
if (maxCapacity <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxCapacity), "maxCapacity must be greater than 0");
}
_objectType = objectType;
_maxCapacity = maxCapacity;
}
public object Get()
{
object item = _fastItem;
if (item == null || Interlocked.CompareExchange(ref _fastItem, null, item) != item)
{
if (_items.TryDequeue(out item))
{
Interlocked.Decrement(ref _numItems);
}
else
{
Profiler.BeginSample("Pool.CreateInstance");
item = Activator.CreateInstance(_objectType);
Profiler.EndSample();
}
}
return item;
}
public bool Return(object obj)
{
if (_fastItem != null || Interlocked.CompareExchange(ref _fastItem, obj, null) != null)
{
if (Interlocked.Increment(ref _numItems) <= _maxCapacity)
{
_items.Enqueue(obj);
return true;
}
Interlocked.Decrement(ref _numItems);
#if UNITY_EDITOR
Debug.LogWarning($"Pool<{_objectType.FullName}>.Return: Exceed max capacity({_maxCapacity}), consider increase max capacity.");
#endif
}
return false;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d258d352f22a4281ab0968530960bee5
timeCreated: 1713765955

View File

@@ -0,0 +1,350 @@
using System;
using System.Collections.Generic;
namespace GAS.General
{
/// <summary>
/// 按优先级取值
/// <para>可以动态修改默认优先级和默认值, 适用于延迟初始化的情况</para>
/// <para>通过设置默认值, 配合RemoveAll()使用可立刻恢复默认值</para>
/// <para>一般来说默认值的优先级应该是最低的, 但是允许默认值的优先级高于其他值</para>
/// <para>优先级可以相同, 则取优先级为: 默认值 > 先添加的值 > 后添加的值</para>
/// </summary>
public sealed class PriorityValue<T> : IDisposable
{
#if false //C# 10.0+
public readonly record struct Data(string Key, int Priority, T Value);
#else
public record Data(string Key, int Priority, T Value)
{
public string Key { get; } = Key;
public int Priority { get; } = Priority;
public T Value { get; } = Value;
}
#endif
private readonly List<Data> _datas; // 按优先级降序排列
private readonly object _lock;
public string CurrentKey
{
get
{
lock (_lock)
{
return _currentData.Key;
}
}
}
public int CurrentPriority
{
get
{
lock (_lock)
{
return _currentData.Priority;
}
}
}
public T CurrentValue
{
get
{
lock (_lock)
{
return _currentData.Value;
}
}
}
public string DefaultKey => "@default";
public int DefaultPriority
{
get
{
lock (_lock)
{
return _defaultData.Priority;
}
}
}
public T DefaultValue
{
get
{
lock (_lock)
{
return _defaultData.Value;
}
}
}
private Data _defaultData;
private Data _currentData;
public delegate void PostValueChanged(T oldValue, T newValue);
private event PostValueChanged OnPostValueChanged;
public PriorityValue() : this(default)
{
}
public PriorityValue(T defaultValue) :
this(int.MinValue, defaultValue)
{
}
public PriorityValue(int defaultPriority, T defaultValue)
{
_defaultData = new(DefaultKey, defaultPriority, defaultValue);
_currentData = _defaultData;
_datas = new();
_lock = new();
}
public T Add(string key, int priority, T value)
{
lock (_lock)
{
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
var index = _datas.FindIndex(data => data.Key == key);
if (index < 0)
{
Insert(key, priority, value);
Refresh();
}
else
{
throw new ArgumentException($"Key {key} already exists.");
}
return CurrentValue;
}
}
public T AddOrSet(string key, int priority, T value)
{
lock (_lock)
{
if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key));
var index = _datas.FindIndex(data => data.Key == key);
if (index >= 0)
{
if (_datas[index].Priority == priority)
{
_datas[index] = new(key, priority, value);
}
else
{
_datas.RemoveAt(index);
Insert(key, priority, value);
}
}
else
{
Insert(key, priority, value);
}
Refresh();
return CurrentValue;
}
}
private void Insert(string key, int priority, T value)
{
var data = new Data(key, priority, value);
var insertIndex = _datas.Count;
for (var i = 0; i < _datas.Count; i++)
{
if (priority > _datas[i].Priority)
{
insertIndex = i;
break;
}
}
_datas.Insert(insertIndex, data);
}
public T SetDefault(int priority, T value)
{
lock (_lock)
{
if (_defaultData.Priority != priority || !EqualityComparer<T>.Default.Equals(_defaultData.Value, value))
{
_defaultData = new(DefaultKey, priority, value);
Refresh();
}
return CurrentValue;
}
}
public T SetDefaultPriority(int priority)
{
return SetDefault(priority, DefaultValue);
}
public T SetDefaultValue(T value)
{
return SetDefault(DefaultPriority, value);
}
public T Remove(string key)
{
lock (_lock)
{
var index = _datas.FindIndex(data => data.Key == key);
if (index >= 0)
{
_datas.RemoveAt(index);
Refresh();
}
return CurrentValue;
}
}
public T RemoveAll()
{
lock (_lock)
{
if (_datas.Count > 0)
{
_datas.Clear();
Refresh();
}
return CurrentValue;
}
}
public void Dispose()
{
lock (_lock)
{
_currentData = _defaultData;
_datas.Clear();
OnPostValueChanged = null;
}
}
private void Refresh()
{
var oldValue = _currentData.Value;
_currentData = _defaultData;
if (_datas.Count > 0)
{
var data = _datas[0];
if (data.Priority > _defaultData.Priority)
{
_currentData = data;
}
}
if (EqualityComparer<T>.Default.Equals(oldValue, _currentData.Value) == false)
{
OnPostValueChanged?.Invoke(oldValue, _currentData.Value);
}
}
public void RegisterPostValueChanged(PostValueChanged listener)
{
lock (_lock)
{
OnPostValueChanged += listener;
}
}
public void UnregisterPostValueChanged(PostValueChanged listener)
{
lock (_lock)
{
OnPostValueChanged -= listener;
}
}
}
/// <summary>
/// 通过Enable控制PriorityValue的某个key是否生效, 省去了手动管理Add/Remove等繁琐操作
/// </summary>
public sealed class PriorityValueToggle<T> : IDisposable
{
private PriorityValue<T>.Data _data;
public PriorityValue<T> PriorityValue { get; }
public string Key => _data.Key;
public int Priority
{
get => _data.Priority;
set
{
if (_data.Priority != value)
{
_data = new(Key, value, Value);
if (Enabled)
{
PriorityValue?.AddOrSet(Key, Priority, Value);
}
}
}
}
public T Value
{
get => _data.Value;
set
{
if (!EqualityComparer<T>.Default.Equals(_data.Value, value))
{
_data = new(Key, Priority, value);
if (Enabled)
{
PriorityValue?.AddOrSet(Key, Priority, Value);
}
}
}
}
private bool _enabled;
public bool Enabled
{
get => _enabled;
set
{
if (_enabled != value)
{
_enabled = value;
if (_enabled) PriorityValue?.AddOrSet(Key, Priority, Value);
else PriorityValue?.Remove(Key);
}
}
}
public PriorityValueToggle(PriorityValue<T> priorityValue, string key, int priority, T value, bool enableImmediately = false)
{
PriorityValue = priorityValue;
_data = new(key, priority, value);
if (enableImmediately)
Enabled = true;
}
public void Dispose()
{
Enabled = false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5fa92b69503043a58c6b9b79b15b8f2c
timeCreated: 1719295275

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace GAS.General
{
public static class TypeUtil
{
public static Type[] GetAllSonTypesOf(Type parentType)
{
List<Type> sonTypes = new List<Type>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes();
sonTypes.AddRange(types.Where(type => type.IsSubclassOf(parentType) && !type.IsAbstract));
}
catch (ReflectionTypeLoadException)
{
}
}
return sonTypes.ToArray();
}
public static Type FindTypeInAllAssemblies(string typeName)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
Type type = assembly.GetType(typeName);
if (type != null)
{
// 如果找到了类型,返回它
return type;
}
}
catch (ReflectionTypeLoadException)
{
// 忽略因无法加载类型而引发的异常
}
}
// 如果没有找到类型返回null
return null;
}
/// <summary>
/// 获取类型的继承链
/// </summary>
public static string[] GetInheritanceChain(this Type type, bool fullName = true)
{
var inheritanceChain = new List<string>();
var currentType = type;
while (currentType != null)
{
var name = fullName ? currentType.FullName : currentType.Name;
inheritanceChain.Add(name);
currentType = currentType.BaseType;
}
return inheritanceChain.ToArray();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d45910d5e40e47ac99874b673b71078d
timeCreated: 1709442037

View File

@@ -0,0 +1,45 @@
using System.Text.RegularExpressions;
namespace GAS.General.Validation
{
public readonly struct ValidationResult
{
public readonly bool IsValid;
public readonly string Message;
public ValidationResult(bool isValid, string message)
{
IsValid = isValid;
Message = message;
}
public static readonly ValidationResult Valid = new ValidationResult(true, null);
public static ValidationResult Invalid(string message) => new ValidationResult(false, message);
}
public delegate ValidationResult ValidationDelegate(string input);
public static class Validations
{
// https://learn.microsoft.com/zh-cn/dotnet/csharp/fundamentals/coding-style/identifier-names
// 可以在标识符上使用 @ 前缀来声明与 C# 关键字匹配的标识符。 @ 不是标识符名称的一部分。 例如,@if 声明名为 if 的标识符。
// 因此类似 @123abc 这样的标识符是不合法的。因为抛开@之后, 它实际上是以数字开头。
private const string VariableNamePattern = @"^@?[a-zA-Z_][a-zA-Z0-9_]*$";
public static readonly Regex VariableNameRegex = new Regex(VariableNamePattern);
public static ValidationResult ValidateVariableName(string name)
{
if (string.IsNullOrEmpty(name))
return ValidationResult.Invalid("The name is empty!");
return VariableNameRegex.IsMatch(name)
? ValidationResult.Valid
: ValidationResult.Invalid($"The name(\"{name}\") is invalid!");
}
public static bool IsValidVariableName(string name)
{
return ValidateVariableName(name).IsValid;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 51efeef0278341a9ab883d49a20bd485
timeCreated: 1711514345