更新Odin插件 更新到 3.3.1 版本 增加 EX- GAS 插件

This commit is contained in:
SnowShow 2025-03-26 12:05:14 +08:00
parent 525648b072
commit d517dda6e2
549 changed files with 26236 additions and 524 deletions

8
EintooAR/Assets/GAS.meta Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,239 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.8] - 2024-07-30
进行了一系列的优化。From: BCC @kenkinky
### Changed
- 进行了一系列的优化
## [1.1.6] - 2024-06-26
修复了AbilitySpec中CheckCost时modifier为减法时的计算错误追加了Attribute的钳制功能。
### Changed
- 追加了Attribute的钳制功能From: BCC @kenkinky
### Fixed
- 修复了AbilitySpec中CheckCost时modifier为减法时的计算错误。
## [1.1.6] - 2024-06-26
修复了由于优化GE创建流程时导致的Granted Ability生成错误优化了period的边界问题
### Changed
- 优化了period的边界问题From: BCC @kenkinky
### Fixed
- 修复了由于优化GE创建流程时导致的Granted Ability生成错误。
## [1.1.5] - 2024-06-19
修复了AttrBasedMMC的快照读取错误Modifier新增了减法除法操作类型。
### Changed
- Modifier新增了减法除法操作类型。From: BCC @kenkinky
### Fixed
- 修复了AttrBasedMMC的快照读取错误。
## [1.1.4] - 2024-06-14
重新整理了ASC的ApplyGameplayEffect方法的逻辑,现在GE的Tag相关判断是在实例化之后。允许用户在GameplayEffectSpec生效前对GE进行修改和操作。
### Changed
- 重新整理了ASC的ApplyGameplayEffectTo(GameplayEffect gameplayEffect, AbilitySystemComponent target)方法的逻辑
- ASC新增ApplyGameplayEffectTo(GameplayEffectSpec gameplayEffectSpec, AbilitySystemComponent target) 和
ApplyGameplayEffectToSelf(GameplayEffectSpec gameplayEffectSpec)
## [1.1.3] - 2024-06-13
添加了带level形参的ApplyGE方法。
### Changed
- 添加了带level形参的ApplyGE方法。
## [1.1.2] - 2024-06-12
修复了部分bug。编辑器界面部分优化
### Changed
- 编辑器界面部分优化From: BCC @kenkinky
### Fixed
- 修复了时间轴能力的durational cue重复调用OnRemove()的错误
- 修复了时间轴编辑器的TargetCatcher的Inspector不更新的错误
## [1.1.1] - 2024-05-31
补充了Stacking相关功能。
### Changed
- 添加Stack相关MMC
- 补充stack刷新计算current value逻辑
- 添加stack count变化监听事件
### Fixed
- 修复了Attribute Aggregator的事件注册逻辑错误。
## [1.1.0] - 2024-05-30
补充了Granted Ability和GameplayEffect Stacking两个功能优化了部分GC优化了编辑器界面操作等修复了部分bug。
### Changed
- 补充了Granted Ability详情可见README文档的2.8.c
- 补充了GameplayEffect Stacking详情可见README文档的2.7中Stacking部分
- 优化了部分GC。From: BCC @kenkinky
- 优化了部分执行逻辑,增强了代码可读性。
### Fixed
- 修复了部分逻辑bug。From: BCC @kenkinky
## [1.0.9] - 2024-04-25
优化type查找优化GAS的项目级配置文件管理。
### Changed
- 新增TimelineAbilityT, 方便继承和扩展TimelineAbility.From: BCC @kenkinky
### Fixed
- 修正TryAddDynamicAddedTag添加不同类型Source时类型转换失败异常From: BCC @kenkinky
- 修复了Setting中生成配置目录后未调用AssetDatabase.Refresh()导致配置文件目录未及时更新的问题。
## [1.0.8] - 2024-04-23
优化了部分GC。
### Fixed
- AttributeSetContainer的TryGetAttributeSet方法中Type.Name存在GC。
- 新增了预缓存接口:GasCache.CacheAttributeSetName。
- 使用方法在GAS初始化时调用GasCache.CacheAttributeSetName(GAttrSetLib.TypeToName);
- GameplayTagAggregator的Tag判断相关方法存在GC。GC来源是LINQ表达式的过程匿名方法产生的GC。已经把LINQ表达式改成了普通循环做法。
- 新增了Pool工具类优化了部分GC。From: BCC @kenkinky
## [1.0.7] - 2024-04-17
修复全局配置保存失败问题修复Editor代码不该编译问题
### Fixed
- 修复全局配置保存失败问题TagAttributeAttributeSetSetting的配置文件保存不该使用AssetDataBase。
- 修正无法打包编译异常 #11 From: BCC @kenkinky
## [1.0.6] - 2024-04-16
优化type查找优化GAS的项目级配置文件管理。
### Changed
- 修改了TagAttributeAttributeSetSetting的配置文件路径调整至ProjectSettings并且为单例配置文件。
- 优化了TypeUtilEditor环境下类型查找范围改为全程序集。
### Fixed
- 修复一个严重bug: 修复AttributeBasedModCalculation不能正确保存的问题, 还有一些小优化.From: BCC @kenkinky
## [1.0.5] - 2024-04-12
修复了部分bug优化编辑器操作。
### Added
- 优化编辑器操作。From: BCC @kenkinky
### Fixed
- 修复了TryActivateAbility的返回值逻辑错误。
## [1.0.4] - 2024-04-11
修复了部分bug测试通过了推导属性设计优化了GE容器的管理增强代码可读性。
### Added
- 添加了GAS内部的子Event系统为方便之后用上事件系统做准备。
### Fixed
- 推导属性的实时更新错误。补上了AttributeBasedMMC的Track类修改器属性变化监听。
- 修复GASHost销毁时的错误逻辑Host的静态单例改为饿汉式同步GAS的初始化只会执行一次。
### Changed
- 优化GameplayEffectContainer结构现在只维护一个GameplayEffect列表
### Removed
- 移除DerivedAttribute和MetaAttribute脚本弃用。这两个属性式设计方式而不是实际存在的类。
## [1.0.3] - 2024-04-09
删除SetByCallerModCalculation,弃用。
### Removed
- 删除SetByCallerModCalculation,弃用。
## [1.0.2] - 2024-04-08
优化Editor使用体验From: BCC @kenkinky
### Changed
- 优化Editor使用体验From: BCC @kenkinky
## [1.0.1] - 2024-03-29
删除Instant类型GameplayCue的Apply Target参数。
### Removed
- Instant类型GameplayCue的Apply Target弃用。
## [1.0.0] - 2024-03-13
EX-GAS 1.0.0 发布
### Added
### Fixed
- none
### Changed
- none
### Removed
- none

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 34ae97a0d741c5844a4fac12ff8e1c45
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ceb70b71a95f4810bb22c72560de1755
timeCreated: 1701928918

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 916ae4e58ce74ba2b0686ddc372419d9
timeCreated: 1703772158

View File

@ -0,0 +1,135 @@
using System;
using System.IO;
using System.Linq;
using GAS.Runtime;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor
{
public class AbilityCollectionGenerator
{
public static void Gen()
{
string pathWithoutAssets = Application.dataPath.Substring(0, Application.dataPath.Length - 6);
var filePath =
$"{pathWithoutAssets}/{GASSettingAsset.CodeGenPath}/{GasDefine.GAS_ABILITY_LIB_CSHARP_SCRIPT_NAME}";
GenerateAbilityCollection(filePath);
}
private static void GenerateAbilityCollection(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 GAbilityLib");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public struct AbilityInfo");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine("public string Name;");
writer.WriteLine("public string AssetPath;");
writer.WriteLine("public Type AbilityClassType;");
//writer.WriteLine("public AbilityAsset Asset()");
// writer.WriteLine("{");
// writer.Indent++;
// {
// string loadAbilityAssetCode = string.Format(loadMethodCodeString, "AssetPath");
// writer.WriteLine($"return {loadAbilityAssetCode};");
// }
// writer.Indent--;
// writer.WriteLine("}");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine("");
var abilityAssets = EditorUtil
.FindAssetsByType<AbilityAsset>(GASSettingAsset.GameplayAbilityLibPath)
.OrderBy(x => x.UniqueName)
.ThenBy(x => x.name)
.ToArray();
foreach (var ability in abilityAssets)
{
var path = AssetDatabase.GetAssetPath(ability);
#if true
writer.WriteLine(
$"public static AbilityInfo {ability.UniqueName} = " +
$"new AbilityInfo {{ " +
$"Name = \"{ability.UniqueName}\", " +
$"AssetPath = \"{path}\"," +
$"AbilityClassType = typeof({ability.InstanceAbilityClassFullName}) }};");
#else
writer.WriteLine($"public static AbilityInfo {ability.UniqueName} = new AbilityInfo");
writer.WriteLine("{");
writer.Indent++;
{
writer.WriteLine($"Name = \"{ability.UniqueName}\",");
writer.WriteLine($"AssetPath = \"{path}\",");
writer.WriteLine($"AbilityClassType = typeof({ability.InstanceAbilityClassFullName})");
}
writer.Indent--;
writer.WriteLine("};");
#endif
// writer.WriteLine($"private static {ability.InstanceAbilityClassFullName} _{validName};");
// writer.WriteLine($"public static {ability.InstanceAbilityClassFullName} {validName}()");
// writer.WriteLine("{");
// writer.Indent++;
// {
// writer.WriteLine($"if (_{validName} == null) _{validName} = new {ability.InstanceAbilityClassFullName}({validName}_Info.Asset());");
// writer.Indent++;
// {
// writer.WriteLine($"return _{validName};");
// }
// writer.Indent--;
// }
// writer.Indent--;
// writer.WriteLine("}");
writer.WriteLine("");
}
writer.WriteLine("");
writer.WriteLine(
"public static Dictionary<string, AbilityInfo> AbilityMap = new Dictionary<string, AbilityInfo>");
writer.WriteLine("{");
writer.Indent++;
{
foreach (var ability in abilityAssets)
{
writer.WriteLine($"[\"{ability.UniqueName}\"] = {ability.UniqueName},");
}
}
writer.Indent--;
writer.WriteLine("};");
}
writer.Indent--;
writer.WriteLine("}");
}
writer.Indent--;
writer.Write("}");
Console.WriteLine($"Generated GTagLib at path: {filePath}");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 499ab276139746f595dbcfc5fc935f68
timeCreated: 1705071386

View File

@ -0,0 +1,37 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System.Collections.Generic;
using System;
using System.Reflection;
using Runtime;
public static class AbilityEditorUtil
{
public static List<string> GetAbilityClassNames()
{
var classNames = new List<string>();
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.IsSubclassOf(typeof(AbstractAbility)))
{
classNames.Add(type.FullName);
}
}
}
catch (ReflectionTypeLoadException)
{
continue;
}
}
return classNames;
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3f6afbd9c3ac4e958729c82c6ef46146
timeCreated: 1704263269

View File

