리그오브레전드리그오브레전드

온라인 598

[기술 인사이드] 챔피언 행동에 맥락 입히기

조회수 2,663댓글 1추천 4

 

기술 인사이드는 리그 오브 레전드의 기술적인 측면을 들여다보는 새로운 개발자 블로그 시리즈입니다.
리그 오브 레전드를 지탱하는 시스템에 대한 더 자세한 기술 이야기가 궁금하다면
라이엇 게임즈 엔지니어링 블로그(영문)도 확인해 보세요.

 

안녕하세요. 리그 오브 레전드 코어 게임플레이 팀의 소프트웨어 엔지니어 AaronMike와 Lucida가 인사
드립니다! 오늘은 챔피언의 개성을 한층 더해주는 맥락 인식 행동 요소(Contextual Action Component,
이하 ‘CAC’)라는 시스템에 대해 말씀드리려고 합니다. 먼저 핵심만 말씀드리자면, CAC는 개발자들이 챔피언
맞춤형 상호작용을 만들 수 있도록 돕는 시스템입니다. 그 결과, 챔피언들은 소환사의 협곡에서 일어나는
일에 더 자연스럽게 반응할 수 있죠.

CAC 들여다보기
먼저 뽀삐가 혼자 있을 때와 황금빛 날개를 지닌 아군 갈리오 옆에 있을 때 각각 도발 감정 표현을 사용하면
어떤 차이가 있는지 보시죠.
 

 

갈리오가 없을 때 뽀삐의 도발 l 갈리오 앞에서 뽀삐의 도발          


뽀삐가 혼자 있거나 관련이 없는 챔피언 근처에서 도발하면 일반적인 대사 중 하나가 재생됩니다. 뽀삐가
아군 갈리오 근처에 있으면 뽀삐가 장난스러운 농담을 건네고, 게다가 뽀삐의 대사가 끝났을 때 갈리오가
여전히 뽀삐 근처에 있다면 갈리오가 뽀삐의 농담에 답하죠. 지금은 당연한 것처럼 보이지만, 리그 오브
레전드가 2010년에 처음 선보였을 때 이러한 상호작용은 불가능했습니다. 변형을 만들어내는 유일한
방법은 사운드 엔진이 대사 목록에서 무작위로 하나를 고르도록 하는 것이었죠. 그래서 오디오 디자이너들은
특정 상황에서 부적절한 대사가 나오는 것을 예방하기 위해 일반적인 대사를 사용할 수밖에 없었습니다.
 

예를 들어, 럭스의 대사인 “오호~ 남매간의 대결! 재밌겠는데?”는 럭스가 가렌 옆에 있을 때는 적절하죠.
하지만 카타리나 앞에서 이런 말을 한다면 적절한 대사는 아니죠. 리그 오브 레전드의 예전 오디오 시스템은
이 대사를 적절히 선택할 수 없었기 때문에 이런 대사를 사용할 수 없었습니다. 각 챔피언의 개성과 다른
챔피언과의 관계를 보여줄 수 있는 좋은 기회를 활용할 수 없었던 거죠.

이런 기회를 활용하는 것이 바로 CAC의 목표입니다. 위와 같은 상호작용에서 가장 핵심적인 역할을 하는
것이 CAC입니다. CAC로 인해 뽀삐와 럭스는 근처에 있는 아군, 구입한 아이템, 방금 죽인 챔피언 등의
실시간 정보를 ‘맥락에 맞게 파악’할 수 있습니다. 또한 CAC 덕분에 펄스 건 케이틀린 고유의 펜타킬 대사와
자야와 라칸 사이의 달콤한 대화도 가능해졌습니다.

더 깊이 들여다보기
CAC 설계 시 CAC의 용도는 간단했습니다. 바로 게임 내 특정 상황의 맥락에 따라 다른 행동을 실행하는
것이죠. 이 시스템의 대표적인 구조는 아래와 같습니다.

    • 상황
          • 규칙 1
                • 조건
                • 행동
          • 규칙 2
                • 조건
                • 행동
          • 추가 규칙

