hcg
03.
08.2017

Technika: Jak šampioni dostávají kontext

   Autor: Kr.Pa.   Rubriky: Aktuality, Hlavni, Novinky

Jak Komponent pro kontextuální akce obohacuje interakce šampionů.

Technika je nový seriál, který zkoumá technologie, jež pohání League of Legends. Pokud se vám tento článek líbí, podívejte se na Programátorský blog Riot Games, kde najdete komplexnější rozbory systémů, jež pohánějí naši hru.

Ahoj, lidi, hlásí se vám AaronMike a Lucida! Jsme softwaroví technici z týmu pro základní herní systém LoL a chtěli jsme vám říct něco málo o systému jménem Komponent pro kontextuální akce (Contextual Action Component, zkráceně CAC), který osobnostem našich šampionů dodává dodatečnou vrstvu úžasnosti. V podstatě je CAC systém, který vývojářům umožňuje vybudovat na míru šité interakce mezi šampiony, takže tito šampioni mohou mnohem přirozeněji reagovat na to, co se ve Žlebu děje.

 

Prozkoumáváme systém

Pro začátek se podíváme na příklad toho, co se stane, když Poppy použije své provokativní gesto o samotě oproti tomu, když stojí vedle svého spojence se zlatými křídly, Galia:

 

Poppyina provokace bez Galia

Poppyina provokace před Galiem

Když Poppy provokuje o samotě nebo vedle nesouvisejících šampionů, její provokace bude jednou z generických hlášek. Když Poppy stojí poblíž svého spojence Galia, začne ho místo toho trochu přátelsky škádlit – ba co víc, Galio na to zareaguje, pokud bude v její blízkosti, až skončí. Teď se to zdá samozřejmé, ale když LoL v roce 2010 vyšlo, tento druh interakce nebyl možný. Jediný způsob, jak vytvářet nějakou variaci, byl ten, že zvukový engine vybral jednu náhodnou hlášku ze seznamu. Kvůli tomu museli designéři zvuku používat generické hlášky, aby předešli tomu, že se v určitých situacích spustí nevhodný text.

 

Například Luxina hláška: „Sourozenecká rivalita! Tohle bude zábava!“ dává smysl, když stojí vedle Garena. Ale když stojí vedle Katariny? Moc ne. Takováto hláška by v audio systému ve starém LoL vůbec nebyla možná, protože systém vůbec neuměl takovou hlášku správně vybrat. Marnili jsme tím cenné příležitosti, jak předvést osobnost každého šampiona a jeho vztah k ostatním šampionům!

A právě proto teď máme CAC. CAC je srdcem podobných interakcí: právě díky němu jsou si Poppy a Lux „kontextuálně vědomé“ informací v reálném čase jako třeba toho, kterého spojence mají nejblíž, který předmět si koupily nebo kterého šampiona právě zabily. Díky němu může Pulzní Caitlyn přednést svou jedinečnou hlášku při pětizářezu a Xayah a Rakan spolu mohou ve Žlebu láskyplně tokat.

 

Pod kapotou

CAC byl navržen za prostým účelem: provádět různé akce na základě kontextu libovolné situace ve hře. Strukturu systému můžeme znázornit takto:

  • - Situace A
    • - Pravidlo 1
      • - Podmínky
      • - Akce
    • - Pravidlo 2
      • - Podmínkys
      • - Akce
    • - Další pravidla

Situace je předem definovaný konstrukt v naší hře, jako třeba KillChampion (zabití šampiona), AttackBuilding (útok na budovu) nebo BuyItem (zakoupení předmětu). Za těch pár let jsme do LoL těchto situací zabudovali několik desítek. Každá situace může obsahovat seznam pravidel, která sama obsahují seznam podmínek a jedinečnou akci. Když nastane situace, vybere se pravidlo, jehož podmínky jsou splněné, a pak se provede akce. Remízu mezi vícerem platných pravidel lze vyřešit buď podle toho, co nastane první, anebo náhodně – záleží na nastavení. Podmínky jsou předem definovaný kontext, jako třeba „úroveň vlastního zdraví“, „zaměření konkrétního šampiona“, „region mapy“, „úroveň kouzla“ atd. Hlasové akce mohou vybírat hlášky pro různé posluchače – sebe, spojence, nepřátele a diváky.

