update doc

This commit is contained in:
2026-06-24 21:26:29 -03:00
parent b210ed4f92
commit 592a5a17e4
5 changed files with 876 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
{
"position": 4,
"label": "Crie suas Atividades",
"collapsible": true,
"collapsed": false,
"className": "red",
"link": {
"type": "generated-index",
"title": "Crie suas Atividades"
},
"customProps": {
"description": "Guia prático para criar novas atividades de programação na plataforma Decoda, do zero ao deploy."
}
}

View File

@@ -0,0 +1,216 @@
---
sidebar_position: 4
title: "Game Design Pedagógico"
---
Esta página não é sobre tecnologia — é sobre **como pensar antes de escrever código**. Uma atividade tecnicamente perfeita pode ser um fracasso pedagógico se o aluno não aprender nada ou desistir frustrado antes de terminar.
:::warning
**Premissa inegociável**: Toda atividade da Decoda existe para **ensinar ou reforçar um conceito de programação**. Entretenimento é um meio, não o fim. Se a atividade for divertida mas o aluno não aprender nada de programação, ela não pertence à plataforma.
:::
---
## Princípio 1 — Uma fase, um conceito
Cada fase deve ter **um único objetivo de aprendizagem**. Não "aprender loops e condicionais ao mesmo tempo" — ou loops, ou condicionais.
```
✅ Fase 1: O aluno aprende a usar o bloco "repetir N vezes"
✅ Fase 2: O aluno aprende a combinar repetição com condicionais
❌ Fase 1: O aluno aprende repetição, condicionais, variáveis e sensores
```
Isso não significa que a atividade deve ser trivial. Significa que a **novidade cognitiva** introduzida por fase deve ser uma coisa de cada vez.
**Como implementar:** o campo `allowedBlocks` em `config.js` é o seu mecanismo. Libere blocos novos progressivamente conforme as fases avançam.
```javascript
// Fase 1 — só movimento
allowedBlocks: ["mover_direita", "mover_baixo"]
// Fase 2 — adiciona repetição
allowedBlocks: ["controls_repeat_ext", "math_number", "mover_direita", "mover_baixo"]
// Fase 3 — adiciona condicionais
allowedBlocks: ["controls_whileUntil", "robo_if", "sensor_frente", "mover_direita", "mover_baixo"]
```
---
## Princípio 2 — A curva de dificuldade
A progressão de dificuldade deve seguir uma curva suave: o aluno nunca deve sentir um salto abrupto entre duas fases consecutivas.
```
Dificuldade
5 │ ╭──── Fase 5
4 │ ╭───────╯
3 │ ╭────────╯
2 │ ╭────────╯
1 │───╯
└──────────────────────────────── Fase
1 2 3 4 5
```
Se a curva tiver um degrau vertical, o aluno vai travar. Isso gera frustração, não aprendizado.
**Sinais de que a curva está errada:**
- Alunos em teste completam a Fase N facilmente e travam na Fase N+1 por mais de 5 minutos sem progresso.
- A Fase N+1 exige um conceito que nunca foi apresentado antes.
- O `maxBlocks` da Fase N+1 é incompatível com a solução esperada.
**Regra prática:** a Fase N+1 deve ser solucionável por alguém que acabou de completar a Fase N sem ajuda.
---
## Princípio 3 — O `maxBlocks` como professor, não como punição
O limite de blocos é uma **diretriz pedagógica**, não uma punição. Seu papel é impedir que o aluno resolva o problema "na força bruta" sem aprender o conceito esperado.
```javascript
// ❌ Errado: maxBlocks tão alto que qualquer solução serve
{ maxBlocks: 50, allowedBlocks: ["controls_repeat_ext", "mover_direita"] }
// → O aluno coloca 10 blocos "mover_direita" sem usar o loop. Não aprende nada.
// ✅ Correto: maxBlocks força o loop
{ maxBlocks: 4, allowedBlocks: ["controls_repeat_ext", "math_number", "mover_direita"] }
// → Solução: repetir(4) { moverDireita() } ← exatamente 3 blocos. Ensina loops.
```
**Como calcular o `maxBlocks` ideal:**
1. Escreva a solução ótima (que usa o conceito da fase).
2. Conte os blocos dessa solução.
3. Some 1 ou 2 blocos de margem para pequenas variações.
4. Esse é o seu `maxBlocks`.
---
## Princípio 4 — Mensagens de erro que ensinam
A mensagem de falha é um **momento de aprendizado**. Ela não pode ser genérica, técnica ou intimidadora.
```javascript
// ❌ Errado
mensagens: {
falha: "Erro: validação falhou.",
timeout: "Execution timeout exceeded."
}
// ✅ Correto
mensagens: {
semMovimento: "Seu robô está parado! Use o bloco 'mover' para sair do lugar.",
saiu: "Ops! O personagem saiu da tela. Verifique quantos passos ele deu.",
timeoutExcedido: "Tempo esgotado! Seu programa parece estar em loop infinito. Verifique a condição de parada.",
naoAlcancou: "Quase lá! O personagem não chegou ao destino. Tente ajustar o número de passos."
}
```
**Checklist de mensagem de erro:**
- [ ] Está em português, sem jargão técnico
- [ ] Diz **o que aconteceu** (não apenas "erro")
- [ ] Dá uma **dica** do que verificar (sem entregar a resposta)
- [ ] Usa linguagem de encorajamento ("quase lá", "tente de novo")
---
## Princípio 5 — O ciclo de feedback imediato
O aluno deve receber feedback **visual e imediato** para cada ação que executa. Isso é o que diferencia um jogo educativo de uma tarefa de casa.
```mermaid
flowchart LR
A[Aluno executa blocos] --> B[Personagem anima]
B --> C{Condição de fim?}
C -->|Sucesso| D[Animação de vitória\n+ Modal de parabéns]
C -->|Falha| E[Animação de erro\n+ Modal com dica]
C -->|Continua| B
```
**Na prática:**
1. **Toda ação deve ter animação.** Se o personagem "teletransporta" sem transição, o aluno perde a referência do que aconteceu.
2. **`onSuccess()` deve celebrar.** Use tweens, partículas, mudança de cor — qualquer efeito que transmita conquista. O modal de sucesso aparece *depois* da animação.
3. **`onFailure()` deve ser empático, não assustador.** Um leve tremor, uma cor vermelha por um instante. Nada que desanime o aluno.
4. **O highlight de blocos é obrigatório.** Sempre chame `configurarGerador()` em `blocks.js`. Ver o bloco iluminado enquanto executa ajuda o aluno a conectar o bloco visual com a ação no jogo.
---
## Princípio 6 — Projetar contra a frustração
A frustração acontece quando o aluno **não sabe o que fazer a seguir**. Projete a atividade para eliminar esse estado.
### O que causa frustração
| Causa | Como evitar |
|---|---|
| Fase impossível sem conhecimento prévio | Introduza o conceito na fase anterior, ou no texto da fase |
| `timeout` curto demais | Calcule o tempo da execução correta e multiplique por 3 |
| Mensagem de erro genérica | Escreva uma mensagem por condição de falha possível |
| `maxBlocks` abaixo da solução ótima | Sempre teste a solução antes de publicar |
| Fase sem saída (loop inevitável) | Teste com `timeout` ativado; se travar, redesenhe o mapa |
| Salto de dificuldade abrupto | Adicione uma fase de transição |
### A regra dos 5 minutos
Se um aluno iniciante trava em uma fase por mais de 5 minutos sem nenhum progresso visível, a fase está mal projetada. Não é o aluno que está errado.
---
## Princípio 7 — O jogo deve ser jogável sem o enunciado
O texto de `descricao` em `config.js` aparece na interface. Mas o jogo deve fazer sentido mesmo sem que o aluno o leia. O layout visual, os blocos disponíveis e o cenário devem comunicar o objetivo por si mesmos.
```
✅ Um robô no canto superior esquerdo de uma grade, com sujeira espalhada
e o cursor piscando no bloco "mover" → o objetivo é óbvio.
❌ Uma tela escura com um ponto e texto dizendo
"mova o objeto para a posição de destino" → nada é óbvio.
```
**Dicas de design visual:**
- Use **verde** para o alvo/destino (convenção universal de "vá aqui").
- Use **vermelho** apenas para erro ou perigo.
- A posição inicial do personagem deve estar **visivelmente separada** do alvo.
- O tamanho do personagem deve ser proporcional à grade (não pequeno demais).
---
## Checklist de Game Design
Antes de finalizar uma atividade, valide:
**Pedagogia**
- [ ] Cada fase tem exatamente um conceito principal a ensinar
- [ ] Os blocos disponíveis são os mínimos necessários para o conceito da fase
- [ ] A progressão entre fases é gradual (sem saltos abruptos)
- [ ] A solução ótima usa o conceito que a fase pretende ensinar
**Jogabilidade**
- [ ] `timeout` é pelo menos 3× o tempo da execução da solução correta
- [ ] `maxBlocks` é suficiente para a solução ótima + 1-2 blocos de margem
- [ ] `onSuccess()` tem uma animação de celebração
- [ ] `onFailure()` tem feedback visual empático (tremor, cor)
- [ ] O highlight de blocos está funcionando durante a execução
**Comunicação com o aluno**
- [ ] Cada condição de falha tem sua própria mensagem específica
- [ ] As mensagens usam linguagem de encorajamento
- [ ] O objetivo da fase é visualmente óbvio no cenário
- [ ] O texto de `descricao` é curto, motivador e sem jargão técnico
---
## Referências de boas práticas nas atividades existentes
| Atividade | O que observar |
|---|---|
| **Aspirador** | Progressão pedagógica exemplar: 10 fases com curva gradual de sequência → loops → condicionais → variáveis → algoritmos |
| **Aspirador Fase 1** | `maxBlocks: 3` força o aluno a usar `while` logo na primeira fase |
| **Semáforo** | Uso de `expectedSequence` em `config.js` para validação por sequência exata de ações |
| **Puzzle** | Exemplo de atividade onde a validação ocorre por estado final, não por sequência |

