← Portfólio
Artigo Técnico · MVP · Jogo Indie · 2025–2026

CAPOEIRA
SOUL

Análise técnica sem omissões do desenvolvimento, decisões de arquitetura, débitos reais e estado atual do produto.

Capoeira Soul Logo
Autor
José Darci Rodrigues Junior
Repositório
github.com/josedarci/capoeirasoul
Status
MVP Estabilizado · Internal Testing
Versão
versionCode 5 · versionName 1.3

O Que É e O Que Foi Entregue

Capoeira Soul é um jogo arcade 2D single-player desenvolvido por um único desenvolvedor, construído sobre React 18, TypeScript, Phaser 3 e um backend PHP 8.1 + MySQL. O jogo percorre a jornada de um praticante de corda branca até mestre, distribuída em 41 fases organizadas em 3 capítulos ambientados em locais reais do Brasil.

O MVP foi concluído com funcionalidade integral: combate com FSM, 13 inimigos folclóricos com IA individualizada, sistema de progressão persistente, monetização via Google Play Billing, wrapper Android nativo e publicação em Internal Testing na Play Store.

41fases
3 capítulos lineares
13IA
Criaturas folclóricas
−80%
Redução de bundle (446KB→90KB gzip)
0erros
TypeScript em build de produção
39mp3
Narrações de lore (13 × 3 idiomas)
57%
TypeScript (linguagem dominante)
Os quatro protagonistas de Capoeira Soul

Arte conceitual · Os 4 protagonistas — Raiz, Dourado, Equilibrado e Instrutor Aurora

Gameplay em Vídeo

Demonstração do sistema de combate, cenários vivos, trilha sonora e inimigos folclóricos em ação.

Decisões de Stack e Suas Consequências

Roda de capoeira no Rio de Janeiro

Tela de vitória · Roda de Capoeira no Rio de Janeiro com Cristo Redentor ao fundo

A escolha de combinar React com Phaser 3 dentro de um WebView Android não é ortodoxa. A maioria dos jogos mobile nativos usa Unity, Godot ou engines com pipeline dedicado. Aqui, a decisão foi pragmática: um desenvolvedor solo com domínio de TypeScript e necessidade de reaproveitamento de código entre web e Android.

Camada Tecnologia Justificativa Real
Engine de Jogo Phaser 3 Maturidade, FSM, física 2D, suporte a canvas e WebGL. Curva de aprendizado aceitável em TypeScript.
UI/React React 18 + Vite Menus, HUD, configurações e bestiário vivem fora do canvas Phaser — React gerencia esse layer.
Backend PHP 8.1 MVC + MySQL Hospedagem compartilhada disponível e custo zero adicional. Não havia razão para Node/Python.
Áudio Howler.js + ElevenLabs API Howler resolve streaming e pooling de <audio>. ElevenLabs gerou vozes e SFX de bioma.
Android WebView + Play Billing SDK Evita duplicação de codebase. A bridge JS↔Java expõe billing, AdMob e hápticos.
Assets Visuais IA generativa + scripts Node.js Desenvolvimento solo torna inviável arte manual em escala. Pipeline automatizado remove fundos e comprime.
Custo Real da Decisão WebView O wrapper WebView introduz overhead de renderização e limitações de pool de áudio. O Howler precisou de configuração explícita de html5PoolSize = 10 para evitar travamento do WebView Android com ~15+ elementos <audio> simultâneos. Isso não é problema em browser desktop, mas é uma constraint real em mobile low-end.

Motor de Jogo: Data-Driven com FSM

O core de combate opera via moves.ts, onde cada ataque segue o fluxo Startup → Active → Recovery. A Finite State Machine (FSM) desacopla lógica de combos, cancelamentos e I-Frames (frames de invencibilidade). Isso é arquitetura padrão de fighting games — a execução em Phaser/TypeScript é não-convencional, mas o modelo conceitual é correto.

Comunicação Frontend ↔ Backend

// Cada fase gera um POST ao encerrar
POST /api/progress
{
  "stageIndex": 12,
  "hp": 68,
  "time": 94,
  "combo": 7,
  "skin": "raiz",
  "difficulty": "medium"
}

