Escrevendo uma arena PvP baseada em turnos com movimentos simultâneos

Neste artigo, falarei sobre o que motivou a criação do jogo em um gênero tão incomum, que tipo de gênero era, como o desenvolvimento progrediu, quais dificuldades encontramos e como menos de um ano de trabalho à noite conseguimos criar um protótipo totalmente jogável.



Na primavera de 2017, me deparei com um incentivo do Atlas Reactor . O jogo era uma espécie de mistura selvagem de xadrez, pôquer e mobs, e uma combinação tão incomum de gêneros realmente chamou minha atenção. Ela se tornou meu jogo favorito, participei de torneios e eventos online e tudo ficaria bem, mas ...

No verão de 2019, os servidores foram fechados, porque devido à baixa popularidade do jogo, seu suporte não foi lucrativo para o editor. O jogo foi construído com o modelo de jogo como serviço , portanto, o desligamento dos servidores transformou os clientes em pedaços de código quebrados.

Devido à combinação única de gêneros de jogos semelhantes, simplesmente não havia jogos no mercado, mas eu realmente queria jogar algo assim.

Logo após o fechamento, me deparei com um post no reddit que um grupo de entusiastas entre ex-jogadores decidiu criar um servidor privado e ajustar o código do cliente para trabalhar com ele, mas não tinha muito desejo de me juntar a eles. Nunca me interessei por engenharia reversa e, além disso, essa atividade não parece muito legal.

No final, decidi reunir pessoas e organizar trabalhos sobre o “herdeiro espiritual” - para fazer um jogo com mecânica semelhante, mas com novos personagens, habilidades, cartas e ENT. Começamos a trabalhar com puro entusiasmo à noite e nos fins de semana. Queríamos trazer algo próprio para o gênero, adicionar variabilidade e profundidade e corrigir as falhas do original. Desde o início, ficou claro que o jogo não se tornaria super popular, mas eu tinha certeza de que pelo menos vários milhares de pessoas poderiam estar interessadas nele. Pelo menos - fãs do reator Atlas fechado.

Jogabilidade


Em geral, a ação lembra o XCOM multiplayer, mas com 100% de chance de atropelar e heróis (como no gênero MOBA).

A idéia do jogo é que todos os resultados sejam completamente determinados pelas ações fixas dos jogadores. A aleatoriedade está completamente ausente e a variação é baixa o suficiente para que jogadores experientes possam calcular todos os resultados prováveis ​​e agir com base nessas informações. Para reduzir a variabilidade, é usado um campo de jogo plano dividido em células, ou seja, os heróis só podem ficar no centro das células (embora as habilidades sejam geralmente aplicadas em qualquer lugar, sem referência à grade).

Geralmente, 8 pessoas participam de uma partida. Eles são divididos em 2 equipes de 4 pessoas, e cada um controla um personagem. No entanto, devido ao passo a passo, nada impede a criação de um modo no qual apenas 2 pessoas participam, e cada uma controla os quatro personagens de sua equipe.

O jogo é dividido em jogadas. Cada movimento é dividido em duas etapas - decisão (tomada de decisão) e resolução(exibição dos resultados das ações selecionadas). A dinâmica é adicionada pelo fato de que os dois estágios ocorrem para as duas equipes ao mesmo tempo (esse tipo de passo é chamado We-Go) - não existe algo que uma equipe pense e a segunda apenas espere, olhando para a tela onde nada acontece. Em vez disso, ambas as equipes tomam decisões e observam os resultados ao mesmo tempo. A simultaneidade dos movimentos e a presença do campo de visão dos personagens leva ao fato de que o jogo não pode ser atribuído a jogos com informações completas .

Decisão- uma fase em que os jogadores têm uma oportunidade imediata de escolher suas ações desejadas. O campo de jogo "congela" por várias dezenas de segundos, e os jogadores devem escolher quais ações específicas os personagens controlados por eles terão que executar quando o "tempo parar" terminar. Cada jogador vê quais ações seus aliados vão tomar, mas as ações escolhidas pelos oponentes estão ocultas.

Resolução - a fase na qual as ações selecionadas são executadas. Com o tempo, leva quase o mesmo que Decisão, ou um pouco menos. Durante isso, os jogadores não têm controle - eles simplesmente observam o que está acontecendo e pensam sobre as ações para o próximo movimento.

A resolução é dividida em fases. As fases passam sequencialmente e, para cada habilidade, os jogadores sabem com antecedência em que fase ele irá funcionar. Nesse caso, a ordem de operação das habilidades dentro de uma fase não importa.



