/tech: Un contesto da campioni

Da RiotAaronMike, Lucida

/tech è una nuova serie Nexus che analizza il lato tecnologico di League of Legends. Se vi piacciono queste cose, date un'occhiata al blog degli sviluppatori di Riot Games per analisi ancora più tecniche e approfondite del sistema che fa muovere gli ingranaggi del gioco.

Ciao a tutti da AaronMike e Lucida! Siamo due ingegneri del software all'interno del team Gameplay di LoL, e oggi vogliamo condividere con voi alcune informazioni sul sistema chiamato Componente d'Azione Contestuale (o CAC), che aggiunge un pizzico di profondità alla personalità dei nostri campioni. Il CAC è fondamentalmente un sistema che permette agli sviluppatori di creare interazioni specifiche per i campioni, permettendo loro di reagire con maggior naturalezza a ciò che accade nella Landa.


Analisi del sistema

Iniziamo osservando un esempio di ciò che succede quando Poppy usa la sua emote Provoca quando è da sola rispetto a quando è in compagnia del suo alleato dalle ali d'oro, Galio:


Provocazione di Poppy senza Galio

Provocazione di Poppy in presenza di Galio

Quando Poppy usa la provocazione da sola o in presenza di campioni non associati a lei, la sua battuta sarà generica. Quando invece si trova vicino a un Galio alleato, dà il via a un breve scambio di battute mirate, a cui lo stesso Galio risponde se si trova ancora nelle vicinanze. Ora sembra un dettaglio quasi ovvio, ma nel 2010, al lancio di LoL, questo tipo di interazione era impossibile. L'unico modo per creare una certa varietà era lasciare che il motore sonoro scegliesse una battuta casuale da un elenco. Questo costringeva i progettisti audio a usare battute generiche per evitare che venissero riprodotte casualmente frasi non adatte al contesto.


Per fare un esempio, la battuta di Lux "Rivalità tra fratelli? Sarà divertente!" ha senso quando si trova vicino a Garen. Ma quando è accanto a Katarina... Beh, non proprio. Una battuta del genere non sarebbe mai stata accettata nel vecchio sistema audio di LoL, dal momento che il programma non aveva i mezzi per selezionarla solo nelle situazioni appropriate. Questo ci privava di una grande occasione per mostrare la personalità di ciascun campione e il loro rapporto con gli altri personaggi!

Il sistema CAC serve proprio a questo. Rappresenta il cuore di queste interazioni; è lui a "informare" Poppy e Lux del contesto grazie a informazioni in tempo reale sugli alleati vicini, sugli oggetti o sui nemici uccisi. Inoltre, è ciò che fornisce a Caitlyn Pulsefire la sua speciale battuta per la Pentakill e che permette a Xayah e Rakan di fare i piccioncini nella Landa.


Come funziona

Il sistema CAC è stato creato per uno scopo molto semplice: eseguire azioni diverse nel gioco in base al contesto in una data situazione. La struttura del sistema può essere rappresentata in questo modo:

  • - Situazione
    • - Regola 1
      • - Condizioni
      • - Azione
    • - Regola 2
      • - Condizioni
      • - Azione
    • - Altre regole

Una situazione è un costrutto predefinito all'interno del gioco (per esempio KillChampion, AttackBuilding, o BuyItem). Negli ultimi due anni abbiamo inserito in LoL decine di situazioni diverse. Ogni situazione può contenere un elenco di regole, al cui interno si trova una lista di condizioni e una singola azione. Quando questa situazione si verifica, viene scelta una delle regole le cui condizioni sono soddisfatte e di conseguenza viene eseguita la relativa azione. Nel caso in cui più regole siano applicabili, la decisione avviene per priorità o per scelta casuale, se specificato. Le condizioni rappresentano contesti ben definiti, come "quantità di Punti salute del giocatore", "nome campione bersaglio", "zona della mappa", "livello abilità" ecc. Le azioni vocali determinano le battute per i diversi ascoltatori, ovvero se stessi, alleati, nemici o spettatori.

Qui sotto trovate un esempio del processo che avviene quando Camille con l'aspetto Programma Camille provoca Ashe con l'aspetto Progetto: Ashe:


Anche questo sistema, come la maggior parte del codice base di LoL, è scritto in C++ e usa il nostro Game Data Server (GDS) per la sua configurazione. Ecco un frammento ridotto del codice che viene richiamato ogni volta che un campione uccide un altro campione:


// Da richiamare ogni volta che un campione uccide un altro campione
void HandleChampionKillSituation(Champion* killer, Champion* victim, 
uint8_t killerMultikill = 0)
{
  ContextualActionComponent& cac = killer->GetContextualActionComponent();

 // Verificare se l'uccisore soddisfa la situazione KillChampion (UccisioneCampione)
  const ContextualSituation* situation = cac.FindSituation(kKillChampion);
  if (situation != nullptr) {
    // Impostare i fattori rilevanti
    ContextualFacts& facts = cac.GetFacts();
    facts.mKiller = killer;
    facts.mVictim = victim;
    facts.mKillerMultiKillSize = killerMultikill;

    // Individuare una regola che corrisponda ai fattori elencati
    const ContextualRule* rule = situation->PickRule(facts);
    if (rule) {  //  è stata individuata una regola idonea
      if (rule->ExecuteAudioAction(facts)) {
        // Informa le altre CAC che l'uccisore ha appena causato questo evento
        cac.NotifyAllCacsOfPlayedAction(rule->GetAudioSituationTrigger());
      }

      // Azzerare i fattori momentanei
      facts.mKiller = nullptr;
      facts.mVictim = nullptr;
      facts.mKillerMultiKillSize = 0;
    }
  }
}