@ -0,0 +1,141 @@
using System.Collections.Generic;
using System.Linq;
using GAS.General.Validation;
using GAS.Runtime;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor
{
public class AbilityOverview
{
[BoxGroup("Warning", order: -1)]
[HideLabel]
[ShowIf("ExistAbilityWithEmptyUniqueName")]
[DisplayAsString(TextAlignment.Left, true)]
public string Warning_AbilityUniqueNameIsNull =
"<size=13><color=yellow>The <color=orange>Unique Name</color> of the ability must not be <color=red><b>EMPTY</b></color>! " +
"Please check!</color></size>";
[BoxGroup("Warning", order: -1)]
[HideLabel]
[ShowIf("ExistAbilityWithDuplicatedUniqueName")]
[DisplayAsString(TextAlignment.Left, true)]
public string Warning_AbilityUniqueNameRepeat =
"<size=13><color=yellow>The <color=orange>Unique Name</color> of the ability must not be <color=red><b>DUPLICATED</b></color>! " +
"The duplicated abilities are as follows:<color=white> Move,Attack </color>.</color></size>";
[VerticalGroup("Abilities", order: 1)]
[ListDrawerSettings(ShowFoldout = true, ShowIndexLabels = false, ShowItemCount = true, IsReadOnly = true)]
[DisplayAsString]
public List<string> Abilities = new List<string>();
public AbilityOverview()
{
Refresh();
}
[HorizontalGroup("Buttons", order: 0, MarginRight = 0.2f)]
[GUIColor(0, 0.9f, 0.1f, 1)]
[Button("Generate Ability Collection", ButtonSizes.Large, ButtonStyle.Box, Expanded = true)]
void GenerateAbilityCollection()
{
if (ExistAbilityWithEmptyUniqueName() || ExistAbilityWithDuplicatedUniqueName())
{
EditorUtility.DisplayDialog("Warning", "Please check the warning message!\n" +
"Fix the Unique Name Error!\n" +
"(If you have fixed all and the warning still exist," +
" try to refresh the abilities with the REFRESH button.)", "OK");
return;
}
AbilityCollectionGenerator.Gen();
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()
{
_showDetail = !_showDetail;
Refresh();
}
[HorizontalGroup("Buttons", width: 50)]
[GUIColor(1, 1f, 0)]
[Button(SdfIconType.ArrowRepeat, "", ButtonHeight = 30)]
[HideLabel]
public void Refresh()
{
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 =>
{
var text = Validations.ValidateVariableName(ability.UniqueName).IsValid
? ability.UniqueName
: $"{ability.UniqueName}(非法UniqueName)";
if (_showDetail)
{
text += $" - asset: {ability.name}, type: {ability.GetType().FullName}";
}
return text;
}).ToList();
}
bool ExistAbilityWithEmptyUniqueName()
{
bool existEmpty = Abilities.Exists(string.IsNullOrEmpty);
return existEmpty;
}
bool ExistAbilityWithDuplicatedUniqueName()
{
var duplicateStrings = FindDuplicateStrings(Abilities);
bool existDuplicated = duplicateStrings.Length > 0;
if (existDuplicated)
{
string duplicatedUniqueName = duplicateStrings.Aggregate("", (current, d) => current + (d + ","));
duplicatedUniqueName = duplicatedUniqueName.Remove(duplicatedUniqueName.Length - 1, 1);
Warning_AbilityUniqueNameRepeat =
"<size=13><color=yellow>The <color=orange>Unique Name</color> of the ability must not be <color=red><b>DUPLICATED</b></color>! " +
$"The duplicated abilities are as follows: \n <size=15><b><color=white> {duplicatedUniqueName} </color></b></size>.</color></size>";
}
return existDuplicated;
}
static string[] FindDuplicateStrings(IEnumerable<string> names)
{
var duplicates = names
.Where(name => !string.IsNullOrEmpty(name))
.GroupBy(name => name)
.Where(group => group.Count() > 1)
.Select(group => group.Key)
.ToArray();
return duplicates;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3209fb75be944c36b4f2b9d347aee120
timeCreated: 1705290851

View File

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

View File

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

View File

@ -0,0 +1,17 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements">
<ui:VisualElement name="RightContent" style="flex-grow: 1; height: 100%; max-height: 100%; min-height: 100%; flex-shrink: 0;">
<GAS.Editor.General.VisualElement.SplitView orientation="Horizontal" usage-hints="None" fixed-pane-initial-dimension="200" fixed-pane-index="1" style="position: relative; left: 0; top: 0; flex-direction: column; width: 100%; max-width: 100%; min-width: 100%; height: 100%; max-height: 100%; min-height: 100%;">
<ui:VisualElement name="RightTimeline" style="flex-grow: 1; padding-right: 0; width: 500px; flex-shrink: 0; height: 100%; max-height: 100%; min-height: 100%; min-width: auto;">
<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: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>
<ui:IMGUIContainer name="SelectLine" style="position: absolute; left: 0;" />
<ui:IMGUIContainer name="FinishLine" style="position: absolute; left: 0;" />
<ui:IMGUIContainer name="DottedLine" style="position: absolute; left: 0;" />
<ui:IMGUIContainer name="DragItemPreview" style="position: absolute; left: 0; top: 0;" />
</ui:VisualElement>
<ui:VisualElement name="ClipInspector" style="flex-grow: 0; width: 300px; min-width: 100px; border-left-color: rgb(120, 120, 120); border-right-color: rgb(120, 120, 120); border-top-color: rgb(120, 120, 120); border-bottom-color: rgb(120, 120, 120); border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; max-width: none; align-self: auto; align-items: flex-start; flex-shrink: 1; height: 100%; max-height: 100%; min-height: 100%;" />
</GAS.Editor.General.VisualElement.SplitView>
</ui:VisualElement>
</ui:UXML>

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 3f6767c7cb94e1940bb9595b5fba5d45
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@ -0,0 +1,15 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using GAS.General;
public class AbilityTimelineEditorConfig
{
public int FrameUnitWidth = 10;
public const int StandardFrameUnitWidth = 1;
public const int MaxFrameUnitLevel= 20;
public const float MinTimerShaftFrameDrawStep = 5;
public int DefaultFrameRate => GASTimer.FrameRate;
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 51b96cc42da3453c9db1934582d0a8c2
timeCreated: 1708482904

View File

@ -0,0 +1,397 @@
using System;
using GAS.Runtime;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using Object = UnityEngine.Object;
namespace GAS.Editor
{
/// <summary>
/// 这个类被反射引用到, 重构请小心!!
/// </summary>
public class AbilityTimelineEditorWindow : EditorWindow
{
[SerializeField]
private VisualTreeAsset m_VisualTreeAsset;
private VisualElement _root;
public static AbilityTimelineEditorWindow Instance { get; private set; }
public TimelineTrackView TrackView { get; private set; }
public TimelineInspector TimelineInspector { get; private set; }
private static EditorWindow _childInspector;
public void CreateGUI()
{
Instance = this;
_root = rootVisualElement;
// Instantiate UXML
VisualElement labelFromUxml = m_VisualTreeAsset.Instantiate();
_root.Add(labelFromUxml);
InitAbilityAssetBar();
InitTopBar();
InitController();
TimerShaftView = new TimerShaftView(_root);
TrackView = new TimelineTrackView(_root);
TimelineInspector = new TimelineInspector(_root);
}
/// <summary>
/// 这个方法被反射引用到, 重构请小心!!
/// </summary>
public static void ShowWindow(TimelineAbilityAssetBase asset)
{
var wnd = GetWindow<AbilityTimelineEditorWindow>();
wnd.titleContent = new GUIContent("AbilityTimelineEditorWindow");
wnd.InitAbility(asset);
// 打开子Inspector
EditorApplication.delayCall += () => wnd.ShowChildInspector();
}
public void Save()
{
AbilityAsset.Save();
}
private void InitAbility(TimelineAbilityAssetBase asset)
{
_abilityAsset.value = asset;
MaxFrame.value = AbilityAsset.FrameCount;
CurrentSelectFrameIndex = 0;
TimerShaftView.RefreshTimerDraw();
TrackView.RefreshTrackDraw();
}
private void SaveAsset()
{
EditorUtility.SetDirty(AbilityAsset);
AssetDatabase.SaveAssetIfDirty(AbilityAsset);
}
#region Config
public AbilityTimelineEditorConfig Config { get; } = new();
private ObjectField _abilityAsset;
private Button _btnShowAbilityAssetDetail;
public TimelineAbilityAssetBase AbilityAsset => _abilityAsset.value as TimelineAbilityAssetBase;
// private TimelineAbilityEditorWindow AbilityAssetEditor => AbilityAsset != null
// ? UnityEditor.Editor.CreateEditor(AbilityAsset) as TimelineAbilityEditorWindow
// : null;
private void InitAbilityAssetBar()
{
_abilityAsset = _root.Q<ObjectField>("SequentialAbilityAsset");
_abilityAsset.RegisterValueChangedCallback(OnSequentialAbilityAssetChanged);
_btnShowAbilityAssetDetail = _root.Q<Button>("BtnShowAbilityAssetDetail");
_btnShowAbilityAssetDetail.clickable.clicked += ShowAbilityAssetDetail;
}
private void OnSequentialAbilityAssetChanged(ChangeEvent<Object> evt)
{
if (AbilityAsset != null)
{
MaxFrame.value = AbilityAsset.FrameCount;
}
else
{
Selection.activeObject = null;
}
CurrentSelectFrameIndex = 0;
TimerShaftView.RefreshTimerDraw();
TrackView.RefreshTrackDraw();
}
private void ShowAbilityAssetDetail()
{
if (AbilityAsset == null) return;
Selection.activeObject = AbilityAsset;
}
#endregion
#region TopBar
private string _previousScenePath;
private Button BtnLoadPreviewScene;
private Button BtnBackToScene;
private Button BtnChildInspector;
private ObjectField _previewObjectField;
public GameObject PreviewObject => _previewObjectField.value as GameObject;
private void InitTopBar()
{
BtnLoadPreviewScene = _root.Q<Button>(nameof(BtnLoadPreviewScene));
BtnLoadPreviewScene.clickable.clicked += LoadPreviewScene;
BtnBackToScene = _root.Q<Button>(nameof(BtnBackToScene));
BtnBackToScene.clickable.clicked += BackToScene;
BtnChildInspector = _root.Q<Button>(nameof(BtnChildInspector));
BtnChildInspector.clickable.clicked += ShowChildInspector;
_previewObjectField = _root.Q<ObjectField>("PreviewInstance");
_previewObjectField.RegisterValueChangedCallback(OnPreviewObjectChanged);
}
private void ShowChildInspector()
{
if (_childInspector == null)
{
_childInspector = GetInspectTarget();
_childInspector.Show();
}
EditorApplication.delayCall += () =>
DockUtilities.DockWindow(this, _childInspector, DockUtilities.DockPosition.Right);
}
private void OnPreviewObjectChanged(ChangeEvent<Object> evt)
{
// TODO : 在这里处理预览对象的变化
}
private void BackToScene()
{
// 判断是否有记录前一个Scene
if (!string.IsNullOrEmpty(_previousScenePath))
// 激活前一个Scene
EditorSceneManager.OpenScene(_previousScenePath);
else
Debug.LogWarning("No previous scene available.");
}
private void LoadPreviewScene()
{
// 记录当前Scene
_previousScenePath = SceneManager.GetActiveScene().path;
// 创建一个新的Scene
var newScene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
// 在这里添加临时预览的内容,例如放置一些对象
// 这里只是演示,具体可以根据需求添加你的内容
// GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// SceneManager.MoveGameObjectToScene(cube, newScene);
// 激活新创建的Scene
SceneManager.SetActiveScene(newScene);
}
#endregion
#region TimerShaft
public TimerShaftView TimerShaftView { get; private set; }
private int _currentMaxFrame;
public int CurrentMaxFrame
{
get => _currentMaxFrame;
private set
{
if (AbilityAsset == null)
{
_currentMaxFrame = 0;
return;
}
if (_currentMaxFrame == value) return;
_currentMaxFrame = value;
AbilityAsset.FrameCount = _currentMaxFrame;
SaveAsset();
MaxFrame.value = _currentMaxFrame;
TrackView.UpdateContentSize();
TimerShaftView.RefreshTimerDraw();
}
}
private int _currentSelectFrameIndex;
public int CurrentSelectFrameIndex
{
get => _currentSelectFrameIndex;
set
{
if (_currentSelectFrameIndex == value) return;
_currentSelectFrameIndex = Mathf.Clamp(value, 0, MaxFrame.value);
CurrentFrame.value = _currentSelectFrameIndex;
TimerShaftView.RefreshTimerDraw();
EvaluateFrame(_currentSelectFrameIndex);
}
}
public float CurrentFramePos => Mathf.Abs(TimerShaftView.TimeLineContainer.transform.position.x);
public float CurrentSelectFramePos => _currentSelectFrameIndex * Config.FrameUnitWidth;
public float CurrentEndFramePos => CurrentMaxFrame * Config.FrameUnitWidth;
public int GetFrameIndexByPosition(float x)
{
return TimerShaftView.GetFrameIndexByPosition(x);
}
public int GetFrameIndexByMouse(float x)
{
return TimerShaftView.GetFrameIndexByMouse(x);
}
#endregion
#region Controller
private Button BtnPlay;
private Button BtnLeftFrame;
private Button BtnRightFrame;
private IntegerField CurrentFrame;
private IntegerField MaxFrame;
private void InitController()
{
BtnPlay = _root.Q<Button>(nameof(BtnPlay));
BtnPlay.clickable.clicked += OnPlay;
BtnLeftFrame = _root.Q<Button>(nameof(BtnLeftFrame));
BtnLeftFrame.clickable.clicked += OnLeftFrame;
BtnRightFrame = _root.Q<Button>(nameof(BtnRightFrame));
BtnRightFrame.clickable.clicked += OnRightFrame;
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;
}
private void OnCurrentFrameChanged(ChangeEvent<int> evt)
{
CurrentSelectFrameIndex = evt.newValue;
CurrentFrame.value = CurrentSelectFrameIndex;
}
private void RefreshPlayButton()
{
BtnPlay.text = !IsPlaying ? "▶" : "⏹";
BtnPlay.style.backgroundColor =
!IsPlaying ? new Color(0.5f, 0.5f, 0.5f, 0.5f) : new Color(0.1f, 0.8f, 0.1f, 0.5f);
}
private void OnPlay()
{
if (AbilityAsset == null) return;
IsPlaying = !IsPlaying;
}
private void OnLeftFrame()
{
if (AbilityAsset == null) return;
IsPlaying = false;
CurrentSelectFrameIndex -= 1;
}
private void OnRightFrame()
{
if (AbilityAsset == null) return;
IsPlaying = false;
CurrentSelectFrameIndex += 1;
}
#endregion
#region Clip Inspector
public object CurrentInspectorObject => TimelineInspector.CurrentInspectorObject;
public void SetInspector(object target = null)
{
if (AbilityAsset == null) return;
TimelineInspector.SetInspector(target);
}
#endregion
#region TimelinePreview
private DateTime _startTime;
private int _startPlayFrameIndex;
private bool _isPlaying;
public bool IsPlaying
{
get => _isPlaying;
private set
{
_isPlaying = CanPlay() && value;
if (_isPlaying)
{
_startTime = DateTime.Now;
_startPlayFrameIndex = CurrentSelectFrameIndex;
}
RefreshPlayButton();
}
}
private void Update()
{
if (IsPlaying)
{
var deltaTime = (DateTime.Now - _startTime).TotalSeconds;
var frameIndex = (int)(deltaTime * Config.DefaultFrameRate) + _startPlayFrameIndex;
if (frameIndex >= CurrentMaxFrame)
{
frameIndex = CurrentMaxFrame;
IsPlaying = false;
}
CurrentSelectFrameIndex = frameIndex;
}
}
private void EvaluateFrame(int frameIndex)
{
if (AbilityAsset == null || _previewObjectField.value == null) return;
foreach (var track in TrackView.TrackList)
track.TickView(frameIndex);
}
private bool CanPlay()
{
var canPlay = AbilityAsset != null && _previewObjectField.value != null;
return canPlay;
}
#endregion
#region Another Inspector
private static EditorWindow GetInspectTarget(Object targetGO = null)
{
Type inspectorType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.InspectorWindow");
EditorWindow inspectorInstance = CreateInstance(inspectorType) as EditorWindow;
if (targetGO) Selection.activeObject = targetGO;
return inspectorInstance;
}
#endregion
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8d389b67c6c74be4d9e22a30e4527d0f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- m_VisualTreeAsset: {fileID: 9197481963319205126, guid: 0ad52cc89065e3e4f9fef4bc5674a5f5, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
.custom-label {
font-size: 20px;
-unity-font-style: bold;
color: rgb(68, 138, 255);
height: 100%;
max-height: 100%;
min-height: 100%;
flex-direction: column;
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 28fcc296ac2d3554db81c84901f63835
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
disableValidation: 0

View File

@ -0,0 +1,46 @@
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
<Style src="project://database/Assets/GAS/Editor/Ability/AbilityTimelineEditor/EditorWindow/AbilityTimelineEditorWindow.uss?fileID=7433441132597879392&amp;guid=28fcc296ac2d3554db81c84901f63835&amp;type=3#AbilityTimelineEditorWindow" />
<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, com.exhard.exgas.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;" />
<ui:Button text="查看能力基本信息" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnShowAbilityAssetDetail" style="justify-content: center; height: 20px;" />
<ui:VisualElement name="Right" style="flex-grow: 1; flex-direction: row; align-items: center; align-self: flex-start; -unity-text-align: upper-left; justify-content: flex-start; padding-left: 30px; border-left-color: rgba(0, 0, 0, 0.53); border-right-color: rgba(0, 0, 0, 0.53); border-top-color: rgba(0, 0, 0, 0.53); border-bottom-color: rgba(0, 0, 0, 0.53); border-right-width: 0; border-left-width: 2px;">
<uie:ObjectField label="预览实例" name="PreviewInstance" type="UnityEngine.GameObject, UnityEngine.CoreModule" allow-scene-objects="true" style="align-items: center; align-self: center; flex-direction: row; height: 20px; -unity-font-style: bold; font-size: 15px; -unity-text-align: middle-center; width: 300px; border-left-color: rgb(255, 255, 255); border-right-color: rgb(255, 255, 255); border-top-color: rgb(255, 255, 255); border-bottom-color: rgb(255, 255, 255); border-top-width: 0; border-right-width: 0; border-bottom-width: 0; border-left-width: 0; border-top-left-radius: 0; border-top-right-radius: 0; border-bottom-right-radius: 0; border-bottom-left-radius: 0;" />
<ui:VisualElement name="Buttons" style="flex-grow: 1; flex-direction: row;">
<ui:Button text="预览场景" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnLoadPreviewScene" style="-unity-font-style: bold; width: 120px; height: 25px; align-self: auto; justify-content: flex-start; align-items: auto;" />
<ui:Button text="返回原场景" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnBackToScene" style="width: 120px; -unity-font-style: bold; height: 25px;" />
<ui:Button text="显示子Inspector" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnChildInspector" enable-rich-text="true" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
<ui:VisualElement name="Content" style="flex-grow: 1; flex-direction: row; flex-shrink: 1; height: auto;">
<ui:VisualElement name="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="ButtonGroup" style="flex-grow: 1; flex-direction: row; justify-content: flex-start; align-self: center;">
<ui:Button text="&lt;" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnLeftFrame" style="font-size: 13px; -unity-font-style: bold; align-self: center;" />
<ui:Button text="▶" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnPlay" style="-unity-font-style: bold; font-size: 13px; align-self: center;" />
<ui:Button text="&gt;" parse-escape-sequences="true" display-tooltip-when-elided="true" name="BtnRightFrame" style="-unity-font-style: bold; font-size: 13px; align-self: center;" />
</ui: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;" />
<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;" />
</ui:VisualElement>
</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: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>
<ui:IMGUIContainer name="SelectLine" style="position: absolute; left: 0;" />
<ui:IMGUIContainer name="FinishLine" style="position: absolute; left: 0;" />
<ui:IMGUIContainer name="DottedLine" style="position: absolute; left: 0;" />
<ui:IMGUIContainer name="DragItemPreview" style="position: absolute; left: 0; top: 0;" />
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:UXML>

View File

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0ad52cc89065e3e4f9fef4bc5674a5f5
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}

View File

@ -0,0 +1,55 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEngine.UIElements;
public class TimelineInspector
{
private VisualElement _root;
public object CurrentInspectorObject;
public TimelineInspector(VisualElement root)
{
_root = root;
SetInspector();
}
public void SetInspector(object target=null)
{
UpdateInspector(false,target);
}
private void UpdateInspector(bool force = false, object target = null)
{
if (CurrentInspectorObject == target && !force) return;
if (CurrentInspectorObject != null && !force)
{
if (CurrentInspectorObject is TrackClipBase oldTrackItem) oldTrackItem.ClipVe.OnUnSelect();
if (CurrentInspectorObject is TrackBase oldTrack) oldTrack.OnUnSelect();
if (CurrentInspectorObject is TrackMarkBase oldMark) oldMark.OnUnSelect();
}
CurrentInspectorObject = target;
switch (CurrentInspectorObject)
{
case null:
UnityEditor.Selection.activeObject = null;
return;
case TrackClipBase trackClip:
UnityEditor.Selection.activeObject = trackClip.DataInspector;
break;
case TrackBase track:
UnityEditor.Selection.activeObject = track.DataInspector;
break;
case TrackMarkBase mark:
UnityEditor.Selection.activeObject = mark.DataInspector;
break;
}
}
public void RefreshInspector()
{
UpdateInspector(true,CurrentInspectorObject);
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: de22069e09424610a3b23bf48b2c2523
timeCreated: 1709101722

View File

@ -0,0 +1,144 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class TimelineTrackView
{
private static List<Type> _trackTypeList;
private static readonly Dictionary<string, Type> _trackTypeMap = new();
private readonly VisualElement _root;
private Button _btnAddTrack;
private VisualElement _contentTrackListParent;
private MenuTrack _menuBuffGameplayEffect;
private MenuTrack _menuDurationalCue;
private MenuTrack _menuInstantCue;
private MenuTrack _menuInstantTask;
private MenuTrack _menuOngoingTask;
private MenuTrack _menuReleaseGameplayEffect;
private VisualElement _trackMenuParent;
public TimelineTrackView(VisualElement root)
{
_root = root;
InitTracks();
}
public List<TrackBase> TrackList { get; } = new();
private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private void InitTracks()
{
_contentTrackListParent = _root.Q<VisualElement>("ContentTrackList");
_trackMenuParent = _root.Q<VisualElement>("TrackMenu");
RefreshTrackDraw();
UpdateContentSize();
}
public void RefreshTrackDraw()
{
TrackList.Clear();
_contentTrackListParent.Clear();
_trackMenuParent.Clear();
if (AbilityAsset == null) return;
// Instant Cue
_menuInstantCue = new MenuTrack();
_menuInstantCue.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
typeof(InstantCueTrack), typeof(InstantCueTrackData), "Instant Cue",
new Color(0.1f, 0.2f, 0.6f, 0.2f), new Color(0.1f, 0.6f, 0.9f, 0.9f));
foreach (var durationalCueTrackData in AbilityAsset.InstantCues)
{
var instantCueTrack = new InstantCueTrack();
instantCueTrack.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
durationalCueTrackData);
TrackList.Add(instantCueTrack);
}
// Release GameplayEffect
_menuReleaseGameplayEffect = new MenuTrack();
_menuReleaseGameplayEffect.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
typeof(ReleaseGameplayEffectTrack), typeof(ReleaseGameplayEffectTrackData), "Release Effect",
new Color(0.9f, 0.3f, 0.35f, 0.2f), new Color(0.9f, 0.3f, 0.35f, 0.9f));
foreach (var releaseGameplayEffectTrackData in AbilityAsset.ReleaseGameplayEffect)
{
var releaseGameplayEffectTrack = new ReleaseGameplayEffectTrack();
releaseGameplayEffectTrack.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
releaseGameplayEffectTrackData);
TrackList.Add(releaseGameplayEffectTrack);
}
// Instant Task
_menuInstantTask = new MenuTrack();
_menuInstantTask.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
typeof(TaskMarkEventTrack), typeof(TaskMarkEventTrackData), "Instant Task",
new Color(0.1f, 0.6f, 0.6f, 0.2f), new Color(0.1f, 0.6f, 0.6f, 0.9f));
foreach (var instantTaskEventTrackData in AbilityAsset.InstantTasks)
{
var instantTaskEventTrack = new TaskMarkEventTrack();
instantTaskEventTrack.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
instantTaskEventTrackData);
TrackList.Add(instantTaskEventTrack);
}
// Durational Cue
_menuDurationalCue = new MenuTrack();
_menuDurationalCue.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
typeof(DurationalCueTrack), typeof(DurationalCueTrackData), "Durational Cue",
new Color(0.1f, 0.6f, 0.1f, 0.2f), new Color(0.1f, 0.6f, 0.1f, 1));
foreach (var durationalCueTrackData in AbilityAsset.DurationalCues)
{
var cueTrack = new DurationalCueTrack();
cueTrack.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth, durationalCueTrackData);
TrackList.Add(cueTrack);
}
// Buff GameplayEffect
_menuBuffGameplayEffect = new MenuTrack();
_menuBuffGameplayEffect.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
typeof(BuffGameplayEffectTrack), typeof(BuffGameplayEffectTrackData), "Buff",
new Color(0.9f, 0.6f, 0.6f, 0.2f), new Color(0.9f, 0.6f, 0.6f, 1));
foreach (var buffGameplayEffectTrackData in AbilityAsset.BuffGameplayEffects)
{
var buffTrack = new BuffGameplayEffectTrack();
buffTrack.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
buffGameplayEffectTrackData);
TrackList.Add(buffTrack);
}
// Ongoing Task
_menuOngoingTask = new MenuTrack();
_menuOngoingTask.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
typeof(TaskClipEventTrack), typeof(TaskClipEventTrackData), "Ongoing Task",
new Color(0.7f, 0.3f, 0.7f, 0.2f), new Color(0.5f, 0.3f, 0.5f, 1));
foreach (var customClipEventTrackData in AbilityAsset.OngoingTasks)
{
var customClipTrack = new TaskClipEventTrack();
customClipTrack.Init(_contentTrackListParent, _trackMenuParent, Config.FrameUnitWidth,
customClipEventTrackData);
TrackList.Add(customClipTrack);
}
UpdateContentSize();
}
public void UpdateContentSize()
{
_contentTrackListParent.style.width =
AbilityTimelineEditorWindow.Instance.CurrentMaxFrame * Config.FrameUnitWidth;
foreach (var track in TrackList) track.RefreshShow(Config.FrameUnitWidth);
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0fd4dc4ec6be4b149e428db5c2ca0fb0
timeCreated: 1709022190

View File

@ -0,0 +1,272 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public class TimerShaftView
{
private readonly VisualElement _root;
private int _dottedLineFrameIndex = -1;
private Rect _dragItemPreviewRect;
private bool _showDragItemPreview;
private VisualElement contentViewPort;
private IMGUIContainer DottedLine;
private IMGUIContainer DragItemPreview;
private IMGUIContainer FinishLine;
private IMGUIContainer SelectLine;
private bool timerShaftMouseIn;
public TimerShaftView(VisualElement root)
{
_root = root;
InitTimerShaft();
}
private static AbilityTimelineEditorWindow EditorInst => AbilityTimelineEditorWindow.Instance;
private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config;
public IMGUIContainer TimerShaft { get; private set; }
public VisualElement TimeLineContainer { get; private set; }
public ScrollView MainContent { get; private set; }
public int DottedLineFrameIndex
{
get => _dottedLineFrameIndex;
set
{
if (_dottedLineFrameIndex == value) return;
_dottedLineFrameIndex = value;
var showDottedLine = _dottedLineFrameIndex * Config.FrameUnitWidth >= EditorInst.CurrentFramePos &&
_dottedLineFrameIndex * Config.FrameUnitWidth <
EditorInst.CurrentFramePos + TimerShaft.contentRect.width;
DottedLine.style.display = showDottedLine ? DisplayStyle.Flex : DisplayStyle.None;
DottedLine.MarkDirtyRepaint();
}
}
public bool ShowDragItemPreview
{
get => _showDragItemPreview;
set
{
if (_showDragItemPreview == value) return;
_showDragItemPreview = value;
DragItemPreview.MarkDirtyRepaint();
}
}
public Rect DragItemPreviewRect
{
get => _dragItemPreviewRect;
set
{
_dragItemPreviewRect = value;
DragItemPreview.MarkDirtyRepaint();
}
}
private void InitTimerShaft()
{
var mainContainer = _root.Q<ScrollView>("MainContent");
MainContent = mainContainer;
TimeLineContainer = mainContainer.Q<VisualElement>("unity-content-container");
contentViewPort = mainContainer.Q<VisualElement>("unity-content-viewport");
TimerShaft = _root.Q<IMGUIContainer>(nameof(TimerShaft));
TimerShaft.onGUIHandler = OnTimerShaftGUI;
TimerShaft.RegisterCallback<WheelEvent>(OnWheelEvent);
TimerShaft.RegisterCallback<MouseDownEvent>(OnTimerShaftMouseDown);
TimerShaft.RegisterCallback<MouseMoveEvent>(OnTimerShaftMouseMove);
TimerShaft.RegisterCallback<MouseUpEvent>(OnTimerShaftMouseUp);
TimerShaft.RegisterCallback<MouseOutEvent>(OnTimerShaftMouseOut);
SelectLine = _root.Q<IMGUIContainer>(nameof(SelectLine));
SelectLine.onGUIHandler = OnSelectLineGUI;
FinishLine = _root.Q<IMGUIContainer>(nameof(FinishLine));
FinishLine.onGUIHandler = OnFinishLineGUI;
DottedLine = _root.Q<IMGUIContainer>(nameof(DottedLine));
DottedLine.onGUIHandler = OnDottedLineGUI;
DragItemPreview = _root.Q<IMGUIContainer>(nameof(DragItemPreview));
DragItemPreview.onGUIHandler = OnDragItemPreviewGUI;
}
public void RefreshTimerDraw()
{
TimerShaft.MarkDirtyRepaint();
SelectLine.MarkDirtyRepaint();
FinishLine.MarkDirtyRepaint();
}
private void OnTimerShaftMouseDown(MouseDownEvent evt)
{
timerShaftMouseIn = true;
EditorInst.CurrentSelectFrameIndex = GetFrameIndexByMouse(evt.localMousePosition.x);
}
private void OnTimerShaftMouseUp(MouseUpEvent evt)
{
timerShaftMouseIn = false;
}
private void OnTimerShaftMouseMove(MouseMoveEvent evt)
{
if (timerShaftMouseIn) EditorInst.CurrentSelectFrameIndex = GetFrameIndexByMouse(evt.localMousePosition.x);
}
private void OnTimerShaftMouseOut(MouseOutEvent evt)
{
timerShaftMouseIn = false;
}
public int GetFrameIndexByMouse(float x)
{
return GetFrameIndexByPosition(x + EditorInst.CurrentFramePos);
}
public int GetFrameIndexByPosition(float x)
{
return Mathf.RoundToInt(x) / Config.FrameUnitWidth;
}
private void OnSelectLineGUI()
{
if (EditorInst.CurrentSelectFramePos >= EditorInst.CurrentFramePos &&
EditorInst.CurrentSelectFramePos < EditorInst.CurrentFramePos + TimerShaft.contentRect.width)
{
Handles.BeginGUI();
Handles.color = Color.green;
var length = contentViewPort.contentRect.height + TimerShaft.contentRect.height;
var x = EditorInst.CurrentSelectFramePos - EditorInst.CurrentFramePos;
x = Mathf.Max(x, 1);
Handles.DrawLine(new Vector3(x, 0), new Vector3(x, length));
Handles.EndGUI();
}
}
private void OnFinishLineGUI()
{
if (EditorInst.CurrentEndFramePos >= EditorInst.CurrentFramePos &&
EditorInst.CurrentEndFramePos < EditorInst.CurrentFramePos + TimerShaft.contentRect.width)
{
Handles.BeginGUI();
Handles.color = Color.red;
var length = contentViewPort.contentRect.height + TimerShaft.contentRect.height;
var x = EditorInst.CurrentEndFramePos - EditorInst.CurrentFramePos;
x = Mathf.Max(x, 1);
Handles.DrawLine(new Vector3(x, 0), new Vector3(x, length));
Handles.EndGUI();
}
}
private void OnDottedLineGUI()
{
var dottedLinePos = DottedLineFrameIndex * Config.FrameUnitWidth;
if (dottedLinePos >= EditorInst.CurrentFramePos &&
dottedLinePos < EditorInst.CurrentFramePos + TimerShaft.contentRect.width)
{
Handles.BeginGUI();
Handles.color = new Color(1, 0.5f, 0, 1);
var length = contentViewPort.contentRect.height + TimerShaft.contentRect.height;
var x = dottedLinePos - EditorInst.CurrentFramePos;
x = Mathf.Max(x, 1);
var lineUnitSize = 10f;
var lineUnitsCount = 0;
for (float i = 0; i < length; i += lineUnitSize)
if (lineUnitsCount++ % 2 == 0)
Handles.DrawLine(new Vector3(x, i), new Vector3(x, i + lineUnitSize));
Handles.EndGUI();
}
}
private void OnDragItemPreviewGUI()
{
if (ShowDragItemPreview)
{
Handles.BeginGUI();
Handles.color = new Color(0.5f, 0.5f, 0.5f, 0.5f);
Handles.DrawSolidRectangleWithOutline(DragItemPreviewRect, new Color(0.9f, 0.5f, 0, 0.9f), Color.white);
Handles.EndGUI();
}
}
private void OnWheelEvent(WheelEvent evt)
{
var deltaY = (int)evt.delta.y;
Config.FrameUnitWidth =
Mathf.Clamp(Config.FrameUnitWidth - deltaY,
AbilityTimelineEditorConfig.StandardFrameUnitWidth,
Mathf.RoundToInt(AbilityTimelineEditorConfig.MaxFrameUnitLevel *
AbilityTimelineEditorConfig.StandardFrameUnitWidth));
// 以鼠标为缩放中心
// var mousePos = evt.localMousePosition.x;
// var mouseFrame = GetFrameIndexByMouse(mousePos);
// var mouseFramePos = mouseFrame * Config.FrameUnitWidth;
// var deltaFrame = mouseFramePos - EditorInst.CurrentFramePos;
// var contentWidth = contentViewPort.contentRect.width;
// var scrollViewWidth = MainContent.worldBound.width;
// var scrollOffsetDelta = (EditorInst.CurrentFramePos + deltaFrame * Config.FrameUnitWidth) / contentWidth *
// scrollViewWidth;
// MainContent.scrollOffset = new Vector2(MainContent.scrollOffset.x - scrollOffsetDelta, 0);
RefreshTimerDraw();
EditorInst.TrackView.UpdateContentSize();
}
private void OnTimerShaftGUI()
{
Handles.BeginGUI();
Handles.color = Color.white;
var rect = TimerShaft.contentRect;
var tickStep = AbilityTimelineEditorConfig.MaxFrameUnitLevel + 1 -
Config.FrameUnitWidth / AbilityTimelineEditorConfig.StandardFrameUnitWidth;
tickStep /= 2;
tickStep = Mathf.Max(tickStep, 2);
var index = Mathf.CeilToInt(EditorInst.CurrentFramePos / Config.FrameUnitWidth);
var startFrameOffset =
index > 0 ? Config.FrameUnitWidth - EditorInst.CurrentFramePos % Config.FrameUnitWidth : 0;
var minDrawStep = AbilityTimelineEditorConfig.MinTimerShaftFrameDrawStep;
var tooSmall = Config.FrameUnitWidth < minDrawStep;
var drawStepFrame = tooSmall ? Mathf.CeilToInt(minDrawStep / Config.FrameUnitWidth) : 1;
tickStep *= drawStepFrame;
for (var i = startFrameOffset; i <= rect.width; i += Config.FrameUnitWidth)
{
var isDraw = !tooSmall || index % drawStepFrame == 0;
if (isDraw)
{
var isTick = index % tickStep == 0;
var x = i;
var startY = isTick ? rect.height * 0.5f : rect.height * 0.85f;
var endY = rect.height;
Handles.DrawLine(new Vector3(x, startY), new Vector3(x, endY));
if (isTick)
{
var frameStr = index.ToString();
Handles.Label(new Vector3(x, rect.height * 0.3f), frameStr);
}
}
index++;
}
Handles.EndGUI();
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5eef4c200759470f9d78578b25f638ce
timeCreated: 1709022708

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 57ce001ccda69fb42a45620a155e9f0a
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 9779130815dc9124f8b6f8eb91b639ed
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 92f1dbb6ded22b743a877eefd625e2e9
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 0c3a5a8bb9c5ac04cbefb66939648675
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 275b0fae483c03b41bf38af8e22b53cb
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 532c2163527942a1980d38f4f7b939ba
timeCreated: 1708439991

View File

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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a14fb0e3c3d64a5ca183c242d3bd07f0
timeCreated: 1709188522

View File

@ -0,0 +1,70 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using System.Linq;
using GAS.Runtime;
public class TaskClip : TrackClip<TaskClipEventTrack>
{
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public TaskClipEvent TaskClipData => clipData as TaskClipEvent;
public TaskClipEvent ClipDataForSave
{
get
{
var cueTrackDataForSave = track.TaskClipTrackDataForSave;
for (var i = 0; i < cueTrackDataForSave.clipEvents.Count; i++)
if (cueTrackDataForSave.clipEvents[i] == TaskClipData)
return track.TaskClipTrackDataForSave.clipEvents[i];
return null;
}
}
public override void Delete()
{
var success = track.TaskClipTrackDataForSave.clipEvents.Remove(TaskClipData);
AbilityTimelineEditorWindow.Instance.Save();
if (!success) return;
track.RemoveTrackItem(this);
AbilityTimelineEditorWindow.Instance.SetInspector();
}
public override void RefreshShow(float newFrameUnitWidth)
{
base.RefreshShow(newFrameUnitWidth);
var taskType = TaskClipData.ongoingTask.TaskData.Type;
var shortName = taskType.Split('.').Last();
ItemLabel.text = !string.IsNullOrEmpty(shortName) ? shortName : "Null!";
}
public override void UpdateClipDataStartFrame(int newStartFrame)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.startFrame = newStartFrame;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public override void UpdateClipDataDurationFrame(int newDurationFrame)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.durationFrame = newDurationFrame;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public override void OnTickView(int frameIndex, int startFrame, int endFrame)
{
if (frameIndex < startFrame || frameIndex > endFrame) return;
var ongoingAbilityTask = TaskClipData.Load();
ongoingAbilityTask.OnEditorPreview( frameIndex, startFrame, endFrame);
}
public override UnityEngine.Object DataInspector => TaskClipEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fa654674f82841b0aca803e511168082
timeCreated: 1709188590

View File

@ -0,0 +1,145 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
using System.Linq;
using System.Collections;
using GAS.General;
using System;
using System.Collections.Generic;
using GAS.Runtime;
public class TaskClipEditor:OdinEditorWindow
{
private static IEnumerable OngoingTaskSonTypes = OngoingTaskData.OngoingTaskSonTypeChoices;
private static Type[] _ongoingTaskInspectorTypes;
public static Type[] OngoingTaskInspectorTypes =>
_ongoingTaskInspectorTypes ??= TypeUtil.GetAllSonTypesOf(typeof(OngoingTaskInspector));
private static Dictionary<Type, Type> _ongoingTaskInspectorMap;
private static Dictionary<Type, Type> OngoingTaskInspectorMap
{
get
{
if (_ongoingTaskInspectorMap != null) return _ongoingTaskInspectorMap;
_ongoingTaskInspectorMap = new Dictionary<Type, Type>();
foreach (var inspectorType in OngoingTaskInspectorTypes)
{
var taskType = inspectorType.BaseType.GetGenericArguments()[0];
_ongoingTaskInspectorMap.Add(taskType, inspectorType);
}
return _ongoingTaskInspectorMap;
}
}
private const string GRP_BOX = "GRP_BOX";
private const string GRP_BOX_TASK = "GRP_BOX/Task";
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private TaskClip _clip;
public static TaskClipEditor Create(TaskClip clip)
{
var window = new TaskClipEditor();
window._clip = clip;
window.Refresh();
return window;
}
[BoxGroup(GRP_BOX)]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string RunInfo;
[Delayed]
[BoxGroup(GRP_BOX)]
[LabelText("Duration(f)")]
[OnValueChanged("OnDurationFrameChanged")]
public int Duration;
[Delayed]
[BoxGroup(GRP_BOX_TASK)]
[LabelText("OngoingTask")]
[ValueDropdown("OngoingTaskSonTypes")]
[InfoBox("This Task has no inspector!",InfoMessageType.Warning, "OngoingTaskIsNull")]
[OnValueChanged("OnTaskTypeChanged")]
public string OngoingTaskType;
[BoxGroup(GRP_BOX_TASK)]
[HideReferenceObjectPicker]
[HideIf("OngoingTaskIsNull")]
[LabelText("Task Detail")]
public OngoingTaskInspector OngoingTask;
[BoxGroup(GRP_BOX)]
[Button]
[GUIColor(0.9f,0.2f,0.2f)]
void Delete()
{
_clip.Delete();
}
void Refresh()
{
RunInfo = $"<b>Run(f):{_clip.TaskClipData.startFrame} -> {_clip.TaskClipData.EndFrame}</b>";
Duration = _clip.TaskClipData.durationFrame;
OngoingTaskType = _clip.TaskClipData.ongoingTask.TaskData.Type;
RefreshTaskInspector();
}
void RefreshTaskInspector()
{
// 根据选择的OngoingAbilityTask子类显示对应的属性
var ongoingAbilityTask = _clip.TaskClipData.Load();
if (OngoingTaskInspectorMap.TryGetValue(ongoingAbilityTask.GetType(), out var inspectorType))
{
var taskInspector = (OngoingTaskInspector)Activator.CreateInstance(inspectorType);
taskInspector.Init(ongoingAbilityTask);
OngoingTask = taskInspector;
}
else
{
OngoingTask = null;
}
}
private void OnDurationFrameChanged()
{
// 钳制
var max = AbilityAsset.FrameCount - _clip.ClipDataForSave.startFrame;
Duration = Mathf.Clamp(Duration, 1, max);
_clip.UpdateClipDataDurationFrame(Duration);
_clip.RefreshShow(_clip.FrameUnitWidth);
Refresh();
}
private void OnTaskTypeChanged()
{
_clip.ClipDataForSave.ongoingTask.TaskData.Type = OngoingTaskType;
_clip.ClipDataForSave.ongoingTask.TaskData.Data = null;
AbilityTimelineEditorWindow.Instance.Save();
AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
RefreshTaskInspector();
}
bool OngoingTaskIsNull()
{
return OngoingTask == null;
}
}
[CustomEditor(typeof(TaskClipEditor))]
public class TaskClipInspector:OdinEditorWithoutHeader
{
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cba12e6f7c134f23bfef110061a95041
timeCreated: 1710404613

View File

@ -0,0 +1,97 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class TaskClipEventTrack:TrackBase
{
private TaskClipEventTrackData _taskClipEventTrackData;
public override Type TrackDataType => typeof(TaskClipEventTrackData);
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;
public TaskClipEventTrackData TaskClipTrackDataForSave
{
get
{
for (int i = 0; i < AbilityAsset.OngoingTasks.Count; i++)
{
if(AbilityAsset.OngoingTasks[i] == _taskClipEventTrackData)
return AbilityAsset.OngoingTasks[i];
}
return null;
}
}
public override void TickView(int frameIndex, params object[] param)
{
foreach (var item in _trackItems)
{
var taskClip = item as TaskClip;
taskClip.OnTickView(frameIndex, taskClip.StartFrameIndex, taskClip.EndFrameIndex);
}
}
public override void Init(VisualElement trackParent, VisualElement menuParent, float frameWidth, TrackDataBase trackData)
{
base.Init(trackParent, menuParent, frameWidth, trackData);
_taskClipEventTrackData = trackData as TaskClipEventTrackData;
MenuText.text = _taskClipEventTrackData.trackName;
}
public override void RefreshShow(float newFrameWidth)
{
base.RefreshShow(newFrameWidth);
foreach (var item in _trackItems) Track.Remove(((TrackClipBase)item).ClipVe);
_trackItems.Clear();
if (AbilityTimelineEditorWindow.Instance.AbilityAsset != null)
foreach (var clipEvent in _taskClipEventTrackData.clipEvents)
{
var item = new TaskClip();
item.InitTrackClip(this, Track, _frameWidth, clipEvent);
_trackItems.Add(item);
}
}
protected override void OnAddTrackItem(DropdownMenuAction action)
{
// 添加Clip数据
var clipEvent = new TaskClipEvent
{
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x),
durationFrame = 5,
ongoingTask = new OngoingTaskData()
};
TaskClipTrackDataForSave.clipEvents.Add(clipEvent);
// 刷新显示
var item = new TaskClip();
item.InitTrackClip(this, Track, _frameWidth, clipEvent);
_trackItems.Add(item);
// 选中新Clip
item.ClipVe.OnSelect();
Debug.Log("[EX] Add a new Custom Clip Event");
}
protected override void OnRemoveTrack(DropdownMenuAction action)
{
// 删除数据
AbilityAsset.OngoingTasks.Remove(_taskClipEventTrackData);
AbilityTimelineEditorWindow.Instance.Save();
// 删除显示
TrackParent.Remove(TrackRoot);
MenuParent.Remove(MenuRoot);
Debug.Log("[EX] Remove Task Clip Track");
}
public override UnityEngine.Object DataInspector => TaskClipEventTrackEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c984467e4d15458089c9831f5f9a55d7
timeCreated: 1709188570

View File

@ -0,0 +1,60 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
public class TaskClipEventTrackEditor:OdinEditorWindow
{
private TaskClipEventTrack _track;
public static TaskClipEventTrackEditor Create(TaskClipEventTrack track)
{
var window = new TaskClipEventTrackEditor();
window._track = track;
window.TrackName = track.TaskClipTrackDataForSave.trackName;
window.UpdateTrackInfo();
return window;
}
[Delayed]
[BoxGroup]
[LabelText("Name")]
[OnValueChanged("OnTrackNameChanged")]
public string TrackName;
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string TrackInfo;
void UpdateTrackInfo()
{
string info = "";
foreach (var clip in _track.TaskClipTrackDataForSave.clipEvents)
{
var taskName = clip.ongoingTask.TaskData.Type;
var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1);
info += $"[{shortName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
}
TrackInfo = $"<b>{info}</b>";
}
void OnTrackNameChanged()
{
_track.TaskClipTrackDataForSave.trackName = TrackName;
_track.MenuText.text = TrackName;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(TaskClipEventTrackEditor))]
public class TaskClipEventTrackInspector:OdinEditorWithoutHeader
{
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6632bf9b61448918503ab3c971fb532
timeCreated: 1710402173

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4c3476b4812248ed8e94d2e2e48d492a
timeCreated: 1709188546

View File

@ -0,0 +1,197 @@
using GAS.Runtime;
using UnityEngine;
namespace GAS.Editor
{
public class TaskMark : TrackMark<TaskMarkEventTrack>
{
public new TaskMarkEvent MarkData => markData as TaskMarkEvent;
public TaskMarkEvent MarkDataForSave
{
get
{
var trackDataForSave = track.InstantTaskEventTrackData;
for (var i = 0; i < trackDataForSave.markEvents.Count; i++)
if (trackDataForSave.markEvents[i] == MarkData)
return track.InstantTaskEventTrackData.markEvents[i];
return null;
}
}
public override Object DataInspector => TaskMarkEditor.Create(this);
public override void Duplicate()
{
// 添加Mark数据
var startFrame = markData.startFrame < AbilityAsset.FrameCount
? markData.startFrame + 1
: markData.startFrame - 1;
startFrame = Mathf.Clamp(startFrame, 0, AbilityAsset.FrameCount);
var markEvent = new TaskMarkEvent
{
startFrame = startFrame,
InstantTasks = (markData as TaskMarkEvent)?.InstantTasks
};
track.InstantTaskEventTrackData.markEvents.Add(markEvent);
// 刷新显示
var mark = new TaskMark();
mark.InitTrackMark(track, track.Track, FrameUnitWidth, markEvent);
track.TrackItems.Add(mark);
mark.OnSelect();
}
public override void RefreshShow(float newFrameUnitWidth)
{
base.RefreshShow(newFrameUnitWidth);
ItemLabel.text = "";
}
// #region Inspector
//
// private VisualElement taskSonInspector;
// private ListView taskList;
//
// public override VisualElement Inspector()
// {
// var inspector = TrackInspectorUtil.CreateTrackInspector();
// var markFrame = TrackInspectorUtil.CreateLabel($"Trigger(f):{markData.startFrame}");
// inspector.Add(markFrame);
//
// taskSonInspector = TrackInspectorUtil.CreateSonInspector();
// inspector.Add(taskSonInspector);
//
// // task列表
// taskList = TrackInspectorUtil.CreateListView<InstantTaskData>("Task", MarkData.InstantTasks,
// MakeInstantTaskData,BindInstantTaskData, OnSelectionChanged,OnItemAdded,OnItemRemoved);
// inspector.Add(taskList);
// taskList.SetSelection(0);
//
// // InstantTask面板渲染
// DrawTaskSonInspector(taskSonInspector);
//
// return inspector;
// }
//
// private void OnItemRemoved(IEnumerable<int> obj)
// {
// if (taskList.childCount == 0)
// {
// taskSonInspector.Clear();
// }
// }
//
// private void OnItemAdded(IEnumerable<int> obj)
// {
// if (taskList.childCount == 1)
// {
// taskList.SetSelection(0);
// DrawTaskSonInspector(taskSonInspector);
// }
// }
//
// private void OnSelectionChanged(IEnumerable<object> obj)
// {
// DrawTaskSonInspector(taskSonInspector);
// }
//
// private void DrawTaskSonInspector(VisualElement parent)
// {
// parent.Clear();
//
// // 选择项所有InstantAbilityTask子类
// if (taskList.selectedIndex < MarkData.InstantTasks.Count && taskList.selectedIndex >= 0)
// {
// taskList.SetSelection(0);
// var taskSonTypes = InstantTaskData.InstantTaskSonTypes;
// List<string> taskSons = taskSonTypes.Select(sonType => sonType.FullName).ToList();
// var initValue = MarkData.InstantTasks[taskList.selectedIndex].TaskData.Type;
// var typeSelector =
// TrackInspectorUtil.CreateDropdownField("", taskSons, initValue, (evt) =>
// {
// MarkDataForSave.InstantTasks[taskList.selectedIndex].TaskData.Type = evt.newValue;
// MarkDataForSave.InstantTasks[taskList.selectedIndex].TaskData.Data = null;
// AbilityTimelineEditorWindow.Instance.Save();
// AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
// });
// parent.Add(typeSelector);
//
// // 根据选择的InstantAbilityTask子类显示对应的属性
// var task = MarkDataForSave.InstantTasks[taskList.selectedIndex].Load();
// if(InstantTaskInspectorMap.TryGetValue(task.GetType(), out var inspectorType))
// {
// var taskInspector = (InstantTaskInspector)Activator.CreateInstance(inspectorType, task);
// parent.Add(taskInspector.Inspector());
// }
// else
// {
// parent.Add(TrackInspectorUtil.CreateLabel($"{task.GetType()}'s Inspector not found!"));
// }
// }
//
// parent.MarkDirtyRepaint();
// }
//
// private void BindInstantTaskData(VisualElement root, int i)
// {
// MarkData.InstantTasks[i] ??= new InstantTaskData();
// var taskValue = MarkData.InstantTasks[i];
// var label = (Label)root;
// var shotName = taskValue.TaskData.Type.Split('.').Last();
// label.text = shotName;
//
// // taskValue.Task.l
// //
// // var textField = (TextField)e;
// // textField.value = list[i];
// // textField.RegisterValueChangedCallback(evt =>
// // {
// // onItemValueChanged(i, evt);
// // });
// }
//
// private VisualElement MakeInstantTaskData()
// {
// return TrackInspectorUtil.CreateLabel("");
// }
//
//
// #endregion
public override void Delete()
{
var success = track.InstantTaskEventTrackData.markEvents.Remove(MarkData);
AbilityTimelineEditorWindow.Instance.Save();
if (!success) return;
track.RemoveTrackItem(this);
AbilityTimelineEditorWindow.Instance.SetInspector();
}
public override void UpdateMarkDataFrame(int newStartFrame)
{
var updatedClip = MarkDataForSave;
MarkDataForSave.startFrame = newStartFrame;
AbilityTimelineEditorWindow.Instance.Save();
markData = updatedClip;
}
public override void OnTickView(int frameIndex)
{
if (frameIndex == StartFrameIndex)
{
foreach (var t in MarkData.InstantTasks)
{
var task = t.Load() as InstantAbilityTask;
task?.OnEditorPreview();
}
}
}
public void SaveCurrentTask(InstantAbilityTask task)
{
//MarkDataForSave.InstantTasks[taskList.selectedIndex].Save(task);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d5fe9e9767964d92b29e5ce9101f9251
timeCreated: 1709188609

View File

@ -0,0 +1,175 @@
using System;
using System.Collections;
using System.Collections.Generic;
using GAS.General;
using GAS.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)]
public string RunInfo;
private TaskMark _mark;
[Delayed]
[BoxGroup]
[HideReferenceObjectPicker]
[ListDrawerSettings(ShowFoldout = true, DraggableItems = true)]
[OnValueChanged("OnTaskListChanged", true)]
public List<InstantTaskCellInspector> Tasks;
public static TaskMarkEditor Create(TaskMark mark)
{
var window = CreateInstance<TaskMarkEditor>();
window._mark = mark;
window.UpdateMarkInfo();
return window;
}
[BoxGroup]
[Button]
[GUIColor(0.9f, 0.2f, 0.2f)]
private void Delete()
{
_mark.Delete();
}
private void UpdateMarkInfo()
{
RunInfo = $"<b>Trigger(f):{_mark.MarkData.startFrame}</b>";
Tasks = new List<InstantTaskCellInspector>();
foreach (var taskData in _mark.MarkData.InstantTasks) Tasks.Add(new InstantTaskCellInspector(taskData));
}
private void OnTaskListChanged()
{
var tasks = new List<InstantTaskData>();
foreach (var t in Tasks)
tasks.Add(new InstantTaskData
{
TaskData = new JsonData
{
Type = t.InstantTaskType,
Data = t.Data()
}
});
_mark.MarkDataForSave.InstantTasks = tasks;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(TaskMarkEditor))]
public class TaskMarkInspector : OdinEditorWithoutHeader
{
}
public class InstantTaskCellInspector
{
private static IEnumerable InstantTaskSonTypes = InstantTaskData.InstantTaskSonTypeChoices;
private static Type[] _instantTaskInspectorTypes;
private static Dictionary<Type, Type> _instantTaskInspectorMap;
private readonly InstantTaskData _data;
private InstantAbilityTask _instantAbilityTask;
[Delayed]
[BoxGroup]
[LabelText("Task")]
[ValueDropdown("InstantTaskSonTypes")]
[InfoBox("This Task has no inspector!", InfoMessageType.Warning, "InstantTaskIsNull")]
[OnValueChanged("OnTaskTypeChanged")]
public string InstantTaskType;
[BoxGroup]
[HideReferenceObjectPicker]
[HideIf("InstantTaskIsNull")]
[LabelText("Detail")]
public InstantTaskInspector InstantTask;
public InstantTaskCellInspector(InstantTaskData data)
{
_data = data;
_instantAbilityTask = data.Load() as InstantAbilityTask;
InstantTaskType = data.TaskData.Type;
RefreshDetailInspector();
}
public InstantTaskCellInspector()
{
_data = new InstantTaskData();
_instantAbilityTask = _data.Load() as InstantAbilityTask;
InstantTaskType = _data.TaskData.Type;
RefreshDetailInspector();
}
public static Type[] InstantTaskInspectorTypes =>
_instantTaskInspectorTypes ??= TypeUtil.GetAllSonTypesOf(typeof(InstantTaskInspector));
private static Dictionary<Type, Type> InstantTaskInspectorMap
{
get
{
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;
}
}
public string Data()
{
if (_instantAbilityTask == null) return null;
_data.Save(_instantAbilityTask);
return _data.TaskData.Data;
}
private void OnTaskTypeChanged()
{
_data.TaskData.Type = InstantTaskType;
_data.TaskData.Data = null;
RefreshDetailInspector();
}
private bool InstantTaskIsNull()
{
return InstantTask == null;
}
public void RefreshDetailInspector()
{
_instantAbilityTask = _data.Load() as InstantAbilityTask;
if (_instantAbilityTask != null && InstantTaskInspectorMap.TryGetValue(_instantAbilityTask.GetType(), out var inspectorType))
{
var taskInspector = (InstantTaskInspector)Activator.CreateInstance(inspectorType);
taskInspector.Init(_instantAbilityTask);
InstantTask = taskInspector;
}
else
{
InstantTask = null;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 24bdabb4d76b46f3a93fd5999a611fe7
timeCreated: 1710412687

View File

@ -0,0 +1,94 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class TaskMarkEventTrack : TrackBase
{
private TaskMarkEventTrackData _instantTasksTrackData;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public TaskMarkEventTrackData InstantTaskEventTrackData
{
get
{
for (var i = 0; i < AbilityAsset.InstantTasks.Count; i++)
if (AbilityAsset.InstantTasks[i] == _instantTasksTrackData)
return AbilityAsset.InstantTasks[i];
return null;
}
}
public override Type TrackDataType => typeof(TaskMarkEventTrackData);
protected override Color TrackColor => new(0.1f, 0.6f, 0.6f, 0.2f);
protected override Color MenuColor => new(0.1f, 0.6f, 0.6f, 0.9f);
public override void Init(VisualElement trackParent, VisualElement menuParent, float frameWidth,
TrackDataBase trackData)
{
base.Init(trackParent, menuParent, frameWidth, trackData);
_instantTasksTrackData = trackData as TaskMarkEventTrackData;
MenuText.text = _instantTasksTrackData.trackName;
}
public override void TickView(int frameIndex, params object[] param)
{
foreach (var item in _trackItems)
((TrackMarkBase)item).OnTickView(frameIndex);
}
public override void RefreshShow(float newFrameWidth)
{
base.RefreshShow(newFrameWidth);
foreach (var item in _trackItems) Track.Remove(((TrackMarkBase)item).Ve);
_trackItems.Clear();
if (AbilityTimelineEditorWindow.Instance.AbilityAsset == null) return;
foreach (var markEvent in _instantTasksTrackData.markEvents)
{
var item = new TaskMark();
item.InitTrackMark(this, Track, _frameWidth, markEvent);
_trackItems.Add(item);
}
}
public override UnityEngine.Object DataInspector => TaskMarkEventTrackEditor.Create(this);
protected override void OnAddTrackItem(DropdownMenuAction action)
{
// 添加Mark数据
var markEvent = new TaskMarkEvent
{
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x)
};
InstantTaskEventTrackData.markEvents.Add(markEvent);
// 刷新显示
var mark = new TaskMark();
mark.InitTrackMark(this, Track, _frameWidth, markEvent);
_trackItems.Add(mark);
mark.OnSelect();
Debug.Log("[EX] Add Instant Task Mark");
}
protected override void OnRemoveTrack(DropdownMenuAction action)
{
// 删除数据
AbilityAsset.InstantTasks.Remove(_instantTasksTrackData);
AbilityTimelineEditorWindow.Instance.Save();
// 删除显示
TrackParent.Remove(TrackRoot);
MenuParent.Remove(MenuRoot);
Debug.Log("[EX] Remove Instant Task Track");
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e77bb736b8f4172b97e0c5cbb29e734
timeCreated: 1709188602

View File

@ -0,0 +1,65 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
public class TaskMarkEventTrackEditor:OdinEditorWindow
{
private TaskMarkEventTrack _track;
public static TaskMarkEventTrackEditor Create(TaskMarkEventTrack track)
{
var window = new TaskMarkEventTrackEditor();
window._track = track;
window.TrackName = track.InstantTaskEventTrackData.trackName;
window.UpdateTrackInfo();
return window;
}
[Delayed]
[BoxGroup]
[LabelText("Name")]
[OnValueChanged("OnTrackNameChanged")]
public string TrackName;
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string TrackInfo;
void UpdateTrackInfo()
{
string info = "";
foreach (var mark in _track.InstantTaskEventTrackData.markEvents)
{
info += $"Trigger(f):{mark.startFrame} \n";
foreach (var task in mark.InstantTasks)
{
var taskName = task.TaskData.Type;
var shortName = taskName.Substring(taskName.LastIndexOf('.') + 1);
info += $" |-> {shortName}\n";
}
info += "\n";
}
TrackInfo = $"<b>{info}</b>";
}
void OnTrackNameChanged()
{
_track.InstantTaskEventTrackData.trackName = TrackName;
_track.MenuText.text = TrackName;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(TaskMarkEventTrackEditor))]
public class TaskMarkEventTrackInspector:OdinEditorWithoutHeader
{
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f9b48a0ead1e46fda171f81526de090c
timeCreated: 1710401923

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87b8970b4a8f40dc8ceca574d4a7f7f3
timeCreated: 1709177306

View File

@ -0,0 +1,78 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using Runtime;
using UnityEngine;
using UnityEngine.UIElements;
public class BuffGameplayEffectClip : TrackClip<BuffGameplayEffectTrack>
{
public BuffGameplayEffectClipEvent BuffGameplayEffectClipData => clipData as BuffGameplayEffectClipEvent;
private BuffGameplayEffectClipEvent ClipDataForSave
{
get
{
var cueTrackDataForSave = track.BuffTrackDataForSave;
for (var i = 0; i < cueTrackDataForSave.clipEvents.Count; i++)
if (cueTrackDataForSave.clipEvents[i] == BuffGameplayEffectClipData)
return track.BuffTrackDataForSave.clipEvents[i];
return null;
}
}
public override void Delete()
{
var success = track.BuffTrackDataForSave.clipEvents.Remove(BuffGameplayEffectClipData);
AbilityTimelineEditorWindow.Instance.Save();
if (!success) return;
track.RemoveTrackItem(this);
AbilityTimelineEditorWindow.Instance.SetInspector();
}
public override void RefreshShow(float newFrameUnitWidth)
{
base.RefreshShow(newFrameUnitWidth);
// clip 文本
ItemLabel.text = BuffGameplayEffectClipData.gameplayEffect
? BuffGameplayEffectClipData.gameplayEffect.name
: "【NULL】";
// 刷新面板显示
if (AbilityTimelineEditorWindow.Instance.CurrentInspectorObject == this)
AbilityTimelineEditorWindow.Instance.SetInspector(this);
}
public override void UpdateClipDataStartFrame(int newStartFrame)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.startFrame = newStartFrame;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public override void UpdateClipDataDurationFrame(int newDurationFrame)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.durationFrame = newDurationFrame;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public void UpdateClipDataBuff(GameplayEffectAsset newBuff)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.gameplayEffect = newBuff;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public override void OnTickView(int frameIndex, int startFrame, int endFrame)
{
}
public override Object DataInspector=> BuffGameplayEffectClipEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6dc3231fa98249a9a40eb1239dc40654
timeCreated: 1709177333

View File

@ -0,0 +1,91 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
using GAS.Runtime;
public class BuffGameplayEffectClipEditor : OdinEditorWindow
{
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private BuffGameplayEffectClip _clip;
public static BuffGameplayEffectClipEditor Create(BuffGameplayEffectClip clip)
{
var window = new BuffGameplayEffectClipEditor();
window._clip = clip;
window.Refresh();
return window;
}
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left, true)]
public string RunInfo;
[Delayed]
[BoxGroup]
[LabelText("Duration(f)")]
[OnValueChanged("OnDurationFrameChanged")]
public int Duration;
[Delayed]
[BoxGroup]
[AssetSelector]
[OnValueChanged("OnBuffChanged")]
public GameplayEffectAsset Buff;
[BoxGroup]
[Button]
[GUIColor(0.9f, 0.2f, 0.2f)]
void Delete()
{
_clip.Delete();
}
void Refresh()
{
Buff = _clip.BuffGameplayEffectClipData.gameplayEffect;
RunInfo = $"<b>Run(f):{_clip.BuffGameplayEffectClipData.startFrame} -> {_clip.BuffGameplayEffectClipData.EndFrame}</b>";
Duration = _clip.BuffGameplayEffectClipData.durationFrame;
}
private void OnDurationFrameChanged()
{
// 钳制
var max = AbilityAsset.FrameCount - _clip.BuffGameplayEffectClipData.startFrame;
var newValue = Mathf.Clamp(Duration, 1, max);
// 保存数据
_clip.UpdateClipDataDurationFrame(newValue);
_clip.RefreshShow(_clip.FrameUnitWidth);
Refresh();
}
private void OnBuffChanged()
{
_clip.UpdateClipDataBuff(Buff);
_clip.RefreshShow(_clip.FrameUnitWidth);
Refresh();
}
}
[CustomEditor(typeof(BuffGameplayEffectClipEditor))]
public class BuffGameplayEffectClipInspector : OdinEditorWithoutHeader
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("【注意】请确保设置的GameplayEffect类型为Durational或Infinite。 非持续类型的GameplayEffect不会生效。且GameplayEffect执行时会设置为Infinite执行策略 生命周期由Clip长度Duration决定。", MessageType.Info);
GUILayout.Space(20);
base.OnInspectorGUI();
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 893063b1dce94875864d8b866874c352
timeCreated: 1710403004

View File

@ -0,0 +1,92 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class BuffGameplayEffectTrack : TrackBase
{
private BuffGameplayEffectTrackData _buffGameplayEffectTrackData;
public override Type TrackDataType => typeof(BuffGameplayEffectTrackData);
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;
public BuffGameplayEffectTrackData BuffTrackDataForSave
{
get
{
for (var i = 0; i < AbilityAsset.BuffGameplayEffects.Count; i++)
if (AbilityAsset.BuffGameplayEffects[i] == _buffGameplayEffectTrackData)
return AbilityAsset.BuffGameplayEffects[i];
return null;
}
}
public override void TickView(int frameIndex, params object[] param)
{
}
public override void Init(VisualElement trackParent, VisualElement menuParent, float frameWidth,
TrackDataBase trackData)
{
base.Init(trackParent, menuParent, frameWidth, trackData);
_buffGameplayEffectTrackData = trackData as BuffGameplayEffectTrackData;
MenuText.text = _buffGameplayEffectTrackData.trackName;
}
public override void RefreshShow(float newFrameWidth)
{
base.RefreshShow(newFrameWidth);
foreach (var item in _trackItems) Track.Remove(((TrackClipBase)item).ClipVe);
_trackItems.Clear();
if (AbilityTimelineEditorWindow.Instance.AbilityAsset != null)
foreach (var clipEvent in _buffGameplayEffectTrackData.clipEvents)
{
var item = new BuffGameplayEffectClip();
item.InitTrackClip(this, Track, _frameWidth, clipEvent);
_trackItems.Add(item);
}
}
protected override void OnAddTrackItem(DropdownMenuAction action)
{
// 添加Clip数据
var clipEvent = new BuffGameplayEffectClipEvent
{
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x),
durationFrame = 5
};
BuffTrackDataForSave.clipEvents.Add(clipEvent);
// 刷新显示
var item = new BuffGameplayEffectClip();
item.InitTrackClip(this, Track, _frameWidth, clipEvent);
_trackItems.Add(item);
// 选中新Clip
item.ClipVe.OnSelect();
Debug.Log("[EX] Add a new Buff GameplayEffect Clip");
}
protected override void OnRemoveTrack(DropdownMenuAction action)
{
// 删除数据
AbilityAsset.BuffGameplayEffects.Remove(_buffGameplayEffectTrackData);
AbilityTimelineEditorWindow.Instance.Save();
// 删除显示
TrackParent.Remove(TrackRoot);
MenuParent.Remove(MenuRoot);
Debug.Log("[EX] Remove Durational Cue Track");
}
public override UnityEngine.Object DataInspector => BuffGameplayEffectTrackEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 228f546dcb634e75af2ff3d991d753bf
timeCreated: 1709177320

View File

@ -0,0 +1,59 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
public class BuffGameplayEffectTrackEditor:OdinEditorWindow
{
private BuffGameplayEffectTrack _track;
public static BuffGameplayEffectTrackEditor Create(BuffGameplayEffectTrack track)
{
var window = new BuffGameplayEffectTrackEditor();
window._track = track;
window.TrackName = track.BuffTrackDataForSave.trackName;
window.UpdateTrackInfo();
return window;
}
[Delayed]
[BoxGroup]
[LabelText("Name")]
[OnValueChanged("OnTrackNameChanged")]
public string TrackName;
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string TrackInfo;
void UpdateTrackInfo()
{
string info = "";
foreach (var clip in _track.BuffTrackDataForSave.clipEvents)
{
var clipName = clip.gameplayEffect != null ? clip.gameplayEffect.name : "NULL";
info += $"[{clipName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
}
TrackInfo = $"<b>{info}</b>";
}
void OnTrackNameChanged()
{
_track.BuffTrackDataForSave.trackName = TrackName;
_track.MenuText.text = TrackName;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(BuffGameplayEffectTrackEditor))]
public class BuffGameplayEffectTrackInspector:OdinEditorWithoutHeader
{
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6f8eef4e32cd448da39b120ab098b342
timeCreated: 1710401707

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 780123e8286b4fe1be3f0ad55c4375fb
timeCreated: 1709030487

View File

@ -0,0 +1,113 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class DurationalCueClip : TrackClip<DurationalCueTrack>
{
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public DurationalCueClipEvent DurationalCueClipData => clipData as DurationalCueClipEvent;
private DurationalCueClipEvent ClipDataForSave
{
get
{
var cueTrackDataForSave = track.CueTrackDataForSave;
for (var i = 0; i < cueTrackDataForSave.clipEvents.Count; i++)
if (cueTrackDataForSave.clipEvents[i] == DurationalCueClipData)
return track.CueTrackDataForSave.clipEvents[i];
return null;
}
}
public override void InitTrackClip(TrackBase track, VisualElement parent, float frameUnitWidth,
ClipEventBase clipData)
{
base.InitTrackClip(track, parent, frameUnitWidth, clipData);
//ve.RegisterFuncGetMinStartFrameIndex(MinStartFrameIndex);
//ve.RegisterFuncGetMaxEndFrameIndex(MaxEndFrameIndex);
}
public override void Delete()
{
var success = track.CueTrackDataForSave.clipEvents.Remove(DurationalCueClipData);
AbilityTimelineEditorWindow.Instance.Save();
if (!success) return;
track.RemoveTrackItem(this);
AbilityTimelineEditorWindow.Instance.SetInspector();
}
public override void RefreshShow(float newFrameUnitWidth)
{
base.RefreshShow(newFrameUnitWidth);
// clip 文本
ItemLabel.text = DurationalCueClipData.cue ? DurationalCueClipData.cue.name : "NULL!";
// 刷新面板显示
if (AbilityTimelineEditorWindow.Instance.CurrentInspectorObject == this)
AbilityTimelineEditorWindow.Instance.SetInspector(this);
}
public override void UpdateClipDataStartFrame(int newStartFrame)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.startFrame = newStartFrame;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public override void UpdateClipDataDurationFrame(int newDurationFrame)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.durationFrame = newDurationFrame;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
}
public void UpdateClipDataCue(GameplayCueDurational newCue)
{
var updatedClip = ClipDataForSave;
ClipDataForSave.cue = newCue;
AbilityTimelineEditorWindow.Instance.Save();
clipData = updatedClip;
RefreshShow(FrameUnitWidth);
}
public override void OnTickView(int frameIndex, int startFrame, int endFrame)
{
DurationalCueClipData.cue.OnEditorPreview(AbilityTimelineEditorWindow.Instance.PreviewObject, frameIndex,
startFrame, endFrame);
}
#region Clip Visual Element Event
// private int MinStartFrameIndex(float lastMainDragStartPos)
// {
// var minFrame = 0;
// foreach (var clipEvent in AbilityTimelineEditorWindow.Instance.AbilityAsset.AnimationData.animationClipData)
// if (clipEvent != ClipData && clipEvent.EndFrame <= lastMainDragStartPos)
// minFrame = Mathf.Max(minFrame, clipEvent.EndFrame);
//
// return minFrame;
// }
//
// private int MaxEndFrameIndex(float lastMainDragStartPos)
// {
// var maxFrame = AbilityTimelineEditorWindow.Instance.AbilityAsset.FrameCount;
// foreach (var clipEvent in AbilityTimelineEditorWindow.Instance.AbilityAsset.AnimationData.animationClipData)
// if (clipEvent != ClipData && clipEvent.startFrame >= lastMainDragStartPos + DurationFrame)
// maxFrame = Mathf.Min(maxFrame, clipEvent.startFrame);
//
// return maxFrame;
// }
#endregion
public override Object DataInspector => DurationalCueClipEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 34e96f7eaaca42f5b4252685084ad440
timeCreated: 1709030571

View File

@ -0,0 +1,86 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
using GAS.Runtime;
public class DurationalCueClipEditor : OdinEditorWindow
{
private TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private DurationalCueClip _clip;
public static DurationalCueClipEditor Create(DurationalCueClip clip)
{
var window = new DurationalCueClipEditor();
window._clip = clip;
window.Refresh();
return window;
}
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string RunInfo;
[Delayed]
[BoxGroup]
[LabelText("Duration(f)")]
[OnValueChanged("OnDurationFrameChanged")]
public int Duration;
[Delayed]
[BoxGroup]
[AssetSelector]
[OnValueChanged("OnCueChanged")]
public GameplayCueDurational Cue;
[BoxGroup]
[Button]
[GUIColor(0.9f, 0.2f, 0.2f)]
void Delete()
{
_clip.Delete();
}
void Refresh()
{
RunInfo = $"<b>Run(f):{_clip.DurationalCueClipData.startFrame} -> {_clip.DurationalCueClipData.EndFrame}</b>";
Duration = _clip.DurationalCueClipData.durationFrame;
Cue = _clip.DurationalCueClipData.cue;
}
private void OnDurationFrameChanged()
{
// 钳制
var max = AbilityAsset.FrameCount - _clip.DurationalCueClipData.startFrame;
Duration = Mathf.Clamp(Duration, 1, max);
_clip.UpdateClipDataDurationFrame(Duration);
_clip.RefreshShow(_clip.FrameUnitWidth);
Refresh();
}
private void OnCueChanged()
{
_clip.UpdateClipDataCue(Cue);
}
}
[CustomEditor(typeof(DurationalCueClipEditor))]
public class DurationalCueClipInspector : OdinEditorWithoutHeader
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("注意!! TimelineAbility下的持续性Cue 只会执行OnAddCue播放的第一帧OnRemoveCue播放的最后一帧及OnTick 和GameplayEffect相关的方法不会被执行", MessageType.Info);
GUILayout.Space(20);
base.OnInspectorGUI();
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 555dbb9258c24339b1246835568b4c94
timeCreated: 1710404017

View File

@ -0,0 +1,97 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class DurationalCueTrack : TrackBase
{
private DurationalCueTrackData _durationalCueTrackData;
public override Type TrackDataType => typeof(DurationalCueTrackData);
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;
public DurationalCueTrackData CueTrackDataForSave
{
get
{
for (var i = 0; i < AbilityAsset.DurationalCues.Count; i++)
if (AbilityAsset.DurationalCues[i] == _durationalCueTrackData)
return AbilityAsset.DurationalCues[i];
return null;
}
}
public override void TickView(int frameIndex, params object[] param)
{
foreach (var item in _trackItems)
{
var durationalCueClip = item as DurationalCueClip;
durationalCueClip?.OnTickView(frameIndex, durationalCueClip.StartFrameIndex,
durationalCueClip.EndFrameIndex);
}
}
public override void Init(VisualElement trackParent, VisualElement menuParent, float frameWidth,
TrackDataBase trackData)
{
base.Init(trackParent, menuParent, frameWidth, trackData);
_durationalCueTrackData = trackData as DurationalCueTrackData;
MenuText.text = _durationalCueTrackData.trackName;
}
public override void RefreshShow(float newFrameWidth)
{
base.RefreshShow(newFrameWidth);
foreach (var item in _trackItems) Track.Remove(((TrackClipBase)item).ClipVe);
_trackItems.Clear();
if (AbilityTimelineEditorWindow.Instance.AbilityAsset != null)
foreach (var clipEvent in _durationalCueTrackData.clipEvents)
{
var item = new DurationalCueClip();
item.InitTrackClip(this, Track, _frameWidth, clipEvent);
_trackItems.Add(item);
}
}
protected override void OnAddTrackItem(DropdownMenuAction action)
{
// 添加Clip数据
var clipEvent = new DurationalCueClipEvent
{
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x),
durationFrame = 5
};
CueTrackDataForSave.clipEvents.Add(clipEvent);
// 刷新显示
var item = new DurationalCueClip();
item.InitTrackClip(this, Track, _frameWidth, clipEvent);
_trackItems.Add(item);
// 选中新Clip
item.ClipVe.OnSelect();
Debug.Log("[EX] Add a new Durational Cue Clip");
}
protected override void OnRemoveTrack(DropdownMenuAction action)
{
// 删除数据
AbilityAsset.DurationalCues.Remove(_durationalCueTrackData);
AbilityTimelineEditorWindow.Instance.Save();
// 删除显示
TrackParent.Remove(TrackRoot);
MenuParent.Remove(MenuRoot);
Debug.Log("[EX] Remove Durational Cue Track");
}
public override UnityEngine.Object DataInspector => DurationalCueTrackEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 899a06ffd86a4b6e832ce82f279207bb
timeCreated: 1709030523

View File

@ -0,0 +1,59 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
public class DurationalCueTrackEditor:OdinEditorWindow
{
private DurationalCueTrack _track;
public static DurationalCueTrackEditor Create(DurationalCueTrack track)
{
var window = new DurationalCueTrackEditor();
window._track = track;
window.TrackName = track.CueTrackDataForSave.trackName;
window.UpdateTrackInfo();
return window;
}
[Delayed]
[BoxGroup]
[LabelText("Name")]
[OnValueChanged("OnTrackNameChanged")]
public string TrackName;
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string TrackInfo;
void UpdateTrackInfo()
{
string info = "";
foreach (var clip in _track.CueTrackDataForSave.clipEvents)
{
var clipName = clip.cue != null ? clip.cue.name : "NULL";
info += $"[{clipName}] Run(f):{clip.startFrame} -> {clip.EndFrame} \n";
}
TrackInfo = $"<b>{info}</b>";
}
void OnTrackNameChanged()
{
_track.CueTrackDataForSave.trackName = TrackName;
_track.MenuText.text = TrackName;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(DurationalCueTrackEditor))]
public class DurationalCueTrackInspector:OdinEditorWithoutHeader
{
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bd1f531896fc433285c1c6c53cda8f89
timeCreated: 1710401400

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bdd0f3a0481a4cc686203d7b0d8e0cd4
timeCreated: 1709105891

View File

@ -0,0 +1,79 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using Runtime;
using UnityEngine;
using UnityEngine.UIElements;
public class InstantCueMark : TrackMark<InstantCueTrack>
{
public InstantCueMarkEvent InstantCueMarkData => markData as InstantCueMarkEvent;
public InstantCueMarkEvent MarkDataForSave
{
get
{
var cueTrackDataForSave = track.InstantCueTrackData;
for (var i = 0; i < cueTrackDataForSave.markEvents.Count; i++)
if (cueTrackDataForSave.markEvents[i] == InstantCueMarkData)
return track.InstantCueTrackData.markEvents[i];
return null;
}
}
public override void Duplicate()
{
// 添加Mark数据
var startFrame = markData.startFrame < AbilityAsset.FrameCount
? markData.startFrame + 1
: markData.startFrame - 1;
startFrame = Mathf.Clamp(startFrame, 0, AbilityAsset.FrameCount);
var markEvent = new InstantCueMarkEvent
{
startFrame = startFrame,
cues = (markData as InstantCueMarkEvent)?.cues
};
track.InstantCueTrackData.markEvents.Add(markEvent);
AbilityTimelineEditorWindow.Instance.Save();
var mark = new InstantCueMark();
mark.InitTrackMark(track, track.Track, FrameUnitWidth, markEvent);
track.TrackItems.Add(mark);
mark.OnSelect();
}
public override void RefreshShow(float newFrameUnitWidth)
{
base.RefreshShow(newFrameUnitWidth);
ItemLabel.text = InstantCueMarkData.cues.Count.ToString();
}
public override Object DataInspector => InstantCueMarkEditor.Create(this);
public override void Delete()
{
var success = track.InstantCueTrackData.markEvents.Remove(InstantCueMarkData);
AbilityTimelineEditorWindow.Instance.Save();
if (!success) return;
track.RemoveTrackItem(this);
AbilityTimelineEditorWindow.Instance.SetInspector();
}
public override void UpdateMarkDataFrame(int newStartFrame)
{
var updatedClip = MarkDataForSave;
MarkDataForSave.startFrame = newStartFrame;
AbilityTimelineEditorWindow.Instance.Save();
markData = updatedClip;
}
public override void OnTickView(int frameIndex)
{
foreach (var cue in InstantCueMarkData.cues)
cue.OnEditorPreview(AbilityTimelineEditorWindow.Instance.PreviewObject, frameIndex,
InstantCueMarkData.startFrame);
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 006183b1e1fa46918aa44a1c0cda7a2c
timeCreated: 1709105913

View File

@ -0,0 +1,60 @@
using System.Collections.Generic;
using GAS.Runtime;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
namespace GAS.Editor
{
public class InstantCueMarkEditor : OdinEditorWindow
{
private InstantCueMark _mark;
public static InstantCueMarkEditor Create(InstantCueMark mark)
{
var window = CreateInstance<InstantCueMarkEditor>();
window._mark = mark;
window.UpdateMarkInfo();
return window;
}
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left, true)]
public string RunInfo;
[Delayed]
[BoxGroup]
[AssetSelector]
[ListDrawerSettings(ShowFoldout = true, DraggableItems = true)]
[OnValueChanged("OnCueListChanged")]
public List<GameplayCueInstant> Cues;
[BoxGroup]
[Button]
[GUIColor(0.9f, 0.2f, 0.2f)]
void Delete()
{
_mark.Delete();
}
void UpdateMarkInfo()
{
RunInfo = $"<b>Trigger(f):{_mark.InstantCueMarkData.startFrame}</b>";
Cues = _mark.InstantCueMarkData.cues;
}
void OnCueListChanged()
{
_mark.MarkDataForSave.cues = Cues;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(InstantCueMarkEditor))]
public class InstantCueMarkInspector : OdinEditorWithoutHeader
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2d3b0634bef24b3fb624c6a2184b99e9
timeCreated: 1710411938

View File

@ -0,0 +1,124 @@

#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using GAS.Runtime;
public class InstantCueTrack : TrackBase
{
private InstantCueTrackData _instantCuesTrackData;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
public InstantCueTrackData InstantCueTrackData
{
get
{
for (var i = 0; i < AbilityAsset.InstantCues.Count; i++)
if (AbilityAsset.InstantCues[i] == _instantCuesTrackData)
return AbilityAsset.InstantCues[i];
return null;
}
}
public override Type TrackDataType => typeof(InstantCueTrackData);
protected override Color TrackColor => new(0.1f, 0.2f, 0.6f, 0.2f);
protected override Color MenuColor => new(0.1f, 0.6f, 0.9f, 0.9f);
public override void Init(VisualElement trackParent, VisualElement menuParent, float frameWidth,
TrackDataBase trackData)
{
base.Init(trackParent, menuParent, frameWidth, trackData);
_instantCuesTrackData = trackData as InstantCueTrackData;
MenuText.text = _instantCuesTrackData.trackName;
}
public override void TickView(int frameIndex, params object[] param)
{
foreach (var item in _trackItems)
((TrackMarkBase)item).OnTickView(frameIndex);
}
public override void RefreshShow(float newFrameWidth)
{
base.RefreshShow(newFrameWidth);
foreach (var item in _trackItems) Track.Remove(((TrackMarkBase)item).Ve);
_trackItems.Clear();
if (AbilityTimelineEditorWindow.Instance.AbilityAsset == null) return;
foreach (var markEvent in _instantCuesTrackData.markEvents)
{
var item = new InstantCueMark();
item.InitTrackMark(this, Track, _frameWidth, markEvent);
_trackItems.Add(item);
}
}
// public override VisualElement Inspector()
// {
// var inspector = TrackInspectorUtil.CreateTrackInspector();
//
// var trackName =
// TrackInspectorUtil.CreateTextField("Name", _instantCuesTrackData.trackName,
// e =>
// {
// _instantCuesTrackData.trackName = e.newValue;
// MenuText.text = _instantCuesTrackData.trackName;
// AbilityTimelineEditorWindow.Instance.Save();
// });
// inspector.Add(trackName);
//
// foreach (var mark in _instantCuesTrackData.markEvents)
// {
// var markFrame = TrackInspectorUtil.CreateLabel($"Trigger(f):{mark.startFrame}");
// inspector.Add(markFrame);
// foreach (var c in mark.cues)
// {
// var cueName = c != null ? c.name : "NULL";
// var cueCount = TrackInspectorUtil.CreateLabel($" |-> Cue:{cueName}");
// inspector.Add(cueCount);
// }
// }
//
// return inspector;
// }
protected override void OnAddTrackItem(DropdownMenuAction action)
{
// 添加Mark数据
var markEvent = new InstantCueMarkEvent
{
startFrame = GetTrackIndexByMouse(action.eventInfo.localMousePosition.x),
cues = new List<GameplayCueInstant>()
};
InstantCueTrackData.markEvents.Add(markEvent);
// 刷新显示
var mark = new InstantCueMark();
mark.InitTrackMark(this, Track, _frameWidth, markEvent);
_trackItems.Add(mark);
mark.OnSelect();
Debug.Log("[EX] Add Instant Cue Mark");
}
protected override void OnRemoveTrack(DropdownMenuAction action)
{
// 删除数据
AbilityAsset.InstantCues.Remove(_instantCuesTrackData);
AbilityTimelineEditorWindow.Instance.Save();
// 删除显示
TrackParent.Remove(TrackRoot);
MenuParent.Remove(MenuRoot);
Debug.Log("[EX] Remove Instant Cue Track");
}
public override UnityEngine.Object DataInspector => InstantCueTrackEditor.Create(this);
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f519c65b0761462ebb77625d5081eddf
timeCreated: 1709105875

View File

@ -0,0 +1,65 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using UnityEditor;
using Editor;
using UnityEngine;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector;
public class InstantCueTrackEditor:OdinEditorWindow
{
private InstantCueTrack _track;
public static InstantCueTrackEditor Create(InstantCueTrack track)
{
var window = new InstantCueTrackEditor();
window._track = track;
window.TrackName = track.InstantCueTrackData.trackName;
window.UpdateTrackInfo();
return window;
}
[Delayed]
[BoxGroup]
[LabelText("Name")]
[OnValueChanged("OnTrackNameChanged")]
public string TrackName;
[BoxGroup]
[HideLabel]
[DisplayAsString(TextAlignment.Left,true)]
public string TrackInfo;
void UpdateTrackInfo()
{
string info = "";
foreach (var mark in _track.InstantCueTrackData.markEvents)
{
info += $"Trigger(f):{mark.startFrame} \n";
foreach (var c in mark.cues)
{
var cueName = c != null ? c.name : "NULL";
info += $" |-> {cueName}\n";
}
info += "\n";
}
TrackInfo = $"<b>{info}</b>";
}
void OnTrackNameChanged()
{
Debug.Log("TrackName Changed");
_track.InstantCueTrackData.trackName = TrackName;
_track.MenuText.text = TrackName;
AbilityTimelineEditorWindow.Instance.Save();
}
}
[CustomEditor(typeof(InstantCueTrackEditor))]
public class InstantCueTrackInspector:OdinEditorWithoutHeader
{
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d5865319f3db4441b7c249a5ed3127b2
timeCreated: 1710387029

View File

@ -0,0 +1,118 @@
#if UNITY_EDITOR
namespace GAS.Editor
{
using System;
using Runtime;
using UnityEngine;
using UnityEngine.UIElements;
public class MenuTrack : TrackBase
{
private Color _menuColor;
private Color _trackColor;
private Type _trackDataType;
private Type _trackType;
private static TimelineAbilityAssetBase AbilityAsset => AbilityTimelineEditorWindow.Instance.AbilityAsset;
private static AbilityTimelineEditorConfig Config => AbilityTimelineEditorWindow.Instance.Config;
private static TimelineTrackView TrackView => AbilityTimelineEditorWindow.Instance.TrackView;
public override Type TrackDataType { get; }
protected override Color TrackColor => _trackColor;
protected override Color MenuColor => _menuColor;
protected override string MenuAssetGuid => "944173d62639bb04b8b64be960c8ef29";
public Button AddButton { get; private set; }
public void Init(VisualElement trackParent, VisualElement menuParent, float frameWidth, Type trackType,
Type trackDataType,
string label, Color trackColor, Color menuColor)
{
_trackType = trackType;
_trackDataType = trackDataType;
_trackColor = trackColor;
_menuColor = menuColor;
base.Init(trackParent, menuParent, frameWidth, null);
AddButton = MenuRoot.Q<Button>("BtnAdd");
AddButton.style.display = DisplayStyle.Flex;
AddButton.clickable.clicked += OnClickAddTrack;
MenuBox.style.left = 0;
MenuBox.style.right = new StyleLength(StyleKeyword.Auto);
MenuText.text = label;
Track.style.backgroundColor = new Color(0, 0, 0, 0);
BoundingBox.style.backgroundColor = MenuColor;
const int height = 20;
MenuRoot.style.height = height;
MenuRoot.style.minHeight = height;
MenuRoot.style.maxHeight = height;
TrackRoot.style.height = height;
TrackRoot.style.minHeight = height;
TrackRoot.style.maxHeight = height;
}
private void OnClickAddTrack()
{
// 创建View
var track = (TrackBase)Activator.CreateInstance(_trackType);
if (track.IsFixedTrack()) return;
// 创建Data
var data = (TrackDataBase)Activator.CreateInstance(_trackDataType);
data.DefaultInit();
data.AddToAbilityAsset(AbilityAsset);
// 初始化View
track.Init(TrackParent, MenuParent, Config.FrameUnitWidth, data);
TrackParent.Remove(track.TrackRoot);
MenuParent.Remove(track.MenuRoot);
var index = CurrentAddIndex();
TrackParent.Insert(index, track.TrackRoot);
MenuParent.Insert(index, track.MenuRoot);
TrackView.TrackList.Add(track);
Debug.Log("[EX] Add a new track:" + _trackType.Name);
AbilityAsset.Save();
}
public override void TickView(int frameIndex, params object[] param)
{
}
protected override void OnAddTrackItem(DropdownMenuAction action)
{
}
protected override void OnRemoveTrack(DropdownMenuAction action)
{
}
private int CurrentAddIndex()
{
var baseIndex = TrackParent.IndexOf(TrackRoot);
if (_trackType == typeof(InstantCueTrack))
return baseIndex + (AbilityAsset.InstantCues?.Count ?? 0);
if (_trackType == typeof(TaskMarkEventTrack))
return baseIndex + (AbilityAsset.InstantTasks?.Count ?? 0);
if (_trackType == typeof(ReleaseGameplayEffectTrack))
return baseIndex + (AbilityAsset.ReleaseGameplayEffect?.Count ?? 0);
if (_trackType == typeof(BuffGameplayEffectTrack))
return baseIndex + (AbilityAsset.BuffGameplayEffects?.Count ?? 0);
if (_trackType == typeof(TaskClipEventTrack))
return baseIndex + (AbilityAsset.OngoingTasks?.Count ?? 0);
if (_trackType == typeof(DurationalCueTrack))
return baseIndex + (AbilityAsset.DurationalCues?.Count ?? 0);
return -1;
}
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ccfa764d58764d56b5b0ecd3cb16e064
timeCreated: 1709798443

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf6fdec790cf4bfe9e0c9df3e0534071
timeCreated: 1709133883

View File

@ -0,0 +1,130 @@
using GAS.Runtime;
using UnityEngine;
namespace GAS.Editor
{
public class ReleaseGameplayEffectMark : TrackMark<ReleaseGameplayEffectTrack>
{
private new ReleaseGameplayEffectMarkEvent MarkData => markData as ReleaseGameplayEffectMarkEvent;
public ReleaseGameplayEffectMarkEvent MarkDataForSave
{
get
{
var trackDataForSave = track.ReleaseGameplayEffectTrackData;
for (var i = 0; i < trackDataForSave.markEvents.Count; i++)
if (trackDataForSave.markEvents[i] == MarkData)
return track.ReleaseGameplayEffectTrackData.markEvents[i];
return null;
}
}
public override void Duplicate()
{
var startFrame = markData.startFrame < AbilityAsset.FrameCount
? markData.startFrame + 1
: markData.startFrame - 1;
startFrame = Mathf.Clamp(startFrame, 0, AbilityAsset.FrameCount);
var markEvent = new ReleaseGameplayEffectMarkEvent
{
startFrame = startFrame,
jsonTargetCatcher = (markData as ReleaseGameplayEffectMarkEvent)?.jsonTargetCatcher,
gameplayEffectAssets = (markData as ReleaseGameplayEffectMarkEvent)?.gameplayEffectAssets
};
track.ReleaseGameplayEffectTrackData.markEvents.Add(markEvent);
AbilityTimelineEditorWindow.Instance.Save();
var mark = new ReleaseGameplayEffectMark();
mark.InitTrackMark(track, track.Track, FrameUnitWidth, markEvent);
track.TrackItems.Add(mark);
mark.OnSelect();
}
public override void RefreshShow(float newFrameUnitWidth)
{
base.RefreshShow(newFrameUnitWidth);
ItemLabel.text = MarkData.gameplayEffectAssets.Count.ToString();
}
public override Object DataInspector => ReleaseGameplayEffectMarkEditor.Create(this);
// public override VisualElement Inspector()
// {
// var inspector = TrackInspectorUtil.CreateTrackInspector();
// var markFrame = TrackInspectorUtil.CreateLabel($"Trigger(f):{markData.startFrame}");
// inspector.Add(markFrame);
//
// // 目标捕捉器
// // 选择项所有TargetCatcher子类
// var targetCatcherSonTypes = ReleaseGameplayEffectMarkEvent.TargetCatcherSonTypes;
// var targetCatcherSons = targetCatcherSonTypes.Select(sonType => sonType.FullName).ToList();
// var catcherTypeSelector =
// TrackInspectorUtil.CreateDropdownField("TargetCatcher", targetCatcherSons,
// MarkData.jsonTargetCatcher.Type, OnTargetCatcherChanged);
// inspector.Add(catcherTypeSelector);
//
// // 根据选择的TargetCatcher子类显示对应的属性
// var targetCatcher = MarkData.LoadTargetCatcher();
// if (TargetCatcherInspectorMap.TryGetValue(targetCatcher.GetType(), out var inspectorType))
// {
// var targetCatcherInspector =
// (TargetCatcherInspector)Activator.CreateInstance(inspectorType, targetCatcher);
// inspector.Add(targetCatcherInspector.Inspector());
// }
// else
// {
// Debug.LogError($"[EX] TargetCatcherInspector not found: {targetCatcher.GetType()}");
// }
//
// // GameplayEffects
// var list = TrackInspectorUtil.CreateObjectListView("GameplayEffects", MarkData.gameplayEffectAssets,
// OnGameplayEffectAssetChanged);
// inspector.Add(list);
//
// return inspector;
// }
// private void OnTargetCatcherChanged(ChangeEvent<string> evt)
// {
// MarkDataForSave.jsonTargetCatcher.Type = evt.newValue;
// MarkDataForSave.jsonTargetCatcher.Data = null;
// AbilityTimelineEditorWindow.Instance.Save();
//
// AbilityTimelineEditorWindow.Instance.TimelineInspector.RefreshInspector();
// }
//
// private void OnGameplayEffectAssetChanged(int index, ChangeEvent<Object> evt)
// {
// var gameplayEffectAsset = evt.newValue as GameplayEffectAsset;
// MarkDataForSave.gameplayEffectAssets[index] = gameplayEffectAsset;
// AbilityTimelineEditorWindow.Instance.Save();
//
// RefreshShow(FrameUnitWidth);
// }
public override void Delete()
{
var success = track.ReleaseGameplayEffectTrackData.markEvents.Remove(MarkData);
AbilityTimelineEditorWindow.Instance.Save();
if (!success) return;
track.RemoveTrackItem(this);
AbilityTimelineEditorWindow.Instance.SetInspector();
}
public override void UpdateMarkDataFrame(int newStartFrame)
{
var updatedClip = MarkDataForSave;
MarkDataForSave.startFrame = newStartFrame;
AbilityTimelineEditorWindow.Instance.Save();
markData = updatedClip;
}
public override void OnTickView(int frameIndex)
{
if (frameIndex == StartFrameIndex)
{
var targetCatcher = MarkData.TargetCatcher;
targetCatcher.OnEditorPreview(AbilityTimelineEditorWindow.Instance.PreviewObject);
}
}
}
}

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