RIT-B03 | RiteSeekers — Job/Class가 Ability로 연결되는 흐름
B02에서는 Experience와 PawnData를 따라가면서 Player가 Ability와 Input을 사용할 수 있는 상태까지 어떻게 준비되는지 확인했습니다.
그다음으로 궁금했던 것은 직업이 바뀌었을 때였습니다.
처음에는 직업 시스템을 단순하게 생각했습니다.
Text
Fighter
→ Fighter Skill
Wizard
→ Wizard Skill선택한 직업에 맞는 Skill을 실행하면 된다고 생각했습니다.
그런데 Source를 따라가 보니 직업 선택과 실제 Gameplay Ability 사이에 몇 단계가 더 있었습니다.
Text
Class Select
→ ClassData
→ ClassAbilitySet
→ ASC
→ AbilitySpec
→ InputTag
→ Gameplay Ability직업 하나를 선택하는 것이 단순히 Fighter, Wizard 같은 값을 바꾸는 일이 아니라 현재 Player가 가지고 있을 Ability 구성을 바꾸는 시작점으로 이어지고 있었습니다.
직업을 선택하면 ClassData를 찾는다
화면에서는 직업 버튼 하나를 선택하는 기능처럼 보입니다.
Source에서는 선택한 직업을 기준으로 먼저 ClassData에서 해당 구성을 찾습니다.
Text
Class Select
→ ClassData Lookup
→ ClassInfoEntry
→ ClassAbilitySet
→ DefaultItemEntriesClassInfoEntry에는 해당 직업에서 사용할 AbilitySet과 기본 Item 구성이 연결되어 있습니다.
처음에는 직업마다 Character C++ Class를 하나씩 두는 식으로 생각하기 쉬웠습니다.
현재 RiteSeekers에서는 직업을 어떤 Gameplay 구성을 사용할지 정하는 Data 쪽에 더 가깝게 보고 있습니다.
ClassData에 Ability와 기본 Item 구성이 같이 있었다
RiteSeekers에는 ClassData_RiteCombatGame이 있고, 직업별 설정이 ClassInfoEntry로 나뉘어 있습니다.
제가 먼저 본 것은 두 가지였습니다.
Text
ClassAbilitySet
→ 해당 직업의 Ability 구성
DefaultItemEntries
→ 해당 직업의 기본 Item / Equipment 구성여기서 ClassData가 직접 Gameplay Ability를 실행하거나 Item을 Equip하는 것은 아닙니다.
Text
ClassData
→ 어떤 구성을 사용할지 제공
Ability / Item System
→ 실제 Runtime State 구성ClassData에서는 필요한 정보를 넘겨주고, 실제 Runtime 상태는 이후 시스템에서 만들어집니다.

📸 Screenshot 1. ClassData_RiteCombatGame의 대표 ClassInfoEntry에서 ClassAbilitySet과 DefaultItemEntries가 연결된 설정.
이 Screenshot은 여러 직업을 한꺼번에 보여주기보다 직업 하나에서 **ClassAbilitySet**과 DefaultItemEntries****가 같이 보이는 화면이면 충분합니다.
AbilitySet과 AbilitySpec은 같은 것이 아니었다
ClassData에서 사용할 ClassAbilitySet을 찾은 뒤에는 ASC로 이어집니다.
Text
ClassData
→ ClassAbilitySet
→ ASC
→ AbilitySpec여기서 처음 헷갈렸던 것이 AbilitySet과 AbilitySpec이었습니다.
제가 지금 구분해서 보는 기준은 단순합니다.
Text
AbilitySet
= 어떤 Ability를 줄 것인지 구성한 데이터
AbilitySpec
= 해당 Ability가 특정 ASC에 실제로 들어간 Runtime 상태Ability Class가 프로젝트에 존재하고 ClassData에 AbilitySet이 연결되어 있어도, 그것만으로 현재 Player가 그 Ability를 가지고 있다고 볼 수는 없습니다.
AbilitySet이 실제 ASC에 Grant되고 FGameplayAbilitySpec이 만들어져야 Runtime의 Ability 상태가 생깁니다.

AbilitySet_Swordmaster ScreenShot.

