14 pontos por cybrshin 2026-01-04 | 9 comentários | Compartilhar no WhatsApp

Eu usava claude code e cursor, mas depois de sair da empresa no ano passado, mudei para antigravity em novembro e vinha fazendo vários projetos de toy web service, praticamente um por dia. Aí, de repente, 3 dias atrás (2/1), fiquei com vontade de tentar criar um jogo de tower defense.
Originalmente sou desenvolvedor de frontend web, então fiquei com preguiça de empacotar como app e decidi fazer algo que funcionasse só em ambiente de navegador desktop. Como achei que uma implementação baseada em canvas e trabalho com arquivos de imagem levaria tempo demais, comecei com a ideia de aproveitar emojis ao máximo.

Eu queria terminar em um dia, mas conforme fui fazendo, ficou divertido e acabei ajustando/adicionando várias coisas, então levou 3 dias. T_T

A stack técnica básica é a seguinte.

  • Frontend: Next.js 14+ (App Router), TypeScript, HTML5 Canvas
  • Backend: FastAPI, Python 3.9+, Pydantic
  • Database: Supabase (PostgreSQL)
  • Deployment: Vercel

Depois de vários ajustes, a lógica final do jogo ficou definida assim.

1. Poder de ataque das torres

As torres aumentam o alcance (Range) em 10 a cada subida de nível via Merge, mas a eficiência do poder de ataque é ajustada para 1,8x (balanceamento).

  • Fórmula: Damage = floor(BaseDamage * (1.8 ^ (Level - 1)))
    • Lv 1: 10
    • Lv 2: 18
    • Lv 3: 32
    • Lv 4: 57

2. Padrão de surgimento dos inimigos (Wave Logic)

Para evitar monotonia, foram aplicados vários padrões de wave.

  • Wave normal (Horde Mix): aparecem misturados inimigos do tier atual (70%) e do tier anterior (30%).
  • Wave de chefe (a cada 6ª): surge 1 inimigo chefe mais forte da era, com lacaios ao redor fazendo escolta.
    • Número de inimigos na wave de chefe: Wave / 6 (1 no wave 6, 2 no wave 12...)

4. Recompensa em ouro (Active)

Ao derrotar inimigos ou limpar uma wave, você ganha ouro.

ps. Não considerei muito o ambiente mobile. Adicionei eventos de toque, então funciona bem em Fold ou tablet, mas não pensei muito em telas pequenas~

ps. Se me avisarem nos comentários sobre melhorias ou bugs, vou aplicando quando tiver tempo.

https://tower.dsp.ai.kr/

9 comentários

 
wancomplete 2026-01-07

Eu também experimentei usar o https://verse8.io recentemente enquanto fazia um jogo, e ele até cria as imagens e as animações de sprites 2D, então consegui focar só na lógica do jogo sem gastar tempo com a produção de assets. Além disso, como dá para publicar direto pela própria plataforma, foi muito prático não precisar me preocupar com hospedagem separada.

 
huiya 2026-01-06

É divertido. Achei bem original o uso ativo de emojis.
Como eu não costumo jogar muito tower defense, não sei se esta é uma opinião válida, mas me parece estranho que não haja separação entre o tempo da onda e o tempo de preparação, a ponto de ser possível mudar a posição das torres até no momento em que a onda está chegando.
Num caso extremo, dá para ficar mudando de posição o tempo todo e fazer com que ataquem a cada instante, então sinto que o alcance acaba perdendo o sentido.

 
cybrshin 2026-01-06

Obrigado por se divertirem com o jogo.
Enquanto eu também estava fazendo, senti que mover a posição parecia estranho, mas achei que isso poderia ser, à sua maneira, um ponto forte deste jogo e um elemento dinâmico e controlável, então deixei assim.
Na verdade, para mover rápido, é muito difícil se você não fizer merge das torres.
Acho que o balanceamento do jogo, como por exemplo adicionar uma penalidade por mover, vai continuar sendo um ponto para refletir.

 
huiya 2026-01-07

Ah, você já tinha considerado tudo. Fui precipitado.

 
cybrshin 2026-01-07

Não. Obrigado pela opinião.

 
waylake 2026-01-04

Olá, joguei e me diverti bastante. Estou enviando um feedback relacionado à segurança.

  • Atualmente, o segredo HMAC (chave de assinatura) está exposto no frontend
    • No frontend, é possível alterar diretamente gold, wave, lives e towers

Também estou avisando porque pode haver um problema com os custos do plano da Vercel e do Supabase.

const state = {  
  towers: [],    
  gold: 9999,  
  wave: 1,       
  lives: 1       
};  
  
const state_raw = JSON.stringify(state);  
const user_id = "11111";  
const secret = "11111";  
  
(async () => {  
  const key = await crypto.subtle.importKey(  
    "raw",  
    new TextEncoder().encode(secret),  
    { name: "HMAC", hash: "SHA-256" },  
    false,  
    ["sign"]  
  );  
  const signatureBuffer = await crypto.subtle.sign(  
    "HMAC",  
    key,  
    new TextEncoder().encode(state_raw)  
  );  
  const signature = Array.from(new Uint8Array(signatureBuffer))  
    .map(b => b.toString(16).padStart(2, "0")).join("");  
    
  fetch("https://tower.dsp.ai.kr/api/sync";, {  
    method: "POST",  
    headers: {  
      "Content-Type": "application/json",  
      "Accept": "*/*"  
    },  
    body: JSON.stringify({  
      user_id,  
      state_raw,  
      signature  
    })  
  })  
  .then(res => res.text())  
  .then(console.log)  
  .catch(console.error);  
})();  
  
 
cybrshin 2026-01-04

Ah, obrigada~

 
roxie 2026-02-27

Não, isso não faz sentido... 700 waves............???

 
cybrshin 2026-02-28

É possível hehe