ToolActToolAct

Jogo de Alfinete (Pin Spin)

Atire agulhas em um disco giratório sem encostar nas outras — 10 fases com velocidade crescente

1
Fase atual
10
Total de fases
8
Agulhas restantes
1
Melhor fase
Fase 1 / 10
Agulhas restantes: 8
8

O que é o Jogo de Alfinete?

Pin Spin é um jogo casual de navegador da mesma família dos sucessos mobile AA e Pin Out. Ao centro da tela, um disco branco gira em velocidade constante e você dispara agulhas uma a uma a partir de baixo. Cada agulha precisa fincar no disco sem encostar em nenhuma das que já estão fincadas. Cada fase dá uma quantidade fixa de agulhas: encaixe todas para avançar, e qualquer colisão encerra a rodada na hora. As regras são simples o bastante para qualquer um pegar em segundos, mas a curva de dificuldade rapidamente vira um teste sério de timing e autocontrole. A ferramenta roda inteira no navegador, sem cadastro nem download — clique com o mouse no desktop, toque a tela no celular.

Como Usar

Passo a passo

  1. Ao abrir a página, o jogo começa automaticamente na fase 1 com o disco já girando
  2. Clique na área de jogo (ou pressione Espaço ou Enter) para disparar uma agulha
  3. A agulha voa até o centro e finca no disco; ela não pode encostar em nenhuma agulha já fincada
  4. Coloque todas as agulhas da fase com segurança para passar para a próxima
  5. Qualquer colisão encerra a rodada — escolha «Repetir fase» ou «Reiniciar» para continuar (observação: pressionar Espaço / Enter na tela de fim de jogo dispara «Reiniciar» e leva você de volta à fase 1, não «Repetir fase» — use o botão se quiser refazer a mesma fase)

Dicas para concluir as fases

  • O disco gira em velocidade constante, então o ritmo importa mais do que a rapidez do clique: mire na linha do meio do espaço.
  • Atire logo depois que uma agulha vizinha passou; a agulha leva uns 120 ms para chegar, então adiante um pouquinho.
  • Até 3 disparos ficam em fila e saem em sequência; cliques além desse limite são descartados, não enfileirados, então não adianta martelar o clique.
  • Nas fases avançadas os espaços ficam estreitíssimos e o giro acelera bastante; pular um disparo arriscado é melhor que uma colisão por ganância.

Dificuldade por fase

  • Fases 1-3: muitas agulhas, espaços largos — bom para pegar o ritmo.
  • Fases 4-7: já há mais agulhas no disco e é preciso prever a rotação.
  • Fases 8-10: o disco fica quase cheio, todo disparo precisa de timing milimétrico.

Casos de uso

Pausa rápida durante o trabalho ou os estudosUma partida completa de 10 fases dura por volta de 3 a 5 minutos e perder quase não pesa — perfeito para os minutos antes de uma reunião ou para o intervalo logo depois de fechar um trecho de código. O visual é um look monocromático limpo, sem pop-ups de anúncio nem sons gritantes, então deixar a aba aberta num cantinho não atrapalha ninguém.
Treinar coordenação motora visual e senso de ritmoNo fundo é um exercício de timing dentro de um referencial girando a velocidade constante. Para passar com regularidade, você cria um metrônomo interno: dispara assim que uma agulha cruza a linha-alvo, levando em conta os 120 ms de voo. A habilidade transfere bem para jogos de ação e para o ritmo musical, e vai um passo além de um simples teste de tempo de reação.
Mini competição na sala de aula ou em team buildingAbra o URL em uma tela grande e deixe cada um jogar uma rodada — vence quem terminar todas as 10 fases primeiro ou quem chegar mais longe. As regras servem para qualquer idade e, ao contrário de jogos mobile completos, não precisa de cadastro. Ótimo como quebra-gelo ou como atividade leve numa aula de informática. Os resultados ficam apenas no navegador local, nada é registrado.
Roda em notebooks fracos e celulares antigosO jogo todo usa apenas nós DOM e CSS transforms — cada quadro atualiza no máximo 15 valores rotate(deg), sem precisar de canvas. Mantém 60 fps estáveis em um notebook velho de 4 GB, em um Android de entrada ou em um computador escolar. Sem dependência de backend, segue funcionando offline também.
Exemplo didático de animação e loop de jogo no front-endPara quem aprende front-end, é um caso compacto e completo de loop principal com requestAnimationFrame + detecção de colisão + máquina de estados: rotação, disparo, julgamento, buffer de disparos e fim de rodada vivem todos em um único componente. Útil para observar a composição em GPU dos transforms, comparar transform com left/top em desempenho e mostrar por que useRef é preferível a useState em animações de alta frequência.

