Como criamos o Purrfect Code: um quebra-cabeças para desenvolvedores

AGO 01, 2024

Link to Youtube Video (visible only when JS is disabled)

O "Purrfect Code" é um novo quebra-cabeças de programação com caixas que devem empurradas. Ele é habilitado pelo Flutter, pelo Dart e pelo mecanismo de jogo Flame e foi projetado para desafiar os desenvolvedores a resolverem o quebra-cabeças por meio da escrita de código.


Visão geral do jogo

No "Purrfect Code", os jogadores têm a tarefa de atualizar a programação de um robô zelador na nova sede da estação espacial do Google, onde um carregamento de gatos chegou por engano. A meta do jogo é ajudar o robô a juntar, com segurança, as caixas que contêm os gatos e empurrá-las até as placas de teletransporte para que eles possam voltar para casa. Os jogadores escrevem em JavaScript para controlar os movimentos do robô enquanto tentam encontrar soluções eficazes para navegar pelos quebra-cabeças baseados em grade.

O jogo é uma forma divertida de explorar a codificação usando inteligência e criatividade. Cada nível tem como foco um conceito de programação, e a complexidade aumenta gradualmente ao longo dos cinco níveis do jogo.

Este é o loop de jogo:

  1. Os jogadores escrevem código JavaScript para movimentar o robô (para cima, para baixo, para a esquerda ou para a direita).

2. Depois, eles executam o código e observam o robô tentando resolver o nível.

3. O nível é concluído quando todas as caixas que contêm os gatos são empurradas até as placas de teletransporte.

4. Os jogadores recebem pontuações com base na eficiência de suas soluções, incluindo o número de espaços movidos e a brevidade do código.

Criação do jogo com o Flutter, o Dart e o Flame

Optamos por criar o "Purrfect Code" usando o Flutter e o Dart, pois eles fornecem um framework avançado e flexível para a criação de aplicativos e jogos multiplataforma. Com o sistema de widgets e o modelo de programação reativa do Flutter, conseguimos criar uma interface do usuário responsiva para uma variedade de tamanhos de tela no navegador. Os recursos de programação fortemente tipada e orientada por objetos do Dart facilitaram a estruturação do código e a boa organização da base de código. O mecanismo de jogo Flame, baseado no Flutter, ofereceu uma boa base de recursos essenciais necessários para o desenvolvimento do jogo, permitindo que nos concentrássemos em nossa lógica de jogabilidade e nos aspectos exclusivos do jogo. O Flutter e o Flame foram uma escolha óbvia para esse projeto porque o Purrfect Code é um híbrido de jogo/app exclusivo, com uma IU inspirada em ambiente de desenvolvimento integrado e uma visualização de jogo com som e sprites animados.


Chrome e JavaScript: uso do suporte integrado do navegador

Depois de definir o conceito de programação do jogo, precisávamos escolher a linguagem de programação que os jogadores usariam. Queríamos algo familiar e popular entre os desenvolvedores. Inicialmente, consideramos o uso do Python como linguagem de programação no jogo. No entanto, após algumas considerações, decidimos usar o JavaScript para aproveitar o suporte integrado ao JavaScript que o Chrome fornece. Ao utilizar o JavaScript, conseguimos acessar os recursos nativos do navegador sem a necessidade de carregar um interpretador de linguagem adicional. Essa decisão simplificou nosso processo de desenvolvimento e garantiu uma experiência perfeita para os jogadores, pois o jogo poderia ser carregado rapidamente com o mínimo de dependências.

A versão 3.22 do Flutter lançada no Google I/O deste ano introduziu o suporte estável ao WebAssembly (WASM), o que nos permitiu otimizar as partes críticas para o desempenho do jogo. Ao compilar certas lógicas de jogo para o WASM, garantimos que o "Purrfect Code" seja executado com eficiência no navegador, proporcionando uma experiência de jogo perfeita e responsiva sem comprometer o desempenho.

var dir = [moveEast,moveNorth,moveWest,moveSouth];
for(i=0;i<4;i++){
    for(j=0;j<5;j++)dir[i]();
}

Mecanismo de jogo Flame: desenvolvimento de jogos modular e eficiente