Como regra, o dano é causado na fase Explosão, então as habilidades defensivas são acionadas mais cedo - você pode aplicar escudos na fase Preparação ou mover (esquivar) na fase Dash. Mas ao se esquivar, o inimigo pode sofrer dano se passar por uma armadilha (definida pelo jogador na fase de preparação). Além disso, se o inimigo não se esquivar, mas simplesmente permanecer no lugar, a armadilha não funcionará e não causará nenhum dano a ele. A mesma coisa com os escudos: se você não atacar um personagem protegido, ele simplesmente queimará no final do turno, sem causar nenhum efeito. O interesse aqui é que, como mencionado acima, as ações dos oponentes no momento da tomada de decisão sejam ocultas e, portanto, devem ser previstas.

O valor típico de dano não excede 35 para facilitar a contagem dos jogadores em suas mentes. Efeitos e habilidades de status são poucos o suficiente pela mesma razão. O principal modo de jogo é o usual deathmatch, que chega a 5 mortes ou 20 jogadas.

Implementação


Decidi escrever em C # (já que esse é meu idioma principal) e escolhi o Unity como o mecanismo (já que não tinha experiência anterior na criação de jogos, mas ele suporta nativamente o C # e é bastante amigável para iniciantes).

Os jogos baseados em turnos não requerem uma grande quantidade de recursos; portanto, desde o início, considerei as opções mais econômicas para hospedar o servidor principal. Havia uma idéia para hospedar no heroku (porque geralmente é gratuito), mas reiniciar o aplicativo aleatoriamente pelo menos uma vez por dia é extremamente inconveniente. Ele parou no VPS com Linux por 45 rublos por mês.

Considerando os recursos limitados na hospedagem (especialmente - muito pouco espaço no disco rígido) e o fato de toda a lógica do jogo funcionar em 2D, decidi fazê-lo no .NET Core (escrevendo meu próprio mecanismo 2D leve para calcular as áreas afetadas de habilidades) e use o Unity exclusivamente para visualização do lado do cliente. Isso tornou possível simplesmente controlar o campo de visão dos caracteres no lado do servidor e enviar aos clientes apenas as informações que eles deveriam conhecer. As ações selecionadas dos jogadores antes do processamento no servidor são validadas, o que exclui completamente a possibilidade de trapaça.

Para maior comodidade dos jogadores, um iniciador simples do WinForms o cortou, que, se necessário, baixa a versão atualizada do cliente do Dropbox.

Agora, vou abordar com mais detalhes os momentos mais interessantes (ou causando dificuldades) que encontramos ao desenvolver o jogo.

O procedimento para usar habilidades


Como mencionado acima, o resultado obtido não deve depender da ordem de aplicação das habilidades dentro da fase. No servidor, as habilidades são usadas em ordem crescente do ID do herói, mas o jogador não deve pensar nisso. Essa condição impõe restrições sobre o que e em que fases podem ocorrer (os designers de jogos são forçados a levar isso em consideração ao criar habilidades).

Por exemplo, uma vez na fase de preparação, você pode aplicar escudos e não causar danos nela (caso contrário, isso dependerá do ID, o dano passará para os escudos ou diretamente para a saúde antes de serem aplicados). Da mesma forma, como o dano pode ser causado na fase Explosão, não é permitido aplicar o efeito de status “Poderoso”, que aumenta o dano causado pelo herói. Regras semelhantes apareceram para todos os elementos do jogo.

Lógica de jogo simples


A própria idéia do jogo é que os jogadores são capazes de calcular mentalmente todo o desenvolvimento de eventos dentro do curso e, portanto, a lógica de todos os elementos do jogo deve ser simples e intuitiva. Mas com a implementação do plano surgiram muitas dificuldades.

Em primeiro lugar, o movimento . Os heróis podem se mover durante as fases Dash e Move, e esse movimento deve ocorrer simultaneamente. É permitido que dois heróis estejam na mesma célula enquanto estão em movimento, mas no final das fases do movimento, não deve permanecer mais de um personagem em cada célula.

A regra mais simples para resolver conflitos no final do movimento é "quem se levantou primeiro e o chinelo". Se o herói já percorreu um longo caminho antes de estar em uma célula "controversa", ele deve ser empurrado para trás, e aquele que percorreu um caminho menor deve ser deixado no lugar. Se os heróis chegaram ao seu destino, tendo percorrido a mesma distância, empurre os dois. Devido ao seu determinismo, decidiu-se empurrar os heróis de volta ao longo das trajetórias de seus movimentos. A repulsão é repetida até que todos os conflitos sejam resolvidos.

O segundo problema é a equivalência . Suponha que desejemos criar um perfil que, ao atingir um inimigo, cause dano a ele e ricocheteie automaticamente para o mais próximopara ele um personagem. O problema aqui é uma combinação de "automaticamente" e "mais próximo". Os heróis estão localizados em um campo quadriculado, o que significa que próximo ao alvo principal na mesma distância mínima, pode haver vários caracteres.

O que fazer?
  • Ricochet em ambos
  • Classificar heróis em ordem alfabética
  • Classificar por ID do jogador
