🎮ArcadeLab

Estacionar Ônibus - Fases

by DriftPenguin96
218 lines8.6 KB
▶ Play
<!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.