[GAS/sec8] RPG Attribute #2
8. RPG Attribute
8.8. Player Level & Combat Interface
Player Level
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// AuraPlayerState.h
public:
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
private:
UFUNCTION()
void OnRep_Level(int32 OldLevel);
UPROPERTY(VisibleAnywhere, ReplicatedUsing=OnRep_Level)
int32 Level = 1;
// AuraPlayerState.cpp
void AAuraPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AAuraPlayerState, Level);
}
-
Level
변수를 Replicate하기 위해GetLifetimeReplicatedProps
함수 재정의 -
Level이 변경될 때마다
OnRep_Level
함수 호출(notify)-
OldLevel
변수를 통해 이전 값과 현재 값 비교 -
UFUNCTION()
선언이 있어야 notify 역할 수행 가능
-
-
ReplicatedUsing
: Replicated된 변수가 서버에서 변경되면 클라이언트에서 바인딩 된 함수가 자동으로 실행- eg) 서버에서 복제된
Level
변경 시 클라이언트에서OldRep_Level
함수가 실행됨
- eg) 서버에서 복제된
-
DOREPLIFETIME(클래스명, 복제할 변수)
: 복제된 항목을 표시하는 가장 기본적인 매크로
Combat Interface
전투를 전용으로 다루는 인터페이스를 만들어 플레이어 캐릭터와 적 캐릭터를 관리할 수 있다. AAuraCharacterBase
클래스의 부모 클래스로 CombatInterface
클래스를 추가한다.
AuraCharacter
의 Level은 AuraPlayerState
클래스에 지역 변수로 있으므로 캐릭터 클래스의 구현부에서 getter로 받아 Level을 리턴한다.
1
2
3
4
5
6
7
8
9
10
11
// AuraPlayerState.h
FORCEINLINE int32 GetPlayerLevel() const { return Level; };
// AuraCharacter.cpp
int32 AAuraCharacter::GetPlayerLevel()
{
const AAuraPlayerState* AuraPlayerState = GetPlayerState<AAuraPlayerState>();
check(AuraPlayerState);
return AuraPlayerState->GetPlayerLevel();
}
-
인라인 함수에
FORCEINLINE
매크로를 붙여 최적화할 수 있다.GetPlayerLevel
함수를 호출할 때마다 컴파일 이전 전처리기가 대체 (컴파일 속도 향상)
💡주의!
캐릭터 클래스의 부모 클래스로 인터페이스를 추가하면 기존에 사용하던 TObjectPtr<IEnemyInterface>
에 오류가 발생한다.
TObjectPtr
은 가리키는 오브젝트가 유효한지 검사하기 위해 하위 클래스의 모든 멤버 변수와 함수를 관리하는데, AuraEnemy
가 상속한 ICombatInterface
에 접근할 수 없기 때문!
이렇게 오류가 발생할 경우에는 TObjectPtr
대신 원시 포인터를 사용해 원하는 클래스에 바로 접근하자.
1
2
3
4
// AuraPlayerController.h
private:
IEnemyInterface* LastActor; /** 원시포인터로 변경 */
IEnemyInterface* ThisActor;
8.9. Modifier Magnitude Calculations
attributes를 ovrride나 add 대신 직접 커스텀하고 싶다면 MMC 클래스를 상속받아 코드로 작성할 수 있다.
1
2
3
4
5
// UMMC_MaxHealth.h
public:
UMMC_MaxHealth();
virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
FGameplayEffectSpec
에 접근->Attribute 내부의 모든 정보에 액세스가 가능함!
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
// UAuraAttributeSet.h
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_Vigor, Category = "Primary Attributes")
FGameplayAttributeData Vigor;
ATTRIBUTE_ACCESSORS(UAuraAttributeSet, Vigor);
// UMMC_MaxHealth.cpp
UMMC_MaxHealth::UMMC_MaxHealth()
{
/** 캡쳐할 속성 지정 */
VigorDef.AttributeToCapture = UAuraAttributeSet::GetVigorAttribute();
/** AuraCharacter에게 Vigor 캡처 */
VigorDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target;
VigorDef.bSnapshot = false;
// 캡처할 속성은 RelevantAttributesToCapture에 추가해야 함(TArray 형식)
RelevantAttributesToCapture.Add(VigorDef);
}
-
AuraAttributeSet
클래스에서Vigor
속성에 매크로 작성-
GAMEPLAYATTRIBUTE_PROPERTY_GETTER
매크로를 통해 별도의 함수 구현 없이 다른 클래스에서 getter 사용 가능 -
getter로 불러온 함수는 정적 함수이므로 별도의 포인터가 필요하지 않음
-
-
Vigor를 어디로부터 캡처할지 Source/Target 중 선택해야 함
-
bSnapshot
: 속성을 캡처할 타이밍. 효과가 적용되는 시점에서 속성 캡처-
Vigor
는 GE가 생성되는 즉시 동일한 함수에서 GameplayEffectSpec이 적용되므로 관련x -
cf) GESpec을 생성하고 공기 중으로 날아가는 불덩이에 설정할 경우, 별도의 타이밍 지정이 필요함
-
-
RelevantAttributesToCapture
: 캡처할 속성을TArray
에 추가해야 함- 생성자에서 추가해 구현부에서 해당 배열에 접근해 원하는 속성 연산
Leave a comment