Níže je příklad toho, jak si Camille se skinem Naprogramovaná Camille dobírá Ashe se skinem PROJEKT: Ashe:

 

Stejně jako většina kódové databáze LoL je tento systém napsán v C++ a pro konfiguraci využívá náš Herní datový server (GDS). Podívejte se na zkrácený úryvek kódu, který je vyvolán pokaždé, když jeden šampion zabije jiného:

 

// Vyvolá se pokaždé, když jeden šampion zabije jiného
void HandleChampionKillSituation(Champion* killer, Champion* victim, 
uint8_t killerMultikill = 0)
{
  ContextualActionComponent& cac = killer->GetContextualActionComponent();

  // Zjistí, jestli vrah má situaci KillChampion
  const ContextualSituation* situation = cac.FindSituation(kKillChampion);
  if (situation != nullptr) {
    // Nastaví relevantní fakta
    ContextualFacts& facts = cac.GetFacts();
    facts.mKiller = killer;
    facts.mVictim = victim;
    facts.mKillerMultiKillSize = killerMultikill;

    // Pokusit se najít pravidlo, které odpovídá faktům tohoto zářezu
    const ContextualRule* rule = situation->PickRule(facts);
    if (rule) {  // bylo nalezeno kvalifikované pravidlo
      if (rule->ExecuteAudioAction(facts)) {
        // Řekne ostatním CAC, že vrah právě provedl tuto akci
        cac.NotifyAllCacsOfPlayedAction(rule->GetAudioSituationTrigger());
      }

      // Reset dočasných faktů
      facts.mKiller = nullptr;
      facts.mVictim = nullptr;
      facts.mKillerMultiKillSize = 0;
    }
  }
}

Tento úryvek ukazuje, jak systém určuje, která akce by se měla podle kontextu odehrát. Funkce PickRule (vybrat pravidlo) se bude opakovat pro každé pravidlo u situace KillChampion, dokud nenajde to pravidlo, které splňuje všechny podmínky, načež provede příslušnou akci (či akce).

 

Tvorba

Následující screenshot ukazuje pravidlo, které jsme nastavili pro každého hráče, jenž je dostatečně schopný (nebo má dostatečné štěstí), aby získal pětizářez s Pulzní Caitlyn:

 

Pokaždé, když Pulzní Caitlyn zabije nepřátelského šampiona, projede CAC pravidla u situace KillChampion. Pravidlo říká: pokud je to pátý zářez v sérii, přehraj hlášku KillChampion3DPentakill pro sebe (hráče) a pro hráčovy nepřátele. Všimněte si, že toto pravidlo má limit 3 výskytů – v té angličtině je navíc překlep (hned jsem zpátky, jen to opravím) –, takže přehraje pouze první tři úspěšné pětizářezy, protože jak víme, u toho čtvrtého to začíná být docela hlasité.

 

Výhry

Historicky vzato se audio spouštělo přímo prostřednictvím událostí v různých systémech v celé hře. Těmito událostmi mohlo být stvoření částice, animace, seslání kouzla, vstup uživatele atd. Například když hráč pohne se svým šampionem, herní klient vytvoří zvukovou událost, která se jmenuje nejspíš nějak „Champion_VO_MoveCommand“ a pokusí se přehrát odpovídající audio hlášku. Jelikož historické spouštěče si nebyly vědomy kontextu hry, nedokázaly přehrávat interakce šité na míru.

