Compare commits

...

10 Commits

Author SHA1 Message Date
Caleb Buhungiro
f84a711b4f setup AI Perception and forgatting behavior from Blackboard 2025-09-14 23:22:37 +08:00
Caleb Buhungiro
1c1c56ea46 Add perception component 2025-09-14 19:47:24 +08:00
Caleb Buhungiro
1ccb111012 Add minion character & CAIController 2025-09-13 21:48:34 +08:00
Caleb Buhungiro
1676ed7135 prevent player from damaging team member 2025-09-13 16:30:18 +08:00
Caleb Buhungiro
91fb35aa74 Add Player Start Spot 2025-09-13 15:32:32 +08:00
Caleb Buhungiro
1e91d4c4a0 setup teamID placeholder 2025-09-13 14:48:25 +08:00
Caleb Buhungiro
0eac201dad update map 2025-09-13 13:14:38 +08:00
Caleb Buhungiro
0ffb67d983 Add level assets 2025-09-13 13:13:38 +08:00
Caleb Buhungiro
40d5962dc0 finetune ragdoll physics 2025-09-13 12:59:34 +08:00
Caleb Buhungiro
2446f29cad enable ragdoll on death 2025-09-13 12:00:19 +08:00
90 changed files with 573 additions and 26 deletions

View File

@@ -1,17 +1,17 @@
[/Script/EngineSettings.GameMapsSettings] [/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Game/TempLevel.TempLevel GameDefaultMap=/Game/Maps/GameLevel.GameLevel
EditorStartupMap=/Game/TempLevel.TempLevel EditorStartupMap=/Game/Maps/GameLevel.GameLevel
[/Script/Engine.RendererSettings] [/Script/Engine.RendererSettings]
r.AllowStaticLighting=False r.AllowStaticLighting=False
r.GenerateMeshDistanceFields=True r.GenerateMeshDistanceFields=True
r.DynamicGlobalIlluminationMethod=1 r.DynamicGlobalIlluminationMethod=0
r.ReflectionMethod=1 r.ReflectionMethod=0
r.SkinCache.CompileShaders=True r.SkinCache.CompileShaders=True
@@ -91,3 +91,17 @@ ConnectionType=USBOnly
bUseManualIPAddress=False bUseManualIPAddress=False
ManualIPAddress= ManualIPAddress=
[/Script/GameplayDebugger.GameplayDebuggerConfig]
CategorySlot5=Five
CategorySlot1=One
CategorySlot2=Two
CategorySlot3=Three
CategorySlot4=Four
CategorySlot6=Six
CategorySlot7=Seven
CategorySlot8=Eight
CategorySlot9=Nine
[/Script/AIModule.AISystem]
bForgetStaleActors=True

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Maps/GameLevel.umap LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Maps/materials/R.uasset LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Models/bush/grass.uasset LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -16,13 +16,14 @@ public class Crunch : ModuleRules
"EnhancedInput", "EnhancedInput",
"GameplayAbilities", "GameplayAbilities",
"GameplayTasks", "GameplayTasks",
"GameplayTags" "GameplayTags",
"AIModule"
]); ]);
PrivateDependencyModuleNames.AddRange([ PrivateDependencyModuleNames.AddRange([
"UMG", "UMG",
"Slate", "Slate",
"SlateCore" "SlateCore",
]); ]);
// Uncomment if you are using Slate UI // Uncomment if you are using Slate UI

View File

