完成Demo

This commit is contained in:
dd
2026-06-05 20:21:20 +08:00
parent 531a648a88
commit 1385efdee3
46 changed files with 329 additions and 14 deletions
+65 -3
View File
@@ -16,10 +16,15 @@
namespace
{
FGameplayTag GetDeadTag()
FGameplayTag GetBotDeadTag()
{
return FGameplayTag::RequestGameplayTag(FName(TEXT("State.Dead")));
}
FGameplayTag GetBotSlowedTag()
{
return FGameplayTag::RequestGameplayTag(FName(TEXT("State.Slowed")));
}
}
// Sets default values
@@ -45,6 +50,7 @@ void ABotCharacter::BeginPlay()
void ABotCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
UnbindSlowedTagChangedDelegate();
UnbindHealthChangedDelegate();
if (UWorld* World = GetWorld())
{
@@ -87,9 +93,12 @@ void ABotCharacter::InitAbilityActorInfo()
return;
}
UnbindSlowedTagChangedDelegate();
UnbindHealthChangedDelegate();
AbilitySystemComponent->InitAbilityActorInfo(this, this);
CacheDefaultMovementState();
BindHealthChangedDelegate();
BindSlowedTagChangedDelegate();
if (HasAuthority())
{
@@ -123,6 +132,34 @@ void ABotCharacter::UnbindHealthChangedDelegate()
HealthChangedHandle.Reset();
}
void ABotCharacter::BindSlowedTagChangedDelegate()
{
if (!AbilitySystemComponent || SlowedTagChangedHandle.IsValid())
{
return;
}
const FGameplayTag SlowedTag = GetBotSlowedTag();
SlowedTagChangedHandle = AbilitySystemComponent
->RegisterGameplayTagEvent(SlowedTag, EGameplayTagEventType::NewOrRemoved)
.AddUObject(this, &ThisClass::OnSlowedTagChanged);
ApplySlowedState(AbilitySystemComponent->GetTagCount(SlowedTag) > 0);
}
void ABotCharacter::UnbindSlowedTagChangedDelegate()
{
if (!AbilitySystemComponent || !SlowedTagChangedHandle.IsValid())
{
return;
}
AbilitySystemComponent
->RegisterGameplayTagEvent(GetBotSlowedTag(), EGameplayTagEventType::NewOrRemoved)
.Remove(SlowedTagChangedHandle);
SlowedTagChangedHandle.Reset();
}
void ABotCharacter::OnHealthChanged(const FOnAttributeChangeData& Data)
{
if (HasAuthority() && Data.NewValue <= 0.f)
@@ -131,6 +168,31 @@ void ABotCharacter::OnHealthChanged(const FOnAttributeChangeData& Data)
}
}
void ABotCharacter::OnSlowedTagChanged(const FGameplayTag Tag, int32 NewCount)
{
ApplySlowedState(NewCount > 0);
}
void ABotCharacter::CacheDefaultMovementState()
{
if (bHasCachedDefaultMovementState)
{
return;
}
DefaultMaxWalkSpeed = GetCharacterMovement()->MaxWalkSpeed;
DefaultGlobalAnimRateScale = GetMesh()->GlobalAnimRateScale;
bHasCachedDefaultMovementState = true;
}
void ABotCharacter::ApplySlowedState(bool bSlowed)
{
CacheDefaultMovementState();
GetMesh()->GlobalAnimRateScale = bSlowed ? 0.5f : DefaultGlobalAnimRateScale;
GetCharacterMovement()->MaxWalkSpeed = bSlowed ? 300.f : DefaultMaxWalkSpeed;
}
void ABotCharacter::GiveDefaultAbilities()
{
if (bHasGivenDefaultAbilities || !AbilitySystemComponent)
@@ -180,7 +242,7 @@ void ABotCharacter::HandleDeath()
if (AbilitySystemComponent)
{
AbilitySystemComponent->SetLooseGameplayTagCount(GetDeadTag(), 1, EGameplayTagReplicationState::TagOnly);
AbilitySystemComponent->SetLooseGameplayTagCount(GetBotDeadTag(), 1, EGameplayTagReplicationState::TagOnly);
}
MulticastHandleDeath();
@@ -223,7 +285,7 @@ void ABotCharacter::HandleRespawn()
if (AbilitySystemComponent)
{
AbilitySystemComponent->SetLooseGameplayTagCount(GetDeadTag(), 0, EGameplayTagReplicationState::TagOnly);
AbilitySystemComponent->SetLooseGameplayTagCount(GetBotDeadTag(), 0, EGameplayTagReplicationState::TagOnly);
const float MaxHealth = AbilitySystemComponent->GetNumericAttribute(UMainAttributeSet::GetMaxHealthAttribute());
AbilitySystemComponent->SetNumericAttributeBase(UMainAttributeSet::GetHealthAttribute(), MaxHealth);
+2 -2
View File
@@ -8,7 +8,7 @@
namespace
{
FGameplayTag GetDeadTag()
FGameplayTag GetAttributeSetDeadTag()
{
return FGameplayTag::RequestGameplayTag(FName(TEXT("State.Dead")));
}
@@ -56,7 +56,7 @@ void UMainAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallba
AActor* OwningActor = ASC->GetOwnerActor();
if (OwningActor && OwningActor->HasAuthority())
{
ASC->SetLooseGameplayTagCount(GetDeadTag(), 1, EGameplayTagReplicationState::TagOnly);
ASC->SetLooseGameplayTagCount(GetAttributeSetDeadTag(), 1, EGameplayTagReplicationState::TagOnly);
}
}
}
+10
View File
@@ -12,6 +12,7 @@ class UAbilitySystemComponent;
class UAttributeSet;
class UGameplayAbility;
class UGameplayEffect;
struct FGameplayTag;
struct FOnAttributeChangeData;
UCLASS()
@@ -62,7 +63,12 @@ private:
void InitAbilityActorInfo();
void BindHealthChangedDelegate();
void UnbindHealthChangedDelegate();
void BindSlowedTagChangedDelegate();
void UnbindSlowedTagChangedDelegate();
void OnHealthChanged(const FOnAttributeChangeData& Data);
void OnSlowedTagChanged(const FGameplayTag Tag, int32 NewCount);
void CacheDefaultMovementState();
void ApplySlowedState(bool bSlowed);
void GiveDefaultAbilities();
void ApplyDefaultAttributeEffect();
void HandleDeath();
@@ -79,5 +85,9 @@ private:
bool bHasHandledDeath = false;
bool bHasNotifiedDeath = false;
FDelegateHandle HealthChangedHandle;
FDelegateHandle SlowedTagChangedHandle;
float DefaultMaxWalkSpeed = 0.f;
float DefaultGlobalAnimRateScale = 1.f;
bool bHasCachedDefaultMovementState = false;
FTimerHandle RespawnTimer;
};
+97 -4
View File
@@ -22,10 +22,15 @@
namespace
{
FGameplayTag GetDeadTag()
FGameplayTag GetPvPDemoCharacterDeadTag()
{
return FGameplayTag::RequestGameplayTag(FName(TEXT("State.Dead")));
}
FGameplayTag GetPvPDemoCharacterSlowedTag()
{
return FGameplayTag::RequestGameplayTag(FName(TEXT("State.Slowed")));
}
}
@@ -85,6 +90,7 @@ void APvPDemoCharacter::OnRep_PlayerState()
void APvPDemoCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
UnbindSlowedTagChangedDelegate();
UnbindHealthChangedDelegate();
Super::EndPlay(EndPlayReason);
@@ -188,18 +194,54 @@ UAbilitySystemComponent* APvPDemoCharacter::GetAbilitySystemComponent() const
return AbilitySystemComponent;
}
bool APvPDemoCharacter::AddHealth(float Amount)
{
if (Amount <= 0.f)
{
UE_LOG(LogPvPDemo, Warning, TEXT("AddHealth failed: Amount must be greater than 0."));
return false;
}
if (!HasAuthority())
{
ServerAddHealth(Amount);
return true;
}
if (!AbilitySystemComponent)
{
UE_LOG(LogPvPDemo, Warning, TEXT("AddHealth failed: AbilitySystemComponent is null."));
return false;
}
const float CurrentHealth = AbilitySystemComponent->GetNumericAttribute(UMainAttributeSet::GetHealthAttribute());
const float MaxHealth = AbilitySystemComponent->GetNumericAttribute(UMainAttributeSet::GetMaxHealthAttribute());
const float NewHealth = FMath::Clamp(CurrentHealth + Amount, 0.f, MaxHealth);
AbilitySystemComponent->SetNumericAttributeBase(UMainAttributeSet::GetHealthAttribute(), NewHealth);
return true;
}
void APvPDemoCharacter::ServerAddHealth_Implementation(float Amount)
{
AddHealth(Amount);
}
void APvPDemoCharacter::InitAbilityActorInfo()
{
AMainPlayerState* MainPlayerState = GetPlayerState<AMainPlayerState>();
check(MainPlayerState);
UnbindSlowedTagChangedDelegate();
UnbindHealthChangedDelegate();
// 设置ASC的所有者,OwnerActor是所有者,AvatarActor是游戏中的形象
MainPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(MainPlayerState, this);
AbilitySystemComponent = MainPlayerState->GetAbilitySystemComponent();
AttributeSet = MainPlayerState->GetAttributeSet();
CacheDefaultMovementState();
BindHealthChangedDelegate();
BindSlowedTagChangedDelegate();
if (HasAuthority() && AbilitySystemComponent && HeldItem)
{
@@ -234,6 +276,34 @@ void APvPDemoCharacter::UnbindHealthChangedDelegate()
HealthChangedHandle.Reset();
}
void APvPDemoCharacter::BindSlowedTagChangedDelegate()
{
if (!AbilitySystemComponent || SlowedTagChangedHandle.IsValid())
{
return;
}
const FGameplayTag SlowedTag = GetPvPDemoCharacterSlowedTag();
SlowedTagChangedHandle = AbilitySystemComponent
->RegisterGameplayTagEvent(SlowedTag, EGameplayTagEventType::NewOrRemoved)
.AddUObject(this, &ThisClass::OnSlowedTagChanged);
ApplySlowedState(AbilitySystemComponent->GetTagCount(SlowedTag) > 0);
}
void APvPDemoCharacter::UnbindSlowedTagChangedDelegate()
{
if (!AbilitySystemComponent || !SlowedTagChangedHandle.IsValid())
{
return;
}
AbilitySystemComponent
->RegisterGameplayTagEvent(GetPvPDemoCharacterSlowedTag(), EGameplayTagEventType::NewOrRemoved)
.Remove(SlowedTagChangedHandle);
SlowedTagChangedHandle.Reset();
}
void APvPDemoCharacter::OnHealthChanged(const FOnAttributeChangeData& Data)
{
if (HasAuthority() && Data.NewValue <= 0.f)
@@ -242,6 +312,31 @@ void APvPDemoCharacter::OnHealthChanged(const FOnAttributeChangeData& Data)
}
}
void APvPDemoCharacter::OnSlowedTagChanged(const FGameplayTag Tag, int32 NewCount)
{
ApplySlowedState(NewCount > 0);
}
void APvPDemoCharacter::CacheDefaultMovementState()
{
if (bHasCachedDefaultMovementState)
{
return;
}
DefaultMaxWalkSpeed = GetCharacterMovement()->MaxWalkSpeed;
DefaultGlobalAnimRateScale = GetMesh()->GlobalAnimRateScale;
bHasCachedDefaultMovementState = true;
}
void APvPDemoCharacter::ApplySlowedState(bool bSlowed)
{
CacheDefaultMovementState();
GetMesh()->GlobalAnimRateScale = bSlowed ? 0.5f : DefaultGlobalAnimRateScale;
GetCharacterMovement()->MaxWalkSpeed = bSlowed ? 300.f : DefaultMaxWalkSpeed;
}
void APvPDemoCharacter::HandleDeath()
{
if (bHasHandledDeath)
@@ -253,7 +348,7 @@ void APvPDemoCharacter::HandleDeath()
if (AbilitySystemComponent)
{
AbilitySystemComponent->SetLooseGameplayTagCount(GetDeadTag(), 1, EGameplayTagReplicationState::TagOnly);
AbilitySystemComponent->SetLooseGameplayTagCount(GetPvPDemoCharacterDeadTag(), 1, EGameplayTagReplicationState::TagOnly);
}
if (UWorld* World = GetWorld())
@@ -333,5 +428,3 @@ bool APvPDemoCharacter::RemoveHeldItemTag(FGameplayTag Tag)
ASC->RemoveLooseGameplayTag(Tag);
return true;
}
+19
View File
@@ -15,6 +15,7 @@ class UAbilitySystemComponent;
class UAttributeSet;
struct FInputActionValue;
struct FOnAttributeChangeData;
struct FGameplayTag;
class UGameplayAbility;
DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);
@@ -57,6 +58,9 @@ protected:
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* AttackAction;
UPROPERTY(EditAnywhere, Category="Input")
UInputAction* SprintAction;
// ASC
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
@@ -72,6 +76,9 @@ public:
UFUNCTION(BlueprintCallable)
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
UFUNCTION(BlueprintCallable, Category="GAS|Health")
bool AddHealth(float Amount);
virtual void PossessedBy(AController* NewController) override;
virtual void OnRep_PlayerState() override;
@@ -135,9 +142,17 @@ private:
void InitAbilityActorInfo();
void BindHealthChangedDelegate();
void UnbindHealthChangedDelegate();
void BindSlowedTagChangedDelegate();
void UnbindSlowedTagChangedDelegate();
void OnHealthChanged(const FOnAttributeChangeData& Data);
void OnSlowedTagChanged(const FGameplayTag Tag, int32 NewCount);
void CacheDefaultMovementState();
void ApplySlowedState(bool bSlowed);
void HandleDeath();
UFUNCTION(Server, Reliable)
void ServerAddHealth(float Amount);
UFUNCTION(NetMulticast, Reliable)
void MulticastHandleDeath();
@@ -151,6 +166,10 @@ private:
bool RemoveHeldItemTag(FGameplayTag Tag);
FDelegateHandle HealthChangedHandle;
FDelegateHandle SlowedTagChangedHandle;
float DefaultMaxWalkSpeed = 0.f;
float DefaultGlobalAnimRateScale = 1.f;
bool bHasCachedDefaultMovementState = false;
bool bHasHandledDeath = false;
bool bHasNotifiedDeath = false;
};
+2 -2
View File
@@ -12,7 +12,7 @@
namespace
{
FGameplayTag GetDeadTag()
FGameplayTag GetGameModeDeadTag()
{
return FGameplayTag::RequestGameplayTag(FName(TEXT("State.Dead")));
}
@@ -86,7 +86,7 @@ void APvPDemoGameMode::ResetRespawnAttributes(AController* Controller) const
return;
}
ASC->SetLooseGameplayTagCount(GetDeadTag(), 0, EGameplayTagReplicationState::TagOnly);
ASC->SetLooseGameplayTagCount(GetGameModeDeadTag(), 0, EGameplayTagReplicationState::TagOnly);
const float MaxHealth = ASC->GetNumericAttribute(UMainAttributeSet::GetMaxHealthAttribute());
ASC->SetNumericAttributeBase(UMainAttributeSet::GetHealthAttribute(), MaxHealth);
+2 -1
View File
@@ -76,7 +76,8 @@ void APvPDemoPlayerController::SetupInputComponent()
bool APvPDemoPlayerController::ShouldUseTouchControls() const
{
// are we on a mobile platform? Should we force touch?
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
// return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
return false;
}
UAbilitySystemComponent* APvPDemoPlayerController::GetMainWidgetAbilitySystemComponent() const