
Olá! Meu nome é Vladimir Popov e sou um desenvolvedor cliente do projeto War Robots.
War Robots existe há vários anos: durante este tempo, dezenas de novos mechs surgiram no jogo. E, claro, nenhum deles seria único sem seu próprio conjunto de habilidades.
Contarei neste artigo como funciona o sistema de habilidades do nosso jogo e como evoluiu, de forma simples e sem nenhum detalhe técnico.
Primeiro, vamos mergulhar na história e olhar para a implementação antiga - agora ela não é mais usada no projeto.
As antigas habilidades eram muito triviais: eles tinham um componente que estava pendurado no robô. Foi uma construção monolítica em que o programador descreveu completamente como a habilidade funciona: seu fluxo, como e com o que ela interage. Toda a lógica é descrita dentro de um componente, que o designer do jogo poderia simplesmente pendurar no robô e ajustar os parâmetros. Não havia como alterar as habilidades de fluxo - os designers de jogos só podiam alterar os parâmetros e tempos.
A habilidade antiga só poderia existir em dois estados: ativo e inativo. Cada estado pode ter sua própria ação atribuída.

Considere o exemplo da habilidade Jammer. Ela já foi, por exemplo, o robô Stalker. Ela trabalhou da seguinte maneira:
- Se a habilidade estiver ativa, a animação é reproduzida e o robô entra no estado Jammer. Nesse estado, o robô não pode ser direcionado.
- Se a habilidade estiver inativa, nada acontece.
- Quando você tenta ativar a habilidade, é verificado se mais de n segundos se passaram desde a última ativação.
- A desativação ocorre automaticamente após m segundos.
Por muito tempo, essa funcionalidade foi suficiente para nós. Mas com o tempo, tudo mudou: tanto os designers de jogos quanto os programadores não estavam mais satisfeitos com essa abordagem. Era difícil para os programadores manter essas habilidades, porque o código se tornava monstruoso - com uma cadeia de herança muito longa, onde cada situação tinha que ser descrita. Os designers de jogos não tinham flexibilidade de sistema. Ou seja, para o bem de qualquer mudança em uma habilidade, eles tiveram que solicitar a revisão dos programadores, mesmo se exatamente a mesma funcionalidade existisse na habilidade vizinha.
Então percebemos que precisávamos mudar algo. E eles desenvolveram um novo sistema. Nele, cada habilidade passou a ser representada como um conjunto de diversos objetos relacionados. A função foi dividida em estados, habilidades e componentes de estados.
Como funciona?
Qualquer habilidade tem um mestre . Este é seu objeto central. Ele conecta o resto dos objetos de habilidade com o mundo externo e vice-versa. E ele também toma todas as decisões principais. Pode haver qualquer número de
estados . Em essência, o estado aqui não é muito diferente do estado "ativo" / "inativo" da versão antiga. Mas agora pode haver qualquer número deles, e seu propósito se tornou mais abstrato. Apenas um estado pode estar ativo por vez.
A principal inovação em relação ao antigo sistema eram os componentes . O componente descreve algum tipo de ação. Cada estado pode ter qualquer número de componentes.

Como funcionam as novas habilidades?
A habilidade só pode estar em um dos estados de cada vez. O mestre está empenhado em trocá-los. Os componentes que se vinculam ao estado reagem à ativação / desativação do estado e, dependendo disso, podem começar a executar uma ação ou parar de executá-la.
Todos os objetos agora são personalizáveis. O designer do jogo pode misturar estados e componentes entre si de qualquer maneira e, assim, obter uma nova capacidade dos blocos pré-instalados. Os programadores agora são necessários apenas para criar um novo componente ou estado, o que facilita muito a escrita de código. Agora eles trabalham com pequenas entidades, descrevem alguns elementos simples e não montam a habilidade sozinhos - os designers de jogos começaram a fazer isso.
O fluxo ficou assim:
- O mestre ativa o primeiro estado;
- ;
- ;
- ;
- ;
- ;
- .
Posteriormente, este procedimento é repetido várias vezes. Para facilidade de uso, um estado não é apenas um contêiner para componentes, mas também determina quando alternar para outro estado e pede ao mestre para alternar.
Com o tempo, isso se tornou insuficiente para nós, e o esquema da habilidade foi transformado da seguinte forma:

