GAS에서 사용되는 클래스들에 대해 어느정도는 알아보았으니, 이제 직접적으로 사용되는 곳을 보면서 분석해보려고 한다.

 

예제 프로젝트에 보면 아래와 같은 박스들이 있다.

 

왼쪽 박스는 충돌시 Health를 감소시키고, 중간 박스는 Health, Mana,Stamina를 회복시켜준다.

오른쪽 박스는, Health, Mana, Stamina의 Regen량을 올려서 간접적으로 회복시켜준다.

 

이 오브젝트에 구현된 기능을 역추적하면서 어떤식으로 GAS가 작동되는지 알아보는 것이 목표이다.

중간중간에 처음보는 구조체나 클래스가 나온다면 알아가면서 분석해보도록 하자.

 

왼쪽 박스의 이벤트 그래프를 보자.

BeginPlay에서 1.0초마다 특정 이벤트를 반복하는 Timer를 생성하였다.

타이머에는 DamageTick이라는 이벤트가 바인딩되어 있다.

 

DamageTick 구현부를 보자.

 

먼저, 해당 이벤트가 서버에서 실행되고 있는지 클라이언트에서 실행되고 있는지 Switch Has authority 를 통해 확인한 뒤 분기를 나누고 있다. Hp를 변화시키는 로직이기 때문에 해당 이벤트는 클라이언트에서는 실행되지 않고 서버에서만 실행되도록 되어있다.

 

현재 Overlap중인 액터 목록을 받아와서, GDCharacterBase로 타입캐스팅이 가능한 액터에 대해서만 해당 이벤트를 실행하도록 하고있다.

 

 

이후, isAlive함수와 ASC의 isValid가 모두 true일 때만 이벤트가 실행되도록 조건이 세팅되어 있다.

isAlive함수는 아래와 같다.

 

 

즉, 데미지를 입히고자 하는 대상이 살아있으면서 ASC가 유효할 때에만 이벤트를 실행하겠다는 것이다.

아래는 실직적으로 데미지를 입히는 로직이다.

 

먼저, 이쪽을 자세히 분석해보자.

 

MakeOutGoing 함수를 찾아보니, GameplayEffectSpecHandle이라는 구조체를 만들어서 반환해주는 함수라고 한다.

그럼, GameplayEffectSpec에 대해 먼저 알아봐야 할 듯 하다.

 

예제 프로젝트의 깃허브에선 아래와 같이 설명해주고 있다.

 

 

 

더보기

GameplayEffectSpec(GESpec)는 GameplayEffect가 인스턴스화된 것이라고 생각할 수 있다. GESpec는 이것이 만들어진 레벨이나 누가 이것을 만들었는지 등, GameplayEffect가 가지고 있는 정보의 참조값을 보유하게 된다. GESpec는 런타임 전에 디자이너가 미리 만들어두어야 했던 GameplayEffect와 다르게 런타임에서 자유롭게 만들고 수정할 수 있다. GameplayEffect를 적용하고자 할 때, GESpec이 GameplayEffectSpec에 의해 생성되고, GESpec이 실직적으로 대상에게 효과를 적용하게 된다. 

 

GESpec은 BlueprintCallable인 ACS의 MakeOutgoingSpec함수를 사용하여 GameplayEffect로부터 생성된다. GESpec은 즉시 적용될 필요는 없다. 발사체를 만들고, 이 발사체가 나중에 대상에게 적중했을 때 적용하는 것이 일반적이다. GameplayEffectSpec이 성공적으로 적중했을 때, 그들은 FActiveGameplayEffect라는 구조체를 반환해준다. 

GameplayEffectSpec이란, 정적으로 생성되어 있는 GameplayEffect의 정보를 참조하여 런타임에 동적으로 정보를 변환하며 상호작용할 수 있도록 만들어진 구조체인 것 같다. Effect를 적용하고자 하는 대상의 정보같은 것은 런타임 이전에 미리 정해놓을 수 없기 때문에, GameplayEffectSpec이라는 구조체를 활용하는 것 같다.

 

GameplayEffectSpec은 총알과 같은 발사체가 적과 충돌했을 때 Effect를 적용하도록 하는 것이 가능하다는 것 같다.  FActiveGameplayEffect는 아마 HitResult와 비슷한 역할이 아닐까 추측해본다.

 

위의 블루프린트를 보면, Make OutGoing Spec의 파라미터에 4가지의 인자가 있다. 하나씩 보자.

Target은 대상의 ASC이다. Attribute를 변경하려면 ASC가 필요하기 때문에 ASC를 인자로 받는 것 같다.

 

