Por que organização de Event Sheets vira “sistema” em projetos grandes
Quando o projeto cresce, o problema deixa de ser “fazer funcionar” e passa a ser “manter funcionando” sem quebrar outras partes. Em Construct, isso significa: encontrar eventos rápido, entender intenções sem abrir dezenas de grupos, reaproveitar lógica sem duplicar, e conseguir adicionar features sem virar um emaranhado de condições e ações.
Organização profissional de eventos é um conjunto de práticas para deixar a lógica legível (qualquer pessoa entende) e escalável (você adiciona conteúdo sem multiplicar trabalho). O objetivo é transformar seus event sheets em módulos: cada sistema com sua responsabilidade, com padrões de nomes, comentários úteis e funções para ações repetidas.
Estrutura recomendada: separar por sistemas e por responsabilidade
Divisão por sistemas (macro)
Uma estrutura comum e fácil de manter é separar por sistemas principais. Exemplo de event sheets:
ES_Main(orquestra o fluxo geral e inclui outros sheets)ES_Player(controle e regras do jogador)ES_Enemies(IA, dano recebido, drops)ES_Combat(regras de dano, invencibilidade, knockback, hitstop)ES_UI(HUD, telas, botões)ES_Audio(roteamento de SFX/música, volumes, chamadas centralizadas)ES_Spawns(spawners, ondas, pontos de spawn)ES_Data(variáveis globais, save/load, preferências)
Mesmo que você já tenha lógica espalhada, a refatoração consiste em mover eventos para o sheet “dono” do assunto e deixar o ES_Main apenas como ponto de entrada (include/organização).
Divisão por responsabilidade (micro)
Dentro de cada sheet, use grupos e subgrupos para separar responsabilidades. Exemplo no ES_Player:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
[PLAYER] Init[PLAYER] Input[PLAYER] Movement[PLAYER] Combat[PLAYER] Damage & Invulnerability[PLAYER] Animation[PLAYER] Debug(desativado por padrão)
O padrão [SISTEMA] Assunto facilita a busca e mantém consistência visual. Em projetos com muitos sheets, isso reduz o tempo de “caça ao evento”.
Grupos e subgrupos: como usar sem virar labirinto
Regras práticas
- Evite profundidade excessiva (muitos níveis de subgrupo). Prefira 1–2 níveis.
- Um grupo deve ter um objetivo claro. Se ele mistura “input”, “animação” e “dano”, ele está grande demais.
- Use grupos para ligar/desligar sistemas (ex.:
[UI] Pausepode ser ativado/desativado). - Coloque eventos de “Setup/Init” no topo do sheet, e “Debug” no final.
Modelo de cabeçalho de grupo (comentário padrão)
Use um comentário no início de cada grupo para documentar intenção e dependências:
[PLAYER] Damage & Invulnerability
Responsabilidade: aplicar dano ao Player, invencibilidade temporária e feedback.
Depende de: ES_Combat (funções), ES_Audio (PlaySFX), variáveis globais de dificuldade.
Eventos-chave: OnPlayerHit, ApplyDamage_Player, StartIFrames.Esse comentário reduz a necessidade de “ler tudo” para entender o que está acontecendo.
Nomenclatura consistente: variáveis, grupos, funções e tags
Padrões recomendados (simples e escaláveis)
- Variáveis globais:
g_*(ex.:g_gameState,g_difficulty,g_musicVolume) - Variáveis locais (do sheet/escopo):
l_*(ex.:l_damage,l_sourceX) - Variáveis de instância: prefixo por objeto ou função (ex.:
hpno Player; ouenemy_hpse você prefere explicitar) - Grupos:
[SISTEMA] Assunto - Funções: verbo + objeto (ex.:
ApplyDamage,PlaySFX,ChangeScreen) - Constantes (quando usar):
CONST_*(ex.:CONST_MAX_HP)
O importante não é o “prefixo perfeito”, e sim escolher um padrão e aplicar em todo o projeto. Isso evita variáveis duplicadas com nomes parecidos e facilita buscas.
Tags e identificadores: padronize para evitar “strings mágicas”
Se você usa tags (por exemplo, em inimigos, pickups, UI), defina um padrão e centralize os valores. Exemplo:
tag = "enemy",tag = "player",tag = "ui"- Ou um campo
typecom valores controlados:"slime","bat","boss"
Boa prática: mantenha uma lista documentada (em comentário no ES_Main ou ES_Data) com os valores válidos para evitar inconsistência (ex.: "Enemy" vs "enemy").
Funções (Function): quando e por que usar
Funções são o principal mecanismo de reutilização e padronização de ações. Elas ajudam a:
- Evitar duplicação (mesma lógica repetida em Player, Enemies, hazards, etc.).
- Garantir consistência (mesmo cálculo de dano, mesmo SFX, mesmo efeito).
- Facilitar manutenção (muda em um lugar e reflete no projeto todo).
Critérios para transformar eventos em função
- Você copiou/colou a mesma sequência 2+ vezes.
- Existe uma “regra” que deve ser igual em todo lugar (ex.: invencibilidade, clamp de HP, tocar SFX).
- Você quer uma API interna do projeto (ex.: “para trocar de tela, sempre chame
ChangeScreen”).
Onde colocar funções
Crie um sheet dedicado, por exemplo ES_Functions (ou por sistema: ES_Combat para funções de combate, ES_UI para funções de UI). O importante é que seja fácil de encontrar.
Boas práticas para evitar duplicação (sem perder clareza)
1) Centralize “efeitos colaterais” em funções
Exemplo: ao aplicar dano, normalmente você também quer: tocar SFX, spawnar partículas, piscar sprite, atualizar HUD. Se cada inimigo fizer isso de um jeito, vira inconsistência. Centralize em uma função que receba parâmetros.
2) Use eventos “gatilho” (broadcast) com cuidado
Se você usa variáveis globais como “sinais” (ex.: g_playerHit = 1), prefira funções ou eventos bem nomeados para evitar estados difíceis de rastrear. Quando precisar de sinalização, documente e resete no mesmo lugar.
3) Prefira dados a ramificações
Em vez de criar um bloco enorme “se inimigo A, faz X; se inimigo B, faz Y”, tente armazenar parâmetros no inimigo (ex.: damage, knockback, sfxHit) e usar a mesma função para todos.
4) Uma fonte de verdade para estados
Se existe g_gameState, evite criar isPaused, inMenu, isGameOver em paralelo sem necessidade. Se precisar de flags, defina regras claras: o que manda em quê.
Passo a passo: refatoração prática dos eventos dos capítulos anteriores
O exercício abaixo assume que você já tem: dano/vida, SFX, troca de telas (menu/pause/game over), UI e inimigos básicos. A tarefa é reorganizar e criar funções reutilizáveis para ações repetidas.
Passo 1: criar a “árvore” de event sheets e grupos
- Crie (ou renomeie) os event sheets para refletir sistemas:
ES_Main,ES_Player,ES_Enemies,ES_UI,ES_Audio,ES_Combat,ES_Functions(se preferir centralizar). - No
ES_Main, organize os includes (ou referências) e deixe apenas eventos de alto nível (ex.: inicialização global, roteamento de estados). - Dentro de cada sheet, crie grupos com o padrão
[SISTEMA] Assuntoe mova os eventos correspondentes.
Passo 2: padronizar variáveis e nomes de grupos
- Liste variáveis globais existentes e renomeie para
g_*(ex.:GameState→g_gameState). - Escolha um padrão para HP e dano: por exemplo,
hpcomo variável de instância em Player e Enemies, edamagecomo variável de instância em hitboxes/projéteis. - Padronize nomes de grupos e comentários de cabeçalho.
Passo 3: criar a função reutilizável “PlaySFX”
Objetivo: qualquer sistema toca SFX chamando um único ponto, que já respeita volume e evita spam.
Assinatura sugerida:
PlaySFX(name, volume, pitch)
Implementação (estrutura):
- No sheet
ES_Audio(ouES_Functions), crie o evento:On function PlaySFX. - Dentro, aplique volume final com base em
g_sfxVolume(global) e no parâmetrovolume. - Toque o som pelo nome/identificador recebido.
Function: PlaySFX(name, volume, pitch)
- Set local l_finalVol = volume * g_sfxVolume
- Audio: Play (tag: "sfx", name: name, volume: l_finalVol, pitch: pitch)Refatoração: procure em todos os sheets ações “Audio: Play …” e substitua por chamadas PlaySFX.
Passo 4: criar a função “ChangeScreen” (troca de tela/estado)
Objetivo: padronizar transições (menu, jogo, pause, game over, vitória), evitando eventos duplicados e inconsistentes.
Assinatura sugerida:
ChangeScreen(targetState, optionalLayout)
Implementação (estrutura):
- Crie
On function ChangeScreennoES_UIouES_Main. - Centralize: setar
g_gameState, habilitar/desabilitar grupos relevantes, mostrar/esconder camadas de UI, e se necessário trocar de layout. - Opcional: tocar SFX de navegação e aplicar fade.
Function: ChangeScreen(targetState, optionalLayout)
- Set g_gameState = targetState
- If optionalLayout != "": System: Go to layout optionalLayout
- Call PlaySFX("ui_confirm", 1, 0)
- (Opcional) UI: ativar grupo [UI] HUD quando targetState = "playing"Refatoração: substitua eventos espalhados de “ir para layout X”, “mostrar menu”, “pausar” por chamadas a ChangeScreen.
Passo 5: criar a função “ApplyDamage” (aplicar dano padronizado)
Objetivo: uma regra única para reduzir HP, aplicar invencibilidade, feedback visual e disparar morte quando necessário. Isso reduz duplicação entre Player e Enemies.
Decisão importante: em Construct, funções não “tipam” objetos automaticamente. Você pode padronizar de duas formas:
- Abordagem A (recomendada): criar duas funções:
ApplyDamage_PlayereApplyDamage_Enemy(mais simples e explícito). - Abordagem B (genérica): uma função
ApplyDamageque opera no objeto atualmente selecionado (exige disciplina de seleção/picking).
A seguir, um modelo simples e robusto usando a Abordagem A.
Passo 5A: ApplyDamage_Player
Assinatura sugerida:
ApplyDamage_Player(amount, sourceX, sourceY, sfxName)
Implementação (estrutura):
- Cheque invencibilidade (ex.:
Player.iFrames > 0) e ignore dano se ativo. - Subtraia HP e faça clamp (não deixar negativo).
- Inicie invencibilidade (set
iFrames). - Chame
PlaySFXe dispare feedback (flash/partículas) de forma centralizada. - Se HP <= 0, chame
ChangeScreen("gameover", ...)ou dispare evento de morte.
Function: ApplyDamage_Player(amount, sourceX, sourceY, sfxName)
- If Player.iFrames > 0: return
- Player.hp = max(Player.hp - amount, 0)
- Player.iFrames = 0.6
- Call PlaySFX(sfxName, 1, 0)
- (Opcional) Spawn hit effect at Player
- If Player.hp = 0: Call ChangeScreen("gameover", "")Passo 5B: ApplyDamage_Enemy
Assinatura sugerida:
ApplyDamage_Enemy(uid, amount, sourceX, sourceY, sfxName)
Por que passar UID: você garante que está aplicando dano no inimigo correto, mesmo se a seleção mudar.
Implementação (estrutura):
- Selecione o inimigo pelo UID.
- Subtraia HP, feedback, e se morrer: dropar item/pontos, tocar SFX, destruir.
Function: ApplyDamage_Enemy(uid, amount, sourceX, sourceY, sfxName)
- Pick Enemy by UID = uid
- Enemy.hp = max(Enemy.hp - amount, 0)
- Call PlaySFX(sfxName, 1, 0)
- (Opcional) Spawn hit effect at Enemy
- If Enemy.hp = 0:
- Add score
- (Opcional) Drop item
- Destroy EnemyRefatoração: onde quer que você tenha eventos “Enemy hp -= X” ou “Player hp -= X”, substitua por chamadas às funções.
Passo 6: transformar sequências repetidas em “eventos reutilizáveis” (padrões)
Além de funções, você pode criar padrões reutilizáveis com grupos “template” e subgrupos por tipo. Exemplos:
- Template de morte:
[ENEMY] Deathcom subeventos para pontuação, drop e efeitos. - Template de interação:
[UI] Buttoncom lógica padrão de hover/click e chamadaChangeScreen. - Template de hit:
[COMBAT] HitResolutionque chamaApplyDamage_*e feedback.
Regra: se o template precisar de parâmetros, prefira função. Se for uma sequência fixa e bem localizada, grupo pode bastar.
Checklist de legibilidade (use como revisão rápida)
| Item | Verificação |
|---|---|
| Grupos nomeados | Todos os grupos seguem [SISTEMA] Assunto e têm comentário de cabeçalho |
| Sem duplicação óbvia | Sequências repetidas viraram funções (PlaySFX, ApplyDamage, ChangeScreen) |
| Variáveis consistentes | Globais g_*, locais l_*, instância com padrão definido |
| Estados centralizados | g_gameState (ou equivalente) é a fonte principal; flags auxiliares são documentadas |
| Busca rápida | Você encontra qualquer regra em até 10–20 segundos usando nomes e estrutura |
Exercício guiado: refatorar seu projeto atual
Tarefa A — reorganização por sistemas
- Crie os sheets:
ES_Main,ES_Player,ES_Enemies,ES_UI,ES_Audio,ES_Combat. - Mova eventos para o sheet correspondente (sem alterar lógica ainda).
- Crie grupos com o padrão
[SISTEMA] Assuntoe adicione comentários de cabeçalho.
Tarefa B — padronização
- Renomeie variáveis globais para
g_*. - Escolha e aplique padrão de HP/dano (instância) para Player e Enemies.
- Documente valores válidos de tags/strings usadas (em um comentário central).
Tarefa C — funções obrigatórias
- Implemente
PlaySFX(name, volume, pitch)e substitua todas as ações diretas de tocar som. - Implemente
ChangeScreen(targetState, optionalLayout)e substitua transições espalhadas. - Implemente
ApplyDamage_Player(amount, sourceX, sourceY, sfxName)eApplyDamage_Enemy(uid, amount, sourceX, sourceY, sfxName).
Tarefa D — caça à duplicação (auditoria)
- Use busca por palavras-chave:
hp,Audio: Play,Go to layout,Set visible,Destroy. - Para cada sequência repetida 2+ vezes, decida: vira função ou vira template de grupo.
- Após refatorar, remova eventos antigos duplicados e valide o comportamento (dano, UI, áudio, transições).