Histórico de participação no Game Jam. Snowbox

imagem No final de 2017, tive a oportunidade de testar minha força e entusiasmo como participante de um dos muitos Game Jams mundiais.

Como essa foi minha primeira experiência em um projeto como esse, aprendi algumas lições úteis e algumas surpresas agradáveis. Bem, eu também ganhei um brinquedo que eu poderia brincar com meus colegas nas férias de sexta-feira.

Sob o gato, uma descrição de quão intensos 30 dias de desenvolvimento e 20 dias lentos de espera por resultados foram.

Nota: o artigo é de natureza narrativa, com poucos detalhes técnicos.

Prólogo


Há muito tempo eu queria me testar como desenvolvedor de jogos e, no último ano, esse desejo se tornou cada vez mais intrusivo e intrusivo. No final do ano, decidi começar a fazer alguma coisa - participar de reuniões sobre desenvolvimento de jogos, ler livros e até fazer um pequeno protótipo do jogo.

E então, um dia, Selim, meu colega, se ofereceu para participar de um dos muitos Game Jam e pensei que essa era a melhor oportunidade para estudar a cozinha de jogos sem obrigações de longo prazo. Este não é um protótipo, mas ainda não é um projeto de longa duração. Além disso, prazos apertados são frequentemente alguns dos melhores motivadores. E a principal vantagem é que, em qualquer caso, o resultado é algo holístico e completo, que sempre inspira novas conquistas.

Então decidimos participar. Dois desenvolvedores de Java, com absolutamente nenhuma experiência em criação de jogos. Mas quando é preciso começar?

Preparação


O Game Jam escolhido era temático, e os organizadores tiveram que anunciar o tópico estritamente antes do início do desenvolvimento. Os competidores receberam 1 mês para criar o jogo.

Registramos uma semana antes do início e, devido à falta de um tópico, passamos um tempo em lânguida antecipação e inação. Talvez fosse melhor gastar esse tempo escolhendo um gênero e uma mecânica de jogo. Em seguida, envolva-o em um estilo adequado.

Na hora X, os organizadores anunciaram o tema do concurso - Retrocesso (retorno) e a contagem regressiva começou.

Selim tinha o único desejo para o jogo - a presença de um servidor de jogo e necessariamente em tempo real. Ele estava interessado em conhecer as delícias e dificuldades do desenvolvimento do lado do servidor. Além disso, o jogo deveria ter sido simples, caso contrário, talvez não tenhamos tempo para concluí-lo.

A respeito do tópico, a princípio, os pensamentos eram sobre tempos retrô ou pré-históricos. O tema retrô não é interessante para mim, então tentei criar algo sobre dinossauros ou pessoas antigas. No entanto, não consegui criar algo simples e multiusuário sobre esse tópico.

E então tive a idéia de fazer um jogo de sandbox, no sentido literal - uma referência ao entretenimento infantil na praia. Construímos castelos, corremos e jogamos areia em nossos rivais (espero que nem todos tenham tido uma infância tão difícil), mas pais insatisfeitos são punidos por isso. Tudo isso em 2D com uma vista superior.

Selim considerou que a areia é muito cruel aos olhos e sugeriu a substituição de areia por neve e castelos por fortalezas de gelo. Nesta opção, paramos. Para o melhor de nossa capacidade e tempo, poderíamos adicionar novas funcionalidades e variedade.

Desenvolvimento. Semana 1


Nossos papéis no projeto foram divididos por eles mesmos. Como eu disse, Selim estava interessado apenas no desenvolvimento de servidores. E eu estava interessado em experimentar o desenvolvimento da interface do usuário, porque, na minha opinião, é mais diferente do desenvolvimento de aplicativos de negócios. A interface do jogo foi planejada na web.

Breve descrição das tecnologias utilizadas
A interação cliente-servidor foi completamente criada em soquetes da Web, porque precisávamos enviar mensagens do servidor para o cliente de forma assíncrona e regular. As mensagens do cliente também foram transmitidas pelo soquete da Web, mas simplesmente pelo motivo "por que não". Todas as mensagens estavam no formato Json.

