mirror of
https://gitee.com/jisol/jisol-game/
synced 2025-09-27 02:36:14 +00:00
提交GAS 打算做一个帧同步的GAS
This commit is contained in:
402
JEX_GAS/Assets/GAS/Runtime/Component/AbilitySystemComponent.cs
Normal file
402
JEX_GAS/Assets/GAS/Runtime/Component/AbilitySystemComponent.cs
Normal file
@@ -0,0 +1,402 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
public class AbilitySystemComponent : MonoBehaviour, IAbilitySystemComponent
|
||||
{
|
||||
[SerializeField]
|
||||
private AbilitySystemComponentPreset preset;
|
||||
|
||||
public AbilitySystemComponentPreset Preset => preset;
|
||||
|
||||
public int Level { get; protected set; }
|
||||
|
||||
public GameplayEffectContainer GameplayEffectContainer { get; private set; }
|
||||
|
||||
public GameplayTagAggregator GameplayTagAggregator { get; private set; }
|
||||
|
||||
public AbilityContainer AbilityContainer { get; private set; }
|
||||
|
||||
public AttributeSetContainer AttributeSetContainer { get; private set; }
|
||||
|
||||
public object UserData { get; set; }
|
||||
|
||||
private bool _ready;
|
||||
|
||||
private void Prepare()
|
||||
{
|
||||
if (_ready) return;
|
||||
AbilityContainer = new AbilityContainer(this);
|
||||
GameplayEffectContainer = new GameplayEffectContainer(this);
|
||||
AttributeSetContainer = new AttributeSetContainer(this);
|
||||
GameplayTagAggregator = new GameplayTagAggregator(this);
|
||||
_ready = true;
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
AttributeSetContainer.OnEnable();
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
AttributeSetContainer.OnDisable();
|
||||
DisableAllAbilities();
|
||||
ClearGameplayEffects();
|
||||
GameplayTagAggregator?.OnDisable();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Prepare();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
AttributeSetContainer.OnDestroy();
|
||||
UserData = null;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Prepare();
|
||||
GameplayAbilitySystem.GAS.Register(this);
|
||||
GameplayTagAggregator?.OnEnable();
|
||||
Enable();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Disable();
|
||||
GameplayAbilitySystem.GAS.Unregister(this);
|
||||
}
|
||||
|
||||
public void SetPreset(AbilitySystemComponentPreset ascPreset)
|
||||
{
|
||||
preset = ascPreset;
|
||||
}
|
||||
|
||||
public void Init(GameplayTag[] baseTags, Type[] attrSetTypes, AbilityAsset[] baseAbilities, int level)
|
||||
{
|
||||
Prepare();
|
||||
SetLevel(level);
|
||||
if (baseTags != null) GameplayTagAggregator.Init(baseTags);
|
||||
|
||||
if (attrSetTypes != null)
|
||||
{
|
||||
foreach (var attrSetType in attrSetTypes)
|
||||
AttributeSetContainer.AddAttributeSet(attrSetType);
|
||||
}
|
||||
|
||||
if (baseAbilities != null)
|
||||
{
|
||||
foreach (var info in baseAbilities)
|
||||
GrantAbility(info);
|
||||
}
|
||||
}
|
||||
|
||||
private void GrantAbility(AbilityAsset info)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
Debug.LogWarning($"[EX] Try To Grant a NULL Ability!");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ability = Activator.CreateInstance(info.AbilityType(), args: info) as AbstractAbility;
|
||||
AbilityContainer.GrantAbility(ability);
|
||||
}
|
||||
#pragma warning disable CS0168 // 声明了变量,但从未使用过
|
||||
catch (MissingMethodException e)
|
||||
#pragma warning restore CS0168 // 声明了变量,但从未使用过
|
||||
{
|
||||
// 踩坑日志:
|
||||
// 复制了某个AbilityAsset实现类的代码,但忘记更新AbilityType()方法的返回值。
|
||||
// 一般来说AbilityAsset和Ability应该是配套的, 比如在"GAA_xxx"中返回"GA_xxx"的类型.
|
||||
Debug.LogError($"[EX] 创建能力失败: " +
|
||||
$"请检查AbilityAsset实现类'{info.GetType().FullName}'中的AbilityType()方法" +
|
||||
$"是否正确返回了能力类型(当前为'{info.AbilityType()?.FullName ?? "null"}')。");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLevel(int level)
|
||||
{
|
||||
Level = level;
|
||||
}
|
||||
|
||||
public bool HasTag(in GameplayTag gameplayTag)
|
||||
{
|
||||
return GameplayTagAggregator.HasTag(gameplayTag);
|
||||
}
|
||||
|
||||
public bool HasAllTags(in GameplayTagSet tags)
|
||||
{
|
||||
return GameplayTagAggregator.HasAllTags(tags);
|
||||
}
|
||||
|
||||
public bool HasAnyTags(in GameplayTagSet tags)
|
||||
{
|
||||
return GameplayTagAggregator.HasAnyTags(tags);
|
||||
}
|
||||
|
||||
public void AddFixedTags(in GameplayTagSet tags)
|
||||
{
|
||||
GameplayTagAggregator.AddFixedTag(tags);
|
||||
}
|
||||
|
||||
public void RemoveFixedTags(in GameplayTagSet tags)
|
||||
{
|
||||
GameplayTagAggregator.RemoveFixedTag(tags);
|
||||
}
|
||||
|
||||
public void AddFixedTag(in GameplayTag gameplayTag)
|
||||
{
|
||||
GameplayTagAggregator.AddFixedTag(gameplayTag);
|
||||
}
|
||||
|
||||
public void RemoveFixedTag(in GameplayTag gameplayTag)
|
||||
{
|
||||
GameplayTagAggregator.RemoveFixedTag(gameplayTag);
|
||||
}
|
||||
|
||||
public void RemoveGameplayEffect(GameplayEffectSpec spec)
|
||||
{
|
||||
GameplayEffectContainer.RemoveGameplayEffectSpec(spec);
|
||||
}
|
||||
|
||||
public void RemoveGameplayEffect(in EntityRef<GameplayEffectSpec> spec)
|
||||
{
|
||||
GameplayEffectContainer.RemoveGameplayEffectSpec(spec);
|
||||
}
|
||||
|
||||
public void RemoveGameplayEffectWithAnyTags(in GameplayTagSet tags)
|
||||
{
|
||||
GameplayEffectContainer.RemoveGameplayEffectWithAnyTags(tags);
|
||||
}
|
||||
|
||||
public EntityRef<GameplayEffectSpec> ApplyGameplayEffectTo(GameplayEffectSpec gameplayEffectSpec, AbilitySystemComponent target)
|
||||
{
|
||||
return target.AddGameplayEffect(this, gameplayEffectSpec);
|
||||
}
|
||||
|
||||
public EntityRef<GameplayEffectSpec> ApplyGameplayEffectTo(in EntityRef<GameplayEffectSpec> gameplayEffectSpecRef, AbilitySystemComponent target)
|
||||
{
|
||||
var gameplayEffectSpec = gameplayEffectSpecRef.Value;
|
||||
if (gameplayEffectSpec == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.LogError($"[EX] Try To Apply a invalid EntityRef of GameplayEffectSpec From {name} To {target.name}!");
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
return target.AddGameplayEffect(this, gameplayEffectSpec);
|
||||
}
|
||||
|
||||
public EntityRef<GameplayEffectSpec> ApplyGameplayEffectTo(GameplayEffect gameplayEffect, AbilitySystemComponent target)
|
||||
{
|
||||
if (gameplayEffect == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.LogError($"[EX] Try To Apply a NULL GameplayEffect From {name} To {target.name}!");
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
var spec = gameplayEffect.CreateSpec();
|
||||
return ApplyGameplayEffectTo(spec, target);
|
||||
}
|
||||
|
||||
public EntityRef<GameplayEffectSpec> ApplyGameplayEffectTo(GameplayEffect gameplayEffect, AbilitySystemComponent target, int effectLevel)
|
||||
{
|
||||
if (gameplayEffect == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Debug.LogError($"[EX] Try To Apply a NULL GameplayEffect From {name} To {target.name}!");
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
var spec = gameplayEffect.CreateSpec();
|
||||
spec.Value.SetLevel(effectLevel);
|
||||
return ApplyGameplayEffectTo(spec, target);
|
||||
}
|
||||
|
||||
public EntityRef<GameplayEffectSpec> ApplyGameplayEffectToSelf(GameplayEffectSpec gameplayEffectSpec)
|
||||
{
|
||||
return ApplyGameplayEffectTo(gameplayEffectSpec, this);
|
||||
}
|
||||
|
||||
public EntityRef<GameplayEffectSpec> ApplyGameplayEffectToSelf(GameplayEffect gameplayEffect)
|
||||
{
|
||||
return ApplyGameplayEffectTo(gameplayEffect, this);
|
||||
}
|
||||
|
||||
public void RemoveGameplayEffectSpec(GameplayEffectSpec gameplayEffectSpec)
|
||||
{
|
||||
GameplayEffectContainer.RemoveGameplayEffectSpec(gameplayEffectSpec);
|
||||
}
|
||||
|
||||
public void RemoveGameplayEffectSpec(in EntityRef<GameplayEffectSpec> gameplayEffectSpecRef)
|
||||
{
|
||||
GameplayEffectContainer.RemoveGameplayEffectSpec(gameplayEffectSpecRef);
|
||||
}
|
||||
|
||||
public AbilitySpec GrantAbility(AbstractAbility ability)
|
||||
{
|
||||
AbilityContainer.GrantAbility(ability);
|
||||
return AbilityContainer.AbilitySpecs()[ability.Name];
|
||||
}
|
||||
|
||||
public void RemoveAbility(string abilityName)
|
||||
{
|
||||
AbilityContainer.RemoveAbility(abilityName);
|
||||
}
|
||||
|
||||
public AttributeValue? GetAttributeAttributeValue(string attrSetName, string attrShortName)
|
||||
{
|
||||
var value = AttributeSetContainer.GetAttributeAttributeValue(attrSetName, attrShortName);
|
||||
return value;
|
||||
}
|
||||
|
||||
public CalculateMode? GetAttributeCalculateMode(string attrSetName, string attrShortName)
|
||||
{
|
||||
var value = AttributeSetContainer.GetAttributeCalculateMode(attrSetName, attrShortName);
|
||||
return value;
|
||||
}
|
||||
|
||||
public float? GetAttributeCurrentValue(string setName, string attributeShortName)
|
||||
{
|
||||
var value = AttributeSetContainer.GetAttributeCurrentValue(setName, attributeShortName);
|
||||
return value;
|
||||
}
|
||||
|
||||
public float? GetAttributeBaseValue(string setName, string attributeShortName)
|
||||
{
|
||||
var value = AttributeSetContainer.GetAttributeBaseValue(setName, attributeShortName);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
AbilityContainer.Tick();
|
||||
GameplayEffectContainer.Tick();
|
||||
}
|
||||
|
||||
public Dictionary<string, float> DataSnapshot()
|
||||
{
|
||||
return AttributeSetContainer.Snapshot();
|
||||
}
|
||||
|
||||
public bool TryActivateAbility(string abilityName, object arg = null)
|
||||
{
|
||||
return AbilityContainer.TryActivateAbility(abilityName, arg, null);
|
||||
}
|
||||
|
||||
internal bool TryActivateAbility(string abilityName, GameplayEffectSpec gameplayEffectSpec)
|
||||
{
|
||||
return AbilityContainer.TryActivateAbility(abilityName, null, gameplayEffectSpec);
|
||||
}
|
||||
|
||||
public void TryEndAbility(string abilityName)
|
||||
{
|
||||
AbilityContainer.EndAbility(abilityName);
|
||||
}
|
||||
|
||||
public void TryCancelAbility(string abilityName)
|
||||
{
|
||||
AbilityContainer.CancelAbility(abilityName);
|
||||
}
|
||||
|
||||
public void ApplyModFromInstantGameplayEffect(GameplayEffectSpec spec)
|
||||
{
|
||||
foreach (var modifier in spec.Modifiers)
|
||||
{
|
||||
var attributeValue = GetAttributeAttributeValue(modifier.AttributeSetName, modifier.AttributeShortName);
|
||||
if (attributeValue == null) continue;
|
||||
if (attributeValue.Value.IsSupportOperation(modifier.Operation) == false)
|
||||
{
|
||||
throw new InvalidOperationException("Unsupported operation.");
|
||||
}
|
||||
|
||||
if (attributeValue.Value.CalculateMode != CalculateMode.Stacking)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"[EX] Instant GameplayEffect Can Only Modify Stacking Mode Attribute! " +
|
||||
$"But {modifier.AttributeSetName}.{modifier.AttributeShortName} is {attributeValue.Value.CalculateMode}");
|
||||
}
|
||||
|
||||
var magnitude = modifier.CalculateMagnitude(spec, modifier.ModiferMagnitude);
|
||||
var baseValue = attributeValue.Value.BaseValue;
|
||||
switch (modifier.Operation)
|
||||
{
|
||||
case GEOperation.Add:
|
||||
baseValue += magnitude;
|
||||
break;
|
||||
case GEOperation.Minus:
|
||||
baseValue -= magnitude;
|
||||
break;
|
||||
case GEOperation.Multiply:
|
||||
baseValue *= magnitude;
|
||||
break;
|
||||
case GEOperation.Divide:
|
||||
baseValue /= magnitude;
|
||||
break;
|
||||
case GEOperation.Override:
|
||||
baseValue = magnitude;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
AttributeSetContainer.Sets[modifier.AttributeSetName]
|
||||
.ChangeAttributeBase(modifier.AttributeShortName, baseValue);
|
||||
}
|
||||
}
|
||||
|
||||
public CooldownTimer CheckCooldownFromTags(in GameplayTagSet tags)
|
||||
{
|
||||
return GameplayEffectContainer.CheckCooldownFromTags(tags);
|
||||
}
|
||||
|
||||
public T AttrSet<T>() where T : AttributeSet
|
||||
{
|
||||
AttributeSetContainer.TryGetAttributeSet<T>(out var attrSet);
|
||||
return attrSet;
|
||||
}
|
||||
|
||||
public void ClearGameplayEffect()
|
||||
{
|
||||
// _abilityContainer = new AbilityContainer(this);
|
||||
// GameplayEffectContainer = new GameplayEffectContainer(this);
|
||||
// _attributeSetContainer = new AttributeSetContainer(this);
|
||||
// tagAggregator = new GameplayTagAggregator(this);
|
||||
GameplayEffectContainer.ClearGameplayEffect();
|
||||
}
|
||||
|
||||
private EntityRef<GameplayEffectSpec> AddGameplayEffect(AbilitySystemComponent source, GameplayEffectSpec effectSpec)
|
||||
{
|
||||
return GameplayEffectContainer.AddGameplayEffectSpec(source, effectSpec);
|
||||
}
|
||||
|
||||
private EntityRef<GameplayEffectSpec> AddGameplayEffect(AbilitySystemComponent source, GameplayEffectSpec effectSpec, int effectLevel)
|
||||
{
|
||||
return GameplayEffectContainer.AddGameplayEffectSpec(source, effectSpec, true, effectLevel);
|
||||
}
|
||||
|
||||
private void DisableAllAbilities()
|
||||
{
|
||||
AbilityContainer.CancelAllAbilities();
|
||||
}
|
||||
|
||||
private void ClearGameplayEffects()
|
||||
{
|
||||
GameplayEffectContainer.ClearGameplayEffect();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 183fdb9275244e99bf882bd2e9cb85d5
|
||||
timeCreated: 1701941555
|
@@ -0,0 +1,53 @@
|
||||
using System.Linq;
|
||||
using GAS.General;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
[CreateAssetMenu(fileName = "AbilitySystemComponentPreset", menuName = "GAS/AbilitySystemComponentPreset")]
|
||||
public class AbilitySystemComponentPreset : ScriptableObject
|
||||
{
|
||||
private 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;
|
||||
|
||||
[TabGroup("Base/H1/V2", "Attribute Sets", SdfIconType.PersonLinesFill, TextColor = "#FF7F00", Order = 2)]
|
||||
[LabelText(GASTextDefine.ASC_AttributeSet)]
|
||||
[LabelWidth(WIDTH_LABEL)]
|
||||
[ValueDropdown("@ValueDropdownHelper.AttributeSetChoices", IsUniqueList = true)]
|
||||
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
|
||||
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
|
||||
[CustomContextMenu("排序", "@SortAttributeSets()")]
|
||||
public string[] AttributeSets;
|
||||
|
||||
private void SortAttributeSets() => AttributeSets = AttributeSets.OrderBy(x => x).ToArray();
|
||||
|
||||
[HorizontalGroup("Base/H2", Width = 1 - 0.618f)]
|
||||
[TabGroup("Base/H2/V1", "Tags", SdfIconType.TagsFill, TextColor = "#45B1FF", Order = 1)]
|
||||
[LabelText(GASTextDefine.ASC_BASE_TAG)]
|
||||
[ValueDropdown("@ValueDropdownHelper.GameplayTagChoices", IsUniqueList = true, HideChildProperties = true)]
|
||||
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
|
||||
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
|
||||
[CustomContextMenu("排序", "@BaseTags = TagHelper.Sort($value)")]
|
||||
public GameplayTag[] BaseTags;
|
||||
|
||||
[TabGroup("Base/H2/V2", "Abilities", SdfIconType.YinYang, TextColor = "#D6626E", Order = 2)]
|
||||
[LabelText(GASTextDefine.ASC_BASE_ABILITY)]
|
||||
[ListDrawerSettings(ShowFoldout = true, ShowItemCount = false, DraggableItems = false)]
|
||||
[DisableContextMenu(disableForMember: false, disableCollectionElements: true)]
|
||||
[CustomContextMenu("排序", "@SortBaseAbilities()")]
|
||||
[AssetSelector]
|
||||
[ValidateInput("@ValidateInput_BaseAbilities()", "存在无效的能力")]
|
||||
public AbilityAsset[] BaseAbilities;
|
||||
|
||||
private void SortBaseAbilities() => BaseAbilities = BaseAbilities.OrderBy(x => x.name).ToArray();
|
||||
|
||||
bool ValidateInput_BaseAbilities() => BaseAbilities != null && BaseAbilities.All(ability => ability != null);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85dd2a6201f04545b5a8b020edcb2690
|
||||
timeCreated: 1703831206
|
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GAS.Runtime
|
||||
{
|
||||
public interface IAbilitySystemComponent
|
||||
{
|
||||
void SetPreset(AbilitySystemComponentPreset ascPreset);
|
||||
|
||||
void Init(GameplayTag[] baseTags, Type[] attrSetTypes, AbilityAsset[] baseAbilities, int level);
|
||||
|
||||
void SetLevel(int level);
|
||||
|
||||
bool HasTag(in GameplayTag tag);
|
||||
|
||||
bool HasAllTags(in GameplayTagSet tags);
|
||||
|
||||
bool HasAnyTags(in GameplayTagSet tags);
|
||||
|
||||
void AddFixedTags(in GameplayTagSet tags);
|
||||
void AddFixedTag(in GameplayTag gameplayTag);
|
||||
|
||||
void RemoveFixedTags(in GameplayTagSet tags);
|
||||
void RemoveFixedTag(in GameplayTag gameplayTag);
|
||||
|
||||
EntityRef<GameplayEffectSpec> ApplyGameplayEffectTo(GameplayEffect gameplayEffect, AbilitySystemComponent target);
|
||||
|
||||
EntityRef<GameplayEffectSpec> ApplyGameplayEffectToSelf(GameplayEffect gameplayEffect);
|
||||
|
||||
void ApplyModFromInstantGameplayEffect(GameplayEffectSpec spec);
|
||||
|
||||
void RemoveGameplayEffect(GameplayEffectSpec spec);
|
||||
|
||||
void Tick();
|
||||
|
||||
Dictionary<string, float> DataSnapshot();
|
||||
|
||||
AbilitySpec GrantAbility(AbstractAbility ability);
|
||||
|
||||
void RemoveAbility(string abilityName);
|
||||
|
||||
float? GetAttributeCurrentValue(string setName, string attributeShortName);
|
||||
float? GetAttributeBaseValue(string setName, string attributeShortName);
|
||||
|
||||
bool TryActivateAbility(string abilityName, object arg = null);
|
||||
void TryEndAbility(string abilityName);
|
||||
|
||||
CooldownTimer CheckCooldownFromTags(in GameplayTagSet tags);
|
||||
|
||||
T AttrSet<T>() where T : AttributeSet;
|
||||
|
||||
void ClearGameplayEffect();
|
||||
|
||||
public object UserData { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f87bef00028d4db28c8c66dd5b24d412
|
||||
timeCreated: 1701941525
|
Reference in New Issue
Block a user