简单提交

This commit is contained in:
PC-20230316NUNE\Administrator 2024-10-22 16:10:12 +08:00
parent 930911e7df
commit 0e94e376fb
562 changed files with 26182 additions and 29365 deletions

File diff suppressed because it is too large Load Diff

View File

@ -43,13 +43,13 @@
<ItemGroup> <ItemGroup>
<None Include="Assets\Plugins\Sirenix\Odin Inspector\Assets\Editor\Bootstrap License.txt" /> <None Include="Assets\Plugins\Sirenix\Odin Inspector\Assets\Editor\Bootstrap License.txt" />
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>C:\APP\UnityEdit\2022.3.16f1c1\Editor\Data\Managed\UnityEngine\UnityEngine.dll</HintPath> <HintPath>D:\Unity\2022.3.1f1\Editor\Data\Managed\UnityEngine\UnityEngine.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine.CoreModule"> <Reference Include="UnityEngine.CoreModule">
<HintPath>C:\APP\UnityEdit\2022.3.16f1c1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll</HintPath> <HintPath>D:\Unity\2022.3.1f1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEditor"> <Reference Include="UnityEditor">
<HintPath>C:\APP\UnityEdit\2022.3.16f1c1\Editor\Data\Managed\UnityEngine\UnityEditor.dll</HintPath> <HintPath>D:\Unity\2022.3.1f1\Editor\Data\Managed\UnityEngine\UnityEditor.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: af0af80f518a09a43bc4895a97f6d06d guid: d35ae3f4d549fbb4e9a9edb5d6318639
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: fa26d750811c9a44f88f4e478131110e guid: 3bcde4791b2da434ca62faacb472df78
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@ -1,6 +1,7 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 34ae97a0d741c5844a4fac12ff8e1c45 guid: 52f44ade52e60e247a5ef830c3152bc0
TextScriptImporter: folderAsset: yes
DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:
assetBundleName: assetBundleName:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fda7b329dfe891948b714c3f8966e36f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ed22e0034a143d443ad93ed0c1f0fe47
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,14 @@
// auto generate by tools, DO NOT Modify it!!!
using GAS.Runtime;
namespace JNGame.GAS
{
#region MMC枚举ID自动生成
#endregion
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: a829f1acecbd5d348800bcbf18fb138e guid: 13b4d6ef882241949b349a1492f466c4
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -0,0 +1,6 @@
// auto generate by tools, DO NOT Modify it!!!
using GAS.Runtime;
using JNGame.Serialization;

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 79aaab94f725e0347b32b6383dc6361e guid: 2b2c4acd8004e3144ae1fddc5c8eee6d
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@ -1,12 +1,16 @@
using System; using JNGame.Serialization;
using System.IO;
using System.Linq;
using GAS.Runtime;
using UnityEditor;
using UnityEngine;
#if UNITY_EDITOR
namespace GAS.Editor namespace GAS.Editor
{ {
using Runtime;
using UnityEditor;
using System;
using System.IO;
using GAS;
using Editor;
using UnityEngine;
public class AbilityCollectionGenerator public class AbilityCollectionGenerator
{ {
public static void Gen() public static void Gen()
@ -62,12 +66,8 @@ namespace GAS.Editor
writer.WriteLine(""); writer.WriteLine("");
var abilityAssets = EditorUtil var abilityAssets =
.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath) EditorUtil.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath);
.OrderBy(x => x.UniqueName)
.ThenBy(x => x.name)
.ToArray();
foreach (var ability in abilityAssets) foreach (var ability in abilityAssets)
{ {
var path = AssetDatabase.GetAssetPath(ability); var path = AssetDatabase.GetAssetPath(ability);
@ -130,6 +130,9 @@ namespace GAS.Editor
writer.Write("}"); writer.Write("}");
Console.WriteLine($"Generated GTagLib at path: {filePath}"); Console.WriteLine($"Generated GTagLib at path: {filePath}");
Debug.Log("GenerateAbilityCollection Finish");
} }
} }
} }
#endif

View File

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using GAS.General.Validation;
using GAS.Runtime; using GAS.Runtime;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEditor; using UnityEditor;
@ -54,22 +53,12 @@ namespace GAS.Editor
AssetDatabase.Refresh(); AssetDatabase.Refresh();
} }
private bool _orderByUniqueName = true;
[HorizontalGroup("Buttons", width: 180)]
[Button(SdfIconType.SortAlphaDown, "@_orderByUniqueName?\"Sort By AssetName\":\"Sort By UniqueName\"",
ButtonHeight = 30)]
public void ToggleOrderByUniqueName()
{
_orderByUniqueName = !_orderByUniqueName;
Refresh();
}
private bool _showDetail = false; private bool _showDetail = false;
[HorizontalGroup("Buttons", width: 120)] [HorizontalGroup("Buttons", width: 120)]
[Button(SdfIconType.TicketDetailed, "@_showDetail?\"Hide Detail\":\"Show Detail\"", ButtonHeight = 30)] [Button(SdfIconType.TicketDetailed, "@_showDetail?\"Hide Detail\":\"Show Detail\"", ButtonHeight = 30)]
public void ToggleShowDetail() public void Toggle()
{ {
_showDetail = !_showDetail; _showDetail = !_showDetail;
Refresh(); Refresh();
@ -83,25 +72,16 @@ namespace GAS.Editor
{ {
Abilities.Clear(); Abilities.Clear();
var abilityAssets = EditorUtil.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath); var abilityAssets = EditorUtil.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath);
var orderedAbilityAssets = _orderByUniqueName abilityAssets.ForEach(ability =>
? abilityAssets
.OrderBy(x => x.UniqueName)
.ThenBy(x => x.name)
: abilityAssets.OrderBy(x => x.name);
Abilities = orderedAbilityAssets.Select(ability =>
{ {
var text = Validations.ValidateVariableName(ability.UniqueName).IsValid var text = $"{ability.UniqueName}";
? ability.UniqueName
: $"{ability.UniqueName}(非法UniqueName)";
if (_showDetail) if (_showDetail)
{ {
text += $" - asset: {ability.name}, type: {ability.GetType().FullName}"; text += $" - asset: {ability.name}, type: {ability.GetType().FullName}";
} }
return text; Abilities.Add(text);
}).ToList(); });
} }
bool ExistAbilityWithEmptyUniqueName() bool ExistAbilityWithEmptyUniqueName()

View File

@ -1,5 +1,3 @@
using GAS.Runtime;
#if UNITY_EDITOR #if UNITY_EDITOR
namespace GAS.Editor namespace GAS.Editor
{ {
@ -11,7 +9,8 @@ namespace GAS.Editor
public const int StandardFrameUnitWidth = 1; public const int StandardFrameUnitWidth = 1;
public const int MaxFrameUnitLevel = 20; public const int MaxFrameUnitLevel = 20;
public const float MinTimerShaftFrameDrawStep = 5; public const float MinTimerShaftFrameDrawStep = 5;
public int DefaultFrameRate => JexGasManager.FrameRate; public int TickTime => GASTimer.TimeLineAbilityTickTime;
public int FrameRate => GASTimer.FrameRateValue;
} }
} }
#endif #endif

View File

