Mini Mario – HTML, CSS, JS

Share
mario-logo
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Mini Mario Platformer</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: linear-gradient(to bottom, #87ceeb 0%, #fff 80%);
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
            font-family: sans-serif;
            position: relative;
        }

        canvas {
            background: transparent;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
            border-radius: 8px;
        }

        #score {
            position: absolute;
            top: 20px;
            right: 30px;
            font-size: 1.2rem;
            color: #333;
        }
    </style>
</head>
<body>
    <div id="score">Coins: 0</div>
    <canvas id="game" width="800" height="450"></canvas>

    <script>
        const cvs = document.getElementById('game');
        const ctx = cvs.getContext('2d');
        const scoreEl = document.getElementById('score');

        const GRAVITY = 0.5, FRICTION = 0.8;
        const keys = { left: false, right: false, up: false };

        const player = {
            x: 100, y: 0, w: 32, h: 48,
            vx: 0, vy: 0, onGround: false,

            draw() {
                let x = this.x, y = this.y, w = this.w, h = this.h;

                // Head
                ctx.fillStyle = '#f1c40f';
                ctx.beginPath();
                ctx.arc(x + w / 2, y + h * 0.2, w * 0.18, 0, Math.PI * 2);
                ctx.fill();

                // Hat
                ctx.fillStyle = '#c0392b';
                ctx.fillRect(x + w * 0.15, y + h * 0.02, w * 0.7, h * 0.17);
                ctx.beginPath();
                ctx.arc(x + w / 2, y + h * 0.12, w * 0.3, Math.PI, 0);
                ctx.fill();

                // Eyes
                ctx.fillStyle = '#34495e';
                [0.4, 0.6].forEach(mx => {
                    ctx.beginPath();
                    ctx.arc(x + w * mx, y + h * 0.2, w * 0.04, 0, Math.PI * 2);
                    ctx.fill();
                });

                // Body
                ctx.fillStyle = '#2980b9';
                ctx.fillRect(x + w * 0.1, y + h * 0.3, w * 0.8, h * 0.5);

                // Straps
                ctx.fillStyle = '#c0392b';
                ctx.fillRect(x + w * 0.15, y + h * 0.3, w * 0.15, h * 0.28);
                ctx.fillRect(x + w * 0.7, y + h * 0.3, w * 0.15, h * 0.28);

                // Shoes
                ctx.fillStyle = '#2c3e50';
                ctx.fillRect(x, y + h * 0.8, w * 0.4, h * 0.2);
                ctx.fillRect(x + w * 0.6, y + h * 0.8, w * 0.4, h * 0.2);
            }
        };

        const platforms = [
            { x: 0, y: 400, w: 800, h: 50 },
            { x: 200, y: 330, w: 120, h: 20 },
            { x: 400, y: 270, w: 150, h: 20 },
            { x: 600, y: 200, w: 100, h: 20 }
        ];

        let coins = [
            { x: 230, y: 300, r: 8, collected: false },
            { x: 470, y: 240, r: 8, collected: false },
            { x: 630, y: 170, r: 8, collected: false }
        ];
        let coinCount = 0;

        window.addEventListener('keydown', e => {
            if (e.code === 'ArrowLeft') keys.left = true;
            if (e.code === 'ArrowRight') keys.right = true;
            if (e.code === 'ArrowUp') keys.up = true;
        });

        window.addEventListener('keyup', e => {
            if (e.code === 'ArrowLeft') keys.left = false;
            if (e.code === 'ArrowRight') keys.right = false;
            if (e.code === 'ArrowUp') keys.up = false;
        });

        function update() {
            if (keys.left) player.vx -= 0.5;
            if (keys.right) player.vx += 0.5;
            if (keys.up && player.onGround) {
                player.vy = -12;
                player.onGround = false;
            }

            player.vy += GRAVITY;
            player.x += player.vx;
            player.y += player.vy;
            player.vx *= FRICTION;

            player.onGround = false;
            for (let p of platforms) {
                if (
                    player.x + player.w > p.x &&
                    player.x < p.x + p.w &&
                    player.y + player.h > p.y &&
                    player.y + player.h < p.y + p.h + player.vy
                ) {
                    player.y = p.y - player.h;
                    player.vy = 0;
                    player.onGround = true;
                }
            }

            if (player.x < 0) player.x = 0;
            if (player.x + player.w > cvs.width) player.x = cvs.width - player.w;
            if (player.y > cvs.height) reset();

            for (let c of coins) {
                if (!c.collected) {
                    const dx = player.x + player.w / 2 - c.x;
                    const dy = player.y + player.h / 2 - c.y;
                    if (Math.hypot(dx, dy) < c.r + player.w / 4) {
                        c.collected = true;
                        coinCount++;
                        scoreEl.textContent = 'Coins: ' + coinCount;
                    }
                }
            }
        }

        function draw() {
            ctx.clearRect(0, 0, cvs.width, cvs.height);

            // Draw platforms
            for (let p of platforms) {
                ctx.fillStyle = '#7f8c8d';
                ctx.fillRect(p.x, p.y, p.w, p.h);
                ctx.strokeStyle = '#2c3e50';
                ctx.strokeRect(p.x, p.y, p.w, p.h);
            }

            // Draw coins
            for (let c of coins) {
                if (!c.collected) {
                    ctx.beginPath();
                    ctx.arc(c.x, c.y, c.r, 0, Math.PI * 2);
                    ctx.fillStyle = '#f1c40f';
                    ctx.fill();
                    ctx.strokeStyle = '#e67e22';
                    ctx.stroke();
                }
            }

            player.draw();
        }

        function loop() {
            update();
            draw();
            requestAnimationFrame(loop);
        }

        function reset() {
            player.x = 100;
            player.y = 0;
            player.vx = 0;
            player.vy = 0;
            coins.forEach(c => c.collected = false);
            coinCount = 0;
            scoreEl.textContent = 'Coins: 0';
        }

        loop();
    </script>
</body>
</html>