View File

@@ -0,0 +1,73 @@
---
sidebar_position: 1
title: "Visão Geral"
---
Esta seção é o ponto de partida para quem quer **criar ou adaptar uma atividade de programação** na plataforma Decoda.
A plataforma já entrega toda a infraestrutura: editor Blockly, interpretador seguro (JS-Interpreter), motor de física e renderização (Phaser), ciclo de sucesso/falha e controles de execução. Você cuida apenas da **lógica do jogo, dos blocos customizados e das regras de validação**.
## O que você vai encontrar aqui
| Página | O que cobre |
|---|---|
| [Usando os Exemplos](./usando-os-exemplos) | Como rodar e interpretar as atividades `Exemplo` e `Exemplo 2`, que foram criadas como código de referência |
| [Passo a Passo](./passo-a-passo) | Roteiro completo: da cópia do template ao registro da rota |
| [Game Design Pedagógico](./game-design) | Princípios para criar atividades que ensinam sem frustrar |
## Pré-requisitos
Antes de criar uma atividade, certifique-se de conhecer:
- A [estrutura de atividades de programação](../plataforma/atividades_programacao/estrutura-de-jogo) da plataforma.
- O papel do [`BaseGameScene`](../plataforma/atividades_programacao/base-game-scene) no ciclo de execução.
- Como o [Blockly](../plataforma/atividades_programacao/blockly) gera código JavaScript.
## Arquitetura em uma visão
Cada atividade é composta por **6 camadas** que se comunicam de forma bem definida:
```mermaid
flowchart TD
JSX["🎮 XGame.jsx\nComponente React + GameStateProvider"]
CONFIG["⚙️ config/config.js\nFases, maxBlocks, mensagens"]
BLOCKS["🧩 blocks/blocks.js\nBlocos customizados + toolbox"]
API["🔌 hooks/setupXAPI.js\nPonte JS-Interpreter ↔ Phaser"]
GAME["🖼️ game.js\nCena Phaser + createGame factory"]
VALID["✅ validation/validators.js\nRegras de sucesso e falha"]
UI["🎨 ui/\nconstants.js + layout.js"]
JSX --> CONFIG
JSX --> BLOCKS
JSX --> GAME
GAME --> API
GAME --> VALID
GAME --> UI
```
## Fluxo de execução resumido
```mermaid
sequenceDiagram
participant Aluno
participant Blockly
participant Interpreter as JS-Interpreter
participant Phaser
participant Validator
Aluno->>Blockly: Monta blocos
Blockly->>Interpreter: Código JS gerado
Interpreter->>Phaser: Chama API da cena (ex: moverDireita())
Phaser->>Phaser: Anima sprite, atualiza estado lógico
Interpreter-->>Validator: Execução concluída
Validator->>Aluno: Sucesso ou Falha com mensagem
```
## Dois caminhos para começar
**Caminho rápido** — copie o `Exemplo` ou o `Exemplo 2` e adapte:
```
app/src/atividades/programacao/exemplo/ ← visual (grade + sprite)
app/src/atividades/programacao/exemplo2/ ← textual (display + imprimir)
```
**Caminho estruturado** — siga o [Passo a Passo](./passo-a-passo) do zero, usando os exemplos como referência de cada decisão.