Mestre, estado e componentes permaneceram em seus lugares, mas novos elementos foram adicionados a eles.
A primeira coisa que chama sua atenção é que adicionamos condições a cada estado e componente. Para os estados, eles definem requisitos adicionais para deixar o estado. Para componentes, eles determinam se o componente pode executar sua ação.
Um contêiner de cargas (cargas) contém cargas, recarrega-as, interrompe a recarga quando necessário e fornece taxas aos estados para uso.
O cronômetro é usado quando vários estados devem ter um tempo de execução comum, mas seu próprio tempo de execução não está definido.
É importante notar que todos os objetos de habilidade são opcionais. Tecnicamente, apenas o mestre e um estado são suficientes para a capacidade de trabalho.
Não há tantas habilidades que são completamente montadas sem o envolvimento dos programadores, mas o desenvolvimento em geral tornou-se visivelmente mais barato, porque os programadores agora escrevem coisas muito pequenas: por exemplo, um novo estado ou dois componentes, o resto é reutilizado.
Vamos resumir quais são as partes constituintes das habilidades que temos e quais são elas:
- O mestre atua como uma máquina de estado. Ele fornece estados e componentes com informações sobre o mundo, e o mundo - informações sobre uma habilidade. O mestre serve como um elo entre os estados, componentes e peças de serviço de uma habilidade: cobranças e temporizadores externos.
- O estado escuta os comandos de ativação e desativação do mestre e, consequentemente, ativa e desativa os componentes, e também pede ao mestre para mudar para outro estado. O estado decide quando ele precisa mudar para o próximo. Para fazer isso, ele usa sua condição interna: se o jogador clicou no botão de habilidade, se um certo tempo passou desde que o estado foi ativado, etc. - e condições externas vinculadas ao estado.
- : . : , , .
- , , . . , . , . — , .
- Um contêiner de cargas contém cargas, as recarrega, interrompe a recarga quando necessário e concede encargos aos estados. É usado em habilidades de carga múltipla quando você precisa dar ao jogador a oportunidade de usá-lo várias vezes, mas não mais do que n vezes consecutivas.
- O cronômetro é usado quando vários estados têm uma duração comum, mas não se sabe por quanto tempo cada um deles é válido. Qualquer estado pode iniciar um cronômetro de n segundos. Todos os estados interessados se inscrevem no evento sobre o fim do cronômetro e fazem algo quando ele termina.
Agora vamos voltar ao diagrama de habilidade. Como ela começou a agir?
- No início do jogo, o mestre escolhe o primeiro estado e o ativa;
- O estado ativa todos os seus componentes;
- ;
- ;
- , ;
- ;
- .
Os estados podem usar cobranças como uma condição de transição adicional. Se essa transição ocorrer, o número de cobranças diminui. Além disso, os estados podem usar um cronômetro comum. Neste caso, o tempo total de sua execução será determinado pelo cronômetro, e cada estado individualmente pode durar a qualquer momento.
Não criamos algo completamente novo para a IU. Está organizado assim conosco.
O mestre tem sua própria IU. Ele define alguns elementos que sempre devem estar na IU e não dependem de qual estado está ativo no momento.
Cada estadohá alguns na IU. O estado da IU é exibido apenas quando seu estado está ativo. Ele recebe dados sobre seu estado e pode exibi-los de uma forma ou de outra. Por exemplo, os estados de duração geralmente têm uma barra e um texto em sua IU que representa o tempo restante.
No caso em que o estado está esperando por um comando externo para continuar a habilidade, sua IU exibe um botão. E pressioná-lo envia o comando para o estado.

Agora, vamos examinar o trabalho de habilidades usando exemplos específicos. Vamos começar com um robô chamado Inquisitor.
Temos quatro estados que mudam um após o outro. Acima dos estados, você pode ver sua exibição na IU. Em dois deles, você pode ver os componentes que se referem a eles. Os outros dois estados simplesmente não têm componentes.
Fluxo de trabalho da habilidade:
- WaitForClick. .
- , . WaitForGrounded.
- . , . , , Jammer, .
- .
- : Sound Jammer, Shake, n.
- Duration, n , .
- Duration, : .
- Após a conclusão, a habilidade retorna ao primeiro estado.

Outro exemplo é o Phantom. Muito acontece aqui de forma semelhante ao Inquisitor, mas ainda existem algumas nuances:
- Começamos com WaitForClick.
- Em seguida, a duração, na qual o teletransportador é definido, as estatísticas da mudança de mecanismo, som e animação são reproduzidos.
- Depois disso - DurationOrClick, em que as estatísticas de pele são alteradas, a animação e o FX são reproduzidos.
- Se for feito um clique, passamos para outra Duration, na qual o pelo é teletransportado, as estatísticas mudam, a animação, FX e sons são reproduzidos.
- Após este estado ou após o término do tempo DurationOrClick, vamos para Duration.
A principal diferença é que os estados ramificados aparecem aqui. DurationOrClick vai para o estado a se o tempo especificado passou, ou para o estado b , se o jogador teve tempo para clicar no botão de habilidade antes.

Assim, parece que nosso sistema evoluiu de simples para complexo, mas simplificou a vida de programadores e designers de jogos. A ajuda do primeiro agora é mais necessária ao adicionar pequenos componentes, enquanto o último recebeu maior autonomia e pode agora montar de forma independente novas habilidades a partir de estados e componentes existentes. Ao mesmo tempo, os jogadores também recebiam um lucro na forma de habilidades mais diversas e complexas dos mechs.