Přímé události jen kloužou po povrchu toho, co lze prostřednictvím CAC udělat. Kombinace situací a pravidel umožňuje vytvářet velmi specializované interakce. Před tímto systémem jsme ve hře pár specializovaných interakcí měli, ale spoléhali jsme se na náhodu, která je používala s příslušnou frekvencí. Například Zac má dvě generické provokativní hlášky: „Ukaž, co v tobě je, anebo táhni“ a „Nejde o to, kolik zvedneš, ale jak dobře vypadáš.“ Když někoho provokuje, hra náhodně vybere jednu hlášku, kterou přehraje. Nyní máme nástroje proto, aby některé hlášky zazněly jen v patřičných situacích. Díky tomu můžeme záměrně vytvářet vzácné a detailní interakce, než abychom je ponechávali náhodě.

 

Xayah a Rakan

Na začátku roku 2016 jsme se rozhodli udělat první pár šampionů ve světě League of Legends. Naším cílem bylo, aby tito dva šampioni interagovali ve hře jako milenci, kterými jsou, a ne aby jen používali generické hlášky a zaměnitelné interakce. Co kdybychom chtěli, aby Rakan dokázal během tanečku zvednout Xayu do vzduchu? Co kdybychom chtěli, aby Xayah Rakana trochu (láskyplně) popichovala? Co kdyby Rakan potřeboval varovat Xayu před hrozícím nebezpečím?

Abychom tato „co kdyby“ proměnili v realitu, museli jsme vylepšit CAC o několik nových akcí a situací.

 

Xayah a Rakan

 

Animace

Systém animací nebyl vybaven dostatečným kontextem, aby animátoři mohli vytvořit požadovaný synchronizovaný taneček či animaci návratu. Abychom tohoto konečného výsledku dosáhli, přidali jsme do CAC nový typ akce, který ovládá animace šampionů. Kdykoliv Xayah něco udělá – nebo během nečinnosti nedělá nic –, vyšle animačnímu systému požadavek PlayAnimation (přehraj animaci) s názvem požadované animace. Tento tok jsme upravili tak, aby CAC tyto požadavky zachytil a zkontroloval, jestli jsou splněny některé kontextuální podmínky. Pokud se shodují, animace se vymění za jinou, která je kontextuálně příhodnější. Pak se požadavek vydá na svou cestu k animačnímu systému, který ho vykoná.

 

Interaktivní CAC

Další výzvou byl ten taneček. Jak by si měli Xayah s Rakanem dát vzájemně vědět, že spolu chtějí začít tančit? Toho jsme dosáhli přidáním nové situace, která se spustí, kdykoliv jiný šampion provede akci CAC. Všechny CAC ve hře dostanou oznámení o splněné akci spolu se stávajícím herním kontextem, aby mohly určit, jestli je zapotřebí nějaké reakce.

 

Zleva doprava: Oba jsou nečinní, Xayah začíná svůj sólový taneček, Rakan se rozhodne přidat a spolu předvedou svůj synchronizovaný taneček.

 

Kontextuální signály

Další úžasná věc vznikla tehdy, když jsme začali posílat žádosti o signály přes CAC. Nyní si kromě běžných signálů milenci mohou říkat věci jako „Kotě, pozor!“ u signálu Nebezpečí a „Nejsou tady!“ u signálu Nezvěstný nepřítel.

 

Technické obavy

 

Systém proti podvodům

Kdykoliv do League of Legends přidáváme nové systémy jako třeba CAC, bereme obrovský zřetel na podvody a hacky. Jeden z hacků spočívá v zachytávání více informací, než hra samotná hráčům nabízí, za účelem získání soutěžní výhody. Podvodník by mohl zneužít kontextuální systém a tyto informace získat. Představte si, kdyby Elise spouštěla hlášku jako „Můj pavoučí smysl mě varuje…“, kdykoliv by se váš tým schovával v nedalekém křoví. Abychom tomuto zneužívání předešli, navrhli jsme CAC tak, aby jednal pouze na základě informací, které klient už má, a nic víc (jinými slovy vidí jen to, co vidíte vy).

 