Para trabalhar com soquetes da Web no servidor, foi utilizada a micro-estrutura Spark. E como um mecanismo físico, o box2d foi usado, como um dos mais populares. Ele foi responsável pelo cálculo da física no servidor.

O cliente foi escrito em JS puro, sem usar nenhuma estrutura, já que não sou forte com eles e, além disso, isso faz pouco sentido para um projeto pequeno. Como mecanismo de jogo, o Phaser 2 foi usado - um mecanismo JS jovem e promissor. Ao contrário do mecanismo físico no servidor, seu principal objetivo era gráficos e cálculos físicos simples. KnockoutJS também foi usado para ligação de dados .

Os produtos JetBrains foram usados ​​como o IDE: Intellij IDEA e uma versão de avaliação do WebStorm (apenas uma versão de 30 dias, no momento do Game Jam).

O desenvolvimento começou com uma reunião conjunta no dia de folga. Nas primeiras 2 horas, eu peguei sprites "temporários" e percebi um garoto correndo que sabia jogar bolas de neve. Poderia ter havido uma imagem de “era / se tornou”, mas é desprovida de qualquer significado - visualmente o que era, então permanece.

Com uma interface de trabalho, restava apenas reforçar a interação do servidor. Portanto, o resto do dia foi dedicado à integração: discussão da API, configuração da conexão, implementação da interação. No final do dia, nosso homenzinho poderia mover 1 pixel para o lado pressionando uma tecla. Foi modesto, mas ainda bem sucedido.

O desenvolvimento continuou apenas no tempo livre do trabalho, com 6 dias por semana separadamente para residências e um dia de folga juntos. Isso foi suficiente graças a uma API simples e a uma distribuição rígida de funções.

Para repetir o sucesso do primeiro dia (correr e jogar bolas de neve), mas já através do servidor, levamos uma semana. Isso foi afetado igualmente por muitos fatores técnicos, incluindo problemas com ferramentas mal projetadas para o nosso modelo de interação cliente-servidor. Eu gostaria de me debruçar sobre esse modelo com mais detalhes.

Modelo de Interação Cliente-Servidor


Inicialmente, foi decidido que o servidor seria responsável por qualquer movimento e o cliente enviaria apenas comandos, como pressionar uma tecla, e desenharia o que veio do servidor. Mais tarde, essa filosofia foi ligeiramente modificada, mas o servidor até o final permaneceu o elo central.

Na primeira implementação, o servidor enviou atualizações para o cliente a cada tick. I.e. por exemplo, se uma tecla for pressionada, a posição do jogador mudará a cada poucos milissegundos e uma nova posição será enviada ao cliente.

Usando um algoritmo tão simples, implementamos o movimento do jogador. Mas buscar o melhor nos levou a alterar o algoritmo para um evento: apenas eventos importantes suficientes para sincronização são enviados ao servidor e ao cliente.

Por exemplo, para mover um jogador no mapa, você precisa de 4 eventos:

  1. Cliente : tecla para cima pressionada;
  2. Servidor : o jogador começou a se mover do ponto [x, y1 ], em um ângulo α, com velocidade ν;
  3. Cliente : tecla para cima pressionada;
  4. Servidor : o jogador terminou de se mover no ponto [x, y2 ].

Essa abordagem tem prós e contras.

Contras

  • muito mais lógica no cliente: o próprio cliente deve calcular o movimento, colisões, etc.
  • a lógica no cliente deve repetir com precisão a lógica no servidor, caso contrário, são obtidos saltos no movimento dos objetos. Além disso, no artigo, haverá vários exemplos sobre esse assunto;
  • em caso de problemas de rede, receber eventos posteriormente pode levar a estados incorretos (sair dos limites, passar por objetos etc.);
  • em geral, é muito mais fácil obter o estado fora de sincronia no servidor e no cliente.

Prós

  • muito menos carga em E / S;
  • lógica adicional pode ser pendurada em eventos. Por exemplo, no início / fim de um movimento, você pode iniciar / parar a animação;
  • problemas de rede afetam menos a suavidade do movimento. Enquanto a bola de neve estiver voando, um atraso de até um segundo não afetará nada, pois o servidor não deve enviar nada;
  • A API e a própria interação se tornam mais transparentes.