// Backend persiste via ProgressRepository::updateMaxStage
// com validação de progressão estrita: só aceita maxStageIndex + 1
// Impede exploits de pulo de fase via requisição direta

O Que Foi Implementado de Fato

Sistema de Combate

Seis golpes técnicos implementados com sprites dedicados por skin: Martelo, Benção, Rasteira, Cocorinha (esquiva), Aú (com I-Frames) e Meia-lua de Compasso (especial). A Rasteira inicialmente reutilizava o sprite de esquiva com setTint(0xaaaaaa) — débito técnico detectado em auditoria e corrigido com sprite próprio e partículas de impacto.

Sistema de Axé (Energia Especial)

IA dos 13 Inimigos Folclóricos

Cada criatura tem comportamento especializado com golpe especial único e VFX exclusivo. Não são variações de um template genérico — o código de EnemyBrain e BossBrain foi individualizado:

CriaturaArquétipo de IAGolpe Especial
IaraDebufferInverte controles por 2s
BoitatáZonerNova de fogo radial com screen shake
MapinguariTankTerremoto ativa abaixo de 50% HP
PisadeiraAssassinDash com invisibilidade parcial + paralisia
Corpo-SecoDrainerDrena HP e escurece a tela
Mãe-do-OuroZoner Elite3 explosões douradas sequenciais abaixo de 40% HP
BotoTricksterTeleporte + espiral de charme duplo
CucaSummonerInvoca minions com círculo arcano

Sistema de Cenários Vivos

O sistema mais sofisticado tecnicamente do projeto. Cada background gera vida procedural via GPU sem GIFs ou frames pré-renderizados. O método StageScene.drawBackground() analisa substrings do nome do arquivo de fundo e injeta 4 pipelines:

// Exemplo: bg_quilombo.png ativa automaticamente:
addAmbientParticles()    // faíscas laranja/amarelas, blendMode: ADD
addAmbientTweens()       // brilho pulsante de fogueira (alpha 0.4→0.75)
addLivingScenarioElements() // 30 estrelas + estrelas cadentes
getAmbientSFXKey()       // → "sfx_fogueira_ambiente"

// Todos os assets visuais são gerados em runtime via canvas HTML5
// e registrados com this.textures.addCanvas()
// Zero bytes adicionais no bundle por novo cenário
Decisão Arquitetural Relevante A camada de efeitos roda em depth: 1-2, lutadores em depth: 10. Essa separação estrita evita z-index conflicts entre elementos procedurais e sprites de combate. Não é sofisticação desnecessária — sem ela, partículas sobrepõem personagens em determinados biomas.

Problemas Reais Encontrados e Como Foram Resolvidos

Documentar apenas o que funcionou é marketing, não engenharia. Os bugs abaixo foram reais, causaram regressões e exigiram correção explícita:

Crash de Produção: R8/Proguard (WorkDatabase_Impl)

Problema

Build release crashava no boot. O R8 removia WorkDatabase_Impl, dependência transitiva do Firebase Crashlytics via WorkManager. A regra -keep class *_*_Impl usava asterisco simples que não atravessa pacotes.

Solução

Regra corrigida para -keep class **_Impl { *; } com duplo asterisco, somada a -keep class androidx.work.impl.** { *; }. versionCode bumpado de 4 para 5.

Bug de Loop de Redirecionamento (HubScene)

Problema

Race condition: o Phaser transitava pela HubScene na inicialização e ativava a regra React de retorno ao /menu, pois startedRef já estava pré-sinalizado antes do jogo iniciar de fato.

Solução

Criação do gameplayStartedRef — flag ativada somente ao entrar em cena de combate real (StageScene, BossScene, IntroScene, etc.). Inicialização do Phaser não ativa o guard.

Bug de Flip de Sprite do Player

Problema

Os sprites das 4 skins do player são desenhados olhando para a direita — convenção oposta à dos inimigos. Player.ts usava flipX = this.facing === 1 (regra do Enemy), fazendo o player chucar visualmente para o lado oposto do inimigo.

Solução

