Analytics 시스템 전체 구조와 동작 원리 정리
1. 핵심 아키텍쳐: 이중 데이터 구조
시스템의 근간은 두 종류의 데이터를 효율적으로 관리하기 위한 이중 구조에 있음. 각 데이터 유형의 본질적인 특성을 고려한 설계로, 하나는 연속적인 수치를, 다른 하나는 명확한 상태(On/Off)를 다루는 데 최적화됨.
- 수치 데이터 (Numeric Data): Dictionary<string, float> 자료구조를 통해 관리됨. "jump_count", "playtime"과 같이 지속적으로 누적되거나 변화하는 값을 저장하는 데 사용됨. Key-Value 쌍을 이용하므로 특정 데이터에 O(1) 시간 복잡도로 접근할 수 있어 조회가 빠르고, 새로운 추적 항목을 코드 수정 없이 동적으로 추가할 수 있는 유연성을 확보함.
- 상태 데이터 (Flag Data): Bit Flag 연산을 기반으로 한 정수(int) 타입으로 관리됨. 이는 AnalyticsFlags.UsedChargeJump와 같이 플레이어의 특정 경험 여부, 행동 달성 여부 등 명확한 참/거짓 상태를 표현하는 데 사용됨. 각 상태를 비트의 자리(1 << n)에 할당하므로 극도로 적은 메모리만으로 수많은 상태를 동시에 저장하고, 비트 연산자(&, |, ~)를 통해 여러 조건을 단 한 번의 연산으로 빠르게 검사할 수 있는 성능상의 이점을 가짐
2. 통합 조건 시스템 Condition Parser
기획자가 코드 수정 없이 복잡한 논리 조건을 문자열 형태로 직접 정의할 수 있도록 지원하는 파서. 입력된 문자열을 논리적 연산 우선순위에 따라 체계적으로 분석하여 최종 참/거짓을 반환함.
1. 괄호 우선 처리: 재귀 호출을 통해 가장 안쪽 괄호에 포함된 표현식부터 먼저 평가하여 복잡한 논리 그룹을 단순화함.
2. OR 분할: || 연산자를 기준으로 전체 조건을 분할. 이 중 하나라도 참이면 전체를 참으로 간주하고 즉시 평가를 종료할 수 있음 (Short-circuit evaluation).
3. AND 분할: && 연산자를 기준으로 남은 조건들을 분할. 모든 하위 조건이 참이어야만 해당 그룹이 참으로 평가됨.
4. 단일 조건 평가: 최종적으로 분리된 개별 조건을 정규 표현식(Regex)을 통해 분석.
- 수치 조건 (@"^Analytics\.(\w+)\s*([><=!]+)\s*(-?\d+(?:\.\d+)?)$"): "Analytics.metric_name > value" 형태의 문자열을 파싱하여, AnalyticsDataManager에서 해당 메트릭 값을 가져와 비교 연산을 수행.
- Flag 조건 (@"^(\w+)\.(\w+)$"): "Category.FlagName" 형태의 문자열을 파싱하여, FlagManager에 해당 카테고리와 플래그가 설정되어 있는지 질의함.
3. 데이터 수집 메커니즘
플레이어의 행동을 누락 없이, 그리고 시스템에 부하를 주지 않는 방식으로 추적하기 위해 이벤트 기반 방식과 지속적인 폴링(Polling) 방식을 혼합하여 사용.
실시간 자동 추적:
시간 데이터: Update()에서 Time.time을 이용해 총 플레이 시간(playtime)과 마지막 입력이 감지된 후 경과한 유휴 시간(idle_time)을 지속적으로 갱신.
private void UpdateTimeTracking()
{
SetNumericValue("playtime", Time.time - _startTime);
if (Input.anyKey) _lastInputTime = Time.time;
else SetNumericValue("idle_time", Time.time - _lastInputTime);
}
점프 데이터: PlayerController의 AbilityRunner가 OnAbilityExecuted 이벤트를 발생시키는 구조를 활용. 이벤트 핸들러가 어떤 종류의 어빌리티(예: ChargeJump)가 실행되었는지 감지하고, 관련 데이터(차지 시간 등)와 함께 기록 요청을 보냄. 이는 불필요한 프레임별 검사를 없애고 실제 행동이 발생한 시점에만 데이터를 수집하는 효율적인 방식.
// PlayerController에서 이벤트 기반
_runner.OnAbilityExecuted += (ability) => {
switch (ability) {
case ChargeJump: RecordJump(JumpType.ChargeJump, chargeTime);
}
};
이동 데이터: FixedUpdate()에서 이전 프레임의 위치(_lastPosition)와 현재 위치를 비교하여 수평 이동 거리를 계산하고, 이를 horizontal_distance 메트릭에 계속 누적.
// FixedUpdate에서 지속적 추적
public void UpdatePlayerPosition(Vector3 currentPosition)
{
float horizontalMove = Mathf.Abs(currentPosition.x - _lastPosition.x);
AddNumericValue("horizontal_distance", horizontalMove);
}
4. Flag 자동 설정 로직
수치 데이터와 상태 데이터를 유기적으로 연결하는 핵심 로직. 시스템은 특정 수치 데이터가 미리 정의된 임계값을 넘어서는 순간, 이를 의미 있는 상태(Flag)로 자동 변환함.
임계값 기반 Flag 활성화:
// 5분 플레이 시
if (GetNumericValue("playtime") > 300f)
FlagManager.Instance.SetFlag("Analytics", (int)AnalyticsFlags.PlayedOver5Minutes);
// 차지점프 숙련도
if (chargeTime >= 2f)
FlagManager.Instance.SetFlag("Analytics", (int)AnalyticsFlags.MasteredChargeJump);
5. 게임 시스템 통합 및 활용
이 시스템의 최종 목표는 수집하고 분석한 데이터를 실제 게임 콘텐츠 분기에 활용하는 것. SequenceTriggerSO와 같은 데이터 에셋에 문자열 조건을 직접 명시하는 방식으로 게임 로직과 완벽하게 통합됨.
실제 활용 예시 (NPC 대화): NPCController의 TriggerDialogue() 메서드 내에서 대화 규칙(Rule)을 평가할 때, FlagManager.Instance.CheckAllConditions를 호출. 이 함수는 규칙에 정의된 Flag 조건 목록과 함께 문자열 조건(StringConditions)을 ConditionParser에 전달.
- StringConditions 예시: "Analytics.jump_count >= 10 && Story.MetFirstNPC && Analytics.playtime > 300"
- 이러한 조건 문자열 하나로 '점프를 10번 이상 했고, 첫 NPC를 만났으며, 플레이 시간이 5분을 넘긴' 플레이어에게만 특별한 대사를 출력하는 복잡한 분기 로직을 손쉽게 구현 가능.
6. 기술적 특징 요약
- 성능: 핵심 데이터 접근은 Dictionary의 해시 조회를, 상태 검사는 비트 연산을 사용하므로 대부분의 연산이 O(1)에 가까운 속도를 보장. 조건 파싱은 필요한 시점(예: 대화 시작)에만 수행되어 프레임 드랍을 유발하지 않음.
- 확장성: 새로운 분석 지표가 필요할 경우, 수치 데이터는 단순히 새로운 문자열 키로 값을 추가하면 되고, 상태 데이터는 AnalyticsFlags Enum에 한 줄 추가하는 것만으로 시스템 전체에 즉시 통합됨. 조건 파서의 정규식을 수정하여 새로운 조건 문법을 추가하는 것 또한 가능.
- 안정성: 데이터 조회 시 키가 존재하지 않을 경우를 대비한 기본값 반환 로직, ConditionParser 내부의 Try-Catch 구문을 통한 문법 오류 예외 처리 등, 잘못된 데이터나 조건식으로 인해 게임 전체가 멈추는 일이 없도록 방어적으로 설계됨.
이 Analytics 시스템은 플레이어의 모든 행동을 자동으로 추적하여 정량적 데이터(수치)와 정성적 데이터(상태)로 변환하고, 이를 기반으로 복잡한 게임 로직을 기획자가 직접 문자열 조건으로 간단하게 구성할 수 있게 하는, 유연하고 강력하며 안정적인 데이터 기반 콘텐츠 분기 솔루션을 목표로 설계됨
'도토리도굴단 > Project: HSR 개발일지' 카테고리의 다른 글
| [🐹Project: HSR] #3 Unity Localization + Google Sheets 연동 및 후처리 (0) | 2025.09.11 |
|---|---|
| [🐹Project: HSR] #1 NPC 대화 시스템 데이터 기반 설계 (0) | 2025.08.27 |