일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- 게임개발
- Delegate
- 유니티
- gas
- MAC
- Security
- 게임 개발
- Replication
- unity
- DP
- 유스케이스
- gameplay effect
- photon fusion2
- 언리얼 엔진
- stride
- CTF
- Multiplay
- ret2libc
- ability task
- dirty cow
- 운영체제
- animation
- 메카님
- MLFQ
- Race condition
- Unreal Engine
- DSP
- 언리얼엔진
- Rr
- gameplay ability system
Archives
- Today
- Total
Replicated
[ChronoSpace] ** Attribute Replication ** 본문
전 게시글에서 Patrol한테 맞아도, 클라이언트에서 UI에서 동기화가 안되어 보이길래 문제를 찾아봤다
단순 UI 문제라기 보다는, 애초에 Replication 설정을 안해놨다
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "AttributeSet.h"
#include "AbilitySystemComponent.h"
#include "CSAttributeSet.generated.h"
/**
*
*/
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
UCLASS()
class CHRONOSPACE_API UCSAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
UCSAttributeSet();
ATTRIBUTE_ACCESSORS(UCSAttributeSet, Energy);
ATTRIBUTE_ACCESSORS(UCSAttributeSet, MaxEnergy);
ATTRIBUTE_ACCESSORS(UCSAttributeSet, Damage);
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
//virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) override;
virtual bool PreGameplayEffectExecute(struct FGameplayEffectModCallbackData& Data) override;
virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) override;
protected:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
UPROPERTY(BlueprintReadOnly, Category = "Energy", ReplicatedUsing = OnRep_Energy, Meta = (AllowPrivateAccess = true))
FGameplayAttributeData Energy;
UPROPERTY(BlueprintReadOnly, Category = "Energy", ReplicatedUsing = OnRep_MaxEnergy, Meta = (AllowPrivateAccess = true))
FGameplayAttributeData MaxEnergy;
UPROPERTY(BlueprintReadOnly, Category = "Energy", Meta = (AllowPrivateAccess = true))
FGameplayAttributeData Damage;
UFUNCTION()
void OnRep_Energy(const FGameplayAttributeData& OldEnergy);
UFUNCTION()
void OnRep_MaxEnergy(const FGameplayAttributeData& OldMaxEnergy);
};
Energy랑 MaxEnergy를 Replication 되도록 했다
Damage는 애초에 서버에서만 계산할 거고 메타 어트리뷰트니까 굳이 안 해주기도 했고,
게임플레이 이펙트로 계산되니까 알아서 되긴 할 거다
// Fill out your copyright notice in the Description page of Project Settings.
#include "Attribute/CSAttributeSet.h"
#include "ChronoSpace.h"
#include "GameplayEffectExtension.h"
#include "Player/CSPlayerController.h"
//#include "CoreMinimal.h"
#include "Net/UnrealNetwork.h"
UCSAttributeSet::UCSAttributeSet() : MaxEnergy(100.0f), Damage(0.0f)
{
InitEnergy(GetMaxEnergy());
}
void UCSAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
if (Attribute == GetDamageAttribute())
{
NewValue = NewValue < 0.0f ? 0.0f : NewValue;
}
// -> 최소 데미지 0.0f 으로 설정.
}
bool UCSAttributeSet::PreGameplayEffectExecute(FGameplayEffectModCallbackData& Data)
{
return true;
}
void UCSAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
float MinimumEnergy = 0.0f;
if (Data.EvaluatedData.Attribute == GetEnergyAttribute())
{
UE_LOG(LogTemp, Warning, TEXT("Direct Health Access : %f"), GetEnergy());
SetEnergy(FMath::Clamp(GetEnergy(), MinimumEnergy, GetMaxEnergy()));
}
if ( Data.EvaluatedData.Attribute == GetDamageAttribute() )
{
SetEnergy(FMath::Clamp(GetEnergy() - GetDamage(), MinimumEnergy, GetMaxEnergy()));
UE_LOG(LogCS, Log, TEXT("[NetMode : %d] Damage Detected : %f | Now Energy : %f"), GetWorld()->GetNetMode(), GetDamage(), GetEnergy());
AActor* TargetActor = Data.Target.GetAvatarActor();
if (TargetActor == nullptr) return;
if (APawn* Pawn = Cast<APawn>(TargetActor))
{
ACSPlayerController* PC = Cast<ACSPlayerController>(Pawn->GetController());
if (PC)
{
PC->ShakeCamera();
}
}
}
}
void UCSAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UCSAttributeSet, Energy, COND_None, REPNOTIFY_Always);
DOREPLIFETIME_CONDITION_NOTIFY(UCSAttributeSet, MaxEnergy, COND_None, REPNOTIFY_Always);
}
void UCSAttributeSet::OnRep_Energy(const FGameplayAttributeData& OldEnergy)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCSAttributeSet, Energy, OldEnergy);
}
void UCSAttributeSet::OnRep_MaxEnergy(const FGameplayAttributeData& OldMaxEnergy)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UCSAttributeSet, MaxEnergy, OldMaxEnergy);
}
재밌는 점은
이걸 해줘야 GAS 시스템이 알아서 처리해주는게 좀 있는 것 같아
클라이언트 UI 업데이트가 이걸 해줘야 작동하던데 아마
저 델리게이트 실행시키는 로직이 위에 REPNOTOFY에 포함되어 있지 않나 싶다
로그 찍어보면 맞는 것 같긴 하다
동기화 잘 되는 걸 확인 가능하다
'언리얼 엔진 > ChronoSpace' 카테고리의 다른 글
[ChronoSpace] Interaction 구현 (0) | 2025.02.19 |
---|---|
[ChronoSpace] Patrol 배치, Clockwork Labyrinth 프로토타입 완성 (0) | 2025.02.17 |
[ChronoSpace] Behavior Tree + Ability & Effect + Camera Shake (0) | 2025.02.17 |
[ChronoSpace] NavMesh와 Behavior Tree를 이용한 AI NPC (Clockwork Labyrinth) (3) | 2025.02.16 |
[ChronoSpace] Key 흩뿌리기 (Clorkwork Labyrinth) (0) | 2025.02.15 |