Lógica invertida em 5 ocorrências de Player.ts para flipX = this.facing === -1. Enemy.ts e Boss.ts permaneceram inalterados.

Audio Seek Corrompido no Minigame de Ritmo

Problema

RhythmScene usava GlobalAudio.currentMusicMs() (Howler.seek()) para temporização. Como o AudioSystem cacheia Howls entre cenas, se a faixa já tinha tocado, o seek retornava ~14s já decorridos — marcando as primeiras 25+ notas como MISS instantâneo.

Solução

Temporização trocada para this.time.now - this.startTime (relógio interno do Phaser). Adicionado emit de time:changed que estava faltando, deixando o cronômetro do HUD em 00:00.

Tela Preta entre Fases (Produção)

Problema

Em builds de produção, cenários ficavam pretos após assistir ou pular introduções de capítulo. O preload() executava durante desvio de tick para IntroScene, registrando entradas corrompidas de textura no gerenciador global do Phaser.

Solução

Guard do estado transitioning adicionado em StageScene.ts e BossScene.ts para anular o preload() durante transições. Não reproduzível em desenvolvimento — bug exclusivo de produção minificada.

Geração e Tratamento de Arte com IA

Desenvolver um jogo com 41 backgrounds, 65+ sprites de inimigos, 4 skins de player e cenários cinematográficos como desenvolvedor solo não é viável com arte manual. A solução foi construir um pipeline de geração e pós-processamento:

Problema Real com Arte Gerada por IA Arte gerada por IA frequentemente vem com fundos "bakeados" (grades cinzas, halos, chão pintado). Remoção automática via flood-fill funciona para ~85% dos casos. Os outros 15% exigiram calibração manual de coordenadas de crop — documentado no backlog como "Calibração Manual dos Crops Folclóricos". Não existe automação perfeita aqui: é um compromisso entre velocidade e qualidade.

Modelo de Receita e Estado do Release

Estrutura de Monetização

Dois vetores de receita implementados:

O backend valida o purchase token via Google Play Developer API em PremiumController.php, persiste is_premium em player_profiles e audita compras em premium_purchases. Em ausência do google-service-account.json, o endpoint aceita on-trust (modo staging).

Estado Real do Release

ItemStatus
Internal Testing (Play Store)✅ Publicado — versionCode 5
Google OAuth Consent✅ In Production — sem verificação manual necessária
SHA-1 Debug + Play Store✅ 2 OAuth Clients configurados
Keystore Backup✅ Duplicado (HD + pen-drive) — 3º backup (cloud criptografada) pendente
Ad Unit de Produção⚠️ Ainda usando ID de teste no código — troca imediata antes do AAB de produção
Service Account JSON⚠️ Ausente no servidor — PremiumController em modo on-trust
Migration 008 (is_premium)⚠️ Não aplicada no MySQL de produção
SKU no Play Console⚠️ Produto capoeira_soul_premium_lifetime não criado

Suporte a Três Idiomas

O jogo tem localização completa em Português, Inglês e Espanhol via LocalizationSystem com hook useL10n. Isso cobre: 45 nomes de fases, menus, UI, 13 criaturas do bestiário, narrações de lore (39 MP3s: 13 × 3 idiomas), vozes de combate dos 4 protagonistas e telas de autenticação.

A trilha sonora do menu principal troca automaticamente por idioma: berimbau (PT), capoeira_soul_en (EN), alma_de_capoeira_es (ES). A troca de idioma dispara unloadUnusedMenuMusic no AudioSystem para evitar acúmulo de 3 faixas de ~4MB na RAM do dispositivo.

A Jornada e Quem a Faz

A estrutura narrativa de Capoeira Soul segue o arco clássico do aprendizado na capoeira: da corda branca à mestria. Mas o jogo não é uma abstração genérica de artes marciais — cada personagem carrega identidade, motivação e destino específicos, todos resolvidos nos epílogos cinematográficos ao completar a Stage 39.

Os Quatro Protagonistas

RAIZ
Força · Ancestralidade · Corda Branca

