提交GAS 打算做一个帧同步的GAS

This commit is contained in:
DESKTOP-5RP3AKU\Jisol
2024-10-18 03:16:09 +08:00
parent b0a2e4a900
commit d9b0c78827
726 changed files with 76601 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.IO;
using GAS;
using UnityEngine;
public static class AbilitySystemComponentUtilGenerator
{
public static void Gen()
{
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
var filePath =
$"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ASCUTIL_CSHARP_SCRIPT_NAME}";
GenerateASCUtil(filePath);
}
private static void GenerateASCUtil(string filePath)
{
using var writer = new IndentedWriter(new StreamWriter(filePath));
writer.WriteLine("///////////////////////////////////");
writer.WriteLine("//// This is a generated file. ////");
writer.WriteLine("//// Do not modify it. ////");
writer.WriteLine("///////////////////////////////////");
writer.WriteLine("");
writer.WriteLine("using System;");
writer.WriteLine("using System.Linq;");
writer.WriteLine("using UnityEngine;");
writer.WriteLine("");
writer.WriteLine("namespace GAS.Runtime");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public static class AbilitySystemComponentExtension");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public static Type[] PresetAttributeSetTypes(this AbilitySystemComponent asc)");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("if (asc.Preset == null) return null;");
writer.WriteLine("var attrSetTypes = new Type[asc.Preset.AttributeSets.Length];");
writer.WriteLine("for (var i = 0; i < asc.Preset.AttributeSets.Length; i++)");
writer.Indent++;
{
writer.WriteLine(
"attrSetTypes[i] = GAttrSetLib.AttrSetTypeDict[asc.Preset.AttributeSets[i]];");
}
writer.Indent--;
writer.WriteLine("return attrSetTypes;");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
writer.WriteLine(
"public static GameplayTag[] PresetBaseTags(this AbilitySystemComponent asc)");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("if (asc.Preset == null) return null;");
writer.WriteLine("return asc.Preset.BaseTags;");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
writer.WriteLine(
"public static void InitWithPreset(this AbilitySystemComponent asc, int level, AbilitySystemComponentPreset preset = null)");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("if (preset != null) asc.SetPreset(preset);");
writer.WriteLine("if (asc.Preset == null) return;");
writer.WriteLine("");
writer.WriteLine("#if UNITY_EDITOR", true);
writer.WriteLine("if (asc.Preset.BaseAbilities != null && asc.Preset.BaseAbilities.Any(x => x == null))");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine(
"Debug.LogWarning($\"BaseAbilities contains null in preset: {asc.Preset.name}\");");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("#endif", true);
writer.WriteLine("");
writer.WriteLine(
"asc.Init(asc.PresetBaseTags(), asc.PresetAttributeSetTypes(), asc.Preset.BaseAbilities, level);");
}
writer.Indent--;
writer.WriteLine("}");
}
writer.Indent--;
writer.WriteLine("}");
}
writer.Indent--;
writer.Write("}");
Console.WriteLine($"Generated AbilitySystemComponentExtension at path: {filePath}");
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 81779b899f67484b9cb4241ac5a8077f
timeCreated: 1706092633

View File

