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

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

View File

@@ -0,0 +1,12 @@
namespace GAS.Runtime
{
public enum AbilityActivateResult
{
Success,
FailHasActivated,
FailTagRequirement,
FailCost,
FailCooldown,
FailOtherReason
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0c4e3a515a6c4662a1d3a3abd8aad589
timeCreated: 1711357854

View File

@@ -0,0 +1,78 @@
using System;
using UnityEngine;
namespace GAS.Runtime
{
public enum EffectCenterType
{
SelfOffset,
WorldSpace,
TargetOffset
}
public static class AbilityAreaUtil
{
[Obsolete("请使用OverlapBox2DNonAlloc方法来避免产生垃圾收集GC。")]
public static Collider2D[] OverlapBox2D(this AbilitySystemComponent asc, Vector2 offset, Vector2 size,
float angle, int layerMask, Transform relativeTransform = null)
{
relativeTransform ??= asc.transform;
var center = (Vector2)relativeTransform.position;
offset.x *= relativeTransform.lossyScale.x > 0 ? 1 : -1;
center += offset;
angle += asc.transform.eulerAngles.z;
return Physics2D.OverlapBoxAll(center, size, angle, layerMask);
}
public static int OverlapBox2DNonAlloc(this AbilitySystemComponent asc, Vector2 offset, Vector2 size,
float angle, Collider2D[] results, int layerMask, Transform relativeTransform = null)
{
relativeTransform ??= asc.transform;
var center = (Vector2)relativeTransform.position;
offset.x *= relativeTransform.lossyScale.x > 0 ? 1 : -1;
center += offset;
angle += asc.transform.eulerAngles.z;
var count = Physics2D.OverlapBoxNonAlloc(center, size, angle, results, layerMask);
return count;
}
public static int TimelineAbilityOverlapBox2D(this TimelineAbilitySpec spec,
Vector2 offset, Vector2 size, float angle, int layerMask, Collider2D[] results,
EffectCenterType centerType, Transform relativeTransform = null)
{
return centerType switch
{
EffectCenterType.SelfOffset => spec.Owner.OverlapBox2DNonAlloc(offset, size, angle, results, layerMask, relativeTransform),
EffectCenterType.WorldSpace => Physics2D.OverlapBoxNonAlloc(offset, size, angle, results, layerMask),
EffectCenterType.TargetOffset => spec.Target.OverlapBox2DNonAlloc(offset, size, angle, results, layerMask, relativeTransform),
_ => 0
};
}
[Obsolete("请使用OverlapCircle2DNonAlloc方法来避免产生垃圾收集GC。")]
public static Collider2D[] OverlapCircle2D(this AbilitySystemComponent asc, Vector2 offset, float radius,
int layerMask, Transform relativeTransform = null)
{
relativeTransform ??= asc.transform;
var center = (Vector2)relativeTransform.position;
offset.x *= relativeTransform.lossyScale.x > 0 ? 1 : -1;
center += offset;
return Physics2D.OverlapCircleAll(center, radius, layerMask);
}
public static int OverlapCircle2DNonAlloc(this AbilitySystemComponent asc, Vector2 offset, float radius,
Collider2D[] results, int layerMask, Transform relativeTransform = null)
{
relativeTransform ??= asc.transform;
var center = (Vector2)relativeTransform.position;
offset.x *= relativeTransform.lossyScale.x > 0 ? 1 : -1;
center += offset;
var count = Physics2D.OverlapCircleNonAlloc(center, radius, results, layerMask);
return count;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0df3b800fe5b41cbb03fb6d14234e584
timeCreated: 1709711068

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections;
using System.Linq;
using GAS.General;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
namespace GAS.Runtime
{
public abstract class AbilityAsset : ScriptableObject
{
protected const int WIDTH_LABEL = 70;
public abstract Type AbilityType();
[TitleGroup("Base")]
[HorizontalGroup("Base/H1", Width = 1 - 0.618f)]
[TabGroup("Base/H1/V1", "Summary", SdfIconType.InfoSquareFill, TextColor = "#0BFFC5", Order = 1)]
[HideLabel]
[MultiLineProperty(10)]
public string Description;
[TabGroup("Base/H1/V2", "General", SdfIconType.AwardFill, TextColor = "#FF7F00", Order = 2)]
[LabelText("所属能力", SdfIconType.FileCodeFill)]
[LabelWidth(WIDTH_LABEL)]
[ShowInInspector]
[ValidateInput("@AbilityType() != null", "Ability Class is NULL!!! Please check.")]
[PropertyOrder(-1)]
public string InstanceAbilityClassFullName => AbilityType() != null ? AbilityType().FullName : null;
#if UNITY_EDITOR
[TabGroup("Base/H1/V2", "General")]
[TabGroup("Base/H1/V2", "Detail", SdfIconType.TicketDetailedFill, TextColor = "#BC2FDE")]
[LabelText("类型名称", SdfIconType.FileCodeFill)]
[LabelWidth(WIDTH_LABEL)]
[ShowInInspector]
[PropertyOrder(-1)]
public string TypeName => GetType().Name;
[TabGroup("Base/H1/V2", "Detail")]
[LabelText("类型全名", SdfIconType.FileCodeFill)]
[LabelWidth(WIDTH_LABEL)]
[ShowInInspector]
[PropertyOrder(-1)]
public string TypeFullName => GetType().FullName;
[TabGroup("Base/H1/V2", "Detail")]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, ShowPaging = false)]
[ShowInInspector]
[LabelText("继承关系")]
[LabelWidth(WIDTH_LABEL)]
[PropertyOrder(-1)]
public string[] InheritanceChain => GetType().GetInheritanceChain().Reverse().ToArray();
#endif
[TabGroup("Base/H1/V2", "General", SdfIconType.AwardFill)]
[InfoBox(GASTextDefine.TIP_UNAME, InfoMessageType.None)]
[LabelText("U-Name", SdfIconType.Fingerprint)]
[LabelWidth(WIDTH_LABEL)]
[ValidateInput("@GAS.General.Validation.Validations.IsValidVariableName($value)", "无效的名字 - 不符合C#标识符命名规则")]
[InlineButton("@UniqueName = name", "Auto", Icon = SdfIconType.Hammer)]
public string UniqueName;
[TabGroup("Base/H1/V2", "General")]
[Title("消耗&冷却", bold: true)]
[LabelWidth(WIDTH_LABEL)]
[AssetSelector]
[LabelText(SdfIconType.HeartHalf, Text = GASTextDefine.ABILITY_EFFECT_COST)]
public GameplayEffectAsset Cost;
[TabGroup("Base/H1/V2", "General")]
[LabelWidth(WIDTH_LABEL)]
[AssetSelector]
[LabelText(SdfIconType.StopwatchFill, Text = GASTextDefine.ABILITY_EFFECT_CD)]
public GameplayEffectAsset Cooldown;
[TabGroup("Base/H1/V2", "General")]
[LabelWidth(WIDTH_LABEL)]
[LabelText(SdfIconType.ClockFill, Text = GASTextDefine.ABILITY_CD_TIME)]
[Unit(Units.Second)]
public float CooldownTime;
// Tags
[TabGroup("Base/H1/V3", "Tags", SdfIconType.TagsFill, TextColor = "#45B1FF", Order = 3)]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@AssetTags = TagHelper.Sort($value)")]
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
[Tooltip("描述性质的标签用来描述Ability的特性表现比如伤害、治疗、控制等。")]
[FormerlySerializedAs("AssetTag")]
public GameplayTag[] AssetTags;
[Space]
[TabGroup("Base/H1/V3", "Tags")]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@CancelAbilityTags = TagHelper.Sort($value)")]
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
[LabelText("CancelAbility With Tags ")]
[Tooltip("Ability激活时Ability持有者当前持有的所有Ability中拥有【任意】这些标签的Ability会被取消。")]
public GameplayTag[] CancelAbilityTags;
[Space]
[TabGroup("Base/H1/V3", "Tags")]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@BlockAbilityTags = TagHelper.Sort($value)")]
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
[LabelText("BlockAbility With Tags ")]
[Tooltip("Ability激活时Ability持有者当前持有的所有Ability中拥有【任意】这些标签的Ability会被阻塞激活。")]
public GameplayTag[] BlockAbilityTags;
[Space]
[TabGroup("Base/H1/V3", "Tags")]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@ActivationOwnedTags = TagHelper.Sort($value)")]
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
[Tooltip("Ability激活时持有者会获得这些标签Ability被失活时这些标签也会被移除。")]
[FormerlySerializedAs("ActivationOwnedTag")]
public GameplayTag[] ActivationOwnedTags;
[Space]
[TabGroup("Base/H1/V3", "Tags")]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@ActivationRequiredTags = TagHelper.Sort($value)")]
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
[Tooltip("Ability只有在其拥有者拥有【所有】这些标签时才可激活。")]
public GameplayTag[] ActivationRequiredTags;
[Space]
[TabGroup("Base/H1/V3", "Tags")]
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
[CustomContextMenu("排序", "@ActivationBlockedTags = TagHelper.Sort($value)")]
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
[Tooltip("Ability在其拥有者拥有【任意】这些标签时不能被激活。")]
public GameplayTag[] ActivationBlockedTags;
// public GameplayTag[] SourceRequiredTags;
// public GameplayTag[] SourceBlockedTags;
// public GameplayTag[] TargetRequiredTags;
// public GameplayTag[] TargetBlockedTags;
}
public abstract class AbilityAssetT<T> : AbilityAsset where T : class
{
public sealed override Type AbilityType()
{
return typeof(T);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 544108d1f0e34b7fb2a77be04ed89be6
timeCreated: 1703762750

View File

@@ -0,0 +1,121 @@
using System.Collections.Generic;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
public class AbilityContainer
{
private readonly AbilitySystemComponent _owner;
private readonly Dictionary<string, AbilitySpec> _abilities = new();
public AbilityContainer(AbilitySystemComponent owner)
{
_owner = owner;
}
public void Tick()
{
var abilitySpecs = ObjectPool.Instance.Fetch<List<AbilitySpec>>();
abilitySpecs.AddRange(_abilities.Values);
foreach (var abilitySpec in abilitySpecs)
{
abilitySpec.Tick();
}
abilitySpecs.Clear();
ObjectPool.Instance.Recycle(abilitySpecs);
}
public void GrantAbility(AbstractAbility ability)
{
if (_abilities.ContainsKey(ability.Name)) return;
var abilitySpec = ability.CreateSpec(_owner);
_abilities.Add(ability.Name, abilitySpec);
}
public void RemoveAbility(AbstractAbility ability)
{
RemoveAbility(ability.Name);
}
public void RemoveAbility(string abilityName)
{
if (!_abilities.ContainsKey(abilityName)) return;
EndAbility(abilityName);
_abilities[abilityName].Dispose();
_abilities.Remove(abilityName);
}
public bool TryActivateAbility(string abilityName, object arg = null, GameplayEffectSpec gameplayEffectSpec = null)
{
if (!_abilities.ContainsKey(abilityName))
{
// 开发指南:
// 如果你的Preset里配置了固有技能却没该技能(甚至_abilities里一个技能都没有)
// 可能是你忘记调用ASC::Init(), 请检查AbilitySystemComponent的初始化
// 通常我们使用ASC::InitWithPreset()来间接调用ASC::Init()执行初始化
#if UNITY_EDITOR
// 这个输出可以删掉, 某些情况下确实会尝试激活不存在的技能(失败了也无所谓), 但是对开发期间的调试有帮助
Debug.LogWarning(
$"you are trying to activate an ability that does not exist: " +
$"abilityName=\"{abilityName}\", GameObject=\"{_owner.name}\", " +
$"Preset={(_owner.Preset != null ? _owner.Preset.name : "null")}");
#endif
return false;
}
if (!_abilities[abilityName].TryActivateAbility(arg, gameplayEffectSpec)) return false;
var tags = _abilities[abilityName].Ability.Tag.CancelAbilitiesWithTags;
foreach (var kv in _abilities)
{
var abilityTag = kv.Value.Ability.Tag;
if (abilityTag.AssetTag.HasAnyTags(tags))
{
_abilities[kv.Key].TryCancelAbility();
}
}
return true;
}
public void EndAbility(string abilityName)
{
if (!_abilities.ContainsKey(abilityName)) return;
_abilities[abilityName].TryEndAbility();
}
public void CancelAbility(string abilityName)
{
if (!_abilities.ContainsKey(abilityName)) return;
_abilities[abilityName].TryCancelAbility();
}
private void CancelAbilitiesByTag(in GameplayTagSet tags)
{
foreach (var kv in _abilities)
{
var abilityTag = kv.Value.Ability.Tag;
if (abilityTag.AssetTag.HasAnyTags(tags))
{
_abilities[kv.Key].TryCancelAbility();
}
}
}
public Dictionary<string, AbilitySpec> AbilitySpecs() => _abilities;
public void CancelAllAbilities()
{
foreach (var kv in _abilities)
_abilities[kv.Key].TryCancelAbility();
}
public bool HasAbility(string abilityName) => _abilities.ContainsKey(abilityName);
public bool HasAbility(AbstractAbility ability) => HasAbility(ability.Name);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 45b14d2f214f400ab305f80a6f8cb3a2
timeCreated: 1703663556

View File

@@ -0,0 +1,10 @@
using System;
namespace GAS.Runtime
{
public struct AbilityInstanceInfo
{
public AbilityAsset abilityAsset;
public Type abilityType;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 99ad85c3caba4604b9950ae1105e85b4
timeCreated: 1706090943

View File

@@ -0,0 +1,243 @@
using System;
namespace GAS.Runtime
{
public abstract class AbilitySpec
{
/// <summary>
/// 获取激活能力时传递给能力的参数。
/// 在技能过程中不应该修改, 考虑使用UserData
/// </summary>
/// <remarks>
/// <para>旧版本为 object[] 类型, 有内存分配和装箱/拆箱问题, 应自行创建一个数据结构(推荐record)来传参.</para>
/// </remarks>
public object AbilityArgument { get; private set; }
/// <summary>
/// 仅限GrantedAbility, 激活能力时传递给能力的效果规格。
/// 可以通过给gameplayEffectSpec添加自定义数据(UserData)来传递更多信息。
/// </summary>
public EntityRef<GameplayEffectSpec> GameplayEffectSpec { get; private set; }
/// <summary>
/// 获取或设置与能力关联的自定义数据。
/// </summary>
/// <remarks>
/// <para>此属性用于存储能力的自定义信息,以便在能力的不同任务之间共享数据。</para>
/// <para>例如,可以在一个技能的任务(AbilityTask)中设置此数据,然后在同一个技能的另一个任务(AbilityTask)中检索和使用该数据。</para>
/// </remarks>
public object UserData { get; set; }
public AbilitySpec(AbstractAbility ability, AbilitySystemComponent owner)
{
Ability = ability;
Owner = owner;
}
public virtual void Dispose()
{
_onActivateResult = null;
_onEndAbility = null;
_onCancelAbility = null;
}
public AbstractAbility Ability { get; }
public AbilitySystemComponent Owner { get; protected set; }
public int Level { get; protected set; }
public bool IsActive { get; private set; }
public int ActiveCount { get; private set; }
protected event Action<AbilityActivateResult> _onActivateResult;
protected event Action _onEndAbility;
protected event Action _onCancelAbility;
public void RegisterActivateResult(Action<AbilityActivateResult> onActivateResult)
{
_onActivateResult += onActivateResult;
}
public void UnregisterActivateResult(Action<AbilityActivateResult> onActivateResult)
{
_onActivateResult -= onActivateResult;
}
public void RegisterEndAbility(Action onEndAbility)
{
_onEndAbility += onEndAbility;
}
public void UnregisterEndAbility(Action onEndAbility)
{
_onEndAbility -= onEndAbility;
}
public void RegisterCancelAbility(Action onCancelAbility)
{
_onCancelAbility += onCancelAbility;
}
public void UnregisterCancelAbility(Action onCancelAbility)
{
_onCancelAbility -= onCancelAbility;
}
public virtual void SetLevel(int level)
{
Level = level;
}
public virtual AbilityActivateResult CanActivate()
{
if (IsActive) return AbilityActivateResult.FailHasActivated;
if (!CheckGameplayTagsValidTpActivate()) return AbilityActivateResult.FailTagRequirement;
if (!CheckCost()) return AbilityActivateResult.FailCost;
if (CheckCooldown().TimeRemaining > 0) return AbilityActivateResult.FailCooldown;
return AbilityActivateResult.Success;
}
private bool CheckGameplayTagsValidTpActivate()
{
var hasAllTags = Owner.HasAllTags(Ability.Tag.ActivationRequiredTags);
var notHasAnyTags = !Owner.HasAnyTags(Ability.Tag.ActivationBlockedTags);
var notBlockedByOtherAbility = true;
foreach (var kv in Owner.AbilityContainer.AbilitySpecs())
{
var abilitySpec = kv.Value;
if (abilitySpec.IsActive)
if (Ability.Tag.AssetTag.HasAnyTags(abilitySpec.Ability.Tag.BlockAbilitiesWithTags))
{
notBlockedByOtherAbility = false;
break;
}
}
return hasAllTags && notHasAnyTags && notBlockedByOtherAbility;
}
protected virtual bool CheckCost()
{
if (Ability.Cost == null) return true;
var costSpec = Ability.Cost.CreateSpec(Owner, Owner, Level);
if (costSpec.Value == null) return false;
if (Ability.Cost.DurationPolicy != EffectsDurationPolicy.Instant) return true;
foreach (var modifier in Ability.Cost.Modifiers)
{
// 常规来说消耗是减法, 但是加一个负数也应该被视为减法
if (modifier.Operation != GEOperation.Add && modifier.Operation != GEOperation.Minus) continue;
var costValue = modifier.CalculateMagnitude(costSpec, modifier.ModiferMagnitude);
var attributeCurrentValue =
Owner.GetAttributeCurrentValue(modifier.AttributeSetName, modifier.AttributeShortName);
if (modifier.Operation == GEOperation.Add)
if (attributeCurrentValue + costValue < 0)
return false;
if (modifier.Operation == GEOperation.Minus)
if (attributeCurrentValue - costValue < 0)
return false;
}
return true;
}
protected virtual CooldownTimer CheckCooldown()
{
return Ability.Cooldown == null
? new CooldownTimer { TimeRemaining = 0, Duration = Ability.CooldownTime }
: Owner.CheckCooldownFromTags(Ability.Cooldown.TagContainer.GrantedTags);
}
/// <summary>
/// Some skills include wind-up and follow-through, where the wind-up may be interrupted, causing the skill not to be
/// successfully released.
/// Therefore, the actual timing and logic of skill release (triggering costs and initiating cooldown) should be
/// determined by developers within the AbilitySpec,
/// rather than being systematically standardized.
/// </summary>
public void DoCost()
{
if (Ability.Cost != null) Owner.ApplyGameplayEffectToSelf(Ability.Cost);
if (Ability.Cooldown != null)
{
var cdSpec = Owner.ApplyGameplayEffectToSelf(Ability.Cooldown);
cdSpec.Value.SetDuration(Ability.CooldownTime); // Actually, it should be set by the ability's cooldown time.
}
}
public virtual bool TryActivateAbility(object arg = null, GameplayEffectSpec gameplayEffectSpec = null)
{
AbilityArgument = arg;
GameplayEffectSpec = gameplayEffectSpec;
var result = CanActivate();
var success = result == AbilityActivateResult.Success;
if (success)
{
IsActive = true;
ActiveCount++;
Owner.GameplayTagAggregator.ApplyGameplayAbilityDynamicTag(this);
ActivateAbility();
}
_onActivateResult?.Invoke(result);
return success;
}
public virtual void TryEndAbility()
{
if (!IsActive) return;
IsActive = false;
Owner.GameplayTagAggregator.RestoreGameplayAbilityDynamicTags(this);
EndAbility();
_onEndAbility?.Invoke();
}
public virtual void TryCancelAbility()
{
if (!IsActive) return;
IsActive = false;
Owner.GameplayTagAggregator.RestoreGameplayAbilityDynamicTags(this);
CancelAbility();
_onCancelAbility?.Invoke();
}
public void Tick()
{
if (IsActive)
{
AbilityTick();
}
}
protected virtual void AbilityTick()
{
}
public abstract void ActivateAbility();
public abstract void CancelAbility();
public abstract void EndAbility();
}
public abstract class AbilitySpec<T> : AbilitySpec where T : AbstractAbility
{
public T Data { get; private set; }
protected AbilitySpec(T ability, AbilitySystemComponent owner) : base(ability, owner)
{
Data = ability;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d1dfde8ae93348678664a5e08769ffdb
timeCreated: 1702894219

View File

@@ -0,0 +1,51 @@
using System;
namespace GAS.Runtime
{
/// <summary>
/// https://github.com/BillEliot/GASDocumentation_Chinese?tab=readme-ov-file#4610-gameplay-ability-spec
/// goto 4.6.9 Ability Tag
/// </summary>
[Serializable]
public struct AbilityTagContainer
{
public GameplayTagSet AssetTag;
public GameplayTagSet CancelAbilitiesWithTags;
public GameplayTagSet BlockAbilitiesWithTags;
public GameplayTagSet ActivationOwnedTag;
public GameplayTagSet ActivationRequiredTags;
public GameplayTagSet ActivationBlockedTags;
// // TODO
// public GameplayTagSet SourceRequiredTags;
// public GameplayTagSet SourceBlockedTags;
//
// // TODO
// public GameplayTagSet TargetRequiredTags;
// public GameplayTagSet TargetBlockedTags;
public AbilityTagContainer(
GameplayTag[] assetTags,
GameplayTag[] cancelAbilityTags,
GameplayTag[] blockAbilityTags,
GameplayTag[] activationOwnedTag,
GameplayTag[] activationRequiredTags,
GameplayTag[] activationBlockedTags)
{
AssetTag = new GameplayTagSet(assetTags);
CancelAbilitiesWithTags = new GameplayTagSet(cancelAbilityTags);
BlockAbilitiesWithTags = new GameplayTagSet(blockAbilityTags);
ActivationOwnedTag = new GameplayTagSet(activationOwnedTag);
ActivationRequiredTags = new GameplayTagSet(activationRequiredTags);
ActivationBlockedTags = new GameplayTagSet(activationBlockedTags);
// SourceRequiredTags = new GameplayTagSet(sourceRequiredTags);
// SourceBlockedTags = new GameplayTagSet(sourceBlockedTags);
// TargetRequiredTags = new GameplayTagSet(targetRequiredTags);
// TargetBlockedTags = new GameplayTagSet(targetBlockedTags);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a29c341083404ab0bbaa4d8feb6d3f05
timeCreated: 1703496738

View File

@@ -0,0 +1,76 @@
using System.Collections.Generic;
using GAS.Runtime;
namespace GAS.Runtime
{
public abstract class AbstractAbility
{
public readonly string Name;
public readonly AbilityAsset DataReference;
// TODO : AbilityTask
// public List<OngoingAbilityTask> OngoingAbilityTasks=new List<OngoingAbilityTask>();
// public List<AsyncAbilityTask> AsyncAbilityTasks = new List<AsyncAbilityTask>();
public AbilityTagContainer Tag { get; protected set; }
public GameplayEffect Cooldown { get; protected set; }
public float CooldownTime { get; protected set; }
public GameplayEffect Cost { get; protected set; }
public AbstractAbility(AbilityAsset abilityAsset)
{
DataReference = abilityAsset;
Name = DataReference.UniqueName;
Tag = new AbilityTagContainer(
DataReference.AssetTags, DataReference.CancelAbilityTags, DataReference.BlockAbilityTags,
DataReference.ActivationOwnedTags, DataReference.ActivationRequiredTags, DataReference.ActivationBlockedTags);
Cooldown = DataReference.Cooldown ? DataReference.Cooldown.SharedInstance : default;
Cost = DataReference.Cost ? DataReference.Cost.SharedInstance: default;
CooldownTime = DataReference.CooldownTime;
}
public abstract AbilitySpec CreateSpec(AbilitySystemComponent owner);
public void SetCooldown(GameplayEffect coolDown)
{
if (coolDown.DurationPolicy == EffectsDurationPolicy.Duration)
{
Cooldown = coolDown;
}
#if UNITY_EDITOR
else
{
UnityEngine.Debug.LogError("[EX] Cooldown must be duration policy!");
}
#endif
}
public void SetCost(GameplayEffect cost)
{
if (cost.DurationPolicy == EffectsDurationPolicy.Instant)
{
Cost = cost;
}
#if UNITY_EDITOR
else
{
UnityEngine.Debug.LogError("[EX] Cost must be instant policy!");
}
#endif
}
}
public abstract class AbstractAbility<T> : AbstractAbility where T : AbilityAsset
{
public T AbilityAsset => DataReference as T;
protected AbstractAbility(T abilityAsset) : base(abilityAsset)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e9b50ca889544518c8aeb712988a44e
timeCreated: 1701938761

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8b3dda2fee2e4fbe83d66dc080bde768
timeCreated: 1709451484

View File

@@ -0,0 +1,18 @@
using System;
using GAS.Runtime;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public abstract class CatchAreaBase : TargetCatcherBase
{
public LayerMask checkLayer;
public void Init(AbilitySystemComponent owner, LayerMask checkLayer)
{
base.Init(owner);
this.checkLayer = checkLayer;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 58b4935f7a5a43d69506e28666032462
timeCreated: 1709451834

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public sealed class CatchAreaBox2D : CatchAreaBase
{
public Vector2 offset;
public float rotation;
public Vector2 size;
public EffectCenterType centerType;
public void Init(AbilitySystemComponent owner, LayerMask tCheckLayer, Vector2 offset, Vector2 size,
float rotation)
{
base.Init(owner, tCheckLayer);
this.offset = offset;
this.size = size;
this.rotation = rotation;
}
private static readonly Collider2D[] Collider2Ds = new Collider2D[32];
protected override void CatchTargetsNonAlloc(AbilitySystemComponent mainTarget, List<AbilitySystemComponent> results)
{
int count = centerType switch
{
EffectCenterType.SelfOffset => Owner.OverlapBox2DNonAlloc(offset, size, rotation, Collider2Ds, checkLayer),
EffectCenterType.WorldSpace => Physics2D.OverlapBoxNonAlloc(offset, size, rotation, Collider2Ds, checkLayer),
EffectCenterType.TargetOffset => mainTarget.OverlapBox2DNonAlloc(offset, size, rotation, Collider2Ds, checkLayer),
_ => 0
};
for (var i = 0; i < count; ++i)
{
var targetUnit = Collider2Ds[i].GetComponent<AbilitySystemComponent>();
if (targetUnit != null)
{
results.Add(targetUnit);
}
}
}
#if UNITY_EDITOR
public override void OnEditorPreview(GameObject previewObject)
{
// 使用Debug 绘制box预览
float showTime = 1;
Color color = Color.green;
var relativeTransform = previewObject.transform;
var center = offset;
var angle = rotation + relativeTransform.eulerAngles.z;
switch (centerType)
{
case EffectCenterType.SelfOffset:
center = relativeTransform.position;
center.y += relativeTransform.lossyScale.y > 0 ? offset.y : -offset.y;
center.x += relativeTransform.lossyScale.x > 0 ? offset.x : -offset.x;
break;
case EffectCenterType.WorldSpace:
center = offset;
break;
case EffectCenterType.TargetOffset:
//center = _spec.Target.transform.position + (Vector3)_task.Offset;
break;
}
DebugExtension.DebugBox(center, size, angle, color, showTime);
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e6b866d9cbaa44caa47f9dfc06bd9855
timeCreated: 1709451757

View File

@@ -0,0 +1,68 @@
using System.Collections.Generic;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
public sealed class CatchAreaCircle2D : CatchAreaBase
{
public float radius;
public Vector2 offset;
public EffectCenterType centerType;
public void Init(AbilitySystemComponent owner, LayerMask tCheckLayer, Vector2 offset, float radius)
{
base.Init(owner, tCheckLayer);
this.offset = offset;
this.radius = radius;
}
private static readonly Collider2D[] Collider2Ds = new Collider2D[32];
protected override void CatchTargetsNonAlloc(AbilitySystemComponent mainTarget, List<AbilitySystemComponent> results)
{
int count = centerType switch
{
EffectCenterType.SelfOffset => Owner.OverlapCircle2DNonAlloc(offset, radius, Collider2Ds, checkLayer),
EffectCenterType.WorldSpace => Physics2D.OverlapCircleNonAlloc(offset, radius, Collider2Ds, checkLayer),
EffectCenterType.TargetOffset => mainTarget.OverlapCircle2DNonAlloc(offset, radius, Collider2Ds, checkLayer),
_ => 0
};
for (var i = 0; i < count; ++i)
{
var targetUnit = Collider2Ds[i].GetComponent<AbilitySystemComponent>();
if (targetUnit != null)
{
results.Add(targetUnit);
}
}
}
#if UNITY_EDITOR
public override void OnEditorPreview(GameObject previewObject)
{
// 使用Debug 绘制box预览
float showTime = 1;
Color color = Color.green;
var relativeTransform = previewObject.transform;
var center = offset;
switch (centerType)
{
case EffectCenterType.SelfOffset:
center = relativeTransform.position;
center.y += relativeTransform.lossyScale.y > 0 ? offset.y : -offset.y;
center.x += relativeTransform.lossyScale.x > 0 ? offset.x : -offset.x;
break;
case EffectCenterType.WorldSpace:
center = offset;
break;
case EffectCenterType.TargetOffset:
//center = _targetCatcher.Target.transform.position;
break;
}
DebugExtension.DebugDrawCircle(center, radius, color, showTime);
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e60fe4199379464da56e2d33224b98b5
timeCreated: 1709889749

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace GAS.Runtime
{
public sealed class CatchSelf : TargetCatcherBase
{
protected override void CatchTargetsNonAlloc(AbilitySystemComponent mainTarget, List<AbilitySystemComponent> results)
{
results.Add(Owner);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be9321649ff24febb2f8a1c8e0ec2e4c
timeCreated: 1709451646

View File

@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace GAS.Runtime
{
public sealed class CatchTarget : TargetCatcherBase
{
protected override void CatchTargetsNonAlloc(AbilitySystemComponent mainTarget, List<AbilitySystemComponent> results)
{
results.Add(mainTarget);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 42c991b40b9d414fb7b87d2c7d44c078
timeCreated: 1709534433

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace GAS.Runtime
{
public abstract class TargetCatcherBase
{
public AbilitySystemComponent Owner;
protected TargetCatcherBase()
{
}
public virtual void Init(AbilitySystemComponent owner)
{
Owner = owner;
}
[Obsolete("请使用CatchTargetsNonAlloc方法来避免产生垃圾收集GC。")]
public List<AbilitySystemComponent> CatchTargets(AbilitySystemComponent mainTarget)
{
var result = new List<AbilitySystemComponent>();
CatchTargetsNonAlloc(mainTarget, result);
return result;
}
public void CatchTargetsNonAllocSafe(AbilitySystemComponent mainTarget, List<AbilitySystemComponent> results)
{
results.Clear();
CatchTargetsNonAlloc(mainTarget, results);
}
protected abstract void CatchTargetsNonAlloc(AbilitySystemComponent mainTarget, List<AbilitySystemComponent> results);
#if UNITY_EDITOR
public virtual void OnEditorPreview(GameObject obj)
{
}
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7720c3db62774f24943b28e0160c291c
timeCreated: 1709451499

View File

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

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 459d02062f69400c85df659d87d8ed9a
timeCreated: 1701938994

View File

@@ -0,0 +1,12 @@
namespace GAS.Runtime
{
public abstract class AbilityTaskBase
{
protected AbilitySpec _spec;
public AbilitySpec Spec => _spec;
public virtual void Init(AbilitySpec spec)
{
_spec = spec;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b4936b5b85e340a0ae770d010de0b8d6
timeCreated: 1702888759

View File

@@ -0,0 +1,21 @@
namespace GAS.Runtime
{
public abstract class InstantAbilityTask : AbilityTaskBase
{
#if UNITY_EDITOR
/// <summary>
/// 编辑器预览用
/// 【注意】 覆写时记得用UNITY_EDITOR宏包裹这是预览表现用的函数不该被编译。
/// </summary>
public virtual void OnEditorPreview()
{
}
#endif
public abstract void OnExecute();
}
public abstract class InstantAbilityTaskT<T> : InstantAbilityTask where T : AbilitySpec
{
public new T Spec => (T)_spec;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1002d6c36c354bf1845dd82d12e4aca1
timeCreated: 1702889566

View File

@@ -0,0 +1,28 @@
namespace GAS.Runtime
{
public abstract class OngoingAbilityTask : AbilityTaskBase
{
#if UNITY_EDITOR
/// <summary>
/// 编辑器预览用
/// 【注意】 覆写时记得用UNITY_EDITOR宏包裹这是预览表现用的函数不该被编译。
/// </summary>
/// <param name="frame"></param>
/// <param name="startFrame"></param>
/// <param name="endFrame"></param>
public virtual void OnEditorPreview(int frame, int startFrame, int endFrame)
{
}
#endif
public abstract void OnStart(int startFrame);
public abstract void OnEnd(int endFrame);
public abstract void OnTick(int frameIndex, int startFrame, int endFrame);
}
public abstract class OngoingAbilityTaskT<T> : OngoingAbilityTask where T : AbilitySpec
{
public new T Spec => (T)_spec;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7cdf6d03d4434baf93a42aaea7e839be
timeCreated: 1702889536

View File

@@ -0,0 +1,37 @@

namespace GAS.Runtime
{
/// <summary>
/// Ability激活期总是执行的AbilityTask
/// </summary>
public abstract class PassiveAbilityTask : AbilityTaskBase
{
#if UNITY_EDITOR
/// <summary>
/// 编辑器预览用
/// 【注意】 覆写时记得用UNITY_EDITOR宏包裹这是预览表现用的函数不该被编译。
/// </summary>
/// <param name="frame"></param>
/// <param name="startFrame"></param>
/// <param name="endFrame"></param>
public virtual void OnEditorPreview(int frame, int startFrame, int endFrame)
{
}
#endif
/// <summary>
/// 开始执行仍是Timeline的触发点
/// </summary>
public abstract void OnStart();
/// <summary>
/// 结束执行Ability结束激活时而非Timeline结束时
/// </summary>
public abstract void OnEnd();
/// <summary>
/// 每帧Tick
/// </summary>
/// <param name="deltaTime"></param>
public abstract void OnTick(int deltaTime);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5a9d88b958e546c7aaef67ab354c71a4
timeCreated: 1729189790

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bbeb02116afc4e53b4325da574d435c7
timeCreated: 1709537763

View File

@@ -0,0 +1,32 @@
using System;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public abstract class AbilityTaskData
{
public JsonData TaskData;
public virtual AbilityTaskBase Create(AbilitySpec abilitySpec)
{
var task = Load();
task.Init(abilitySpec);
return task;
}
public void Save(AbilityTaskBase task)
{
var jsonData = JsonUtility.ToJson(task);
var dataType = task.GetType().FullName;
TaskData = new JsonData
{
Type = dataType,
Data = jsonData
};
}
public abstract AbilityTaskBase Load();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 94b0d93e175744309457ec20f699f54d
timeCreated: 1709537306

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public class InstantTaskData : AbilityTaskData
{
public InstantTaskData()
{
TaskData = new JsonData()
{
Type = typeof(DefaultInstantAbilityTask).FullName,
};
}
public InstantAbilityTask CreateTask(AbilitySpec abilitySpec)
{
var task = base.Create(abilitySpec);
var instantAbilityTask = task as InstantAbilityTask;
return instantAbilityTask;
}
public override AbilityTaskBase Load()
{
InstantAbilityTask task = null;
var jsonData = TaskData.Data;
var dataType = string.IsNullOrEmpty(TaskData.Type) ? typeof(DefaultInstantAbilityTask).FullName : TaskData.Type;
var type = InstantTaskSonTypes.FirstOrDefault(sonType => sonType.FullName == dataType);
if (type == null)
{
Debug.LogError("[EX] InstantAbilityTask SonType not found: " + dataType);
}
else
{
if (string.IsNullOrEmpty(jsonData))
task = Activator.CreateInstance(type) as InstantAbilityTask;
else
task = JsonUtility.FromJson(jsonData, type) as InstantAbilityTask;
}
return task;
}
#region SonTypes
private static Type[] _instantTaskSonTypes;
public static Type[] InstantTaskSonTypes =>
_instantTaskSonTypes ??= TypeUtil.GetAllSonTypesOf(typeof(InstantAbilityTask));
public static List<string> InstantTaskSonTypeChoices
{
get
{
return InstantTaskSonTypes.Select(sonType => sonType.FullName).ToList();
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 474a7b1c8cea4801abb10dccf9a04a9a
timeCreated: 1709537777

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public class OngoingTaskData : AbilityTaskData
{
public OngoingTaskData()
{
TaskData = new JsonData()
{
Type = typeof(DefaultOngoingAbilityTask).FullName,
};
}
public OngoingAbilityTask CreateTask(AbilitySpec abilitySpec)
{
var task = base.Create(abilitySpec);
var ongoingAbilityTask = task as OngoingAbilityTask;
return ongoingAbilityTask;
}
public override AbilityTaskBase Load()
{
OngoingAbilityTask task = null;
var jsonData = TaskData.Data;
var dataType = string.IsNullOrEmpty(TaskData.Type) ? typeof(DefaultOngoingAbilityTask).FullName : TaskData.Type;
var type = OngoingTaskSonTypes.FirstOrDefault(sonType => sonType.FullName == dataType);
if (type == null)
{
Debug.LogError("[EX] OngoingAbilityTask SonType not found: " + dataType);
}
else
{
if (string.IsNullOrEmpty(jsonData))
task = Activator.CreateInstance(type) as OngoingAbilityTask;
else
task = JsonUtility.FromJson(jsonData, type) as OngoingAbilityTask;
}
return task;
}
#region SonTypes
private static Type[] _ongoingTaskSonTypes;
public static Type[] OngoingTaskSonTypes =>
_ongoingTaskSonTypes ??= TypeUtil.GetAllSonTypesOf(typeof(OngoingAbilityTask));
public static List<string> OngoingTaskSonTypeChoices
{
get
{
return OngoingTaskSonTypes.Select(sonType => sonType.FullName).ToList();
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6237a13b66514fb98982b19e91b75259
timeCreated: 1709538170

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using GAS.General;
using System;
using System.Collections.Generic;
using System.Linq;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public class PassiveTaskData : AbilityTaskData
{
public PassiveTaskData()
{
TaskData = new JsonData()
{
Type = typeof(DefaultPassiveAbilityTask).FullName,
};
}
public PassiveAbilityTask CreateTask(AbilitySpec abilitySpec)
{
var task = base.Create(abilitySpec);
var passiveAbilityTask = task as PassiveAbilityTask;
return passiveAbilityTask;
}
public override AbilityTaskBase Load()
{
PassiveAbilityTask task = null;
var jsonData = TaskData.Data;
var dataType = string.IsNullOrEmpty(TaskData.Type) ? typeof(DefaultPassiveAbilityTask).FullName : TaskData.Type;
var type = PassiveTaskSonTypes.FirstOrDefault(sonType => sonType.FullName == dataType);
if (type == null)
{
Debug.LogError("[EX] PassiveAbilityTask SonType not found: {0}" + dataType);
}
else
{
if (string.IsNullOrEmpty(jsonData))
{
task = Activator.CreateInstance(type) as PassiveAbilityTask;
}
else
{
task = JsonUtility.FromJson(jsonData, type) as PassiveAbilityTask;
}
}
return task;
}
#region SonTypes
private static Type[] _passiveTaskSonTypes;
public static Type[] PassiveTaskSonTypes =>
_passiveTaskSonTypes ??= TypeUtil.GetAllSonTypesOf(typeof(PassiveAbilityTask));
public static List<string> PassiveTaskSonTypeChoices
{
get
{
var list = new List<String>();
foreach (var sonType in PassiveTaskSonTypes)
{
list.Add(sonType.FullName);
}
return list;
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5dce896cd86840d68e56efd1948114f5
timeCreated: 1729189740

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a388877ce48c4d0d80d3318fd7b9c0f6
timeCreated: 1709277110

View File

@@ -0,0 +1,13 @@
using System;
namespace GAS.Runtime
{
[Serializable]
public class ApplyCostAndCoolDown : InstantAbilityTask
{
public override void OnExecute()
{
_spec.DoCost();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3c8689e959d44db393a7821d0923f750
timeCreated: 1709277143

View File

@@ -0,0 +1,9 @@
namespace GAS.Runtime
{
public class DefaultInstantAbilityTask : InstantAbilityTask
{
public override void OnExecute()
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a33a8f839e2749f3820f585110ffbd58
timeCreated: 1709538427

View File

@@ -0,0 +1,17 @@
namespace GAS.Runtime
{
public class DefaultOngoingAbilityTask : OngoingAbilityTask
{
public override void OnStart(int startFrame)
{
}
public override void OnEnd(int endFrame)
{
}
public override void OnTick(int frameIndex, int startFrame, int endFrame)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fb71402b7b0843079876bc5f37e12a11
timeCreated: 1709538476

View File

@@ -0,0 +1,18 @@

namespace GAS.Runtime
{
public class DefaultPassiveAbilityTask : PassiveAbilityTask
{
public override void OnStart()
{
}
public override void OnEnd()
{
}
public override void OnTick(int deltaTime)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3826985009604c178d6623947f501916
timeCreated: 1729189761

View File

@@ -0,0 +1,91 @@
using UnityEngine.Profiling;
namespace GAS.Runtime
{
public abstract class TimelineAbilityT<T> : AbstractAbility<T> where T : TimelineAbilityAssetBase
{
protected TimelineAbilityT(T abilityAsset) : base(abilityAsset)
{
}
}
public abstract class TimelineAbilitySpecT<AbilityT, AssetT> : AbilitySpec<AbilityT> where AbilityT : TimelineAbilityT<AssetT> where AssetT : TimelineAbilityAssetBase
{
protected TimelineAbilityPlayer<AbilityT, AssetT> _player;
public int FrameCount => _player.FrameCount;
public int FrameRate => _player.FrameRate;
/// <summary>
/// 不受播放速率影响的总时间
/// </summary>
public float TotalTime => _player.TotalTime;
/// <summary>
/// 向性技能的作用目标
/// </summary>
public AbilitySystemComponent Target { get; private set; }
protected TimelineAbilitySpecT(AbilityT ability, AbilitySystemComponent owner) : base(ability, owner)
{
_player = new(this);
}
public void SetAbilityTarget(AbilitySystemComponent mainTarget)
{
Target = mainTarget;
}
public override void ActivateAbility()
{
_player.Play();
}
public virtual float GetPlaySpeed()
{
return Data.AbilityAsset.Speed;
}
public override void CancelAbility()
{
_player.Stop();
}
public override void EndAbility()
{
_player.Stop();
}
protected override void AbilityTick()
{
Profiler.BeginSample("TimelineAbilitySpecT<T>::AbilityTick()");
_player.Tick();
Profiler.EndSample();
}
}
/// <summary>
/// 这是一个最朴素的TimelineAbility实现, 如果要实现更复杂的TimelineAbility, 请用TimelineAbilityT和TimelineAbilitySpecT为基类
/// </summary>
public sealed class TimelineAbility : TimelineAbilityT<TimelineAbilityAsset>
{
public TimelineAbility(TimelineAbilityAsset abilityAsset) : base(abilityAsset)
{
}
public override AbilitySpec CreateSpec(AbilitySystemComponent owner)
{
return new TimelineAbilitySpec(this, owner);
}
}
/// <summary>
/// 这是一个最朴素的TimelineAbilitySpec实现, 如果要实现更复杂的TimelineAbility, 请用TimelineAbilityT和TimelineAbilitySpecT为基类
/// </summary>
public sealed class TimelineAbilitySpec : TimelineAbilitySpecT<TimelineAbilityT<TimelineAbilityAsset>, TimelineAbilityAsset>
{
public TimelineAbilitySpec(TimelineAbilityT<TimelineAbilityAsset> ability, AbilitySystemComponent owner) : base(ability, owner)
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c5e9c13b27a641ee81a8faa360da21e2
timeCreated: 1708250442

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using GAS.General;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
namespace GAS.Runtime
{
public abstract class TimelineAbilityAssetBase : AbilityAsset
{
[TitleGroup("Data")]
[HorizontalGroup("Data/H1", 1 / 3f)]
[TabGroup("Data/H1/V1", "Timeline", SdfIconType.ClockHistory, TextColor = "#00FF00")]
[Button("查看/编辑能力时间轴", ButtonSizes.Large, Icon = SdfIconType.Hammer)]
[PropertyOrder(-1)]
private void EditAbilityTimeline()
{
try
{
var assembly = Assembly.Load("com.exhard.exgas.editor");
var type = assembly.GetType("GAS.Editor.AbilityTimelineEditorWindow");
var methodInfo = type.GetMethod("ShowWindow", BindingFlags.Public | BindingFlags.Static);
methodInfo!.Invoke(null, new object[] { this });
}
catch (Exception e)
{
Debug.LogError(
$"调用\"GAS.Editor.AbilityTimelineEditorWindow\"类的静态方法ShowWindow(TimelineAbilityAsset asset)失败, 代码可能被重构了: {e}");
}
}
/// <summary>
/// 播放速率, 常用于加速或减速播放(例如基于攻击速度的技能, 播放速率随攻击速度变化)
/// </summary>
[TabGroup("Data/H1/V1", "Timeline")]
[LabelText(GASTextDefine.ABILITY_PLAY_RATE)]
[LabelWidth(100)]
[MinValue(0)]
public float Speed = 1.0f;
[TabGroup("Data/H1/V1", "Timeline")]
[LabelText(GASTextDefine.ABILITY_MANUAL_ENDABILITY)]
[LabelWidth(100)]
[FormerlySerializedAs("manualEndAbility")]
public bool ManualEndAbility;
[HideInInspector]
public int FrameCount; // 能力结束时间
[HideInInspector]
public List<DurationalCueTrackData> DurationalCues = new List<DurationalCueTrackData>();
[HideInInspector]
public List<InstantCueTrackData> InstantCues = new List<InstantCueTrackData>();
[HideInInspector]
public List<ReleaseGameplayEffectTrackData> ReleaseGameplayEffect = new List<ReleaseGameplayEffectTrackData>();
[HideInInspector]
public List<BuffGameplayEffectTrackData> BuffGameplayEffects = new List<BuffGameplayEffectTrackData>();
[HideInInspector]
public List<TaskMarkEventTrackData> InstantTasks = new List<TaskMarkEventTrackData>();
[HideInInspector]
public List<TaskClipEventTrackData> OngoingTasks = new List<TaskClipEventTrackData>();
[HideInInspector]
public List<PassiveGameplayEffectTrackData> PassiveGameplayEffects = new List<PassiveGameplayEffectTrackData>();
[HideInInspector]
public List<PassiveTaskClipEventTrackData> PassiveTasks = new List<PassiveTaskClipEventTrackData>();
#if UNITY_EDITOR
public void Save()
{
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}
#endif
}
public abstract class TimelineAbilityAssetT<T> : TimelineAbilityAssetBase where T : class
{
public sealed override Type AbilityType()
{
return typeof(T);
}
}
/// <summary>
/// 这是一个最朴素的TimelineAbilityAsset实现, 如果要实现更复杂的TimelineAbilityAsset, 请用TimelineAbilityAssetBase或TimelineAbilityAssetT为基类
/// </summary>
public sealed class TimelineAbilityAsset : TimelineAbilityAssetT<TimelineAbility>
{
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d763af469c524557946c477b9bea3a46
timeCreated: 1708250516

View File

@@ -0,0 +1,428 @@
using System;
using System.Collections.Generic;
using GAS.General;
using UnityEngine;
//using UnityEngine.Profiling;
namespace GAS.Runtime
{
internal abstract class RuntimeClipInfo
{
public int endFrame;
public int startFrame;
}
internal class RuntimeDurationCueClip : RuntimeClipInfo
{
public GameplayCueDurationalSpec cueSpec;
}
internal class RuntimeBuffClip : RuntimeClipInfo
{
public GameplayEffect buff;
public EntityRef<GameplayEffectSpec> buffSpec;
}
internal class RuntimeTaskClip : RuntimeClipInfo
{
public OngoingAbilityTask task;
}
internal class RuntimeTaskMark
{
public int startFrame;
public InstantAbilityTask task;
}
public class TimelineAbilityPlayer<AbilityT, AssetT> where AssetT : TimelineAbilityAssetBase where AbilityT : TimelineAbilityT<AssetT>
{
private readonly TimelineAbilitySpecT<AbilityT, AssetT> _abilitySpec;
private readonly List<RuntimeBuffClip> _cacheBuffGameplayEffectTrack = new();
private readonly List<RuntimeDurationCueClip> _cacheDurationalCueTrack = new();
private readonly List<InstantCueMarkEvent> _cacheInstantCues = new();
private readonly List<RuntimeTaskMark> _cacheInstantTasks = new();
private readonly List<RuntimeTaskClip> _cacheOngoingTaskTrack = new();
private readonly List<ReleaseGameplayEffectMarkEvent> _cacheReleaseGameplayEffect = new();
// cache for target catcher, avoid new in TickFrame
// 这个是一个泛型类, 这个变量就不作为static了
private readonly List<AbilitySystemComponent> _targets = new();
private int _currentFrame;
private float _playTotalTime;
public TimelineAbilityPlayer(TimelineAbilitySpecT<AbilityT, AssetT> abilitySpec)
{
_abilitySpec = abilitySpec;
Cache();
}
public bool IsPlaying { get; private set; }
public AssetT AbilityAsset => _abilitySpec.Data.AbilityAsset;
public int FrameCount => AbilityAsset.FrameCount;
public int FrameRate => GASTimer.FrameRate;
/// <summary>
/// 不受播放速率影响的总时间
/// </summary>
public float TotalTime => (float)FrameCount / FrameRate;
private void Cache()
{
Cache_InstantCues();
Cache_ReleaseGameplayEffects();
Cache_InstantTasks();
Cache_DurationalGameplayCues();
Cache_BuffGameplayEffects();
Cache_OngoingTasks();
}
private void Cache_InstantCues()
{
_cacheInstantCues.Clear();
foreach (var trackData in AbilityAsset.InstantCues)
{
_cacheInstantCues.AddRange(trackData.markEvents);
}
_cacheInstantCues.Sort((a, b) => a.startFrame.CompareTo(b.startFrame));
}
private void Cache_ReleaseGameplayEffects()
{
_cacheReleaseGameplayEffect.Clear();
foreach (var trackData in AbilityAsset.ReleaseGameplayEffect)
{
_cacheReleaseGameplayEffect.AddRange(trackData.markEvents);
}
_cacheReleaseGameplayEffect.Sort((a, b) => a.startFrame.CompareTo(b.startFrame));
foreach (var releaseGameplayEffectMarkEvent in _cacheReleaseGameplayEffect)
{
releaseGameplayEffectMarkEvent.CacheTargetCatcher();
}
}
private void Cache_InstantTasks()
{
_cacheInstantTasks.Clear();
foreach (var trackData in AbilityAsset.InstantTasks)
{
foreach (var markEvent in trackData.markEvents)
{
foreach (var taskData in markEvent.InstantTasks)
{
var runtimeTaskMark = new RuntimeTaskMark
{
startFrame = markEvent.startFrame,
task = taskData.CreateTask(_abilitySpec)
};
_cacheInstantTasks.Add(runtimeTaskMark);
}
}
}
_cacheInstantTasks.Sort((a, b) => a.startFrame.CompareTo(b.startFrame));
}
private void Cache_DurationalGameplayCues()
{
_cacheDurationalCueTrack.Clear();
foreach (var track in AbilityAsset.DurationalCues)
{
foreach (var clipEvent in track.clipEvents)
{
var cueSpec = clipEvent.cue.ApplyFrom(_abilitySpec);
if (cueSpec == null) continue;
var runtimeDurationCueClip = new RuntimeDurationCueClip
{
startFrame = clipEvent.startFrame,
endFrame = clipEvent.EndFrame,
cueSpec = cueSpec
};
_cacheDurationalCueTrack.Add(runtimeDurationCueClip);
}
}
}
private void Cache_BuffGameplayEffects()
{
_cacheBuffGameplayEffectTrack.Clear();
foreach (var track in AbilityAsset.BuffGameplayEffects)
{
foreach (var clipEvent in track.clipEvents)
{
// 只有持续型的GameplayEffect可视作buff
if (clipEvent.gameplayEffect.DurationPolicy is EffectsDurationPolicy.Duration
or EffectsDurationPolicy.Infinite)
{
var runtimeBuffClip = new RuntimeBuffClip
{
startFrame = clipEvent.startFrame,
endFrame = clipEvent.EndFrame,
buff = clipEvent.gameplayEffect.SharedInstance,
buffSpec = default
};
_cacheBuffGameplayEffectTrack.Add(runtimeBuffClip);
}
}
}
}
private void Cache_OngoingTasks()
{
_cacheOngoingTaskTrack.Clear();
foreach (var track in AbilityAsset.OngoingTasks)
{
foreach (var clip in track.clipEvents)
{
var runtimeTaskClip = new RuntimeTaskClip
{
startFrame = clip.startFrame,
endFrame = clip.EndFrame,
task = clip.ongoingTask.CreateTask(_abilitySpec)
};
_cacheOngoingTaskTrack.Add(runtimeTaskClip);
}
}
}
private void Prepare()
{
foreach (var runtimeBuffClip in _cacheBuffGameplayEffectTrack)
{
runtimeBuffClip.buffSpec.Value?.Recycle();
runtimeBuffClip.buffSpec = default;
}
}
public void Play()
{
_currentFrame = -1; // 为了播放第0帧
_playTotalTime = 0;
IsPlaying = true;
Prepare();
}
public void Stop()
{
if (!IsPlaying) return;
foreach (var clip in _cacheDurationalCueTrack)
{
if (_currentFrame <= clip.endFrame)
clip.cueSpec.OnRemove();
}
foreach (var clip in _cacheBuffGameplayEffectTrack)
{
var spec = clip.buffSpec.Value;
if (spec != null)
{
_abilitySpec.Owner.RemoveGameplayEffect(spec);
spec.Recycle();
}
clip.buffSpec = default;
}
foreach (var clip in _cacheOngoingTaskTrack)
{
clip.task.OnEnd(clip.endFrame);
}
IsPlaying = false;
}
public void Tick()
{
if (!IsPlaying) return;
var speed = _abilitySpec.GetPlaySpeed();
speed = Math.Max(0, speed);
_playTotalTime += Time.deltaTime * speed;
var targetFrame = (int)(_playTotalTime * FrameRate);
// 追帧
while (_currentFrame < targetFrame)
{
_currentFrame++;
TickFrame(_currentFrame);
}
if (_currentFrame >= FrameCount)
{
_currentFrame++; //确保不重复触发cue的onRemove
OnPlayEnd();
}
}
/// <summary>
/// 播放结束
/// </summary>
private void OnPlayEnd()
{
IsPlaying = false;
if (!AbilityAsset.ManualEndAbility)
_abilitySpec.TryEndAbility();
}
/// <summary>
/// 当前帧的事件
/// </summary>
/// <param name="frame"></param>
private void TickFrame(int frame)
{
TickFrame_InstantGameplayCues(frame);
TickFrame_ReleaseGameplayEffects(frame);
TickFrame_InstantTasks(frame);
TickFrame_DurationalGameplayCues(frame);
TickFrame_BuffGameplayEffects(frame);
TickFrame_OngoingTasks(frame);
}
private void TickFrame_InstantGameplayCues(int frame)
{
foreach (var cueMark in _cacheInstantCues)
{
if (frame == cueMark.startFrame)
{
foreach (var cue in cueMark.cues)
{
cue.ApplyFrom(_abilitySpec);
}
}
}
}
private void TickFrame_ReleaseGameplayEffects(int frame)
{
foreach (var mark in _cacheReleaseGameplayEffect)
{
if (frame == mark.startFrame)
{
var catcher = mark.TargetCatcher;
catcher.Init(_abilitySpec.Owner);
catcher.CatchTargetsNonAllocSafe(_abilitySpec.Target, _targets);
foreach (var asc in _targets)
{
foreach (var gea in mark.gameplayEffectAssets)
{
var ge = gea.SharedInstance;
_abilitySpec.Owner.ApplyGameplayEffectTo(ge, asc);
}
}
_targets.Clear();
}
}
}
private void TickFrame_InstantTasks(int frame)
{
foreach (var instantTask in _cacheInstantTasks)
{
if (frame == instantTask.startFrame)
{
instantTask.task.OnExecute();
}
}
}
private void TickFrame_DurationalGameplayCues(int frame)
{
foreach (var cueClip in _cacheDurationalCueTrack)
{
if (frame == cueClip.startFrame)
{
cueClip.cueSpec.OnAdd();
}
if (frame >= cueClip.startFrame && frame <= cueClip.endFrame)
{
cueClip.cueSpec.OnTick();
}
if (frame == cueClip.endFrame)
{
cueClip.cueSpec.OnRemove();
}
}
}
private void TickFrame_BuffGameplayEffects(int frame)
{
// buff持续时间以Timeline配置时间为准执行策略全部改为Infinite
// Profiler.BeginSample("TickFrame_BuffGameplayEffects");
// {
foreach (var buffClip in _cacheBuffGameplayEffectTrack)
{
if (frame == buffClip.startFrame)
{
//Profiler.BeginSample("buffGameplayEffect.Start");
var buffSpec = _abilitySpec.Owner.ApplyGameplayEffectToSelf(buffClip.buff);
buffSpec.Value.SetDurationPolicy(EffectsDurationPolicy.Infinite);
buffClip.buffSpec = buffSpec;
//Profiler.EndSample();
}
if (frame == buffClip.endFrame)
{
var spec = buffClip.buffSpec.Value;
if (spec != null)
{
//Profiler.BeginSample("buffGameplayEffect.End");
_abilitySpec.Owner.RemoveGameplayEffect(spec);
//Profiler.EndSample();
spec.Recycle();
}
buffClip.buffSpec = default;
}
}
// }
// Profiler.EndSample();
}
private void TickFrame_OngoingTasks(int frame)
{
// Profiler.BeginSample("TickFrame_OngoingTasks");
// {
foreach (var taskClip in _cacheOngoingTaskTrack)
{
if (frame == taskClip.startFrame)
{
//Profiler.BeginSample("Ongoing Task.OnStart()");
taskClip.task.OnStart(frame);
//Profiler.EndSample();
}
if (frame >= taskClip.startFrame && frame <= taskClip.endFrame)
{
//Profiler.BeginSample("Ongoing Task.OnTick()");
taskClip.task.OnTick(frame, taskClip.startFrame, taskClip.endFrame);
//Profiler.EndSample();
}
if (frame == taskClip.endFrame)
{
//Profiler.BeginSample("Ongoing Task.OnEnd()");
taskClip.task.OnEnd(frame);
//Profiler.EndSample();
}
}
// }
// Profiler.EndSample();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4b17a33afb534946b70239808a6015eb
timeCreated: 1708672766

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3cfd0d21847f461d96661a737d97d32e
timeCreated: 1708504113

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using GAS.Runtime;
using UnityEngine.Serialization;
namespace GAS.Runtime
{
[Serializable]
public class BuffGameplayEffectTrackData:TrackDataBase
{
public List<BuffGameplayEffectClipEvent> clipEvents = new List<BuffGameplayEffectClipEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.BuffGameplayEffects.Add(this);
}
public override void DefaultInit()
{
base.DefaultInit();
trackName = "Buff";
}
}
[Serializable]
public class BuffGameplayEffectClipEvent : ClipEventBase
{
public BuffTarget buffTarget;
[FormerlySerializedAs("gameplayEffects")] public GameplayEffectAsset gameplayEffect;
}
public enum BuffTarget
{
Self,
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5742faeb4aee4f058f69e6b0922a540d
timeCreated: 1709018398

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using GAS.Runtime;
namespace GAS.Runtime
{
[Serializable]
public class DurationalCueTrackData:TrackDataBase
{
public List<DurationalCueClipEvent> clipEvents = new List<DurationalCueClipEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.DurationalCues.Add(this);
}
}
[Serializable]
public class DurationalCueClipEvent : ClipEventBase
{
public GameplayCueDurational cue;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eda585ff2c7143d8bfcb34857b7cc068
timeCreated: 1709017864

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using GAS.Runtime;
namespace GAS.Runtime
{
[Serializable]
public class InstantCueTrackData:TrackDataBase
{
public List<InstantCueMarkEvent> markEvents = new List<InstantCueMarkEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.InstantCues.Add(this);
}
}
[Serializable]
public class InstantCueMarkEvent:MarkEventBase
{
public List<GameplayCueInstant> cues = new List<GameplayCueInstant>();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a2f9017c20dc4fc5b36fa15ba97371df
timeCreated: 1709106070

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using UnityEngine.Serialization;
namespace GAS.Runtime
{
[Serializable]
public sealed class PassiveGameplayEffectTrackData : TrackDataBase
{
public List<PassiveGameplayEffectClipEvent> clipEvents = new List<PassiveGameplayEffectClipEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.PassiveGameplayEffects.Add(this);
}
}
[Serializable]
public sealed class PassiveGameplayEffectClipEvent : ClipEventBase
{
[FormerlySerializedAs("GameplayEffectValueId")]
public int gameplayEffectValueId;
public GameplayEffectAsset gameplayEffect;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ece82d688852440699c4a50ee47a671e
timeCreated: 1729189527

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
namespace GAS.Runtime
{
[Serializable]
public sealed class PassiveTaskClipEventTrackData : TrackDataBase
{
public List<PassiveTaskClipEvent> clipEvents = new List<PassiveTaskClipEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.PassiveTasks.Add(this);
}
}
[Serializable]
public sealed class PassiveTaskClipEvent : ClipEventBase
{
public PassiveTaskData passiveTask;
public PassiveAbilityTask Load()
{
return passiveTask.Load() as PassiveAbilityTask;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6a98a9a0278441cada7de695771be21
timeCreated: 1729189725

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using GAS.General;
using UnityEngine;
namespace GAS.Runtime
{
[Serializable]
public class ReleaseGameplayEffectTrackData : TrackDataBase
{
public List<ReleaseGameplayEffectMarkEvent> markEvents = new List<ReleaseGameplayEffectMarkEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.ReleaseGameplayEffect.Add(this);
}
}
[Serializable]
public class ReleaseGameplayEffectMarkEvent : MarkEventBase
{
public JsonData jsonTargetCatcher = new JsonData()
{
Type = typeof(CatchSelf).FullName // 默认 CatchSelf
};
public List<GameplayEffectAsset> gameplayEffectAssets = new List<GameplayEffectAsset>();
private TargetCatcherBase _targetCatcher;
public TargetCatcherBase TargetCatcher
{
get
{
// 如果是反序列化的数据,没有执行构造函数, 需要加载
_targetCatcher ??= LoadTargetCatcher();
return _targetCatcher;
}
}
public void CacheTargetCatcher()
{
_targetCatcher = LoadTargetCatcher();
}
public void SaveTargetCatcher(TargetCatcherBase targetCatcher)
{
var jsonData = JsonUtility.ToJson(targetCatcher);
var dataType = targetCatcher.GetType().FullName;
jsonTargetCatcher = new JsonData
{
Type = dataType,
Data = jsonData
};
}
public TargetCatcherBase LoadTargetCatcher()
{
TargetCatcherBase targetCatcher = null;
var jsonData = jsonTargetCatcher.Data;
var dataType = jsonTargetCatcher.Type;
Type type = null;
foreach (var t in TargetCatcherSonTypes)
{
if (t.FullName == dataType)
{
type = t;
break;
}
}
if (type == null)
{
Debug.LogError("[EX] TargetCatcherBase SonType not found: " + dataType);
}
else
{
if (string.IsNullOrEmpty(jsonData))
{
targetCatcher = Activator.CreateInstance(type) as TargetCatcherBase;
}
else
{
targetCatcher = JsonUtility.FromJson(jsonData, type) as TargetCatcherBase;
}
}
return targetCatcher;
}
#region TargetCatcher SonTypes
private static Type[] _targetCatcherSonTypes;
public static Type[] TargetCatcherSonTypes =>
_targetCatcherSonTypes ??= TypeUtil.GetAllSonTypesOf(typeof(TargetCatcherBase));
#endregion
}
// public enum LockMethod
// {
// Self,
// Circle2D,
// Box2D,
// Sphere3D,
// Box3D,
// Custom
// }
//
// public enum CenterType
// {
// Relative,
// WorldSpace
// }
//
// [Serializable]
// public class LockOnTargetMethod
// {
// public LockMethod method;
//
// // 检测碰撞
// public LayerMask checkLayer;
// public CenterType centerType;
//
// public Vector3 center;
//
// // Circle2D,Sphere3D
// public float radius;
//
// // Box2D,Box3D
// public Vector3 size;
//
// // Custom
// public string customMethodRegisterKey;
// }
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd623f4d1cda4cca925861de81dec26a
timeCreated: 1709134057

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using GAS.Runtime;
using UnityEngine.Serialization;
namespace GAS.Runtime
{
[Serializable]
public class TaskClipEventTrackData:TrackDataBase
{
public List<TaskClipEvent> clipEvents = new List<TaskClipEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.OngoingTasks.Add(this);
}
public override void DefaultInit()
{
base.DefaultInit();
trackName = "Task Clips";
}
}
[Serializable]
public class TaskClipEvent : ClipEventBase
{
public OngoingTaskData ongoingTask;
public OngoingAbilityTask Load()
{
return ongoingTask.Load() as OngoingAbilityTask;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1768937addbc4d9f8d41e4fd0f8b244b
timeCreated: 1709188720

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using GAS.Runtime;
namespace GAS.Runtime
{
[Serializable]
public class TaskMarkEventTrackData : TrackDataBase
{
public List<TaskMarkEvent> markEvents = new List<TaskMarkEvent>();
public override void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
base.AddToAbilityAsset(abilityAsset);
abilityAsset.InstantTasks.Add(this);
}
}
[Serializable]
public class TaskMarkEvent:MarkEventBase
{
public List<InstantTaskData> InstantTasks = new List<InstantTaskData>();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: af15d283c4a24926bc40197f96a859f6
timeCreated: 1709188733

View File

@@ -0,0 +1,18 @@
using System;
namespace GAS.Runtime
{
[Serializable]
public class TrackDataBase
{
public string trackName;
public virtual void AddToAbilityAsset(TimelineAbilityAssetBase abilityAsset)
{
}
public virtual void DefaultInit()
{
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6a32efd9a68f4192a3531ed7c05576d5
timeCreated: 1709018475

View File

@@ -0,0 +1,23 @@
using System;
using UnityEngine.Serialization;
namespace GAS.Runtime
{
[Serializable]
public abstract class TrackEventBase
{
public int startFrame;
}
[Serializable]
public abstract class MarkEventBase:TrackEventBase
{
}
[Serializable]
public abstract class ClipEventBase:TrackEventBase
{
public int durationFrame;
public int EndFrame => startFrame + durationFrame;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8307d7f9f424132a2d448a75c22fa63
timeCreated: 1708504213