View File

@@ -0,0 +1,465 @@
---
sidebar_position: 3
title: "Passo a Passo"
---
Roteiro completo para criar uma nova atividade de programação do zero. Use a atividade `Exemplo` como gabarito de cada etapa.
:::tip
Se sua atividade for visual (sprite em grade/mapa), copie `exemplo/`. Se for baseada em texto ou saída de dados, copie `exemplo2/`. Depois siga este guia para entender o que adaptar.
:::
---
## Passo 1 — Estrutura de pastas
Crie a pasta da atividade em `app/src/atividades/programacao/<nome-da-atividade>/`:
```
meu-jogo/
├── MeuJogoGame.jsx ← componente de entrada (React)
├── game.js ← cena Phaser + factory createGame
├── blocks/
│ └── blocks.js ← definição e geradores dos blocos
├── config/
│ └── config.js ← fases, maxBlocks, mensagens
├── hooks/
│ └── setupMeuJogoAPI.js ← ponte JS-Interpreter ↔ Phaser
├── validation/
│ └── validators.js ← regras de sucesso e falha
└── ui/
├── constants.js ← assets, cores, dimensões
└── layout.js ← funções de construção visual
```
---
## Passo 2 — `config/config.js`
O config é a **espinha dorsal pedagógica** da atividade. Define todas as fases e seus parâmetros.
```javascript
export const gameConfig = {
gameId: "meu-jogo",
gameName: "Meu Jogo",
route: "/atividades/programacao/meu-jogo",
component: "MeuJogoGame",
fases: [
{
id: 1,
nome: "Fase 1: Introdução",
descricao: "Descrição curta e motivadora do desafio desta fase.",
timeout: 15, // segundos — protege contra loops infinitos
maxBlocks: 4, // limite pedagógico (não técnico)
allowedBlocks: ["meu_bloco_a", "meu_bloco_b"],
// Dados específicos do jogo (lidos em montarFase)
// ex: posição inicial, mapa, obstáculos...
},
],
mensagens: {
semMovimento: "Você não executou nenhuma ação!",
// Mensagens específicas da sua atividade...
},
};
```
**Checklist do config:**
- [ ] `allowedBlocks` contém apenas os blocos necessários para **esta fase**
- [ ] `maxBlocks` força a solução elegante (não a solução de força bruta)
- [ ] `timeout` é generoso o suficiente para o aluno ter tempo de ver a execução
- [ ] Mensagens de erro são em linguagem acessível (sem jargão técnico)
---
## Passo 3 — `blocks/blocks.js`
Define os blocos visuais e seus geradores de código JavaScript.
```javascript
"use strict";
import "blockly/blocks";
import { CORES_CUSTOMIZADAS } from "@/blockly/blocklyColors";
import { configurarGerador, gerarStatement } from "@/blockly/generator";
import { gerarToolboxDeEstrutura } from "@/blockly/toolbox";
import { criarBlocoStatementSimples } from "@/blockly/blockFactory";
const ESTRUTURA_TOOLBOX = [
{
nome: "Movimento",
cor: CORES_CUSTOMIZADAS.MOVIMENTO,
cssContainer: "cat_movimento",
blocos: ["meu_bloco_a"],
},
];
export const registerBlocks = () => {
defineBlocks();
defineGenerators();
};
export const generateDynamicToolbox = (allowedBlocks = []) =>
gerarToolboxDeEstrutura(ESTRUTURA_TOOLBOX, allowedBlocks);
const defineBlocks = () => {
criarBlocoStatementSimples("meu_bloco_a", "executar ação A", CORES_CUSTOMIZADAS.MOVIMENTO);
};
const defineGenerators = () => {
configurarGerador(); // ← sempre chamar primeiro (ativa highlight)
gerarStatement("meu_bloco_a", "executarAcaoA");
};
```
### Factories de bloco disponíveis (`@/blockly/blockFactory`)
| Factory | Quando usar |
|---|---|
| `criarBlocoStatementSimples` | Ação sem parâmetros: `mover()`, `girar()` |
| `criarBlocoStatementComDropdown` | Ação com opção: `virar('DIREITA' \| 'ESQUERDA')` |
| `criarBlocoStatementComValor` | Ação com input de valor: `imprimir(texto)`, `repetir(n)` |
| `criarBlocoExpressaoSimples` | Condição sem parâmetros: `aindaTemSujeira()` |
| `criarBlocoExpressaoComDropdown` | Condição com opção: `caminhoBloqueado('FRENTE')` |
| `criarBlocoCondicional` | Bloco Se/Faça |
| `criarBlocoNegacao` | Bloco NÃO |
### Blocos nativos do Blockly (não precisam de `defineBlocks`)
Estes blocos estão disponíveis após `import "blockly/blocks"` — adicione-os direto em `ESTRUTURA_TOOLBOX` e em `allowedBlocks`:
| ID | Bloco |
|---|---|
| `controls_repeat_ext` | Repetir N vezes |
| `controls_whileUntil` | Enquanto / Até |
| `math_number` | Número literal |
| `text` | Texto literal (string) |
| `text_join` | Juntar textos |
| `robo_if` / `robo_if_else` | Se / Se-Senão (customizados da plataforma) |
---
## Passo 4 — `ui/constants.js` e `ui/layout.js`
Mantenha **todo código visual fora do `game.js`**. A cena deve ser legível — quem a lê não deve precisar vasculhar criação de objetos Phaser.
**`ui/constants.js`** — valores que nunca mudam:
```javascript
// Assets em /public — URL direta, sem import de módulo
export const Assets = {
CHAVES: { PERSONAGEM: "meu_personagem" },
PATHS: { PERSONAGEM: "/img/meu-personagem.png" },
};
export const Constantes = { CELL_SIZE: 80, COLS: 6, ROWS: 6 };
export const Cores = { FUNDO: 0x1a1a2e, ALVO: 0x69f0ae };
```
:::info
**Assets em `/public` vs. `src/`**
Arquivos em `public/` (ex: `public/img/logo.png`) são servidos diretamente pelo servidor web. Referencie-os com a string da URL: `"/img/logo.png"`.
Arquivos em `src/atividades/.../assets/` devem ser importados como módulos: `import img from "./assets/img.png"`. O Vite os processa e otimiza durante o build.
:::
**`ui/layout.js`** — funções que constroem a cena:
```javascript
import { Assets, Constantes, Cores } from "./constants.js";
export function montarCenario(scene, cols, rows) { /* ... */ }
export function criarPersonagem(scene, col, row) { /* ... */ }
export function criarAlvo(scene, col, row) { /* ... */ }
```
---
## Passo 5 — `hooks/setupMeuJogoAPI.js`
A **ponte** entre o código do aluno (rodando no JS-Interpreter) e a cena Phaser.
```javascript
import { ApiHelpers } from "../../../../interpreters/ApiHelpers.js";
export const setupMeuJogoAPI = (scene, config) => {
const delay = config?.animationSpeed || 200;
// Retorna a função de init do js-interpreter
return function(interpreter, globalScope) {
// Ações com animação → createAsyncFunction (aguarda callback para continuar)
ApiHelpers.registerFunction(
interpreter, globalScope,
"executarAcaoA",
ApiHelpers.createActionWrapper(scene, "executarAcaoA", delay),
true // isAsync = true
);
// Condições síncronas → createNativeFunction (retorna valor imediatamente)
ApiHelpers.registerFunction(
interpreter, globalScope,
"verificarCondicao",
ApiHelpers.createConditionWrapper(scene, "verificarCondicao"),
false // isAsync = false
);
// Obrigatório para o highlight de blocos durante a execução
ApiHelpers.registerFunction(
interpreter, globalScope,
"highlightBlock",
ApiHelpers.createHighlightWrapper(scene),
false
);
};
};
```
**Regra de ouro:**
- Funções que **animam algo**`createAsyncFunction` (`isAsync: true`)
- Funções que **retornam um valor** (sensores, condições) → `createNativeFunction` (`isAsync: false`)
---
## Passo 6 — `game.js`
A cena Phaser. Herda `BaseGameScene` e implementa os hooks do ciclo de execução.
```javascript
import Phaser from "phaser";
import { BaseGameScene } from "../../../shared/BaseGameScene.js";
import { setupMeuJogoAPI } from "./hooks/setupMeuJogoAPI.js";
import { validationSolution } from "./validation/validators.js";
import { gameConfig } from "./config/config.js";
import { Assets, Constantes } from "./ui/constants.js";
import { montarCenario, criarPersonagem, criarAlvo } from "./ui/layout.js";
export class MeuJogoScene extends BaseGameScene {
constructor() {
super("MeuJogoScene");
// Estado lógico do jogo (NÃO posição visual — isso é do Phaser)
this.posicaoLogica = { x: 0, y: 0 };
this.executionStopped = false;
}
preload() {
this.preloadGlobalAssets(); // sons globais de sucesso/falha
this.load.image(Assets.CHAVES.PERSONAGEM, Assets.PATHS.PERSONAGEM);
}
create() {
// 1. Cria o validador com referência à cena (para ler estado em tempo de validação)
this.validatorFunc = (historico) =>
validationSolution(historico, this.configFase, gameConfig, this);
// 2. Conecta o interpreter e os handlers de run/reset ao gameEventBus
this.setupStandardController(
() => setupMeuJogoAPI(this, { animationSpeed: 200 }),
this.validatorFunc
);
this.montarFase();
}
onBeforeRun() {
this.isRunning = true;
this.historico = [];
this.executionStopped = false;
}
onReset() {
this.isRunning = false;
this.executionStopped = true;
this.montarFase();
}
async onSuccess() {
this.isRunning = false;
// Animação de vitória antes do modal aparecer
return new Promise(resolve => {
this.tweens.add({ targets: this.personagemSprite, /* ... */, onComplete: resolve });
});
}
async onFailure() {
this.isRunning = false;
// Animação de falha antes do modal aparecer
return new Promise(resolve => {
this.tweens.add({ targets: this.personagemSprite, /* ... */, onComplete: resolve });
});
}
montarFase() {
if (this.personagemSprite) this.personagemSprite.destroy();
// ... destroy outros sprites
this.personagemSprite = criarPersonagem(this, 0, 0);
this.executionStopped = false;
}
// --- API exposta ao interpreter ---
executarAcaoA() {
if (this.executionStopped) return Promise.resolve();
// Lógica da ação, registro no historico, animação...
this.historico.push({ tipo: "acao_a" });
return this._animarPersonagem(() => this._checarCondicaoFim());
}
_checarCondicaoFim() {
if (this.executionStopped) return;
// ... verifica sucesso ou falha ...
// Sucesso detectado mid-execution:
this.executionStopped = true;
this.gameInterpreter.stopInternal(); // para sem marcar como parado pelo usuário
this.time.delayedCall(300, () => this.handleValidation(this.validatorFunc));
// Falha detectada mid-execution:
// this.executionStopped = true;
// this.gameInterpreter.stopInternal();
// this.time.delayedCall(100, () => this.handleFailure("Mensagem de falha"));
}
}
export const createGame = (elementoPai, configFaseAtual) => {
const scene = new MeuJogoScene();
return {
type: Phaser.AUTO,
width: /* largura */,
height: /* altura */,
parent: elementoPai,
scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
scene,
callbacks: {
// Injeta configs no registry antes do boot → BaseGameScene.init() as lê
preBoot: (game) => {
game.registry.set("configFase", configFaseAtual);
game.registry.set("gameConfig", gameConfig);
},
},
};
};
```
### Quando usar `stopInternal()` vs. deixar o interpreter terminar
| Cenário | Abordagem |
|---|---|
| Condição de fim detectada **durante** uma ação (ex: chegou ao alvo) | `stopInternal()` + `handleValidation()` com delay |
| Condição de falha detectada **durante** uma ação (ex: saiu da tela) | `stopInternal()` + `handleFailure()` com delay |
| Execução termina naturalmente (todos os blocos rodaram) | Nada — `BaseGameScene` agenda `handleValidation()` automaticamente |
---
## Passo 7 — `validation/validators.js`
```javascript
import { BaseGameValidator } from "../../../../shared/BaseGameValidator";
export class MeuJogoValidator extends BaseGameValidator {
validatePhase(history, config, gameConfig, sceneRef) {
// sceneRef é a cena Phaser — leia o estado lógico direto dela
const { posicaoLogica } = sceneRef;
const alvo = config?.alvo;
if (posicaoLogica.x === alvo.x && posicaoLogica.y === alvo.y) {
return this.success();
}
return this.failure(
gameConfig?.mensagens?.naoAlcancou || "Não chegou ao destino. Tente de novo!"
);
}
}
export function validationSolution(history, config, gameConfig, sceneRef) {
return new MeuJogoValidator().validate(history, config, gameConfig, sceneRef);
}
```
`BaseGameValidator.validate()` já executa antes do seu código:
1. Verifica se a config da fase existe.
2. Verifica se o `historico` tem ao menos uma ação (evita validação de tela em branco).
3. (Opcional) Verifica sequência exata via `configFase.expectedSequence`.
Implemente apenas as **regras específicas** do seu jogo em `validatePhase()`.
---
## Passo 8 — `MeuJogoGame.jsx`
O componente React de entrada segue sempre o mesmo padrão:
```jsx
import React, { useEffect, useMemo } from "react";
import GameBase from "../../../components/game/GameBase";
import GameEditor from "../../../components/game/GameEditor";
import BlocklyEditor from "../../../components/game/editors/BlocklyEditor";
import { createGame } from "./game";
import { gameConfig } from "./config/config";
import { generateDynamicToolbox, registerBlocks } from "./blocks/blocks";
import { GameStateProvider, useGameState } from "../../../contexts/GameStateContext";
function MeuJogoContent() {
const { setFailureMessage } = useGameState();
useEffect(() => { registerBlocks(); }, []);
const toolboxGenerator = useMemo(
() => (allowedBlocks) => generateDynamicToolbox(allowedBlocks),
[]
);
return (
<GameBase
gameFactory={createGame}
gameConfig={gameConfig}
customFailureHandler={setFailureMessage}
failureHandler={setFailureMessage}
>
<GameEditor>
<BlocklyEditor toolboxGenerator={toolboxGenerator} />
</GameEditor>
</GameBase>
);
}
export default function MeuJogoGame() {
return (
<GameStateProvider gameConfig={gameConfig}>
<MeuJogoContent />
</GameStateProvider>
);
}
```
---
## Passo 9 — Registrar a rota em `App.jsx`
```jsx
// app/src/App.jsx
// 1. Adicionar o lazy import com os outros jogos:
const MeuJogoGame = lazy(() => import("./atividades/programacao/meu-jogo/MeuJogoGame"));
// 2. Adicionar a rota dentro de <Routes>:
<Route path="/atividades/programacao/meu-jogo" element={<MeuJogoGame />} />
```
---
## Checklist final
Antes de considerar a atividade pronta, verifique:
- [ ] A atividade roda sem erros no console do navegador
- [ ] O fluxo de **Sucesso** dispara o modal corretamente
- [ ] O fluxo de **Falha** dispara o modal com a mensagem certa
- [ ] O botão **Reset** restaura o estado visual e lógico da fase
- [ ] O `timeout` da fase é suficiente para o aluno executar a solução correta
- [ ] O `maxBlocks` é suficiente para a solução ótima, mas não para a solução "preguiçosa"
- [ ] As mensagens de erro são compreensíveis para um aluno iniciante
- [ ] A rota está registrada em `App.jsx`
- [ ] O código visual (criação de sprites, cores, fontes) está em `ui/`, não em `game.js`