GameplayEffectClass는 GESpec을 만들기 위한 토대가 되는 GameplayEffect이다. Damage에 관한 Effect를 적용하려면 당연히 Damage에 관해서 미리 만들어두었던 GameplayEffect를 전달해야 할 것이다. 그래서, 해당 이벤트 그래프에선 Damage Gameplay Effect라는 GameplayEffect를 인자로 전달하고 있다.

 

Level은 잘 모르겠다. GESpec 내부에 있는 level을 초기화해주는 값이라는 것은 확실한데, GESpec 내부의 Level이 무슨 역할인지 찾아봐도 잘 나오지가 않는다.

 

예제 프로젝트의 깃허브에는 위와 같이 설명되어 있다. 일반적으로 GESpec을 만든 능력의 레벨과 동일하지만, 다를 수도 있다고 적혀있다. 아마, 스킬 레벨에 따라 데미지가 다르게 들어가는 등의 기능을 구현하는데 활용하는 것이 아닐까 추측은 해보지만 아직은 잘 모르겠다. 이 부분은 조금 더 찾아보아야 할 듯 하다.

 

Context는 FGameplayEffectContextHandle 이라는 구조체이다. 이름에서 유추할 수 있듯이, GameplayEffectContext라는 구조체를 사용할 수 있도록 도와주는 구조체이다. GameplayEffectContext에는 gameplayEffect를 만든 주체(공격자)의 정보 등이 담겨있다. 또한, 상황에 따라 피격자의 정보도 담을 수 있는 듯 하다.

 

Make Outgoing Spec함수는 이렇게 4가지 인자를 사용하여, GameplayEffectSpec을 생성하고 GameplayEffectSpec을 활용할 수 있도록 GameplayEffectSpecHandle이라는 구조체에 GameplayEffectSpec을 담아서 반환해주게 된다.

 

이벤트 그래프에선 그렇게 만들어진 GameplayEffectSpecHandle을 통해, 다시 아래와 같은 함수를 실행하고 있다.

 

이 함수는 인자로 3가지를 받고 있는데, Spec Handle은 이전에 생성한 GameplayEffectSpecHandle 구조체이고

DataTag는 해당 Effect를 적용할 때 대상에게 적용할 Tag인 것 같다.

 

현재는 데미지를 주는 Effect이기 때문에, Data.Damage 태그를 께 적용하여, 대상이 피격시에 해야하는 행동을 실행해주도록 하고 있다. (피격 애니메이션 등..)

 

Magnitude는 적용할 기본 수치이다. Damage에 대상에게 가하는 데미지를 전달하면 GameplayEffect에 정의해놓은 계산식에 의해 대상이 실제로 입게될 Damage를 계산하고, 그 값을 기반으로 대상의 Attribute에 영향을 주는 것 같다.

 

 

위의 그림은 예제 프로젝트에서 Damage를 위해 만들어놓은 GameplayEffect이다.

보면, GDDamageExecCalculation이라는 클래스를 Calculation Class로 연결해두었다.

 

아래 코드는 GDDamageExecCalculation클래스의 내부 함수의 코드 일부이다.

 

방어력을 이용하여, Damage를 계산하고있다. 이 계산식 자체를 정확히 이해할 필요는 없고 여기서 Damage의 계산식을 정의하고 있다는 것만 알면 될 듯 하다.

 

아무튼 Assign Tag Set By Caller Magnitude 함수는 상수를 전달하면 이런식으로 정의된 계산식을 통해, 실제로 적용될 값을 계산해준다. 이렇게 계산된 값과 대상에게 적용할 GameplayTag를 GameplayEffectSpecHandle에 저장한 뒤에, GESpecHandle을 다시 반환해준다.

 

그 이후, 이 Handle을 사용하여 대상의 ASC에 적용하면 된다.

 

 

예제 프로젝트에선 이렇게 스스로에게 GameplayEffectSpec을 적용해주고 있다.

 

이렇게 분석하면서 알게된 것인데, 예제 프로젝트에서는 Damage 박스가 플레이어를 공격하는 방식이 아니고, 박스 안에 들어가면 플레이어 스스로가 스스로를 공격하는 방식으로 구현되어 있다. 이 점을 이해하고서 코드를 다시 보니까 어떤 방식으로 GAS가 작동하는지 더 이해가 잘 되었다.

 

일단, 이렇게 GameplayEffect가 직접 적용되는 부분을 찾아가서 역추적하며 과정을 살펴보니 확실히 어떻게 상호작용이 이루어지는지 쉽게 이해할 수 있었다. 이제 조금 더 구체적인 부분들을 살펴본 뒤에, 직접 적용해보며 GAS를 체화 해보아야 할 듯 하다.

 

+ Recent posts