Para dar vida ao "Purrfect Code", utilizamos o Flame, um mecanismo de jogo modular de código aberto baseado no Flutter que fornece muitos dos recursos comumente necessários para o desenvolvimento de jogos. O Flame usa a infraestrutura robusta do Flutter e simplifica o código necessário para criar nosso projeto. Ele oferece uma implementação de loop de jogo simples, porém eficaz, e uma ampla variedade de funcionalidades essenciais, como reprodução de áudio, gerenciamento de sprites, recursos de animação, colisão e o Flame Component System (FCS). Com a arquitetura do mecanismo baseada em componentes, a renderização de sprites e o suporte a animações, conseguimos criar gráficos visualmente atrativos, animações fluidas e elementos de jogabilidade interativos, sem precisar reinventar a roda. O uso do Flame tornou o processo de desenvolvimento mais eficiente com recursos essenciais prontamente disponíveis, permitindo que nos concentrássemos na criação de uma experiência de jogo envolvente para os jogadores.


Implementação dos recursos do jogo com o Flame

O Flame oferece uma implementação de loop de jogo simples, porém eficaz, e uma ampla variedade de funcionalidades essenciais, como reprodução de áudio, gerenciamento de sprites, recursos de animação, colisão e o Flame Component System (FCS).

O sistema de renderização e animação de sprites do Flame nos permitiu dar vida às personagens e aos ambientes do jogo sem a necessidade de escrever código gráfico. Conseguimos criar folhas de sprites, definir sequências de animação e animar facilmente movimentos e efeitos especiais das personagens. Usamos o sistema de prioridades do Flame para escrever um sistema de classificação visual para nossa perspectiva de jogo "de cima para baixo". Nosso artista criou níveis com muitos recursos sobrepostos para melhorar a sensação de profundidade com os sprites e conferir à visualização do jogo uma aparência menos "gradeada". Precisávamos garantir que, quando o robô se movimentasse atrás desses elementos, ele fosse ocultado corretamente. O sistema de prioridades do Flame nos permitiu atribuir prioridades a diferentes elementos visuais, garantindo que eles fossem desenhados na ordem correta e sobrepostos adequadamente. Esse sistema foi flexível o suficiente para incluirmos um sistema de sombras no qual componentes individuais pudessem apresentar sombras animadas que espelhassem ações de objetos e dessem à arte uma sensação de profundidade para tornar as cenas mais vibrantes e mais fáceis de compreender visualmente.

A classe BoxShadow em nossa base de código é um bom exemplo disso e mostra como criamos sombras dinâmicas e interativas para os objetos de caixa em movimento do jogo. Ao estender o SpriteAnimationComponent e implementar os mixins GridElement e HasVisibility, conseguimos carregar folhas de sprites das animações de sombra e de teletransporte de caixas, definir várias animações para os estados "aberta", "fechada", "inativa" e "em teletransporte" e integrá-las ao layout baseado em grade. O método onLoad carrega as animações e define a posição inicial e a prioridade do componente com base em sua posição na grade, enquanto o método update garante que a prioridade do componente seja atualizada dinamicamente se a caixa se movimentar na frente ou atrás de um objeto oclusivo. Usar os sistemas de prioridade e animação do Flame dessa maneira nos permitiu criar efeitos de sombra que sempre ajudam os usuários a entender visualmente os espaços virtuais, tornando-os mais verossímeis e levando a uma apresentação visual mais integrada.

Trecho de box_shadow.dart; veja a classe completa no GitHub para saber mais.

@override
  Future<void> onLoad() async {
    await _loadAnimations().then((_) => {animation = _boxClosed});
 
 
    position.add(Vector2(
        ((gridPosition.x * gridPixelDimensions.x) + gridPixelOffset.x),
        ((gridPosition.y * gridPixelDimensions.y) + gridPixelOffset.y)));
    priority = getLayeredGridValue();
  }
  @override
  void update(double dt) async {
    super.update(dt);
 
 
    if (getLayeredGridValue() != priority) {
      priority = getLayeredGridValue();
    }
  }

Selos e programa Google Developers

Como queríamos manter o escopo do jogo pequeno e conseguir implantá-lo como uma página da Web simples, optamos por evitar a configuração de um back-end para o jogo. No entanto, precisávamos encontrar uma forma de premiar os desenvolvedores pelo progresso obtido, algo semelhante aos sistemas de conquistas em plataformas de jogos populares. Em vez de criar um sistema de back-end separado para acompanhar o progresso e as conquistas dos jogadores, fizemos uma integração com o programa Google Developers, permitindo que os jogadores cliquem em um link e coletem um selo em seu perfil depois de concluírem um nível, o que dá uma sensação de realização e reconhecimento dentro da comunidade de desenvolvedores.

Purrfect Code Google Developer Program profile badges

Project IDX: um ambiente de desenvolvimento simplificado