@@ -0,0 +1,102 @@
// Multiplayer By Caleb
#include "AI/CAIController.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "Character/CCharacter.h"
#include "Perception/AIPerceptionComponent.h"
#include "Perception/AISenseConfig_Sight.h"
ACAIController::ACAIController()
{
PrimaryActorTick.bCanEverTick = true;
AIPerceptionComponent = CreateDefaultSubobject<UAIPerceptionComponent>("AI Perception Component");
SightConfig = CreateDefaultSubobject<UAISenseConfig_Sight>("Sight config");
SightConfig->DetectionByAffiliation.bDetectEnemies = true;
SightConfig->DetectionByAffiliation.bDetectFriendlies = true;
SightConfig->DetectionByAffiliation.bDetectNeutrals = false;
SightConfig->SightRadius = 1000.f;
SightConfig->LoseSightRadius = 1200.f;
SightConfig->SetMaxAge(5.f);
SightConfig->PeripheralVisionAngleDegrees = 100.f;
AIPerceptionComponent->ConfigureSense(*SightConfig);
AIPerceptionComponent->OnTargetPerceptionUpdated.AddDynamic(this, &ThisClass::TargetPerceptionUpdated);
AIPerceptionComponent->OnTargetPerceptionForgotten.AddDynamic(this, &ACAIController::TargetForgotten);
}
void ACAIController::OnPossess(APawn* InPawn)
{
Super::OnPossess(InPawn);
SetGenericTeamId(FGenericTeamId(0));
if (const auto PawnTeamInterface{Cast<IGenericTeamAgentInterface>(InPawn)})
{
PawnTeamInterface->SetGenericTeamId(GetGenericTeamId());
}
}
void ACAIController::BeginPlay()
{
Super::BeginPlay();
RunBehaviorTree(BehaviorTree);
}
void ACAIController::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ACAIController::TargetPerceptionUpdated(AActor* TargetActor, FAIStimulus Stimulus)
{
if (Stimulus.WasSuccessfullySensed())
{
if (!GetCurrentTarget()) SetCurrentTarget(TargetActor);
}
else
{
}
}
void ACAIController::TargetForgotten(AActor* ForgottenActor)
{
if (!ForgottenActor) return;
if (GetCurrentTarget() == ForgottenActor)
{
SetCurrentTarget(GetNextPerceivedActor());
}
}
const UObject* ACAIController::GetCurrentTarget() const
{
if (const auto BlackboardComponent{GetBlackboardComponent()})
{
return BlackboardComponent->GetValueAsObject(BlackboardTargetName);
}
return nullptr;
}
void ACAIController::SetCurrentTarget(AActor* NewTarget)
{
const auto BlackboardComponent{GetBlackboardComponent()};
if (!BlackboardComponent) return;
if (NewTarget) BlackboardComponent->SetValueAsObject(BlackboardTargetName, NewTarget);
else BlackboardComponent->ClearValue(BlackboardTargetName);
}
AActor* ACAIController::GetNextPerceivedActor() const
{
if (PerceptionComponent)
{
TArray<AActor*> Actors;
AIPerceptionComponent->GetPerceivedHostileActors(Actors);
if (Actors.Num() != 0) return Actors[0];
}
return nullptr;
}

View File

