Estacionar Ônibus - Fases
by DriftPenguin96218 lines8.6 KB
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Estacionar Ônibus - Fases</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
canvas { border: 3px solid #2563eb; background: #e2e8f0; cursor: pointer; }
body { font-family: Arial, sans-serif; background: #f8fafc; }
</style>
</head>
<body class="p-4 max-w-3xl mx-auto">
<h1 class="text-2xl font-bold text-center text-blue-700 mb-2">🚌 Estacionar Ônibus</h1>
<div class="text-center mb-3">
<span class="text-lg font-semibold">Fase: <span id="faseAtual">1</span></span>
<p class="text-gray-700 mt-1" id="descricaoFase">Posicione o ônibus na vaga reta</p>
</div>
<canvas id="jogo" width="600" height="400" class="mx-auto rounded-lg shadow-lg"></canvas>
<div class="flex flex-wrap justify-center gap-3 mt-4">
<button onclick="mover('frente')" class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded text-lg">↑ Frente</button>
<button onclick="mover('tras')" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded text-lg">↓ Trás</button>
<button onclick="virar('esq')" class="bg-amber-500 hover:bg-amber-600 text-white px-4 py-2 rounded text-lg">← Esquerda</button>
<button onclick="virar('dir')" class="bg-amber-500 hover:bg-amber-600 text-white px-4 py-2 rounded text-lg">→ Direita</button>
<button onclick="reiniciarFase()" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded text-lg">🔄 Reiniciar</button>
</div>
<div id="mensagem" class="mt-4 text-center text-xl font-bold h-8"></div>
<script>
const canvas = document.getElementById('jogo');
const ctx = canvas.getContext('2d');
// Dados das fases
const fases = [
{
descricao: "Fase 1: Vaga reta e ampla — sem obstáculos",
vaga: { x: 220, y: 50, larg: 160, alt: 80, angulo: 0 },
obstaculos: [],
tolerancia: 12
},
{
descricao: "Fase 2: Vaga levemente inclinada",
vaga: { x: 240, y: 60, larg: 160, alt: 80, angulo: 15 },
obstaculos: [],
tolerancia: 10
},
{
descricao: "Fase 3: Vaga com poste ao lado",
vaga: { x: 220, y: 50, larg: 160, alt: 80, angulo: 0 },
obstaculos: [{ tipo: 'poste', x: 180, y: 60, raio: 12 }],
tolerancia: 9
},
{
descricao: "Fase 4: Vaga estreita e inclinada",
vaga: { x: 250, y: 70, larg: 140, alt: 75, angulo: -20 },
obstaculos: [{ tipo: 'caixa', x: 200, y: 40, larg: 30, alt: 25 }],
tolerancia: 8
},
{
descricao: "Fase 5: Desafio final — vaga apertada com dois obstáculos",
vaga: { x: 230, y: 65, larg: 145, alt: 78, angulo: 10 },
obstaculos: [
{ tipo: 'poste', x: 190, y: 55, raio: 12 },
{ tipo: 'caixa', x: 350, y: 80, larg: 32, alt: 28 }
],
tolerancia: 7
}
];
let fase = 0;
let onibus = { x: 300, y: 320, angulo: 0, larg: 120, alt: 40 };
const passo = 6;
const rotacao = 2;
function iniciarFase() {
document.getElementById('faseAtual').textContent = fase + 1;
document.getElementById('descricaoFase').textContent = fases[fase].descricao;
onibus = { x: 300, y: 320, angulo: 0, larg: 120, alt: 40 };
desenharCena();
}
function desenharCena() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
desenharVaga();
desenharObstaculos();
desenharOnibus();
verificarPosicao();
}
function desenharVaga() {
const v = fases[fase].vaga;
ctx.save();
ctx.translate(v.x + v.larg / 2, v.y + v.alt / 2);
ctx.rotate(v.angulo * Math.PI / 180);
ctx.fillStyle = '#bfdbfe';
ctx.fillRect(-v.larg / 2, -v.alt / 2, v.larg, v.alt);
ctx.strokeStyle = '#1e40af';
ctx.lineWidth = 3;
ctx.setLineDash([10, 8]);
ctx.strokeRect(-v.larg / 2, -v.alt / 2, v.larg, v.alt);
ctx.restore();
}
function desenharObstaculos() {
fases[fase].obstaculos.forEach(obs => {
if (obs.tipo === 'poste') {
ctx.fillStyle = '#4b5563';
ctx.beginPath();
ctx.arc(obs.x, obs.y, obs.raio, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#111';
ctx.lineWidth = 2;
ctx.stroke();
} else if (obs.tipo === 'caixa') {
ctx.fillStyle = '#92400e';
ctx.fillRect(obs.x, obs.y, obs.larg, obs.alt);
ctx.strokeStyle = '#451a03';
ctx.lineWidth = 2;
ctx.strokeRect(obs.x, obs.y, obs.larg, obs.alt);
}
});
}
function desenharOnibus() {
ctx.save();
ctx.translate(onibus.x + onibus.larg / 2, onibus.y + onibus.alt / 2);
ctx.rotate(onibus.angulo * Math.PI / 180);
ctx.fillStyle = '#2563eb';
ctx.fillRect(-onibus.larg / 2, -onibus.alt / 2, onibus.larg, onibus.alt);
ctx.fillStyle = '#bfdbfe';
ctx.fillRect(-onibus.larg / 2 + 10, -onibus.alt / 2 + 5, onibus.larg - 20, onibus.alt - 10);
ctx.restore();
}
function mover(direcao) {
const rad = onibus.angulo * Math.PI / 180;
const desloc = direcao === 'frente' ? passo : -passo;
onibus.x += Math.sin(rad) * desloc;
onibus.y -= Math.cos(rad) * desloc;
limitarMovimento();
desenharCena();
}
function virar(lado) {
onibus.angulo += lado === 'dir' ? rotacao : -rotacao;
desenharCena();
}
function limitarMovimento() {
onibus.x = Math.max(20, Math.min(canvas.width - onibus.larg - 20, onibus.x));
onibus.y = Math.max(20, Math.min(canvas.height - onibus.alt - 20, onibus.y));
}
function verificarPosicao() {
const v = fases[fase].vaga;
const tol = fases[fase].tolerancia;
const centroOnibus = {
x: onibus.x + onibus.larg / 2,
y: onibus.y + onibus.alt / 2
};
const centroVaga = {
x: v.x + v.larg / 2,
y: v.y + v.alt / 2
};
const distX = Math.abs(centroOnibus.x - centroVaga.x);
const distY = Math.abs(centroOnibus.y - centroVaga.y);
const difAng = Math.abs(onibus.angulo - v.angulo);
const bateu = verificarColisao();
const msgEl = document.getElementById('mensagem');
if (bateu) {
msgEl.textContent = "⚠️ Cuidado! Bateu em obstáculo";
msgEl.className = "mt-4 text-center text-xl font-bold text-red-600";
return;
}
if (distX < tol && distY < tol && difAng < tol) {
msgEl.textContent = "✅ Estacionado! Passar para próxima fase";
msgEl.className = "mt-4 text-center text-xl font-bold text-green-600";
if (fase < fases.length - 1) {
setTimeout(() => { fase++; iniciarFase(); }, 2200);
} else {
msgEl.textContent = "🏆 Parabéns! Todas as fases concluídas";
}
} else {
msgEl.textContent = "";
}
}
function verificarColisao() {
return fases[fase].obstaculos.some(obs => {
if (obs.tipo === 'poste') {
const dx = (onibus.x + onibus.larg / 2) - obs.x;
const dy = (onibus.y + onibus.alt / 2) - obs.y;
return Math.sqrt(dx * dx + dy * dy) < obs.raio + onibus.alt / 2 + 5;
} else {
return onibus.x < obs.x + obs.larg &&
onibus.x + onibus.larg > obs.x &&
onibus.y < obs.y + obs.alt &&
onibus.y + onibus.alt > obs.y;
}
});
}
function reiniciarFase() {
iniciarFase();
}
iniciarFase();
</script>
</body>
</html>
Game Source: Estacionar Ônibus - Fases
Creator: DriftPenguin96
Libraries: none
Complexity: complex (218 lines, 8.6 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: estacionar-nibus-fases-driftpenguin96" to link back to the original. Then publish at arcadelab.ai/publish.