AbilitySet_Wizard ScreenShot.
📸 Screenshot 2. 대표 직업의 ClassAbilitySet에서 해당 Class에 사용할 Gameplay Ability와 InputTag가 구성된 설정.
직업이 바뀌면 이전 Ability도 같이 빠져야 했다
처음에는 새로운 직업을 선택하면 그 직업의 Ability를 추가하면 된다고 생각하기 쉽습니다.
하지만 이미 다른 직업 Ability가 들어 있는 상태에서 직업이 바뀐다면 이전 Ability도 같이 정리되어야 합니다.
현재 Source에서는 다음 흐름을 확인했습니다.
Text
Old Class AbilitySet
→ GrantedHandles
→ TakeFromAbilitySystem
New ClassAbilitySet
→ GiveToAbilitySystem
→ New AbilitySpec즉 직업 변경은:
Text
새 Ability 추가만 일어나는 것이 아니라:
Text
이전 Ability 구성 제거
→ 새로운 Ability 구성으로 이어집니다.
이 부분을 따라가면서 Ability를 Character에 고정된 기능처럼 보기보다 현재 Gameplay 상태에 따라 ASC에 들어오고 빠질 수 있는 Runtime 구성으로 보게 됐습니다.
여기서 실제 RiteSeekers Source를 같이 확인할 수 있습니다.