‘상황’은 KillChampion(챔피언 킬), AttackBuilding(구조물 공격), BuyItem(아이템 구입) 등 게임 내 미리
정의된 상황입니다. 지난 몇 년에 걸쳐 저희는 리그 오브 레전드 내에 수십 가지의 ‘상황’을 구축했습니다. 각
‘상황’에는 일련의 ‘규칙’이 포함되어 있을 수 있죠. 다시 ‘규칙’에는 일련의 ‘조건’과 하나의 ‘행동’이 포함되어
있습니다. 하나의 ‘상황’이 발생하면 ‘조건’이 충족된 ‘규칙’이 선택된 후 그 규칙의 ‘행동’이 실행되죠. 여러
유효한 ‘규칙’ 중 하나를 선택해야 할 때는 선착순 방식이나 정해진 경우에는 무작위 방식으로 선택이 이루어
집니다. ‘조건’은 “자신의 체력 범위”, “목표 챔피언 이름”, “맵 내 특정 지역”, “스킬 레벨” 등 미리 정의된
맥락입니다. 음성 ‘행동’은 자신, 아군, 적, 관전자 등 다른 대상별로 대사를 선택할 수 있습니다.

아래는 프로그램 카밀 스킨을 장착한 카밀이 프로젝트: 애쉬 스킨을 장착한 애쉬를 도발할 때의 예시입니다.
















대부분의 리그 오브 레전드 코드베이스처럼 CAC도 C 로 작성되었고 저희 게임 데이터 서버(GDS)
(영문 링크)
를 활용해서 구성되었습니다. 아래는 챔피언이 다른 챔피언을 죽일 때마다 호출되는 코드를
요약한 것입니다.
 

// To be called whenever a champion kills another champion (챔피언이 다른 챔피언을 죽일 때마다 호출)
void HandleChampionKillSituation(Champion* killer, Champion* victim, uint8_t killerMultikill = 0)
{

   ContextualActionComponent& cac = killer->GetContextualActionComponent();

   // See if the killer has a KillChampion situation (킬을 기록한 챔피언이 KillChampion 상황을 보유했는지 확인)
   const ContextualSituation* situation = cac.FindSituation(kKillChampion);
   if (situation != nullptr) {
      // Set the relevant facts (관련 팩트 설정)
      ContextualFacts& facts = cac.GetFacts();
      facts.mKiller = killer;
      facts.mVictim = victim;
      facts.mKillerMultiKillSize = killerMultikill;

      // Attempt to find a rule that matches these kill facts (이러한 킬 팩트에 맞는 규칙 검색)
      const ContextualRule* rule = situation->PickRule(facts);
      if (rule) { // a qualified rule has been found (충족하는 규칙 찾음)
         if (rule->ExecuteAudioAction(facts)) {
            // Tell the other CACs that the killer just executed this event (킬을 기록한 챔피언이 이 이벤트를
            방금 수행했다고 다른 CAC에게 알림)
            cac.NotifyAllCacsOfPlayedAction(rule->GetAudioSituationTrigger());
         }

         // Reset the momentary facts (일시적인 팩트 재설정)
         facts.mKiller = nullptr;
         facts.mVictim = nullptr;
         facts.mKillerMultiKillSize = 0;
      }
   }
}

위 코드는 CAC가 맥락에 따라 어떤 행동을 취할지 결정하는 과정을 보여주죠. PickRule(규칙 선택) 함수는
모든 ‘조건’을 만족하는 ‘규칙’을 찾을 때까지 KillChampion(챔피언 킬) ‘상황’에 대한 각각의 ‘규칙’에 대해
반복된 후, 부합하는 ‘행동’(들)을 실행합니다.