Durante o desenvolvimento do "Purrfect Code", aproveitamos o Project IDX do Google, um espaço de trabalho assistido por IA para desenvolvimento de aplicativos full-stack e multiplataforma na nuvem. Como já estávamos confortáveis ​​trabalhando no VS Code, o Project IDX forneceu um ambiente familiar para codificação, depuração e teste de nosso jogo e nos permitiu começar a trabalhar rapidamente. Com o Flutter e o Dart já configurados e prontos para uso no navegador, poderíamos mergulhar direto no desenvolvimento sem o incômodo de configuração do ambiente local. A conclusão inteligente de código, a verificação de erros em tempo real e as ferramentas de depuração integradas oferecidas pelo Project IDX nos ajudaram a manter nossa produtividade alta. Se você estiver curioso, o Project IDX é uma ótima maneira de experimentar rapidamente o Purrfect Code e explorar seu código diretamente do seu navegador. Clique neste link para abrir o projeto diretamente no IDX e executá-lo você mesmo. Certifique-se de marcar a caixa que pergunta se este é um aplicativo Flutter.


Firebase para hospedagem rápida e segura

Escolhemos o Firebase Hosting para garantir a entrega global segura e eficiente do Purrfect Code. O SSL de configuração zero da plataforma garante que o conteúdo seja disponibilizado por HTTPS, o que aumenta a segurança. Além disso, seu suporte a frameworks da Web modernos e versões automatizadas de nosso repositório GitHub possibilitou a rápida implantação de atualizações. A CLI do Firebase, a emulação local e os URLs de pré-lançamento simplificaram nossos processos de teste e colaboração. Esses recursos, em conjunto com o potencial de futuras evoluções do jogo usando os modelos de exemplo do Gemini para integração de IA, tornaram o Firebase Hosting a escolha ideal para o lançamento de nosso jogo.


Configuração do Firebase com WASM

O Purrfect Code usa o WebAssembly, que requer algumas etapas adicionais durante a implantação. Em nossa configuração firebase.json, adicionamos um comando de pré-implantação que permite que a versão da Web seja criada com o WASM. O argumento "--no-strip-wasm" impede que o código seja executado por meio de uma última etapa de minimização, o que dificulta a leitura e depuração dos erros. O WASM também requer uma política de abertura de origem cruzada e uma política de incorporação de origem cruzada para vários threads e compartilhamento de memória.

"hosting": {
      "predeploy": "flutter build web --wasm",
      "public": "build/web",
      "ignore": [
        "firebase.json",
        "**/.*"
      ],
      "headers": [
        {
          "source": "**/*",
          "headers": [
            {
              "key": "cross-origin-opener-policy",
              "value": "same-origin"
            },
            {
              "key": "cross-origin-embedder-policy",
              "value": "require-corp"
            }
          ]
        }
      ]
    }

Aprendizado e recursos

Ao longo do desenvolvimento do "Purrfect Code", contamos com vários recursos e nos inspiramos em projetos existentes, um conjunto de obras para o qual esperamos que esse projeto possa contribuir. Estes são alguns dos principais recursos de aprendizado e as referências que consideramos importantes:


Projetos de referência

Usamos os jogos Super Dash e I/O Flip como referências de práticas recomendadas e ideias de implementação. Ambos os projetos forneceram insights valiosos sobre a estruturação de um jogo Flutter, o tratamento de estados do jogo e a implementação de mecânicas de jogo. O Super Dash foi muito relevante porque era simples e não exigia serviços de back-end, assim como o nosso jogo. O I/O Flip era maior e tinha suporte a um back-end, bem como a recursos de IA generativa, portanto pode ser de interesse para os desenvolvedores de jogos que exigem esses recursos. Ambos serão recursos valiosos se você tiver interesse em criar jogos no Flutter.


Conclusão

Esperamos que "Purrfect Code" não apenas forneça uma experiência de jogo agradável, mas também sirva como um recurso de aprendizagem para desenvolvedores interessados ​​no desenvolvimento de jogos com Flutter e Flame. A combinação de Flutter, Flame e Chrome provou ser uma ótima opção para nosso híbrido jogo/aplicativo, fornecendo-nos uma base sólida para desenvolvimento de UI, renderização gráfica, gerenciamento de som e muito mais. Encorajamos você a explorar o código-fonte do jogo e experimentar estendê-lo ainda mais. Existem inúmeras possibilidades para adicionar novos recursos, níveis e mecânicas de jogo. Entre na base de código do Projeto IDX, marque a caixa que pergunta se este é um aplicativo Flutter e deixe sua criatividade correr solta!