A partir dessas listas, fica claro que, se você trabalhar duro na implementação do cliente, haverá quase uma vantagem. Em nosso projeto, conseguimos isso e a interação cliente-servidor funcionou perfeitamente. Mas devemos prestar homenagem, foi preciso muita força e nervosismo.

Desenvolvimento. Semanas 2 e 3


Uma semana depois, quando conseguimos dominar a integração, no mínimo, e dar suporte a vários tipos de mensagens entre o servidor e o cliente, era hora de adicionar mais ao jogo do que apenas um garoto em execução.

Portanto, decidimos adicionar uma garota correndo! No caminho, para a garota, tive que fazer uma janela de seleção de personagens. E como a janela ainda precisava ser feita, é um pecado não adicionar um nome também. Além disso, tive que esticar as imagens, semelhante à abordagem de 9 patches .

imagem
Parecia a primeira versão da janela de login

Por tudo isso, uma tarefa tão simples e importante de adicionar uma garota levou algumas noites, mas claramente valeu a pena. E então veio a vida cotidiana cinzenta com as mesmas tarefas: novas funções simples, correções de bugs, pequenas melhorias visuais.

À medida que meu progresso progredia no lado do cliente, surgia o problema de velocidade de desenvolvimento desigual do servidor e da interface do usuário. E como o servidor é uma parte central de qualquer interação, sem a funcionalidade final e operacional do servidor, o desenvolvimento do cliente foi interrompido.

Portanto, implementei um servidor simulado simples diretamente no cliente. Muitas coisas nele foram implementadas de maneira desajeitada, incluindo através do uso do estado global do mundo do jogo. Isso foi suficiente para desenvolver uma interface do usuário completamente independente do servidor e me poupou muito tempo.

O servidor simulado tinha outra vantagem definitiva. Havia bots nele que não sabiam atirar, então havia pelo menos uma chance de alguém vencer.

Ao mesmo tempo, Selim lutou com o servidor. No servidor, ele conectou o mecanismo de física box2d para simular a física no jogo. Esse mecanismo tem muitas nuances e o estudo deles levou muito tempo. A maior dificuldade no desenvolvimento foi a falta de visualização do mundo do jogo. Nosso cliente processou apenas o necessário. E alguns elementos do "mundo dos servidores" foram ocultados do lado do cliente. Além disso, não havia garantias completas de que o cliente renderize tudo corretamente.

Uma das subtarefas importantes que Selim teve que resolver foi verificar colisões de objetos. No cliente, as colisões foram verificadas apenas para elementos fixos. No servidor, era necessário fazer tudo honestamente para não violar os direitos dos objetos em movimento.

Durante o desenvolvimento de colisões, lembrei-me de um bug engraçado que poderia fingir uma funcionalidade especial: quando um jogador jogava uma bola de neve, ele era jogado para trás com um “recuo”. Isso aconteceu porque no box2d, por padrão, todos podem colidir com todos, e a repulsa sempre acontece.

Esse problema foi resolvido com a introdução de máscaras, ou seja, especificando classes de objetos que não podem colidir entre si. Por exemplo, para um jogador e suas bolas de neve, a máscara era igual.

Selim decidiu não perder tempo lidando com colisões especiais de bolas de neve e comentou sobre essa colisão:

// for the unlikely event that we collide with a sibling snowball

Como a prática demonstrou, a frequência desse evento "improvável" tende à frequência do lançamento de bolas de neve, porque quando você fica em frente um do outro, as trajetórias das bolas de neve coincidem. Por causa disso, bolas de neve alienígenas constantemente se destacam. Nossas opiniões sobre esse comportamento divergiram, então deixamos como está.

Enquanto Selim estava se divertindo com colisões, depurei o movimento síncrono de objetos no servidor e no cliente. Houve pequenos bugs em nossa própria implementação, mas a Phaser apresentou a maior surpresa. Em seu mecanismo de física, a velocidade real dos objetos é ajustada para o FPS e para o mundo. Isso é feito para aumentar a suavidade. Infelizmente, esse comportamento inteligente não era consistente com a operação síncrona em relação ao servidor, e eu tive que fazer minha própria implementação de objetos em movimento.

