mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 10:46:17 +00:00
临时提交
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2cace2b74934df1ba47e6682854751c
|
||||
timeCreated: 1729504662
|
@@ -0,0 +1,176 @@
|
||||
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,IAbilityAsset
|
||||
{
|
||||
protected const int WIDTH_LABEL = 70;
|
||||
|
||||
public abstract Type AbilityType();
|
||||
|
||||
public string Name => name;
|
||||
public string UniqueName => uniqueName;
|
||||
public IGameplayEffectData Cost => cost;
|
||||
public IGameplayEffectData Cooldown => cooldown;
|
||||
public int CooldownTime => cooldownTime;
|
||||
public GameplayTag[] AssetTags => assetTags;
|
||||
public GameplayTag[] CancelAbilityTags => cancelAbilityTags;
|
||||
public GameplayTag[] BlockAbilityTags => blockAbilityTags;
|
||||
public GameplayTag[] ActivationOwnedTags => activationOwnedTags;
|
||||
public GameplayTag[] ActivationRequiredTags => activationRequiredTags;
|
||||
public GameplayTag[] ActivationBlockedTags => activationBlockedTags;
|
||||
|
||||
[TitleGroup("Base")]
|
||||
[HorizontalGroup("Base/H1", Width = 1 - 0.618f)]
|
||||
[TabGroup("Base/H1/V1", "Summary", SdfIconType.InfoSquareFill, TextColor = "#0BFFC5", Order = 1)]
|
||||
[HideLabel]
|
||||
[MultiLineProperty(10)]
|
||||
[FormerlySerializedAs("Description")]
|
||||
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#标识符命名规则")]
|
||||
[FormerlySerializedAs("UniqueName")]
|
||||
public string uniqueName;
|
||||
|
||||
[TabGroup("Base/H1/V2", "General")]
|
||||
[Title("消耗&冷却", bold: true)]
|
||||
[LabelWidth(WIDTH_LABEL)]
|
||||
[AssetSelector]
|
||||
[LabelText(SdfIconType.HeartHalf, Text = GASTextDefine.ABILITY_EFFECT_COST)]
|
||||
[FormerlySerializedAs("Cost")]
|
||||
public GameplayEffectAsset cost;
|
||||
|
||||
[TabGroup("Base/H1/V2", "General")]
|
||||
[LabelWidth(WIDTH_LABEL)]
|
||||
[AssetSelector]
|
||||
[LabelText(SdfIconType.StopwatchFill, Text = GASTextDefine.ABILITY_EFFECT_CD)]
|
||||
[FormerlySerializedAs("Cooldown")]
|
||||
public GameplayEffectAsset cooldown;
|
||||
|
||||
[TabGroup("Base/H1/V2", "General")]
|
||||
[LabelWidth(WIDTH_LABEL)]
|
||||
[LabelText(SdfIconType.ClockFill, Text = GASTextDefine.ABILITY_CD_TIME)]
|
||||
[Unit(Units.Millisecond)]
|
||||
[FormerlySerializedAs("CooldownTime")]
|
||||
public int 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("AssetTags")]
|
||||
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会被取消。")]
|
||||
[FormerlySerializedAs("CancelAbilityTags")]
|
||||
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会被阻塞激活。")]
|
||||
[FormerlySerializedAs("BlockAbilityTags")]
|
||||
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只有在其拥有者拥有【所有】这些标签时才可激活。")]
|
||||
[FormerlySerializedAs("ActivationRequiredTags")]
|
||||
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在其拥有者拥有【任意】这些标签时不能被激活。")]
|
||||
[FormerlySerializedAs("ActivationBlockedTags")]
|
||||
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);
|
||||
// }
|
||||
// }
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 544108d1f0e34b7fb2a77be04ed89be6
|
||||
timeCreated: 1703762750
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86bcaa66216445e19e861ed7d4c980ba
|
||||
timeCreated: 1729505774
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bbeb02116afc4e53b4325da574d435c7
|
||||
timeCreated: 1709537763
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94b0d93e175744309457ec20f699f54d
|
||||
timeCreated: 1709537306
|
@@ -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
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 474a7b1c8cea4801abb10dccf9a04a9a
|
||||
timeCreated: 1709537777
|
@@ -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
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6237a13b66514fb98982b19e91b75259
|
||||
timeCreated: 1709538170
|
@@ -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
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5dce896cd86840d68e56efd1948114f5
|
||||
timeCreated: 1729189740
|
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using GAS.General;
|
||||
using JNGame.Math;
|
||||
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("JNGame.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 LFloat Speed = 1;
|
||||
|
||||
[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>
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d763af469c524557946c477b9bea3a46
|
||||
timeCreated: 1708250516
|
@@ -0,0 +1,429 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GAS.General;
|
||||
using JNGame.Math;
|
||||
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 LFloat _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 => JexGasManager.FrameRate;
|
||||
|
||||
/// <summary>
|
||||
/// 不受播放速率影响的总时间
|
||||
/// </summary>
|
||||
public LFloat TotalTime => (LFloat)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(_currentFrame,clip.startFrame,clip.endFrame);
|
||||
}
|
||||
|
||||
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(int dt)
|
||||
{
|
||||
if (!IsPlaying) return;
|
||||
|
||||
var speed = _abilitySpec.GetPlaySpeed();
|
||||
speed = Math.Max(0, speed);
|
||||
_playTotalTime += dt * speed;
|
||||
var targetFrame = ((int)(_playTotalTime * FrameRate)) / 1000;
|
||||
|
||||
// 追帧
|
||||
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(frame,cueClip.startFrame,cueClip.endFrame);
|
||||
}
|
||||
|
||||
if (frame >= cueClip.startFrame && frame <= cueClip.endFrame)
|
||||
{
|
||||
cueClip.cueSpec.OnTick(frame,cueClip.startFrame,cueClip.endFrame);
|
||||
}
|
||||
|
||||
if (frame == cueClip.endFrame)
|
||||
{
|
||||
cueClip.cueSpec.OnRemove(frame,cueClip.startFrame,cueClip.endFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b17a33afb534946b70239808a6015eb
|
||||
timeCreated: 1708672766
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cfd0d21847f461d96661a737d97d32e
|
||||
timeCreated: 1708504113
|
@@ -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,
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5742faeb4aee4f058f69e6b0922a540d
|
||||
timeCreated: 1709018398
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eda585ff2c7143d8bfcb34857b7cc068
|
||||
timeCreated: 1709017864
|
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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>();
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2f9017c20dc4fc5b36fa15ba97371df
|
||||
timeCreated: 1709106070
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ece82d688852440699c4a50ee47a671e
|
||||
timeCreated: 1729189527
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6a98a9a0278441cada7de695771be21
|
||||
timeCreated: 1729189725
|
@@ -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 LVector3 center;
|
||||
//
|
||||
// // Circle2D,Sphere3D
|
||||
// public LFloat radius;
|
||||
//
|
||||
// // Box2D,Box3D
|
||||
// public LVector3 size;
|
||||
//
|
||||
// // Custom
|
||||
// public string customMethodRegisterKey;
|
||||
// }
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd623f4d1cda4cca925861de81dec26a
|
||||
timeCreated: 1709134057
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1768937addbc4d9f8d41e4fd0f8b244b
|
||||
timeCreated: 1709188720
|
@@ -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>();
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af15d283c4a24926bc40197f96a859f6
|
||||
timeCreated: 1709188733
|
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a32efd9a68f4192a3531ed7c05576d5
|
||||
timeCreated: 1709018475
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8307d7f9f424132a2d448a75c22fa63
|
||||
timeCreated: 1708504213
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9230cd6f3c114761b2b515646df8dfda
|
||||
timeCreated: 1729504843
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e648b1c47094c0b82684f11e0851160
|
||||
timeCreated: 1729504879
|
@@ -0,0 +1,73 @@
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于属性混合GE堆栈的MMC
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "AttrBasedWithStackModCalculation", menuName = "GAS/MMC/AttrBasedWithStackModCalculation")]
|
||||
public class AttrBasedWithStackModCalculation:AttributeBasedModCalculation
|
||||
{
|
||||
public enum StackMagnitudeOperation
|
||||
{
|
||||
Add,
|
||||
Multiply
|
||||
}
|
||||
|
||||
[InfoBox(" 公式:StackCount * sK + sB")]
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[Title("堆叠幅值计算")]
|
||||
[LabelText("系数(sK)")]
|
||||
public LFloat sK = 1;
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[LabelText("常量(sB)")]
|
||||
public LFloat sB = 0;
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[Title("最终结果")]
|
||||
[InfoBox(" 最终公式: \n" +
|
||||
"Add:(AttributeValue * k + b)+(StackCount * sK + sB); \n" +
|
||||
"Multiply:(AttributeValue * k + b)*(StackCount * sK + sB)")]
|
||||
[LabelText("Stack幅值与Attr幅值计算方式")]
|
||||
public StackMagnitudeOperation stackMagnitudeOperation;
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[LabelText("最终公式")]
|
||||
[ShowInInspector]
|
||||
[DisplayAsString(TextAlignment.Left, true)]
|
||||
public string FinalFormulae
|
||||
{
|
||||
get
|
||||
{
|
||||
var formulae = stackMagnitudeOperation switch
|
||||
{
|
||||
StackMagnitudeOperation.Add => $"({attributeName} * {k} + {b}) + (StackCount * {sK} + {sB})",
|
||||
StackMagnitudeOperation.Multiply => $"({attributeName} * {k} + {b}) * (StackCount * {sK} + {sB})",
|
||||
_ => ""
|
||||
};
|
||||
|
||||
return $"<size=15><b><color=green>{formulae}</color></b></size>";
|
||||
}
|
||||
}
|
||||
|
||||
public override LFloat CalculateMagnitude(GameplayEffectSpec spec, LFloat modifierMagnitude)
|
||||
{
|
||||
var attrMagnitude = base.CalculateMagnitude(spec, modifierMagnitude);
|
||||
|
||||
if (spec.Stacking.stackingType == StackingType.None) return attrMagnitude;
|
||||
|
||||
var stackMagnitude = spec.StackCount * sK + sB;
|
||||
|
||||
return stackMagnitudeOperation switch
|
||||
{
|
||||
StackMagnitudeOperation.Add => attrMagnitude + stackMagnitude,
|
||||
StackMagnitudeOperation.Multiply => attrMagnitude * stackMagnitude,
|
||||
_ => attrMagnitude + stackMagnitude
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8a883d343584d7d9b8a711cf045ca84
|
||||
timeCreated: 1717082065
|
@@ -0,0 +1,119 @@
|
||||
using System.Linq;
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AttributeBasedModCalculation", menuName = "GAS/MMC/AttributeBasedModCalculation")]
|
||||
public class AttributeBasedModCalculation : ModifierMagnitudeCalculation
|
||||
{
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation", SdfIconType.PersonBoundingBox, TextColor = "blue")]
|
||||
[InfoBox(" 以什么方式(Capture Type)从谁身上(Attribute From)捕获哪个属性的值(Attribute Name)。")]
|
||||
[EnumToggleButtons]
|
||||
[LabelText("捕获方式(Capture Type)")]
|
||||
public GEAttributeCaptureType captureType;
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[EnumToggleButtons]
|
||||
[LabelText("捕获目标(Attribute From)")]
|
||||
public AttributeFrom attributeFromType;
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[ValueDropdown("@ValueDropdownHelper.AttributeChoices", IsUniqueList = true)]
|
||||
[LabelText("属性的名称(Attribute Name)")]
|
||||
[OnValueChanged("@OnAttributeNameChanged()")]
|
||||
[ValidateInput("@AttributeValidator.IsValidAttributeName($value)", "属性名无效")]
|
||||
public string attributeName;
|
||||
|
||||
[TabGroup("Default", "Details", SdfIconType.Bug, TextColor = "orange")]
|
||||
[ReadOnly]
|
||||
public string attributeSetName;
|
||||
|
||||
[TabGroup("Default", "Details")]
|
||||
[ReadOnly]
|
||||
public string attributeShortName;
|
||||
|
||||
[InfoBox("计算逻辑与ScalableLFloatModCalculation一致, 公式:AttributeValue * k + b")]
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[LabelText("系数(k)")]
|
||||
public LFloat k = 1;
|
||||
|
||||
[TabGroup("Default", "AttributeBasedModCalculation")]
|
||||
[LabelText("常量(b)")]
|
||||
public LFloat b = 0;
|
||||
|
||||
public override LFloat CalculateMagnitude(GameplayEffectSpec spec, LFloat modifierMagnitude)
|
||||
{
|
||||
LFloat attributeValue;
|
||||
if (attributeFromType == AttributeFrom.Source)
|
||||
{
|
||||
if (captureType == GEAttributeCaptureType.SnapShot)
|
||||
{
|
||||
var snapShot = spec.SnapshotSourceAttributes;
|
||||
if (snapShot == null || snapShot.TryGetValue(attributeName, out attributeValue) == false)
|
||||
{
|
||||
Debug.LogError($"Source snapshot Attribute '{attributeName}' not found in source snapshot for spec: '{spec.GameplayEffect.GameplayEffectName}'.");
|
||||
attributeValue = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var attributeCurrentValue = spec.Source.GetAttributeCurrentValue(attributeSetName, attributeShortName);
|
||||
if (attributeCurrentValue == null)
|
||||
{
|
||||
Debug.LogError($"Source Attribute '{attributeName}' not found in source for spec: '{spec.GameplayEffect.GameplayEffectName}'.");
|
||||
attributeValue = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
attributeValue = attributeCurrentValue.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (captureType == GEAttributeCaptureType.SnapShot)
|
||||
{
|
||||
var snapShot = spec.SnapshotTargetAttributes;
|
||||
if (snapShot == null || snapShot.TryGetValue(attributeName, out attributeValue) == false)
|
||||
{
|
||||
Debug.LogError($"Target snapshot Attribute '{attributeName}' not found in target snapshot for spec: '{spec.GameplayEffect.GameplayEffectName}'.");
|
||||
attributeValue = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var attributeCurrentValue = spec.Owner.GetAttributeCurrentValue(attributeSetName, attributeShortName);
|
||||
if (attributeCurrentValue == null)
|
||||
{
|
||||
Debug.LogError($"Source Attribute '{attributeName}' not found in source for spec: '{spec.GameplayEffect.GameplayEffectName}'.");
|
||||
attributeValue = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
attributeValue = attributeCurrentValue.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attributeValue * k + b;
|
||||
}
|
||||
|
||||
private void OnAttributeNameChanged()
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(attributeName))
|
||||
{
|
||||
var split = attributeName.Split('.');
|
||||
attributeSetName = split[0];
|
||||
attributeShortName = split[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
attributeSetName = null;
|
||||
attributeShortName = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c712e05924784c5593896e5c7c1d2708
|
||||
timeCreated: 1703494252
|
@@ -0,0 +1,59 @@
|
||||
using System.Linq;
|
||||
using GAS.General;
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
public abstract class ModifierMagnitudeCalculation : ScriptableObject,IModifierMagnitudeCalculation
|
||||
{
|
||||
public virtual ushort TypeId => 0;
|
||||
|
||||
protected const int WIDTH_LABEL = 70;
|
||||
|
||||
[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;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[TabGroup("Base/H1/V2", "General", SdfIconType.AwardFill, TextColor = "#FF7F00", Order = 2)]
|
||||
[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
|
||||
|
||||
public abstract LFloat CalculateMagnitude(GameplayEffectSpec spec, LFloat modifierMagnitude);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
// if(Application.isPlaying) return;
|
||||
// EditorUtility.SetDirty(this);
|
||||
// AssetDatabase.SaveAssets();
|
||||
// AssetDatabase.Refresh();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a19086b31e7847ccaa9ec35657197357
|
||||
timeCreated: 1702624467
|
@@ -0,0 +1,25 @@
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
[CreateAssetMenu(fileName = "ScalableLFloatModCalculation", menuName = "GAS/MMC/ScalableLFloatModCalculation")]
|
||||
public class ScalableLFloatModCalculation : ModifierMagnitudeCalculation
|
||||
{
|
||||
private const string Desc = "计算公式:ModifierMagnitude * k + b";
|
||||
|
||||
private const string Detail =
|
||||
"ScalableLFloatModCalculation:可缩放浮点数计算\n该类型是根据Magnitude计算Modifier模值的,计算公式为:ModifierMagnitude * k + b 实际上就是一个线性函数,k和b为可编辑参数,可以在编辑器中设置。";
|
||||
|
||||
[DetailedInfoBox(Desc, Detail, InfoMessageType.Info)] [SerializeField]
|
||||
private LFloat k = LFloat.L1;
|
||||
|
||||
[SerializeField] private LFloat b = LFloat.L0;
|
||||
|
||||
public override LFloat CalculateMagnitude(GameplayEffectSpec spec, LFloat input)
|
||||
{
|
||||
return input * k + b;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b127451ee4eb4575be21eb8e5744a5da
|
||||
timeCreated: 1703493960
|
@@ -0,0 +1,19 @@
|
||||
using JNGame.Math;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
[CreateAssetMenu(fileName = "SetByCallerFromName", menuName = "GAS/MMC/SetByCallerFromNameModCalculation")]
|
||||
public class SetByCallerFromNameModCalculation : ModifierMagnitudeCalculation
|
||||
{
|
||||
[SerializeField] private string valueName;
|
||||
public override LFloat CalculateMagnitude(GameplayEffectSpec spec,LFloat input)
|
||||
{
|
||||
var value = spec.GetMapValue(valueName);
|
||||
#if UNITY_EDITOR
|
||||
if(value==null) Debug.LogWarning($"[EX] SetByCallerModCalculation: GE's '{valueName}' value(name map) is not set");
|
||||
#endif
|
||||
return value ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46c94907308d49a9965a836e3447b1a4
|
||||
timeCreated: 1706243411
|
@@ -0,0 +1,24 @@
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
[CreateAssetMenu(fileName = "SetByCallerFromTag", menuName = "GAS/MMC/SetByCallerFromTagModCalculation")]
|
||||
public class SetByCallerFromTagModCalculation : ModifierMagnitudeCalculation
|
||||
{
|
||||
[SerializeField]
|
||||
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", HideChildProperties = true)]
|
||||
private GameplayTag _tag;
|
||||
|
||||
public override LFloat CalculateMagnitude(GameplayEffectSpec spec, LFloat input)
|
||||
{
|
||||
var value = spec.GetMapValue(_tag);
|
||||
#if UNITY_EDITOR
|
||||
if (value == null)
|
||||
Debug.LogWarning($"[EX] SetByCallerModCalculation: GE's '{_tag.Name}' value(tag map) is not set");
|
||||
#endif
|
||||
return value ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 235035fa9abf451594198fe2ed2b2e28
|
||||
timeCreated: 1706240763
|
@@ -0,0 +1,27 @@
|
||||
using JNGame.Math;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
[CreateAssetMenu( fileName = "StackModCalculation", menuName = "GAS/MMC/StackModCalculation" )]
|
||||
public class StackModCalculation : ModifierMagnitudeCalculation
|
||||
{
|
||||
[InfoBox("计算逻辑与ScalableLFloatModCalculation一致, 公式:(StackCount) * k + b")]
|
||||
[TabGroup("Default", "StackModCalculation")]
|
||||
[LabelText("系数(k)")]
|
||||
public LFloat k = 1;
|
||||
|
||||
[TabGroup("Default", "StackModCalculation")]
|
||||
[LabelText("常量(b)")]
|
||||
public LFloat b = 0;
|
||||
|
||||
public override LFloat CalculateMagnitude(GameplayEffectSpec spec, LFloat modifierMagnitude)
|
||||
{
|
||||
if (spec.Stacking.stackingType == StackingType.None) return 0;
|
||||
|
||||
var stackCount = spec.StackCount;
|
||||
return stackCount * k + b;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21d2ef437f1e4ec9900650b169b66e77
|
||||
timeCreated: 1717073260
|
Reference in New Issue
Block a user