저작 작업
아래 캡처 화면은 펄스 건 케이틀린으로 펜타킬을 기록할 만큼 실력이 (또는 운이) 좋은 플레이어를 위해
저희가 만든 ‘규칙’입니다.
 

펄스 건 케이틀린이 적 챔피언을 죽일 때마다 CAC가 KillChampion(챔피언 킬) ‘상황’의 규칙들을 검사하죠.
위 ‘규칙’은 이번 킬이 적 학살 중 다섯 번째 킬일 경우, 자신(킬을 기록한 플레이어)과 플레이어의 적들에게
KillChampion3DPentakill의 대사를 재생하는 것입니다. 위에서 보시는 것처럼 이 규칙은 최다 세 번
“발생”(occurrences)합니다. 그래서 첫 세 번의 펜타킬에만 이 규칙이 적용됩니다. 다들 아시듯
네 번째부터는 약간 시끄럽잖아요.

결과물
기존의 오디오는 많은 게임 시스템 내 이벤트에 의해 직접 발생했습니다. 이러한 이벤트에는 파티클 생성,
애니메이션 이벤트, 주문 시전, 사용자의 입력 등이 있죠. 예를 들어, 플레이어가 자신의 챔피언을 움직이면
게임 클라이언트는 “Champion_VO_MoveCommand(챔피언 이동 명령)”와 같은 오디오 이벤트를 만들고
그에 상응하는 오디오 클립을 재생하려 하죠. 기존의 발동 조건들은 게임 내 맥락을 고려하지 않았기 때문에
맞춤형 상호작용을 만들어낼 수 없었습니다.

직접적 이벤트로 구현할 수 있는 것은 CAC로 구현할 수 있는 것에 비하면 수박 겉핥기 수준입니다. 상황과
규칙의 조합으로 인해 아주 특화된 상호작용을 연출할 수 있죠. 이 시스템 도입 전에도 일부 특화된 게임 내
상호작용이 있었지만, 그러한 상호작용을 적절한 빈도로 적용하기 위해 무작위적인 방식에 의존했습니다.
예를 들어, 자크의 일반적인 도발 대사 두 개는 “우쭈쭈쭈쭈쭈. 가서 맘마 좀 더 먹고 올래?”와 “내 탱탱한
근육질 몸매에 넋을 잃으셨군”이죠. 자크가 도발하면 게임이 무작위로 재생할 대사를 선택합니다. 이제는
이러한 대사가 적절한 상황에서만 재생되도록 세부적으로 조정할 수 있는 수단이 생긴 거죠. 이러한 방식을
통해 흔하지 않고 구체적인 상호작용이 운이 좋을 때만 나타나도록 하는 것이 아니라 의도적으로 이루어지게
할 수 있습니다.

자야와 라칸

2016년 초, 저희는 리그 오브 레전드 세계에 첫 번째 연인 챔피언을 만들기 위한 작업을 시작했습니다.
목표는 이 두 챔피언이 단순히 일반적이고 다른 챔피언과도 할 수 있는 상호작용을 하는 것이 아니라 게임
내에서 연인 다운 상호작용을 하도록 만드는 것이었죠. 라칸이 춤을 추는 동안 자야를 공중으로 들어 올릴 수
있게 만드는 건 어떨까요? 자야가 라칸에게 (사랑이 넘치는) 농담을 던지는 건 어떨까요? 또, 라칸이
자야에게 곧 닥칠 위험에 대해 경고하도록 하는 건 어떨까요?

이러한 아이디어를 실제로 구현하기 위해서 CAC에 몇 가지 새로운 ‘행동’과 ‘상황’을 추가해야 했습니다.
 

 

자야와 라칸

 

