mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-06-26 03:14:47 +00:00
简单提交
This commit is contained in:
parent
930911e7df
commit
0e94e376fb
File diff suppressed because it is too large
Load Diff
@ -43,13 +43,13 @@
|
||||
<ItemGroup>
|
||||
<None Include="Assets\Plugins\Sirenix\Odin Inspector\Assets\Editor\Bootstrap License.txt" />
|
||||
<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 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 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>
|
||||
</ItemGroup>
|
||||
<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
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af0af80f518a09a43bc4895a97f6d06d
|
||||
guid: d35ae3f4d549fbb4e9a9edb5d6318639
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa26d750811c9a44f88f4e478131110e
|
||||
guid: 3bcde4791b2da434ca62faacb472df78
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
@ -1,6 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34ae97a0d741c5844a4fac12ff8e1c45
|
||||
TextScriptImporter:
|
||||
guid: 52f44ade52e60e247a5ef830c3152bc0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fda7b329dfe891948b714c3f8966e36f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed22e0034a143d443ad93ed0c1f0fe47
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,14 @@
|
||||
|
||||
// auto generate by tools, DO NOT Modify it!!!
|
||||
|
||||
using GAS.Runtime;
|
||||
|
||||
namespace JNGame.GAS
|
||||
{
|
||||
#region MMC枚举ID自动生成
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a829f1acecbd5d348800bcbf18fb138e
|
||||
guid: 13b4d6ef882241949b349a1492f466c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@ -0,0 +1,6 @@
|
||||
|
||||
// auto generate by tools, DO NOT Modify it!!!
|
||||
|
||||
using GAS.Runtime;
|
||||
using JNGame.Serialization;
|
||||
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79aaab94f725e0347b32b6383dc6361e
|
||||
guid: 2b2c4acd8004e3144ae1fddc5c8eee6d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
@ -1,12 +1,16 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GAS.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
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 AbilityCollectionGenerator
|
||||
{
|
||||
public static void Gen()
|
||||
@ -62,12 +66,8 @@ namespace GAS.Editor
|
||||
|
||||
writer.WriteLine("");
|
||||
|
||||
var abilityAssets = EditorUtil
|
||||
.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath)
|
||||
.OrderBy(x => x.UniqueName)
|
||||
.ThenBy(x => x.name)
|
||||
.ToArray();
|
||||
|
||||
var abilityAssets =
|
||||
EditorUtil.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath);
|
||||
foreach (var ability in abilityAssets)
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(ability);
|
||||
@ -130,6 +130,9 @@ namespace GAS.Editor
|
||||
writer.Write("}");
|
||||
|
||||
Console.WriteLine($"Generated GTagLib at path: {filePath}");
|
||||
|
||||
Debug.Log("GenerateAbilityCollection Finish");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GAS.General.Validation;
|
||||
using GAS.Runtime;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEditor;
|
||||
@ -54,22 +53,12 @@ namespace GAS.Editor
|
||||
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;
|
||||
|
||||
[HorizontalGroup("Buttons", width: 120)]
|
||||
[Button(SdfIconType.TicketDetailed, "@_showDetail?\"Hide Detail\":\"Show Detail\"", ButtonHeight = 30)]
|
||||
public void ToggleShowDetail()
|
||||
public void Toggle()
|
||||
{
|
||||
_showDetail = !_showDetail;
|
||||
Refresh();
|
||||
@ -83,25 +72,16 @@ namespace GAS.Editor
|
||||
{
|
||||
Abilities.Clear();
|
||||
var abilityAssets = EditorUtil.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath);
|
||||
var orderedAbilityAssets = _orderByUniqueName
|
||||
? abilityAssets
|
||||
.OrderBy(x => x.UniqueName)
|
||||
.ThenBy(x => x.name)
|
||||
: abilityAssets.OrderBy(x => x.name);
|
||||
|
||||
Abilities = orderedAbilityAssets.Select(ability =>
|
||||
abilityAssets.ForEach(ability =>
|
||||
{
|
||||
var text = Validations.ValidateVariableName(ability.UniqueName).IsValid
|
||||
? ability.UniqueName
|
||||
: $"{ability.UniqueName}(非法UniqueName)";
|
||||
|
||||
var text = $"{ability.UniqueName}";
|
||||
if (_showDetail)
|
||||
{
|
||||
text += $" - asset: {ability.name}, type: {ability.GetType().FullName}";
|
||||
}
|
||||
|
||||
return text;
|
||||
}).ToList();
|
||||
Abilities.Add(text);
|
||||
});
|
||||
}
|
||||
|
||||
bool ExistAbilityWithEmptyUniqueName()
|
||||
|
@ -1,5 +1,3 @@
|
||||
using GAS.Runtime;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
@ -11,7 +9,8 @@ namespace GAS.Editor
|
||||
public const int StandardFrameUnitWidth = 1;
|
||||
public const int MaxFrameUnitLevel = 20;
|
||||
public const float MinTimerShaftFrameDrawStep = 5;
|
||||
public int DefaultFrameRate => JexGasManager.FrameRate;
|
||||
public int TickTime => GASTimer.TimeLineAbilityTickTime;
|
||||
public int FrameRate => GASTimer.FrameRateValue;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,4 +1,8 @@
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using Editor;
|
||||
using GAS.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
@ -8,15 +12,9 @@ using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UIElements;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 这个类被反射引用到, 重构请小心!!
|
||||
/// </summary>
|
||||
public class AbilityTimelineEditorWindow : EditorWindow
|
||||
{
|
||||
[SerializeField]
|
||||
private VisualTreeAsset m_VisualTreeAsset;
|
||||
[SerializeField] private VisualTreeAsset m_VisualTreeAsset;
|
||||
|
||||
private VisualElement _root;
|
||||
|
||||
@ -27,7 +25,6 @@ namespace GAS.Editor
|
||||
public TimelineInspector TimelineInspector { get; private set; }
|
||||
|
||||
private static EditorWindow _childInspector;
|
||||
|
||||
public void CreateGUI()
|
||||
{
|
||||
Instance = this;
|
||||
@ -45,10 +42,7 @@ namespace GAS.Editor
|
||||
TimelineInspector = new TimelineInspector(_root);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这个方法被反射引用到, 重构请小心!!
|
||||
/// </summary>
|
||||
public static void ShowWindow(TimelineAbilityAssetBase asset)
|
||||
public static void ShowWindow(TimelineAbilityAsset asset)
|
||||
{
|
||||
var wnd = GetWindow<AbilityTimelineEditorWindow>();
|
||||
wnd.titleContent = new GUIContent("AbilityTimelineEditorWindow");
|
||||
@ -63,7 +57,7 @@ namespace GAS.Editor
|
||||
AbilityAsset.Save();
|
||||
}
|
||||
|
||||
private void InitAbility(TimelineAbilityAssetBase asset)
|
||||
private void InitAbility(TimelineAbilityAsset asset)
|
||||
{
|
||||
_abilityAsset.value = asset;
|
||||
MaxFrame.value = AbilityAsset.FrameCount;
|
||||
@ -84,11 +78,11 @@ namespace GAS.Editor
|
||||
|
||||
private ObjectField _abilityAsset;
|
||||
private Button _btnShowAbilityAssetDetail;
|
||||
public TimelineAbilityAssetBase AbilityAsset => _abilityAsset.value as TimelineAbilityAssetBase;
|
||||
public TimelineAbilityAsset AbilityAsset => _abilityAsset.value as TimelineAbilityAsset;
|
||||
|
||||
// private TimelineAbilityEditorWindow AbilityAssetEditor => AbilityAsset != null
|
||||
// ? UnityEditor.Editor.CreateEditor(AbilityAsset) as TimelineAbilityEditorWindow
|
||||
// : null;
|
||||
private TimelineAbilityEditorWindow AbilityAssetEditor => AbilityAsset != null
|
||||
? UnityEditor.Editor.CreateEditor(AbilityAsset) as TimelineAbilityEditorWindow
|
||||
: null;
|
||||
|
||||
private void InitAbilityAssetBar()
|
||||
{
|
||||
@ -101,6 +95,7 @@ namespace GAS.Editor
|
||||
|
||||
private void OnSequentialAbilityAssetChanged(ChangeEvent<Object> evt)
|
||||
{
|
||||
var asset = evt.newValue as TimelineAbilityAsset;
|
||||
if (AbilityAsset != null)
|
||||
{
|
||||
MaxFrame.value = AbilityAsset.FrameCount;
|
||||
@ -208,7 +203,7 @@ namespace GAS.Editor
|
||||
|
||||
if (_currentMaxFrame == value) return;
|
||||
_currentMaxFrame = value;
|
||||
AbilityAsset.FrameCount = _currentMaxFrame;
|
||||
AbilityAsset.frameCount = _currentMaxFrame;
|
||||
SaveAsset();
|
||||
MaxFrame.value = _currentMaxFrame;
|
||||
TrackView.UpdateContentSize();
|
||||
@ -267,22 +262,29 @@ namespace GAS.Editor
|
||||
BtnRightFrame = _root.Q<Button>(nameof(BtnRightFrame));
|
||||
BtnRightFrame.clickable.clicked += OnRightFrame;
|
||||
|
||||
InitInfo();
|
||||
|
||||
CurrentFrame = _root.Q<IntegerField>(nameof(CurrentFrame));
|
||||
CurrentFrame.RegisterValueChangedCallback(OnCurrentFrameChanged);
|
||||
MaxFrame = _root.Q<IntegerField>(nameof(MaxFrame));
|
||||
MaxFrame.RegisterValueChangedCallback(OnMaxFrameChanged);
|
||||
|
||||
}
|
||||
|
||||
private void OnMaxFrameChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
CurrentMaxFrame = evt.newValue;
|
||||
MaxFrame.value = CurrentMaxFrame;
|
||||
|
||||
TotalTime.text = $"total:{CurrentMaxFrame * Config.TickTime}ms";
|
||||
}
|
||||
|
||||
private void OnCurrentFrameChanged(ChangeEvent<int> evt)
|
||||
{
|
||||
CurrentSelectFrameIndex = evt.newValue;
|
||||
CurrentFrame.value = CurrentSelectFrameIndex;
|
||||
|
||||
CurrentTime.text = $"time:{CurrentSelectFrameIndex * Config.TickTime}ms";
|
||||
}
|
||||
|
||||
private void RefreshPlayButton()
|
||||
@ -312,6 +314,26 @@ namespace GAS.Editor
|
||||
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
|
||||
|
||||
#region Clip Inspector
|
||||
@ -354,7 +376,7 @@ namespace GAS.Editor
|
||||
if (IsPlaying)
|
||||
{
|
||||
var deltaTime = (DateTime.Now - _startTime).TotalSeconds;
|
||||
var frameIndex = (int)(deltaTime * Config.DefaultFrameRate) + _startPlayFrameIndex;
|
||||
var frameIndex = (int)(deltaTime * FrameRateValue) + _startPlayFrameIndex;
|
||||
if (frameIndex >= CurrentMaxFrame)
|
||||
{
|
||||
frameIndex = CurrentMaxFrame;
|
||||
@ -384,9 +406,10 @@ namespace GAS.Editor
|
||||
|
||||
#region Another Inspector
|
||||
|
||||
|
||||
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;
|
||||
if(targetGO) Selection.activeObject = targetGO;
|
||||
return inspectorInstance;
|
||||
@ -395,3 +418,4 @@ namespace GAS.Editor
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
@ -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">
|
||||
<Style src="project://database/Assets/GAS/Editor/Ability/AbilityTimelineEditor/EditorWindow/AbilityTimelineEditorWindow.uss?fileID=7433441132597879392&guid=28fcc296ac2d3554db81c84901f63835&type=3#AbilityTimelineEditorWindow" />
|
||||
<Style src="project://database/Assets/GameScript/GAS/Editor/Ability/AbilityTimelineEditor/EditorWindow/AbilityTimelineEditorWindow.uss?fileID=7433441132597879392&guid=28fcc296ac2d3554db81c84901f63835&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="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;" />
|
||||
@ -14,24 +14,29 @@
|
||||
</ui:VisualElement>
|
||||
</ui:VisualElement>
|
||||
<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="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="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: 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:Button text="<" 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="BtnRightFrame" style="-unity-font-style: bold; font-size: 13px; align-self: center;" />
|
||||
</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: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: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 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>
|
||||
<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: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:VisualElement name="ContentTrackList" style="flex-grow: 1; height: 1000px; flex-shrink: 1; width: auto;" />
|
||||
</ui:ScrollView>
|
||||
|
@ -15,14 +15,14 @@ namespace GAS.Editor
|
||||
private readonly VisualElement _root;
|
||||
private Button _btnAddTrack;
|
||||
private VisualElement _contentTrackListParent;
|
||||
private MenuTrack _menuPassiveGameplayEffect;
|
||||
private MenuTrack _menuBuffGameplayEffect;
|
||||
private MenuTrack _menuDurationalCue;
|
||||
private MenuTrack _menuInstantCue;
|
||||
private MenuTrack _menuInstantTask;
|
||||
private MenuTrack _menuOngoingTask;
|
||||
private MenuTrack _menuReleaseGameplayEffect;
|
||||
private MenuTrack _menuPassiveGameplayEffect;
|
||||
private MenuTrack _menuPassiveTask;
|
||||
private MenuTrack _menuReleaseGameplayEffect;
|
||||
private VisualElement _trackMenuParent;
|
||||
|
||||
public TimelineTrackView(VisualElement root)
|
||||
@ -34,7 +34,7 @@ namespace GAS.Editor
|
||||
public List<TrackBase> TrackList { get; } = new();
|
||||
|
||||
private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config;
|
||||
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
private void InitTracks()
|
||||
{
|
||||
|
@ -1,31 +1,31 @@
|
||||
//
|
||||
// #if UNITY_EDITOR
|
||||
// namespace GAS.Editor
|
||||
// {
|
||||
// using Runtime;
|
||||
// using Sirenix.OdinInspector.Editor;
|
||||
// using UnityEditor;
|
||||
// using UnityEngine;
|
||||
// using GAS.General;
|
||||
//
|
||||
// [CustomEditor(typeof(TimelineAbilityAsset))]
|
||||
// public class TimelineAbilityEditorWindow : OdinEditor
|
||||
// {
|
||||
// private TimelineAbilityAsset _asset => target as TimelineAbilityAsset;
|
||||
//
|
||||
// public override void OnInspectorGUI()
|
||||
// {
|
||||
// base.OnInspectorGUI();
|
||||
//
|
||||
// EditorGUILayout.BeginVertical(GUI.skin.box);
|
||||
// if (GUILayout.Button(GASTextDefine.BUTTON_CHECK_TIMELINE_ABILITY, GUILayout.Height(30), GUILayout.Width(300))) EditAbilityTimeline();
|
||||
// EditorGUILayout.EndVertical();
|
||||
// }
|
||||
//
|
||||
// private void EditAbilityTimeline()
|
||||
// {
|
||||
// AbilityTimelineEditorWindow.ShowWindow(_asset);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using Runtime;
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using GAS.General;
|
||||
|
||||
[CustomEditor(typeof(TimelineAbilityAsset))]
|
||||
public class TimelineAbilityEditorWindow : OdinEditor
|
||||
{
|
||||
private TimelineAbilityAsset _asset => target as TimelineAbilityAsset;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
base.OnInspectorGUI();
|
||||
|
||||
EditorGUILayout.BeginVertical(GUI.skin.box);
|
||||
if (GUILayout.Button(GASTextDefine.BUTTON_CHECK_TIMELINE_ABILITY, GUILayout.Height(30), GUILayout.Width(300))) EditAbilityTimeline();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
private void EditAbilityTimeline()
|
||||
{
|
||||
AbilityTimelineEditorWindow.ShowWindow(_asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -8,7 +8,7 @@ namespace GAS.Editor
|
||||
|
||||
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 ClipDataForSave
|
||||
@ -36,7 +36,7 @@ namespace GAS.Editor
|
||||
public override void RefreshShow(float newFrameUnitWidth)
|
||||
{
|
||||
base.RefreshShow(newFrameUnitWidth);
|
||||
var taskType = TaskClipData.ongoingTask.TaskData.Type;
|
||||
var taskType = TaskClipData.ongoingTask.taskData.Type;
|
||||
var shortName = taskType.Split('.').Last();
|
||||
ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!";
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace GAS.Editor
|
||||
|
||||
private const string GRP_BOX = "GRP_BOX";
|
||||
private const string GRP_BOX_TASK = "GRP_BOX/Task";
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private 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>";
|
||||
Duration = _clip.TaskClipData.durationFrame;
|
||||
OngoingTaskType = _clip.TaskClipData.ongoingTask.TaskData.Type;
|
||||
OngoingTaskType = _clip.TaskClipData.ongoingTask.taskData.Type;
|
||||
|
||||
RefreshTaskInspector();
|
||||
}
|
||||
@ -123,8 +123,8 @@ namespace GAS.Editor
|
||||
|
||||
private void OnTaskTypeChanged()
|
||||
{
|
||||
_clip.ClipDataForSave.ongoingTask.TaskData.Type = OngoingTaskType;
|
||||
_clip.ClipDataForSave.ongoingTask.TaskData.Data = null;
|
||||
_clip.ClipDataForSave.ongoingTask.taskData.Type = OngoingTaskType;
|
||||
_clip.ClipDataForSave.ongoingTask.taskData.Data = null;
|
||||
AbilityTimelineEditorWindow.Instance.Save();
|
||||
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace GAS.Editor
|
||||
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);
|
||||
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
public TaskClipEventTrackData TaskClipTrackDataForSave
|
||||
{
|
||||
get
|
||||
|
@ -36,7 +36,7 @@ namespace GAS.Editor
|
||||
string info = "";
|
||||
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);
|
||||
info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad3afdc4d0854b7e87d840e52dbedd44
|
||||
timeCreated: 1729189674
|
||||
guid: b4a27decc519488fb2d4065ccafb75d9
|
||||
timeCreated: 1717572791
|
@ -5,9 +5,10 @@ namespace GAS.Editor
|
||||
using System.Linq;
|
||||
using GAS.Runtime;
|
||||
|
||||
|
||||
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 ClipDataForSave
|
||||
@ -22,6 +23,7 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void Delete()
|
||||
{
|
||||
var success = track.TaskClipTrackDataForSave.clipEvents.Remove(TaskClipData);
|
||||
@ -34,7 +36,7 @@ namespace GAS.Editor
|
||||
public override void RefreshShow(float newFrameUnitWidth)
|
||||
{
|
||||
base.RefreshShow(newFrameUnitWidth);
|
||||
var taskType = TaskClipData.passiveTask.TaskData.Type;
|
||||
var taskType = TaskClipData.passiveTask.taskData.Type;
|
||||
var shortName = taskType.Split('.').Last();
|
||||
ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!";
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ namespace GAS.Editor
|
||||
|
||||
private const string GRP_BOX = "GRP_BOX";
|
||||
private const string GRP_BOX_TASK = "GRP_BOX/Task";
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private PassiveTaskClip _clip;
|
||||
|
||||
public static PassiveTaskClipEditor Create(PassiveTaskClip clip)
|
||||
@ -96,7 +96,7 @@ namespace GAS.Editor
|
||||
RunInfo = $"<b>Start(f):{_clip.TaskClipData.startFrame}</b>";
|
||||
// Duration = _clip.TaskClipData.durationFrame;
|
||||
StartFrame = _clip.TaskClipData.startFrame;
|
||||
PassiveTaskType = _clip.TaskClipData.passiveTask.TaskData.Type;
|
||||
PassiveTaskType = _clip.TaskClipData.passiveTask.taskData.Type;
|
||||
|
||||
RefreshTaskInspector();
|
||||
}
|
||||
@ -138,8 +138,8 @@ namespace GAS.Editor
|
||||
|
||||
private void OnTaskTypeChanged()
|
||||
{
|
||||
_clip.ClipDataForSave.passiveTask.TaskData.Type = PassiveTaskType;
|
||||
_clip.ClipDataForSave.passiveTask.TaskData.Data = null;
|
||||
_clip.ClipDataForSave.passiveTask.taskData.Type = PassiveTaskType;
|
||||
_clip.ClipDataForSave.passiveTask.taskData.Data = null;
|
||||
AbilityTimelineEditorWindow.Instance.Save();
|
||||
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace GAS.Editor
|
||||
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);
|
||||
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
public PassiveTaskClipEventTrackData TaskClipTrackDataForSave
|
||||
{
|
||||
get
|
||||
|
@ -36,7 +36,7 @@ namespace GAS.Editor
|
||||
string info = "";
|
||||
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);
|
||||
info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
|
||||
}
|
||||
|
@ -1,11 +1,18 @@
|
||||
using GAS.Runtime;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
#if UNITY_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 new TaskMarkEvent MarkData => markData as TaskMarkEvent;
|
||||
public TaskMarkEvent MarkData => markData as TaskMarkEvent;
|
||||
|
||||
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()
|
||||
{
|
||||
@ -31,7 +38,7 @@ namespace GAS.Editor
|
||||
var markEvent = new TaskMarkEvent
|
||||
{
|
||||
startFrame = startFrame,
|
||||
InstantTasks = (markData as TaskMarkEvent)?.InstantTasks
|
||||
instantTasks = (markData as TaskMarkEvent)?.instantTasks
|
||||
};
|
||||
track.InstantTaskEventTrackData.markEvents.Add(markEvent);
|
||||
|
||||
@ -181,7 +188,7 @@ namespace GAS.Editor
|
||||
{
|
||||
if (frameIndex == StartFrameIndex)
|
||||
{
|
||||
foreach (var t in MarkData.InstantTasks)
|
||||
foreach (var t in MarkData.instantTasks)
|
||||
{
|
||||
var task = t.Load() as InstantAbilityTask;
|
||||
task?.OnEditorPreview();
|
||||
@ -195,3 +202,4 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,20 +1,20 @@
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Editor;
|
||||
using GAS.General;
|
||||
using GAS.Runtime;
|
||||
using Runtime;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
public class TaskMarkEditor : OdinEditorWindow
|
||||
{
|
||||
[BoxGroup]
|
||||
[HideLabel]
|
||||
[DisplayAsString(TextAlignment.Left, true)]
|
||||
[BoxGroup] [HideLabel] [DisplayAsString(TextAlignment.Left, true)]
|
||||
public string RunInfo;
|
||||
|
||||
private TaskMark _mark;
|
||||
@ -22,13 +22,13 @@ namespace GAS.Editor
|
||||
[Delayed]
|
||||
[BoxGroup]
|
||||
[HideReferenceObjectPicker]
|
||||
[ListDrawerSettings(ShowFoldout = true, DraggableItems = true)]
|
||||
[ListDrawerSettings(Expanded = true, DraggableItems = true)]
|
||||
[OnValueChanged("OnTaskListChanged", true)]
|
||||
public List<InstantTaskCellInspector> Tasks;
|
||||
|
||||
public static TaskMarkEditor Create(TaskMark mark)
|
||||
{
|
||||
var window = CreateInstance<TaskMarkEditor>();
|
||||
var window = new TaskMarkEditor();
|
||||
window._mark = mark;
|
||||
|
||||
window.UpdateMarkInfo();
|
||||
@ -47,7 +47,7 @@ namespace GAS.Editor
|
||||
{
|
||||
RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>";
|
||||
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()
|
||||
@ -56,14 +56,14 @@ namespace GAS.Editor
|
||||
foreach (var t in Tasks)
|
||||
tasks.Add(new InstantTaskData
|
||||
{
|
||||
TaskData = new JsonData
|
||||
taskData = new JsonData
|
||||
{
|
||||
Type = t.InstantTaskType,
|
||||
Data = t.Data()
|
||||
}
|
||||
});
|
||||
|
||||
_mark.MarkDataForSave.InstantTasks = tasks;
|
||||
_mark.MarkDataForSave.instantTasks = tasks;
|
||||
AbilityTimelineEditorWindow.Instance.Save();
|
||||
}
|
||||
}
|
||||
@ -93,17 +93,14 @@ namespace GAS.Editor
|
||||
[OnValueChanged("OnTaskTypeChanged")]
|
||||
public string InstantTaskType;
|
||||
|
||||
[BoxGroup]
|
||||
[HideReferenceObjectPicker]
|
||||
[HideIf("InstantTaskIsNull")]
|
||||
[LabelText("Detail")]
|
||||
[BoxGroup] [HideReferenceObjectPicker] [HideIf("InstantTaskIsNull")] [LabelText("Detail")]
|
||||
public InstantTaskInspector InstantTask;
|
||||
|
||||
public InstantTaskCellInspector(InstantTaskData data)
|
||||
{
|
||||
_data = data;
|
||||
_instantAbilityTask = data.Load() as InstantAbilityTask;
|
||||
InstantTaskType = data.TaskData.Type;
|
||||
InstantTaskType = data.taskData.Type;
|
||||
RefreshDetailInspector();
|
||||
}
|
||||
|
||||
@ -111,7 +108,7 @@ namespace GAS.Editor
|
||||
{
|
||||
_data = new InstantTaskData();
|
||||
_instantAbilityTask = _data.Load() as InstantAbilityTask;
|
||||
InstantTaskType = _data.TaskData.Type;
|
||||
InstantTaskType = _data.taskData.Type;
|
||||
RefreshDetailInspector();
|
||||
}
|
||||
|
||||
@ -125,13 +122,10 @@ namespace GAS.Editor
|
||||
if (_instantTaskInspectorMap != null) return _instantTaskInspectorMap;
|
||||
_instantTaskInspectorMap = new Dictionary<Type, Type>();
|
||||
foreach (var inspectorType in InstantTaskInspectorTypes)
|
||||
{
|
||||
if (inspectorType.BaseType != null)
|
||||
{
|
||||
var taskType = inspectorType.BaseType.GetGenericArguments()[0];
|
||||
_instantTaskInspectorMap.Add(taskType, inspectorType);
|
||||
}
|
||||
}
|
||||
|
||||
return _instantTaskInspectorMap;
|
||||
}
|
||||
@ -142,13 +136,13 @@ namespace GAS.Editor
|
||||
if (_instantAbilityTask == null) return null;
|
||||
|
||||
_data.Save(_instantAbilityTask);
|
||||
return _data.TaskData.Data;
|
||||
return _data.taskData.Data;
|
||||
}
|
||||
|
||||
private void OnTaskTypeChanged()
|
||||
{
|
||||
_data.TaskData.Type = InstantTaskType;
|
||||
_data.TaskData.Data = null;
|
||||
_data.taskData.Type = InstantTaskType;
|
||||
_data.taskData.Data = null;
|
||||
RefreshDetailInspector();
|
||||
}
|
||||
|
||||
@ -160,7 +154,7 @@ namespace GAS.Editor
|
||||
public void RefreshDetailInspector()
|
||||
{
|
||||
_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);
|
||||
taskInspector.Init(_instantAbilityTask);
|
||||
@ -173,3 +167,4 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -11,7 +11,7 @@ namespace GAS.Editor
|
||||
public class TaskMarkEventTrack : TrackBase
|
||||
{
|
||||
private TaskMarkEventTrackData _instantTasksTrackData;
|
||||
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
public TaskMarkEventTrackData InstantTaskEventTrackData
|
||||
{
|
||||
|
@ -37,9 +37,9 @@ namespace GAS.Editor
|
||||
foreach (var mark in _track.InstantTaskEventTrackData.markEvents)
|
||||
{
|
||||
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);
|
||||
info += $" |-> {shortName}\n";
|
||||
}
|
||||
|
@ -60,6 +60,14 @@ namespace GAS.Editor
|
||||
clipData = updatedClip;
|
||||
}
|
||||
|
||||
public void UpdateGameplayEffectValueId(int id)
|
||||
{
|
||||
var updatedClip = ClipDataForSave;
|
||||
ClipDataForSave.gameplayEffectValueId = id;
|
||||
AbilityTimelineEditorWindow.Instance.Save();
|
||||
clipData = updatedClip;
|
||||
}
|
||||
|
||||
public void UpdateClipDataBuff(GameplayEffectAsset newBuff)
|
||||
{
|
||||
var updatedClip = ClipDataForSave;
|
||||
|
@ -10,7 +10,7 @@ namespace GAS.Editor
|
||||
|
||||
public class BuffGameplayEffectClipEditor : OdinEditorWindow
|
||||
{
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
private BuffGameplayEffectClip _clip;
|
||||
|
||||
@ -33,6 +33,12 @@ namespace GAS.Editor
|
||||
[OnValueChanged("OnDurationFrameChanged")]
|
||||
public int Duration;
|
||||
|
||||
[Delayed]
|
||||
[BoxGroup]
|
||||
[LabelText("BuffValueId")]
|
||||
[OnValueChanged("OnGameplayEffectValueIdChanged")]
|
||||
public int GameplayEffectValueId;
|
||||
|
||||
[Delayed]
|
||||
[BoxGroup]
|
||||
[AssetSelector]
|
||||
@ -52,6 +58,7 @@ namespace GAS.Editor
|
||||
Buff = _clip.BuffGameplayEffectClipData.gameplayEffect;
|
||||
RunInfo = $"<b>Run(f):{_clip.BuffGameplayEffectClipData.startFrame} -> {_clip.BuffGameplayEffectClipData.EndFrame}</b>";
|
||||
Duration = _clip.BuffGameplayEffectClipData.durationFrame;
|
||||
GameplayEffectValueId = _clip.BuffGameplayEffectClipData.gameplayEffectValueId;
|
||||
}
|
||||
|
||||
private void OnDurationFrameChanged()
|
||||
@ -66,6 +73,13 @@ namespace GAS.Editor
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void OnGameplayEffectValueIdChanged()
|
||||
{
|
||||
// 保存数据
|
||||
_clip.UpdateGameplayEffectValueId(GameplayEffectValueId);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void OnBuffChanged()
|
||||
{
|
||||
_clip.UpdateClipDataBuff(Buff);
|
||||
|
@ -13,7 +13,7 @@ namespace GAS.Editor
|
||||
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);
|
||||
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
public BuffGameplayEffectTrackData BuffTrackDataForSave
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ namespace GAS.Editor
|
||||
|
||||
public class DurationalCueClip : TrackClip<DurationalCueTrack>
|
||||
{
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
public DurationalCueClipEvent DurationalCueClipData => clipData as DurationalCueClipEvent;
|
||||
|
||||
private DurationalCueClipEvent ClipDataForSave
|
||||
|
@ -10,7 +10,7 @@ namespace GAS.Editor
|
||||
|
||||
public class DurationalCueClipEditor : OdinEditorWindow
|
||||
{
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
private DurationalCueClip _clip;
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace GAS.Editor
|
||||
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);
|
||||
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
public DurationalCueTrackData CueTrackDataForSave
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ namespace GAS.Editor
|
||||
public class InstantCueTrack : TrackBase
|
||||
{
|
||||
private InstantCueTrackData _instantCuesTrackData;
|
||||
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
public InstantCueTrackData InstantCueTrackData
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace GAS.Editor
|
||||
private Color _trackColor;
|
||||
private Type _trackDataType;
|
||||
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 TimelineTrackView TrackView => AbilityTimelineEditorWindow.Instance.TrackView;
|
||||
public override Type TrackDataType { get; }
|
||||
@ -61,7 +61,7 @@ namespace GAS.Editor
|
||||
// 创建Data
|
||||
var data = (TrackDataBase)Activator.CreateInstance(_trackDataType);
|
||||
data.DefaultInit();
|
||||
data.AddToAbilityAsset(AbilityAsset);
|
||||
data.AddToAbilityAsset(AbilityAsset, _trackDataType);
|
||||
|
||||
// 初始化View
|
||||
track.Init(TrackParent, MenuParent, Config.FrameUnitWidth, data);
|
||||
@ -105,15 +105,15 @@ namespace GAS.Editor
|
||||
if (_trackType == typeof(BuffGameplayEffectTrack))
|
||||
return baseIndex + (AbilityAsset.BuffGameplayEffects?.Count ?? 0);
|
||||
|
||||
if (_trackType == typeof(PassiveGameplayEffectTrack))
|
||||
return baseIndex + (AbilityAsset.PassiveGameplayEffects?.Count ?? 0);
|
||||
|
||||
if (_trackType == typeof(TaskClipEventTrack))
|
||||
return baseIndex + (AbilityAsset.OngoingTasks?.Count ?? 0);
|
||||
|
||||
if (_trackType == typeof(DurationalCueTrack))
|
||||
return baseIndex + (AbilityAsset.DurationalCues?.Count ?? 0);
|
||||
|
||||
if (_trackType == typeof(PassiveGameplayEffectTrack))
|
||||
return baseIndex + (AbilityAsset.PassiveGameplayEffects?.Count ?? 0);
|
||||
|
||||
if (_trackType == typeof(PassiveTaskClipEventTrack))
|
||||
return baseIndex + (AbilityAsset.PassiveTasks?.Count ?? 0);
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2da34a43d154bf7af1859c786b7e438
|
||||
timeCreated: 1729189458
|
||||
fileFormatVersion: 2
|
||||
guid: 3a6f1fc54df943d3a4143196c049703d
|
||||
timeCreated: 1717123975
|
@ -10,7 +10,7 @@ namespace GAS.Editor
|
||||
|
||||
public class PassiveGameplayEffectClipEditor : OdinEditorWindow
|
||||
{
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
private PassiveGameplayEffectClip _clip;
|
||||
|
||||
|
@ -9,12 +9,11 @@ namespace GAS.Editor
|
||||
public class PassiveGameplayEffectTrack : TrackBase
|
||||
{
|
||||
private PassiveGameplayEffectTrackData _PassiveGameplayEffectTrackData;
|
||||
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
public override Type TrackDataType => typeof(PassiveGameplayEffectTrackData);
|
||||
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);
|
||||
|
||||
private TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
|
||||
public PassiveGameplayEffectTrackData BuffTrackDataForSave
|
||||
{
|
||||
|
@ -1,11 +1,18 @@
|
||||
using GAS.Runtime;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_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>
|
||||
{
|
||||
private new ReleaseGameplayEffectMarkEvent MarkData => markData as ReleaseGameplayEffectMarkEvent;
|
||||
private ReleaseGameplayEffectMarkEvent MarkData => markData as ReleaseGameplayEffectMarkEvent;
|
||||
|
||||
public ReleaseGameplayEffectMarkEvent MarkDataForSave
|
||||
{
|
||||
@ -128,3 +135,4 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,25 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GAS.General;
|
||||
using GAS.Runtime;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_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
|
||||
{
|
||||
private const string GRP_BOX = "GRP_BOX";
|
||||
private const string GRP_BOX_CATCHER = "GRP_BOX/Catcher";
|
||||
|
||||
private ReleaseGameplayEffectMark _mark;
|
||||
|
||||
public static ReleaseGameplayEffectMarkEditor Create(ReleaseGameplayEffectMark mark)
|
||||
{
|
||||
var window = CreateInstance<ReleaseGameplayEffectMarkEditor>();
|
||||
var window = ScriptableObject.CreateInstance<ReleaseGameplayEffectMarkEditor>();
|
||||
window._mark = mark;
|
||||
window.UpdateMarkInfo();
|
||||
return window;
|
||||
@ -47,26 +49,44 @@ namespace GAS.Editor
|
||||
[OnValueChanged("OnCatcherChanged",true)]
|
||||
public TargetCatcherInspector Catcher;
|
||||
|
||||
// [Delayed]
|
||||
// [BoxGroup(GRP_BOX)]
|
||||
// [AssetSelector]
|
||||
// [ListDrawerSettings(Expanded = true, DraggableItems = true)]
|
||||
// [OnValueChanged("OnGameplayEffectListChanged")]
|
||||
// public List<GameplayEffectAsset> gameplayEffects;
|
||||
|
||||
[Delayed]
|
||||
[BoxGroup(GRP_BOX)]
|
||||
[AssetSelector]
|
||||
[ListDrawerSettings(ShowFoldout = true, DraggableItems = true)]
|
||||
[OnValueChanged("OnGameplayEffectListChanged")]
|
||||
public List<GameplayEffectAsset> gameplayEffects;
|
||||
[ListDrawerSettings(Expanded = true, DraggableItems = true)]
|
||||
[OnValueChanged("OnGameplayEffectAssetDataListChanged",true)]
|
||||
public List<GameplayEffectAssetData> gameplayEffects;
|
||||
|
||||
[BoxGroup(GRP_BOX)]
|
||||
[Button]
|
||||
[GUIColor(0.9f, 0.2f, 0.2f)]
|
||||
void Delete()
|
||||
{
|
||||
_mark.Delete();
|
||||
}
|
||||
|
||||
// [BoxGroup(GRP_BOX)]
|
||||
// [Button]
|
||||
// void Save()
|
||||
// {
|
||||
// // 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()
|
||||
{
|
||||
RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>";
|
||||
// gameplayEffects = _mark.MarkDataForSave.gameplayEffectAssets;
|
||||
gameplayEffects= _mark.MarkDataForSave.gameplayEffectAssets;
|
||||
|
||||
CatcherType = _mark.MarkDataForSave.jsonTargetCatcher.Type;
|
||||
RefreshCatcherInspector();
|
||||
}
|
||||
@ -74,7 +94,7 @@ namespace GAS.Editor
|
||||
void RefreshCatcherInspector()
|
||||
{
|
||||
// 根据选择的OngoingAbilityTask子类,显示对应的属性
|
||||
var catcher = _mark.MarkDataForSave.LoadTargetCatcher();
|
||||
var catcher = _mark.MarkDataForSave.TargetCatcher;
|
||||
if (TargetCatcherInspectorMap.TryGetValue(catcher.GetType(), out var inspectorType))
|
||||
{
|
||||
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 ;
|
||||
AbilityTimelineEditorWindow.Instance.Save();
|
||||
@ -98,13 +124,14 @@ namespace GAS.Editor
|
||||
{
|
||||
_mark.MarkDataForSave.jsonTargetCatcher.Type = CatcherType;
|
||||
_mark.MarkDataForSave.jsonTargetCatcher.Data = null;
|
||||
|
||||
AbilityTimelineEditorWindow.Instance.Save();
|
||||
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
|
||||
}
|
||||
|
||||
void OnCatcherChanged()
|
||||
{
|
||||
// _mark.MarkDataForSave.jsonTargetCatcher.Data = Catcher.ToJson();
|
||||
// _mark.MarkDataForSave.jsonTargetCatcher.Data = JsonUtility.ToJson(Catcher);
|
||||
// AbilityTimelineEditorWindow.Instance.Save();
|
||||
}
|
||||
|
||||
@ -133,13 +160,10 @@ namespace GAS.Editor
|
||||
if (_targetCatcherInspectorMap != null) return _targetCatcherInspectorMap;
|
||||
_targetCatcherInspectorMap = new Dictionary<Type, Type>();
|
||||
foreach (var catcherInspectorType in TargetCatcherInspectorTypes)
|
||||
{
|
||||
if (catcherInspectorType.BaseType != null)
|
||||
{
|
||||
var targetCatcherType = catcherInspectorType.BaseType.GetGenericArguments()[0];
|
||||
_targetCatcherInspectorMap.Add(targetCatcherType, catcherInspectorType);
|
||||
}
|
||||
}
|
||||
|
||||
return _targetCatcherInspectorMap;
|
||||
}
|
||||
@ -151,3 +175,4 @@ namespace GAS.Editor
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
@ -9,7 +9,7 @@ namespace GAS.Editor
|
||||
|
||||
public class ReleaseGameplayEffectTrack : TrackBase
|
||||
{
|
||||
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
public ReleaseGameplayEffectTrackData ReleaseGameplayEffectTrackData {
|
||||
get
|
||||
{
|
||||
@ -65,7 +65,7 @@ namespace GAS.Editor
|
||||
var markEvent = new ReleaseGameplayEffectMarkEvent
|
||||
{
|
||||
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x),
|
||||
gameplayEffectAssets = new List<GameplayEffectAsset>()
|
||||
gameplayEffectAssets = new List<GameplayEffectAssetData>()
|
||||
};
|
||||
ReleaseGameplayEffectTrackData.markEvents.Add(markEvent);
|
||||
|
||||
|
@ -37,9 +37,19 @@ namespace GAS.Editor
|
||||
foreach (var mark in _track.ReleaseGameplayEffectTrackData.markEvents)
|
||||
{
|
||||
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";
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ namespace GAS.Editor
|
||||
using UnityEngine.UIElements;
|
||||
public abstract class TrackMarkBase:TrackItemBase
|
||||
{
|
||||
protected static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
protected static TimelineAbilityAsset AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
|
||||
private static string MarkAssetGuid => "5a3b3360bcba29b4cac2875f518af19d";
|
||||
public float FrameUnitWidth { get;protected set; }
|
||||
public int StartFrameIndex=>markData.startFrame;
|
||||
|
@ -364,7 +364,7 @@ namespace GAS.Editor
|
||||
listView.showFoldoutHeader = true;
|
||||
listView.itemsSource = list;
|
||||
listView.selectionType = SelectionType.Single;
|
||||
listView.selectionChanged += onSelectionChanged;
|
||||
listView.onSelectionChange += onSelectionChanged;
|
||||
listView.itemsAdded += onItemsAdded;
|
||||
listView.itemsRemoved += onItemsRemoved;
|
||||
|
||||
|
@ -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
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16da9d76e7c940b5a560bb31dab5d588
|
||||
timeCreated: 1709458583
|
@ -1,3 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8378e98772454500b5c36d8f4c14862d
|
||||
timeCreated: 1729190435
|
||||
guid: 45bb062aed8542afab83194d24ee85c8
|
||||
timeCreated: 1717573129
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3432f172cdc3c04da96b7ea7ee26942
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0a57c5f1e00ea4a4299f302338ee43a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -3,8 +3,8 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GAS.Editor.General;
|
||||
using GAS.Editor.General.Validation;
|
||||
using GAS.General;
|
||||
using GAS.General.Validation;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.Utilities.Editor;
|
||||
using UnityEditor;
|
||||
@ -23,21 +23,28 @@ namespace GAS.Editor
|
||||
[HorizontalGroup("A/R", order: 1)]
|
||||
[DisplayAsString(TextAlignment.Left, FontSize = 18)]
|
||||
[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_EmptyName, InfoMessageType.Error, VisibleIf = "EmptyAttributeSetName")]
|
||||
public string Name = "Unnamed";
|
||||
|
||||
[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)]
|
||||
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
|
||||
[CustomContextMenu("排序", "@SortAttributeNames()")]
|
||||
[LabelText("Attributes")]
|
||||
[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/L", order: 0, Width = 50)]
|
||||
@ -100,20 +107,24 @@ namespace GAS.Editor
|
||||
[ListDrawerSettings(ShowFoldout = true,
|
||||
CustomAddFunction = "OnAddAttributeSet",
|
||||
CustomRemoveElementFunction = "OnRemoveElement",
|
||||
CustomRemoveIndexFunction = "OnRemoveIndex")]
|
||||
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
|
||||
[CustomContextMenu("排序", "@SortAttributeSetConfigs()")]
|
||||
CustomRemoveIndexFunction = "OnRemoveIndex", OnTitleBarGUI = "DrawAttributeSetConfigsButtons")]
|
||||
[Searchable]
|
||||
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()
|
||||
{
|
||||
AttributeSetConfig.ParentAsset = this;
|
||||
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(attributeChoices);
|
||||
AttributeSetConfig.SetAttributeChoices(asset?.AttributeNames);
|
||||
}
|
||||
|
||||
public void SaveAsset()
|
||||
|
@ -1,14 +1,13 @@
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using GAS.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
public static class AttributeSetClassGen
|
||||
{
|
||||
public static void Gen()
|
||||
@ -16,8 +15,7 @@ namespace GAS.Editor
|
||||
var attributeSetAsset = AttributeSetAsset.LoadOrCreate();
|
||||
|
||||
var attributeAsset = AttributeAsset.LoadOrCreate();
|
||||
var attributeNames =
|
||||
(from t in attributeAsset.attributes where !string.IsNullOrWhiteSpace(t.Name) select t.Name)
|
||||
var attributeNames = (from t in attributeAsset.attributes where !string.IsNullOrWhiteSpace(t.Name) select t.Name)
|
||||
.ToList();
|
||||
|
||||
// Check if AttributeSet contains attribute that is not defined in AttributeAsset
|
||||
@ -27,7 +25,8 @@ namespace GAS.Editor
|
||||
{
|
||||
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);
|
||||
EditorUtility.DisplayDialog("Error", msg, "OK");
|
||||
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);
|
||||
var filePath =
|
||||
$"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ATTRIBUTESET_LIB_CSHARP_SCRIPT_NAME}";
|
||||
GenerateAttributeCollection(attributeSetAsset.AttributeSetConfigs, attributeAsset, filePath);
|
||||
|
||||
Console.WriteLine($"Generated Code Script at path: {filePath}");
|
||||
GenerateAttributeCollection(attributeSetAsset.AttributeSetConfigs, filePath);
|
||||
}
|
||||
|
||||
private static void GenerateAttributeCollection(List<AttributeSetConfig> attributeSetConfigs,
|
||||
AttributeAsset attributeAsset, string filePath)
|
||||
private static void GenerateAttributeCollection(List<AttributeSetConfig> attributeSetConfigs, string filePath)
|
||||
{
|
||||
using var writer = new IndentedWriter(new StreamWriter(filePath));
|
||||
|
||||
@ -70,7 +53,7 @@ namespace GAS.Editor
|
||||
|
||||
writer.WriteLine("using System;");
|
||||
writer.WriteLine("using System.Collections.Generic;");
|
||||
writer.WriteLine("using JNGame.Math;");
|
||||
writer.WriteLine("using GAS.General;");
|
||||
|
||||
writer.WriteLine("");
|
||||
|
||||
@ -78,18 +61,16 @@ namespace GAS.Editor
|
||||
writer.WriteLine("{");
|
||||
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("{");
|
||||
writer.Indent++;
|
||||
{
|
||||
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;
|
||||
else writer.WriteLine("");
|
||||
|
||||
@ -97,15 +78,46 @@ namespace GAS.Editor
|
||||
writer.WriteLine($"#region {attributeName}");
|
||||
writer.WriteLine("");
|
||||
{
|
||||
writer.WriteLine($"/// <summary>{attributeAccessor.Comment}</summary>");
|
||||
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});");
|
||||
writer.WriteLine(
|
||||
$"private AttributeBase _{validAttrName} = new AttributeBase(\"AS_{validName}.{attributeName}\",\"AS_{validName}\", \"{attributeName}\");");
|
||||
|
||||
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 void SetBase{validAttrName}(LFloat value) => {validAttrName}.SetBaseValue(value);");
|
||||
writer.WriteLine($"public void SetMin{validAttrName}(LFloat value) => {validAttrName}.SetMinValue(value);");
|
||||
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 AttributeBase {validAttrName} => _{validAttrName};");
|
||||
|
||||
writer.WriteLine("");
|
||||
|
||||
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($"#endregion {attributeName}");
|
||||
@ -125,13 +137,13 @@ namespace GAS.Editor
|
||||
writer.WriteLine("{");
|
||||
writer.Indent++;
|
||||
{
|
||||
foreach (var attributeName in attributeSetConfig.AttributeNames)
|
||||
foreach (var attributeName in attributeSet.AttributeNames)
|
||||
{
|
||||
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
|
||||
writer.WriteLine($"case \"{validAttrName}\":");
|
||||
writer.Indent++;
|
||||
{
|
||||
writer.WriteLine($"return {validAttrName};");
|
||||
writer.WriteLine($"return _{validAttrName};");
|
||||
}
|
||||
writer.Indent--;
|
||||
}
|
||||
@ -153,7 +165,7 @@ namespace GAS.Editor
|
||||
writer.WriteLine("{");
|
||||
writer.Indent++;
|
||||
{
|
||||
foreach (var attributeName in attributeSetConfig.AttributeNames)
|
||||
foreach (var attributeName in attributeSet.AttributeNames)
|
||||
{
|
||||
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
|
||||
writer.WriteLine($"\"{validAttrName}\",");
|
||||
@ -168,10 +180,10 @@ namespace GAS.Editor
|
||||
writer.Indent++;
|
||||
{
|
||||
writer.WriteLine("_owner = owner;");
|
||||
foreach (var attributeName in attributeSetConfig.AttributeNames)
|
||||
foreach (var attributeName in attributeSet.AttributeNames)
|
||||
{
|
||||
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
|
||||
writer.WriteLine($"{validAttrName}.SetOwner(owner);");
|
||||
writer.WriteLine($"_{validAttrName}.SetOwner(owner);");
|
||||
}
|
||||
}
|
||||
writer.Indent--;
|
||||
@ -182,15 +194,16 @@ namespace GAS.Editor
|
||||
writer.WriteLine("{");
|
||||
writer.Indent++;
|
||||
{
|
||||
foreach (var attributeName in attributeSetConfig.AttributeNames)
|
||||
foreach (var attributeName in attributeSet.AttributeNames)
|
||||
{
|
||||
string validAttrName = EditorUtil.MakeValidIdentifier(attributeName);
|
||||
writer.WriteLine(
|
||||
$"public const string {attributeName} = \"AS_{validName}.{validAttrName}\";");
|
||||
writer.WriteLine($"public const string {attributeName} = \"AS_{validName}.{validAttrName}\";");
|
||||
}
|
||||
}
|
||||
writer.Indent--;
|
||||
writer.WriteLine("}");
|
||||
|
||||
writer.WriteLine($"public static readonly string SetName = \"AS_{validName}\";");
|
||||
}
|
||||
writer.Indent--;
|
||||
writer.WriteLine("}");
|
||||
@ -201,7 +214,8 @@ namespace GAS.Editor
|
||||
writer.WriteLine("{");
|
||||
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.Indent++;
|
||||
{
|
||||
@ -216,7 +230,8 @@ namespace GAS.Editor
|
||||
|
||||
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.Indent++;
|
||||
{
|
||||
@ -229,9 +244,32 @@ namespace GAS.Editor
|
||||
writer.Indent--;
|
||||
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("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.Indent++;
|
||||
{
|
||||
@ -245,14 +283,27 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.Indent--;
|
||||
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.WriteLine("}");
|
||||
}
|
||||
writer.Indent--;
|
||||
writer.Write("}");
|
||||
Console.WriteLine($"Generated Code Script at path: {filePath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,11 +1,14 @@
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GAS;
|
||||
using Editor;
|
||||
using Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
public class AttributeSetConfigEditorWindow : EditorWindow
|
||||
{
|
||||
private static List<string> _attributeOptions;
|
||||
@ -17,7 +20,7 @@ namespace GAS.Editor
|
||||
private Action<string, List<string>> _callback;
|
||||
private Func<AttributeSetConfig, bool> _checkAttributeSetValid;
|
||||
|
||||
private List<int> _selectedAttributeIndexes;
|
||||
private List<int> _selectedAttributeIndexs;
|
||||
|
||||
private GUIStyle BigFontLabelStyle;
|
||||
|
||||
@ -28,7 +31,7 @@ namespace GAS.Editor
|
||||
if (_attributeOptions == null)
|
||||
{
|
||||
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;
|
||||
@ -57,7 +60,7 @@ namespace GAS.Editor
|
||||
if (GUILayout.Button("Add Attribute", GUILayout.Width(100)))
|
||||
{
|
||||
attributeNames.Add("");
|
||||
_selectedAttributeIndexes.Add(-1);
|
||||
_selectedAttributeIndexs.Add(-1);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
@ -69,13 +72,13 @@ namespace GAS.Editor
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
EditorGUILayout.LabelField("Attribute:", GUILayout.Width(80));
|
||||
_selectedAttributeIndexes[i] = EditorGUILayout.Popup("", _selectedAttributeIndexes[i],
|
||||
_selectedAttributeIndexs[i] = EditorGUILayout.Popup("", _selectedAttributeIndexs[i],
|
||||
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)))
|
||||
{
|
||||
@ -101,9 +104,9 @@ namespace GAS.Editor
|
||||
_callback = callback;
|
||||
_checkAttributeSetValid = checkAttributeSetValid;
|
||||
this.attributeNames = attributeNames;
|
||||
_selectedAttributeIndexes = new List<int>();
|
||||
_selectedAttributeIndexs = new List<int>();
|
||||
foreach (var attributeName in attributeNames)
|
||||
_selectedAttributeIndexes.Add(AttributeOptions.IndexOf(attributeName));
|
||||
_selectedAttributeIndexs.Add(AttributeOptions.IndexOf(attributeName));
|
||||
|
||||
|
||||
BigFontLabelStyle = new GUIStyle(EditorStyles.label);
|
||||
@ -112,7 +115,7 @@ namespace GAS.Editor
|
||||
|
||||
private void Save()
|
||||
{
|
||||
var attributeSetConfig = new AttributeSetConfig()
|
||||
AttributeSetConfig attributeSetConfig = new AttributeSetConfig()
|
||||
{
|
||||
Name = editedName,
|
||||
AttributeNames = attributeNames
|
||||
@ -131,3 +134,4 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,48 +1,57 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GAS.Editor.General;
|
||||
using GAS.General;
|
||||
using GAS.General.Validation;
|
||||
using GAS.Runtime;
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using GAS.General;
|
||||
using Sirenix.Utilities.Editor;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
[FilePath(GasDefine.GAS_ATTRIBUTE_ASSET_PATH)]
|
||||
internal class AttributeAsset : ScriptableSingleton<AttributeAsset>
|
||||
public class AttributeAsset : ScriptableSingleton<AttributeAsset>
|
||||
{
|
||||
[BoxGroup("Warning", order: -1)]
|
||||
[HideLabel]
|
||||
[ShowIf("ExistDuplicatedAttribute")]
|
||||
[DisplayAsString(TextAlignment.Left, true)]
|
||||
[NonSerialized]
|
||||
[ShowInInspector]
|
||||
private string Warning_DuplicatedAttribute = "";
|
||||
public string Warning_DuplicatedAttribute = "";
|
||||
|
||||
[VerticalGroup("Attributes", order: 1)]
|
||||
[ListDrawerSettings(
|
||||
ShowFoldout = true,
|
||||
Expanded = true,
|
||||
CustomRemoveElementFunction = "OnRemoveElement",
|
||||
CustomRemoveIndexFunction = "OnRemoveIndex",
|
||||
CustomAddFunction = "OnAddAttribute",
|
||||
ShowPaging = false)]
|
||||
ShowPaging = false, OnTitleBarGUI = "DrawAttributeButtons")]
|
||||
[Searchable]
|
||||
[OnValueChanged("@SaveAsset()", true)]
|
||||
[OnCollectionChanged(after: "@SaveAsset()")]
|
||||
[CustomContextMenu("排序", "@SortAttributes()")]
|
||||
[SerializeField]
|
||||
public List<AttributeAccessor> attributes = new();
|
||||
[OnValueChanged("Save")]
|
||||
public List<AttributeAccessor> attributes = new List<AttributeAccessor>();
|
||||
|
||||
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)]
|
||||
[GUIColor(0, 0.9f, 0)]
|
||||
[Button(SdfIconType.Upload, GASTextDefine.BUTTON_GenerateAttributeCollection, ButtonHeight = 30, Expanded = true)]
|
||||
[ValidateInput("@ExistEmptyAttribute() == false", GASTextDefine.TIP_Warning_EmptyAttribute)]
|
||||
[Button(SdfIconType.Upload, GASTextDefine.BUTTON_GenerateAttributeCollection, ButtonHeight = 30,
|
||||
Expanded = true)]
|
||||
[InfoBox(GASTextDefine.TIP_Warning_EmptyAttribute, InfoMessageType.Error, VisibleIf = "ExistEmptyAttribute")]
|
||||
void GenCode()
|
||||
{
|
||||
if (ExistEmptyAttribute() || ExistDuplicatedAttribute())
|
||||
@ -95,27 +104,12 @@ namespace GAS.Editor
|
||||
|
||||
private void OnAddAttribute()
|
||||
{
|
||||
StringEditWindow.OpenWindow("创建新属性", null, newName =>
|
||||
AttributeEditorWindow.OpenWindow(new AttributeEditorWindow.Data(), AttributeNames, (d =>
|
||||
{
|
||||
var validateVariableName = Validations.ValidateVariableName(newName);
|
||||
|
||||
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 });
|
||||
attributes.Add(new AttributeAccessor(d.Name, d.Comment));
|
||||
SaveAsset();
|
||||
Debug.Log("[EX] Attribute Asset add element!");
|
||||
});
|
||||
}), "Add new Attribute");
|
||||
GUIUtility.ExitGUI(); // In order to solve: "EndLayoutGroup: BeginLayoutGroup must be called first."
|
||||
}
|
||||
|
||||
@ -145,85 +139,42 @@ namespace GAS.Editor
|
||||
}
|
||||
|
||||
[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)]
|
||||
[LabelText("属性名"), LabelWidth(LabelWidth)]
|
||||
[DelayedProperty]
|
||||
[ValidateInput("@Validations.IsValidVariableName($value)", "Attribute name is invalid!")]
|
||||
[PropertyOrder(1)]
|
||||
public string Name = "Unnamed";
|
||||
[HorizontalGroup("A")] [HorizontalGroup("A/R", order: 3)] [DisplayAsString] [HideLabel]
|
||||
public string Comment;
|
||||
|
||||
[FoldoutGroup("$DisplayName")]
|
||||
[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()
|
||||
public AttributeAccessor(string attributeName, string attributeComment = "")
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
[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;
|
||||
}, "Edit Attribute Asset");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,11 +1,13 @@
|
||||
using System;
|
||||
#if UNITY_EDITOR
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
public static class AttributeCollectionGen
|
||||
{
|
||||
private sealed class AttributeInfo
|
||||
@ -28,8 +30,6 @@ namespace GAS.Editor
|
||||
|
||||
|
||||
GenerateAttributeCollection(attributeInfos, filePath);
|
||||
|
||||
Console.WriteLine($"Generated GTagLib at path: {filePath}");
|
||||
}
|
||||
|
||||
private static void GenerateAttributeCollection(IEnumerable<AttributeInfo> attributes, string filePath)
|
||||
@ -63,7 +63,9 @@ namespace GAS.Editor
|
||||
else writer.WriteLine();
|
||||
|
||||
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}\";");
|
||||
names.Add(validName);
|
||||
}
|
||||
@ -71,13 +73,13 @@ namespace GAS.Editor
|
||||
writer.WriteLine("");
|
||||
|
||||
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.Indent++;
|
||||
{
|
||||
foreach (var name in names)
|
||||
{
|
||||
writer.WriteLine($"{name},");
|
||||
writer.WriteLine($"\"{name}\",");
|
||||
}
|
||||
}
|
||||
writer.Indent--;
|
||||
@ -88,6 +90,9 @@ namespace GAS.Editor
|
||||
}
|
||||
writer.Indent--;
|
||||
writer.Write("}");
|
||||
|
||||
Console.WriteLine($"Generated GTagLib at path: {filePath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bf0629a3e1f4cba9b8040ae65a6ea2b
|
||||
timeCreated: 1711509167
|
@ -23,42 +23,42 @@ namespace GAS.Editor
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
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());
|
||||
idx = Mathf.Clamp(idx, 0, _attributeOptions.Count - 1);
|
||||
_sourceModifier.AttributeName = _attributeOptions[idx];
|
||||
var nameSplit = _sourceModifier.AttributeName.Split('.');
|
||||
_sourceModifier.AttributeSetName = nameSplit[0];
|
||||
_sourceModifier.AttributeShortName = nameSplit[1];
|
||||
_sourceModifier.attributeName = _attributeOptions[idx];
|
||||
var nameSplit = _sourceModifier.attributeName.Split('.');
|
||||
_sourceModifier.attributeSetName = nameSplit[0];
|
||||
_sourceModifier.attributeShortName = nameSplit[1];
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Operation:", GUILayout.Width(100));
|
||||
_sourceModifier.Operation = (GEOperation)EditorGUILayout.EnumPopup("", _sourceModifier.Operation);
|
||||
_sourceModifier.operation = (EnumGEOperation)EditorGUILayout.EnumPopup("", _sourceModifier.operation);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
EditorGUILayout.LabelField("Value:", GUILayout.Width(100));
|
||||
_sourceModifier.ModiferMagnitude = EditorGUILayout.FloatField("", _sourceModifier.ModiferMagnitude).ToLFloat();
|
||||
_sourceModifier.modiferMagnitude = (LFloat)EditorGUILayout.FloatField("", _sourceModifier.modiferMagnitude);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
EditorGUILayout.BeginVertical();
|
||||
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);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
if (_sourceModifier.MMC != null)
|
||||
if (_sourceModifier.mmc != null)
|
||||
{
|
||||
EditorGUILayout.BeginVertical(GUI.skin.box);
|
||||
EditorGUILayout.LabelField("MMC Info:");
|
||||
var editor = UnityEditor.Editor.CreateEditor(_sourceModifier.MMC);
|
||||
var editor = UnityEditor.Editor.CreateEditor(_sourceModifier.mmc);
|
||||
editor.OnInspectorGUI();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
@ -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();
|
||||
// }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f4c5ffc9547f8b43842d4de7d67c406
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,16 +1,16 @@
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableObject
|
||||
{
|
||||
private static T s_Instance;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
@ -19,24 +19,21 @@ namespace GAS.Editor
|
||||
{
|
||||
LoadOrCreate();
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static T LoadOrCreate()
|
||||
{
|
||||
string filePath = GetFilePath();
|
||||
if (!string.IsNullOrEmpty(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
|
||||
{
|
||||
Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid");
|
||||
}
|
||||
|
||||
return s_Instance;
|
||||
}
|
||||
|
||||
@ -52,18 +49,11 @@ namespace GAS.Editor
|
||||
if (!string.IsNullOrEmpty(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))
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
Object[] obj = { s_Instance };
|
||||
UnityEngine.Object[] obj = new T[1] { s_Instance };
|
||||
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText);
|
||||
//Debug.Log($"Saved ScriptableSingleton to {filePath}");
|
||||
}
|
||||
@ -84,12 +74,10 @@ namespace GAS.Editor
|
||||
?.filepath;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class FilePathAttribute : Attribute
|
||||
{
|
||||
internal string filepath;
|
||||
|
||||
/// <summary>
|
||||
/// 单例存放路径
|
||||
/// </summary>
|
||||
@ -100,13 +88,12 @@ namespace GAS.Editor
|
||||
{
|
||||
throw new ArgumentException("Invalid relative path (it is empty)");
|
||||
}
|
||||
|
||||
if (path[0] == '/')
|
||||
{
|
||||
path = path[1..];
|
||||
path = path.Substring(1);
|
||||
}
|
||||
|
||||
filepath = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -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
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8ef92065c5ad0445827c5835accf697
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -29,8 +29,6 @@ namespace GAS.Editor
|
||||
writer.WriteLine("");
|
||||
|
||||
writer.WriteLine("using System;");
|
||||
writer.WriteLine("using System.Linq;");
|
||||
writer.WriteLine("using UnityEngine;");
|
||||
|
||||
writer.WriteLine("");
|
||||
|
||||
@ -76,31 +74,14 @@ namespace GAS.Editor
|
||||
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.Indent++;
|
||||
{
|
||||
writer.WriteLine("if (preset != null) asc.SetPreset(preset);");
|
||||
writer.WriteLine("if (asc.Preset == null) return;");
|
||||
|
||||
writer.WriteLine("");
|
||||
|
||||
writer.WriteLine("#if UNITY_EDITOR", true);
|
||||
writer.WriteLine("if (asc.Preset.BaseAbilities != null && asc.Preset.BaseAbilities.Any(x => x == null))");
|
||||
writer.WriteLine("{");
|
||||
writer.Indent++;
|
||||
{
|
||||
writer.WriteLine(
|
||||
"Debug.LogWarning($\"BaseAbilities contains null in preset: {asc.Preset.name}\");");
|
||||
}
|
||||
writer.Indent--;
|
||||
writer.WriteLine("}");
|
||||
writer.WriteLine("#endif", true);
|
||||
|
||||
writer.WriteLine("");
|
||||
|
||||
writer.WriteLine(
|
||||
"asc.Init(asc.PresetBaseTags(), asc.PresetAttributeSetTypes(), asc.Preset.BaseAbilities, level);");
|
||||
"asc.Init(asc.PresetBaseTags(), asc.PresetAttributeSetTypes(), asc.Preset.BaseAbilities, entityId, level);");
|
||||
}
|
||||
writer.Indent--;
|
||||
writer.WriteLine("}");
|
||||
|
@ -3,9 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using GAS.Editor.General;
|
||||
using GAS.General.Validation;
|
||||
using GAS.Editor.General.Validation;
|
||||
using GAS.Runtime;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
using Sirenix.Utilities;
|
||||
using Sirenix.Utilities.Editor;
|
||||
@ -50,27 +49,13 @@ namespace GAS.Editor
|
||||
"D- Ability System Component"
|
||||
};
|
||||
|
||||
private const string OpenWindow_MenuItemName = "EX-GAS/Asset Aggregator";
|
||||
#if EX_GAS_ENABLE_HOT_KEYS
|
||||
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName + " %F9";
|
||||
#else
|
||||
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName;
|
||||
#endif
|
||||
[MenuItem(OpenWindow_MenuItemNameEnh, priority = 1)]
|
||||
[MenuItem("EX-GAS/Asset Aggregator", priority = 1)]
|
||||
private static void OpenWindow()
|
||||
{
|
||||
CheckLibPaths();
|
||||
var window = GetWindow<GASAssetAggregator>();
|
||||
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1600, 900);
|
||||
window.MenuWidth = 240;
|
||||
}
|
||||
|
||||
private void ShowButton(Rect rect)
|
||||
{
|
||||
if (SirenixEditorGUI.SDFIconButton(rect, "GitHub", SdfIconType.Github))
|
||||
{
|
||||
Application.OpenURL("https://github.com/No78Vino/gameplay-ability-system-for-unity");
|
||||
}
|
||||
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1250, 625);
|
||||
window.MenuWidth = 220;
|
||||
}
|
||||
|
||||
private static void CheckLibPaths()
|
||||
@ -141,10 +126,6 @@ namespace GAS.Editor
|
||||
var directoryInfo = selected.Value is AbilityOverview
|
||||
? _directoryInfos[3]
|
||||
: selected.Value as DirectoryInfo;
|
||||
if (directoryInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (SirenixEditorGUI.ToolbarButton(new GUIContent("浏览")))
|
||||
{
|
||||
@ -154,13 +135,15 @@ namespace GAS.Editor
|
||||
if (SirenixEditorGUI.ToolbarButton(new GUIContent("新建子文件夹")))
|
||||
{
|
||||
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("新建")))
|
||||
{
|
||||
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)
|
||||
@ -168,7 +151,8 @@ namespace GAS.Editor
|
||||
if (SirenixEditorGUI.ToolbarButton(new GUIContent("删除")))
|
||||
{
|
||||
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 =>
|
||||
{
|
||||
var newPath = directoryInfo.Directory + "/" + s;
|
||||
var isExist = AssetDatabase.IsValidFolder(newPath);
|
||||
return isExist ? ValidationResult.Invalid("Folder already exists!") : ValidationResult.Valid;
|
||||
if (AssetDatabase.IsValidFolder(newPath))
|
||||
{
|
||||
return ValidationResult.Invalid("Folder already exists!");
|
||||
}
|
||||
|
||||
return ValidationResult.Valid;
|
||||
},
|
||||
s =>
|
||||
{
|
||||
@ -294,22 +282,13 @@ namespace GAS.Editor
|
||||
|
||||
private void RemoveAsset(ScriptableObject asset)
|
||||
{
|
||||
if (asset == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Warning", "The asset you want to delete is null", "Ok");
|
||||
return;
|
||||
}
|
||||
if (!EditorUtility.DisplayDialog("Warning", "Are you sure you want to delete this asset?", "Yes",
|
||||
"No")) return;
|
||||
|
||||
var assetName = asset.name; // Get the name before deleting
|
||||
var assetPath = AssetDatabase.GetAssetPath(asset);
|
||||
if (EditorUtility.DisplayDialog("Warning",
|
||||
$"Are you sure you want to delete this asset?\n\nName=\"{assetName}\"\nPath=\"{assetPath}\""
|
||||
, "Yes", "No"))
|
||||
{
|
||||
AssetDatabase.DeleteAsset(assetPath);
|
||||
var name = asset.name; // Get the name before deleting
|
||||
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(asset));
|
||||
Refresh();
|
||||
Debug.Log($"[EX] delete asset: Name=\"{assetName}\", Path=\"{assetPath}\"");
|
||||
}
|
||||
Debug.Log($"[EX] {name} asset deleted!");
|
||||
}
|
||||
|
||||
void OnMenuSelectionChange(SelectionChangedType selectionChangedType)
|
||||
|
@ -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
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d416e8b67924353b9ca08561ab6923e
|
||||
timeCreated: 1719801078
|
@ -1,10 +1,11 @@
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using Sirenix.OdinInspector.Editor;
|
||||
using Sirenix.Utilities;
|
||||
using Sirenix.Utilities.Editor;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
public class GASSettingAggregator : OdinMenuEditorWindow
|
||||
{
|
||||
private static GASSettingAsset _settingAsset;
|
||||
@ -51,17 +52,11 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
|
||||
private const string OpenWindow_MenuItemName = "EX-GAS/Settings";
|
||||
#if EX_GAS_ENABLE_HOT_KEYS
|
||||
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName + " %F12";
|
||||
#else
|
||||
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName;
|
||||
#endif
|
||||
[MenuItem(OpenWindow_MenuItemNameEnh, priority = 0)]
|
||||
[MenuItem("EX-GAS/Settings", priority = 0)]
|
||||
public static void OpenWindow()
|
||||
{
|
||||
var window = GetWindow<GASSettingAggregator>();
|
||||
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(1200, 600);
|
||||
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(900, 600);
|
||||
}
|
||||
|
||||
protected override OdinMenuTree BuildMenuTree()
|
||||
@ -87,3 +82,4 @@ namespace GAS.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,19 +1,21 @@
|
||||
using System;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using GAS;
|
||||
using GAS.General;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Editor
|
||||
{
|
||||
[FilePath(GasDefine.GAS_BASE_SETTING_PATH)]
|
||||
public class GASSettingAsset : ScriptableSingleton<GASSettingAsset>
|
||||
{
|
||||
private const int LABEL_WIDTH = 200;
|
||||
private const int SHORT_LABEL_WIDTH = 200;
|
||||
private static GASSettingAsset _setting;
|
||||
|
||||
|
||||
[Title(GASTextDefine.TITLE_SETTING,Bold = true)]
|
||||
[BoxGroup("A", false,order:1)]
|
||||
@ -30,24 +32,35 @@ namespace GAS.Editor
|
||||
[OnValueChanged("SaveAsset")]
|
||||
public string GASConfigAssetPath = "Assets/GAS/Config";
|
||||
|
||||
public static GASSettingAsset Setting
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_setting == null) _setting = LoadOrCreate();
|
||||
return _setting;
|
||||
}
|
||||
}
|
||||
[BoxGroup("A")]
|
||||
[LabelText("二进制配置文件生成路径")]
|
||||
[LabelWidth(LABEL_WIDTH)]
|
||||
[FolderPath]
|
||||
[OnValueChanged("SaveAsset")]
|
||||
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]
|
||||
[BoxGroup("V",false,order:0)]
|
||||
[HideLabel]
|
||||
[DisplayAsString(TextAlignment.Left, true)]
|
||||
private static string Version =>
|
||||
$"<size=15><b><color=white>EX-GAS Version: {GasDefine.GAS_VERSION}</color></b></size>";
|
||||
[HideLabel][DisplayAsString(TextAlignment.Left,true)]
|
||||
private static string Version => $"<size=15><b><color=white>EX-GAS Version: {GasDefine.GAS_VERSION}</color></b></size>";
|
||||
|
||||
public static string CodeGenPath => Setting.CodeGeneratePath;
|
||||
|
||||
public static string LogicCodeGenPath => Setting.LogicCodeGeneratePath;
|
||||
|
||||
[Title(GASTextDefine.TITLE_PATHS,Bold = true)]
|
||||
[PropertySpace(10)]
|
||||
@ -87,8 +100,7 @@ namespace GAS.Editor
|
||||
[BoxGroup("A")]
|
||||
[DisplayAsString(TextAlignment.Left,true)]
|
||||
[LabelWidth(SHORT_LABEL_WIDTH)]
|
||||
public static string AbilityTaskLib =>
|
||||
$"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ABILITY_TASK_LIBRARY_FOLDER}";
|
||||
public static string AbilityTaskLib => $"{Setting.GASConfigAssetPath}/{GasDefine.GAS_ABILITY_TASK_LIBRARY_FOLDER}";
|
||||
|
||||
[ShowInInspector]
|
||||
[BoxGroup("A")]
|
||||
@ -154,6 +166,7 @@ namespace GAS.Editor
|
||||
CheckPathFolderExist(GameplayCueLibPath);
|
||||
CheckPathFolderExist(MMCLibPath);
|
||||
CheckPathFolderExist(AbilityTaskLib);
|
||||
CheckPathFolderExist(LogicCodeGeneratePath);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
@ -167,7 +180,7 @@ namespace GAS.Editor
|
||||
{
|
||||
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
|
||||
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))
|
||||
{
|
||||
@ -185,36 +198,6 @@ namespace GAS.Editor
|
||||
UpdateAsset(this);
|
||||
Save();
|
||||
}
|
||||
|
||||
private const string EX_GAS_ENABLE_HOT_KEYS = "EX_GAS_ENABLE_HOT_KEYS";
|
||||
|
||||
#if EX_GAS_ENABLE_HOT_KEYS
|
||||
public const bool EnableHotKeys = true;
|
||||
#else
|
||||
public const bool EnableHotKeys = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[TabGroup("Advance", "Advance", SdfIconType.Gear, TextColor = "#FF7F00"), PropertyOrder(1)]
|
||||
[InfoBox(
|
||||
"@\"当前快捷键状态: \" + (EnableHotKeys ? \"启用\":\"禁用\") + \", 冲突时可禁用快捷键\"")]
|
||||
#if EX_GAS_ENABLE_HOT_KEYS
|
||||
[Button(SdfIconType.ToggleOn, "禁用快捷键")]
|
||||
#else
|
||||
[Button(SdfIconType.ToggleOff, "开启快捷键")]
|
||||
#endif
|
||||
private void ToggleScriptDefineSymbol_EX_GAS_ENABLE_HOT_KEYS()
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Ex-GAS",
|
||||
"切换快捷键状态\n将在你的项目中切换\"EX_GAS_ENABLE_HOT_KEYS\"宏定义\n\n这会重新编译你的代码, 之后你可能需要手动保存你的项目(请留意ProjectSettings.asset的变化).",
|
||||
"确定", "取消"))
|
||||
{
|
||||
#pragma warning disable 162
|
||||
if (EnableHotKeys)
|
||||
ScriptingDefineSymbolsHelper.Remove(EX_GAS_ENABLE_HOT_KEYS);
|
||||
else
|
||||
ScriptingDefineSymbolsHelper.Add(EX_GAS_ENABLE_HOT_KEYS);
|
||||
#pragma warning restore 162
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using JNGame.Game;
|
||||
using JNGame.GAS;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace GAS.Editor
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
@ -24,13 +28,10 @@ namespace GAS.Editor
|
||||
|
||||
private AbilitySystemComponent _selected;
|
||||
|
||||
[HideLabel]
|
||||
[DisplayAsString(TextAlignment.Center, true)]
|
||||
[HideLabel] [DisplayAsString(TextAlignment.Center, true)]
|
||||
public string windowTitle = "<size=18><b>EX Gameplay Ability System Watcher</b></size>";
|
||||
|
||||
[BoxGroup(BOXGROUP_TIPS)]
|
||||
[HideLabel]
|
||||
[DisplayAsString(TextAlignment.Left, true)]
|
||||
[BoxGroup(BOXGROUP_TIPS)] [HideLabel] [DisplayAsString(TextAlignment.Left, true)]
|
||||
public string tips = GASTextDefine.TIP_WATCHER;
|
||||
|
||||
[BoxGroup(BOXGROUP_TIPS_RUNNINGTIP, false)]
|
||||
@ -49,6 +50,16 @@ namespace GAS.Editor
|
||||
[ShowIf("IsPlaying")]
|
||||
public string Navis = "NAVI";
|
||||
|
||||
[HorizontalGroup(BOXGROUP_ASC_H)]
|
||||
[BoxGroup(BOXGROUP_ASC_H_R, false)]
|
||||
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
|
||||
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
|
||||
[Title("Object Mark", bold: true)]
|
||||
[DisplayAsString]
|
||||
[LabelWidth(75)]
|
||||
[ShowIf("IsPlaying")]
|
||||
public int ASC_Number;
|
||||
|
||||
[HorizontalGroup(BOXGROUP_ASC_H)]
|
||||
[BoxGroup(BOXGROUP_ASC_H_R, false)]
|
||||
[HorizontalGroup(BOXGROUP_ASC_H_R_A, PaddingRight = 0.01f)]
|
||||
@ -59,17 +70,21 @@ namespace GAS.Editor
|
||||
[ShowIf("IsPlaying")]
|
||||
public int IID;
|
||||
|
||||
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)]
|
||||
[ReadOnly]
|
||||
[LabelWidth(75)]
|
||||
[ShowIf("IsPlaying")]
|
||||
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)] [ReadOnly] [LabelWidth(75)] [ShowIf("IsPlaying")]
|
||||
public GameObject instance;
|
||||
|
||||
[VerticalGroup(BOXGROUP_ASC_H_R_A_V)] [DisplayAsString] [LabelWidth(75)] [ShowIf("IsPlaying")]
|
||||
public int Level;
|
||||
|
||||
[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("TargetID Mark", bold: true)]
|
||||
[DisplayAsString]
|
||||
[LabelWidth(75)]
|
||||
[ShowIf("IsPlaying")]
|
||||
public int Level;
|
||||
public int TargetIID;
|
||||
|
||||
[Space]
|
||||
[Title("Abilities", bold: true)]
|
||||
@ -80,7 +95,18 @@ namespace GAS.Editor
|
||||
[LabelText(" ")]
|
||||
[ShowIf("IsPlaying")]
|
||||
[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)]
|
||||
[VerticalGroup(BOXGROUP_ASC_H_R_A_VB)]
|
||||
@ -91,8 +117,17 @@ namespace GAS.Editor
|
||||
[LabelText(" ")]
|
||||
[ShowIf("IsPlaying")]
|
||||
[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)]
|
||||
[Title("GameplayEffects", bold: true)]
|
||||
@ -103,7 +138,7 @@ namespace GAS.Editor
|
||||
[InfoBox("format: [ActiveState][DurationInfo]GeName", InfoMessageType.None, "IsPlaying")]
|
||||
[ShowIf("IsPlaying")]
|
||||
[Searchable]
|
||||
public List<string> Effects = new List<string>();
|
||||
public List<string> Effects = new();
|
||||
|
||||
|
||||
[HorizontalGroup(BOXGROUP_ASC_H_R_A)]
|
||||
@ -114,7 +149,7 @@ namespace GAS.Editor
|
||||
[DisplayAsString]
|
||||
[ShowIf("IsPlaying")]
|
||||
[Searchable]
|
||||
public List<string> FixedTag = new List<string>();
|
||||
public List<string> FixedTag = new();
|
||||
|
||||
[Title(" ", bold: true)]
|
||||
[VerticalGroup(BOXGROUP_ASC_H_R_A_VC)]
|
||||
@ -123,7 +158,16 @@ namespace GAS.Editor
|
||||
[DisplayAsString]
|
||||
[ShowIf("IsPlaying")]
|
||||
[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;
|
||||
@ -134,25 +178,17 @@ namespace GAS.Editor
|
||||
{
|
||||
if (IsPlaying)
|
||||
{
|
||||
if (_selected == null || _selected.Id == 0)
|
||||
{
|
||||
_selected = JexGasManager.Editor.AbilitySystemComponents.Count > 0
|
||||
? JexGasManager.Editor.AbilitySystemComponents[0] as AbilitySystemComponent
|
||||
if (_selected == null || _selected.EntityId == 0)
|
||||
_selected = GameplayAbilitySystem.GAS.AbilitySystemComponents.Count > 0
|
||||
? GameplayAbilitySystem.GAS.AbilitySystemComponents[0] as AbilitySystemComponent
|
||||
: null;
|
||||
}
|
||||
|
||||
RefreshAscInfo();
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private const string OpenWindow_MenuItemName = "EX-GAS/Runtime Watcher";
|
||||
#if EX_GAS_ENABLE_HOT_KEYS
|
||||
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName + " %F11";
|
||||
#else
|
||||
private const string OpenWindow_MenuItemNameEnh = OpenWindow_MenuItemName;
|
||||
#endif
|
||||
[MenuItem(OpenWindow_MenuItemNameEnh, priority = 3)]
|
||||
[MenuItem("EX-GAS/Runtime Watcher", priority = 3)]
|
||||
private static void OpenWindow()
|
||||
{
|
||||
var window = GetWindow<GASWatcher>();
|
||||
@ -160,16 +196,16 @@ namespace GAS.Editor
|
||||
window.Show();
|
||||
}
|
||||
|
||||
void OnDrawNavi()
|
||||
private void OnDrawNavi()
|
||||
{
|
||||
if (!IsPlaying) return;
|
||||
|
||||
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 presetName = asc.Preset != null ? asc.Preset.name : "NoPreset";
|
||||
if (GUILayout.Button($"{presetName}#{asc.Id}"))
|
||||
var presetName = asc.Preset != null ? asc.Preset.Name : "NoPreset";
|
||||
if (GUILayout.Button($"{presetName}#{asc.EntityId}"))
|
||||
{
|
||||
_selected = asc;
|
||||
RefreshAscInfo();
|
||||
@ -183,44 +219,57 @@ namespace GAS.Editor
|
||||
{
|
||||
if (_selected == null)
|
||||
{
|
||||
ASC_Number = -1;
|
||||
IID = 0;
|
||||
instance = null;
|
||||
Level = 0;
|
||||
|
||||
TargetIID = 0;
|
||||
|
||||
Abilities.Clear();
|
||||
OutsideAttributes.Clear();
|
||||
Attributes.Clear();
|
||||
FinalAttributes.Clear();
|
||||
Effects.Clear();
|
||||
FixedTag.Clear();
|
||||
DynamicTag.Clear();
|
||||
DamageRecorder.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
IID = _selected.Id;
|
||||
// instance = _selected.GetView().gameObject;
|
||||
ASC_Number = _selected.SelfNumber;
|
||||
IID = _selected.EntityId;
|
||||
instance = GameObject.Find(_selected.EntityName);
|
||||
Level = _selected.Level;
|
||||
|
||||
|
||||
RefreshAbilityInfo();
|
||||
RefreshAttributesInfo();
|
||||
RefreshGameplayEffectsInfo();
|
||||
RefreshTagsInfo();
|
||||
RefreshDamageInfo();
|
||||
}
|
||||
|
||||
|
||||
private void RefreshGameplayEffectsInfo()
|
||||
{
|
||||
Effects.Clear();
|
||||
foreach (var ge in _selected.GameplayEffectContainer.GameplayEffects())
|
||||
var list = _selected.GameplayEffectContainer.FetchGameplayEffects();
|
||||
foreach (var ge in list)
|
||||
{
|
||||
string isActive = ge.IsActive ? "√" : "×";
|
||||
string durationStr = ge.DurationPolicy switch
|
||||
var isActive = ge.IsActive ? "√" : "×";
|
||||
var durationStr = ge.DurationPolicy switch
|
||||
{
|
||||
EffectsDurationPolicy.Duration => $"{ge.DurationRemaining():N2}/{ge.Duration:N2}(s)",
|
||||
EffectsDurationPolicy.Infinite => "∞",
|
||||
EffectsDurationPolicy.Instant => "N/A",
|
||||
_ => "Unknown"
|
||||
};
|
||||
var stackCountText = ge.Stacking.stackingType != StackingType.None ? $"[S:{ge.StackCount}]" : "";
|
||||
var stackCountText = ge.Stacking.stackingType != EnumStackingType.None ? $"[S:{ge.StackCount}]" : "";
|
||||
Effects.Add($"[{isActive}][{durationStr}]{stackCountText}{ge.GameplayEffect.GameplayEffectName}");
|
||||
}
|
||||
|
||||
_selected.GameplayEffectContainer.ReturnGameplayEffects(list);
|
||||
}
|
||||
|
||||
private void RefreshAbilityInfo()
|
||||
@ -228,7 +277,7 @@ namespace GAS.Editor
|
||||
Abilities.Clear();
|
||||
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}");
|
||||
}
|
||||
}
|
||||
@ -243,7 +292,7 @@ namespace GAS.Editor
|
||||
{
|
||||
var attr = attributeSet[attributeName];
|
||||
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();
|
||||
}
|
||||
|
||||
void RefreshFixedTagsInfo()
|
||||
private void RefreshFixedTagsInfo()
|
||||
{
|
||||
FixedTag.Clear();
|
||||
foreach (var tag in _selected.GameplayTagAggregator.FixedTags)
|
||||
{
|
||||
FixedTag.Add(tag.Name);
|
||||
}
|
||||
foreach (var tag in _selected.GameplayTagAggregator.FixedTags) FixedTag.Add(tag.Name);
|
||||
}
|
||||
|
||||
void RefreshDynamicTagsInfo()
|
||||
private void RefreshDynamicTagsInfo()
|
||||
{
|
||||
DynamicTag.Clear();
|
||||
foreach (var kv in _selected.GameplayTagAggregator.DynamicAddedTags)
|
||||
@ -272,24 +318,36 @@ namespace GAS.Editor
|
||||
DynamicTag.Add($"{tagName} ↓ ");
|
||||
|
||||
foreach (var obj in kv.Value)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case GameplayEffectSpec spec:
|
||||
{
|
||||
DynamicTag.Add(
|
||||
$" - From: {spec.Owner.Id}'s GE: {spec.GameplayEffect.GameplayEffectName}");
|
||||
$" - From: {spec.Owner.EntityId}'s GE: {spec.GameplayEffect.GameplayEffectName}");
|
||||
break;
|
||||
}
|
||||
case AbilitySpec ability:
|
||||
{
|
||||
DynamicTag.Add(
|
||||
$" - From: {ability.Owner.Id}'s Ability: {ability.Ability.Name}");
|
||||
$" - From: {ability.Owner.EntityId}'s Ability: {ability.Ability.Name}");
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fb990bd3236241498ff0493651b7bcf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -32,24 +32,13 @@ namespace GAS.Editor
|
||||
_streamWriter.Write(GetIndentation() + text);
|
||||
}
|
||||
|
||||
public void WriteLine(string line = null, bool ignoreIndent = false)
|
||||
public void WriteLine(string line = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
_streamWriter.WriteLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ignoreIndent)
|
||||
{
|
||||
_streamWriter.WriteLine(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
_streamWriter.WriteLine(GetIndentation() + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9055976068442439bd3318d8b42a345
|
||||
timeCreated: 1718347417
|
@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using GAS.General.Validation;
|
||||
using GAS.Editor.General.Validation;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
@ -1,8 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b84e44e848a2cc54a9f86e88d1219512
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
guid: 8207bd0f7b514c9a9a29ef37d242fd70
|
||||
timeCreated: 1729568975
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69be32fe4d27dde489209c5885c1e5dc
|
||||
guid: 1fd36c55067263b4e8a57d66895368a6
|
||||
timeCreated: 1472024155
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd65e8f324e17a344a97ddcf5a8d89d2
|
||||
guid: 57988073b2c6d944dbba8856bcae1b6d
|
||||
timeCreated: 1471616285
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
|
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f9fab1cf2636a6439c644bf08108abb
|
||||
guid: ddf437cbf1378614fb3fb17f4ba85426
|
||||
timeCreated: 1472122507
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
|
@ -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
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 69be32fe4d27dde489209c5885c1e5dc
|
||||
timeCreated: 1472024155
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd65e8f324e17a344a97ddcf5a8d89d2
|
||||
timeCreated: 1471616285
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f9fab1cf2636a6439c644bf08108abb
|
||||
timeCreated: 1472122507
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51efeef0278341a9ab883d49a20bd485
|
||||
guid: 16f67068a31c73648b2ef2baeb91a326
|
||||
timeCreated: 1711514345
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 688920bc1afba114290b8e45101a8054
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fdc093094756c434781a2996f811416d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user