O personagem de entrada e o mais visceral do elenco. Raiz não é um nome — é uma identidade. Ele representa o praticante que se conecta à capoeira pela via corporal, não pela filosófica. Seu estilo privilegia força bruta, golpes baixos e presença física na roda. A jornada de Raiz é a de um jovem que começa sem entender o que a capoeira significa e termina carregando seu peso histórico. Seu epílogo é o mais austero: não há vitória grandiosa, há pertencimento.

Afro curto · Cordão branco · Pixel art NeoGeo
DOURADO
Velocidade · Luz · Movimento Fluido

O contraponto técnico de Raiz. Dourado é o jogador que leu sobre capoeira antes de praticá-la — conhece a teoria, os toques do berimbau, a história das rodas na Bahia. Seu estilo é ginga constante, esquivas longas, contragolpes. Menos força, mais mandinga. Narrativamente, Dourado enfrenta a tensão entre performance e autenticidade: ser o melhor na roda ou ser verdadeiro com a roda. Seu epílogo resolve essa tensão com uma escolha que não é óbvia.

Velocidade elevada · Axé regenera mais rápido
LUCAS ANDRADE — EQUILIBRADO
Mediação · Unidade · O Centro da Roda

O personagem narrativamente mais complexo. Lucas Andrade é um mediador — sua função na história não é vencer inimigos, é unir o mundo fragmentado das rodas de capoeira do Brasil. Em um jogo onde os capítulos percorrem de Salvador ao Amazonas, de Brasília à caatinga, Lucas é o fio condutor. Ele não pertence a nenhuma escola específica. Seu nome completo aparece — único entre os protagonistas — porque ele tem uma identidade civil, não apenas uma identidade de praticante. Seu epílogo é o mais longo e o único com consequências coletivas.

Atributos equilibrados · Nenhuma fraqueza, nenhum pico
ANTÔNIO CARLOS — INSTRUTOR AURORA
Mentoria · Pelourinho · Mestre Veterano

Aurora não é um personagem jogável no sentido tradicional — ele é o mentor do trio. Mestre veterano do Pelourinho, Antônio Carlos narra os prólogos dos três capítulos, estabelece o contexto histórico de cada região visitada e é a voz que abre cada luta com o grito localizado de "VAMO JOGAR!" (PT), "LET'S PLAY!" (EN) e "¡A JUGAR!" (ES). Jogar com Aurora é jogar com o peso da responsabilidade: ele já sabe o que Raiz, Dourado e Lucas ainda vão aprender. Seu epílogo é o mais melancólico — o mestre que formou os outros e agora precisa decidir o que fazer com o silêncio que se segue.

Dreadlocks longos soltos · Cordão verde · Sem barba
Dourado em pose de ginga na praia
Dourado · Ginga na praia ao pôr do sol
Inimigo genérico em pose de combate
Arquétipo de inimigo · Postura de combate

Mestre Pássaro — O Boss Final do Capítulo 1

Mestre Pássaro é o guardião do Templo do Pássaro (bg_templo), cenário de atmosfera roxa mística onde o primeiro capítulo culmina. Ele não é um antagonista no sentido narrativo — é um teste. A capoeira tem essa tradição: o mestre que barra a passagem não quer impedir o aluno, quer saber se o aluno está pronto.

Tecnicamente, Mestre Pássaro foi o personagem com mais débito de arte no projeto: o loader do Phaser caía em fallback genérico em vez de renderizar o sprite correto. A correção exigiu mapear explicitamente o ID mestre_passaro para carregar os sprites passaro_*. A arte final foi recriada do zero em HD Digital Painting — estética diferenciada dos outros inimigos, que seguem pixel art NeoGeo.

Curupira — O Boss da Mata (Capítulo 3)

Lutador do sertão

Lutador do sertão · Arquétipo regional do Capítulo 2

Curupira é o guardião da floresta no folclore brasileiro — pés virados para trás, cabelos de fogo, protetor dos animais. No jogo, ele é o boss que representa o Brasil que o progresso esqueceu: a Amazônia, o Pantanal, a mata fechada que os cenários do Capítulo 3 mostram sendo percorrida.