@ -1,4 +1,8 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System; using System;
using Editor;
using GAS.Runtime; using GAS.Runtime;
using UnityEditor; using UnityEditor;
using UnityEditor.SceneManagement; using UnityEditor.SceneManagement;
@ -8,15 +12,9 @@ using UnityEngine.SceneManagement;
using UnityEngine.UIElements; using UnityEngine.UIElements;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
namespace GAS.Editor
{
/// <summary>
/// 这个类被反射引用到, 重构请小心!!
/// </summary>
public class AbilityTimelineEditorWindow : EditorWindow public class AbilityTimelineEditorWindow : EditorWindow
{ {
[SerializeField] [SerializeField] private VisualTreeAsset m_VisualTreeAsset;
private VisualTreeAsset m_VisualTreeAsset;
private VisualElement _root; private VisualElement _root;
@ -27,7 +25,6 @@ namespace GAS.Editor
public TimelineInspector TimelineInspector { get; private set; } public TimelineInspector TimelineInspector { get; private set; }
private static EditorWindow _childInspector; private static EditorWindow _childInspector;
public void CreateGUI() public void CreateGUI()
{ {
Instance = this; Instance = this;
@ -45,10 +42,7 @@ namespace GAS.Editor
TimelineInspector = new TimelineInspector(_root); TimelineInspector = new TimelineInspector(_root);
} }
/// <summary> public static void ShowWindow(TimelineAbilityAsset asset)
/// 这个方法被反射引用到, 重构请小心!!
/// </summary>
public static void ShowWindow(TimelineAbilityAssetBase asset)
{ {
var wnd = GetWindow<AbilityTimelineEditorWindow>(); var wnd = GetWindow<AbilityTimelineEditorWindow>();
wnd.titleContent = new GUIContent("AbilityTimelineEditorWindow"); wnd.titleContent = new GUIContent("AbilityTimelineEditorWindow");
@ -63,7 +57,7 @@ namespace GAS.Editor
AbilityAsset.Save(); AbilityAsset.Save();
} }
private void InitAbility(TimelineAbilityAssetBase asset) private void InitAbility(TimelineAbilityAsset asset)
{ {
_abilityAsset.value = asset; _abilityAsset.value = asset;
MaxFrame.value = AbilityAsset.FrameCount; MaxFrame.value = AbilityAsset.FrameCount;
@ -84,11 +78,11 @@ namespace GAS.Editor
private ObjectField _abilityAsset; private ObjectField _abilityAsset;
private Button _btnShowAbilityAssetDetail; private Button _btnShowAbilityAssetDetail;
public TimelineAbilityAssetBase AbilityAsset => _abilityAsset.value as TimelineAbilityAssetBase; public TimelineAbilityAsset AbilityAsset => _abilityAsset.value as TimelineAbilityAsset;
// private TimelineAbilityEditorWindow AbilityAssetEditor => AbilityAsset != null private TimelineAbilityEditorWindow AbilityAssetEditor => AbilityAsset != null
// ? UnityEditor.Editor.CreateEditor(AbilityAsset) as TimelineAbilityEditorWindow ? UnityEditor.Editor.CreateEditor(AbilityAsset) as TimelineAbilityEditorWindow
// : null; : null;
private void InitAbilityAssetBar() private void InitAbilityAssetBar()
{ {
@ -101,6 +95,7 @@ namespace GAS.Editor
private void OnSequentialAbilityAssetChanged(ChangeEvent<Object> evt) private void OnSequentialAbilityAssetChanged(ChangeEvent<Object> evt)
{ {
var asset = evt.newValue as TimelineAbilityAsset;
if (AbilityAsset != null) if (AbilityAsset != null)
{ {
MaxFrame.value = AbilityAsset.FrameCount; MaxFrame.value = AbilityAsset.FrameCount;
@ -208,7 +203,7 @@ namespace GAS.Editor
if (_currentMaxFrame == value) return; if (_currentMaxFrame == value) return;
_currentMaxFrame = value; _currentMaxFrame = value;
AbilityAsset.FrameCount = _currentMaxFrame; AbilityAsset.frameCount = _currentMaxFrame;
SaveAsset(); SaveAsset();
MaxFrame.value = _currentMaxFrame; MaxFrame.value = _currentMaxFrame;
TrackView.UpdateContentSize(); TrackView.UpdateContentSize();
@ -267,22 +262,29 @@ namespace GAS.Editor
BtnRightFrame = _root.Q<Button>(nameof(BtnRightFrame)); BtnRightFrame = _root.Q<Button>(nameof(BtnRightFrame));
BtnRightFrame.clickable.clicked += OnRightFrame; BtnRightFrame.clickable.clicked += OnRightFrame;
InitInfo();
CurrentFrame = _root.Q<IntegerField>(nameof(CurrentFrame)); CurrentFrame = _root.Q<IntegerField>(nameof(CurrentFrame));
CurrentFrame.RegisterValueChangedCallback(OnCurrentFrameChanged); CurrentFrame.RegisterValueChangedCallback(OnCurrentFrameChanged);
MaxFrame = _root.Q<IntegerField>(nameof(MaxFrame)); MaxFrame = _root.Q<IntegerField>(nameof(MaxFrame));
MaxFrame.RegisterValueChangedCallback(OnMaxFrameChanged); MaxFrame.RegisterValueChangedCallback(OnMaxFrameChanged);
} }
private void OnMaxFrameChanged(ChangeEvent<int> evt) private void OnMaxFrameChanged(ChangeEvent<int> evt)
{ {
CurrentMaxFrame = evt.newValue; CurrentMaxFrame = evt.newValue;
MaxFrame.value = CurrentMaxFrame; MaxFrame.value = CurrentMaxFrame;
TotalTime.text = $"total:{CurrentMaxFrame * Config.TickTime}ms";
} }
private void OnCurrentFrameChanged(ChangeEvent<int> evt) private void OnCurrentFrameChanged(ChangeEvent<int> evt)
{ {
CurrentSelectFrameIndex = evt.newValue; CurrentSelectFrameIndex = evt.newValue;
CurrentFrame.value = CurrentSelectFrameIndex; CurrentFrame.value = CurrentSelectFrameIndex;
CurrentTime.text = $"time:{CurrentSelectFrameIndex * Config.TickTime}ms";
} }
private void RefreshPlayButton() private void RefreshPlayButton()
@ -312,6 +314,26 @@ namespace GAS.Editor
CurrentSelectFrameIndex += 1; CurrentSelectFrameIndex += 1;
} }
#endregion
#region Info
private Label FrameRate;
private Label CurrentTime;
private Label TotalTime;
private int FrameRateValue;
private void InitInfo()
{
FrameRate = _root.Q<Label>(nameof(FrameRate));
FrameRateValue = Config.FrameRate;
FrameRate.text = $"FPS:{FrameRateValue}";
CurrentTime = _root.Q<Label>(nameof(CurrentTime));
TotalTime = _root.Q<Label>(nameof(TotalTime));
}
#endregion #endregion
#region Clip Inspector #region Clip Inspector
@ -354,7 +376,7 @@ namespace GAS.Editor
if (IsPlaying) if (IsPlaying)
{ {
var deltaTime = (DateTime.Now - _startTime).TotalSeconds; var deltaTime = (DateTime.Now - _startTime).TotalSeconds;
var frameIndex = (int)(deltaTime * Config.DefaultFrameRate) + _startPlayFrameIndex; var frameIndex = (int)(deltaTime * FrameRateValue) + _startPlayFrameIndex;
if (frameIndex >= CurrentMaxFrame) if (frameIndex >= CurrentMaxFrame)
{ {
frameIndex = CurrentMaxFrame; frameIndex = CurrentMaxFrame;
@ -384,9 +406,10 @@ namespace GAS.Editor
#region Another Inspector #region Another Inspector
private static EditorWindow GetInspectTarget(Object targetGO=null) private static EditorWindow GetInspectTarget(Object targetGO=null)
{ {
Type inspectorType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.InspectorWindow"); Type inspectorType = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow");
EditorWindow inspectorInstance = CreateInstance(inspectorType) as EditorWindow; EditorWindow inspectorInstance = CreateInstance(inspectorType) as EditorWindow;
if(targetGO) Selection.activeObject = targetGO; if(targetGO) Selection.activeObject = targetGO;
return inspectorInstance; return inspectorInstance;
@ -395,3 +418,4 @@ namespace GAS.Editor
#endregion #endregion
} }
} }
#endif

View File

@ -1,5 +1,5 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True"> <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
<Style src="project://database/Assets/GAS/Editor/Ability/AbilityTimelineEditor/EditorWindow/AbilityTimelineEditorWindow.uss?fileID=7433441132597879392&amp;guid=28fcc296ac2d3554db81c84901f63835&amp;type=3#AbilityTimelineEditorWindow" /> <Style src="project://database/Assets/GameScript/GAS/Editor/Ability/AbilityTimelineEditor/EditorWindow/AbilityTimelineEditorWindow.uss?fileID=7433441132597879392&amp;guid=28fcc296ac2d3554db81c84901f63835&amp;type=3#AbilityTimelineEditorWindow" />
<ui:VisualElement name="Root" style="flex-grow: 1; flex-direction: column; height: 100%; min-height: 100%; max-height: 100%; flex-shrink: 0;"> <ui:VisualElement name="Root" style="flex-grow: 1; flex-direction: column; height: 100%; min-height: 100%; max-height: 100%; flex-shrink: 0;">
<ui:VisualElement name="AbilityAsset" style="flex-grow: 1; min-width: auto; min-height: auto; max-height: 40px; flex-direction: row; border-left-color: rgba(34, 34, 34, 0.84); border-right-color: rgba(34, 34, 34, 0.84); border-top-color: rgba(34, 34, 34, 0.84); border-bottom-color: rgba(34, 34, 34, 0.84); border-top-width: 0; border-right-width: 0; border-bottom-width: 2px; border-left-width: 0; height: 40px; align-self: auto; align-items: center;"> <ui:VisualElement name="AbilityAsset" style="flex-grow: 1; min-width: auto; min-height: auto; max-height: 40px; flex-direction: row; border-left-color: rgba(34, 34, 34, 0.84); border-right-color: rgba(34, 34, 34, 0.84); border-top-color: rgba(34, 34, 34, 0.84); border-bottom-color: rgba(34, 34, 34, 0.84); border-top-width: 0; border-right-width: 0; border-bottom-width: 2px; border-left-width: 0; height: 40px; align-self: auto; align-items: center;">
<uie:ObjectField label="Ability配置 " name="SequentialAbilityAsset" type="GAS.Runtime.TimelineAbilityAsset, JNGame.Runtime" style="-unity-font-style: bold; font-size: 13px; -unity-text-align: middle-left; align-items: center; width: auto; align-self: stretch; flex-direction: row; min-width: auto; max-height: none; max-width: 300px;" /> <uie:ObjectField label="Ability配置 " name="SequentialAbilityAsset" type="GAS.Runtime.TimelineAbilityAsset, JNGame.Runtime" style="-unity-font-style: bold; font-size: 13px; -unity-text-align: middle-left; align-items: center; width: auto; align-self: stretch; flex-direction: row; min-width: auto; max-height: none; max-width: 300px;" />
@ -14,24 +14,29 @@
</ui:VisualElement> </ui:VisualElement>
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="Content" style="flex-grow: 1; flex-direction: row; flex-shrink: 1; height: auto;"> <ui:VisualElement name="Content" style="flex-grow: 1; flex-direction: row; flex-shrink: 1; height: auto;">
<ui:VisualElement name="LeftConsole" style="flex-grow: 1; width: 200px; min-width: 200px; border-left-color: rgb(41, 41, 41); border-right-color: rgb(41, 41, 41); border-top-color: rgb(41, 41, 41); border-bottom-color: rgb(41, 41, 41); border-left-width: 0; border-right-width: 2px; margin-right: 5px; max-width: 200px; height: 100%; max-height: 100%; min-height: auto; flex-shrink: 0;"> <ui:VisualElement name="LeftConsole" style="flex-grow: 1; width: 220px; min-width: 220px; border-left-color: rgb(41, 41, 41); border-right-color: rgb(41, 41, 41); border-top-color: rgb(41, 41, 41); border-bottom-color: rgb(41, 41, 41); border-left-width: 0; border-right-width: 2px; margin-right: 5px; max-width: 220px; height: 100%; max-height: 100%; min-height: auto; flex-shrink: 0;">
<ui:VisualElement name="Controller" style="flex-grow: 1; flex-direction: row; height: 30px; max-height: 30px; min-width: 200px; width: 200px; max-width: 200px; border-left-color: rgba(24, 24, 24, 0.5); border-right-color: rgba(24, 24, 24, 0.5); border-top-color: rgba(24, 24, 24, 0.5); border-bottom-color: rgba(24, 24, 24, 0.5); border-right-width: 0; align-items: auto; align-self: flex-start; justify-content: space-around; position: relative; left: auto; border-bottom-width: 2px;"> <ui:VisualElement name="Controller" style="flex-grow: 1; flex-direction: row; height: 30px; max-height: 30px; min-width: 220px; width: 220px; max-width: 220px; border-left-color: rgba(24, 24, 24, 0.5); border-right-color: rgba(24, 24, 24, 0.5); border-top-color: rgba(24, 24, 24, 0.5); border-bottom-color: rgba(24, 24, 24, 0.5); border-right-width: 0; align-items: auto; align-self: flex-start; justify-content: space-around; position: relative; left: auto; border-bottom-width: 2px;">
<ui:VisualElement name="ButtonGroup" style="flex-grow: 1; flex-direction: row; justify-content: flex-start; align-self: center;"> <ui:VisualElement name="ButtonGroup" style="flex-grow: 1; flex-direction: row; justify-content: flex-start; align-self: center;">
<ui:Button text="&lt;" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnLeftFrame" style="font-size: 13px; -unity-font-style: bold; align-self: center;" /> <ui:Button text="&lt;" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnLeftFrame" style="font-size: 13px; -unity-font-style: bold; align-self: center;" />
<ui:Button text="▶" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnPlay" style="-unity-font-style: bold; font-size: 13px; align-self: center;" /> <ui:Button text="▶" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnPlay" style="-unity-font-style: bold; font-size: 13px; align-self: center;" />
<ui:Button text="&gt;" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnRightFrame" style="-unity-font-style: bold; font-size: 13px; align-self: center;" /> <ui:Button text="&gt;" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnRightFrame" style="-unity-font-style: bold; font-size: 13px; align-self: center;" />
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="FrameCount" style="flex-grow: 1; flex-direction: row-reverse; justify-content: flex-end; align-items: center; align-self: center; flex-wrap: nowrap; flex-shrink: 1;"> <ui:VisualElement name="FrameCount" style="flex-grow: 1; flex-direction: row-reverse; justify-content: flex-end; align-items: center; align-self: center; flex-wrap: nowrap; flex-shrink: 1;">
<ui:IntegerField value="0" name="MaxFrame" is-delayed="true" style="width: 40px;" /> <uie:IntegerField value="0" name="MaxFrame" is-delayed="true" style="width: 40px;" />
<ui:Label tabindex="-1" text="/" parse-escape-sequences="true" display-tooltip-when-elided="true" name="Label" /> <ui:Label tabindex="-1" text="/" parse-escape-sequences="true" display-tooltip-when-elided="true" name="Label" />
<ui:IntegerField value="0" name="CurrentFrame" readonly="false" style="width: 40px;" /> <uie:IntegerField value="0" name="CurrentFrame" readonly="false" style="width: 40px;" />
</ui:VisualElement> </ui:VisualElement>
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="Info" style="flex-grow: 1; flex-direction: row; height: 30px; max-height: 30px; min-width: 220px; width: 220px; max-width: 220px; border-left-color: rgba(24, 24, 24, 0.5); border-right-color: rgba(24, 24, 24, 0.5); border-top-color: rgba(24, 24, 24, 0.5); border-bottom-color: rgba(24, 24, 24, 0.5); border-right-width: 0; align-items: auto; align-self: flex-start; justify-content: space-around; position: relative; left: auto; border-bottom-width: 2px;">
<ui:Label tabindex="-1" text="FPS:0" parse-escape-sequences="true" display-tooltip-when-elided="true" name="FrameRate" style="-unity-text-align: middle-left; height: 25px; width: auto; white-space: nowrap; position: relative;" />
<ui:Label tabindex="-1" text="time:0ms" parse-escape-sequences="true" display-tooltip-when-elided="true" name="CurrentTime" style="-unity-text-align: middle-left; height: 25px; width: auto; white-space: nowrap; position: relative;" />
<ui:Label tabindex="-1" text="total:0ms" parse-escape-sequences="true" display-tooltip-when-elided="true" name="TotalTime" style="-unity-text-align: middle-left; height: 25px; width: auto; white-space: nowrap; position: relative;" />
</ui:VisualElement>
<ui:VisualElement name="TrackMenu" style="flex-grow: 1; height: 100%; max-height: 100%; min-height: auto; padding-left: 3px; padding-right: 3px;" /> <ui:VisualElement name="TrackMenu" style="flex-grow: 1; height: 100%; max-height: 100%; min-height: auto; padding-left: 3px; padding-right: 3px;" />
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="RightContent" style="flex-grow: 1; height: 100%; max-height: 100%; min-height: 100%; flex-shrink: 0;"> <ui:VisualElement name="RightContent" style="flex-grow: 1; height: 100%; max-height: 100%; min-height: 100%; flex-shrink: 0;">
<ui:VisualElement name="RightTimeline" style="flex-grow: 1; padding-right: 0; width: auto; flex-shrink: 0; height: 100%; max-height: 100%; min-height: 100%; min-width: auto; max-width: none;"> <ui:VisualElement name="RightTimeline" style="flex-grow: 1; padding-right: 0; width: auto; flex-shrink: 0; height: 100%; max-height: 100%; min-height: 100%; min-width: auto; max-width: none;">
<ui:IMGUIContainer name="TimerShaft" style="height: 30px; min-height: 30px; border-left-color: rgb(43, 43, 43); border-right-color: rgb(43, 43, 43); border-top-color: rgb(43, 43, 43); border-bottom-color: rgb(43, 43, 43); border-bottom-width: 1px; margin-right: 13px; max-height: 30px;" /> <ui:IMGUIContainer name="TimerShaft" style="height: 58px; min-height: 58px; border-left-color: rgb(43, 43, 43); border-right-color: rgb(43, 43, 43); border-top-color: rgb(43, 43, 43); border-bottom-color: rgb(43, 43, 43); border-bottom-width: 1px; margin-right: 13px; max-height: 58px;" />
<ui:ScrollView name="MainContent" mode="VerticalAndHorizontal" vertical-scroller-visibility="AlwaysVisible" style="flex-grow: 1;"> <ui:ScrollView name="MainContent" mode="VerticalAndHorizontal" vertical-scroller-visibility="AlwaysVisible" style="flex-grow: 1;">
<ui:VisualElement name="ContentTrackList" style="flex-grow: 1; height: 1000px; flex-shrink: 1; width: auto;" /> <ui:VisualElement name="ContentTrackList" style="flex-grow: 1; height: 1000px; flex-shrink: 1; width: auto;" />
</ui:ScrollView> </ui:ScrollView>

View File

@ -15,14 +15,14 @@ namespace GAS.Editor
private readonly VisualElement _root; private readonly VisualElement _root;
private Button _btnAddTrack; private Button _btnAddTrack;
private VisualElement _contentTrackListParent; private VisualElement _contentTrackListParent;
private MenuTrack _menuPassiveGameplayEffect;
private MenuTrack _menuBuffGameplayEffect; private MenuTrack _menuBuffGameplayEffect;
private MenuTrack _menuDurationalCue; private MenuTrack _menuDurationalCue;
private MenuTrack _menuInstantCue; private MenuTrack _menuInstantCue;
private MenuTrack _menuInstantTask; private MenuTrack _menuInstantTask;
private MenuTrack _menuOngoingTask; private MenuTrack _menuOngoingTask;
private MenuTrack _menuReleaseGameplayEffect;
private MenuTrack _menuPassiveGameplayEffect;
private MenuTrack _menuPassiveTask; private MenuTrack _menuPassiveTask;
private MenuTrack _menuReleaseGameplayEffect;
private VisualElement _trackMenuParent; private VisualElement _trackMenuParent;
public TimelineTrackView(VisualElement root) public TimelineTrackView(VisualElement root)
@ -34,7 +34,7 @@ namespace GAS.Editor
public List<TrackBase> TrackList { get; } = new(); public List<TrackBase> TrackList { get; } = new();
private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config; private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private void InitTracks() private void InitTracks()
{ {

View File

@ -1,31 +1,31 @@
// 
// #if UNITY_EDITOR #if UNITY_EDITOR
// namespace GAS.Editor namespace GAS.Editor
// { {
// using Runtime; using Runtime;
// using Sirenix.OdinInspector.Editor; using Sirenix.OdinInspector.Editor;
// using UnityEditor; using UnityEditor;
// using UnityEngine; using UnityEngine;
// using GAS.General; using GAS.General;
//
// [CustomEditor(typeof(TimelineAbilityAsset))] [CustomEditor(typeof(TimelineAbilityAsset))]
// public class TimelineAbilityEditorWindow : OdinEditor public class TimelineAbilityEditorWindow : OdinEditor
// { {
// private TimelineAbilityAsset _asset => target as TimelineAbilityAsset; private TimelineAbilityAsset _asset => target as TimelineAbilityAsset;
//
// public override void OnInspectorGUI() public override void OnInspectorGUI()
// { {
// base.OnInspectorGUI(); base.OnInspectorGUI();
//
// EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUILayout.BeginVertical(GUI.skin.box);
// if (GUILayout.Button(GASTextDefine.BUTTON_CHECK_TIMELINE_ABILITY, GUILayout.Height(30), GUILayout.Width(300))) EditAbilityTimeline(); if (GUILayout.Button(GASTextDefine.BUTTON_CHECK_TIMELINE_ABILITY, GUILayout.Height(30), GUILayout.Width(300))) EditAbilityTimeline();
// EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical();
// } }
//
// private void EditAbilityTimeline() private void EditAbilityTimeline()
// { {
// AbilityTimelineEditorWindow.ShowWindow(_asset); AbilityTimelineEditorWindow.ShowWindow(_asset);
// } }
// } }
// } }
// #endif #endif

View File

@ -8,7 +8,7 @@ namespace GAS.Editor
public class TaskClip : TrackClip<TaskClipEventTrack> public class TaskClip : TrackClip<TaskClipEventTrack>
{ {
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public TaskClipEvent TaskClipData => clipData as TaskClipEvent; public TaskClipEvent TaskClipData => clipData as TaskClipEvent;
public TaskClipEvent ClipDataForSave public TaskClipEvent ClipDataForSave
@ -36,7 +36,7 @@ namespace GAS.Editor
public override void RefreshShow(float newFrameUnitWidth) public override void RefreshShow(float newFrameUnitWidth)
{ {
base.RefreshShow(newFrameUnitWidth); base.RefreshShow(newFrameUnitWidth);
var taskType = TaskClipData.ongoingTask.TaskData.Type; var taskType = TaskClipData.ongoingTask.taskData.Type;
var shortName = taskType.Split('.').Last(); var shortName = taskType.Split('.').Last();
ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!"; ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!";
} }

View File

@ -42,7 +42,7 @@ namespace GAS.Editor
private const string GRP_BOX = "GRP_BOX"; private const string GRP_BOX = "GRP_BOX";
private const string GRP_BOX_TASK = "GRP_BOX/Task"; private const string GRP_BOX_TASK = "GRP_BOX/Task";
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private TaskClip _clip; private TaskClip _clip;
public static TaskClipEditor Create(TaskClip clip) public static TaskClipEditor Create(TaskClip clip)
@ -90,7 +90,7 @@ namespace GAS.Editor
{ {
RunInfo = $"<b>Run(f):{_clip.TaskClipData.startFrame} -> {_clip.TaskClipData.EndFrame}</b>"; RunInfo = $"<b>Run(f):{_clip.TaskClipData.startFrame} -> {_clip.TaskClipData.EndFrame}</b>";
Duration = _clip.TaskClipData.durationFrame; Duration = _clip.TaskClipData.durationFrame;
OngoingTaskType = _clip.TaskClipData.ongoingTask.TaskData.Type; OngoingTaskType = _clip.TaskClipData.ongoingTask.taskData.Type;
RefreshTaskInspector(); RefreshTaskInspector();
} }
@ -123,8 +123,8 @@ namespace GAS.Editor
private void OnTaskTypeChanged() private void OnTaskTypeChanged()
{ {
_clip.ClipDataForSave.ongoingTask.TaskData.Type = OngoingTaskType; _clip.ClipDataForSave.ongoingTask.taskData.Type = OngoingTaskType;
_clip.ClipDataForSave.ongoingTask.TaskData.Data = null; _clip.ClipDataForSave.ongoingTask.taskData.Data = null;
AbilityTimelineEditorWindow.Instance.Save(); AbilityTimelineEditorWindow.Instance.Save();
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector(); AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();

View File

@ -13,7 +13,7 @@ namespace GAS.Editor
protected override Color TrackColor => new Color(0.7f, 0.3f, 0.7f, 0.2f); protected override Color TrackColor => new Color(0.7f, 0.3f, 0.7f, 0.2f);
protected override Color MenuColor => new Color(0.5f, 0.3f, 0.5f, 1); protected override Color MenuColor => new Color(0.5f, 0.3f, 0.5f, 1);
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public TaskClipEventTrackData TaskClipTrackDataForSave public TaskClipEventTrackData TaskClipTrackDataForSave
{ {
get get

View File

@ -36,7 +36,7 @@ namespace GAS.Editor
string info = ""; string info = "";
foreach (var clip in _track.TaskClipTrackDataForSave.clipEvents) foreach (var clip in _track.TaskClipTrackDataForSave.clipEvents)
{ {
var taskName = clip.ongoingTask.TaskData.Type; var taskName = clip.ongoingTask.taskData.Type;
var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1); var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1);
info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n"; info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
} }

View File

@ -1,3 +1,3 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: ad3afdc4d0854b7e87d840e52dbedd44 guid: b4a27decc519488fb2d4065ccafb75d9
timeCreated: 1729189674 timeCreated: 1717572791

View File

@ -5,9 +5,10 @@ namespace GAS.Editor
using System.Linq; using System.Linq;
using GAS.Runtime; using GAS.Runtime;
public class PassiveTaskClip : TrackClip<PassiveTaskClipEventTrack> public class PassiveTaskClip : TrackClip<PassiveTaskClipEventTrack>
{ {
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public PassiveTaskClipEvent TaskClipData => clipData as PassiveTaskClipEvent; public PassiveTaskClipEvent TaskClipData => clipData as PassiveTaskClipEvent;
public PassiveTaskClipEvent ClipDataForSave public PassiveTaskClipEvent ClipDataForSave
@ -22,6 +23,7 @@ namespace GAS.Editor
} }
} }
public override void Delete() public override void Delete()
{ {
var success = track.TaskClipTrackDataForSave.clipEvents.Remove(TaskClipData); var success = track.TaskClipTrackDataForSave.clipEvents.Remove(TaskClipData);
@ -34,7 +36,7 @@ namespace GAS.Editor
public override void RefreshShow(float newFrameUnitWidth) public override void RefreshShow(float newFrameUnitWidth)
{ {
base.RefreshShow(newFrameUnitWidth); base.RefreshShow(newFrameUnitWidth);
var taskType = TaskClipData.passiveTask.TaskData.Type; var taskType = TaskClipData.passiveTask.taskData.Type;
var shortName = taskType.Split('.').Last(); var shortName = taskType.Split('.').Last();
ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!"; ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!";
} }

View File

@ -42,7 +42,7 @@ namespace GAS.Editor
private const string GRP_BOX = "GRP_BOX"; private const string GRP_BOX = "GRP_BOX";
private const string GRP_BOX_TASK = "GRP_BOX/Task"; private const string GRP_BOX_TASK = "GRP_BOX/Task";
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private PassiveTaskClip _clip; private PassiveTaskClip _clip;
public static PassiveTaskClipEditor Create(PassiveTaskClip clip) public static PassiveTaskClipEditor Create(PassiveTaskClip clip)
@ -96,7 +96,7 @@ namespace GAS.Editor
RunInfo = $"<b>Start(f):{_clip.TaskClipData.startFrame}</b>"; RunInfo = $"<b>Start(f):{_clip.TaskClipData.startFrame}</b>";
// Duration = _clip.TaskClipData.durationFrame; // Duration = _clip.TaskClipData.durationFrame;
StartFrame = _clip.TaskClipData.startFrame; StartFrame = _clip.TaskClipData.startFrame;
PassiveTaskType = _clip.TaskClipData.passiveTask.TaskData.Type; PassiveTaskType = _clip.TaskClipData.passiveTask.taskData.Type;
RefreshTaskInspector(); RefreshTaskInspector();
} }
@ -138,8 +138,8 @@ namespace GAS.Editor
private void OnTaskTypeChanged() private void OnTaskTypeChanged()
{ {
_clip.ClipDataForSave.passiveTask.TaskData.Type = PassiveTaskType; _clip.ClipDataForSave.passiveTask.taskData.Type = PassiveTaskType;
_clip.ClipDataForSave.passiveTask.TaskData.Data = null; _clip.ClipDataForSave.passiveTask.taskData.Data = null;
AbilityTimelineEditorWindow.Instance.Save(); AbilityTimelineEditorWindow.Instance.Save();
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector(); AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();

View File

@ -13,7 +13,7 @@ namespace GAS.Editor
protected override Color TrackColor => new Color(0.7f, 0.3f, 0.7f, 0.2f); protected override Color TrackColor => new Color(0.7f, 0.3f, 0.7f, 0.2f);
protected override Color MenuColor => new Color(0.5f, 0.3f, 0.5f, 1); protected override Color MenuColor => new Color(0.5f, 0.3f, 0.5f, 1);
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public PassiveTaskClipEventTrackData TaskClipTrackDataForSave public PassiveTaskClipEventTrackData TaskClipTrackDataForSave
{ {
get get

View File

@ -36,7 +36,7 @@ namespace GAS.Editor
string info = ""; string info = "";
foreach (var clip in _track.TaskClipTrackDataForSave.clipEvents) foreach (var clip in _track.TaskClipTrackDataForSave.clipEvents)
{ {
var taskName = clip.passiveTask.TaskData.Type; var taskName = clip.passiveTask.taskData.Type;
var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1); var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1);
info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n"; info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
} }

View File

@ -1,11 +1,18 @@
using GAS.Runtime; 
using UnityEngine; #if UNITY_EDITOR
namespace GAS.Editor namespace GAS.Editor
{ {
using System;
using System.Collections.Generic;
using System.Linq;
using GAS.General;
using Runtime;
using UnityEngine;
using UnityEngine.UIElements;
public class TaskMark : TrackMark<TaskMarkEventTrack> public class TaskMark : TrackMark<TaskMarkEventTrack>
{ {
public new TaskMarkEvent MarkData => markData as TaskMarkEvent; public TaskMarkEvent MarkData => markData as TaskMarkEvent;
public TaskMarkEvent MarkDataForSave public TaskMarkEvent MarkDataForSave
{ {
@ -19,7 +26,7 @@ namespace GAS.Editor
} }
} }
public override Object DataInspector => TaskMarkEditor.Create(this); public override UnityEngine.Object DataInspector => TaskMarkEditor.Create(this);
public override void Duplicate() public override void Duplicate()
{ {
@ -31,7 +38,7 @@ namespace GAS.Editor
var markEvent = new TaskMarkEvent var markEvent = new TaskMarkEvent
{ {
startFrame = startFrame, startFrame = startFrame,
InstantTasks = (markData as TaskMarkEvent)?.InstantTasks instantTasks = (markData as TaskMarkEvent)?.instantTasks
}; };
track.InstantTaskEventTrackData.markEvents.Add(markEvent); track.InstantTaskEventTrackData.markEvents.Add(markEvent);
@ -181,7 +188,7 @@ namespace GAS.Editor
{ {
if (frameIndex == StartFrameIndex) if (frameIndex == StartFrameIndex)
{ {
foreach (var t in MarkData.InstantTasks) foreach (var t in MarkData.instantTasks)
{ {
var task = t.Load() as InstantAbilityTask; var task = t.Load() as InstantAbilityTask;
task?.OnEditorPreview(); task?.OnEditorPreview();
@ -195,3 +202,4 @@ namespace GAS.Editor
} }
} }
} }
#endif

View File

@ -1,20 +1,20 @@
using System; #if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Editor;
using GAS.General; using GAS.General;
using GAS.Runtime; using Runtime;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor; using Sirenix.OdinInspector.Editor;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
namespace GAS.Editor
{
public class TaskMarkEditor : OdinEditorWindow public class TaskMarkEditor : OdinEditorWindow
{ {
[BoxGroup] [BoxGroup] [HideLabel] [DisplayAsString(TextAlignment.Left, true)]
[HideLabel]
[DisplayAsString(TextAlignment.Left, true)]
public string RunInfo; public string RunInfo;
private TaskMark _mark; private TaskMark _mark;
@ -22,13 +22,13 @@ namespace GAS.Editor
[Delayed] [Delayed]
[BoxGroup] [BoxGroup]
[HideReferenceObjectPicker] [HideReferenceObjectPicker]
[ListDrawerSettings(ShowFoldout = true, DraggableItems = true)] [ListDrawerSettings(Expanded = true, DraggableItems = true)]
[OnValueChanged("OnTaskListChanged", true)] [OnValueChanged("OnTaskListChanged", true)]
public List<InstantTaskCellInspector> Tasks; public List<InstantTaskCellInspector> Tasks;
public static TaskMarkEditor Create(TaskMark mark) public static TaskMarkEditor Create(TaskMark mark)
{ {
var window = CreateInstance<TaskMarkEditor>(); var window = new TaskMarkEditor();
window._mark = mark; window._mark = mark;
window.UpdateMarkInfo(); window.UpdateMarkInfo();
@ -47,7 +47,7 @@ namespace GAS.Editor
{ {
RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>"; RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>";
Tasks = new List<InstantTaskCellInspector>(); Tasks = new List<InstantTaskCellInspector>();
foreach (var taskData in _mark.MarkData.InstantTasks) Tasks.Add(new InstantTaskCellInspector(taskData)); foreach (var taskData in _mark.MarkData.instantTasks) Tasks.Add(new InstantTaskCellInspector(taskData));
} }
private void OnTaskListChanged() private void OnTaskListChanged()
@ -56,14 +56,14 @@ namespace GAS.Editor
foreach (var t in Tasks) foreach (var t in Tasks)
tasks.Add(new InstantTaskData tasks.Add(new InstantTaskData
{ {
TaskData = new JsonData taskData = new JsonData
{ {
Type = t.InstantTaskType, Type = t.InstantTaskType,
Data = t.Data() Data = t.Data()
} }
}); });
_mark.MarkDataForSave.InstantTasks = tasks; _mark.MarkDataForSave.instantTasks = tasks;
AbilityTimelineEditorWindow.Instance.Save(); AbilityTimelineEditorWindow.Instance.Save();
} }
} }
@ -93,17 +93,14 @@ namespace GAS.Editor
[OnValueChanged("OnTaskTypeChanged")] [OnValueChanged("OnTaskTypeChanged")]
public string InstantTaskType; public string InstantTaskType;
[BoxGroup] [BoxGroup] [HideReferenceObjectPicker] [HideIf("InstantTaskIsNull")] [LabelText("Detail")]
[HideReferenceObjectPicker]
[HideIf("InstantTaskIsNull")]
[LabelText("Detail")]
public InstantTaskInspector InstantTask; public InstantTaskInspector InstantTask;
public InstantTaskCellInspector(InstantTaskData data) public InstantTaskCellInspector(InstantTaskData data)
{ {
_data = data; _data = data;
_instantAbilityTask = data.Load() as InstantAbilityTask; _instantAbilityTask = data.Load() as InstantAbilityTask;
InstantTaskType = data.TaskData.Type; InstantTaskType = data.taskData.Type;
RefreshDetailInspector(); RefreshDetailInspector();
} }
@ -111,7 +108,7 @@ namespace GAS.Editor
{ {
_data = new InstantTaskData(); _data = new InstantTaskData();
_instantAbilityTask = _data.Load() as InstantAbilityTask; _instantAbilityTask = _data.Load() as InstantAbilityTask;
InstantTaskType = _data.TaskData.Type; InstantTaskType = _data.taskData.Type;
RefreshDetailInspector(); RefreshDetailInspector();
} }
@ -125,13 +122,10 @@ namespace GAS.Editor
if (_instantTaskInspectorMap != null) return _instantTaskInspectorMap; if (_instantTaskInspectorMap != null) return _instantTaskInspectorMap;
_instantTaskInspectorMap = new Dictionary<Type, Type>(); _instantTaskInspectorMap = new Dictionary<Type, Type>();
foreach (var inspectorType in InstantTaskInspectorTypes) foreach (var inspectorType in InstantTaskInspectorTypes)
{
if (inspectorType.BaseType != null)
{ {
var taskType = inspectorType.BaseType.GetGenericArguments()[0]; var taskType = inspectorType.BaseType.GetGenericArguments()[0];
_instantTaskInspectorMap.Add(taskType, inspectorType); _instantTaskInspectorMap.Add(taskType, inspectorType);
} }
}
return _instantTaskInspectorMap; return _instantTaskInspectorMap;
} }
@ -142,13 +136,13 @@ namespace GAS.Editor
if (_instantAbilityTask == null) return null; if (_instantAbilityTask == null) return null;
_data.Save(_instantAbilityTask); _data.Save(_instantAbilityTask);
return _data.TaskData.Data; return _data.taskData.Data;
} }
private void OnTaskTypeChanged() private void OnTaskTypeChanged()
{ {
_data.TaskData.Type = InstantTaskType; _data.taskData.Type = InstantTaskType;
_data.TaskData.Data = null; _data.taskData.Data = null;
RefreshDetailInspector(); RefreshDetailInspector();
} }
@ -160,7 +154,7 @@ namespace GAS.Editor
public void RefreshDetailInspector() public void RefreshDetailInspector()
{ {
_instantAbilityTask = _data.Load() as InstantAbilityTask; _instantAbilityTask = _data.Load() as InstantAbilityTask;
if (_instantAbilityTask != null && InstantTaskInspectorMap.TryGetValue(_instantAbilityTask.GetType(), out var inspectorType)) if (InstantTaskInspectorMap.TryGetValue(_instantAbilityTask.GetType(), out var inspectorType))
{ {
var taskInspector = (InstantTaskInspector)Activator.CreateInstance(inspectorType); var taskInspector = (InstantTaskInspector)Activator.CreateInstance(inspectorType);
taskInspector.Init(_instantAbilityTask); taskInspector.Init(_instantAbilityTask);
@ -173,3 +167,4 @@ namespace GAS.Editor
} }
} }
} }
#endif

View File

@ -11,7 +11,7 @@ namespace GAS.Editor
public class TaskMarkEventTrack : TrackBase public class TaskMarkEventTrack : TrackBase
{ {
private TaskMarkEventTrackData _instantTasksTrackData; private TaskMarkEventTrackData _instantTasksTrackData;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public TaskMarkEventTrackData InstantTaskEventTrackData public TaskMarkEventTrackData InstantTaskEventTrackData
{ {

View File

@ -37,9 +37,9 @@ namespace GAS.Editor
foreach (var mark in _track.InstantTaskEventTrackData.markEvents) foreach (var mark in _track.InstantTaskEventTrackData.markEvents)
{ {
info += $"Trigger(f):{mark.startFrame} \n"; info += $"Trigger(f):{mark.startFrame} \n";
foreach (var task in mark.InstantTasks) foreach (var task in mark.instantTasks)
{ {
var taskName = task.TaskData.Type; var taskName = task.taskData.Type;
var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1); var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1);
info += $" |-> {shortName}\n"; info += $" |-> {shortName}\n";
} }

View File

@ -60,6 +60,14 @@ namespace GAS.Editor
clipData = updatedClip; clipData = updatedClip;
} }
public void UpdateGameplayEffectValueId(int id)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.gameplayEffectValueId = id;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public void UpdateClipDataBuff(GameplayEffectAsset newBuff) public void UpdateClipDataBuff(GameplayEffectAsset newBuff)
{ {
var updatedClip = ClipDataForSave; var updatedClip = ClipDataForSave;

View File

@ -10,7 +10,7 @@ namespace GAS.Editor
public class BuffGameplayEffectClipEditor : OdinEditorWindow public class BuffGameplayEffectClipEditor : OdinEditorWindow
{ {
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private BuffGameplayEffectClip _clip; private BuffGameplayEffectClip _clip;
@ -33,6 +33,12 @@ namespace GAS.Editor
[OnValueChanged("OnDurationFrameChanged")] [OnValueChanged("OnDurationFrameChanged")]
public int Duration; public int Duration;
[Delayed]
[BoxGroup]
[LabelText("BuffValueId")]
[OnValueChanged("OnGameplayEffectValueIdChanged")]
public int GameplayEffectValueId;
[Delayed] [Delayed]
[BoxGroup] [BoxGroup]
[AssetSelector] [AssetSelector]
@ -52,6 +58,7 @@ namespace GAS.Editor
Buff = _clip.BuffGameplayEffectClipData.gameplayEffect; Buff = _clip.BuffGameplayEffectClipData.gameplayEffect;
RunInfo = $"<b>Run(f):{_clip.BuffGameplayEffectClipData.startFrame} -> {_clip.BuffGameplayEffectClipData.EndFrame}</b>"; RunInfo = $"<b>Run(f):{_clip.BuffGameplayEffectClipData.startFrame} -> {_clip.BuffGameplayEffectClipData.EndFrame}</b>";
Duration = _clip.BuffGameplayEffectClipData.durationFrame; Duration = _clip.BuffGameplayEffectClipData.durationFrame;
GameplayEffectValueId = _clip.BuffGameplayEffectClipData.gameplayEffectValueId;
} }
private void OnDurationFrameChanged() private void OnDurationFrameChanged()
@ -66,6 +73,13 @@ namespace GAS.Editor
Refresh(); Refresh();
} }
private void OnGameplayEffectValueIdChanged()
{
// 保存数据
_clip.UpdateGameplayEffectValueId(GameplayEffectValueId);
Refresh();
}
private void OnBuffChanged() private void OnBuffChanged()
{ {
_clip.UpdateClipDataBuff(Buff); _clip.UpdateClipDataBuff(Buff);

View File

@ -13,7 +13,7 @@ namespace GAS.Editor
protected override Color TrackColor => new(0.9f, 0.6f, 0.6f, 0.2f); protected override Color TrackColor => new(0.9f, 0.6f, 0.6f, 0.2f);
protected override Color MenuColor => new(0.9f, 0.6f, 0.6f, 1); protected override Color MenuColor => new(0.9f, 0.6f, 0.6f, 1);
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public BuffGameplayEffectTrackData BuffTrackDataForSave public BuffGameplayEffectTrackData BuffTrackDataForSave
{ {

View File

@ -8,7 +8,7 @@ namespace GAS.Editor
public class DurationalCueClip : TrackClip<DurationalCueTrack> public class DurationalCueClip : TrackClip<DurationalCueTrack>
{ {
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public DurationalCueClipEvent DurationalCueClipData => clipData as DurationalCueClipEvent; public DurationalCueClipEvent DurationalCueClipData => clipData as DurationalCueClipEvent;
private DurationalCueClipEvent ClipDataForSave private DurationalCueClipEvent ClipDataForSave

View File

@ -10,7 +10,7 @@ namespace GAS.Editor
public class DurationalCueClipEditor : OdinEditorWindow public class DurationalCueClipEditor : OdinEditorWindow
{ {
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private DurationalCueClip _clip; private DurationalCueClip _clip;

View File

@ -13,7 +13,7 @@ namespace GAS.Editor
protected override Color TrackColor => new(0.1f, 0.6f, 0.1f, 0.2f); protected override Color TrackColor => new(0.1f, 0.6f, 0.1f, 0.2f);
protected override Color MenuColor => new(0.1f, 0.6f, 0.1f, 1); protected override Color MenuColor => new(0.1f, 0.6f, 0.1f, 1);
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public DurationalCueTrackData CueTrackDataForSave public DurationalCueTrackData CueTrackDataForSave
{ {

View File

@ -12,7 +12,7 @@ namespace GAS.Editor
public class InstantCueTrack : TrackBase public class InstantCueTrack : TrackBase
{ {
private InstantCueTrackData _instantCuesTrackData; private InstantCueTrackData _instantCuesTrackData;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public InstantCueTrackData InstantCueTrackData public InstantCueTrackData InstantCueTrackData
{ {

View File

@ -13,7 +13,7 @@ namespace GAS.Editor
private Color _trackColor; private Color _trackColor;
private Type _trackDataType; private Type _trackDataType;
private Type _trackType; private Type _trackType;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config; private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config;
private static TimelineTrackView TrackView => AbilityTimelineEditorWindow.Instance.TrackView; private static TimelineTrackView TrackView => AbilityTimelineEditorWindow.Instance.TrackView;
public override Type TrackDataType { get; } public override Type TrackDataType { get; }
@ -61,7 +61,7 @@ namespace GAS.Editor
// 创建Data // 创建Data
var data = (TrackDataBase)Activator.CreateInstance(_trackDataType); var data = (TrackDataBase)Activator.CreateInstance(_trackDataType);
data.DefaultInit(); data.DefaultInit();
data.AddToAbilityAsset(AbilityAsset); data.AddToAbilityAsset(AbilityAsset, _trackDataType);
// 初始化View // 初始化View
track.Init(TrackParent, MenuParent, Config.FrameUnitWidth, data); track.Init(TrackParent, MenuParent, Config.FrameUnitWidth, data);
@ -105,15 +105,15 @@ namespace GAS.Editor
if (_trackType == typeof(BuffGameplayEffectTrack)) if (_trackType == typeof(BuffGameplayEffectTrack))
return baseIndex + (AbilityAsset.BuffGameplayEffects?.Count ?? 0); return baseIndex + (AbilityAsset.BuffGameplayEffects?.Count ?? 0);
if (_trackType == typeof(PassiveGameplayEffectTrack))
return baseIndex + (AbilityAsset.PassiveGameplayEffects?.Count ?? 0);
if (_trackType == typeof(TaskClipEventTrack)) if (_trackType == typeof(TaskClipEventTrack))
return baseIndex + (AbilityAsset.OngoingTasks?.Count ?? 0); return baseIndex + (AbilityAsset.OngoingTasks?.Count ?? 0);
if (_trackType == typeof(DurationalCueTrack)) if (_trackType == typeof(DurationalCueTrack))
return baseIndex + (AbilityAsset.DurationalCues?.Count ?? 0); return baseIndex + (AbilityAsset.DurationalCues?.Count ?? 0);
if (_trackType == typeof(PassiveGameplayEffectTrack))
return baseIndex + (AbilityAsset.PassiveGameplayEffects?.Count ?? 0);
if (_trackType == typeof(PassiveTaskClipEventTrack)) if (_trackType == typeof(PassiveTaskClipEventTrack))
return baseIndex + (AbilityAsset.PassiveTasks?.Count ?? 0); return baseIndex + (AbilityAsset.PassiveTasks?.Count ?? 0);

View File

@ -1,3 +1,3 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: e2da34a43d154bf7af1859c786b7e438 guid: 3a6f1fc54df943d3a4143196c049703d
timeCreated: 1729189458 timeCreated: 1717123975

View File

@ -10,7 +10,7 @@ namespace GAS.Editor
public class PassiveGameplayEffectClipEditor : OdinEditorWindow public class PassiveGameplayEffectClipEditor : OdinEditorWindow
{ {
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private PassiveGameplayEffectClip _clip; private PassiveGameplayEffectClip _clip;

View File

@ -9,12 +9,11 @@ namespace GAS.Editor
public class PassiveGameplayEffectTrack : TrackBase public class PassiveGameplayEffectTrack : TrackBase
{ {
private PassiveGameplayEffectTrackData _PassiveGameplayEffectTrackData; private PassiveGameplayEffectTrackData _PassiveGameplayEffectTrackData;
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public override Type TrackDataType => typeof(PassiveGameplayEffectTrackData); public override Type TrackDataType => typeof(PassiveGameplayEffectTrackData);
protected override Color TrackColor => new(0.9f, 0.6f, 0.6f, 0.2f); protected override Color TrackColor => new(0.9f, 0.6f, 0.6f, 0.2f);
protected override Color MenuColor => new(0.9f, 0.6f, 0.6f, 1); protected override Color MenuColor => new(0.9f, 0.6f, 0.6f, 1);
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public PassiveGameplayEffectTrackData BuffTrackDataForSave public PassiveGameplayEffectTrackData BuffTrackDataForSave
{ {

View File

@ -1,11 +1,18 @@
using GAS.Runtime; #if UNITY_EDITOR
using UnityEngine;
namespace GAS.Editor namespace GAS.Editor
{ {
using System;
using System.Collections.Generic;
using System.Linq;
using GAS.General;
using Runtime;
using UnityEngine;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
public class ReleaseGameplayEffectMark : TrackMark<ReleaseGameplayEffectTrack> public class ReleaseGameplayEffectMark : TrackMark<ReleaseGameplayEffectTrack>
{ {
private new ReleaseGameplayEffectMarkEvent MarkData => markData as ReleaseGameplayEffectMarkEvent; private ReleaseGameplayEffectMarkEvent MarkData => markData as ReleaseGameplayEffectMarkEvent;
public ReleaseGameplayEffectMarkEvent MarkDataForSave public ReleaseGameplayEffectMarkEvent MarkDataForSave
{ {
@ -128,3 +135,4 @@ namespace GAS.Editor
} }
} }
} }
#endif

View File

@ -1,25 +1,27 @@
using System; #if UNITY_EDITOR
using System.Collections.Generic;
using System.Linq;
using GAS.General;
using GAS.Runtime;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor namespace GAS.Editor
{ {
using System.Collections;
using System.Linq;
using UnityEngine.Serialization;
using System;
using System.Collections.Generic;
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
using GAS.General;
using GAS.Runtime;
public class ReleaseGameplayEffectMarkEditor:OdinEditorWindow public class ReleaseGameplayEffectMarkEditor:OdinEditorWindow
{ {
private const string GRP_BOX = "GRP_BOX"; private const string GRP_BOX = "GRP_BOX";
private const string GRP_BOX_CATCHER = "GRP_BOX/Catcher"; private const string GRP_BOX_CATCHER = "GRP_BOX/Catcher";
private ReleaseGameplayEffectMark _mark; private ReleaseGameplayEffectMark _mark;
public static ReleaseGameplayEffectMarkEditor Create(ReleaseGameplayEffectMark mark) public static ReleaseGameplayEffectMarkEditor Create(ReleaseGameplayEffectMark mark)
{ {
var window = CreateInstance<ReleaseGameplayEffectMarkEditor>(); var window = ScriptableObject.CreateInstance<ReleaseGameplayEffectMarkEditor>();
window._mark = mark; window._mark = mark;
window.UpdateMarkInfo(); window.UpdateMarkInfo();
return window; return window;
@ -47,26 +49,44 @@ namespace GAS.Editor
[OnValueChanged("OnCatcherChanged",true)] [OnValueChanged("OnCatcherChanged",true)]
public TargetCatcherInspector Catcher; public TargetCatcherInspector Catcher;
// [Delayed]
// [BoxGroup(GRP_BOX)]
// [AssetSelector]
// [ListDrawerSettings(Expanded = true, DraggableItems = true)]
// [OnValueChanged("OnGameplayEffectListChanged")]
// public List<GameplayEffectAsset> gameplayEffects;
[Delayed] [Delayed]
[BoxGroup(GRP_BOX)] [BoxGroup(GRP_BOX)]
[AssetSelector] [ListDrawerSettings(Expanded = true, DraggableItems = true)]
[ListDrawerSettings(ShowFoldout = true, DraggableItems = true)] [OnValueChanged("OnGameplayEffectAssetDataListChanged",true)]
[OnValueChanged("OnGameplayEffectListChanged")] public List<GameplayEffectAssetData> gameplayEffects;
public List<GameplayEffectAsset> gameplayEffects;
[BoxGroup(GRP_BOX)]
[Button] // [BoxGroup(GRP_BOX)]
[GUIColor(0.9f, 0.2f, 0.2f)] // [Button]
void Delete() // void Save()
{ // {
_mark.Delete(); // // OnGameplayEffectListChanged();
} // OnGameplayEffectAssetDataListChanged();
//
// AbilityTimelineEditorWindow.Instance.Save();
// AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
// }
// [BoxGroup(GRP_BOX)]
// [Button]
// [GUIColor(0.9f,0.2f,0.2f)]
// void Delete()
// {
// _mark.Delete();
// }
void UpdateMarkInfo() void UpdateMarkInfo()
{ {
RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>"; RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>";
// gameplayEffects = _mark.MarkDataForSave.gameplayEffectAssets;
gameplayEffects= _mark.MarkDataForSave.gameplayEffectAssets; gameplayEffects= _mark.MarkDataForSave.gameplayEffectAssets;
CatcherType = _mark.MarkDataForSave.jsonTargetCatcher.Type; CatcherType = _mark.MarkDataForSave.jsonTargetCatcher.Type;
RefreshCatcherInspector(); RefreshCatcherInspector();
} }
@ -74,7 +94,7 @@ namespace GAS.Editor
void RefreshCatcherInspector() void RefreshCatcherInspector()
{ {
// 根据选择的OngoingAbilityTask子类显示对应的属性 // 根据选择的OngoingAbilityTask子类显示对应的属性
var catcher = _mark.MarkDataForSave.LoadTargetCatcher(); var catcher = _mark.MarkDataForSave.TargetCatcher;
if (TargetCatcherInspectorMap.TryGetValue(catcher.GetType(), out var inspectorType)) if (TargetCatcherInspectorMap.TryGetValue(catcher.GetType(), out var inspectorType))
{ {
var targetCatcherInspector = var targetCatcherInspector =
@ -88,7 +108,13 @@ namespace GAS.Editor
} }
} }
void OnGameplayEffectListChanged() // void OnGameplayEffectListChanged()
// {
// _mark.MarkDataForSave.gameplayEffectAssets = gameplayEffects ;
// AbilityTimelineEditorWindow.Instance.Save();
// }
void OnGameplayEffectAssetDataListChanged()
{ {
_mark.MarkDataForSave.gameplayEffectAssets = gameplayEffects ; _mark.MarkDataForSave.gameplayEffectAssets = gameplayEffects ;
AbilityTimelineEditorWindow.Instance.Save(); AbilityTimelineEditorWindow.Instance.Save();
@ -98,13 +124,14 @@ namespace GAS.Editor
{ {
_mark.MarkDataForSave.jsonTargetCatcher.Type = CatcherType; _mark.MarkDataForSave.jsonTargetCatcher.Type = CatcherType;
_mark.MarkDataForSave.jsonTargetCatcher.Data = null; _mark.MarkDataForSave.jsonTargetCatcher.Data = null;
AbilityTimelineEditorWindow.Instance.Save(); AbilityTimelineEditorWindow.Instance.Save();
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector(); AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
} }
void OnCatcherChanged() void OnCatcherChanged()
{ {
// _mark.MarkDataForSave.jsonTargetCatcher.Data = Catcher.ToJson(); // _mark.MarkDataForSave.jsonTargetCatcher.Data = JsonUtility.ToJson(Catcher);
// AbilityTimelineEditorWindow.Instance.Save(); // AbilityTimelineEditorWindow.Instance.Save();
} }
@ -133,13 +160,10 @@ namespace GAS.Editor
if (_targetCatcherInspectorMap != null) return _targetCatcherInspectorMap; if (_targetCatcherInspectorMap != null) return _targetCatcherInspectorMap;
_targetCatcherInspectorMap = new Dictionary<Type, Type>(); _targetCatcherInspectorMap = new Dictionary<Type, Type>();
foreach (var catcherInspectorType in TargetCatcherInspectorTypes) foreach (var catcherInspectorType in TargetCatcherInspectorTypes)
{
if (catcherInspectorType.BaseType != null)
{ {
var targetCatcherType = catcherInspectorType.BaseType.GetGenericArguments()[0]; var targetCatcherType = catcherInspectorType.BaseType.GetGenericArguments()[0];
_targetCatcherInspectorMap.Add(targetCatcherType, catcherInspectorType); _targetCatcherInspectorMap.Add(targetCatcherType, catcherInspectorType);
} }
}
return _targetCatcherInspectorMap; return _targetCatcherInspectorMap;
} }
@ -151,3 +175,4 @@ namespace GAS.Editor
{ {
} }
} }
#endif

View File

@ -9,7 +9,7 @@ namespace GAS.Editor
public class ReleaseGameplayEffectTrack : TrackBase public class ReleaseGameplayEffectTrack : TrackBase
{ {
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public ReleaseGameplayEffectTrackData ReleaseGameplayEffectTrackData { public ReleaseGameplayEffectTrackData ReleaseGameplayEffectTrackData {
get get
{ {
@ -65,7 +65,7 @@ namespace GAS.Editor
var markEvent = new ReleaseGameplayEffectMarkEvent var markEvent = new ReleaseGameplayEffectMarkEvent
{ {
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x), startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x),
gameplayEffectAssets = new List<GameplayEffectAsset>() gameplayEffectAssets = new List<GameplayEffectAssetData>()
}; };
ReleaseGameplayEffectTrackData.markEvents.Add(markEvent); ReleaseGameplayEffectTrackData.markEvents.Add(markEvent);

View File

@ -37,9 +37,19 @@ namespace GAS.Editor
foreach (var mark in _track.ReleaseGameplayEffectTrackData.markEvents) foreach (var mark in _track.ReleaseGameplayEffectTrackData.markEvents)
{ {
info += $"Trigger(f):{mark.startFrame} \n"; info += $"Trigger(f):{mark.startFrame} \n";
foreach (var ge in mark.gameplayEffectAssets) // foreach (var ge in mark.gameplayEffectAssets)
// {
// var geName = ge != null ? ge.name : "NULL";
// info += $" |-> {geName}\n";
// }
foreach (var data in mark.gameplayEffectAssets)
{ {
var geName = ge != null ? ge.name : "NULL"; var geName = "NULL";
if (data != null && data.asset != null)
{
geName = $"{data.asset.name} {data.id}";
}
info += $" |-> {geName}\n"; info += $" |-> {geName}\n";
} }

View File

@ -8,7 +8,7 @@ namespace GAS.Editor
using UnityEngine.UIElements; using UnityEngine.UIElements;
public abstract class TrackMarkBase:TrackItemBase public abstract class TrackMarkBase:TrackItemBase
{ {
protected static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset; protected static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private static string MarkAssetGuid => "5a3b3360bcba29b4cac2875f518af19d"; private static string MarkAssetGuid => "5a3b3360bcba29b4cac2875f518af19d";
public float FrameUnitWidth { get;protected set; } public float FrameUnitWidth { get;protected set; }
public int StartFrameIndex=>markData.startFrame; public int StartFrameIndex=>markData.startFrame;

View File

@ -364,7 +364,7 @@ namespace GAS.Editor
listView.showFoldoutHeader = true; listView.showFoldoutHeader = true;
listView.itemsSource = list; listView.itemsSource = list;
listView.selectionType = SelectionType.Single; listView.selectionType = SelectionType.Single;
listView.selectionChanged += onSelectionChanged; listView.onSelectionChange += onSelectionChanged;
listView.itemsAdded += onItemsAdded; listView.itemsAdded += onItemsAdded;
listView.itemsRemoved += onItemsRemoved; listView.itemsRemoved += onItemsRemoved;

View File

@ -0,0 +1,50 @@

// #if UNITY_EDITOR
// namespace GAS.Editor
// {
// using Editor;
// using GAS.General;
// using Runtime;
// using UnityEngine;
// using UnityEngine.UIElements;
// using Sirenix.OdinInspector;
// public class CatchAreaBox2DInspector:TargetCatcherInspector<CatchAreaBox2D>
// {
// [BoxGroup] [Delayed] [OnValueChanged("OnCatcherChanged")]
// public Vector2 Offset;
// [BoxGroup] [Delayed] [OnValueChanged("OnCatcherChanged")]
// public Vector2 Size;
// [BoxGroup] [Delayed] [LabelText("Rotation")] [OnValueChanged("OnCatcherChanged")]
// public float Rotation;
// [BoxGroup] [Delayed] [LabelText("Detect Layer")] [OnValueChanged("OnCatcherChanged")]
// public LayerMask Layer;
// [BoxGroup] [Delayed] [LabelText("Center Type")] [OnValueChanged("OnCatcherChanged")]
// public EffectCenterType CenterType;
// public CatchAreaBox2DInspector(CatchAreaBox2D targetCatcherBase) : base(targetCatcherBase)
// {
// Offset = targetCatcherBase.offset;
// Size = targetCatcherBase.size;
// Rotation = targetCatcherBase.rotation;
// Layer = targetCatcherBase.checkLayer;
// CenterType = targetCatcherBase.centerType;
// }
// public void OnCatcherChanged()
// {
// _targetCatcher.offset = Offset;
// _targetCatcher.size = Size;
// _targetCatcher.rotation = Rotation;
// _targetCatcher.checkLayer = Layer;
// _targetCatcher.centerType = CenterType;
// Save();
// }
// }
// }
// #endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 16da9d76e7c940b5a560bb31dab5d588
timeCreated: 1709458583

View File

@ -1,3 +1,3 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 8378e98772454500b5c36d8f4c14862d guid: 45bb062aed8542afab83194d24ee85c8
timeCreated: 1729190435 timeCreated: 1717573129

View File

@ -0,0 +1,101 @@
using System;
using UnityEngine;
namespace GAS.Runtime
{
/// <summary>
/// Timeline轨道数据的扩展操作
/// </summary>
public static class TrackDataExtension
{
/// <summary>
/// 编辑器下使用,添加
/// </summary>
/// <param name="trackData"></param>
/// <param name="abilityAsset"></param>
/// <param name="subType"></param>
public static void AddToAbilityAsset(this TrackDataBase trackData, TimelineAbilityAsset abilityAsset, Type subType)
{
CommonAddToAbilityAsset(trackData, abilityAsset);
switch (trackData)
{
case BuffGameplayEffectTrackData buffTrackData:
abilityAsset.BuffGameplayEffects.Add(buffTrackData);
break;
case DurationalCueTrackData durationalCueTrackData:
abilityAsset.DurationalCues.Add(durationalCueTrackData);
break;
case InstantCueTrackData instCueTrackData:
abilityAsset.InstantCues.Add(instCueTrackData);
break;
case PassiveGameplayEffectTrackData passiveGETrackData:
abilityAsset.PassiveGameplayEffects.Add(passiveGETrackData);
break;
case PassiveTaskClipEventTrackData passiveTaskTrackData:
abilityAsset.PassiveTasks.Add(passiveTaskTrackData);
break;
case ReleaseGameplayEffectTrackData releaseGETrackData:
abilityAsset.ReleaseGameplayEffect.Add(releaseGETrackData);
break;
case TaskMarkEventTrackData eventTrackData:
abilityAsset.InstantTasks.Add(eventTrackData);
break;
case TaskClipEventTrackData clipTrackData:
abilityAsset.OngoingTasks.Add(clipTrackData);
break;
default:
Debug.LogError($"AddToAbilityAsset, 未知的轨道数据类型type = {trackData.GetType()}");
break;
}
}
/// <summary>
/// Timeline添加轨道数据的通用操作
/// </summary>
/// <param name="trackData"></param>
/// <param name="abilityAsset"></param>
public static void CommonAddToAbilityAsset(this TrackDataBase trackData, TimelineAbilityAsset abilityAsset)
{
// Do nothing有后续的通用操作在此添加
}
/// <summary>
/// 默认初始化TrackData编辑器下使用
/// </summary>
/// <param name="trackData"></param>
public static void DefaultInit(this TrackDataBase trackData)
{
switch (trackData)
{
case BuffGameplayEffectTrackData buffTrackData:
trackData.trackName = "BuffGE";
break;
case DurationalCueTrackData durationalCueTrackData:
trackData.trackName = "DurationalCue";
break;
case InstantCueTrackData instCueTrackData:
trackData.trackName = "InstantCue";
break;
case PassiveGameplayEffectTrackData passiveGETrackData:
trackData.trackName = "PassiveGE";
break;
case PassiveTaskClipEventTrackData passiveTaskTrackData:
trackData.trackName = "PassiveTask";
break;
case ReleaseGameplayEffectTrackData releaseGETrackData:
trackData.trackName = "ReleaseGE";
break;
case TaskMarkEventTrackData eventTrackData:
trackData.trackName = "TaskMark";
break;
case TaskClipEventTrackData clipTrackData:
trackData.trackName = "TaskClips";
break;
default:
Debug.LogError($"DefaultInit, 未知的轨道数据类型type = {trackData.GetType()}");
break;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3432f172cdc3c04da96b7ea7ee26942
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,59 @@
#if UNITY_EDITOR
using GAS.Runtime;
using JNGame.Serialization;
namespace GAS.Editor
{
public static class AbilitySystemComponentPresetConverter
{
public static byte[] ToBytes(this AbilitySystemComponentPreset preset)
{
Serializer serializer = new Serializer();
serializer.Write(preset.name);
ushort length = (ushort)(preset.attributeSets == null ? 0 : preset.attributeSets.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
serializer.Write(preset.attributeSets[i]);
}
serializer.WriteArray(preset.baseTags);
length = (ushort)(preset.baseAbilities == null ? 0 : preset.baseAbilities.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
serializer.Write(preset.baseAbilities[i] == null ? "" : preset.baseAbilities[i].name);
}
return serializer.CopyData();
}
/// <summary>
/// PureASCPresetAsset数据序列化为字节数据的工具函数
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public static byte[] Serialize(PureASCPresetAsset geAsset)
{
var writer = new Serializer();
geAsset.Serialize(writer);
return writer.CopyData();
}
/// <summary>
/// 字节数据反序列化为PureASCPresetAsset的工具函数
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static PureASCPresetAsset Deserialize(byte[] bytes)
{
var reader = new Deserializer(bytes);
var geAsset = new PureASCPresetAsset();
geAsset.Deserialize(reader);
return geAsset;
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a57c5f1e00ea4a4299f302338ee43a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -3,8 +3,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using GAS.Editor.General; using GAS.Editor.General;
using GAS.Editor.General.Validation;
using GAS.General; using GAS.General;
using GAS.General.Validation;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using Sirenix.Utilities.Editor; using Sirenix.Utilities.Editor;
using UnityEditor; using UnityEditor;
@ -23,21 +23,28 @@ namespace GAS.Editor
[HorizontalGroup("A/R", order: 1)] [HorizontalGroup("A/R", order: 1)]
[DisplayAsString(TextAlignment.Left, FontSize = 18)] [DisplayAsString(TextAlignment.Left, FontSize = 18)]
[HideLabel] [HideLabel]
[ValidateInput("@ExistDuplicatedAttribute() == false", GASTextDefine.ERROR_DuplicatedAttribute)] [InfoBox(GASTextDefine.ERROR_DuplicatedAttribute, InfoMessageType.Error,
VisibleIf = "ExistDuplicatedAttribute")]
[InfoBox(GASTextDefine.ERROR_Empty, InfoMessageType.Error, VisibleIf = "EmptyAttribute")] [InfoBox(GASTextDefine.ERROR_Empty, InfoMessageType.Error, VisibleIf = "EmptyAttribute")]
[InfoBox(GASTextDefine.ERROR_EmptyName, InfoMessageType.Error, VisibleIf = "EmptyAttributeSetName")] [InfoBox(GASTextDefine.ERROR_EmptyName, InfoMessageType.Error, VisibleIf = "EmptyAttributeSetName")]
public string Name = "Unnamed"; public string Name = "Unnamed";
[Space] [Space]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = false, ShowPaging = false)] [ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = false, ShowPaging = false,
OnTitleBarGUI = "DrawAttributeNamesButtons")]
[ValueDropdown("AttributeChoices", IsUniqueList = true)] [ValueDropdown("AttributeChoices", IsUniqueList = true)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@SortAttributeNames()")]
[LabelText("Attributes")] [LabelText("Attributes")]
[Searchable] [Searchable]
public List<string> AttributeNames = new(); public List<string> AttributeNames = new List<string>();
private void SortAttributeNames() => AttributeNames = AttributeNames.OrderBy(x => x).ToList(); private void DrawAttributeNamesButtons()
{
if (SirenixEditorGUI.ToolbarButton(SdfIconType.SortAlphaDown))
{
AttributeNames = AttributeNames.OrderBy(x => x).ToList();
ParentAsset.SaveAsset();
}
}
[HorizontalGroup("A", Width = 50)] [HorizontalGroup("A", Width = 50)]
[HorizontalGroup("A/L", order: 0, Width = 50)] [HorizontalGroup("A/L", order: 0, Width = 50)]
@ -100,20 +107,24 @@ namespace GAS.Editor
[ListDrawerSettings(ShowFoldout = true, [ListDrawerSettings(ShowFoldout = true,
CustomAddFunction = "OnAddAttributeSet", CustomAddFunction = "OnAddAttributeSet",
CustomRemoveElementFunction = "OnRemoveElement", CustomRemoveElementFunction = "OnRemoveElement",
CustomRemoveIndexFunction = "OnRemoveIndex")] CustomRemoveIndexFunction = "OnRemoveIndex", OnTitleBarGUI = "DrawAttributeSetConfigsButtons")]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@SortAttributeSetConfigs()")]
[Searchable] [Searchable]
public List<AttributeSetConfig> AttributeSetConfigs = new List<AttributeSetConfig>(); public List<AttributeSetConfig> AttributeSetConfigs = new List<AttributeSetConfig>();
private void SortAttributeSetConfigs() => AttributeSetConfigs = AttributeSetConfigs.OrderBy(x => x.Name).ToList(); private void DrawAttributeSetConfigsButtons()
{
if (SirenixEditorGUI.ToolbarButton(SdfIconType.SortAlphaDown))
{
AttributeSetConfigs = AttributeSetConfigs.OrderBy(x => x.Name).ToList();
SaveAsset();
}
}
private void OnEnable() private void OnEnable()
{ {
AttributeSetConfig.ParentAsset = this; AttributeSetConfig.ParentAsset = this;
var asset = AttributeAsset.LoadOrCreate(); var asset = AttributeAsset.LoadOrCreate();
var attributeChoices = asset != null ? (from attr in asset.attributes where !string.IsNullOrWhiteSpace(attr.Name) select attr.Name).ToList() : new(); AttributeSetConfig.SetAttributeChoices(asset?.AttributeNames);
AttributeSetConfig.SetAttributeChoices(attributeChoices);
} }
public void SaveAsset() public void SaveAsset()

View File

@ -1,14 +1,13 @@
using System; #if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using GAS.Runtime;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
namespace GAS.Editor
{
public static class AttributeSetClassGen public static class AttributeSetClassGen
{ {
public static void Gen() public static void Gen()
@ -16,8 +15,7 @@ namespace GAS.Editor
var attributeSetAsset = AttributeSetAsset.LoadOrCreate(); var attributeSetAsset = AttributeSetAsset.LoadOrCreate();
var attributeAsset = AttributeAsset.LoadOrCreate(); var attributeAsset = AttributeAsset.LoadOrCreate();
var attributeNames = var attributeNames = (from t in attributeAsset.attributes where !string.IsNullOrWhiteSpace(t.Name) select t.Name)
(from t in attributeAsset.attributes where !string.IsNullOrWhiteSpace(t.Name) select t.Name)
.ToList(); .ToList();
// Check if AttributeSet contains attribute that is not defined in AttributeAsset // Check if AttributeSet contains attribute that is not defined in AttributeAsset
@ -27,7 +25,8 @@ namespace GAS.Editor
{ {
if (!attributeNames.Contains(attributeName)) if (!attributeNames.Contains(attributeName))
{ {
var msg = $"Invalid Attribute(\"{attributeName}\") in AttributeSet(\"{attributeSetConfig.Name}\"), \"{attributeName}\" is not defined in AttributeAsset!"; var msg =
$"Invalid Attribute(\"{attributeName}\") in AttributeSet(\"{attributeSetConfig.Name}\"), \"{attributeName}\" is not defined in AttributeAsset!";
Debug.LogError(msg); Debug.LogError(msg);
EditorUtility.DisplayDialog("Error", msg, "OK"); EditorUtility.DisplayDialog("Error", msg, "OK");
return; return;
@ -35,29 +34,13 @@ namespace GAS.Editor
} }
} }
var invalidAttributes = attributeAsset.attributes.Where(x =>
x.CalculateMode is CalculateMode.MinValueOnly or CalculateMode.MaxValueOnly &&
x.SupportedOperation != SupportedOperation.Override).ToArray();
if (invalidAttributes.Length > 0)
{
var msg =
$"计算模式为\"\"或\"取最大值\"的属性只能支持\"替换\"操作:\n{string.Join("\n", invalidAttributes.Select(x => $"\"{x.Name}\""))}";
Debug.LogError(msg.Replace("\n", ", "));
EditorUtility.DisplayDialog("Error", msg, "OK");
return;
}
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6); string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
var filePath = var filePath =
$"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ATTRIBUTESET_LIB_CSHARP_SCRIPT_NAME}"; $"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ATTRIBUTESET_LIB_CSHARP_SCRIPT_NAME}";
GenerateAttributeCollection(attributeSetAsset.AttributeSetConfigs, attributeAsset, filePath); GenerateAttributeCollection(attributeSetAsset.AttributeSetConfigs, filePath);
Console.WriteLine($"Generated Code Script at path: {filePath}");
} }
private static void GenerateAttributeCollection(List<AttributeSetConfig> attributeSetConfigs, private static void GenerateAttributeCollection(List<AttributeSetConfig> attributeSetConfigs, string filePath)
AttributeAsset attributeAsset, string filePath)
{ {
using var writer = new IndentedWriter(new StreamWriter(filePath)); using var writer = new IndentedWriter(new StreamWriter(filePath));
@ -70,7 +53,7 @@ namespace GAS.Editor
writer.WriteLine("using System;"); writer.WriteLine("using System;");
writer.WriteLine("using System.Collections.Generic;"); writer.WriteLine("using System.Collections.Generic;");
writer.WriteLine("using JNGame.Math;"); writer.WriteLine("using GAS.General;");
writer.WriteLine(""); writer.WriteLine("");
@ -78,18 +61,16 @@ namespace GAS.Editor
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
foreach (var attributeSetConfig in attributeSetConfigs.OrderBy(x => x.Name)) foreach (var attributeSet in attributeSetConfigs)
{ {
var validName = EditorUtil.MakeValidIdentifier(attributeSetConfig.Name); var validName = EditorUtil.MakeValidIdentifier(attributeSet.Name);
writer.WriteLine($"public class AS_{validName} : AttributeSet"); writer.WriteLine($"public class AS_{validName} : AttributeSet");
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
bool skippedFirst = false; bool skippedFirst = false;
foreach (var attributeName in attributeSetConfig.AttributeNames.OrderBy(x => x)) foreach (var attributeName in attributeSet.AttributeNames)
{ {
var attributeAccessor = attributeAsset.attributes.Find(x => x.Name == attributeName);
if (!skippedFirst) skippedFirst = true; if (!skippedFirst) skippedFirst = true;
else writer.WriteLine(""); else writer.WriteLine("");
@ -97,15 +78,46 @@ namespace GAS.Editor
writer.WriteLine($"#region {attributeName}"); writer.WriteLine($"#region {attributeName}");
writer.WriteLine(""); writer.WriteLine("");
{ {
writer.WriteLine($"/// <summary>{attributeAccessor.Comment}</summary>"); writer.WriteLine(
writer.WriteLine($"public AttributeBase {validAttrName} {{ get; }} = new(\"AS_{validName}\", \"{attributeName}\", new LFloat(true,{attributeAccessor.DefaultValue.rawValue}), {(attributeAccessor.LimitMinValue ? $"new LFloat(true,{attributeAccessor.MinValue.rawValue})" : "LFloat.MinValue")}, {(attributeAccessor.LimitMaxValue ? $"new LFloat(true,{attributeAccessor.MaxValue.rawValue})" : "LFloat.MaxValue")}, CalculateMode.{attributeAccessor.CalculateMode}, (SupportedOperation){(byte)attributeAccessor.SupportedOperation});"); $"private AttributeBase _{validAttrName} = new AttributeBase(\"AS_{validName}.{attributeName}\",\"AS_{validName}\", \"{attributeName}\");");
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine($"public void Init{validAttrName}(LFloat value) => {validAttrName}.Init(value);");
writer.WriteLine($"public void SetCurrent{validAttrName}(LFloat value) => {validAttrName}.SetCurrentValue(value);"); writer.WriteLine($"public AttributeBase {validAttrName} => _{validAttrName};");
writer.WriteLine($"public void SetBase{validAttrName}(LFloat value) => {validAttrName}.SetBaseValue(value);");
writer.WriteLine($"public void SetMin{validAttrName}(LFloat value) => {validAttrName}.SetMinValue(value);"); writer.WriteLine("");
writer.WriteLine($"public void SetMax{validAttrName}(LFloat value) => {validAttrName}.SetMaxValue(value);");
writer.WriteLine($"public void SetMinMax{validAttrName}(LFloat min, LFloat max) => {validAttrName}.SetMinMaxValue(min, max);"); writer.WriteLine($"public void Init{validAttrName}(int value)");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine($"_{validAttrName}.SetBaseValue(value);");
writer.WriteLine($"_{validAttrName}.SetCurrentValue(value);");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
writer.WriteLine($"public void SetCurrent{validAttrName}(int value)");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine($"_{validAttrName}.SetCurrentValue(value);");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
writer.WriteLine($"public void SetBase{validAttrName}(int value)");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine($"_{validAttrName}.SetBaseValue(value);");
}
writer.Indent--;
writer.WriteLine("}");
} }
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine($"#endregion {attributeName}"); writer.WriteLine($"#endregion {attributeName}");
@ -125,13 +137,13 @@ namespace GAS.Editor
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
foreach (var attributeName in attributeSetConfig.AttributeNames) foreach (var attributeName in attributeSet.AttributeNames)
{ {
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName); string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
writer.WriteLine($"case \"{validAttrName}\":"); writer.WriteLine($"case \"{validAttrName}\":");
writer.Indent++; writer.Indent++;
{ {
writer.WriteLine($"return {validAttrName};"); writer.WriteLine($"return _{validAttrName};");
} }
writer.Indent--; writer.Indent--;
} }
@ -153,7 +165,7 @@ namespace GAS.Editor
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
foreach (var attributeName in attributeSetConfig.AttributeNames) foreach (var attributeName in attributeSet.AttributeNames)
{ {
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName); string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
writer.WriteLine($"\"{validAttrName}\","); writer.WriteLine($"\"{validAttrName}\",");
@ -168,10 +180,10 @@ namespace GAS.Editor
writer.Indent++; writer.Indent++;
{ {
writer.WriteLine("_owner = owner;"); writer.WriteLine("_owner = owner;");
foreach (var attributeName in attributeSetConfig.AttributeNames) foreach (var attributeName in attributeSet.AttributeNames)
{ {
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName); string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
writer.WriteLine($"{validAttrName}.SetOwner(owner);"); writer.WriteLine($"_{validAttrName}.SetOwner(owner);");
} }
} }
writer.Indent--; writer.Indent--;
@ -182,15 +194,16 @@ namespace GAS.Editor
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
foreach (var attributeName in attributeSetConfig.AttributeNames) foreach (var attributeName in attributeSet.AttributeNames)
{ {
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName); string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
writer.WriteLine( writer.WriteLine($"public const string {attributeName} = \"AS_{validName}.{validAttrName}\";");
$"public const string {attributeName} = \"AS_{validName}.{validAttrName}\";");
} }
} }
writer.Indent--; writer.Indent--;
writer.WriteLine("}"); writer.WriteLine("}");
writer.WriteLine($"public static readonly string SetName = \"AS_{validName}\";");
} }
writer.Indent--; writer.Indent--;
writer.WriteLine("}"); writer.WriteLine("}");
@ -201,7 +214,8 @@ namespace GAS.Editor
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
writer.WriteLine("public static readonly IReadOnlyDictionary<string, Type> AttrSetTypeDict = new Dictionary<string, Type>"); writer.WriteLine(
"public static readonly Dictionary<string, Type> AttrSetTypeDict = new Dictionary<string, Type>()");
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
@ -216,7 +230,8 @@ namespace GAS.Editor
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine("public static readonly IReadOnlyDictionary<Type, string> TypeToName = new Dictionary<Type, string>"); writer.WriteLine(
"public static readonly Dictionary<Type, string> TypeToName = new Dictionary<Type, string>");
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
@ -229,9 +244,32 @@ namespace GAS.Editor
writer.Indent--; writer.Indent--;
writer.WriteLine("};"); writer.WriteLine("};");
// writer.WriteLine("");
// writer.WriteLine(
// "public static AttributeSet GetSetInstance(string attrSetType)");
// writer.WriteLine("{");
// writer.WriteLine(" if (attrSetType.Contains(\"AS_Fight\"))");
// writer.WriteLine("{");
// writer.WriteLine("return ObjectPool.Instance.Fetch<AS_Fight>();");
// writer.WriteLine("}");
// writer.WriteLine("else if(attrSetType.Contains(\"AS_BaseAttr\"))");
// writer.WriteLine("{");
// writer.WriteLine("return ObjectPool.Instance.Fetch<AS_BaseAttr>();");
// writer.WriteLine("}");
// writer.WriteLine("return null;");
// writer.WriteLine("}");
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine("public static readonly IReadOnlyList<string> AttributeFullNames = new List<string>"); writer.WriteLine(
"public static void RecycleSetInstance(object obj)");
writer.WriteLine("{");
writer.WriteLine(" ObjectPool.Instance.Recycle(obj);");
writer.WriteLine("}");
writer.WriteLine("");
writer.WriteLine("public static List<string> AttributeFullNames = new List<string>()");
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
@ -245,14 +283,27 @@ namespace GAS.Editor
} }
} }
} }
writer.Indent--; writer.Indent--;
writer.WriteLine("};"); writer.WriteLine("};");
writer.WriteLine("");
// writer.WriteLine("public static string GetName(string setName, string shortName)");
// writer.WriteLine("{");
// writer.Indent++;
// {
// }
// writer.Indent--;
// writer.WriteLine("};");
} }
writer.Indent--; writer.Indent--;
writer.WriteLine("}"); writer.WriteLine("}");
} }
writer.Indent--; writer.Indent--;
writer.Write("}"); writer.Write("}");
Console.WriteLine($"Generated Code Script at path: {filePath}");
} }
} }
} }
#endif

View File

@ -1,11 +1,14 @@
using System; #if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using GAS;
using Editor;
using Runtime;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
namespace GAS.Editor
{
public class AttributeSetConfigEditorWindow : EditorWindow public class AttributeSetConfigEditorWindow : EditorWindow
{ {
private static List<string> _attributeOptions; private static List<string> _attributeOptions;
@ -17,7 +20,7 @@ namespace GAS.Editor
private Action<string, List<string>> _callback; private Action<string, List<string>> _callback;
private Func<AttributeSetConfig, bool> _checkAttributeSetValid; private Func<AttributeSetConfig, bool> _checkAttributeSetValid;
private List<int> _selectedAttributeIndexes; private List<int> _selectedAttributeIndexs;
private GUIStyle BigFontLabelStyle; private GUIStyle BigFontLabelStyle;
@ -28,7 +31,7 @@ namespace GAS.Editor
if (_attributeOptions == null) if (_attributeOptions == null)
{ {
var asset = AttributeAsset.LoadOrCreate(); var asset = AttributeAsset.LoadOrCreate();
_attributeOptions = asset != null ? (from attr in asset.attributes where !string.IsNullOrWhiteSpace(attr.Name) select attr.Name).OrderBy(x => x).ToList() : new(); _attributeOptions = asset?.AttributeNames;
} }
return _attributeOptions; return _attributeOptions;
@ -57,7 +60,7 @@ namespace GAS.Editor
if (GUILayout.Button("Add Attribute", GUILayout.Width(100))) if (GUILayout.Button("Add Attribute", GUILayout.Width(100)))
{ {
attributeNames.Add(""); attributeNames.Add("");
_selectedAttributeIndexes.Add(-1); _selectedAttributeIndexs.Add(-1);
} }
EditorGUILayout.Space(); EditorGUILayout.Space();
@ -69,13 +72,13 @@ namespace GAS.Editor
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Attribute:", GUILayout.Width(80)); EditorGUILayout.LabelField("Attribute:", GUILayout.Width(80));
_selectedAttributeIndexes[i] = EditorGUILayout.Popup("", _selectedAttributeIndexes[i], _selectedAttributeIndexs[i] = EditorGUILayout.Popup("", _selectedAttributeIndexs[i],
AttributeOptions.ToArray()); AttributeOptions.ToArray());
// 更新选中的字符串 // 更新选中的字符串
attributeNames[i] = _selectedAttributeIndexes[i] < 0 attributeNames[i] = _selectedAttributeIndexs[i] < 0
? "" ? ""
: AttributeOptions[_selectedAttributeIndexes[i]]; : AttributeOptions[_selectedAttributeIndexs[i]];
if (GUILayout.Button("Remove", GUILayout.Width(100))) if (GUILayout.Button("Remove", GUILayout.Width(100)))
{ {
@ -101,9 +104,9 @@ namespace GAS.Editor
_callback = callback; _callback = callback;
_checkAttributeSetValid = checkAttributeSetValid; _checkAttributeSetValid = checkAttributeSetValid;
this.attributeNames = attributeNames; this.attributeNames = attributeNames;
_selectedAttributeIndexes = new List<int>(); _selectedAttributeIndexs = new List<int>();
foreach (var attributeName in attributeNames) foreach (var attributeName in attributeNames)
_selectedAttributeIndexes.Add(AttributeOptions.IndexOf(attributeName)); _selectedAttributeIndexs.Add(AttributeOptions.IndexOf(attributeName));
BigFontLabelStyle = new GUIStyle(EditorStyles.label); BigFontLabelStyle = new GUIStyle(EditorStyles.label);
@ -112,7 +115,7 @@ namespace GAS.Editor
private void Save() private void Save()
{ {
var attributeSetConfig = new AttributeSetConfig() AttributeSetConfig attributeSetConfig = new AttributeSetConfig()
{ {
Name = editedName, Name = editedName,
AttributeNames = attributeNames AttributeNames = attributeNames
@ -131,3 +134,4 @@ namespace GAS.Editor
} }
} }
} }
#endif

View File

@ -1,48 +1,57 @@
using System; 
#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using GAS.Editor.General;
using GAS.General;
using GAS.General.Validation;
using GAS.Runtime;
using JNGame.Math;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using GAS.General;
using Sirenix.Utilities.Editor;
namespace GAS.Editor
{
[FilePath(GasDefine.GAS_ATTRIBUTE_ASSET_PATH)] [FilePath(GasDefine.GAS_ATTRIBUTE_ASSET_PATH)]
internal class AttributeAsset : ScriptableSingleton<AttributeAsset> public class AttributeAsset : ScriptableSingleton<AttributeAsset>
{ {
[BoxGroup("Warning", order: -1)] [BoxGroup("Warning", order: -1)]
[HideLabel] [HideLabel]
[ShowIf("ExistDuplicatedAttribute")] [ShowIf("ExistDuplicatedAttribute")]
[DisplayAsString(TextAlignment.Left, true)] [DisplayAsString(TextAlignment.Left, true)]
[NonSerialized] public string Warning_DuplicatedAttribute = "";
[ShowInInspector]
private string Warning_DuplicatedAttribute = "";
[VerticalGroup("Attributes", order: 1)] [VerticalGroup("Attributes", order: 1)]
[ListDrawerSettings( [ListDrawerSettings(
ShowFoldout = true, Expanded = true,
CustomRemoveElementFunction = "OnRemoveElement", CustomRemoveElementFunction = "OnRemoveElement",
CustomRemoveIndexFunction = "OnRemoveIndex", CustomRemoveIndexFunction = "OnRemoveIndex",
CustomAddFunction = "OnAddAttribute", CustomAddFunction = "OnAddAttribute",
ShowPaging = false)] ShowPaging = false, OnTitleBarGUI = "DrawAttributeButtons")]
[Searchable] [Searchable]
[OnValueChanged("@SaveAsset()", true)] [OnValueChanged("Save")]
[OnCollectionChanged(after: "@SaveAsset()")] public List<AttributeAccessor> attributes = new List<AttributeAccessor>();
[CustomContextMenu("排序", "@SortAttributes()")]
[SerializeField]
public List<AttributeAccessor> attributes = new();
private void SortAttributes() => attributes = attributes.OrderBy(x => x.Name).ToList(); private void DrawAttributeButtons()
{
if (SirenixEditorGUI.ToolbarButton(SdfIconType.SortAlphaDown))
{
attributes = attributes.OrderBy(x => x.Name).ToList();
}
}
public List<string> AttributeNames =>
(from attr in attributes where !string.IsNullOrEmpty(attr.Name) select attr.Name).ToList();
private void OnEnable()
{
AttributeAccessor.ParentAsset = this;
}
[VerticalGroup("Gen Code", order: 0)] [VerticalGroup("Gen Code", order: 0)]
[GUIColor(0, 0.9f, 0)] [GUIColor(0, 0.9f, 0)]
[Button(SdfIconType.Upload, GASTextDefine.BUTTON_GenerateAttributeCollection, ButtonHeight = 30, Expanded = true)] [Button(SdfIconType.Upload, GASTextDefine.BUTTON_GenerateAttributeCollection, ButtonHeight = 30,
[ValidateInput("@ExistEmptyAttribute() == false", GASTextDefine.TIP_Warning_EmptyAttribute)] Expanded = true)]
[InfoBox(GASTextDefine.TIP_Warning_EmptyAttribute, InfoMessageType.Error, VisibleIf = "ExistEmptyAttribute")]
void GenCode() void GenCode()
{ {
if (ExistEmptyAttribute() || ExistDuplicatedAttribute()) if (ExistEmptyAttribute() || ExistDuplicatedAttribute())
@ -95,27 +104,12 @@ namespace GAS.Editor
private void OnAddAttribute() private void OnAddAttribute()
{ {
StringEditWindow.OpenWindow("创建新属性", null, newName => AttributeEditorWindow.OpenWindow(new AttributeEditorWindow.Data(), AttributeNames, (d =>
{ {
var validateVariableName = Validations.ValidateVariableName(newName); attributes.Add(new AttributeAccessor(d.Name, d.Comment));
if (validateVariableName.IsValid == false)
{
return validateVariableName;
}
if (attributes.Exists(x => x.Name == newName))
{
return ValidationResult.Invalid($"属性名已存在: \"{newName}\"!");
}
return ValidationResult.Valid;
}, x =>
{
attributes.Add(new AttributeAccessor { Name = x });
SaveAsset(); SaveAsset();
Debug.Log("[EX] Attribute Asset add element!"); Debug.Log("[EX] Attribute Asset add element!");
}); }), "Add new Attribute");
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first." GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
} }
@ -145,85 +139,42 @@ namespace GAS.Editor
} }
[Serializable] [Serializable]
internal sealed class AttributeAccessor public class AttributeAccessor
{ {
private const int LabelWidth = 100; public static AttributeAsset ParentAsset;
private string DisplayName => $"{Name} - {Comment}"; [HorizontalGroup("A")] [HorizontalGroup("A/R", order: 1)] [DisplayAsString] [HideLabel]
public string Name;
[FoldoutGroup("$DisplayName", false)] [HorizontalGroup("A")] [HorizontalGroup("A/R", order: 3)] [DisplayAsString] [HideLabel]
[LabelText("属性名"), LabelWidth(LabelWidth)] public string Comment;
[DelayedProperty]
[ValidateInput("@Validations.IsValidVariableName($value)", "Attribute name is invalid!")]
[PropertyOrder(1)]
public string Name = "Unnamed";
[FoldoutGroup("$DisplayName")] public AttributeAccessor(string attributeName, string attributeComment = "")
[DelayedProperty]
[LabelText("备注"), LabelWidth(LabelWidth)]
[PropertyOrder(2)]
public string Comment = "";
[FoldoutGroup("$DisplayName")]
[LabelText("计算模式"), LabelWidth(LabelWidth)]
[PropertyOrder(3)]
[OnValueChanged("@OnCalculateModeChanged()")]
[HorizontalGroup("$DisplayName/d")]
public CalculateMode CalculateMode = CalculateMode.Stacking;
private void OnCalculateModeChanged()
{ {
if (CalculateMode is CalculateMode.MinValueOnly or CalculateMode.MaxValueOnly) Name = string.IsNullOrWhiteSpace(attributeName) ? "Unnamed" : attributeName;
Comment = string.IsNullOrWhiteSpace(attributeComment) ? "" : attributeComment;
}
[HorizontalGroup("A", Width = 50)]
[HorizontalGroup("A/L", order: 0, Width = 50)]
[Button(SdfIconType.Brush, "", ButtonHeight = 25)]
public void Edit()
{ {
SupportedOperation = SupportedOperation.Override; if (ParentAsset == null) return;
var nameBlackList = ParentAsset.AttributeNames.Where(x => x != Name);
AttributeEditorWindow.OpenWindow(new AttributeEditorWindow.Data(Name, Comment), nameBlackList,
x =>
{
if (ParentAsset != null)
{
Name = x.Name;
Comment = x.Comment;
ParentAsset.SaveAsset();
} }
} }, "Edit Attribute Asset");
[FoldoutGroup("$DisplayName")]
[LabelText("支持运算"), LabelWidth(LabelWidth)]
[PropertyOrder(4)]
[DisableIf(
"@CalculateMode == GAS.Runtime.CalculateMode.MinValueOnly || CalculateMode == GAS.Runtime.CalculateMode.MaxValueOnly")]
[HorizontalGroup("$DisplayName/d")]
public SupportedOperation SupportedOperation = SupportedOperation.All;
[FoldoutGroup("$DisplayName")]
[LabelText("默认值"), LabelWidth(LabelWidth)]
[DelayedProperty]
[PropertyOrder(5)]
[HorizontalGroup("$DisplayName/Values")]
public LFloat DefaultValue = LFloat.L0;
[FoldoutGroup("$DisplayName")]
[LabelText("最小值"), LabelWidth(40)]
[DelayedProperty]
[PropertyOrder(6)]
[HorizontalGroup("$DisplayName/Values")]
[ToggleLeft]
public bool LimitMinValue = false;
[FoldoutGroup("$DisplayName")]
[HideLabel]
[DelayedProperty]
[PropertyOrder(6)]
[EnableIf("LimitMinValue")]
[HorizontalGroup("$DisplayName/Values")]
public LFloat MinValue = LFloat.MinValue;
[FoldoutGroup("$DisplayName")]
[LabelText("最大值"), LabelWidth(50)]
[DelayedProperty]
[PropertyOrder(6)]
[HorizontalGroup("$DisplayName/Values")]
public bool LimitMaxValue = false;
[FoldoutGroup("$DisplayName")]
[HideLabel]
[DelayedProperty]
[PropertyOrder(7)]
[EnableIf("LimitMaxValue")]
[HorizontalGroup("$DisplayName/Values")]
public LFloat MaxValue = LFloat.MaxValue;
} }
} }
} }
}
#endif

View File

@ -1,11 +1,13 @@
using System; #if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace GAS.Editor
{
public static class AttributeCollectionGen public static class AttributeCollectionGen
{ {
private sealed class AttributeInfo private sealed class AttributeInfo
@ -28,8 +30,6 @@ namespace GAS.Editor
GenerateAttributeCollection(attributeInfos, filePath); GenerateAttributeCollection(attributeInfos, filePath);
Console.WriteLine($"Generated GTagLib at path: {filePath}");
} }
private static void GenerateAttributeCollection(IEnumerable<AttributeInfo> attributes, string filePath) private static void GenerateAttributeCollection(IEnumerable<AttributeInfo> attributes, string filePath)
@ -63,7 +63,9 @@ namespace GAS.Editor
else writer.WriteLine(); else writer.WriteLine();
var validName = EditorUtil.MakeValidIdentifier(attr.Name); var validName = EditorUtil.MakeValidIdentifier(attr.Name);
writer.WriteLine($"/// <summary>{attr.Comment}</summary>"); writer.WriteLine("/// <summary>");
writer.WriteLine($"/// {attr.Comment}");
writer.WriteLine("/// </summary>");
writer.WriteLine($"public const string {validName} = \"{attr.Name}\";"); writer.WriteLine($"public const string {validName} = \"{attr.Name}\";");
names.Add(validName); names.Add(validName);
} }
@ -71,13 +73,13 @@ namespace GAS.Editor
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine("// For facilitating the creation of a Value Dropdown in the editor."); writer.WriteLine("// For facilitating the creation of a Value Dropdown in the editor.");
writer.WriteLine("public static readonly IReadOnlyList<string> AttributeNames = new List<string>"); writer.WriteLine("public static List<string> AttributeNames = new List<string>()");
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
foreach (var name in names) foreach (var name in names)
{ {
writer.WriteLine($"{name},"); writer.WriteLine($"\"{name}\",");
} }
} }
writer.Indent--; writer.Indent--;
@ -88,6 +90,9 @@ namespace GAS.Editor
} }
writer.Indent--; writer.Indent--;
writer.Write("}"); writer.Write("}");
Console.WriteLine($"Generated GTagLib at path: {filePath}");
} }
} }
} }
#endif

View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GAS.Editor.General.Validation;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor
{
public sealed class AttributeEditorWindow : EditorWindow
{
public sealed class Data
{
public string Name;
public string Comment;
public Data(string name = null, string comment = null)
{
Name = name ?? "";
Comment = comment ?? "He is very lazy and left nothing behind.";
}
public override string ToString()
{
return $"Name: {Name}, Comment: {Comment}";
}
}
Data _oldData;
Data _newData;
private IEnumerable<string> _nameBlackList;
private Action<Data> _callback;
public static void OpenWindow(Data data, IEnumerable<string> nameBlackList,
Action<Data> callback, string title = "AttributeEditorWindow")
{
var window = GetWindow<AttributeEditorWindow>();
window.Init(data, nameBlackList, callback);
window.titleContent = new GUIContent(title);
window.ShowModalUtility();
}
private void Init(Data data, IEnumerable<string> nameBlackList,
Action<Data> callback)
{
_oldData = data;
_newData = new Data(data.Name, data.Comment);
_nameBlackList = nameBlackList;
_callback = callback;
}
private void OnGUI()
{
_newData.Name = EditorGUILayout.TextField($"Attribute Name:", _newData.Name);
_newData.Comment = EditorGUILayout.TextField($"Comment:", _newData.Comment);
EditorGUILayout.Space();
if (GUILayout.Button("Ok"))
{
Save();
}
}
private void Save()
{
// name is empty means create a new attribute
// if name is not empty, show the warning dialog
if (_oldData.Name != "" && (_oldData.Name != _newData.Name || _oldData.Comment != _newData.Comment))
{
var sb = new StringBuilder();
if (_oldData.Name != _newData.Name)
{
sb.AppendLine($"- name: \"{_oldData.Name}\" => \"{_newData.Name}\"");
}
if (_oldData.Comment != _newData.Comment)
{
sb.AppendLine($"- comment: \"{_oldData.Comment}\" => \"{_newData.Comment}\"");
}
if (!EditorUtility.DisplayDialog("The following changes will be made:", sb.ToString(), "Yes", "No"))
{
return;
}
}
var validationResult = Validations.ValidateVariableName(_newData.Name);
if (!validationResult.IsValid)
{
EditorUtility.DisplayDialog("Name error", validationResult.Message, "OK");
return;
}
if (_nameBlackList.Contains(_newData.Name))
{
EditorUtility.DisplayDialog("Name error", "The name already exists!", "OK");
return;
}
_callback?.Invoke(_newData);
Close();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1bf0629a3e1f4cba9b8040ae65a6ea2b
timeCreated: 1711509167

View File

@ -23,42 +23,42 @@ namespace GAS.Editor
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Attribute Name:", GUILayout.Width(100)); EditorGUILayout.LabelField("Attribute Name:", GUILayout.Width(100));
var indexOfTag = _attributeOptions.IndexOf(_sourceModifier.AttributeName); var indexOfTag = _attributeOptions.IndexOf(_sourceModifier.attributeName);
var idx = EditorGUILayout.Popup("", indexOfTag, _attributeOptions.ToArray()); var idx = EditorGUILayout.Popup("", indexOfTag, _attributeOptions.ToArray());
idx = Mathf.Clamp(idx, 0, _attributeOptions.Count - 1); idx = Mathf.Clamp(idx, 0, _attributeOptions.Count - 1);
_sourceModifier.AttributeName = _attributeOptions[idx]; _sourceModifier.attributeName = _attributeOptions[idx];
var nameSplit = _sourceModifier.AttributeName.Split('.'); var nameSplit = _sourceModifier.attributeName.Split('.');
_sourceModifier.AttributeSetName = nameSplit[0]; _sourceModifier.attributeSetName = nameSplit[0];
_sourceModifier.AttributeShortName = nameSplit[1]; _sourceModifier.attributeShortName = nameSplit[1];
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(); EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Operation:", GUILayout.Width(100)); EditorGUILayout.LabelField("Operation:", GUILayout.Width(100));
_sourceModifier.Operation = (GEOperation)EditorGUILayout.EnumPopup("", _sourceModifier.Operation); _sourceModifier.operation = (EnumGEOperation)EditorGUILayout.EnumPopup("", _sourceModifier.operation);
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(); EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Value:", GUILayout.Width(100)); EditorGUILayout.LabelField("Value:", GUILayout.Width(100));
_sourceModifier.ModiferMagnitude = EditorGUILayout.FloatField("", _sourceModifier.ModiferMagnitude).ToLFloat(); _sourceModifier.modiferMagnitude = (LFloat)EditorGUILayout.FloatField("", _sourceModifier.modiferMagnitude);
EditorGUILayout.EndHorizontal(); EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(); EditorGUILayout.Space();
EditorGUILayout.BeginVertical(); EditorGUILayout.BeginVertical();
EditorGUILayout.LabelField("Modifier Magnitude Calculation:", GUILayout.Width(200)); EditorGUILayout.LabelField("Modifier Magnitude Calculation:", GUILayout.Width(200));
_sourceModifier.MMC = (ModifierMagnitudeCalculation)EditorGUILayout.ObjectField("", _sourceModifier.MMC, _sourceModifier.mmc = (ModifierMagnitudeCalculation)EditorGUILayout.ObjectField("", _sourceModifier.mmc,
typeof(ModifierMagnitudeCalculation), false); typeof(ModifierMagnitudeCalculation), false);
EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical();
if (_sourceModifier.MMC != null) if (_sourceModifier.mmc != null)
{ {
EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUILayout.BeginVertical(GUI.skin.box);
EditorGUILayout.LabelField("MMC Info:"); EditorGUILayout.LabelField("MMC Info:");
var editor = UnityEditor.Editor.CreateEditor(_sourceModifier.MMC); var editor = UnityEditor.Editor.CreateEditor(_sourceModifier.mmc);
editor.OnInspectorGUI(); editor.OnInspectorGUI();
EditorGUILayout.EndVertical(); EditorGUILayout.EndVertical();
} }

View File

@ -0,0 +1,100 @@
using GAS.Runtime;
using UnityEditor;
namespace GAS.Editor
{
public static class ForEeachScriptableObject
{
// [MenuItem("EX-GAS/Refresh Ability Asset")]
// public static void ForEachAbilityAsset()
// {
// string[] guids = AssetDatabase.FindAssets("t:AbilityAsset");
// if (guids == null || guids.Length == 0) { return; }
// foreach (string guid in guids)
// {
// string path = AssetDatabase.GUIDToAssetPath(guid);
// AbilityAsset abilityAsset = AssetDatabase.LoadAssetAtPath<AbilityAsset>(path);
// EditorUtility.SetDirty(abilityAsset);
// }
// AssetDatabase.Refresh();
// AssetDatabase.SaveAssets();
// }
// [MenuItem("EX-GAS/Refresh GameplayCue Asset")]
// public static void ForEachGameplayCue()
// {
// string[] guids = AssetDatabase.FindAssets("t:GameplayCue");
// if (guids == null || guids.Length == 0) { return; }
// foreach (string guid in guids)
// {
// string path = AssetDatabase.GUIDToAssetPath(guid);
// GameplayCue cueAsset = AssetDatabase.LoadAssetAtPath<GameplayCue>(path);
// EditorUtility.SetDirty(cueAsset);
// }
// AssetDatabase.Refresh();
// AssetDatabase.SaveAssets();
// }
// [MenuItem("EX-GAS/Refresh GameplayEffect Asset")]
// public static void ForEachGameplayEffectAsset()
// {
// string[] guids = AssetDatabase.FindAssets("t:GameplayEffectAsset");
// if (guids == null || guids.Length == 0) { return; }
// foreach (string guid in guids)
// {
// string path = AssetDatabase.GUIDToAssetPath(guid);
// GameplayEffectAsset gameplayEffectAsset = AssetDatabase.LoadAssetAtPath<GameplayEffectAsset>(path);
// EditorUtility.SetDirty(gameplayEffectAsset);
// }
// AssetDatabase.Refresh();
// AssetDatabase.SaveAssets();
// }
// [MenuItem("EX-GAS/Refresh MMC Asset")]
// public static void ForEachMMCAsset()
// {
// string[] guids = AssetDatabase.FindAssets("t:ModifierMagnitudeCalculation");
// if (guids == null || guids.Length == 0) { return; }
// foreach (string guid in guids)
// {
// string path = AssetDatabase.GUIDToAssetPath(guid);
// ModifierMagnitudeCalculation mmcAsset = AssetDatabase.LoadAssetAtPath<ModifierMagnitudeCalculation>(path);
// EditorUtility.SetDirty(mmcAsset);
// }
// AssetDatabase.Refresh();
// AssetDatabase.SaveAssets();
// }
// [MenuItem("EX-GAS/Refresh AscPreset Asset")]
// public static void ForEachAscPreset()
// {
// string[] guids = AssetDatabase.FindAssets("t:AbilitySystemComponentPreset");
// if (guids == null || guids.Length == 0) { return; }
// foreach (string guid in guids)
// {
// string path = AssetDatabase.GUIDToAssetPath(guid);
// AbilitySystemComponentPreset mmcAsset = AssetDatabase.LoadAssetAtPath<AbilitySystemComponentPreset>(path);
// EditorUtility.SetDirty(mmcAsset);
// }
// AssetDatabase.Refresh();
// AssetDatabase.SaveAssets();
// }
// [MenuItem("EX-GAS/Refresh All GAS Asset")]
// public static void ForEachGASAll()
// {
// ForEachAbilityAsset();
// ForEachGameplayCue();
// ForEachGameplayEffectAsset();
// ForEachMMCAsset();
// ForEachAscPreset();
// }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f4c5ffc9547f8b43842d4de7d67c406
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,16 +1,16 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using UnityEditor;
using UnityEditorInternal; using UnityEditorInternal;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object;
namespace GAS.Editor
{
public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableObject public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableObject
{ {
private static T s_Instance; private static T s_Instance;
public static T Instance public static T Instance
{ {
get get
@ -19,24 +19,21 @@ namespace GAS.Editor
{ {
LoadOrCreate(); LoadOrCreate();
} }
return s_Instance; return s_Instance;
} }
} }
public static T LoadOrCreate() public static T LoadOrCreate()
{ {
string filePath = GetFilePath(); string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath)) if (!string.IsNullOrEmpty(filePath))
{ {
var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath); var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
s_Instance = arr.Length > 0 ? arr[0] as T : s_Instance ? s_Instance : CreateInstance<T>(); s_Instance = arr.Length > 0 ? arr[0] as T : s_Instance??CreateInstance<T>();
} }
else else
{ {
Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid"); Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid");
} }
return s_Instance; return s_Instance;
} }
@ -52,18 +49,11 @@ namespace GAS.Editor
if (!string.IsNullOrEmpty(filePath)) if (!string.IsNullOrEmpty(filePath))
{ {
string directoryName = Path.GetDirectoryName(filePath); string directoryName = Path.GetDirectoryName(filePath);
if (directoryName is null)
{
Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid");
return;
}
if (!Directory.Exists(directoryName)) if (!Directory.Exists(directoryName))
{ {
Directory.CreateDirectory(directoryName); Directory.CreateDirectory(directoryName);
} }
UnityEngine.Object[] obj = new T[1] { s_Instance };
Object[] obj = { s_Instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText); InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText);
//Debug.Log($"Saved ScriptableSingleton to {filePath}"); //Debug.Log($"Saved ScriptableSingleton to {filePath}");
} }
@ -84,12 +74,10 @@ namespace GAS.Editor
?.filepath; ?.filepath;
} }
} }
[AttributeUsage(AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Class)]
public class FilePathAttribute : Attribute public class FilePathAttribute : Attribute
{ {
internal string filepath; internal string filepath;
/// <summary> /// <summary>
/// 单例存放路径 /// 单例存放路径
/// </summary> /// </summary>
@ -100,13 +88,12 @@ namespace GAS.Editor
{ {
throw new ArgumentException("Invalid relative path (it is empty)"); throw new ArgumentException("Invalid relative path (it is empty)");
} }
if (path[0] == '/') if (path[0] == '/')
{ {
path = path[1..]; path = path.Substring(1);
} }
filepath = path; filepath = path;
} }
} }
} }
#endif

View File

@ -0,0 +1,278 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using GAS.Runtime;
using JNGame.Serialization;
namespace GAS.Editor
{
public static class GameplayAbilityAssetConverter
{
internal static RuntimeClipNode ConvertRuntimeClipNode(this DurationalCueClipEvent clipNode)
{
return new DurationalCueClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = clipNode.durationFrame,
cueAssetLocation = clipNode.cue == null ? "" : clipNode.cue.name
};
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this InstantCueMarkEvent clipNode)
{
InstantCueClipNode runtimeClipNode = new InstantCueClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = 0,
cueAssetLocations = new string[clipNode.cues.Count],
};
for (int i = 0; i < clipNode.cues.Count; ++i)
{
runtimeClipNode.cueAssetLocations[i] = clipNode.cues[i].name;
}
return runtimeClipNode;
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this ReleaseGameplayEffectMarkEvent clipNode)
{
InstantGEClipNode runtimeClipNode = new InstantGEClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = 0,
targetCatcher = clipNode.TargetCatcher,
linkGEAssets = new LinkGEAsset[clipNode.gameplayEffectAssets.Count],
};
for (int i = 0; i < clipNode.gameplayEffectAssets.Count; ++i)
{
runtimeClipNode.linkGEAssets[i] = new LinkGEAsset()
{
valueId = clipNode.gameplayEffectAssets[i].id,
linkAssetLocation = clipNode.gameplayEffectAssets[i].asset == null ? "" : clipNode.gameplayEffectAssets[i].asset.name,
};
}
return runtimeClipNode;
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this BuffGameplayEffectClipEvent clipNode)
{
DurationalGEClipNode runtimeClipNode = new DurationalGEClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = clipNode.durationFrame,
linkGEAsset = new LinkGEAsset
{
valueId = clipNode.gameplayEffectValueId,
linkAssetLocation = clipNode.gameplayEffect == null ? "" : clipNode.gameplayEffect.name,
}
};
return runtimeClipNode;
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this TaskMarkEvent clipNode, int index)
{
InstantTaskClipNode runtimeClipNode = new InstantTaskClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = 0,
instantTask = clipNode.instantTasks[index].CreateTask(null), // 仅做转换并序列化使用
};
return runtimeClipNode;
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this TaskClipEvent clipNode)
{
DurationalTaskClipNode runtimeClipNode = new DurationalTaskClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = clipNode.durationFrame,
ongoingTask = clipNode.ongoingTask.CreateTask(null),
};
return runtimeClipNode;
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this PassiveGameplayEffectClipEvent clipNode)
{
PassiveGEClipNode runtimeClipNode = new PassiveGEClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = clipNode.durationFrame,
linkGEAsset = new LinkGEAsset()
{
valueId = clipNode.gameplayEffectValueId,
linkAssetLocation = clipNode.gameplayEffect == null ? "" : clipNode.gameplayEffect.name,
}
};
return runtimeClipNode;
}
internal static RuntimeClipNode ConvertRuntimeClipNode(this PassiveTaskClipEvent clipNode)
{
PassiveTaskClipNode runtimeClipNode = new PassiveTaskClipNode
{
startFrame = clipNode.startFrame,
durationalFrame = clipNode.durationFrame,
passiveTask = clipNode.passiveTask.CreateTask(null),
};
return runtimeClipNode;
}
/// <summary>
/// 序列化TimelineAbilityAsset
/// </summary>
/// <param name="timelineAbilityAsset"></param>
/// <returns></returns>
public static byte[] ToBytes(this TimelineAbilityAsset timelineAbilityAsset)
{
Serializer serializer = new Serializer();
// 序列化TimlineAbilityAsset的Ability属性配置
serializer.Write(timelineAbilityAsset.Name);
serializer.Write(timelineAbilityAsset.UniqueName);
serializer.Write(timelineAbilityAsset.SkillId);
serializer.Write(timelineAbilityAsset.Cost == null ? "" : timelineAbilityAsset.Cost.Name);
serializer.Write(timelineAbilityAsset.CooldownTime);
serializer.WriteArray(timelineAbilityAsset.AssetTags);
serializer.WriteArray(timelineAbilityAsset.CancelAbilityTags);
serializer.WriteArray(timelineAbilityAsset.BlockAbilityTags);
serializer.WriteArray(timelineAbilityAsset.ActivationOwnedTags);
serializer.WriteArray(timelineAbilityAsset.ActivationRequiredTags);
serializer.WriteArray(timelineAbilityAsset.ActivationBlockedTags);
// Ability的Timeline执行规则部分转化为TimelineInfo
TimelineInfo timelineInfo = Convert2TimelineInfo(timelineAbilityAsset);
timelineInfo.SerializeForEditor(serializer);
return serializer.CopyData();
}
/// <summary>
/// TimelineAbilityAsset的Timeline部分数据转换为TimelineInfo格式
/// </summary>
/// <param name="timelineAbilityAsset"></param>
/// <returns></returns>
private static TimelineInfo Convert2TimelineInfo(TimelineAbilityAsset timelineAbilityAsset)
{
TimelineInfo timelineInfo = new TimelineInfo
{
endManual = timelineAbilityAsset.ManualEndAbility,
totalFrame = timelineAbilityAsset.FrameCount
};
List<RuntimeClipNode> tempNodes = new List<RuntimeClipNode>();
// InstantGE
foreach (var trackData in timelineAbilityAsset.ReleaseGameplayEffect)
{
foreach (var instantGEClipEvent in trackData.markEvents)
{
tempNodes.Add(instantGEClipEvent.ConvertRuntimeClipNode());
}
}
// DurationalGE
foreach (var trackData in timelineAbilityAsset.BuffGameplayEffects)
{
foreach (var durationalGEClipEvent in trackData.clipEvents)
{
tempNodes.Add(durationalGEClipEvent.ConvertRuntimeClipNode());
}
}
// InstantTask
foreach (var trackData in timelineAbilityAsset.InstantTasks)
{
foreach (var instantTaskClipEvent in trackData.markEvents)
{
int length = instantTaskClipEvent.instantTasks == null ? 0 : instantTaskClipEvent.instantTasks.Count;
for (int i = 0; i < length; ++i)
{
tempNodes.Add(instantTaskClipEvent.ConvertRuntimeClipNode(i));
}
}
}
// DurationalTask
foreach (var trackData in timelineAbilityAsset.OngoingTasks)
{
foreach (var durationalTaskClipEvent in trackData.clipEvents)
{
tempNodes.Add(durationalTaskClipEvent.ConvertRuntimeClipNode());
}
}
// PassiveGE
foreach (var trackData in timelineAbilityAsset.PassiveGameplayEffects)
{
foreach (var passiveGEClipEvent in trackData.clipEvents)
{
tempNodes.Add(passiveGEClipEvent.ConvertRuntimeClipNode());
}
}
// PassvieTask
foreach (var trackData in timelineAbilityAsset.PassiveTasks)
{
foreach (var passiveTaskClipEvent in trackData.clipEvents)
{
tempNodes.Add(passiveTaskClipEvent.ConvertRuntimeClipNode());
}
}
// InstantCue
foreach (var trackData in timelineAbilityAsset.InstantCues)
{
foreach (var instantCueClipEvent in trackData.markEvents)
{
tempNodes.Add(instantCueClipEvent.ConvertRuntimeClipNode());
}
}
// DurationalCue
foreach (var trackData in timelineAbilityAsset.DurationalCues)
{
foreach (var durationalCueClipEvent in trackData.clipEvents)
{
tempNodes.Add(durationalCueClipEvent.ConvertRuntimeClipNode());
}
}
// 按起始触发时间排序
tempNodes.Sort((lhs, rhs) => lhs.startFrame.CompareTo(rhs.startFrame));
timelineInfo.runtimeClipNodes = new RuntimeClipNode[tempNodes.Count];
for (int i = 0; i < tempNodes.Count; ++i)
{
timelineInfo.runtimeClipNodes[i] = tempNodes[i];
}
return timelineInfo;
}
}
public static class AbilityAssetTool
{
/// <summary>
/// PureTimelineAbilityAsset数据序列化为字节数据的工具函数
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public static byte[] Serialize(PureTimelineAbilityAsset abilityAsset)
{
var writer = new Serializer();
abilityAsset.SerializeForEditor(writer);
return writer.CopyData();
}
/// <summary>
/// 字节数据反序列化为PureTimelineAbilityAsset的工具函数
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static PureTimelineAbilityAsset Deserialize(byte[] bytes)
{
var reader = new Deserializer(bytes);
var abilityAsset = new PureTimelineAbilityAsset();
abilityAsset.DeserializeForEditor(reader);
return abilityAsset;
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e8ef92065c5ad0445827c5835accf697
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -29,8 +29,6 @@ namespace GAS.Editor
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine("using System;"); writer.WriteLine("using System;");
writer.WriteLine("using System.Linq;");
writer.WriteLine("using UnityEngine;");
writer.WriteLine(""); writer.WriteLine("");
@ -76,31 +74,14 @@ namespace GAS.Editor
writer.WriteLine(""); writer.WriteLine("");
writer.WriteLine( writer.WriteLine(
"public static void InitWithPreset(this AbilitySystemComponent asc, int level, AbilitySystemComponentPreset preset = null)"); "public static void InitWithPreset(this AbilitySystemComponent asc, int entityId, int level, AbilitySystemComponentPreset preset = null)");
writer.WriteLine("{"); writer.WriteLine("{");
writer.Indent++; writer.Indent++;
{ {
writer.WriteLine("if (preset != null) asc.SetPreset(preset);"); writer.WriteLine("if (preset != null) asc.SetPreset(preset);");
writer.WriteLine("if (asc.Preset == null) return;"); 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( writer.WriteLine(
"Debug.LogWarning($\"BaseAbilities contains null in preset: {asc.Preset.name}\");"); "asc.Init(asc.PresetBaseTags(), asc.PresetAttributeSetTypes(), asc.Preset.BaseAbilities, entityId, level);");
}
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.Indent--;
writer.WriteLine("}"); writer.WriteLine("}");

View File

@ -3,9 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using GAS.Editor.General; using GAS.Editor.General;
using GAS.General.Validation; using GAS.Editor.General.Validation;
using GAS.Runtime; using GAS.Runtime;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor; using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities; using Sirenix.Utilities;
using Sirenix.Utilities.Editor; using Sirenix.Utilities.Editor;
@ -50,27 +49,13 @@ namespace GAS.Editor
"D- Ability System Component" "D- Ability System Component"
}; };
private const string OpenWindow_MenuItemName = "EX-GAS/Asset Aggregator"; [MenuItem("EX-GAS/Asset Aggregator", priority = 1)]
#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() private static void OpenWindow()
{ {
CheckLibPaths(); CheckLibPaths();
var window = GetWindow<GASAssetAggregator>(); var window = GetWindow<GASAssetAggregator>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1600, 900); window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1250, 625);
window.MenuWidth = 240; window.MenuWidth = 220;
}
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() private static void CheckLibPaths()
@ -141,10 +126,6 @@ namespace GAS.Editor
var directoryInfo = selected.Value is AbilityOverview var directoryInfo = selected.Value is AbilityOverview
? _directoryInfos[3] ? _directoryInfos[3]
: selected.Value as DirectoryInfo; : selected.Value as DirectoryInfo;
if (directoryInfo == null)
{
return;
}
if (SirenixEditorGUI.ToolbarButton(new GUIContent("浏览"))) if (SirenixEditorGUI.ToolbarButton(new GUIContent("浏览")))
{ {
@ -154,13 +135,15 @@ namespace GAS.Editor
if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建子文件夹"))) if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建子文件夹")))
{ {
CreateNewSubDirectory(directoryInfo); CreateNewSubDirectory(directoryInfo);
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first." GUIUtility
.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
} }
if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建"))) if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建")))
{ {
CreateNewAsset(directoryInfo); CreateNewAsset(directoryInfo);
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first." GUIUtility
.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
} }
if (!directoryInfo.Root) if (!directoryInfo.Root)
@ -168,7 +151,8 @@ namespace GAS.Editor
if (SirenixEditorGUI.ToolbarButton(new GUIContent("删除"))) if (SirenixEditorGUI.ToolbarButton(new GUIContent("删除")))
{ {
RemoveSubDirectory(directoryInfo); RemoveSubDirectory(directoryInfo);
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first." GUIUtility
.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
} }
} }
} }
@ -247,8 +231,12 @@ namespace GAS.Editor
s => s =>
{ {
var newPath = directoryInfo.Directory + "/" + s; var newPath = directoryInfo.Directory + "/" + s;
var isExist = AssetDatabase.IsValidFolder(newPath); if (AssetDatabase.IsValidFolder(newPath))
return isExist ? ValidationResult.Invalid("Folder already exists!") : ValidationResult.Valid; {
return ValidationResult.Invalid("Folder already exists!");
}
return ValidationResult.Valid;
}, },
s => s =>
{ {
@ -294,22 +282,13 @@ namespace GAS.Editor
private void RemoveAsset(ScriptableObject asset) private void RemoveAsset(ScriptableObject asset)
{ {
if (asset == null) if (!EditorUtility.DisplayDialog("Warning", "Are you sure you want to delete this asset?", "Yes",
{ "No")) return;
EditorUtility.DisplayDialog("Warning", "The asset you want to delete is null", "Ok");
return;
}
var assetName = asset.name; // Get the name before deleting var name = asset.name; // Get the name before deleting
var assetPath = AssetDatabase.GetAssetPath(asset); AssetDatabase.DeleteAsset(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(); Refresh();
Debug.Log($"[EX] delete asset: Name=\"{assetName}\", Path=\"{assetPath}\""); Debug.Log($"[EX] {name} asset deleted!");
}
} }
void OnMenuSelectionChange(SelectionChangedType selectionChangedType) void OnMenuSelectionChange(SelectionChangedType selectionChangedType)

View File

@ -0,0 +1,178 @@
using JNGame.Serialization;
#if UNITY_EDITOR
namespace GAS.Editor
{
using Runtime;
using UnityEditor;
using System;
using System.IO;
using GAS;
using Editor;
using UnityEngine;
public class GASGen
{
[MenuItem("EX-GAS/GenAllLib", priority = 4)]
public static void GenAllLib()
{
AbilityCollectionGenerator.Gen();
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
// TODO 拆分到独立面板 暂时先放这里使用
var ascFilePath = $"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ASCPRESET_LIB_CSHARP_SCRIPT_NAME}";
GenerateASCPrsetCollection(ascFilePath);
// TODO 拆分到独立面板 暂时先放这里使用
var geFilePath = $"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_GAMEPLAYEFFECT_LIB_CSHARP_SCRIPT_NAME}";
GenerateGECollection(geFilePath);
}
private static void GenerateASCPrsetCollection(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.Collections.Generic;");
writer.WriteLine("");
writer.WriteLine("namespace GAS.Runtime");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public static class GASCPresetLib");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public struct ASCPresetInfo");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public string Name;");
writer.WriteLine("public string AssetPath;");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
var abilityAssets = EditorUtil.FindAssetsByType<AbilitySystemComponentPreset>(GASSettingAsset.ASCLibPath);
foreach (var ability in abilityAssets)
{
var path = AssetDatabase.GetAssetPath(ability);
writer.WriteLine(
$"public static ASCPresetInfo {ability.name} = " +
$"new ASCPresetInfo {{ " +
$"Name = \"{ability.name}\", " +
$"AssetPath = \"{path}\"}};");
writer.WriteLine("");
}
writer.WriteLine("");
writer.WriteLine(
"public static Dictionary<string, ASCPresetInfo> ASCPresetMap = new Dictionary<string, ASCPresetInfo>");
writer.WriteLine("{");
writer.Indent++;
{
foreach (var ability in abilityAssets)
{
writer.WriteLine($"[\"{ability.name}\"] = {ability.name},");
}
}
writer.Indent--;
writer.WriteLine("};");
}
writer.Indent--;
writer.WriteLine("}");
}
writer.Indent--;
writer.Write("}");
Console.WriteLine($"Generated GASCPresetLib at path: {filePath}");
Debug.Log("GenerateASCPrsetCollection Finish");
}
private static void GenerateGECollection(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.Collections.Generic;");
writer.WriteLine("");
writer.WriteLine("namespace GAS.Runtime");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public static class GGameplayEffectLib");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public struct GameplayEffectAssetInfo");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public string Name;");
writer.WriteLine("public string AssetPath;");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
var abilityAssets = EditorUtil.FindAssetsByType<GameplayEffectAsset>(GASSettingAsset.GameplayEffectLibPath);
foreach (var ability in abilityAssets)
{
var path = AssetDatabase.GetAssetPath(ability);
writer.WriteLine(
$"public static GameplayEffectAssetInfo {ability.name} = " +
$"new GameplayEffectAssetInfo {{ " +
$"Name = \"{ability.name}\", " +
$"AssetPath = \"{path}\"}};");
writer.WriteLine("");
}
writer.WriteLine("");
writer.WriteLine(
"public static Dictionary<string, GameplayEffectAssetInfo> GameplayEffectMap = new Dictionary<string, GameplayEffectAssetInfo>");
writer.WriteLine("{");
writer.Indent++;
{
foreach (var ability in abilityAssets)
{
writer.WriteLine($"[\"{ability.name}\"] = {ability.name},");
}
}
writer.Indent--;
writer.WriteLine("};");
}
writer.Indent--;
writer.WriteLine("}");
}
writer.Indent--;
writer.Write("}");
Console.WriteLine($"Generated GameplayEffectLib at path: {filePath}");
Debug.Log("GenerateGECollection Finish");
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6d416e8b67924353b9ca08561ab6923e
timeCreated: 1719801078

View File

@ -1,10 +1,11 @@
using Sirenix.OdinInspector.Editor; #if UNITY_EDITOR
namespace GAS.Editor
{
using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities; using Sirenix.Utilities;
using Sirenix.Utilities.Editor; using Sirenix.Utilities.Editor;
using UnityEditor; using UnityEditor;
namespace GAS.Editor
{
public class GASSettingAggregator : OdinMenuEditorWindow public class GASSettingAggregator : OdinMenuEditorWindow
{ {
private static GASSettingAsset _settingAsset; private static GASSettingAsset _settingAsset;
@ -51,17 +52,11 @@ namespace GAS.Editor
} }
} }
private const string OpenWindow_MenuItemName = "EX-GAS/Settings"; [MenuItem("EX-GAS/Settings", priority = 0)]
#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() public static void OpenWindow()
{ {
var window = GetWindow<GASSettingAggregator>(); var window = GetWindow<GASSettingAggregator>();
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1200, 600); window.position = GUIHelper.GetEditorWindowRect().AlignCenter(900, 600);
} }
protected override OdinMenuTree BuildMenuTree() protected override OdinMenuTree BuildMenuTree()
@ -87,3 +82,4 @@ namespace GAS.Editor
} }
} }
} }
#endif