Princípio técnico

O loop principal é dirigido por requestAnimationFrame. A cada quadro, um valor interno rotation é incrementado por um speed fixo (graus por quadro) e, em seguida, todas as agulhas fincadas são percorridas para receber transform: rotate(baseAngle + rotation). Como transform é uma propriedade de camada de composição, o navegador entrega a rotação à thread de composição da GPU sem disparar layout nem paint, então a fase final, com todas as 15 agulhas girando, ainda mantém 60 fps com folga. A detecção de colisão usa a abordagem «diferença de ângulo convertida em comprimento de corda». Cada agulha fincada é representada por seu baseAngle no sistema de coordenadas do disco (um deslocamento fixo em relação ao disco, que não muda com a rotação). Quando uma agulha disparada chega ao centro, seu ângulo em coordenadas do mundo é TARGET_ANGLE = 0, o que mapeia em coordenadas do disco como normalizeAngle(0 - rotation). O novo baseAngle é então comparado a todos os baseAngle já existentes: a diferença angular Δθ é convertida no comprimento de corda 2R·sin(Δθ/2) entre os centros dos pontos da ponta das agulhas (R = 111px do centro de cada ponto-ponta até o centro do disco). Se ficar abaixo do limiar de 24px, o disparo é considerado colisão e a rodada termina. O disparo é regulado por uma pequena máquina de estados. shootLocked marca uma agulha como «em voo» e impede que tanto o evento transitionend quanto o setTimeout de reserva de 180 ms encerrem o mesmo disparo duas vezes. A transição CSS dura 120ms; o setTimeout de 180ms serve como rede de segurança para o caso raro em que transitionend não dispara (por exemplo, quando o DOM é alterado antes que a transição se complete). pendingShots enfileira os cliques recebidos enquanto uma agulha ainda está no ar, com limite de 3 — cliques além desse teto são descartados (não enfileirados), preservando a sensação de tiro rápido sem deixar várias agulhas convergirem ao centro ao mesmo tempo. Tanto a falha quanto a vitória definem gameOver como true: o loop principal continua rodando, mas para de atualizar rotation, congelando a cena. Para escalar a exibição, o jogo mantém um tamanho de design fixo de 420×720, e o invólucro externo calcula um fator de escala a partir do viewport e o aplica via transform: scale. A implementação atual limita a escala a 1, então viewports pequenos são reduzidos proporcionalmente enquanto viewports de desktop mantêm o tamanho original — não aumentam. Assim a detecção de colisão sempre opera no sistema de coordenadas de design com um único conjunto de limiares em pixels, e o veredito é idêntico em qualquer tela.

  • Loop principal: requestAnimationFrame, a cada quadro rotation += speed, todas as agulhas recebem transform: rotate(baseAngle + rotation).
  • Aceleração por GPU: a rotação usa transform em vez de left/top, então o compositor do navegador cuida disso — sem layout nem repintura.
  • Detecção de impacto: o 0° em coordenadas do mundo é projetado de volta para coordenadas do disco como normalizeAngle(0 - rotation); o comprimento de corda 2R·sin(Δθ/2) é comparado a um limiar de 24px.
  • Máquina de estados: shootLocked impede dupla finalização, pendingShots é limitado a 3 com excedente descartado (não enfileirado), gameOver congela as atualizações de rotação.
  • Finalização do disparo: voo CSS de 120ms, transitionend como caminho principal + setTimeout de 180ms como rede de segurança para o caso raro em que transitionend não dispara.
  • Escalonamento: sistema de design fixo 420×720; o invólucro aplica transform: scale conforme o tamanho do viewport, limitado a 1 para que desktops mantenham o tamanho original em vez de ampliar.
  • Armazenamento: somente a melhor fase concluída é salva em localStorage. Nada é enviado ao servidor; sem contas, sem placar.

