<!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;
ctx.fillStyle = '#f1c40f';
ctx.beginPath();
ctx.arc(x + w / 2, y + h * 0.2, w * 0.18, 0, Math.PI * 2);
ctx.fill();
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();
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();
});
ctx.fillStyle = '#2980b9';
ctx.fillRect(x + w * 0.1, y + h * 0.3, w * 0.8, h * 0.5);
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);
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);
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);
}
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>