Výkon

Chtěli jsme vývojářům poskytnout svobodu a praktičnost, kterou potřebovali k tomu, aby vdechli postavám v LoL život, ale také jsme chtěli předejít jakýmkoliv poklesům výkonu pro hráče, ať už by hráli na monstrózním dělu chlazeném tekutým dusíkem, nebo na slabém notebooku. Vždycky jsme se snažili o to, aby byl systém v každé fázi procesu co možná nejméně náročný a co možná nejvíce výkonný. Toho dokážeme dosáhnout pomocí způsobu kódování a těch nejlepších metod:

  1. Situace jsou uloženy v hashmapě a jako klíč používá string hash. Díky této struktuře můžeme rychle získat situaci z objektu CAC. Pokud šampion pro danou situaci nemá žádná data, požadavek se prostě vrátí. Jelikož každý šampion má malou sadu relevantních situací, většina situací je velmi nenáročná.
  2. Dáváme přednost konkrétním situacím před obecnými. Obecně bychom dali přednost obecným, recyklovatelným řešením, která řeší vícero věcí najednou, ale toto je jedinečný případ. Obecnější situace obsahují více pravidel, z nichž každé obsahuje další podmínky, které musí procesor zpracovat. Rozdělení obecné situace na několik málo konkrétních situací snižuje počet pravidel a snižuje výkon. Situace bez pravidel se dokonce mohou přímo vracet. Například máme čtyři konkrétní situace při zářezu: KillChampion (zabití šampiona), KillTurret (zničení věže), KillNeutralMinion (zabití neutrálního poskoka) a KillWard (zničení totemu). KillChampion má nejvíc variací, ale dojde k tomu jen párkrát za zápas. KillNeutralMinion má nejméně variací, ale odehrává se častěji. Pokud bychom místo toho použili obecnou situaci jako KillTarget (zabití cíle) pro všechny zářezové situace, museli bychom procházet obrovský seznam pravidel pokaždé, když by došlo k jednomu z těchto čtyř druhů zářezu.
  3. Dáváme přednost prostým, leč důležitým faktům či podmínkám. Pokud některá z těchto podmínek selže, ostatní komplexní kontroly v tomto procesu se mohou přeskočit.
    1. Přeskakujeme zvukové situace, pokud majitel pronáší audio hlášku. LoL neumožňuje, aby jedna a ta samá postava říkala více hlášek najednou. Díky tomu máme skvělou příležitost pro optimalizaci. Pokud CAC zjistí, že majitel mluví, může nové zvukové situace ignorovat. V případě spamování gest je CAC pořád velmi efektivní, neboť jakmile šampion začne mluvit, většinu procesu přeskočí.
    2. Další důležitou podmínkou je přebíjecí doba situace. Pokud by šampion neměl reagovat na situaci v krátké době po posledním provedení, pak není důvod tuto situaci zpracovávat.
  4. Vyhýbáme se velmi častým situacím, jak je to jen možné. Pokud k nějaké situaci dochází často, existuje pár způsobů, jak zamezit poklesu výkonu:
    1. Nastavíme pro situaci přebíjecí dobu, takže herní klient může přeskočit kontrolu pokaždé, když k situaci dojde.
    2. Zajistíme, aby častější situace měly méně pravidel a mohly tak probíhat rychleji.

 

Závěr

Díky CAC si systémy jako audio a animace lépe uvědomují kontext hry a otevírají tak spoustu nových možností pro naše kreativní kolegy. Díky tomu totiž mohou obohacovat osobnosti našich šampionů a nadále rozšiřovat svět League of Legends. Úplně každá dabingová hláška, kterou do hry přidáme, je příležitost k tomu, abychom někoho rozesmáli a sblížili hráče s jejich oblíbenými šampiony.

 



Zanech odpověď.

Musíš se příhlásit pro vložení komentáře.