View File

@@ -0,0 +1,108 @@
---
sidebar_position: 2
title: "Usando os Exemplos"
---
As atividades `Exemplo` e `Exemplo 2` foram criadas especificamente como **código de referência comentado** para novos desenvolvedores. Elas implementam o fluxo arquitetural completo na sua forma mais simples possível.
## Atividade Exemplo — movimento em grade
**Rota:** `/atividades/programacao/exemplo`
**Código:** `app/src/atividades/programacao/exemplo/`
O aluno move o logo da Decoda em uma grade 5×5 até um alvo verde usando blocos de movimento e repetição.
```
Conceito ensinado: Sequenciamento + Laços de Repetição
Blocos: mover para DIREITA, mover para BAIXO, repetir N vezes, número
Limite: 6 blocos → força o uso de loops em vez de repetição manual
Sucesso: sprite atinge a célula (4,4)
Falha: sprite sai dos limites da grade
```
### O que cada arquivo demonstra
| Arquivo | O que ensina ao desenvolvedor |
|---|---|
| `ui/constants.js` | Como definir assets, cores e dimensões separados do código de jogo |
| `ui/layout.js` | Como criar grade, alvo e sprite em funções isoladas (o `game.js` fica limpo) |
| `game.js``preload()` | Como carregar um asset de `/public` via URL: `this.load.image(chave, "/img/logo.png")` |
| `game.js``_checarAlvo()` | Como detectar condição de sucesso **durante** a execução e usar `stopInternal()` |
| `game.js``_falharSaida()` | Como detectar condição de falha e acionar `handleFailure()` antes que o interpreter termine |
| `hooks/setupExemploAPI.js` | Como registrar funções **assíncronas** no JS-Interpreter via `createAsyncFunction` |
| `validation/validators.js` | Como estender `BaseGameValidator` e ler estado da cena via `sceneRef` |
### Solução de referência
```javascript
// Solução ótima — exatamente 6 blocos
repetir(4) { moverDireita(); }
repetir(4) { moverBaixo(); }
```
---
## Atividade Exemplo 2 — texto
**Rota:** `/atividades/programacao/exemplo2`
**Código:** `app/src/atividades/programacao/exemplo2/`
O aluno usa o bloco `imprimir` com texto (literal ou concatenado) para exibir exatamente `"SOBERANIA DIGITAL"` no display.
```
Conceito ensinado: Strings e Concatenação de texto
Blocos: imprimir, texto (literal), juntar texto
Limite: 4 blocos → comporta tanto a solução simples quanto a com concatenação
Sucesso: scene.textoAtual === "SOBERANIA DIGITAL"
Falha: qualquer outro texto impresso
```
### O que cada arquivo demonstra
| Arquivo | O que ensina ao desenvolvedor |
|---|---|
| `ui/constants.js` | Estilos de texto Phaser centralizados (fontes, cores) |
| `ui/layout.js` | Como criar um display e **retornar a referência** para a cena atualizá-la |
| `hooks/setupExemplo2API.js` | Como registrar funções **síncronas** via `createNativeFunction`, e como extrair valores de string do JS-Interpreter |
| `game.js``imprimir()` | Exemplo de API que atualiza estado interno (`textoAtual`) e visual (`textoDisplay`) ao mesmo tempo |
| `validation/validators.js` | Validação por comparação direta de valor de estado (sem percorrer `historico`) |
### Soluções de referência
```javascript
// Solução simples — 2 blocos
imprimir("SOBERANIA DIGITAL");
// Solução com concatenação — 4 blocos (ensina text_join)
imprimir(juntar("SOBERANIA", " DIGITAL"));
```
---
## Como usar os exemplos como base
### Opção A — copiar e renomear
1. Duplique a pasta `exemplo/` ou `exemplo2/` com o nome da sua atividade.
2. Renomeie os arquivos (ex: `ExemploGame.jsx``MeuJogoGame.jsx`).
3. Faça um `Find & Replace` de `"exemplo"``"meu-jogo"` e `ExemploScene``MeuJogoScene` em todos os arquivos.
4. Ajuste `config.js` com os dados da sua atividade.
5. Registre a rota em `app/src/App.jsx`.
### Opção B — seguir o passo a passo
Use os exemplos como **resposta de gabarito** enquanto segue o [Passo a Passo](./passo-a-passo). Cada etapa do guia aponta para o arquivo correspondente no exemplo.
---
## Arquivos que NÃO precisam mudar
Estes fazem parte da infraestrutura da plataforma e são herdados automaticamente:
- `BaseGameScene` — ciclo de execução completo
- `BaseGameValidator` — checks genéricos (histórico vazio, sequência esperada)
- `GameInterpreter` — execução segura do código do aluno
- `ApiHelpers` — utilitários para registrar funções no interpreter
- `GameBase`, `GameEditor`, `BlocklyEditor` — componentes React do shell do jogo
- `GameStateProvider` / `useGameState` — contexto de estado (fases, debug, falhas)
- `gameEventBus` — canal de comunicação Phaser → React