@@ -0,0 +1,324 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using GAS.Editor.General;
using GAS.General.Validation;
using GAS.Runtime;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
namespace GAS.Editor
{
public class GASAssetAggregator : OdinMenuEditorWindow
{
private static readonly Type[] _types = new Type[5]
{
typeof(ModifierMagnitudeCalculation),
typeof(GameplayCue),
typeof(GameplayEffectAsset),
typeof(AbilityAsset),
typeof(AbilitySystemComponentPreset)
};
private static string[] _libPaths;
static string[] LibPaths
{
get
{
if (_libPaths == null) CheckLibPaths();
return _libPaths;
}
}
private static readonly DirectoryInfo[] _directoryInfos = new DirectoryInfo[5];
private static readonly List<DirectoryInfo> _subDirectoryInfos = new List<DirectoryInfo>();
private static readonly string[] MenuNames = new string[5]
{
"A- Mod Magnitude Calculation",
"A- Gameplay Cue",
"B- Gameplay Effect",
"C- Ability",
"D- Ability System Component"
};
private const string OpenWindow_MenuItemName = "EX-GAS/Asset Aggregator";
#if EX_GAS_ENABLE_HOT_KEYS
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName + " %F9";
#else
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName;
#endif
[MenuItem(OpenWindow_MenuItemNameEnh, priority = 1)]
private static void OpenWindow()
{
CheckLibPaths();
var window = GetWindow<GASAssetAggregator>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1600, 900);
window.MenuWidth = 240;
}
private void ShowButton(Rect rect)
{
if (SirenixEditorGUI.SDFIconButton(rect, "GitHub", SdfIconType.Github))
{
Application.OpenURL("https://github.com/No78Vino/gameplay-ability-system-for-unity");
}
}
private static void CheckLibPaths()
{
_libPaths = new[]
{
GASSettingAsset.MMCLibPath,
GASSettingAsset.GameplayCueLibPath,
GASSettingAsset.GameplayEffectLibPath,
GASSettingAsset.GameplayAbilityLibPath,
GASSettingAsset.ASCLibPath,
};
_subDirectoryInfos.Clear();
for (var i = 0; i < _directoryInfos.Length; i++)
{
var rootMenuName = MenuNames[i];
_directoryInfos[i] = new DirectoryInfo(rootMenuName, _libPaths[i], _libPaths[i], _types[i], true);
foreach (var subDir in _directoryInfos[i].SubDirectory)
_subDirectoryInfos.Add(new DirectoryInfo(rootMenuName, _libPaths[i], subDir, _types[i], false));
}
}
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
tree.Selection.SelectionChanged += OnMenuSelectionChange; //x => Debug.Log(x);
for (var i = 0; i < MenuNames.Length; i++)
{
var menuName = MenuNames[i];
var libPath = LibPaths[i];
var type = _types[i];
tree.Add(menuName, _directoryInfos[i]);
if (menuName == MenuNames[3])
{
tree.Add(menuName, new AbilityOverview());
}
tree.AddAllAssetsAtPath(menuName, libPath, type, true)
.AddThumbnailIcons();
}
foreach (var subDirectoryInfo in _subDirectoryInfos) tree.Add(subDirectoryInfo.MenuName, subDirectoryInfo);
tree.Config.DrawSearchToolbar = true;
tree.Config.SearchToolbarHeight = 30;
tree.Config.AutoScrollOnSelectionChanged = true;
tree.Config.DrawScrollView = true;
tree.Config.AutoHandleKeyboardNavigation = true;
tree.SortMenuItemsByName(true);
return tree;
}
protected override void OnBeginDrawEditors()
{
var selected = MenuTree.Selection.FirstOrDefault();
var toolbarHeight = MenuTree.Config.SearchToolbarHeight;
// Draws a toolbar with the name of the currently selected menu item.
SirenixEditorGUI.BeginHorizontalToolbar(toolbarHeight);
{
if (selected != null) GUILayout.Label(selected.Name + " (" + selected.Value.GetType().FullName + ")");
if (selected != null && (selected.Value is DirectoryInfo || selected.Value is AbilityOverview))
{
var directoryInfo = selected.Value is AbilityOverview
? _directoryInfos[3]
: selected.Value as DirectoryInfo;
if (directoryInfo == null)
{
return;
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("浏览")))
{
OpenDirectoryInExplorer(directoryInfo);
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建子文件夹")))
{
CreateNewSubDirectory(directoryInfo);
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建")))
{
CreateNewAsset(directoryInfo);
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
}
if (!directoryInfo.Root)
{
if (SirenixEditorGUI.ToolbarButton(new GUIContent("删除")))
{
RemoveSubDirectory(directoryInfo);
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
}
}
}
if (selected is { Value: ScriptableObject asset })
{
if (SirenixEditorGUI.ToolbarButton(new GUIContent("定位")))
{
ShowInProject(asset);
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("浏览")))
{
OpenAssetInExplorer(asset);
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("定位脚本")))
{
var monoScript = MonoScript.FromScriptableObject(asset);
string path = AssetDatabase.GetAssetPath(monoScript);
var obj = AssetDatabase.LoadAssetAtPath<Object>(path);
ShowInProject(obj);
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("编辑脚本")))
{
AssetDatabase.OpenAsset(MonoScript.FromScriptableObject(asset));
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("删除")))
{
RemoveAsset(asset);
}
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("GAS设置")))
{
GASSettingAggregator.OpenWindow();
}
}
SirenixEditorGUI.EndHorizontalToolbar();
}
private void Refresh()
{
AssetDatabase.Refresh();
CheckLibPaths();
ForceMenuTreeRebuild();
}
private void OpenDirectoryInExplorer(DirectoryInfo directoryInfo)
{
var path = directoryInfo.Directory.Replace("/", "\\");
Process.Start("explorer.exe", path);
}
private void ShowInProject(Object asset)
{
if (asset != null)
{
EditorGUIUtility.PingObject(asset);
Selection.SetActiveObjectWithContext(asset, null);
}
}
private void OpenAssetInExplorer(ScriptableObject asset)
{
var path = AssetDatabase.GetAssetPath(asset).Replace("/", "\\");
Process.Start("explorer.exe", path);
}
private void CreateNewSubDirectory(DirectoryInfo directoryInfo)
{
StringEditWindow.OpenWindow("Sub Directory Name", "",
s =>
{
var newPath = directoryInfo.Directory + "/" + s;
var isExist = AssetDatabase.IsValidFolder(newPath);
return isExist ? ValidationResult.Invalid("Folder already exists!") : ValidationResult.Valid;
},
s =>
{
var newPath = directoryInfo.Directory + "/" + s;
AssetDatabase.CreateFolder(directoryInfo.Directory, s);
Refresh();
Debug.Log($"[EX] {newPath} folder created!");
});
}
private void RemoveSubDirectory(DirectoryInfo directoryInfo)
{
if (!EditorUtility.DisplayDialog("Warning", "Are you sure you want to delete this folder?", "Yes",
"No")) return;
if (!EditorUtility.DisplayDialog("Second Warning", "ALL FILES in this folder will be DELETED!" +
"\nAre you sure you want to DELETE this Folder?", "Yes",
"No")) return;
AssetDatabase.DeleteAsset(directoryInfo.Directory);
Refresh();
Debug.Log($"[EX] {directoryInfo.Directory} folder deleted!");
}
private void CreateNewAsset(DirectoryInfo directoryInfo)
{
if (directoryInfo.AssetType == _types[0])
ScriptableObjectCreator.ShowDialog<ModifierMagnitudeCalculation>(directoryInfo.RootDirectory,
TrySelectMenuItemWithObject);
else if (directoryInfo.AssetType == _types[1])
ScriptableObjectCreator.ShowDialog<GameplayCue>(directoryInfo.RootDirectory,
TrySelectMenuItemWithObject);
else if (directoryInfo.AssetType == _types[2])
ScriptableObjectCreator.ShowDialog<GameplayEffectAsset>(directoryInfo.RootDirectory,
TrySelectMenuItemWithObject);
else if (directoryInfo.AssetType == _types[3])
ScriptableObjectCreator.ShowDialog<AbilityAsset>(directoryInfo.RootDirectory,
TrySelectMenuItemWithObject);
else if (directoryInfo.AssetType == _types[4])
ScriptableObjectCreator.ShowDialog<AbilitySystemComponentPreset>(directoryInfo.RootDirectory,
TrySelectMenuItemWithObject);
}
private void RemoveAsset(ScriptableObject asset)
{
if (asset == null)
{
EditorUtility.DisplayDialog("Warning", "The asset you want to delete is null", "Ok");
return;
}
var assetName = asset.name; // Get the name before deleting
var assetPath = AssetDatabase.GetAssetPath(asset);
if (EditorUtility.DisplayDialog("Warning",
$"Are you sure you want to delete this asset?\n\nName=\"{assetName}\"\nPath=\"{assetPath}\""
, "Yes", "No"))
{
AssetDatabase.DeleteAsset(assetPath);
Refresh();
Debug.Log($"[EX] delete asset: Name=\"{assetName}\", Path=\"{assetPath}\"");
}
}
void OnMenuSelectionChange(SelectionChangedType selectionChangedType)
{
var selected = MenuTree.Selection.FirstOrDefault();
if (selected is { Value: AbilityOverview abilityOverview })
{
abilityOverview.Refresh();
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59a3cfef5e254d3d8e63b6cfc84ec221
timeCreated: 1704078623

View File

@@ -0,0 +1,89 @@
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
namespace GAS.Editor
{
public class GASSettingAggregator : OdinMenuEditorWindow
{
private static GASSettingAsset _settingAsset;
private static GameplayTagsAsset _tagsAsset;
private static AttributeAsset _attributeAsset;
private static AttributeSetAsset _attributeSetAsset;
private static GASSettingAsset SettingAsset
{
get
{
if (_settingAsset == null) _settingAsset = GASSettingAsset.LoadOrCreate();
return _settingAsset;
}
}
private static GameplayTagsAsset TagsAsset
{
get
{
if (_tagsAsset == null) _tagsAsset = GameplayTagsAsset.LoadOrCreate();
return _tagsAsset;
}
}
private static AttributeAsset AttributeAsset
{
get
{
if (_attributeAsset == null) _attributeAsset = AttributeAsset.LoadOrCreate();
return _attributeAsset;
}
}
private static AttributeSetAsset AttributeSetAsset
{
get
{
if (_attributeSetAsset == null) _attributeSetAsset = AttributeSetAsset.LoadOrCreate();
return _attributeSetAsset;
}
}
private const string OpenWindow_MenuItemName = "EX-GAS/Settings";
#if EX_GAS_ENABLE_HOT_KEYS
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName + " %F12";
#else
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName;
#endif
[MenuItem(OpenWindow_MenuItemNameEnh, priority = 0)]
public static void OpenWindow()
{
var window = GetWindow<GASSettingAggregator>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1200, 600);
}
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
tree.Add("Setting", SettingAsset);
tree.Add("Tags", TagsAsset);
tree.Add("Attribute", AttributeAsset);
tree.Add("Attribute Set", AttributeSetAsset);
tree.Config.AutoScrollOnSelectionChanged = true;
tree.Config.DrawScrollView = true;
tree.Config.AutoHandleKeyboardNavigation = true;
tree.Selection.SelectionChanged += type =>
{
GASSettingAsset.Save();
GameplayTagsAsset.Save();
AttributeAsset.Save();
AttributeSetAsset.Save();
};
return tree;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f9375bc389fe4d75a24aa4ff97973977
timeCreated: 1704195723

View File

@@ -0,0 +1,220 @@
using System;
using System.IO;
using GAS.General;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor
{
[FilePath(GasDefine.GAS_BASE_SETTING_PATH)]
public class GASSettingAsset : ScriptableSingleton<GASSettingAsset>
{
private const int LABEL_WIDTH = 200;
private const int SHORT_LABEL_WIDTH = 200;
private static GASSettingAsset _setting;
[Title(GASTextDefine.TITLE_SETTING, Bold = true)]
[BoxGroup("A", false, order: 1)]
[LabelText(GASTextDefine.LABEL_OF_CodeGeneratePath)]
[LabelWidth(LABEL_WIDTH)]
[FolderPath]
[OnValueChanged("SaveAsset")]
public string CodeGeneratePath = "Assets/Scripts/Gen";
[BoxGroup("A")]
[LabelText(GASTextDefine.LABEL_OF_GASConfigAssetPath)]
[LabelWidth(LABEL_WIDTH)]
[FolderPath]
[OnValueChanged("SaveAsset")]
public string GASConfigAssetPath = "Assets/GAS/Config";
public static GASSettingAsset Setting
{
get
{
if (_setting == null) _setting = LoadOrCreate();
return _setting;
}
}
[ShowInInspector]
[BoxGroup("V", false, order: 0)]
[HideLabel]
[DisplayAsString(TextAlignment.Left, true)]
private static string Version =>
$"<size=15><b><color=white>EX-GAS Version: {GasDefine.GAS_VERSION}</color></b></size>";
public static string CodeGenPath => Setting.CodeGeneratePath;
[Title(GASTextDefine.TITLE_PATHS, Bold = true)]
[PropertySpace(10)]
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
public static string ASCLibPath => $"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ASC_LIBRARY_FOLDER}";
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
public static string GameplayEffectLibPath =>
$"{Setting.GASConfigAssetPath}/{GasDefine.GAS_EFFECT_LIBRARY_FOLDER}";
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
public static string GameplayAbilityLibPath =>
$"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ABILITY_LIBRARY_FOLDER}";
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
public static string GameplayCueLibPath => $"{Setting.GASConfigAssetPath}/{GasDefine.GAS_CUE_LIBRARY_FOLDER}";
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
public static string MMCLibPath => $"{Setting.GASConfigAssetPath}/{GasDefine.GAS_MMC_LIBRARY_FOLDER}";
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
public static string AbilityTaskLib =>
$"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ABILITY_TASK_LIBRARY_FOLDER}";
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
[LabelText("Tag Asset Path")]
public static string GAS_TAG_ASSET_PATH => GasDefine.GAS_TAGS_MANAGER_ASSET_PATH;
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
[LabelText("Attribute Asset Path")]
public static string GAS_ATTRIBUTE_ASSET_PATH => GasDefine.GAS_ATTRIBUTE_ASSET_PATH;
[ShowInInspector]
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[LabelWidth(SHORT_LABEL_WIDTH)]
[LabelText("AttributeSet Asset Path")]
public static string GAS_ATTRIBUTESET_ASSET_PATH => GasDefine.GAS_ATTRIBUTE_SET_ASSET_PATH;
void CheckPathFolderExist(string folderPath)
{
var folders = folderPath.Split('/');
if (folders[0] != "Assets")
{
EditorUtility.DisplayDialog("Error!", "'Config Asset Path/Code Gen Path' must start with Assets!",
"OK");
return;
}
string parentFolderPath = folders[0];
for (var i = 1; i < folders.Length; i++)
{
string newFolderName = folders[i];
if (newFolderName == "") continue;
string newFolderPath = parentFolderPath + "/" + newFolderName;
if (!AssetDatabase.IsValidFolder(newFolderPath))
{
AssetDatabase.CreateFolder(parentFolderPath, newFolderName);
Debug.Log("[EX] Folder created at path: " + newFolderPath);
}
parentFolderPath += "/" + newFolderName;
}
}
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[GUIColor(0, 0.8f, 0)]
[PropertySpace(10)]
[InfoBox(GASTextDefine.TIP_CREATE_FOLDERS)]
[Button(SdfIconType.FolderCheck, GASTextDefine.BUTTON_CheckAllPathFolderExist, ButtonHeight = 38)]
void CheckAllPathFolderExist()
{
CheckPathFolderExist(GASConfigAssetPath);
CheckPathFolderExist(CodeGeneratePath);
CheckPathFolderExist(ASCLibPath);
CheckPathFolderExist(GameplayAbilityLibPath);
CheckPathFolderExist(GameplayEffectLibPath);
CheckPathFolderExist(GameplayCueLibPath);
CheckPathFolderExist(MMCLibPath);
CheckPathFolderExist(AbilityTaskLib);
AssetDatabase.Refresh();
}
[BoxGroup("A")]
[DisplayAsString(TextAlignment.Left, true)]
[GUIColor(0.8f, 0.8f, 0)]
[PropertySpace(10)]
[InfoBox(GASTextDefine.TIP_CREATE_GEN_AscUtilCode)]
[Button(SdfIconType.Upload, GASTextDefine.BUTTON_GenerateAscExtensionCode, ButtonHeight = 38)]
void GenerateAscExtensionCode()
{
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
var filePath =
$"{pathWithoutAssets}/{CodeGenPath}/{GasDefine.GAS_ATTRIBUTESET_LIB_CSHARP_SCRIPT_NAME}";
if (!File.Exists(filePath))
{
EditorUtility.DisplayDialog("Error!", "Please generate AttributeSetAsset first!", "OK");
return;
}
AbilitySystemComponentUtilGenerator.Gen();
AssetDatabase.Refresh();
}
private void SaveAsset()
{
if (Instance == this) return;
UpdateAsset(this);
Save();
}
private const string EX_GAS_ENABLE_HOT_KEYS = "EX_GAS_ENABLE_HOT_KEYS";
#if EX_GAS_ENABLE_HOT_KEYS
public const bool EnableHotKeys = true;
#else
public const bool EnableHotKeys = false;
#endif
[TabGroup("Advance", "Advance", SdfIconType.Gear, TextColor = "#FF7F00"), PropertyOrder(1)]
[InfoBox(
"@\"当前快捷键状态: \" + (EnableHotKeys ? \"启用\":\"禁用\") + \", 冲突时可禁用快捷键\"")]
#if EX_GAS_ENABLE_HOT_KEYS
[Button(SdfIconType.ToggleOn, "禁用快捷键")]
#else
[Button(SdfIconType.ToggleOff, "开启快捷键")]
#endif
private void ToggleScriptDefineSymbol_EX_GAS_ENABLE_HOT_KEYS()
{
if (EditorUtility.DisplayDialog("Ex-GAS",
"切换快捷键状态\n将在你的项目中切换\"EX_GAS_ENABLE_HOT_KEYS\"宏定义\n\n这会重新编译你的代码, 之后你可能需要手动保存你的项目(请留意ProjectSettings.asset的变化).",
"确定", "取消"))
{
#pragma warning disable 162
if (EnableHotKeys)
ScriptingDefineSymbolsHelper.Remove(EX_GAS_ENABLE_HOT_KEYS);
else
ScriptingDefineSymbolsHelper.Add(EX_GAS_ENABLE_HOT_KEYS);
#pragma warning restore 162
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0bf62a9aa7db48b38f37313afe3e6ef5
timeCreated: 1703687775

View File

@@ -0,0 +1,68 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using GAS;
using UnityEngine.UIElements;
using UnityEditor;
using UnityEngine;
public class GASSettingProvider: SettingsProvider
{
private GASSettingAsset _asset;
private Editor _editor;
public GASSettingProvider() : base("Project/EX Gameplay Ability System", SettingsScope.Project)
{
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
var asset = GASSettingAsset.LoadOrCreate();
_asset = asset;
_editor = Editor.CreateEditor(asset);
GASSettingStatusWatcher.OnEditorFocused += OnEditorFocused;
}
public override void OnDeactivate()
{
base.OnDeactivate();
GASSettingStatusWatcher.OnEditorFocused -= OnEditorFocused;
GASSettingAsset.UpdateAsset(_asset);
GASSettingAsset.Save();
}
private void OnEditorFocused()
{
Repaint();
}
public override void OnGUI(string searchContext)
{
base.OnGUI(searchContext);
if (_editor == null) return;
EditorGUILayout.BeginVertical(GUI.skin.box);
EditorGUILayout.Space();
_editor.OnInspectorGUI();
EditorGUILayout.EndVertical();
}
static GASSettingProvider provider;
[SettingsProvider]
public static SettingsProvider CreateMyCustomSettingsProvider()
{
if (GASSettingAsset.Instance && provider == null)
{
provider = new GASSettingProvider();
using (var so = new SerializedObject(GASSettingAsset.Instance))
{
provider.keywords = GetSearchKeywordsFromSerializedObject(so);
}
}
return provider;
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 81d7aa17f3a54ba4b75b15dc751012f2
timeCreated: 1703687613

View File

@@ -0,0 +1,296 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System.Collections.Generic;
using Runtime;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
using GAS.General;
public class GASWatcher : OdinEditorWindow
{
private const string BOXGROUP_TIPS = "Tips";
private const string BOXGROUP_TIPS_RUNNINGTIP = "Tips/Running tip";
private const string BOXGROUP_ASC = "Ability System Components";
private const string BOXGROUP_ASC_H = "Ability System Components/H";
private const string BOXGROUP_ASC_H_L = "Ability System Components/H/L";
private const string BOXGROUP_ASC_H_R = "Ability System Components/H/R";
private const string BOXGROUP_ASC_H_R_A = "Ability System Components/H/R/A";
private const string BOXGROUP_ASC_H_R_A_V = "Ability System Components/H/R/A/V1";
private const string BOXGROUP_ASC_H_R_A_VB = "Ability System Components/H/R/A/VB";
private const string BOXGROUP_ASC_H_R_A_VC = "Ability System Components/H/R/A/VC";
private AbilitySystemComponent _selected;
[HideLabel]
[DisplayAsString(TextAlignment.Center, true)]
public string windowTitle = "<size=18><b>EX Gameplay Ability System Watcher</b></size>";
[BoxGroup(BOXGROUP_TIPS)]
[HideLabel]
[DisplayAsString(TextAlignment.Left, true)]
public string tips = GASTextDefine.TIP_WATCHER;
[BoxGroup(BOXGROUP_TIPS_RUNNINGTIP, false)]
[HideLabel]
[DisplayAsString(TextAlignment.Center, true)]
[HideIf("IsPlaying")]
public string onlyForGameRunning = GASTextDefine.TIP_WATCHER_OnlyForGameRunning;
[BoxGroup(BOXGROUP_ASC)]
[HorizontalGroup(BOXGROUP_ASC_H, 300)]
[BoxGroup(BOXGROUP_ASC_H_L, false)]
[OnInspectorGUI("OnDrawNavi")]
[DisplayAsString(TextAlignment.Center)]
[HideLabel]
[ShowIf("IsPlaying")]
public string Navis = "NAVI";
[HorizontalGroup(BOXGROUP_ASC_H)]
[BoxGroup(BOXGROUP_ASC_H_R, false)]
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
[Title("ID Mark", bold: true)]
[DisplayAsString]
[LabelWidth(75)]
[ShowIf("IsPlaying")]
public int IID;
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
[ReadOnly]
[LabelWidth(75)]
[ShowIf("IsPlaying")]
public GameObject instance;
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
[DisplayAsString]
[LabelWidth(75)]
[ShowIf("IsPlaying")]
public int Level;
[Space]
[Title("Abilities", bold: true)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true,
ShowPaging = false)]
[DisplayAsString]
[LabelText(" ")]
[ShowIf("IsPlaying")]
[Searchable]
public List<string> Abilities = new List<string>();
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_VB)]
[Title("Attributes", bold: true)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true,
ShowPaging = false)]
[DisplayAsString]
[LabelText(" ")]
[ShowIf("IsPlaying")]
[Searchable]
public List<string> Attributes = new List<string>();
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
[Title("GameplayEffects", bold: true)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true,
ShowPaging = false)]
[DisplayAsString]
[LabelText(" ")]
[InfoBox("format: [ActiveState][DurationInfo]GeName", InfoMessageType.None, "IsPlaying")]
[ShowIf("IsPlaying")]
[Searchable]
public List<string> Effects = new List<string>();
[HorizontalGroup(BOXGROUP_ASC_H_R_A)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_VC)]
[Title("Tags", bold: true)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true,
ShowPaging = false)]
[DisplayAsString]
[ShowIf("IsPlaying")]
[Searchable]
public List<string> FixedTag = new List<string>();
[Title(" ", bold: true)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_VC)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true,
ShowPaging = false)]
[DisplayAsString]
[ShowIf("IsPlaying")]
[Searchable]
public List<string> DynamicTag = new List<string>();
private Vector2 menuScrollPos;
private bool IsPlaying => Application.isPlaying;
private void Update()
{
if (IsPlaying)
{
if (_selected == null || _selected.gameObject == null)
{
_selected = GAS.GameplayAbilitySystem.GAS.AbilitySystemComponents.Count > 0
? GAS.GameplayAbilitySystem.GAS.AbilitySystemComponents[0] as AbilitySystemComponent
: null;
}
RefreshAscInfo();
Repaint();
}
}
private const string OpenWindow_MenuItemName = "EX-GAS/Runtime Watcher";
#if EX_GAS_ENABLE_HOT_KEYS
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName + " %F11";
#else
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName;
#endif
[MenuItem(OpenWindow_MenuItemNameEnh, priority = 3)]
private static void OpenWindow()
{
var window = GetWindow<GASWatcher>();
window.titleContent = new GUIContent("EX Gameplay Ability System Watcher");
window.Show();
}
void OnDrawNavi()
{
if (!IsPlaying) return;
menuScrollPos = EditorGUILayout.BeginScrollView(menuScrollPos, GUI.skin.box);
foreach (var iasc in GAS.GameplayAbilitySystem.GAS.AbilitySystemComponents)
{
var asc = (AbilitySystemComponent)iasc;
var presetName = asc.Preset != null ? asc.Preset.name : "NoPreset";
if (GUILayout.Button($"{presetName}#{asc.GetInstanceID()}"))
{
_selected = asc;
RefreshAscInfo();
}
}
EditorGUILayout.EndScrollView();
}
private void RefreshAscInfo()
{
if (_selected == null)
{
IID = 0;
instance = null;
Level = 0;
Abilities.Clear();
Attributes.Clear();
Effects.Clear();
FixedTag.Clear();
DynamicTag.Clear();
return;
}
IID = _selected.GetInstanceID();
instance = _selected.gameObject;
Level = _selected.Level;
RefreshAbilityInfo();
RefreshAttributesInfo();
RefreshGameplayEffectsInfo();
RefreshTagsInfo();
}
private void RefreshGameplayEffectsInfo()
{
Effects.Clear();
foreach (var ge in _selected.GameplayEffectContainer.GameplayEffects())
{
string isActive = ge.IsActive ? "√" : "×";
string durationStr = ge.DurationPolicy switch
{
EffectsDurationPolicy.Duration => $"{ge.DurationRemaining():N2}/{ge.Duration:N2}(s)",
EffectsDurationPolicy.Infinite => "∞",
EffectsDurationPolicy.Instant => "N/A",
_ => "Unknown"
};
var stackCountText = ge.Stacking.stackingType != StackingType.None ? $"[S:{ge.StackCount}]" : "";
Effects.Add($"[{isActive}][{durationStr}]{stackCountText}{ge.GameplayEffect.GameplayEffectName}");
}
}
private void RefreshAbilityInfo()
{
Abilities.Clear();
foreach (var ability in _selected.AbilityContainer.AbilitySpecs())
{
string isActive = ability.Value.IsActive ? "(Active)" : "";
Abilities.Add($"{ability.Key} | Lv.{ability.Value.Level} {isActive}");
}
}
private void RefreshAttributesInfo()
{
Attributes.Clear();
foreach (var (attributeSetName, attributeSet) in _selected.AttributeSetContainer.Sets)
{
Attributes.Add($"AttributeSet: {attributeSetName} ↓");
foreach (var attributeName in attributeSet.AttributeNames)
{
var attr = attributeSet[attributeName];
Attributes.Add(
$" - {attributeName} = {attr.CurrentValue:N2}({attr.BaseValue:N2} + {attr.CurrentValue - attr.BaseValue:N2})");
}
}
}
private void RefreshTagsInfo()
{
RefreshFixedTagsInfo();
RefreshDynamicTagsInfo();
}
void RefreshFixedTagsInfo()
{
FixedTag.Clear();
foreach (var tag in _selected.GameplayTagAggregator.FixedTags)
{
FixedTag.Add(tag.Name);
}
}
void RefreshDynamicTagsInfo()
{
DynamicTag.Clear();
foreach (var kv in _selected.GameplayTagAggregator.DynamicAddedTags)
{
var tagName = kv.Key.Name;
DynamicTag.Add($"{tagName} ↓ ");
foreach (var obj in kv.Value)
{
switch (obj)
{
case GameplayEffectSpec spec:
{
DynamicTag.Add(
$" - From: {spec.Owner.GetInstanceID()}'s GE: {spec.GameplayEffect.GameplayEffectName}");
break;
}
case AbilitySpec ability:
{
DynamicTag.Add(
$" - From: {ability.Owner.GetInstanceID()}'s Ability: {ability.Ability.Name}");
break;
}
}
}
}
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fdb20e1777ef464d86716c3505cab9a6
timeCreated: 1702628354