Questo estratto mostra come il sistema determina l'azione da compiere in base al contesto. La funzione PickRule analizza ciascuna delle regole previste per la situazione KillChampion fino a trovare la regola che rispetta tutte le condizioni, quindi ne esegue la relativa azione (o azioni).


Programmazione

L'immagine seguente mostra una regola scritta per i giocatori abbastanza abili (o fortunati) da eseguire una pentakill con Caitlyn Pulsefire:


Ogni volta che Caitlyn Pulsefire uccide un campione nemico, il sistema CAC analizza tutte le regole della situazione KillChampion. Questa regola dice: se questa è la quinta uccisione di una serie, riproduci la battuta KillChampion3DPentakill per se stesso (il giocatore) e per i nemici del giocatore. Come potete vedere, questa regola ha un limite di 3 "ripetizioni" (sì, c'è un errore di battitura in "occurences", lo correggo subito), quindi la battuta verrà riprodotta solo per le prime tre pentakill, perché poi stufa.


Vittorie

In origine, gli effetti audio venivano attivati direttamente dagli eventi in vari sistemi all'interno del gioco. Tali eventi potevano essere la creazione di particelle, attivazione di animazioni, lanci di abilità, inserimento di comandi da parte dell'utente ecc. Per esempio, quando il giocatore muove il proprio campione, il client costruisce un evento audio con un nome tipo "Campione_VO_ComandoMovimento" e tenta di riprodurre l'effetto sonoro corrispondente. Dal momento che queste attivazioni non erano "consapevoli" del contesto, non potevano eseguire interazioni specifiche.

Gli eventi diretti sono solo una minima parte di ciò che può essere fatto grazie al sistema CAC. La combinazione di situazioni e regole permette di programmare interazioni estremamente specifiche. Alcune interazioni particolari erano presenti anche prima dell'avvento di questo sistema, ma ci si affidava al caso per poterle riprodurre con una frequenza appropriata. Per esempio, Zac ha due provocazioni generiche: "Fai le cose in grande o non fare niente" e "Non conta quanti pesi sollevi. Conta la figura che fai." Quando il giocatore provoca, il gioco sceglie casualmente una delle due battute. Ora abbiamo gli strumenti che permettono di riprodurle solamente in situazioni appropriate. In questo modo possiamo dar vita a interazioni rare e specifiche intenzionalmente piuttosto che lasciare tutto al caso.


Xayah e Rakan

All'inizio del 2016 abbiamo iniziato a creare la nostra prima vera coppia di campioni nell'universo di League of Legends. Il nostro obiettivo principale era farli interagire come due veri innamorati, non usando delle semplici battute generiche e intercambiabili. E se Rakan avesse potuto sollevare Xayah in aria durante una delle sue danze? E se Xayah avesse potuto punzecchiare Rakan con qualche (amorevole) frecciatina? E se Rakan avesse voluto avvertire Xayah di un pericolo imminente?

Per trasformare tutti questi "se" in realtà, dovevamo migliorare il sistema CAC inserendo nuove azioni e situazioni.


Xayah e Rakan


Animazioni

Il sistema di animazione non era dotato del contesto necessario a creare situazioni sincronizzate di ballo o richiamo. Per raggiungere il risultato desiderato, abbiamo aggiunto al CAC una nuova azione che controllava l'animazione dei campioni. Ogni volta che Xayah compie un'azione (oppure quando rimane immobile), invia una richiesta PlayAnimation al sistema di animazione con il nome dell'animazione desiderata. Abbiamo modificato questa sequenza in modo che il sistema CAC potesse intercettare la richiesta e controllare la verifica di eventuali condizioni. Se la risposta è sì, viene richiesta un'animazione più attinente al contesto e la richiesta viene quindi inviata ed eseguita dal sistema di animazione.


CAC interattive

La sfida successiva riguardava le danze. Come potevano Xayah e Rakan far sapere l'uno all'altra quando avevano intenzione di iniziare a ballare insieme? Il problema è stato risolto aggiungendo un'altra situazione che si attiva quando un altro campione esegue un'azione CAC. Tutte le CAC del gioco ricevono notifica dell'azione appena compiuta e del relativo contesto di gioco, in modo da decidere quale reazione sia la più appropriata.


Da sinistra a destra: entrambi i campioni sono immobili, Xayah inizia a ballare da sola, Rakan si inserisce ed entrambi eseguono una danza sincronizzata.


Segnali contestuali

Un'altra fantastica novità resa possibile dal sistema è il reindirizzamento dei segnali attraverso le CAC. Oltre ai normali segnali, i due innamorati possono dirsi cose come "Amore, attenzione!" per il segnale di pericolo e "Non sono qui!" per il segnale nemico disperso.


Problemi tecnici


Anti-trucchi

Il possibile uso di trucchi e hack rappresenta un problema notevole ogni volta che in League of Legends viene aggiunto un nuovo sistema come le CAC. Una particolare forma di hack riguarda la possibilità di ottenere più informazioni di quelle che il gioco fornisce normalmente ai giocatori per avere un vantaggio competitivo sleale. Chi imbroglia potrebbe sfruttare il sistema contestuale per ottenere queste informazioni. Pensate se Elise dicesse qualcosa tipo "I miei sensi di aracnide pizzicano..." ogni volta che voi o un vostro alleato si trova vicino a lei nell'erba alta. Per evitare situazioni simili, abbiamo progettato il sistema CAC in modo che agisca solamente in base alle informazioni in possesso del client (ovvero, niente di più di ciò che potete vedere).


Prestazioni

Volevamo fornire agli sviluppatori la libertà e la fruibilità adeguate a dare più spessore ai personaggi di LoL, ma evitando problemi di prestazioni sia per i giocatori dotati di un sistema all'ultimo grido raffreddato con azoto liquido che per quelli con un semplice portatile. Uno dei nostri obiettivi principali è sempre stato quello di rendere ogni aspetto il più leggero e performante possibile in ogni momento del processo. Grazie a una combinazione di particolari scelte di programmazioni e buone prassi, siamo riusciti nel nostro intento.

  1. Le situazioni sono alloggiate in una HashMap con tipo chiave impostato su stringa hash. Grazie a questa struttura possiamo rapidamente recuperare qualsiasi situazione da un oggetto CAC. Se un campione non possiede dati per una specifica situazione, la funzione di riferimento viene semplicemente restituita. Dato che ogni campione ha un numero di situazioni rilevanti limitate, la maggior parte delle situazioni ha un peso estremamente limitato.
  2. Le situazioni specifiche hanno priorità sulle situazioni generiche. In genere preferiamo avere soluzioni generiche e riutilizzabili che risolvano un maggior numero di problemi contemporaneamente, ma questo è un caso particolare. Un maggior numero di situazioni generiche significa un maggior numero di regole, ciascuna delle quali include ulteriori condizioni che la CPU deve elaborare. Dividere una situazione generica in un certo numero di situazioni specifiche riduce il numero di regole e migliora le prestazioni. Possono essere restituite persino situazioni per le quali non sono previste regole. Per esempio, nel gioco sono presenti quattro specifiche situazioni di uccisione: KillChampion (campioni), KillTurret (torri), KillNeutralMinion (minion) e KillWard (lumi). La situazione KillChampion è spesso quella con più varianti, ma si verifica solo un numero limitato di volte nel corso di una partita. KillNeutralMinion ha il minor numero di variante, ma si verifica molto spesso. Se avessimo usato una situazione generica (per esempio KillTarget) per ogni uccisione, il sistema avrebbe dovuto analizzare un enorme numero di regole ogni volta che uno di questi quattro tipi di bersagli veniva ucciso.
  3. È preferibile verificare prima le condizioni o i fattori più semplici ma più importanti. Se una qualsiasi di queste condizioni non si verifica, gli altri controlli più complessi all'interno di questo processo possono essere evitati.
    1. Evitare altre situazioni audio se il personaggio sta parlando. In LoL un campione non può pronunciare più battute contemporaneamente. Questo ci ha fornito una grande occasione per ottimizzare i processi. Se il sistema CAC riconosce che il personaggio in questione sta parlando, ignora qualsiasi nuova situazione audio. Questo vale anche nel caso di emote usate a raffica: il sistema CAC rimane efficiente e salta gran parte del processo quando il campione inizia a parlare.
    2. Un'altra condizione importante è la "ricarica" delle situazioni. Se ai campioni non è permesso rispondere a una nuova situazione prima che sia passato un determinato periodo di tempo dalla reazione precedente, allora non è necessario elaborarla.
  4. Evitare, ove possibile, le situazioni ad alta frequenza. Se una situazione si verifica molto spesso, si può intervenire in vari modi per impedire che incida sulle prestazioni:
    1. Impostare un "tempo di ricarica" per la situazione, in modo che il client eviti di elaborarla ogni volta che si verifica.
    2. Assicurarsi che le situazioni ad alta frequenza abbiano un minor numero di regole, in modo da essere elaborate più rapidamente.

Conclusioni

Grazie alle CAC, gli altri sistemi come audio e animazione possono avere maggiori informazioni in tempo reale sul contesto della partita e fornire così maggiori possibilità alle menti creative dietro il gioco. Questo fornisce loro più strumenti per approfondire le personalità dei campioni e continuare ad ampliare il mondo di League of Legends. Ogni battuta e ogni dialogo aggiunto al gioco diventa così un'opportunità per strappare un sorriso ai giocatori e farli avvicinare ancora di più ai propri campioni preferiti.



2 years ago


Contenuto collegato