@@ -11,6 +11,8 @@
#include "GAS/CAttributeSet.h" #include "GAS/CAttributeSet.h"
#include "GAS/UCAbilitySystemStatics.h" #include "GAS/UCAbilitySystemStatics.h"
#include "Kismet/GameplayStatics.h" #include "Kismet/GameplayStatics.h"
#include "Net/UnrealNetwork.h"
#include "Player/CPlayerController.h"
#include "Widgets/OverHeadStatsGauge.h" #include "Widgets/OverHeadStatsGauge.h"
@@ -18,6 +20,7 @@ void ACCharacter::BeginPlay()
{ {
Super::BeginPlay(); Super::BeginPlay();
ConfigureOverHeadStatusWidget(); ConfigureOverHeadStatusWidget();
MeshRelativeTransform = GetMesh()->GetRelativeTransform();
} }
ACCharacter::ACCharacter() ACCharacter::ACCharacter()
@@ -68,6 +71,12 @@ void ACCharacter::PossessedBy(AController* NewController)
} }
} }
void ACCharacter::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ACCharacter, TeamId);
}
UAbilitySystemComponent* ACCharacter::GetAbilitySystemComponent() const UAbilitySystemComponent* ACCharacter::GetAbilitySystemComponent() const
{ {
return CAbilitySystemComponent; return CAbilitySystemComponent;
@@ -145,10 +154,39 @@ void ACCharacter::PlayDeathAnimation()
{ {
if (DeathMontage) if (DeathMontage)
{ {
PlayAnimMontage(DeathMontage); const auto MontageDuration{PlayAnimMontage(DeathMontage)};
GetWorldTimerManager().SetTimer(
DeathMontageTimerHandle,
this,
&ThisClass::DeathMontageFinished,
MontageDuration + DeathMontageFinishTimeShift
);
} }
} }
void ACCharacter::DeathMontageFinished()
{
SetRagdollEnabled(true);
}
void ACCharacter::SetRagdollEnabled(bool bIsEnabled)
{
if (bIsEnabled)
{
GetMesh()->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
GetMesh()->SetSimulatePhysics(true);
GetMesh()->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
}
else
{
GetMesh()->SetSimulatePhysics(false);
GetMesh()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
GetMesh()->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
GetMesh()->SetRelativeTransform(MeshRelativeTransform);
}
}
void ACCharacter::StartDeathSequence() void ACCharacter::StartDeathSequence()
{ {
OnDead(); OnDead();
@@ -162,10 +200,20 @@ void ACCharacter::StartDeathSequence()
void ACCharacter::ReSpawn() void ACCharacter::ReSpawn()
{ {
OnRespawn(); OnRespawn();
SetRagdollEnabled(false);
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
GetCharacterMovement()->SetMovementMode(MOVE_Walking); GetCharacterMovement()->SetMovementMode(MOVE_Walking);
GetMesh()->GetAnimInstance()->StopAllMontages(0.f); GetMesh()->GetAnimInstance()->StopAllMontages(0.f);
SetStatusGaugeEnabled(true); SetStatusGaugeEnabled(true);
if (HasAuthority() && GetController())
{
if (const auto StartSpot { GetController()->StartSpot }; StartSpot.IsValid())
{
SetActorTransform(StartSpot->GetActorTransform());
}
}
if (CAbilitySystemComponent) if (CAbilitySystemComponent)
{ {
CAbilitySystemComponent->ApplyFullStatEffect(); CAbilitySystemComponent->ApplyFullStatEffect();
@@ -179,3 +227,13 @@ void ACCharacter::OnDead()
void ACCharacter::OnRespawn() void ACCharacter::OnRespawn()
{ {
} }
void ACCharacter::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
TeamId = NewTeamID;
}
FGenericTeamId ACCharacter::GetGenericTeamId() const
{
return TeamId;
}

View File

@@ -15,11 +15,14 @@ UAnimInstance* UCGameplayAbility::GetOwnerAnimInstance() const
} }
TArray<FHitResult> UCGameplayAbility::GetHitResultFromSweepLocationTargetData( TArray<FHitResult> UCGameplayAbility::GetHitResultFromSweepLocationTargetData(
const FGameplayAbilityTargetDataHandle& TargetDataHandle, float SphereSweepRadius, bool bDrawDebug, const FGameplayAbilityTargetDataHandle& TargetDataHandle, float SphereSweepRadius, ETeamAttitude::Type TargetTeam,
bool bIgnoreSelf) const bool bDrawDebug, bool bIgnoreSelf) const
{ {
TArray<FHitResult> OutResults; TArray<FHitResult> OutResults;
TSet<AActor*> HitActors; TSet<AActor*> HitActors;
const auto OwnerTeamInterface{Cast<IGenericTeamAgentInterface>(GetAvatarActorFromActorInfo())};
for (const auto TargetData : TargetDataHandle.Data) for (const auto TargetData : TargetDataHandle.Data)
{ {
const FVector StartLoc{TargetData->GetOrigin().GetTranslation()}; const FVector StartLoc{TargetData->GetOrigin().GetTranslation()};
@@ -49,6 +52,12 @@ TArray<FHitResult> UCGameplayAbility::GetHitResultFromSweepLocationTargetData(
for (const auto Result : Results) for (const auto Result : Results)
{ {
if (HitActors.Contains(Result.GetActor())) continue; if (HitActors.Contains(Result.GetActor())) continue;
if (OwnerTeamInterface)
{
const auto OtherActorTeamAttitude{OwnerTeamInterface->GetTeamAttitudeTowards(*Result.GetActor())};
if (OtherActorTeamAttitude != TargetTeam) continue;
}
HitActors.Add(Result.GetActor()); HitActors.Add(Result.GetActor());
OutResults.Add(Result); OutResults.Add(Result);
} }

View File

@@ -152,9 +152,7 @@ void UGA_Combo::DoDamage(FGameplayEventData Data)
auto HitResults{ auto HitResults{
GetHitResultFromSweepLocationTargetData( GetHitResultFromSweepLocationTargetData(
Data.TargetData, Data.TargetData,
TargetSweepSphereRadius, TargetSweepSphereRadius
false,
true
) )
}; };

View File

@@ -2,3 +2,45 @@
#include "Crunch/Public/Game/CGameMode.h" #include "Crunch/Public/Game/CGameMode.h"
#include "EngineUtils.h"
#include "GameFramework/PlayerStart.h"
APlayerController* ACGameMode::SpawnPlayerController(ENetRole InRemoteRole, const FString& Options)
{
const auto NewPlayerController{Super::SpawnPlayerController(InRemoteRole, Options)};
const auto NewPlayerControllerTeamInterface{Cast<IGenericTeamAgentInterface>(NewPlayerController)};
const FGenericTeamId TeamId{GetTeamIDForPlayer(NewPlayerController)};
if (NewPlayerControllerTeamInterface)
{
NewPlayerControllerTeamInterface->SetGenericTeamId(TeamId);
}
NewPlayerController->StartSpot = FindNextStartSpotForTeam(TeamId);
return NewPlayerController;
}
FGenericTeamId ACGameMode::GetTeamIDForPlayer(const APlayerController* PlayerController) const
{
static int PlayerCount{0};
++PlayerCount;
return FGenericTeamId(PlayerCount % 2);
}
AActor* ACGameMode::FindNextStartSpotForTeam(const FGenericTeamId& TeamId) const
{
const auto StartSpotTag{TeamStartSpotTagMap.Find(TeamId)};
if (!StartSpotTag)
{
return nullptr;
}
for (TActorIterator<APlayerStart> It{GetWorld()}; It; ++It)
{
if (It->PlayerStartTag == *StartSpotTag)
{
It->PlayerStartTag = FName("Taken");
return *It;
}
}
return nullptr;
}

View File

@@ -5,14 +5,22 @@
#include "Blueprint/UserWidget.h" #include "Blueprint/UserWidget.h"
#include "Character/CPlayerCharacter.h" #include "Character/CPlayerCharacter.h"
#include "Net/UnrealNetwork.h"
#include "Widgets/GameplayWidget.h" #include "Widgets/GameplayWidget.h"
void ACPlayerController::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ACPlayerController, TeamId);
}
void ACPlayerController::OnPossess(APawn* InPawn) void ACPlayerController::OnPossess(APawn* InPawn)
{ {
Super::OnPossess(InPawn); Super::OnPossess(InPawn);
if (CPlayerCharacter = Cast<ACPlayerCharacter>(InPawn); IsValid(CPlayerCharacter)) if (CPlayerCharacter = Cast<ACPlayerCharacter>(InPawn); IsValid(CPlayerCharacter))
{ {
CPlayerCharacter->ServerSideInit(); CPlayerCharacter->ServerSideInit();
CPlayerCharacter->SetGenericTeamId(TeamId);
} }
} }
@@ -26,6 +34,16 @@ void ACPlayerController::AcknowledgePossession(APawn* P)
} }
} }
FGenericTeamId ACPlayerController::GetGenericTeamId() const
{
return TeamId;
}
void ACPlayerController::SetGenericTeamId(const FGenericTeamId& NewTeamID)
{
TeamId = NewTeamID;
}
void ACPlayerController::SpawnGameplayWidget() void ACPlayerController::SpawnGameplayWidget()
{ {
if (!IsLocalPlayerController()) return; if (!IsLocalPlayerController()) return;

View File

@@ -0,0 +1,46 @@
// Multiplayer By Caleb
#pragma once
#include "CoreMinimal.h"
#include "AIController.h"
#include "Perception/AIPerceptionTypes.h"
#include "CAIController.generated.h"
UCLASS()
class CRUNCH_API ACAIController : public AAIController
{
GENERATED_BODY()
public:
ACAIController();
virtual void OnPossess(APawn* InPawn) override;
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
private:
UPROPERTY(EditDefaultsOnly, Category="AI Behavior")
FName BlackboardTargetName {"Target"};
UFUNCTION()
void TargetPerceptionUpdated(AActor* TargetActor, FAIStimulus Stimulus);
UFUNCTION()
void TargetForgotten(AActor* ForgottenActor);
UPROPERTY(EditDefaultsOnly, Category="AI Behavior")
UBehaviorTree* BehaviorTree;
UPROPERTY(VisibleDefaultsOnly, Category="Perception")
UAIPerceptionComponent* AIPerceptionComponent;
UPROPERTY(VisibleDefaultsOnly, Category="Perception")
class UAISenseConfig_Sight* SightConfig;
UFUNCTION()
const UObject* GetCurrentTarget() const ;
void SetCurrentTarget(AActor* NewTarget);
AActor * GetNextPerceivedActor() const;
};

View File

@@ -5,6 +5,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "AbilitySystemInterface.h" #include "AbilitySystemInterface.h"
#include "GameplayTagContainer.h" #include "GameplayTagContainer.h"
#include "GenericTeamAgentInterface.h"
#include "GameFramework/Character.h" #include "GameFramework/Character.h"
#include "CCharacter.generated.h" #include "CCharacter.generated.h"
@@ -13,7 +14,7 @@ class UCAbilitySystemComponent;
class UCAttributeSet; class UCAttributeSet;
UCLASS() UCLASS()
class CRUNCH_API ACCharacter : public ACharacter, public IAbilitySystemInterface class CRUNCH_API ACCharacter : public ACharacter, public IAbilitySystemInterface, public IGenericTeamAgentInterface
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -27,6 +28,7 @@ public:
bool IsLocallyControlledByPlayer() const; bool IsLocallyControlledByPlayer() const;
// Only called on the server // Only called on the server
virtual void PossessedBy(AController* NewController) override; virtual void PossessedBy(AController* NewController) override;
virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
/********************************************************************************************/ /********************************************************************************************/
/* Gameplay Ability */ /* Gameplay Ability */
@@ -62,13 +64,30 @@ private:
/********************************************************************************************/ /********************************************************************************************/
/* Death & Respawning */ /* Death & Respawning */
/********************************************************************************************/ /********************************************************************************************/
FTransform MeshRelativeTransform;
UPROPERTY(EditDefaultsOnly, Category="Death")
float DeathMontageFinishTimeShift { -0.8f };
UPROPERTY(EditDefaultsOnly, Category="Death") UPROPERTY(EditDefaultsOnly, Category="Death")
TObjectPtr<UAnimMontage> DeathMontage; TObjectPtr<UAnimMontage> DeathMontage;
FTimerHandle DeathMontageTimerHandle;
void DeathMontageFinished();
void SetRagdollEnabled(bool bIsEnabled);
void PlayDeathAnimation(); void PlayDeathAnimation();
void StartDeathSequence(); void StartDeathSequence();
void ReSpawn(); void ReSpawn();
virtual void OnDead(); virtual void OnDead();
virtual void OnRespawn(); virtual void OnRespawn();
public:
/********************************************************************************************/
/* Team */
/********************************************************************************************/
virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
virtual FGenericTeamId GetGenericTeamId() const override;
private:
UPROPERTY(Replicated)
FGenericTeamId TeamId;
}; };

View File

@@ -3,12 +3,10 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GenericTeamAgentInterface.h"
#include "Abilities/GameplayAbility.h" #include "Abilities/GameplayAbility.h"
#include "CGameplayAbility.generated.h" #include "CGameplayAbility.generated.h"
/**
*
*/
UCLASS() UCLASS()
class CRUNCH_API UCGameplayAbility : public UGameplayAbility class CRUNCH_API UCGameplayAbility : public UGameplayAbility
{ {
@@ -17,6 +15,8 @@ class CRUNCH_API UCGameplayAbility : public UGameplayAbility
protected: protected:
UAnimInstance* GetOwnerAnimInstance() const; UAnimInstance* GetOwnerAnimInstance() const;
TArray<FHitResult> GetHitResultFromSweepLocationTargetData(const FGameplayAbilityTargetDataHandle& TargetDataHandle, TArray<FHitResult> GetHitResultFromSweepLocationTargetData(const FGameplayAbilityTargetDataHandle& TargetDataHandle,
float SphereSweepRadius = 30.f, bool bDrawDebug = false, float SphereSweepRadius = 30.f,
ETeamAttitude::Type TargetTeam = ETeamAttitude::Hostile,
bool bDrawDebug = false,
bool bIgnoreSelf = true) const; bool bIgnoreSelf = true) const;
}; };

View File

@@ -3,6 +3,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GenericTeamAgentInterface.h"
#include "GameFramework/GameModeBase.h" #include "GameFramework/GameModeBase.h"
#include "CGameMode.generated.h" #include "CGameMode.generated.h"
@@ -10,4 +11,13 @@ UCLASS()
class CRUNCH_API ACGameMode : public AGameModeBase class CRUNCH_API ACGameMode : public AGameModeBase
{ {
GENERATED_BODY() GENERATED_BODY()
public:
virtual APlayerController* SpawnPlayerController(ENetRole InRemoteRole, const FString& Options) override;
private:
FGenericTeamId GetTeamIDForPlayer(const APlayerController* PlayerController) const;
AActor* FindNextStartSpotForTeam(const FGenericTeamId& TeamId) const;
UPROPERTY(EditDefaultsOnly, Category="Team")
TMap<FGenericTeamId, FName> TeamStartSpotTagMap;
}; };

View File

@@ -4,13 +4,14 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameFramework/PlayerController.h" #include "GameFramework/PlayerController.h"
#include "GenericTeamAgentInterface.h"
#include "CPlayerController.generated.h" #include "CPlayerController.generated.h"
class UGameplayWidget; class UGameplayWidget;
class ACPlayerCharacter; class ACPlayerCharacter;
UCLASS() UCLASS()
class CRUNCH_API ACPlayerController : public APlayerController class CRUNCH_API ACPlayerController : public APlayerController, public IGenericTeamAgentInterface
{ {
GENERATED_BODY() GENERATED_BODY()
@@ -18,13 +19,23 @@ public:
virtual void OnPossess(APawn* InPawn) override; virtual void OnPossess(APawn* InPawn) override;
virtual void AcknowledgePossession(APawn* P) override; virtual void AcknowledgePossession(APawn* P) override;
virtual FGenericTeamId GetGenericTeamId() const override;
virtual void SetGenericTeamId(const FGenericTeamId& NewTeamID) override;
virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
private: private:
void SpawnGameplayWidget(); void SpawnGameplayWidget();
UPROPERTY() UPROPERTY()
TObjectPtr<ACPlayerCharacter> CPlayerCharacter; TObjectPtr<ACPlayerCharacter> CPlayerCharacter;
UPROPERTY(EditDefaultsOnly, Category= "UI") UPROPERTY(EditDefaultsOnly, Category= "UI")
TSubclassOf<UGameplayWidget> GameplayWidgetClass; TSubclassOf<UGameplayWidget> GameplayWidgetClass;
UPROPERTY() UPROPERTY()
TObjectPtr<UGameplayWidget> GameplayWidget; TObjectPtr<UGameplayWidget> GameplayWidget;
UPROPERTY(Replicated)
FGenericTeamId TeamId;
}; };