Descrição das especificidades da velocidade em Phaser ou "corrida com sombra"
Para verificar e depurar a velocidade, adicionei o objeto de outro jogador ao mapa, que se movia com a mesma velocidade, mas por perto. Esse jogador sombra e jogador normal usavam algoritmos de movimento diferentes. Organizei competições e comparei a estabilidade da velocidade.

No começo, tentei resolver o problema da velocidade irregular usando as configurações do motor e a física que usamos. Mas, pelo que entendi, esse comportamento não pode ser alterado de forma alguma. Era possível mudar para uma implementação mais complexa da física, mas eu não queria fazer isso apenas por uma questão de velocidade. Além disso, essa implementação também não deu uma velocidade absolutamente estável.

O próximo passo tentei implementar o movimento de objetos, mas a partir do mecanismo assumi o controle e o delta de tempo entre cada ciclo do mundo. Existem vários modelos de tempo na Phaser e a implementação da velocidade padrão é baseada em um deles. Mas, por alguma razão desconhecida, nenhum desses momentos tem estabilidade e eles não podem fornecer velocidade constante. Esse é um problema conhecido que não está planejado para ser corrigido na versão 2: github.com/photonstorm/phaser/issues/798 .

Passei 2 dias na "competição" do jogador e na sombra e não encontrei uma opção de trabalho. Então, no final, eu fiz todo o meu processamento de velocidade com base no tempo padrão em JS. É muito surpreendente que uma função tão importante tenha recebido um apoio tão estranho no motor e tenha que implementar sua própria bicicleta.

Com essas piadas e piadas, passamos silenciosamente a segunda e a terceira semana. Além disso, a cada semana começava com a frase "bem, na próxima semana somos obrigados a preparar uma versão jogável" - cada vez que nos parecia que estávamos prestes a estar prontos. A completa falta de experiência e a presença de um tremendo otimismo é a pior maneira de avaliar o prazo.

Uma semana antes da entrega, na reunião de desenvolvedores, nem conseguimos mostrar a versão normal e tivemos que mostrar o servidor simulado.

É claro que, com essa velocidade de desenvolvimento, não havia o que sonhar com uma lista de funcionalidades inicialmente concebida, e ainda mais com o prazer de ter coisas. Portanto, tivemos que esquecer a "caixa de areia" e parar no mais simples jogo de tiro em 2D.

Desenvolvimento. Semana 4, última


Na última semana de desenvolvimento, focamos na correção de bugs. É melhor ter algo modesto e funcional do que multifuncional, mas desmoronando. Havia muitos problemas, e a maioria deles dizia respeito à integração infeliz. Aqui e ali, pequenas falhas apareciam, piorando bastante a impressão do jogo.

Além de corrigir bugs, eu também trouxe o brilho final para a interface do usuário: adicionando músicas e sons, tocando com fontes e melhorando pequenos detalhes.

De todas as funcionalidades, nesta semana, apenas o sistema de pontos foi adicionado, além da limitação do estoque de bolas de neve e sua restauração.

No último dia do Game Jam, fomos capazes de brincar um pouco com os colegas. Apesar das críticas positivas em geral, esta sessão de jogo não teve êxito devido a um bug crítico - as bolas de neve voaram no servidor e no cliente em velocidades diferentes. Por causa disso, entrar em outros jogadores era mais provável que fosse um acidente.

Descrição da causa e correção do erro
Cometemos esse bug no último momento, reduzindo o número de FPS no servidor de 1000 experimental para 100.

Deve-se observar que, até o momento, não éramos capazes de obter movimentos totalmente síncronos no cliente e no servidor - ocasionalmente havia saltos em movimento. Ao alterar o FPS, tentamos aumentar a capacidade de resposta do servidor.

Quando comecei a pesquisar esse bug, descobri dois padrões:

  • o movimento do jogador funcionou corretamente e de forma estável;
  • O movimento das bolas de neve no cliente sempre foi mais rápido que no servidor.

O valor da velocidade dos objetos chega ao cliente a partir do servidor, ou seja, não pode ser uma incompatibilidade banal de valores. Além disso, um aumento inverso no FPS para 1000 melhorou a situação.