애니메이션
애니메이션 시스템은 애니메이터가 원하는 동작을 맞춘 춤이나 귀환 모션을 만드는 데 필요한 일부 맥락을
갖추고 있지 않았습니다. 최종 결과물을 얻기 위해서 CAC에 새로운 ‘행동’ 유형을 추가해 챔피언
애니메이션을 조절했죠. 자야가 어떤 행동을 취할 때마다 (또는 가만히 서서 아무것도 하지 않을 때마다)
원하는 애니메이션 명칭을 지닌 애니메이션 시스템에 PlayAnimation(애니메이션 재생) 요청을 보냅니다.
저희는 이러한 흐름을 변경해 CAC가 해당 요청에 개입해서 맥락상의 ‘조건’이 충족되었는지 확인하도록
했죠. 조건이 충족되었다면, 원래 애니메이션이 더욱 맥락을 고려한 애니메이션으로 대체됩니다. 그다음
해당 요청이 전송되어 애니메이션 시스템에 의해 실행되죠.

상호작용 CAC
다음 과제는 춤이었습니다. 자야와 라칸은 같이 춤을 추고 싶을 때 어떻게 상대방에게 의사를 전달할까요?
상대 챔피언이 CAC ‘행동’을 취할 때마다 발동되는 새로운 ‘상황’을 추가해서 이 과제를 해결할 수
있었습니다. 게임 내 모든 CAC에게는 완료된 ‘행동’과 현재 게임 맥락에 대한 알림이 이루어지기 때문에
반응이 필요한지 결정할 수 있습니다.
 

 

왼쪽에서 오른쪽 순서로 보시면, 둘 다 가만히 서 있다가 자야가 혼자 춤추기 시작하고, 라칸이 합류해 같이 동작을 맞춰 춤춥니다.

 

맥락에 따른 핑의 활용
핑 요청이 CAC를 거쳐 가도록 변경함으로써 또 다른 큰 결과물을 얻을 수 있었습니다. 이제 일반적인 핑
이외에도 자야와 라칸은 위험 핑에 대해 “자기 조심해!”나 적 사라짐 핑에 대해 “여기 없잖아?!” 같은 말을
할 수 있죠.

기술 측면에서 우려 사항

부정행위에 대한 대처
부정행위와 해킹은 저희가 리그 오브 레전드에 CAC 같은 새로운 시스템을 도입할 때마다 가장 우려하는
사항입니다. 해킹의 일종으로, 게임에서 우위를 점하기 위해 게임이 플레이어에게 제공하는 것 이상의
정보를 빼낼 수 있습니다. 부정행위에 가담하는 유저는 맥락 시스템을 악용해 이러한 정보를 얻어낼 수
있습니다. 여러분 팀이 근처 수풀 속에 숨어 있을 때 엘리스가 “뭔가 내 스파이더 센스를 자극하고 있어…”
같은 말을 한다고 생각해보세요. 이러한 악용을 예방하기 위해 클라이언트가 이미 보유하고 있는 정보만을
토대로 해서 CAC가 동작하도록 설계했습니다. (즉, CAC는 여러분이 보는 것만 본다는 말이죠.)