De fato, a solução é que temos mais dados - a posição a partir da qual o tiro foi disparado. Se você não conseguir escolher um alvo mais próximo para o rebote, poderá classificar os heróis no sentido horário, onde o objetivo principal é o centro e o herói que aplicou a habilidade é a posição inicial.

Geometria Honesta


Como se viu, brincar com geometria honesta não é interessante. As paredes são impermeáveis ​​à maioria dos tiros e bloqueiam o campo de visão dos oponentes, além de servirem de abrigo (e se o dano vier do lado do abrigo, será reduzido em 50%). No entanto, as paredes limitam extremamente a área afetada pelo próprio herói, e por isso, os jogadores não viram nenhum motivo para usá-las.

Tempos de pintura

Deixe o círculo vermelho ser o herói e a violeta quadrada na parede. Então a zona de visibilidade (e danos) são células azuis. Acontece que o herói não é capaz de atacar um ângulo de até 90 graus.

Decidiu-se verificar a faixa de visibilidade não apenas entre os centros, mas entre alguns pontos específicos.

Pintar dois


Portanto, o uso de quatro pontos adicionais ao longo das bordas do herói reduziu a "zona morta" para 60 graus (que já se tornou bastante jogável).

Como resultado, as paredes se tornaram realmente úteis.

Pinte três

As células são marcadas em verde, tiros dos quais contra o herói causarão apenas 50% do dano (nem todas as células podem ser atingidas por ele sem ter que romper as paredes do ataque).

Pontos adicionais estão localizados exatamente no meio entre o centro da célula e suas bordas, e essa opção não é acidental. Para ataques retangulares, é muito importante de onde o tiro foi disparado (já que este ponto é usado para calcular rebotes de paredes, explosões e alguns outros modificadores de ataque). Um algoritmo simples tornou possível mudar automaticamente o ponto de partida dentro do herói, dependendo da posição do mouse. Nesse caso, o ponto está localizado de forma a maximizar o alcance do tiro próximo à parede.

Aqui está um link para o gif (20Mb) , onde você pode ver como a mudança automática do ponto de disparo funciona.

Localização


Para adicionar a localização, usei string.Format (para que alterações equilibradas nos números de danos não criassem novas linhas) e uma placa do Google. Todas as strings usadas para exibir na tela são agrupadas em um método que interpola e verifica o dicionário a partir de traduções conhecidas. No início da versão de estréia do jogo, as traduções do Google Plate são carregadas em um arquivo de texto no formato json, e as linhas do código fonte sem traduções são baixadas automaticamente para o mesmo Google Plate na primeira tentativa de exibição na tela. Na versão de lançamento do jogo, por razões óbvias, não há funcionalidade online para trabalhar com a tabela, e as traduções são simplesmente carregadas a partir do arquivo de texto existente na inicialização.

Para os tradutores, a solução não era muito conveniente (porque alterar uma palavra leva à criação de uma nova linha na tabela), mas era muito simples de implementar e não sobrecarregava os programadores em trabalhar com recursos. Seria ideal se as linhas não mudassem durante o processo de desenvolvimento, mas esse não era o caso conosco.

Ancinho


Agora - o mais interessante. Algumas dicas óbvias que podem ajudar programadores iniciantes que decidem interromper seu projeto de estimação em uma equipe de pessoas afins.

  • Se possível, não reinvente a roda.
    Se o problema for generalizado, deve haver muitas soluções prontas para ele. Encontrar uma solução pronta adequada pode levar menos tempo do que escrever você mesmo do zero (especialmente se o problema for complexo).
    . , . , , . NetworkStream-, TcpClient- Json. , , . 30 ( MagicOnion, )

  • — , . — . . — . — .

  • — , . - summary readme. .
  • ,
    . . — , — , , .
  • Git — ,
    , . .
  • ,
    . .
  • ,
    . , , , , . , , , .


Acabou por criar um protótipo jogável em menos de um ano. Parte da comunidade ativa se juntou ao desenvolvimento ou teste, então agora pelo menos algumas partidas acontecem quase todos os dias. É muito legal ver como as pessoas gostam do jogo, na criação da qual participo ativamente.

Pessoalmente, durante o trabalho no projeto, desenvolvi o inglês falado (como a comunidade é internacional), ganhei experiência trabalhando com Linux, programando e usando o Unity. Projetos para animais de estimação são legais.

Com o tempo, adicionaremos definitivamente treinamento para novos jogadores, modelos e animações normais, um menu adequado e uma interface do usuário mais conveniente como um todo. Como bons modelos 3D são muito caros, eles criaram uma conta no Patreon e a comunidade começou a nos apoiar financeiramente.

Links para os interessados ​​na ideia
Discord-. .

, , - — , Atlas Reactor

All Articles