🔡 SourceCode.
ALyraPlayerState::Server_SelectClass_Implementation에서ClassInfoEntry를 조회한 뒤 기존 Class AbilitySet의 GrantedHandles를 회수하고 새로운ClassAbilitySet을 ASC에 Grant하는 흐름.
현재 Source에서는 다음 순서가 한 함수 안에서 이어집니다.
Text
ALyraPlayerState::Server_SelectClass_Implementation
→ URSClassData::Get().GetClassInfoEntry(CharacterClassType)
→ ClassEntry.DefaultItemEntries
→ AbilitySetGrantedHandles.TakeFromAbilitySystem(...)
→ ClassEntry.ClassAbilitySet
→ AbilitySet->GiveToAbilitySystem(...)실제 Current Source에서 FRSClassInfoEntry를 조회한 뒤 Equipment 초기화 경로를 지나 기존 GrantedHandles를 Take하고 새 ClassAbilitySet을 Give하는 코드가 확인됩니다.
이 SourceCode에서는 Take와 Give가 같이 있다는 점을 가장 중요하게 봤습니다.
Text
새 Ability만 계속 추가
X
Old GrantedHandles 정리
→ New ClassAbilitySet 적용
O즉 Class 변경 시 ASC 전체를 비우는 것이 아니라, 이전 Class가 부여했던 Handle을 회수하고 새 Class의 AbilitySet으로 교체하는 흐름입니다. 기존 Audit에서도 Old Take와 New Give가 각각 LyraPlayerState.cpp:301, 302~304의 Source Anchor로 확인되어 있습니다.
다만 이 Source Screenshot 하나만으로 직업 변경 전후의 실제 ASC AbilitySpec 결과까지 증명한다고 보지는 않습니다.
Source는 Take / Give를 수행하는 구조를 보여주고, 실제 Runtime Spec 변화는 별도의 Runtime State 확인 영역입니다.
AbilitySet의 InputTag도 AbilitySpec으로 들어갔다
Class AbilitySet에는 어떤 Gameplay Ability를 줄지뿐 아니라 해당 Ability를 어떤 InputTag와 연결할지도 설정할 수 있습니다.
Lyra 계열 AbilitySet Source를 따라가면 이 값도 실제 Runtime Spec으로 넘어갑니다.
Text
AbilitySet Entry
→ GameplayAbility Class
→ InputTag
→ FGameplayAbilitySpec
→ DynamicAbilityTags
→ ASC GiveAbilityAbilitySet에 설정된 InputTag가 AbilitySpec의 DynamicAbilityTags에 들어가고, 이후 Player Input에서 들어온 InputTag와 연결될 수 있습니다.
그래서 Class Ability를 볼 때도:
Text
어떤 Ability가 들어가는가만 확인하지 않고:
Text
그 Ability가 어떤 InputTag를 가지고 들어가는가를 같이 봤습니다.
Ability가 ASC에 존재하더라도 Input 쪽에서 찾을 Tag가 기대와 다르면 실제 Skill 실행까지 이어지지 않을 수 있기 때문입니다.
직업을 선택하는 시점과 Skill을 쓰는 시점은 달랐다
전체 흐름만 보면:
Text
Class Select
→ AbilitySet
→ Input
→ Skill이 한 번에 일어나는 것처럼 보일 수 있습니다.
실제로는 직업을 선택하는 순간과 나중에 Player가 Skill 버튼을 누르는 순간이 나뉘어 있습니다.
직업을 선택할 때는 먼저 현재 Ability 구성이 바뀝니다.
Text
Class Select
→ ClassData Lookup
→ Old AbilitySet Remove
→ New AbilitySet Grant
→ AbilitySpec 생성그 뒤 실제 플레이에서 Skill Input이 들어오면:
Text
InputAction
→ InputTag
→ ASC ProcessAbilityInput
→ AbilitySpec Match
→ Gameplay Ability Activation으로 이어집니다.
두 시점 사이에 ASC의 AbilitySpec이 Runtime 상태로 남아 있습니다.
이걸 이해하고 나니 직업을 바꿨는데 Skill이 실행되지 않는 상황에서도 처음부터 Skill 내부 코드를 열기보다 새로운 직업 Ability가 ASC에 실제로 들어왔는지를 먼저 볼 수 있었습니다.
AbilitySpec 이후 Input과 Activation이 이어지는 과정은 B06에서 Runtime 값으로 다시 확인했습니다.
같은 버튼도 현재 Ability 구성에 따라 다르게 사용할 수 있다
Ability와 Input이 분리돼 있다는 걸 이해할 때 이런 예시가 도움이 됐습니다.
구조적으로는 같은 InputTag를 사용하더라도 ASC에 어떤 AbilitySpec이 들어 있는지에 따라 다른 Ability와 연결할 수 있습니다.
Text
Fighter
→ InputTag.Skill.Primary
→ Fighter Skill
Wizard
→ InputTag.Skill.Primary
→ Wizard SkillPlayer 입장에서는 같은 Skill 버튼을 누르더라도 현재 직업에 따라 서로 다른 Ability를 실행하도록 구성할 수 있다는 뜻입니다.
다만 이것은 AbilitySet / AbilitySpec / InputTag 관계를 설명하기 위해 든 예시입니다.
현재 RiteSeekers의 모든 직업이 실제로 하나의 InputTag.Skill.Primary를 공유하면서 서로 다른 Skill을 사용하고 있다는 뜻은 아닙니다.
실제 연결은 각 Class AbilitySet의 설정을 기준으로 봅니다.
DefaultItemEntries는 시작 장비 쪽으로도 이어졌다
ClassData에는 ClassAbilitySet 외에 DefaultItemEntries도 있습니다.
그래서 직업을 고르는 것은 Ability 구성뿐 아니라 시작 Item / Equipment 구성과도 이어질 수 있습니다.
Text
ClassData
├─ ClassAbilitySet
│ → Ability 구성
│
└─ DefaultItemEntries
→ Item / Equipment 구성예를 들어 직업마다 다른 시작 Weapon이나 기본 장비를 준다면 DefaultItemEntries가 그 데이터를 제공하는 시작점이 될 수 있습니다.
하지만 ClassData에서 직접 ItemInstance를 만들고 장착까지 끝내는 것은 아닙니다.
Text
ClassData
→ 사용할 기본 Item 정보 제공
Item / Equipment System
→ 실제 Runtime Item / Equipment State 구성ClassData는 어떤 Item을 사용할지 알려주고, 실제 Item과 Equipment 상태는 이후 시스템에서 만들어집니다.
이 부분은 B05에서 ItemData → ItemTemplate → ItemInstance → Equipment까지 따로 따라갔습니다.
UI에서 직업이 바뀐 것과 ASC가 바뀐 것은 따로 봤다
직업 선택 UI에서 Fighter가 Wizard로 바뀌었다고 해도 Gameplay Ability까지 같이 바뀌었다고 바로 판단하지는 않습니다.
Runtime에서는 다음 흐름이 실제로 이어져야 합니다.
Text
Class Select
→ ClassInfoEntry
→ Old AbilitySet Take
→ New ClassAbilitySet Give
→ ASC AbilitySpec화면에서는 Wizard로 바뀌었는데 새로운 Skill이 실행되지 않는다면 바로 GameplayAbility 안쪽부터 볼 필요는 없습니다.
먼저 새로운 ClassAbilitySet이 선택됐는지, 이전 Ability가 ASC에서 빠졌는지, 새로운 AbilitySpec이 만들어졌는지를 확인할 수 있습니다.
AbilitySpec까지 기대한 상태라면 그다음에 InputTag와 Activation 쪽으로 내려가면 됩니다.
이렇게 보고 나서 UI에서 보이는 Class State와 ASC가 가지고 있는 Ability State도 따로 확인하게 됐습니다.
Source에서 다시 찾아간 위치
직업 선택부터 Ability Runtime까지 다시 따라갈 때 제가 주로 본 위치는 다음 정도였습니다.
Text
Class Select
→ Server_SelectClass 계열
ClassData
→ ClassInfoEntry
→ ClassAbilitySet / DefaultItemEntries
Ability Runtime
→ TakeFromAbilitySystem
→ GiveToAbilitySystem
→ AbilitySpec / DynamicAbilityTags현재 Source에서는 Class 선택 처리에서 ClassInfoEntry를 찾고, 기존 AbilitySet의 Handle을 회수한 뒤 새로운 ClassAbilitySet을 ASC에 Grant하는 흐름까지 확인했습니다.
실제 Source Anchor는 다음과 같습니다.
Text
Source/RSGame/Player/LyraPlayerState.cpp
279
→ ALyraPlayerState::Server_SelectClass_Implementation
288
→ URSClassData::Get().GetClassInfoEntry(CharacterClassType)
294
→ ClassEntry.DefaultItemEntries 순회
301
→ AbilitySetGrantedHandles.TakeFromAbilitySystem(...)
302
→ ClassEntry.ClassAbilitySet
304
→ AbilitySet->GiveToAbilitySystem(...)Input 쪽은 B02에서 PostProcessInput → ProcessAbilityInput을 한 번 따라갔기 때문에 여기서는 Class 선택 이후 ASC의 Ability 구성이 어떻게 바뀌는지까지만 연결해서 봤습니다.
프로젝트를 진행하며 달라진 부분
처음에는 직업 시스템을:
Text
직업을 선택한다
→ 그 직업의 Skill을 준다정도로 생각했습니다.
지금은 그 사이의 상태를 조금 더 나눠서 봅니다.
Text
Class Select
→ 사용할 직업 데이터 결정
ClassData
→ AbilitySet / 기본 Item 구성 제공
AbilitySet
→ ASC에 넣을 Ability 구성
ASC
→ 실제 AbilitySpec 관리
InputTag
→ Player Input과 Ability 연결
GameplayAbility
→ 실제 Skill 실행특히 직업이 변경될 때는 새로운 Ability가 들어오는 것만 보지 않고 이전 직업의 Ability가 같이 빠졌는지도 확인해야 했습니다.
이 흐름을 따라간 뒤부터 직업 선택을 단순한 UI 기능이라기보다 Ability, Input, Item으로 이어지는 Gameplay 구성의 변경으로 보게 됐습니다.
정리
RiteSeekers에서 Job / Class 흐름을 따라가며 가장 크게 달라진 것은 Ability를 보는 방식이었습니다.
전체 흐름은 다음과 같습니다.
Text
Class Select
→ ClassData
→ ClassAbilitySet
→ ASC
→ AbilitySpec
→ InputTag
→ Gameplay Ability직업이 바뀔 때는:
Text
Old AbilitySet
→ Take
New AbilitySet
→ Give가 같이 움직입니다.
그리고 실제 Skill Input이 들어오는 시점에는 이미 ASC에 들어가 있는 AbilitySpec과 InputTag가 다시 연결됩니다.
처음에는 “직업마다 다른 Skill이 있다”는 최종 결과만 봤습니다.
지금은 직업 선택이 어떤 Ability 구성을 만들고, 그 상태가 실제 Player Input까지 어떻게 이어지는지를 같이 보게 됐습니다.