Seu arquétipo de IA é Tank com Super Armor e terremoto. Abaixo de 50% de HP, ativa Fúria Primordial — terremoto duplo com rachaduras visuais e pedras voando. A progressão de dificuldade dele é deliberada: o jogador que chegou até aqui já aprendeu a reagir aos especiais dos inimigos anteriores. Curupira exige que esse aprendizado seja aplicado em janelas de tempo mais curtas.

Os 13 Inimigos Folclóricos — Lore e Função Narrativa

Cada criatura não é apenas um inimigo — é uma representação de uma região, um medo coletivo ou uma força da natureza brasileira. O bestiário funciona como enciclopédia: cada criatura tem ficha de lore textual, narração de áudio em três idiomas (39 MP3s) e arte HD.

CriaturaOrigem do FolcloreO Que Representa no Jogo
SaciPan-brasileiroO trickster. Caos controlado. Fase de introdução às surpresas.
CaiporaTupi-Guarani · NordesteProtetora da mata. Guardião antes de Curupira. Mais ágil, menos poderosa.
CurupiraAmazônia · TupiBoss principal da floresta. Representa a natureza que reage ao invasor.
IaraAmazônia · riosA sedução como arma. Inverte controles — desorientação psicológica.
BoitatáSul · GuaraniO fogo dos campos. Zoner que controla o espaço com rastros persistentes.
Mula-sem-cabeçaCentro-Oeste · interiorViolência sem direção. Rusher que atravessa a arena sem parar.
CucaPan-brasileiro · infantilO medo da infância que se torna real. Invoca outros medos (summoner).
Boto EncantadoAmazônia · ribeirinhoO charme masculino predatório. Trickster com teleporte e confusão.
MapinguariAmazônia · pré-históricoA memória ancestral da floresta. Tank inabalável com ativação tardia.
PisadeiraSudeste · urbanoParalisia do sono. Assassin invisível — representa o medo moderno.
Corpo-SecoSertão · nordesteO morto que a terra rejeitou. Drainer que enfraquece lentamente.
Mãe-do-OuroMinas Gerais · colonialA riqueza que defende a si mesma. Zoner elite ativada pela cobiça do jogador.
MalandroRio de Janeiro · urbanoA capoeira de rua, sem código. O espelho do que o jogador poderia se tornar.
Saci
Saci · Trickster pan-brasileiro
Caipora
Caipora · Guardiã da mata
Mãe-do-Ouro
Mãe-do-Ouro · Boss da mina colonial
Pisadeira
Pisadeira · Assassin dos telhados urbanos
Roda de Capoeira
Roda · Tela de vitória no Rio de Janeiro
Decisão de Design Narrativo O Malandro é o único inimigo folclórico que não vem da tradição oral ou da natureza — ele vem da cidade. Colocá-lo no elenco ao lado de Saci e Curupira é uma afirmação: o Brasil urbano também tem seus monstros, e a capoeira nasceu justamente nessa fronteira entre floresta, quilombo e rua.

Estrutura Narrativa dos Três Capítulos

CapítuloTítuloArco Narrativo
Cap. 1 Raízes e Ruas A origem. Bahia, Lapa, Pelourinho, Quilombo. O aluno descobre que a capoeira não é só luta — é memória de resistência. Culmina no Templo do Pássaro com Mestre Pássaro como prova de prontidão.
Cap. 2 O Despertar da Mestra A consolidação. Terreiro, cachoeiras, caatinga, carnaval, cais. A jornada sai da Bahia e percorre o Nordeste e o litoral. O personagem aprende que cada roda tem suas próprias regras — e que respeitar isso é mandinga, não fraqueza.
Cap. 3 Lendas Urbanas e Folclore A síntese. São Paulo, Rio, Brasília, Amazônia, Pantanal. O Brasil inteiro como palco. Os inimigos deixam de ser humanos — são as forças que o país carrega. Concluir esse capítulo não é vencer: é entender.

Os epílogos são disparados ao completar a Stage 39 (Mina de Ouro Proibida) e apresentam finais personalizados para cada herói com arte pixel art original e trilha solene — quatro histórias distintas para quem chegou ao mesmo lugar por caminhos diferentes.