성능
저희는 개발자들이 리그 오브 레전드의 캐릭터를 구현하는 데 필요한 자유와 이용 편의성을 제공하고자
하지만, 동시에 질소 냉각 방식의 괴물 같은 사양의 컴퓨터를 쓰거나 사양이 낮은 랩톱 컴퓨터를 쓰든 상관
없이 어떤 플레이어도 성능 저하를 경험하는 것은 원하지 않습니다. 저희는 모든 프로세스에서 시스템을
가능한 한 가볍게 하면서도 성능은 극대화하는 것을 언제나 목표로 해 왔죠. 아래와 같은 코딩 방식과 최선의
작업 방침을 통해 그 목표를 달성할 수 있었습니다.

      1. 상황은 (스트링 해시가 키 타입인) 해시맵에 저장됩니다. 이런 구조로 인해 CAC 객체로부터 상황을
          신속히 찾아낼 수 있죠. 챔피언에게 특정 상황에 대한 데이터가 없는 경우, 핸들 함수는 단순히
          반환됩니다. 각 챔피언과 연관된 상황은 적기 때문에 대부분의 상황은 자원을 거의 소모하지 않죠.

      2. 저희는 일반적 상황보다 특수 상황을 선호합니다. 보통이라면 한 번에 여러 문제를 해결할 수 있는
          일반적이고 다시 사용 가능한 해결 방법을 선호했겠지만, 이번은 특이한 경우죠. 일반적인 상황이
          많아지면 규칙도 늘어나고, 각 규칙은 CPU가 처리해야 하는 다른 조건을 포함하고 있습니다.
          일반적인 상황을 몇 개의 특수 상황으로 분리하면 규칙 수가 줄고 성능이 향상됩니다. 규칙이 없는
          상황은 심지어 곧바로 반환될 수 있죠. 예를 들어, 킬 상황에는 KillChampion(챔피언 킬),
          KillTurret(포탑 킬), KillNeutralMinion(중립 미니언 킬), KillWard(와드 킬)의 네 가지 특수 상황이
          있습니다. KillChampion이 가장 다양한 변형을 지닌 경우가 많지만, 게임 내에서 발생 빈도수가 적죠.
          KillNeutralMinion이 변형 가짓수가 가장 적지만, 더 자주 발생합니다. 저희가 모든 킬 상황에 대해
          KillTarget(목표물 킬) 같은 일반적인 상황을 사용했다면 위 네 가지 목표물 중 하나가 처치당할
          때마다 엄청나게 많은 규칙을 검사해야 했겠죠.

      3. 간단하지만 중요한 사실이나 조건을 우선적으로 검사합니다. 해당 조건 중 하나라도 충족되지 않으면
          해당 프로세스에서 기타 복잡한 검사를 생략할 수 있죠.

             a. 해당 챔피언이 말하는 중에는 오디오 상황을 건너뜁니다. 리그 오브 레전드는 한 캐릭터가 동시에
                 여러 대사를 발화하는 것을 허용하지 않습니다. 이것이 저희 입장에서는 큰 기회죠. CAC가 해당 
                 챔피언이 말하는 중이라는 것을 인지하면 새로운 오디오 상황을 무시할 수 있습니다. 감정 표현 
                 키를 연타할 때도 챔피언이 말을 시작하는 순간 CAC는 대부분의 프로세스를 건너뛰기 때문에 
                 CAC는 여전히 아주 효율적이죠.

             b. 또 다른 중요한 조건은 상황의 재발생 대기시간입니다. 챔피언이 최근의 행동 실행 후 짧은 기간
                 내에 상황에 반응하지 않아야 한다면, 해당 상황을 처리할 이유가 없겠죠.

      4. 가능하면 빈도가 높은 상황은 피합니다. 어떤 상황이 자주 발생한다면, 성능 저하를 막기 위해 취할 수
          있는 방법에는 아래와 같은 방법이 있습니다.

             a. 해당 상황에 대해 재발생 대기시간을 정해 해당 상황이 발생할 때마다 게임 클라이언트가 검사를
                 수행할 필요가 없도록 합니다.

             b. 발생 빈도가 높은 상황은 규칙이 거의 없도록 해 처리 속도를 높입니다.

결론

CAC가 도입됨으로써 오디오 및 애니메이션 등의 시스템은 게임의 맥락을 더 잘 인지할 수 있게 되어 저희
창의력 넘치는 동료들에게 많은 새로운 가능성을 열어주었습니다. 챔피언에게 더 큰 개성을 부여하고 리그
오브 레전드의 세계를 계속해서 확장할 힘을 준 것이죠. 저희가 게임에 추가하는 대사 하나하나가 게임을
더 재미있게 만들고 플레이어 여러분이 좋아하는 챔피언이 더욱 친근하게 느껴지도록 도와줄 것입니다.
 

댓글