完成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
+2 -2
View File
@@ -1,9 +1,9 @@
[/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Game/ThirdPerson/L_TestMap.L_TestMap
GameDefaultMap=/Game/ThirdPerson/L_Lobby.L_Lobby
EditorStartupMap=/Game/ThirdPerson/L_TestMap.L_TestMap
GlobalDefaultGameMode=/Game/ThirdPerson/Blueprints/BP_ThirdPersonGameMode.BP_ThirdPersonGameMode_C
GameInstanceClass=/Game/ThirdPerson/Blueprints/BP_GameInstance.BP_GameInstance_C
ServerDefaultMap=/Game/ThirdPerson/L_TestMap.L_TestMap
ServerDefaultMap=/Game/ThirdPerson/L_Lobby.L_Lobby
[/Script/Engine.RendererSettings]
r.ReflectionMethod=1
+110
View File
@@ -1,3 +1,113 @@
[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=BAC1FC37410EC6AF53AC54BD0C2A91AC
ProjectName=Third Person Game Template
[/Script/UnrealEd.ProjectPackagingSettings]
Build=IfProjectHasCode
BuildConfiguration=PPBC_Development
BuildTarget=PvPDemo
FullRebuild=False
ForDistribution=False
IncludeDebugFiles=False
BlueprintNativizationMethod=Disabled
bIncludeNativizedAssetsInProjectGeneration=False
bExcludeMonolithicEngineHeadersInNativizedCode=False
UsePakFile=True
bUseIoStore=True
bUseZenStore=False
bMakeBinaryConfig=False
bGenerateChunks=False
bGenerateNoChunks=False
bChunkHardReferencesOnly=False
bForceOneChunkPerFile=False
MaxChunkSize=0
bBuildHttpChunkInstallData=False
HttpChunkInstallDataDirectory=(Path="")
WriteBackMetadataToAssetRegistry=Disabled
bWritePluginSizeSummaryJsons=False
bCompressed=True
PackageCompressionFormat=Oodle
bForceUseProjectCompressionFormatIgnoreHardwareOverride=False
PackageAdditionalCompressionOptions=
PackageCompressionMethod=Kraken
PackageCompressionLevel_DebugDevelopment=4
PackageCompressionLevel_TestShipping=4
PackageCompressionLevel_Distribution=7
PackageCompressionMinBytesSaved=1024
PackageCompressionMinPercentSaved=5
bPackageCompressionEnableDDC=False
PackageCompressionMinSizeToConsiderDDC=0
HttpChunkInstallDataVersion=
IncludePrerequisites=True
IncludeAppLocalPrerequisites=False
bShareMaterialShaderCode=True
bDeterministicShaderCodeOrder=False
bSharedMaterialNativeLibraries=True
ApplocalPrerequisitesDirectory=(Path="")
IncludeCrashReporter=False
InternationalizationPreset=English
-CulturesToStage=en
+CulturesToStage=en
LocalizationTargetCatchAllChunkId=0
bCookAll=False
bCookMapsOnly=False
bTreatWarningsAsErrorsOnCook=False
bSkipEditorContent=False
bSkipMovies=False
-IniKeyDenylist=KeyStorePassword
-IniKeyDenylist=KeyPassword
-IniKeyDenylist=DebugKeyStorePassword
-IniKeyDenylist=DebugKeyPassword
-IniKeyDenylist=rsa.privateexp
-IniKeyDenylist=rsa.modulus
-IniKeyDenylist=rsa.publicexp
-IniKeyDenylist=aes.key
-IniKeyDenylist=SigningPublicExponent
-IniKeyDenylist=SigningModulus
-IniKeyDenylist=SigningPrivateExponent
-IniKeyDenylist=EncryptionKey
-IniKeyDenylist=DevCenterUsername
-IniKeyDenylist=DevCenterPassword
-IniKeyDenylist=IOSTeamID
-IniKeyDenylist=SigningCertificate
-IniKeyDenylist=MobileProvision
-IniKeyDenylist=AppStoreConnectKeyPath
-IniKeyDenylist=AppStoreConnectIssuerID
-IniKeyDenylist=AppStoreConnectKeyID
-IniKeyDenylist=IniKeyDenylist
-IniKeyDenylist=IniSectionDenylist
+IniKeyDenylist=KeyStorePassword
+IniKeyDenylist=KeyPassword
+IniKeyDenylist=DebugKeyStorePassword
+IniKeyDenylist=DebugKeyPassword
+IniKeyDenylist=rsa.privateexp
+IniKeyDenylist=rsa.modulus
+IniKeyDenylist=rsa.publicexp
+IniKeyDenylist=aes.key
+IniKeyDenylist=SigningPublicExponent
+IniKeyDenylist=SigningModulus
+IniKeyDenylist=SigningPrivateExponent
+IniKeyDenylist=EncryptionKey
+IniKeyDenylist=DevCenterUsername
+IniKeyDenylist=DevCenterPassword
+IniKeyDenylist=IOSTeamID
+IniKeyDenylist=SigningCertificate
+IniKeyDenylist=MobileProvision
+IniKeyDenylist=AppStoreConnectKeyPath
+IniKeyDenylist=AppStoreConnectIssuerID
+IniKeyDenylist=AppStoreConnectKeyID
+IniKeyDenylist=IniKeyDenylist
+IniKeyDenylist=IniSectionDenylist
-IniSectionDenylist=HordeStorageServers
-IniSectionDenylist=StorageServers
-IniSectionDenylist=/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings
+IniSectionDenylist=HordeStorageServers
+IniSectionDenylist=StorageServers
+IniSectionDenylist=/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings
+MapsToCook=(FilePath="/Game/ThirdPerson/L_Lobby")
+MapsToCook=(FilePath="/Game/ThirdPerson/L_LoginMap")
+MapsToCook=(FilePath="/Game/PolygonApocalypse/Maps/Demonstration")
+DirectoriesToAlwaysCook=(Path="/NNEDenoiser")
bRetainStagedDirectory=False
CustomStageCopyHandler=
+1
View File
@@ -15,3 +15,4 @@ NetIndexFirstBitSegment=16
+GameplayTagList=(Tag="Attributes.Vital.Health",DevComment="")
+GameplayTagList=(Tag="Item.Held.Fireball",DevComment="")
+GameplayTagList=(Tag="State.Dead",DevComment="")
+GameplayTagList=(Tag="State.Slowed",DevComment="")
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.
Binary file not shown.
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
# PVPDemo开发日志
### Day1 2026.5.31
#### 任务安排
我认为这个对战方式是以道具驱动的,所以打算先开发道具刷新相关的逻辑
#### 主要开发工作
1. 市场寻找需要用到的美术资产,比如人物模型、特效、场景等
2. 设计道具刷新相关的ItemSpawner以及ItemManager类
### Day2 2026.6.1
#### 任务安排
我认为这个对战方式是以道具驱动的,所以打算先开发道具刷新相关的逻辑
#### 主要开发工作
1. 市场寻找需要用到的美术资产,比如人物模型、特效、场景等
2. 设计道具刷新相关的ItemSpawner以及ItemManager类
+3
View File
@@ -0,0 +1,3 @@
Net PktLag=200
Net PktLagVariance=50N
et PktLoss=2
+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