Last active
July 2, 2023 01:48
-
-
Save x157/d59d729f6bb62d816175048b446376ee to your computer and use it in GitHub Desktop.
How to derive from ALyraPawn to implement Gameplay Ability System, Gameplay Cues and Gameplay Tags.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Source/LyraGame/Character/LyraHeroComponent.h b/Source/LyraGame/Character/LyraHeroComponent.h | |
index 312623d..921feb3 100644 | |
--- a/Source/LyraGame/Character/LyraHeroComponent.h | |
+++ b/Source/LyraGame/Character/LyraHeroComponent.h | |
@@ -22,7 +22,7 @@ class ULyraInputConfig; | |
* A component used to create a player controlled pawns (characters, vehicles, etc..). | |
*/ | |
UCLASS(Blueprintable, Meta=(BlueprintSpawnableComponent)) | |
-class ULyraHeroComponent : public ULyraPawnComponent | |
+class LYRAGAME_API ULyraHeroComponent : public ULyraPawnComponent | |
{ | |
GENERATED_BODY() | |
diff --git a/Source/LyraGame/Character/LyraPawn.h b/Source/LyraGame/Character/LyraPawn.h | |
index d2741b0..4220d90 100644 | |
--- a/Source/LyraGame/Character/LyraPawn.h | |
+++ b/Source/LyraGame/Character/LyraPawn.h | |
@@ -12,7 +12,7 @@ | |
* ALyraPawn | |
*/ | |
UCLASS() | |
-class ALyraPawn : public AModularPawn, public ILyraTeamAgentInterface | |
+class LYRAGAME_API ALyraPawn : public AModularPawn, public ILyraTeamAgentInterface | |
{ | |
GENERATED_BODY() | |
@@ -48,10 +48,12 @@ private: | |
UFUNCTION() | |
void OnControllerChangedTeam(UObject* TeamAgent, int32 OldTeam, int32 NewTeam); | |
-private: | |
+protected: | |
+ // must be protected since ALyraPawn doesn't handle NotifyControllerChanged and child classes must do it | |
UPROPERTY(ReplicatedUsing = OnRep_MyTeamID) | |
FGenericTeamId MyTeamID; | |
+private: | |
UPROPERTY() | |
FOnLyraTeamIndexChangedDelegate OnTeamChangedDelegate; | |
diff --git a/Source/LyraGame/Character/LyraPawnExtensionComponent.h b/Source/LyraGame/Character/LyraPawnExtensionComponent.h | |
index 255984e..758c0a6 100644 | |
--- a/Source/LyraGame/Character/LyraPawnExtensionComponent.h | |
+++ b/Source/LyraGame/Character/LyraPawnExtensionComponent.h | |
@@ -21,7 +21,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLyraDynamicMulticastDelegate); | |
* Component used to add functionality to all Pawn classes. | |
*/ | |
UCLASS() | |
-class ULyraPawnExtensionComponent : public ULyraPawnComponent | |
+class LYRAGAME_API ULyraPawnExtensionComponent : public ULyraPawnComponent | |
{ | |
GENERATED_BODY() | |
diff --git a/Source/LyraGame/LyraGameplayTags.h b/Source/LyraGame/LyraGameplayTags.h | |
index f44e0c3..13c08e7 100644 | |
--- a/Source/LyraGame/LyraGameplayTags.h | |
+++ b/Source/LyraGame/LyraGameplayTags.h | |
@@ -12,7 +12,7 @@ class UGameplayTagsManager; | |
* | |
* Singleton containing native gameplay tags. | |
*/ | |
-struct FLyraGameplayTags | |
+struct LYRAGAME_API FLyraGameplayTags | |
{ | |
public: | |
diff --git a/Source/LyraGame/Teams/LyraTeamAgentInterface.h b/Source/LyraGame/Teams/LyraTeamAgentInterface.h | |
index e685a3a..388519b 100644 | |
--- a/Source/LyraGame/Teams/LyraTeamAgentInterface.h | |
+++ b/Source/LyraGame/Teams/LyraTeamAgentInterface.h | |
@@ -23,7 +23,7 @@ inline FGenericTeamId IntegerToGenericTeamId(int32 ID) | |
/** Interface for actors which can be associated with teams */ | |
UINTERFACE(meta=(CannotImplementInterfaceInBlueprint)) | |
-class ULyraTeamAgentInterface : public UGenericTeamAgentInterface | |
+class LYRAGAME_API ULyraTeamAgentInterface : public UGenericTeamAgentInterface | |
{ | |
GENERATED_UINTERFACE_BODY() | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Character/XCL_Pawn.h" | |
#include "LyraGameplayTags.h" | |
#include "AbilitySystem/LyraAbilitySystemComponent.h" | |
#include "Character/LyraPawnExtensionComponent.h" | |
#include "GameFramework/PawnMovementComponent.h" | |
// Sets default values | |
AXCL_Pawn::AXCL_Pawn() | |
{ | |
PawnExtComponent = CreateDefaultSubobject<ULyraPawnExtensionComponent>(TEXT("PawnExtensionComponent")); | |
PawnExtComponent->OnAbilitySystemInitialized_RegisterAndCall(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ThisClass::OnAbilitySystemInitialized)); | |
PawnExtComponent->OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate::CreateUObject(this, &ThisClass::OnAbilitySystemUninitialized)); | |
} | |
// add support for controller changing teams | |
// (Epic implemented this in ALyraCharacter but not in ALyraPawn) | |
void AXCL_Pawn::NotifyControllerChanged() | |
{ | |
const FGenericTeamId OldTeamId = GetGenericTeamId(); | |
Super::NotifyControllerChanged(); | |
// Update our team ID based on the controller | |
if (HasAuthority() && (Controller != nullptr)) | |
{ | |
if (ILyraTeamAgentInterface* ControllerWithTeam = Cast<ILyraTeamAgentInterface>(Controller)) | |
{ | |
MyTeamID = ControllerWithTeam->GetGenericTeamId(); | |
ConditionalBroadcastTeamChanged(this, OldTeamId, MyTeamID); | |
} | |
} | |
} | |
void AXCL_Pawn::OnRep_Controller() | |
{ | |
Super::OnRep_Controller(); | |
PawnExtComponent->HandleControllerChanged(); | |
} | |
void AXCL_Pawn::OnRep_PlayerState() | |
{ | |
Super::OnRep_PlayerState(); | |
PawnExtComponent->HandlePlayerStateReplicated(); | |
} | |
void AXCL_Pawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) | |
{ | |
Super::SetupPlayerInputComponent(PlayerInputComponent); | |
PawnExtComponent->SetupPlayerInputComponent(); | |
} | |
void AXCL_Pawn::OnAbilitySystemInitialized() | |
{ | |
// [xist] decided not to force a HealthComponent on every pawn. Some pawns are invincible! | |
// override this yourself in your own pawn if you want a health component. | |
// | |
//ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent(); | |
//check(LyraASC); | |
//HealthComponent->InitializeWithAbilitySystem(LyraASC); | |
InitializeGameplayTags(); | |
} | |
void AXCL_Pawn::OnAbilitySystemUninitialized() | |
{ | |
//HealthComponent->UninitializeFromAbilitySystem(); | |
} | |
void AXCL_Pawn::InitializeGameplayTags() | |
{ | |
// Clear tags that may be lingering on the ability system from the previous pawn. | |
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) | |
{ | |
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get(); | |
for (const TPair<uint8, FGameplayTag>& TagMapping : GameplayTags.MovementModeTagMap) | |
{ | |
if (TagMapping.Value.IsValid()) | |
{ | |
LyraASC->SetLooseGameplayTagCount(TagMapping.Value, 0); | |
} | |
} | |
for (const TPair<uint8, FGameplayTag>& TagMapping : GameplayTags.CustomMovementModeTagMap) | |
{ | |
if (TagMapping.Value.IsValid()) | |
{ | |
LyraASC->SetLooseGameplayTagCount(TagMapping.Value, 0); | |
} | |
} | |
// [xist] removed explicit ALyraCharacter movement tag logic, made this the | |
// responsibility of derived classes - override this method to add your | |
// movement-based gameplay tag. | |
// | |
// e.g. SetMovementModeTag(MOVE_Walking, 0, true); | |
} | |
} | |
void AXCL_Pawn::SetMovementModeTag(EMovementMode MovementMode, uint8 CustomMovementMode, bool bTagEnabled) | |
{ | |
if (ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) | |
{ | |
const FLyraGameplayTags& GameplayTags = FLyraGameplayTags::Get(); | |
const FGameplayTag* MovementModeTag = nullptr; | |
if (MovementMode == MOVE_Custom) | |
{ | |
MovementModeTag = GameplayTags.CustomMovementModeTagMap.Find(CustomMovementMode); | |
} | |
else | |
{ | |
MovementModeTag = GameplayTags.MovementModeTagMap.Find(MovementMode); | |
} | |
if (MovementModeTag && MovementModeTag->IsValid()) | |
{ | |
LyraASC->SetLooseGameplayTagCount(*MovementModeTag, (bTagEnabled ? 1 : 0)); | |
} | |
} | |
} | |
ULyraAbilitySystemComponent* AXCL_Pawn::GetLyraAbilitySystemComponent() const | |
{ | |
return PawnExtComponent->GetLyraAbilitySystemComponent(); | |
} | |
UAbilitySystemComponent* AXCL_Pawn::GetAbilitySystemComponent() const | |
{ | |
return PawnExtComponent->GetLyraAbilitySystemComponent(); | |
} | |
void AXCL_Pawn::GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const | |
{ | |
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) | |
{ | |
LyraASC->GetOwnedGameplayTags(TagContainer); | |
} | |
} | |
bool AXCL_Pawn::HasMatchingGameplayTag(FGameplayTag TagToCheck) const | |
{ | |
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) | |
{ | |
return LyraASC->HasMatchingGameplayTag(TagToCheck); | |
} | |
return false; | |
} | |
bool AXCL_Pawn::HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const | |
{ | |
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) | |
{ | |
return LyraASC->HasAllMatchingGameplayTags(TagContainer); | |
} | |
return false; | |
} | |
bool AXCL_Pawn::HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const | |
{ | |
if (const ULyraAbilitySystemComponent* LyraASC = GetLyraAbilitySystemComponent()) | |
{ | |
return LyraASC->HasAnyMatchingGameplayTags(TagContainer); | |
} | |
return false; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "CoreMinimal.h" | |
#include "AbilitySystemInterface.h" | |
#include "GameplayCueInterface.h" | |
#include "GameplayTagAssetInterface.h" | |
#include "Character/LyraPawn.h" | |
#include "XCL_Pawn.generated.h" | |
/** | |
* Base Pawn Class | |
* | |
* This stuff should really be implemented in ALyraPawn, but Epic put all of their ability system, | |
* gameplay cues and gameplay tags into ALyraCharacter, so we need to recreate the wheel. | |
* | |
* For the most part I just copied what they put in ALyraCharacter. | |
* | |
* If they ever move this stuff into ALyraPawn we can get rid of this class. It literally does | |
* nothing at all except what I would have expected Epic to bundle with ALyraPawn rather than | |
* with ALyraCharacter. | |
*/ | |
UCLASS() | |
class XISTCORELYRARUNTIME_API AXCL_Pawn : public ALyraPawn, public IAbilitySystemInterface, public IGameplayCueInterface, public IGameplayTagAssetInterface | |
{ | |
GENERATED_BODY() | |
private: | |
UPROPERTY(EditDefaultsOnly, Category="Lyra|Pawn", Meta = (AllowPrivateAccess = "true")) | |
class ULyraPawnExtensionComponent* PawnExtComponent; | |
public: | |
// Sets default values for this pawn's properties | |
AXCL_Pawn(); | |
//~APawn interface | |
virtual void NotifyControllerChanged() override; | |
//~End of APawn interface | |
//~Support for UPawnExtensionComponent | |
virtual void OnRep_Controller() override; | |
virtual void OnRep_PlayerState() override; | |
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override; | |
//~End Support for UPawnExtensionComponent | |
//~ALyraCharacter-like ability system / tags implementation | |
virtual void OnAbilitySystemInitialized(); | |
virtual void OnAbilitySystemUninitialized(); | |
virtual void InitializeGameplayTags(); // [xist] made this virtual | |
virtual void SetMovementModeTag(EMovementMode MovementMode, uint8 CustomMovementMode, bool bTagEnabled); | |
UFUNCTION(BlueprintCallable, Category = "Lyra|Pawn") | |
class ULyraAbilitySystemComponent* GetLyraAbilitySystemComponent() const; | |
//~End of ALyraCharacter-like ability system implementation | |
//~IAbilitySystemInterface | |
virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; | |
//~End of IAbilitySystemInterface | |
//~IGameplayCueInterface | |
//~End of IGameplayCueInterface | |
//~IGameplayTagAssetInterface | |
virtual void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override; | |
virtual bool HasMatchingGameplayTag(FGameplayTag TagToCheck) const override; | |
virtual bool HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override; | |
virtual bool HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override; | |
//~End of IGameplayTagAssetInterface | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ThirdPersonStrategy/XCL_TPSPlayerPawn.h" | |
#include "Character/LyraHeroComponent.h" | |
#include "Components/SphereComponent.h" | |
#include "GameFramework/FloatingPawnMovement.h" | |
// Sets default values | |
AXCL_TPSPlayerPawn::AXCL_TPSPlayerPawn() | |
{ | |
// Set this pawn to call Tick() every frame. | |
PrimaryActorTick.bCanEverTick = true; | |
SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("PrimitiveComponent")); | |
SphereComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision); | |
SphereComponent->SetCanEverAffectNavigation(false); | |
RootComponent = SphereComponent; | |
MovementComponent = CreateDefaultSubobject<UFloatingPawnMovement>(TEXT("MovementComponent")); | |
MovementComponent->SetUpdatedComponent(SphereComponent); | |
LyraHeroComponent = CreateDefaultSubobject<ULyraHeroComponent>(TEXT("LyraHeroComponent")); | |
} | |
// Called by base class | |
void AXCL_TPSPlayerPawn::InitializeGameplayTags() | |
{ | |
Super::InitializeGameplayTags(); | |
// tell the ability system that this pawn is flying | |
SetMovementModeTag(MOVE_Flying, 0, true); | |
} | |
UPawnMovementComponent* AXCL_TPSPlayerPawn::GetMovementComponent() const | |
{ | |
return MovementComponent; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "CoreMinimal.h" | |
#include "Character/XCL_Pawn.h" | |
#include "XCL_TPSPlayerPawn.generated.h" | |
UCLASS() | |
class XISTCORELYRARUNTIME_API AXCL_TPSPlayerPawn : public AXCL_Pawn | |
{ | |
GENERATED_BODY() | |
private: | |
UPROPERTY(EditDefaultsOnly, Category="Xist|Pawn", Meta = (AllowPrivateAccess = "true")) | |
class USphereComponent* SphereComponent; | |
UPROPERTY(EditDefaultsOnly, Category="Xist|Pawn", Meta = (AllowPrivateAccess = "true")) | |
class UFloatingPawnMovement* MovementComponent; | |
UPROPERTY(EditDefaultsOnly, Category="Xist|Pawn", Meta = (AllowPrivateAccess = "true")) | |
class ULyraHeroComponent* LyraHeroComponent; | |
public: | |
// Sets default values for this pawn's properties | |
AXCL_TPSPlayerPawn(); | |
// Called by base class | |
virtual void InitializeGameplayTags() override; | |
//~APawn interface | |
virtual UPawnMovementComponent* GetMovementComponent() const override; | |
//~End of APawn interface | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"FileVersion": 3, | |
"Version": 1, | |
"VersionName": "0.1", | |
"FriendlyName": "XistCoreLyra", | |
"Description": "", | |
"Category": "Game Features", | |
"CreatedBy": "Xist", | |
"CreatedByURL": "http://xist.gg", | |
"DocsURL": "", | |
"MarketplaceURL": "", | |
"SupportURL": "", | |
"EnabledByDefault": false, | |
"CanContainContent": true, | |
"IsBetaVersion": true, | |
"IsExperimentalVersion": true, | |
"Installed": false, | |
"Modules": [ | |
{ | |
"Name": "XistCoreLyraRuntime", | |
"Type": "Runtime", | |
"LoadingPhase": "Default" | |
} | |
], | |
"Plugins": [ | |
{ | |
"Name": "CommonGame", | |
"Enabled": true | |
}, | |
{ | |
"Name": "CommonUI", | |
"Enabled": true | |
}, | |
{ | |
"Name": "EnhancedInput", | |
"Enabled": true | |
}, | |
{ | |
"Name": "GameplayAbilities", | |
"Enabled": true | |
}, | |
{ | |
"Name": "GameplayMessageRouter", | |
"Enabled": true | |
}, | |
{ | |
"Name": "ModularGameplayActors", | |
"Enabled": true | |
} | |
], | |
"ExplicitlyLoaded": true, | |
"BuiltInInitialFeatureState": "Loaded" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnrealBuildTool; | |
public class XistCoreLyraRuntime : ModuleRules | |
{ | |
public XistCoreLyraRuntime(ReadOnlyTargetRules Target) : base(Target) | |
{ | |
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; | |
PublicIncludePaths.AddRange( | |
new string[] { | |
// ... add public include paths required here ... | |
} | |
); | |
PrivateIncludePaths.AddRange( | |
new string[] { | |
// ... add other private include paths required here ... | |
} | |
); | |
PublicDependencyModuleNames.AddRange( | |
new string[] | |
{ | |
"Core", | |
// ... add other public dependencies that you statically link with here ... | |
"LyraGame", | |
} | |
); | |
PrivateDependencyModuleNames.AddRange( | |
new string[] | |
{ | |
"CoreUObject", | |
"Engine", | |
"Slate", | |
"SlateCore", | |
// ... add private dependencies that you statically link with here ... | |
// Stuff needed for XCL_Pawn: | |
"AIModule", | |
"CommonInput", | |
"EnhancedInput", | |
"GameplayAbilities", | |
"GameplayMessageRuntime", | |
"GameplayTags", | |
"ModularGameplayActors", | |
} | |
); | |
DynamicallyLoadedModuleNames.AddRange( | |
new string[] | |
{ | |
// ... add any modules that your module loads dynamically here ... | |
} | |
); | |
} | |
} |
I had compilation issues and solved them exposing the 2 interface classes not just one as in the GIT above... so adding LYRAGAME_API to the second class...
Yep. Get private static class / linker issues in all functions using. ILyraTeamAgentInterface::ConditionalBroadcastTeamChanged
Your fix seemed to solve those linker issues for those of that want to ILyraTeamAgentInterface on our own characters.
You saved my bacon. No way I would have figured out that was the issue, as I included the AIModule correctly in the build.cs for my custom game feature. I took X_API as a black box macro for granted.
Gotta submit a report to Epic, so they fix this issue.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Additionally I have found another issue. In the base
LyraPawn.cpp
class the function PossessedBy is:However in
LyraCharacter.cpp
That function is
The line
PawnExtComponent->HandleControllerChanged();
is important because it continues the initialization chain for the interfaceIGameFrameworkInitStateInterface
that LyraPawnExtensionComponent uses.To be honest I do not like the way the LyraCharacter is initialized for many reasons. However for my project XCL_Pawn not having
PawnExtComponent->HandleControllerChanged();
caused me some issues.Just throwing this out there just in case someone else get initialization issues. Thanks XCL for all of your work!