View File

@ -1,19 +1,21 @@
using System; using System;
#if UNITY_EDITOR
namespace GAS.Editor
{
using System.Collections.Generic;
using System.IO; using System.IO;
using GAS;
using GAS.General; using GAS.General;
using Sirenix.OdinInspector; using Sirenix.OdinInspector;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
namespace GAS.Editor
{
[FilePath(GasDefine.GAS_BASE_SETTING_PATH)] [FilePath(GasDefine.GAS_BASE_SETTING_PATH)]
public class GASSettingAsset : ScriptableSingleton<GASSettingAsset> public class GASSettingAsset : ScriptableSingleton<GASSettingAsset>
{ {
private const int LABEL_WIDTH = 200; private const int LABEL_WIDTH = 200;
private const int SHORT_LABEL_WIDTH = 200; private const int SHORT_LABEL_WIDTH = 200;
private static GASSettingAsset _setting;
[Title(GASTextDefine.TITLE_SETTING,Bold = true)] [Title(GASTextDefine.TITLE_SETTING,Bold = true)]
[BoxGroup("A", false,order:1)] [BoxGroup("A", false,order:1)]
@ -30,24 +32,35 @@ namespace GAS.Editor
[OnValueChanged("SaveAsset")] [OnValueChanged("SaveAsset")]
public string GASConfigAssetPath = "Assets/GAS/Config"; public string GASConfigAssetPath = "Assets/GAS/Config";
public static GASSettingAsset Setting [BoxGroup("A")]
{ [LabelText("二进制配置文件生成路径")]
get [LabelWidth(LABEL_WIDTH)]
{ [FolderPath]
if (_setting == null) _setting = LoadOrCreate(); [OnValueChanged("SaveAsset")]
return _setting; public string GASBinaryAssetPath = "Assets/GAS/Binary";
}
} [BoxGroup("A")]
[LabelText("业务脚本生成路径")]
[LabelWidth(LABEL_WIDTH)]
[FolderPath]
[OnValueChanged("SaveAsset")]
public string LogicCodeGeneratePath = "Assets/Scripts/Gen";
[BoxGroup("A")]
[LabelText("业务脚本Assembly")]
[LabelWidth(LABEL_WIDTH)]
public List<string> LogicCodeGenerateAssemblies = new List<string>();
public static GASSettingAsset Setting => Instance;
[ShowInInspector] [ShowInInspector]
[BoxGroup("V",false,order:0)] [BoxGroup("V",false,order:0)]
[HideLabel] [HideLabel][DisplayAsString(TextAlignment.Left,true)]
[DisplayAsString(TextAlignment.Left, true)] private static string Version => $"<size=15><b><color=white>EX-GAS Version: {GasDefine.GAS_VERSION}</color></b></size>";
private static string Version =>
$"<size=15><b><color=white>EX-GAS Version: {GasDefine.GAS_VERSION}</color></b></size>";
public static string CodeGenPath => Setting.CodeGeneratePath; public static string CodeGenPath => Setting.CodeGeneratePath;
public static string LogicCodeGenPath => Setting.LogicCodeGeneratePath;
[Title(GASTextDefine.TITLE_PATHS,Bold = true)] [Title(GASTextDefine.TITLE_PATHS,Bold = true)]
[PropertySpace(10)] [PropertySpace(10)]
@ -87,8 +100,7 @@ namespace GAS.Editor
[BoxGroup("A")] [BoxGroup("A")]
[DisplayAsString(TextAlignment.Left,true)] [DisplayAsString(TextAlignment.Left,true)]
[LabelWidth(SHORT_LABEL_WIDTH)] [LabelWidth(SHORT_LABEL_WIDTH)]
public static string AbilityTaskLib => public static string AbilityTaskLib => $"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ABILITY_TASK_LIBRARY_FOLDER}";
$"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ABILITY_TASK_LIBRARY_FOLDER}";
[ShowInInspector] [ShowInInspector]
[BoxGroup("A")] [BoxGroup("A")]
@ -154,6 +166,7 @@ namespace GAS.Editor
CheckPathFolderExist(GameplayCueLibPath); CheckPathFolderExist(GameplayCueLibPath);
CheckPathFolderExist(MMCLibPath); CheckPathFolderExist(MMCLibPath);
CheckPathFolderExist(AbilityTaskLib); CheckPathFolderExist(AbilityTaskLib);
CheckPathFolderExist(LogicCodeGeneratePath);
AssetDatabase.Refresh(); AssetDatabase.Refresh();
} }
@ -167,7 +180,7 @@ namespace GAS.Editor
{ {
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6); string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
var filePath = var filePath =
$"{pathWithoutAssets}/{CodeGenPath}/{GasDefine.GAS_ATTRIBUTESET_LIB_CSHARP_SCRIPT_NAME}"; $"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ATTRIBUTESET_LIB_CSHARP_SCRIPT_NAME}";
if (!File.Exists(filePath)) if (!File.Exists(filePath))
{ {
@ -185,36 +198,6 @@ namespace GAS.Editor
UpdateAsset(this); UpdateAsset(this);
Save(); 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 #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

@ -1,4 +1,8 @@
#if UNITY_EDITOR using System;
using JNGame.Game;
using JNGame.GAS;
#if UNITY_EDITOR
namespace GAS.Editor namespace GAS.Editor
{ {
using System.Collections.Generic; using System.Collections.Generic;
@ -24,13 +28,10 @@ namespace GAS.Editor
private AbilitySystemComponent _selected; private AbilitySystemComponent _selected;
[HideLabel] [HideLabel] [DisplayAsString(TextAlignment.Center, true)]
[DisplayAsString(TextAlignment.Center, true)]
public string windowTitle = "<size=18><b>EX Gameplay Ability System Watcher</b></size>"; public string windowTitle = "<size=18><b>EX Gameplay Ability System Watcher</b></size>";
[BoxGroup(BOXGROUP_TIPS)] [BoxGroup(BOXGROUP_TIPS)] [HideLabel] [DisplayAsString(TextAlignment.Left, true)]
[HideLabel]
[DisplayAsString(TextAlignment.Left, true)]
public string tips = GASTextDefine.TIP_WATCHER; public string tips = GASTextDefine.TIP_WATCHER;
[BoxGroup(BOXGROUP_TIPS_RUNNINGTIP, false)] [BoxGroup(BOXGROUP_TIPS_RUNNINGTIP, false)]
@ -49,6 +50,16 @@ namespace GAS.Editor
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
public string Navis = "NAVI"; 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("Object Mark", bold: true)]
[DisplayAsString]
[LabelWidth(75)]
[ShowIf("IsPlaying")]
public int ASC_Number;
[HorizontalGroup(BOXGROUP_ASC_H)] [HorizontalGroup(BOXGROUP_ASC_H)]
[BoxGroup(BOXGROUP_ASC_H_R, false)] [BoxGroup(BOXGROUP_ASC_H_R, false)]
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)] [HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
@ -59,17 +70,21 @@ namespace GAS.Editor
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
public int IID; public int IID;
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)] [VerticalGroup(BOXGROUP_ASC_H_R_A_V)] [ReadOnly] [LabelWidth(75)] [ShowIf("IsPlaying")]
[ReadOnly]
[LabelWidth(75)]
[ShowIf("IsPlaying")]
public GameObject instance; public GameObject instance;
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)] [DisplayAsString] [LabelWidth(75)] [ShowIf("IsPlaying")]
public int Level;
[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)] [VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
[Title("TargetID Mark", bold: true)]
[DisplayAsString] [DisplayAsString]
[LabelWidth(75)] [LabelWidth(75)]
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
public int Level; public int TargetIID;
[Space] [Space]
[Title("Abilities", bold: true)] [Title("Abilities", bold: true)]
@ -80,7 +95,18 @@ namespace GAS.Editor
[LabelText(" ")] [LabelText(" ")]
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
[Searchable] [Searchable]
public List<string> Abilities = new List<string>(); public List<string> Abilities = new();
[Space]
[Title("局外属性", 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> OutsideAttributes = new();
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)] [HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_VB)] [VerticalGroup(BOXGROUP_ASC_H_R_A_VB)]
@ -91,8 +117,17 @@ namespace GAS.Editor
[LabelText(" ")] [LabelText(" ")]
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
[Searchable] [Searchable]
public List<string> Attributes = new List<string>(); public List<string> Attributes = new();
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
[Title("最终属性", bold: true)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true,
ShowPaging = false)]
[DisplayAsString]
[LabelText(" ")]
[ShowIf("IsPlaying")]
[Searchable]
public List<string> FinalAttributes = new();
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)] [HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
[Title("GameplayEffects", bold: true)] [Title("GameplayEffects", bold: true)]
@ -103,7 +138,7 @@ namespace GAS.Editor
[InfoBox("format: [ActiveState][DurationInfo]GeName", InfoMessageType.None, "IsPlaying")] [InfoBox("format: [ActiveState][DurationInfo]GeName", InfoMessageType.None, "IsPlaying")]
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
[Searchable] [Searchable]
public List<string> Effects = new List<string>(); public List<string> Effects = new();
[HorizontalGroup(BOXGROUP_ASC_H_R_A)] [HorizontalGroup(BOXGROUP_ASC_H_R_A)]
@ -114,7 +149,7 @@ namespace GAS.Editor
[DisplayAsString] [DisplayAsString]
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
[Searchable] [Searchable]
public List<string> FixedTag = new List<string>(); public List<string> FixedTag = new();
[Title(" ", bold: true)] [Title(" ", bold: true)]
[VerticalGroup(BOXGROUP_ASC_H_R_A_VC)] [VerticalGroup(BOXGROUP_ASC_H_R_A_VC)]
@ -123,7 +158,16 @@ namespace GAS.Editor
[DisplayAsString] [DisplayAsString]
[ShowIf("IsPlaying")] [ShowIf("IsPlaying")]
[Searchable] [Searchable]
public List<string> DynamicTag = new List<string>(); public List<string> DynamicTag = new();
[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> DamageRecorder = new();
private Vector2 menuScrollPos; private Vector2 menuScrollPos;
@ -134,25 +178,17 @@ namespace GAS.Editor
{ {
if (IsPlaying) if (IsPlaying)
{ {
if (_selected == null || _selected.Id == 0) if (_selected == null || _selected.EntityId == 0)
{ _selected = GameplayAbilitySystem.GAS.AbilitySystemComponents.Count > 0
_selected = JexGasManager.Editor.AbilitySystemComponents.Count > 0 ? GameplayAbilitySystem.GAS.AbilitySystemComponents[0] as AbilitySystemComponent
? JexGasManager.Editor.AbilitySystemComponents[0] as AbilitySystemComponent
: null; : null;
}
RefreshAscInfo(); RefreshAscInfo();
Repaint(); Repaint();
} }
} }
private const string OpenWindow_MenuItemName = "EX-GAS/Runtime Watcher"; [MenuItem("EX-GAS/Runtime Watcher", priority = 3)]
#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() private static void OpenWindow()
{ {
var window = GetWindow<GASWatcher>(); var window = GetWindow<GASWatcher>();
@ -160,16 +196,16 @@ namespace GAS.Editor
window.Show(); window.Show();
} }
void OnDrawNavi() private void OnDrawNavi()
{ {
if (!IsPlaying) return; if (!IsPlaying) return;
menuScrollPos = EditorGUILayout.BeginScrollView(menuScrollPos, GUI.skin.box); menuScrollPos = EditorGUILayout.BeginScrollView(menuScrollPos, GUI.skin.box);
foreach (var iasc in JexGasManager.Editor.AbilitySystemComponents) foreach (var iasc in GameplayAbilitySystem.GAS.AbilitySystemComponents)
{ {
var asc = (AbilitySystemComponent)iasc; var asc = (AbilitySystemComponent)iasc;
var presetName = asc.Preset != null ? asc.Preset.name : "NoPreset"; var presetName = asc.Preset != null ? asc.Preset.Name : "NoPreset";
if (GUILayout.Button($"{presetName}#{asc.Id}")) if (GUILayout.Button($"{presetName}#{asc.EntityId}"))
{ {
_selected = asc; _selected = asc;
RefreshAscInfo(); RefreshAscInfo();
@ -183,44 +219,57 @@ namespace GAS.Editor
{ {
if (_selected == null) if (_selected == null)
{ {
ASC_Number = -1;
IID = 0; IID = 0;
instance = null; instance = null;
Level = 0; Level = 0;
TargetIID = 0;
Abilities.Clear(); Abilities.Clear();
OutsideAttributes.Clear();
Attributes.Clear(); Attributes.Clear();
FinalAttributes.Clear();
Effects.Clear(); Effects.Clear();
FixedTag.Clear(); FixedTag.Clear();
DynamicTag.Clear(); DynamicTag.Clear();
DamageRecorder.Clear();
return; return;
} }
IID = _selected.Id; ASC_Number = _selected.SelfNumber;
// instance = _selected.GetView().gameObject; IID = _selected.EntityId;
instance = GameObject.Find(_selected.EntityName);
Level = _selected.Level; Level = _selected.Level;
RefreshAbilityInfo(); RefreshAbilityInfo();
RefreshAttributesInfo(); RefreshAttributesInfo();
RefreshGameplayEffectsInfo(); RefreshGameplayEffectsInfo();
RefreshTagsInfo(); RefreshTagsInfo();
RefreshDamageInfo();
} }
private void RefreshGameplayEffectsInfo() private void RefreshGameplayEffectsInfo()
{ {
Effects.Clear(); Effects.Clear();
foreach (var ge in _selected.GameplayEffectContainer.GameplayEffects()) var list = _selected.GameplayEffectContainer.FetchGameplayEffects();
foreach (var ge in list)
{ {
string isActive = ge.IsActive ? "√" : "×"; var isActive = ge.IsActive ? "√" : "×";
string durationStr = ge.DurationPolicy switch var durationStr = ge.DurationPolicy switch
{ {
EffectsDurationPolicy.Duration => $"{ge.DurationRemaining():N2}/{ge.Duration:N2}(s)", EffectsDurationPolicy.Duration => $"{ge.DurationRemaining():N2}/{ge.Duration:N2}(s)",
EffectsDurationPolicy.Infinite => "∞", EffectsDurationPolicy.Infinite => "∞",
EffectsDurationPolicy.Instant => "N/A", EffectsDurationPolicy.Instant => "N/A",
_ => "Unknown" _ => "Unknown"
}; };
var stackCountText = ge.Stacking.stackingType != StackingType.None ? $"[S:{ge.StackCount}]" : ""; var stackCountText = ge.Stacking.stackingType != EnumStackingType.None ? $"[S:{ge.StackCount}]" : "";
Effects.Add($"[{isActive}][{durationStr}]{stackCountText}{ge.GameplayEffect.GameplayEffectName}"); Effects.Add($"[{isActive}][{durationStr}]{stackCountText}{ge.GameplayEffect.GameplayEffectName}");
} }
_selected.GameplayEffectContainer.ReturnGameplayEffects(list);
} }
private void RefreshAbilityInfo() private void RefreshAbilityInfo()
@ -228,7 +277,7 @@ namespace GAS.Editor
Abilities.Clear(); Abilities.Clear();
foreach (var ability in _selected.AbilityContainer.AbilitySpecs()) foreach (var ability in _selected.AbilityContainer.AbilitySpecs())
{ {
string isActive = ability.Value.IsActive ? "(Active)" : ""; var isActive = ability.Value.IsActive ? "(Active)" : "";
Abilities.Add($"{ability.Key} | Lv.{ability.Value.Level} {isActive}"); Abilities.Add($"{ability.Key} | Lv.{ability.Value.Level} {isActive}");
} }
} }
@ -243,7 +292,7 @@ namespace GAS.Editor
{ {
var attr = attributeSet[attributeName]; var attr = attributeSet[attributeName];
Attributes.Add( Attributes.Add(
$" - {attributeName} = {attr.CurrentValue:N2}({attr.BaseValue:N2} + {attr.CurrentValue - attr.BaseValue:N2})"); $" - {attributeName} = {attr.CurrentValue}({attr.BaseValue} + {attr.CurrentValue - attr.BaseValue})");
} }
} }
} }
@ -254,16 +303,13 @@ namespace GAS.Editor
RefreshDynamicTagsInfo(); RefreshDynamicTagsInfo();
} }
void RefreshFixedTagsInfo() private void RefreshFixedTagsInfo()
{ {
FixedTag.Clear(); FixedTag.Clear();
foreach (var tag in _selected.GameplayTagAggregator.FixedTags) foreach (var tag in _selected.GameplayTagAggregator.FixedTags) FixedTag.Add(tag.Name);
{
FixedTag.Add(tag.Name);
}
} }
void RefreshDynamicTagsInfo() private void RefreshDynamicTagsInfo()
{ {
DynamicTag.Clear(); DynamicTag.Clear();
foreach (var kv in _selected.GameplayTagAggregator.DynamicAddedTags) foreach (var kv in _selected.GameplayTagAggregator.DynamicAddedTags)
@ -272,24 +318,36 @@ namespace GAS.Editor
DynamicTag.Add($"{tagName} ↓ "); DynamicTag.Add($"{tagName} ↓ ");
foreach (var obj in kv.Value) foreach (var obj in kv.Value)
{
switch (obj) switch (obj)
{ {
case GameplayEffectSpec spec: case GameplayEffectSpec spec:
{ {
DynamicTag.Add( DynamicTag.Add(
$" - From: {spec.Owner.Id}'s GE: {spec.GameplayEffect.GameplayEffectName}"); $" - From: {spec.Owner.EntityId}'s GE: {spec.GameplayEffect.GameplayEffectName}");
break; break;
} }
case AbilitySpec ability: case AbilitySpec ability:
{ {
DynamicTag.Add( DynamicTag.Add(
$" - From: {ability.Owner.Id}'s Ability: {ability.Ability.Name}"); $" - From: {ability.Owner.EntityId}'s Ability: {ability.Ability.Name}");
break; break;
} }
} }
} }
} }
private void RefreshDamageInfo()
{
DamageRecorder.Clear();
foreach (var kv in _selected.DamageList)
{
DamageRecorder.Add($"--对 {kv.asc.EntityId}@造成了{kv.atkValue}伤害");
DamageRecorder.Add($" 伤害来源 {kv.skillId}");
DamageRecorder.Add($" @{kv.damageType.Name}@{kv.other.ToString()}");
DamageRecorder.Add($" @闪避率:{kv.missRate}@暴击率{kv.criRate}");
DamageRecorder.Add($" @是否双倍{kv.isDouble}@双倍概率{kv.doubleRate}");
DamageRecorder.Add($" 当前时间戳{kv.time}");
}
} }
} }
} }

