O Crossover League nasceu de um cenário hipotético meio duvidoso: um jogo de futebol entre minha turma atual do ensino médio e minha antiga turma do ensino fundamental. O “crossover” no nome veio daí. O problema é que eu tive um hiperfoco, a brincadeira escalou e agora isso é um jogo de futebol arcade rodando no navegador, com uma IA desnecessariamente agressiva, física própria e absolutamente nenhuma justificativa racional para existir — mas existe.
O projeto consiste em uma engine de jogo personalizada construída sobre HTML5 Canvas, gerenciada por um loop de jogo modular. A interface do usuário (HUDs, Menus, Placar) é reativa e controlada via Alpine.js, enquanto a estilização é feita com Tailwind CSS v4.
O jogo oferece partidas rápidas entre times fictícios ("Time A" vs "Time B"), com mecânicas de:
- Movimentação WASD/Setas
- Sprint (Shift) e Chute Carregado (Espaço)
- Posse de bola e Física de colisão
- IA com níveis de dificuldade (Hard/Insane)
- Gameplay Arcade: Física de bola, colisões jogador-jogador e jogador-bola, sistema de estamina e chutes com força variável.
- Modos de Jogo:
- Por Gols: Quem atingir o placar alvo primeiro vence.
- Por Tempo: Partida com duração fixa.
- Interface Reativa (HUD): Placar, tempo e estatísticas atualizados em tempo real via Alpine.js.
- Configuração de Partida: Seleção de times, uniformes, posições táticas e dificuldade da IA.
- Estatísticas Pós-Jogo: MVP da partida, posse de bola, histórico de gols e artilheiros.
O projeto utiliza uma stack moderna e leve:
- Core: JavaScript (ES Modules)
- Renderização: HTML5 Canvas API
- Gerenciamento de Estado/UI: Alpine.js
- Estilização: Tailwind CSS v4
- Build Tool: Vite
- Gerenciador de Pacotes: Bun (compatível com npm)
- Linting/Formatação: Biome
Abaixo uma visão resumida das principais pastas:
crossoverleague/
├── public/ # Arquivos estáticos servidos no final (HTML, Assets, JS buildado)
│ ├── index.html # Entry point da aplicação
│ ├── app.css # CSS gerado pelo Tailwind
│ └── main.js # Bundle final do jogo (gerado pelo Vite)
├── src/ # Código fonte
│ ├── core/ # Sistemas base (Input, UI Controller)
│ ├── game/ # Lógica do jogo
│ │ ├── modules/ # Módulos independentes (IA, Física, Render, Loop, etc.)
│ │ └── Game.js # Singleton que orquestra os módulos
│ ├── main.js # Ponto de entrada (Inicializa Alpine e Game)
│ └── app.css # Fonte do CSS
├── package.json # Dependências e Scripts
├── vite.config.js # Configuração do Vite (Build IIFE)
└── biome.json # Configuração de Linter
# Via Bun (recomendado)
bun install
# Via NPM
npm installO projeto utiliza o Tailwind v4 com watcher e o Vite para build.
Para desenvolvimento ativo (watch CSS):
bun run dev
# Ou: npm run dev
# Este comando roda: tailwindcss -i src/app.css -o public/app.css --watchPara gerar o bundle JS (Vite):
bun run build
# Ou: npm run build
# Gera o arquivo src/main.js -> public/main.jsPor padrão, o jogo não vem com times. Sim: é proposital. Sem clubismo e arquivos inúteis embutido no repo 😌
Pra conseguir jogar, você precisa criar pelo menos 2 times, seguindo a estrutura de pastas abaixo e registrando tudo no data.js.
1) Estrutura de pastas obrigatória
Crie seus times dentro de public/assets/teams/ (ou equivalente no seu setup), seguindo exatamente este padrão:
└── teams
├── infor
│ ├── kitA
│ │ ├── a1.jpg
│ │ ├── a2.jpg
│ │ ├── a3.jpg
│ │ ├── a4.jpg
│ │ └── gk.jpg
│ ├── kitB
│ │ ├── a1.jpg
│ │ ├── a2.jpg
│ │ ├── a3.jpg
│ │ ├── a4.jpg
│ │ └── gk.jpg
│ └── logo.png
├── nine-z
│ ├── kitA
│ │ ├── b1.jpg
│ │ ├── b2.jpg
│ │ ├── b3.jpg
│ │ ├── b4.jpg
│ │ └── gk.jpg
│ ├── kitB
│ │ ├── b1.jpg
│ │ ├── b2.jpg
│ │ ├── b3.jpg
│ │ ├── b4.jpg
│ │ └── gk.jpg
│ └── logo.png
└── data.js
O que é o quê:
-
logo.png: logo do time (usado na UI). -
kitA/ekitB/: dois uniformes do time. -
Dentro de cada kit:
a1.jpg,a2.jpg,a3.jpg,a4.jpg: texturas/skins dos jogadores de linha.gk.jpg: textura/skin do goleiro.
-
Os nomes dos arquivos importam: eles precisam bater com os
iddo elenco que você declarar nodata.js.
2) Indexação no data.js (sem isso o time “não existe”)
Depois de colocar os arquivos, você precisa registrar os times em data.js, populando window.GAME_TEAMS.
Exemplo (do jeito que está no teu data.js):
window.GAME_TEAMS = [
{
id: "infor",
name: "INFOR-2",
logo: "assets/teams/infor/logo.png",
roster: [
{ id: "a1", name: "Guiga", pref: "ST" },
{ id: "a2", name: "Bruno H.", pref: "LB" },
{ id: "a3", name: "Zé Filho", pref: "RB" },
{ id: "a4", name: "Diogo", pref: "DM" },
{ id: "gk", name: "Chaves", pref: "GK" },
],
},
{
id: "nine-z",
name: "CMS Nine-Z",
logo: "assets/teams/nine-z/logo.png",
roster: [
{ id: "b1", name: "Hiury", pref: "ST" },
{ id: "b2", name: "Roberto", pref: "RB" },
{ id: "b3", name: "Gustavo", pref: "LB" },
{ id: "b4", name: "Matthews", pref: "DM" },
{ id: "gk", name: "Fábio", pref: "GK" },
],
},
];Regras importantes (pra não passar raiva):
-
iddo time (ex:"infor","nine-z") precisa ser igual ao nome da pasta do time. -
logoprecisa apontar pro caminho certo dentro deassets/teams/<id>/logo.png. -
Cada item de
rosterprecisa ter:id: igual ao nome do arquivo do jogador (sem extensão). Ex:a1↔a1.jpg,gk↔gk.jpg.name: nome exibido na UI/estatísticas.pref: posição preferida (ex:ST,LB,RB,DM,GK).
-
Você precisa ter no mínimo 2 times no array pra conseguir iniciar partida.
Como o projeto é servido estaticamente a partir da pasta public/ (ou configurado via XAMPP/Apache na pasta htdocs):
Opção A (Vite Preview): Roda um servidor local servindo a pasta de distribuição.
bun run preview
# Acessar: http://localhost:4173 (ou porta indicada)Opção B (Apache/XAMPP):
Se estiver na pasta htdocs, certifique-se de acessar via:
http://localhost/crossoverleague/public/
Nota: Se abrir o
index.htmldiretamente no navegador (file://), pode haver bloqueios de CORS (Cross-Origin Resource Sharing) ao carregar módulos ou imagens. Recomenda-se usar um servidor local.
Este projeto está licenciado sob a licença MIT.