Passei muito tempo tentando corrigir esse erro. Mas nada ajudou. E no final, o motivo foi encontrado usando o Google - o box2d indiretamente limita a velocidade máxima movendo o objeto em não mais de 2 pixels em uma etapa do mundo. I.e. a 100 FPS, a velocidade máxima é de 200 pixels / s (p / s) e a 1000 FPS - 2000 p / s. Este valor é especificado em uma constante e não pode ser alterado dinamicamente. Isso explicava completamente o motivo da desaceleração de nossas bolas de neve, porque sua velocidade deveria ter sido de 700 p / s, o que exigia um FPS estável acima de 350.

Para corrigir esse problema, aumentei o FPS para 500, mas por um motivo. No box2d, você precisa passar para a função step do mundo quanto tempo se passou desde a etapa anterior. Antes da minha alteração, sempre calculávamos essa diferença antes de chamar a função. Mas agora, sabendo a frequência desejada, sempre era possível indicar um delta constante de 2ms. Com o mundo ficando para trás do real, os passos precisavam ser repetidos um após o outro, até que o tempo do mundo alcançasse o atraso. Então, um pouco de sono e tudo é novo.

Essa correção, como esperado, resolveu o problema de velocidade das bolas de neve. Além disso, o problema com o movimento não síncrono no servidor e no cliente finalmente desapareceu. Naquela época, essa cura milagrosa era uma surpresa completa para nós, mas agora eu entendi o motivo: apesar dos 1000 FPS máximos, ninguém cancelou a operação lenta do servidor e, especialmente, a coleta de lixo. I.e. em alguns momentos, o FPS poderia cair livremente abaixo dos 350 FPS necessários, o que levou a saltos arbitrários na velocidade.

Então, felizes com um bug fechado e um brinquedo funcional, duas horas antes do prazo, estávamos prontos para nos render. Restava apenas enviar o jogo para o site do projeto.

Eu esperava que lançar o jogo fosse tranquilo e em vão. Era necessário criar uma página de projeto, fazer uma descrição, fazer upload de capturas de tela e muito mais. Nós nos conhecemos, como esperado, de ponta a ponta. Embora mais tarde, os organizadores ainda aceitassem projetos atrasados ​​individualmente.

imagem
Captura de tela da versão final do jogo

Votação


De acordo com os termos da competição, imediatamente após a conclusão do desenvolvimento, foi aberta uma votação de 20 dias. Durante esse período, todos puderam ver os projetos concluídos, dos quais mais de 200 foram acumulados, e apenas os participantes tiveram permissão para votar nos projetos. Cada jogo pode ser classificado nas seguintes categorias: geral, gráficos, som, jogabilidade, capacidade de inovação e relevância para o tema.

A fase de votação nos preparou uma surpresa ruim relacionada à natureza multiplayer do nosso jogo. Havia relativamente poucas pessoas assistindo aos jogos e a chance de encontrar o inimigo estava tendendo a zero. I.e. as pessoas foram para um cartão vazio, jogaram algumas bolas de neve, correram alguns metros e saíram desapontadas.

Tentamos organizar sessões de jogos através do fórum do projeto. Além disso, Selim e eu entramos periodicamente no jogo, na esperança de divertir um andarilho entediado e solitário. Tudo isso quase não produziu resultados.

Eu estava interessado em assistir algumas pessoas testando o jogo. Lembro-me especialmente do caso em que um jogador digitou vários caracteres ao mesmo tempo e construiu um pentagrama de meninos. Não sei o que o autor quis dizer, mas ainda tenho uma captura de tela do processo de criação.

imagem

Outro jogador "invadiu" o nosso jogo. Temos uma verificação de tamanho de nome, mas apenas no lado do cliente. Consequentemente, ele contornou essa defesa e começou a se corresponder dessa maneira, cada vez que entrava em um novo personagem e entrava em sua frase em nome de um homem.

Junto com a votação, meus colegas e eu jogamos novamente, nos vingando pelo jogo mal sucedido no dia em que o projeto foi concluído. Desta vez, tudo correu muito bem e tivemos várias idéias para novas funções, que, no entanto, era muito tarde para adicionar.