View File

@ -0,0 +1,201 @@
#if UNITY_EDITOR
using GAS.Runtime;
using JNGame.Serialization;
namespace GAS.Editor
{
public static class GameplayEffectConverter
{
/// <summary>
/// 序列化GEAsset配置
/// </summary>
/// <param name="effectAsset"></param>
/// <returns></returns>
public static byte[] ToBytes(this GameplayEffectAsset effectAsset)
{
Serializer serializer = new Serializer();
// 策略相关字段
serializer.Write(effectAsset.Name);
serializer.Write((byte)effectAsset.DurationPolicy);
serializer.Write(effectAsset.Duration);
serializer.Write(effectAsset.Period);
serializer.Write(effectAsset.PeriodExecutionId);
serializer.Write(effectAsset.PeriodExcutionAsset == null ? "" : effectAsset.PeriodExcutionAsset.Name);
// 堆叠规则相关字段
serializer.Write((byte)effectAsset.stacking.stackingType);
serializer.Write(effectAsset.stacking.stackingCodeName);
serializer.Write(effectAsset.stacking.limitCount);
serializer.Write((byte)effectAsset.stacking.durationRefreshPolicy);
serializer.Write((byte)effectAsset.stacking.periodResetPolicy);
serializer.Write((byte)effectAsset.stacking.expirationPolicy);
serializer.Write(effectAsset.stacking.denyOverflowApplication);
serializer.Write(effectAsset.stacking.clearStackOnOverflow);
ushort length = (ushort)(effectAsset.stacking.overflowEffects == null ? 0 : effectAsset.stacking.overflowEffects.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
serializer.Write(effectAsset.stacking.overflowEffects[i].id);
serializer.Write(effectAsset.stacking.overflowEffects[i].asset == null ? "" : effectAsset.stacking.overflowEffects[i].asset.Name);
}
// 授予能力相关字段
length = (ushort)(effectAsset.grantedAbilities == null ? 0 : effectAsset.grantedAbilities.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
serializer.Write(effectAsset.grantedAbilities[i].abilityAsset == null ? "" : effectAsset.grantedAbilities[i].abilityAsset.Name);
serializer.Write(effectAsset.grantedAbilities[i].abilityLevel);
serializer.Write((byte)effectAsset.grantedAbilities[i].activationPolicy);
serializer.Write((byte)effectAsset.grantedAbilities[i].deactivationPolicy);
serializer.Write((byte)effectAsset.grantedAbilities[i].removePolicy);
}
// 属性修改器相关字段
length = (ushort)(effectAsset.modifiers == null ? 0 : effectAsset.modifiers.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
serializer.Write(effectAsset.modifiers[i].attributeName);
serializer.Write(effectAsset.modifiers[i].attributeShortName);
serializer.Write(effectAsset.modifiers[i].attributeSetName);
serializer.Write(effectAsset.modifiers[i].modiferMagnitude);
serializer.Write((byte)effectAsset.modifiers[i].operation);
serializer.Write(effectAsset.modifiers[i].mmc.TypeId);
effectAsset.modifiers[i].mmc.Serialize(serializer);
}
// Tags相关字段
serializer.WriteArray(effectAsset.assetTags);
serializer.WriteArray(effectAsset.grantedTags);
serializer.WriteArray(effectAsset.applicationRequiredTags);
serializer.WriteArray(effectAsset.ongoingRequiredTags);
serializer.WriteArray(effectAsset.removeGameplayEffectsWithTags);
serializer.WriteArray(effectAsset.applicationImmunityTags);
// Cue相关字段
length = (ushort)(effectAsset.cueOnExecute == null ? 0 : effectAsset.cueOnExecute.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
string cueAssetName = effectAsset.cueOnExecute[i] == null ? "" : effectAsset.cueOnExecute[i].name;
serializer.Write(cueAssetName);
}
length = (ushort)(effectAsset.cueDurational == null ? 0 : effectAsset.cueDurational.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
string cueAssetName = effectAsset.cueDurational[i] == null ? "" : effectAsset.cueDurational[i].name;
serializer.Write(cueAssetName);
}
length = (ushort)(effectAsset.cueOnAdd == null ? 0 : effectAsset.cueOnAdd.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
string cueAssetName = effectAsset.cueOnAdd[i] == null ? "" : effectAsset.cueOnAdd[i].name;
serializer.Write(cueAssetName);
}
length = (ushort)(effectAsset.cueOnRemove == null ? 0 : effectAsset.cueOnRemove.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
string cueAssetName = effectAsset.cueOnRemove[i] == null ? "" : effectAsset.cueOnRemove[i].name;
serializer.Write(cueAssetName);
}
length = (ushort)(effectAsset.cueOnActivate == null ? 0 : effectAsset.cueOnActivate.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
string cueAssetName = effectAsset.cueOnActivate[i] == null ? "" : effectAsset.cueOnActivate[i].name;
serializer.Write(cueAssetName);
}
length = (ushort)(effectAsset.cueOnDeactivate == null ? 0 : effectAsset.cueOnDeactivate.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
string cueAssetName = effectAsset.cueOnDeactivate[i] == null ? "" : effectAsset.cueOnDeactivate[i].name;
serializer.Write(cueAssetName);
}
// Expiration相关字段
length = (ushort)(effectAsset.prematureExpirationEffect == null ? 0 : effectAsset.prematureExpirationEffect.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
if (effectAsset.prematureExpirationEffect[i] == null)
{
serializer.Write(0);
serializer.Write("");
}
else
{
serializer.Write(effectAsset.prematureExpirationEffect[i].id);
serializer.Write(effectAsset.prematureExpirationEffect[i].asset == null ? "" : effectAsset.prematureExpirationEffect[i].asset.name);
}
}
length = (ushort)(effectAsset.routineExpirationEffectClasses == null ? 0 : effectAsset.routineExpirationEffectClasses.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
if (effectAsset.routineExpirationEffectClasses[i] == null)
{
serializer.Write(0);
serializer.Write("");
}
else
{
serializer.Write(effectAsset.routineExpirationEffectClasses[i].id);
serializer.Write(effectAsset.routineExpirationEffectClasses[i].asset == null ? "" : effectAsset.routineExpirationEffectClasses[i].asset.name);
}
}
// TickEvent相关字段
serializer.Write(effectAsset.IsUseTickEventGE);
if (effectAsset.IsUseTickEventGE)
{
effectAsset.tickEventTag.Serialize(serializer);
length = (ushort)(effectAsset.tickEventEffect == null ? 0 : effectAsset.tickEventEffect.Length);
serializer.Write(length);
for (int i = 0; i < length; ++i)
{
if (effectAsset.tickEventEffect[i] == null)
{
serializer.Write(0);
serializer.Write("");
}
else
{
serializer.Write(effectAsset.tickEventEffect[i].id);
serializer.Write(effectAsset.tickEventEffect[i].asset == null ? "" : effectAsset.tickEventEffect[i].asset.name);
}
}
}
return serializer.CopyData();
}
}
public static class GameEffectAssetSerializationTool
{
/// <summary>
/// PureGameplayEffectAsset数据序列化为字节数据的工具函数
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public static byte[] Serialize(PureGameplayEffectAsset geAsset)
{
var writer = new Serializer();
geAsset.Serialize(writer);
return writer.CopyData();
}
/// <summary>
/// 字节数据反序列化为PureGameplayEffectAsset的工具函数
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static PureGameplayEffectAsset Deserialize(byte[] bytes)
{
var reader = new Deserializer(bytes);
var geAsset = new PureGameplayEffectAsset();
geAsset.Deserialize(reader);
return geAsset;
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6fb990bd3236241498ff0493651b7bcf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -32,24 +32,13 @@ namespace GAS.Editor
_streamWriter.Write(GetIndentation() + text); _streamWriter.Write(GetIndentation() + text);
} }
public void WriteLine(string line = null, bool ignoreIndent = false) public void WriteLine(string line = null)
{ {
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
{
_streamWriter.WriteLine(); _streamWriter.WriteLine();
}
else else
{
if (ignoreIndent)
{
_streamWriter.WriteLine(line);
}
else
{
_streamWriter.WriteLine(GetIndentation() + line); _streamWriter.WriteLine(GetIndentation() + line);
} }
}
}
private bool _isDisposed; private bool _isDisposed;

View File

@ -1,52 +0,0 @@
using System.Linq;
using GAS.General.Validation;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor
{
public static class ScriptingDefineSymbolsHelper
{
public static void Add(string define)
{
if (!Validations.IsValidVariableName(define))
{
Debug.LogError($@"Add scripting define symbol error: ""{define}"" is not a valid variable name!");
return;
}
var group = EditorUserBuildSettings.selectedBuildTargetGroup;
var definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
var defines = definesString.Split(';');
if (defines.Contains(define))
{
Debug.Log($@"Add scripting define symbol failed: ""{define}"" already exists!");
return;
}
var newDefinesString = string.Join(";", defines.Append(define));
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, newDefinesString);
}
public static void Remove(string define)
{
if (string.IsNullOrWhiteSpace(define))
{
Debug.LogError($@"Remove scripting define symbol error: ""{define}"" is null or empty!");
return;
}
var group = EditorUserBuildSettings.selectedBuildTargetGroup;
var definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
var defines = definesString.Split(';');
if (!defines.Contains(define))
{
Debug.Log($@"Remove scripting define symbol failed: ""{define}"" does not exist!");
return;
}
var newDefinesString = string.Join(";", defines.Where(d => d != define));
PlayerSettings.SetScriptingDefineSymbolsForGroup(group, newDefinesString);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b9055976068442439bd3318d8b42a345
timeCreated: 1718347417

View File

@ -1,5 +1,5 @@
using System; using System;
using GAS.General.Validation; using GAS.Editor.General.Validation;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;

View File

@ -1,8 +1,3 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: b84e44e848a2cc54a9f86e88d1219512 guid: 8207bd0f7b514c9a9a29ef37d242fd70
folderAsset: yes timeCreated: 1729568975
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 69be32fe4d27dde489209c5885c1e5dc guid: 1fd36c55067263b4e8a57d66895368a6
timeCreated: 1472024155 timeCreated: 1472024155
licenseType: Pro licenseType: Pro
MonoImporter: MonoImporter:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: fd65e8f324e17a344a97ddcf5a8d89d2 guid: 57988073b2c6d944dbba8856bcae1b6d
timeCreated: 1471616285 timeCreated: 1471616285
licenseType: Pro licenseType: Pro
MonoImporter: MonoImporter:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 6f9fab1cf2636a6439c644bf08108abb guid: ddf437cbf1378614fb3fb17f4ba85426
timeCreated: 1472122507 timeCreated: 1472122507
licenseType: Pro licenseType: Pro
MonoImporter: MonoImporter:

View File

@ -0,0 +1,65 @@
#if UNITY_EDITOR
namespace UnityEditor.TreeDataModel
{
using System;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class TreeElement
{
[SerializeField] private int _id;
[SerializeField] private string _name;
[SerializeField] private int _depth;
[NonSerialized] private List<TreeElement> _children;
[NonSerialized] private TreeElement _parent;
public TreeElement()
{
}
public TreeElement(string name, int depth, int id)
{
_name = name;
_id = id;
_depth = depth;
}
public int Depth
{
get => _depth;
set => _depth = value;
}
public TreeElement Parent
{
get => _parent;
set => _parent = value;
}
public List<TreeElement> Children
{
get => _children;
set => _children = value;
}
public bool HasChildren => Children != null && Children.Count > 0;
public string Name
{
get => _name;
set => _name = value;
}
public int ID
{
get => _id;
set => _id = value;
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 69be32fe4d27dde489209c5885c1e5dc
timeCreated: 1472024155
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,171 @@
#if UNITY_EDITOR
namespace UnityEditor.TreeDataModel
{
using System;
using System.Collections.Generic;
// TreeElementUtility and TreeElement are useful helper classes for backend tree data structures.
// See tests at the bottom for examples of how to use.
public static class TreeElementUtility
{
public static void TreeToList<T>(T root, IList<T> result) where T : TreeElement
{
if (result == null)
throw new NullReferenceException("The input 'IList<T> result' list is null");
result.Clear();
Stack<T> stack = new Stack<T>();
stack.Push(root);
while (stack.Count > 0)
{
T current = stack.Pop();
result.Add(current);
if (current.Children != null && current.Children.Count > 0)
{
for (int i = current.Children.Count - 1; i >= 0; i--)
{
stack.Push((T)current.Children[i]);
}
}
}
}
// Returns the root of the tree parsed from the list (always the first element).
// Important: the first item and is required to have a depth value of -1.
// The rest of the items should have depth >= 0.
public static T ListToTree<T>(IList<T> list) where T : TreeElement
{
// Validate input
ValidateDepthValues (list);
// Clear old states
foreach (var element in list)
{
element.Parent = null;
element.Children = null;
}
// Set child and parent references using depth info
for (int parentIndex = 0; parentIndex < list.Count; parentIndex++)
{
var parent = list[parentIndex];
bool alreadyHasValidChildren = parent.Children != null;
if (alreadyHasValidChildren)
continue;
int parentDepth = parent.Depth;
int childCount = 0;
// Count children based depth value, we are looking at children until it's the same depth as this object
for (int i = parentIndex + 1; i < list.Count; i++)
{
if (list[i].Depth == parentDepth + 1)
childCount++;
if (list[i].Depth <= parentDepth)
break;
}
// Fill child array
List<TreeElement> childList = null;
if (childCount != 0)
{
childList = new List<TreeElement>(childCount); // Allocate once
childCount = 0;
for (int i = parentIndex + 1; i < list.Count; i++)
{
if (list[i].Depth == parentDepth + 1)
{
list[i].Parent = parent;
childList.Add(list[i]);
childCount++;
}
if (list[i].Depth <= parentDepth)
break;
}
}
parent.Children = childList;
}
return list[0];
}
// Check state of input list
public static void ValidateDepthValues<T>(IList<T> list) where T : TreeElement
{
if (list.Count == 0)
throw new ArgumentException("list should have items, count is 0, check before calling ValidateDepthValues", "list");
if (list[0].Depth != -1)
throw new ArgumentException("list item at index 0 should have a depth of -1 (since this should be the hidden root of the tree). Depth is: " + list[0].Depth, "list");
for (int i = 0; i < list.Count - 1; i++)
{
int depth = list[i].Depth;
int nextDepth = list[i + 1].Depth;
if (nextDepth > depth && nextDepth - depth > 1)
throw new ArgumentException(string.Format("Invalid depth info in input list. Depth cannot increase more than 1 per row. Index {0} has depth {1} while index {2} has depth {3}", i, depth, i + 1, nextDepth));
}
for (int i = 1; i < list.Count; ++i)
if (list[i].Depth < 0)
throw new ArgumentException("Invalid depth value for item at index " + i + ". Only the first item (the root) should have depth below 0.");
if (list.Count > 1 && list[1].Depth != 0)
throw new ArgumentException("Input list item at index 1 is assumed to have a depth of 0", "list");
}
// For updating depth values below any given element e.g after reparenting elements
public static void UpdateDepthValues<T>(T root) where T : TreeElement
{
if (root == null)
throw new ArgumentNullException("root", "The root is null");
if (!root.HasChildren)
return;
Stack<TreeElement> stack = new Stack<TreeElement>();
stack.Push(root);
while (stack.Count > 0)
{
TreeElement current = stack.Pop();
if (current.Children != null)
{
foreach (var child in current.Children)
{
child.Depth = current.Depth + 1;
stack.Push(child);
}
}
}
}
// Returns true if there is an ancestor of child in the elements list
static bool IsChildOf<T>(T child, IList<T> elements) where T : TreeElement
{
while (child != null)
{
child = (T)child.Parent;
if (elements.Contains(child))
return true;
}
return false;
}
public static IList<T> FindCommonAncestorsWithinList<T>(IList<T> elements) where T : TreeElement
{
if (elements.Count == 1)
return new List<T>(elements);
List<T> result = new List<T>(elements);
result.RemoveAll(g => IsChildOf(g, elements));
return result;
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fd65e8f324e17a344a97ddcf5a8d89d2
timeCreated: 1471616285
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,238 @@
#if UNITY_EDITOR
namespace UnityEditor.TreeDataModel
{
using System;
using System.Collections.Generic;
using System.Linq;
// The TreeModel is a utility class working on a list of serializable TreeElements where the order and the depth of each TreeElement define
// the tree structure. Note that the TreeModel itself is not serializable (in Unity we are currently limited to serializing lists/arrays) but the
// input list is.
// The tree representation (parent and children references) are then build internally using TreeElementUtility.ListToTree (using depth
// values of the elements).
// The first element of the input list is required to have depth == -1 (the hiddenroot) and the rest to have
// depth >= 0 (otherwise an exception will be thrown)
public class TreeModel<T> where T : TreeElement
{
IList<T> m_Data;
int m_MaxID;
public T Root { get; private set; }
public event Action modelChanged;
public int NumberOfDataElements => m_Data.Count;
public TreeModel (IList<T> data)
{
SetData (data);
}
public T Find (int id)
{
return m_Data.FirstOrDefault (element => element.ID == id);
}
public void SetData (IList<T> data)
{
Init (data);
}
void Init (IList<T> data)
{
m_Data = data ?? throw new ArgumentNullException("data", "Input data is null. Ensure input is a non-null list.");
if (m_Data.Count > 0)
{
Root = TreeElementUtility.ListToTree(data);
}
else
{
T root = Activator.CreateInstance(typeof(T), "Root", -1, 0) as T;
AddRoot(root);
Root = root;
}
m_MaxID = m_Data.Max(e => e.ID);
}
public int GenerateUniqueID ()
{
return ++m_MaxID;
}
public IList<int> GetAncestors (int id)
{
var parents = new List<int>();
TreeElement T = Find(id);
if (T != null)
{
while (T.Parent != null)
{
parents.Add(T.Parent.ID);
T = T.Parent;
}
}
return parents;
}
public IList<int> GetDescendantsThatHaveChildren (int id)
{
T searchFromThis = Find(id);
if (searchFromThis != null)
{
return GetParentsBelowStackBased(searchFromThis);
}
return new List<int>();
}
IList<int> GetParentsBelowStackBased(TreeElement searchFromThis)
{
Stack<TreeElement> stack = new Stack<TreeElement>();
stack.Push(searchFromThis);
var parentsBelow = new List<int>();
while (stack.Count > 0)
{
TreeElement current = stack.Pop();
if (current.HasChildren)
{
parentsBelow.Add(current.ID);
foreach (var T in current.Children)
{
stack.Push(T);
}
}
}
return parentsBelow;
}
public void RemoveElements (IList<int> elementIDs)
{
IList<T> elements = m_Data.Where (element => elementIDs.Contains (element.ID)).ToArray ();
RemoveElements (elements);
}
public void RemoveElements (IList<T> elements)
{
foreach (var element in elements)
if (element == Root)
throw new ArgumentException("It is not allowed to remove the root element");
var commonAncestors = TreeElementUtility.FindCommonAncestorsWithinList (elements);
foreach (var element in commonAncestors)
{
element.Parent.Children.Remove (element);
element.Parent = null;
}
TreeElementUtility.TreeToList(Root, m_Data);
Changed();
}
public void AddElements (IList<T> elements, TreeElement parent, int insertPosition)
{
if (elements == null)
throw new ArgumentNullException("elements", "elements is null");
if (elements.Count == 0)
throw new ArgumentNullException("elements", "elements Count is 0: nothing to add");
if (parent == null)
throw new ArgumentNullException("parent", "parent is null");
if (parent.Children == null)
parent.Children = new List<TreeElement>();
parent.Children.InsertRange(insertPosition, elements.Cast<TreeElement> ());
foreach (var element in elements)
{
element.Parent = parent;
element.Depth = parent.Depth + 1;
TreeElementUtility.UpdateDepthValues(element);
}
TreeElementUtility.TreeToList(Root, m_Data);
Changed();
}
public void AddRoot (T root)
{
if (root == null)
throw new ArgumentNullException("root", "root is null");
if (m_Data == null)
throw new InvalidOperationException("Internal Error: data list is null");
if (m_Data.Count != 0)
throw new InvalidOperationException("AddRoot is only allowed on empty data list");
root.Name = "Root";
root.ID = GenerateUniqueID ();
root.Depth = -1;
m_Data.Add (root);
}
public void AddElement (T element, TreeElement parent, int insertPosition)
{
if (element == null)
throw new ArgumentNullException("element", "element is null");
if (parent == null)
throw new ArgumentNullException("parent", "parent is null");
if (parent.Children == null)
parent.Children = new List<TreeElement> ();
parent.Children.Insert (insertPosition, element);
element.Parent = parent;
TreeElementUtility.UpdateDepthValues(parent);
TreeElementUtility.TreeToList(Root, m_Data);
Changed ();
}
public void MoveElements(TreeElement parentElement, int insertionIndex, List<TreeElement> elements)
{
if (insertionIndex < 0)
throw new ArgumentException("Invalid input: insertionIndex is -1, client needs to decide what index elements should be reparented at");
// Invalid reparenting input
if (parentElement == null)
return;
// We are moving items so we adjust the insertion index to accomodate that any items above the insertion index is removed before inserting
if (insertionIndex > 0)
insertionIndex -= parentElement.Children.GetRange(0, insertionIndex).Count(elements.Contains);
// Remove draggedItems from their parents
foreach (var draggedItem in elements)
{
draggedItem.Parent.Children.Remove(draggedItem); // remove from old parent
draggedItem.Parent = parentElement; // set new parent
}
if (parentElement.Children == null)
parentElement.Children = new List<TreeElement>();
// Insert dragged items under new parent
parentElement.Children.InsertRange(insertionIndex, elements);
TreeElementUtility.UpdateDepthValues (Root);
TreeElementUtility.TreeToList (Root, m_Data);
Changed ();
}
void Changed ()
{
if (modelChanged != null)
modelChanged ();
}
}
}
#endif

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6f9fab1cf2636a6439c644bf08108abb
timeCreated: 1472122507
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
using System.Text.RegularExpressions;
namespace GAS.Editor.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

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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 688920bc1afba114290b8e45101a8054
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GAS.Runtime;
using JNGame.Tools.CodeGen;
using UnityEngine;
namespace GAS.Editor
{
/// <summary>
/// AbilityTask枚举和构造工厂方法注册代码生成
/// </summary>
public class CodeGenAbilityTaskInjecter
{
private static readonly string s_CodeTemplate = @"
// auto generate by tools, DO NOT Modify it!!!
using GAS.Runtime;
namespace ##NAMESPACE
{
/// <summary>
/// 游戏业务逻辑扩展的AbilityTask枚举
/// </summary>
public enum EnumGameAbilityTaskType
{
##CODEREPLACE_0
}
#region AbilityTask构造工厂方法注册
public partial class GASInjector
{
private partial void InternalAbilityTaskInject()
{
##CODEREPLACE_1
}
}
#endregion
#region AbilityTask枚举ID自动生成
##CODEREPLACE_2
#endregion
}";
public static void GenCode(CodeGenInfo info)
{
string prefix = "\t";
var contextTemplates = new List<string>()
{
prefix + prefix + "##TYPE_NAME = ##INDEX,",
prefix + prefix + prefix + "AbilityTaskFactory.Register((ushort)EnumGameAbilityTaskType.##TYPE_NAME, () => new ##FULL_TYPE_NAME());",
prefix + "public partial class ##TYPE_NAME { public override ushort TypeId => (ushort)EnumGameAbilityTaskType.##TYPE_NAME; }"
};
var types = info.allTypes.ToArray().ToList();
types.Sort((a, b) => a.Name.CompareTo(b.Name));
var finalStr = s_CodeTemplate.Replace("##NAMESPACE", info.codeGenNameSpace);
for (int i = 0; i < contextTemplates.Count; i++)
{
finalStr = finalStr.Replace("##CODEREPLACE_" + i, GenCodeByTemplate(types, contextTemplates[i]));
}
var path = info.outputPath;
FileUtil.SaveFile(path, finalStr);
}
private static string GenCodeByTemplate(List<Type> types, string template)
{
StringBuilder sb = new StringBuilder();
int counterInstantTask = 0;
int counterOngoingTask = 0;
int counterPassiveTask = 0;
for (int i = 0; i < types.Count; i++)
{
var type = types[i];
string index;
if (type.IsSubclassOf(typeof(InstantAbilityTask)))
{
index = ((int)EnumAbilityTaskType.InstantTaskStart + (++counterInstantTask)).ToString();
}
else if (type.IsSubclassOf(typeof(OngoingAbilityTask)))
{
index = ((int)EnumAbilityTaskType.OngoingTaskStart + (++counterOngoingTask)).ToString();
}
else if(type.IsSubclassOf(typeof(PassiveAbilityTask)))
{
index = ((int)EnumAbilityTaskType.PassiveTaskStart + (++counterPassiveTask)).ToString();
}
else
{
Debug.LogError($"AbilityTask的类型不是 InstantAbilityTask /OngoingAbilityTask / PassiveAbilityTask 任意一种,{type.FullName}");
continue;
}
var typeName = type.Name.ToString();
var fullTypeName = type.FullName.ToString();
var str = template.Replace("##INDEX", index).Replace("##TYPE_NAME", typeName).Replace("##FULL_TYPE_NAME", fullTypeName);
sb.AppendLine(str);
}
return sb.ToString();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fdc093094756c434781a2996f811416d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using JNGame.Tools.CodeGen;
namespace GAS.Editor
{
/// <summary>
/// 行为树节点序列化相关代码生成
/// </summary>
public class CodeGenAbilityTaskSerialization
{
/// <summary>
/// 代码文件头部模板代码
/// </summary>
private static readonly string s_CodeHeaderTemplate = @"
// auto generate by tools, DO NOT Modify it!!!
using GAS.Runtime;
using JNGame.Serialization;
";
/// <summary>
/// 类模板代码
/// </summary>
private static readonly string s_ClsTemplate = @"
namespace ##NAMESPACE
{
public partial class ##TYPE_NAME
{
public override void Serialize(Serializer writer)
{
base.Serialize(writer);
##CODEREPLACE_0
}
public override void Deserialize(Deserializer reader)
{
base.Deserialize(reader);
##CODEREPLACE_1
}
}
}
";
private static readonly BindingFlags s_BindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
public static void GenCode(CodeGenInfo info)
{
string prefix = "\t\t\t";
var types = info.allTypes.ToArray().ToList();
types.Sort((a, b) => a.Name.CompareTo(b.Name));
StringBuilder sb = new StringBuilder();
foreach (var type in types)
{
var clsStr = GenCodeByTemplate(type, s_ClsTemplate);
var contextTemplates = new List<CodeGenTemplateInfo>()
{
new CodeGenTemplateInfo()
{
Default = prefix + "writer.Write(##NAME);",
Enum = prefix + "writer.Write((int)##NAME);", // TODO deal with array list &dict
Array = prefix + "writer.WriteArray(##NAME);",
},
new CodeGenTemplateInfo()
{
Default = prefix + "##NAME = reader.Read##TYPE_NAME();",
Enum = prefix + "##NAME = (##TYPE_NAME)reader.ReadInt32();",
Array = prefix + "##NAME = reader.ReadArray(new ##ELEMENT_TYPE_NAME());",
},
};
var fields = type.GetFields(s_BindingFlags)
.Select(a => new CodeGenFieldInfo() { name = a.Name, type = a.FieldType }).ToList();
// var properties = type.GetProperties(s_BindingFlags).Where(a => a.CanRead && a.CanWrite)
// .Select(a => new CodeGenFieldInfo() { name = a.Name, type = a.PropertyType }).ToList();
// fields.AddRange(properties);
for (int i = 0; i < contextTemplates.Count; i++)
{
clsStr = clsStr.Replace("##CODEREPLACE_" + i, GetFieldsCode(fields, contextTemplates[i]));
}
if (fields.Count > 0)
{
sb.AppendLine(clsStr);
}
}
var finalStr = s_CodeHeaderTemplate;
finalStr += sb.ToString();
var path = info.outputPath;
FileUtil.SaveFile(path, finalStr);
}
private static string GetFieldsCode(List<CodeGenFieldInfo> fields, CodeGenTemplateInfo template)
{
StringBuilder sbField = new StringBuilder();
foreach (var field in fields)
{
var templateStr = template.GetTemplateStr(field);
string elementTypeName = "";
if(field.IsArray || field.IsList)
{
elementTypeName = field.type.GetElementType().Name;
}
var str = templateStr.Replace("##NAME", field.name).Replace("##TYPE_NAME", field.TypeName).Replace("##FULL_TYPE_NAME", field.FullTypeName)
.Replace("##ELEMENT_TYPE_NAME", elementTypeName);
sbField.AppendLine(str);
}
return sbField.ToString();
}
private static string GenCodeByTemplate(Type type, string template)
{
StringBuilder sb = new StringBuilder();
var nameSpace = type.Namespace.ToString();
var typeName = type.Name.ToString();
var fullTypeName = type.FullName.ToString();
var str = template.Replace("##NAMESPACE", nameSpace).Replace("##TYPE_NAME", typeName).Replace("##FULL_TYPE_NAME", fullTypeName);
sb.AppendLine(str);
return sb.ToString();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d06f987f50f2b6947beba00d4c7be0b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More