Exemplos

Velocidade e quantidade de agulhas nas 10 fases

Fase   Início Disparos Velocidade (deg/frame)
1      2      8        1.35
2      3      9        1.50
3      3      10       1.65
4      4      11       1.80
5      4      12       2.00
6      5      13       2.20
7      5      14       2.45
8      6      15       2.70
9      7      15       3.00
10     8      15       3.35

Uma rodada típica: falha na fase 5

Fase 5: 4 agulhas iniciais, 12 a disparar.
Disparos 1-7: encaixam direitinho, restam 5.
Disparo 8: feito antes da agulha anterior se acomodar, cedo demais.
Veredito: novo baseAngle a 8° do disparo 6, abaixo de DOT_HIT_DISTANCE.
Resultado: colisão, rodada termina com «Fim de jogo!».
Clique em «Repetir fase» para recomeçar a fase 5.

Resumo de uma rodada completa

Tempo de conclusão: cerca de 2min 40s
Disparos no total: 117
Derrotas: 3
Melhor fase: 10
Mensagem: «Todas as fases concluídas!», botão exibe «Jogar de novo».
A melhor fase é gravada em localStorage e persiste mesmo recarregando a página.

Perguntas frequentes

Cliquei em um espaço vazio, por que perdi mesmo assim?

A agulha não chega instantaneamente. Do clique até o contato real com o disco passam uns 120 ms, e o disco continua girando nesse intervalo. Se o vão estava largo no momento do clique, mas outra agulha girou até a linha-alvo durante o voo, a nova cai em cima dela. A solução simples é disparar logo depois que um vão cruzou a linha, deixando uma folga para a rotação.

Posso segurar a tela para tiro contínuo?

Você pode clicar rápido, mas o disparo é controlado: nenhum sai enquanto o anterior estiver no ar. Até 3 cliques durante o voo ficam em fila e saem automaticamente em sequência; qualquer coisa além disso é descartada, não enfileirada. Nas fases avançadas, é melhor reduzir o ritmo e deixar cada agulha se acomodar antes do próximo disparo, em vez de martelar o clique.

Como a dificuldade aumenta?

Cada fase tem sua própria velocidade de rotação, número de agulhas iniciais e total de disparos exigidos. A velocidade vai de 1,35 deg/frame na fase 1 até 3,35 deg/frame na fase 10 — cerca de 2,5×, sem chegar ao triplo. As agulhas iniciais sobem de 2 para 8, então os vãos sobreviventes encolhem. Nas fases finais, paciência conta mais do que velocidade pura.

Depois de perder, recomeço do zero ou repito a mesma fase?

As duas opções estão disponíveis, mas o que aciona cada uma importa. O botão «Repetir fase» reinicia a fase em que você perdeu, com o mesmo número de agulhas e a mesma velocidade; o botão «Reiniciar» — assim como pressionar Espaço / Enter na tela de fim de jogo — leva você de volta à fase 1. Não há atalho de teclado para «Repetir fase»; quem aperta Espaço por reflexo para continuar acaba caindo na fase 1. Sua melhor fase concluída fica sempre em localStorage.

Por que a área de jogo parece tão estreita?

O tamanho de design é 420×720, como um celular em modo retrato. No celular fica naturalmente em tela cheia; no desktop o invólucro reduz proporcionalmente, mas a escala é limitada a 1, então uma janela grande de desktop não amplia o jogo além de 420×720. A lógica do jogo não muda com o tamanho da janela.

Algum dado é enviado para um servidor?

Não. O jogo todo roda localmente no navegador. A única coisa armazenada é um número — sua melhor fase concluída — em localStorage. Apague os dados do site ou troque de navegador e ele some. Não temos placar e não rastreamos pontuação ou interações do nosso lado.

Algo a observar no celular?

Ative o modo de tela cheia do navegador para que o gesto de puxar para baixo não recarregue a página acidentalmente. Modo paisagem funciona, mas a proporção retrato original combina mais com a mecânica. Em celulares mais simples com queda eventual de quadros, feche outras abas e animações pesadas; o próprio jogo só faz alguns cálculos de transform por quadro, então a carga sobre o aparelho é mínima.