Na minha opinião, a qualidade do jogo, principalmente em relação à interação com o servidor, resultou em um nível muito bom. Quantas vezes jogamos, não percebemos nenhum problema, nem os ouvimos de outros jogadores. Para mim, pessoalmente, isso foi uma surpresa, dada a qualidade e o número de erros alguns dias antes da entrega.

Os 20 dias dados para a avaliação dos jogos duraram muito tempo, mas no final eles terminaram e chegou o tão esperado tempo de resultados.

imagem

Assim, ocupamos o 36º lugar entre pouco mais de 200 participantes. O que, por um lado, não é ruim para o primeiro projeto, mas, por outro lado, é um tanto desagradável para o orgulho. Especialmente considerando que o top 10 conseguiu bons jogos, mas nem todos mereciam atenção especial.

Lições aprendidas


A fim de obter lições em condições de semi-estufa, tudo isso foi iniciado. Tentamos inventar muitas coisas sozinhos e nos limitamos a um conjunto mínimo de ferramentas para sentir os problemas e experimentar más abordagens em nossa própria pele. Mas agora, tendo conhecimento sobre como fazê-lo e por que, estudar a teoria será mais fácil.

A necessidade de um artista . Como a prática demonstrou (não apenas a nossa), você pode fazer um bom jogo sem bons gráficos. No entanto, a seleção das figuras, fontes e elementos da interface do usuário corretos ocupa a maior parte do tempo. E o pior é que, no final, eles não coincidem. Com isso, a atmosfera do tubo quente é perdida e o jogo não parece holístico.

Testes de jogo são muito importantes . Devido a problemas com a velocidade de desenvolvimento, não conseguimos testar a jogabilidade de nossa implementação na maioria das vezes. Quando conseguimos jogar normalmente, havia muito pouco tempo para consertar as áreas problemáticas. E tivemos muita sorte por não haver tantos problemas assim.
Na minha opinião, para jogos, esses testes em usuários reais são muito mais importantes do que em aplicativos de negócios, porque, além de conveniência e resolução de problemas do usuário, ainda é necessário manter um certo nível de emoções e envolvimento.

Nem todas as bibliotecas são igualmente úteis . Quase ninguém desenvolve jogos em seu próprio mecanismo, e existem mecanismos no mercado para todas as ocasiões. No quintal era 2017, e seria de esperar sua alta qualidade. Eu escolhi a Phaser como um dos mecanismos JS mais jovens e recomendados. Depois de todos os problemas que tivemos com ele, tenho medo de imaginar como os motores parecem piores. Não, em geral, as impressões de trabalhar com a Phaser são bastante positivas, especialmente considerando a boa documentação e exemplos. Mas trabalhar com ele sem conhecer um grande número de nuances é muito difícil. Na primavera, uma nova versão será lançada e espero uma melhoria significativa. E também nos meus planos há um miniprojeto em outro mecanismo para comparar.
E lembrarei desse problema com velocidade máxima no box2d por um longo tempo.

No processo do Game Jam, é bem possível aprender . Iniciando este projeto, não sabíamos quase nada sobre o desenvolvimento de jogos, nem sobre as bibliotecas que usamos. E a maior parte do tempo era gasta estudando essas coisas. Apesar disso, ainda conseguimos levar o jogo a um estado completo.

Não é necessária muita funcionalidade . Estou um pouco surpreso que muitas pessoas gostaram do nosso jogo. Sim, eles não ficam sentados todas as noites, mas desfrutam de uma ou duas pequenas sessões. Mas em nosso jogo não há uma idéia original, nem uma grande quantidade de funcionalidades, nem qualquer história. O mesmo pode ser dito da maioria dos jogos no Game Jam que receberam classificações positivas.

(Jogo) Jam é um ótimo motivo para tentar . Não importa o que: uma ideia, uma nova biblioteca, seus próprios pontos fortes. Quando existe um objetivo claro e outras pessoas veem seu resultado, é muito motivador não ficar mole e dar o melhor de si. E mesmo que o resultado seja pior do que o esperado, não será uma pena jogá-lo fora, aprender lições por si mesmo e seguir em frente!

Links para recursos:



Obrigado a todos pelo seu tempo e bom humor!

Source: https://habr.com/ru/post/pt409679/


All Articles