🎮ArcadeLab

Mobile Mario Game

by PrismCobra26
285 lines8.1 KB
▶ Play
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Mobile Mario Game</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; touch-action: manipulation; }
  body {
    background: #87CEEB;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 100vh;
    overflow: hidden;
    font-family: Arial;
  }
  canvas {
    display: block;
    width: 100vw;
    height: 60vh;
    background: linear-gradient(#87CEEB, #E0F7FF);
  }
  .controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100vw;
    height: 40vh;
    padding: 10px 20px;
    background: #333;
  }
  .btn {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    border: none;
    font-size: 30px;
    font-weight: bold;
    color: white;
    display: flex;
    justify-content: center;
    align-items: center;
    user-select: none;
    -webkit-user-select: none;
    touch-action: none;
    box-shadow: 0 4px 0 rgba(0,0,0,0.3);
  }
  .btn:active { transform: translateY(4px); box-shadow: none; }
  .left { background: #E74C3C; }
  .right { background: #E74C3C; }
  .jump { background: #27AE60; width: 100px; height: 100px; font-size: 20px; }
  .btn-group { display: flex; gap: 15px; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<div class="controls">
  <div class="btn-group">
    <button class="btn left" id="leftBtn">◀</button>
    <button class="btn right" id="rightBtn">▶</button>
  </div>
  <button class="btn jump" id="jumpBtn">JUMP</button>
</div>

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

function resize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight * 0.6;
}
resize();
window.addEventListener('resize', resize);

const keys = { left: false, right: false, up: false };

document.getElementById('leftBtn').addEventListener('touchstart', e => { e.preventDefault(); keys.left = true; });
document.getElementById('leftBtn').addEventListener('touchend', e => { e.preventDefault(); keys.left = false; });
document.getElementById('rightBtn').addEventListener('touchstart', e => { e.preventDefault(); keys.right = true; });
document.getElementById('rightBtn').addEventListener('touchend', e => { e.preventDefault(); keys.right = false; });
document.getElementById('jumpBtn').addEventListener('touchstart', e => { e.preventDefault(); keys.up = true; });
document.getElementById('jumpBtn').addEventListener('touchend', e => { e.preventDefault(); keys.up = false; });

// Also support mouse for testing
document.getElementById('leftBtn').addEventListener('mousedown', () => keys.left = true);
document.getElementById('leftBtn').addEventListener('mouseup', () => keys.left = false);
document.getElementById('rightBtn').addEventListener('mousedown', () => keys.right = true);
document.getElementById('rightBtn').addEventListener('mouseup', () => keys.right = false);
document.getElementById('jumpBtn').addEventListener('mousedown', () => keys.up = true);
document.getElementById('jumpBtn').addEventListener('mouseup', () => keys.up = false);

const player = {
  x: 50, y: 300, w: 30, h: 40,
  vx: 0, vy: 0,
  speed: 4, jumpPower: -12,
  onGround: false
};

const gravity = 0.6;
const platforms = [
  {x: 0, y: 420, w: 900, h: 40},
  {x: 150, y: 340, w: 100, h: 20},
  {x: 300, y: 280, w: 120, h: 20},
  {x: 500, y: 220, w: 100, h: 20},
  {x: 680, y: 160, w: 120, h: 20},
  {x: 400, y: 360, w: 80, h: 20}
];

const coins = [
  {x: 180, y: 300, r: 8, collected: false},
  {x: 340, y: 240, r: 8, collected: false},
  {x: 540, y: 180, r: 8, collected: false},
  {x: 720, y: 120, r: 8, collected: false}
];

const enemies = [
  {x: 600, y: 390, w: 30, h: 30, vx: -1.5, minX: 550, maxX: 750, alive: true},
  {x: 250, y: 310, w: 30, h: 30, vx: 1.2, minX: 150, maxX: 280, alive: true}
];

let score = 0;
let gameOver = false;
let win = false;

function rectCollide(a, b) {
  return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
}

function update() {
  if (gameOver || win) return;

  if (keys.left) player.vx = -player.speed;
  else if (keys.right) player.vx = player.speed;
  else { player.vx *= 0.8; if (Math.abs(player.vx) < 0.1) player.vx = 0; }

  if (keys.up && player.onGround) {
    player.vy = player.jumpPower;
    player.onGround = false;
  }

  player.vy += gravity;
  player.x += player.vx;

  for (let p of platforms) {
    if (rectCollide(player, p)) {
      if (player.vx > 0) player.x = p.x - player.w;
      else if (player.vx < 0) player.x = p.x + p.w;
      player.vx = 0;
    }
  }

  player.y += player.vy;
  player.onGround = false;

  for (let p of platforms) {
    if (rectCollide(player, p)) {
      if (player.vy > 0) {
        player.y = p.y - player.h;
        player.vy = 0;
        player.onGround = true;
      } else if (player.vy < 0) {
        player.y = p.y + p.h;
        player.vy = 0;
      }
    }
  }

  if (player.x < 0) player.x = 0;
  if (player.x + player.w > canvas.width) player.x = canvas.width - player.w;
  if (player.y > canvas.height) gameOver = true;

  for (let c of coins) {
    if (!c.collected) {
      const coinRect = {x: c.x - c.r, y: c.y - c.r, w: c.r * 2, h: c.r * 2};
      if (rectCollide(player, coinRect)) { c.collected = true; score += 10; }
    }
  }

  for (let e of enemies) {
    if (!e.alive) continue;
    e.x += e.vx;
    if (e.x < e.minX || e.x + e.w > e.maxX) e.vx *= -1;

    if (rectCollide(player, e)) {
      if (player.vy > 0 && player.y + player.h - player.vy <= e.y + 10) {
        e.alive = false;
        player.vy = -8;
        score += 50;
      } else gameOver = true;
    }
  }

  if (player.x + player.w > canvas.width - 40) win = true;
}

function draw() {
  ctx.fillStyle = '#87CEEB';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.fillStyle = 'white';
  ctx.beginPath();
  ctx.arc(canvas.width * 0.2, 60, 25, 0, Math.PI * 2);
  ctx.arc(canvas.width * 0.25, 50, 35, 0, Math.PI * 2);
  ctx.arc(canvas.width * 0.3, 60, 25, 0, Math.PI * 2);
  ctx.fill();

  for (let p of platforms) {
    ctx.fillStyle = '#8B4513';
    ctx.fillRect(p.x, p.y, p.w, p.h);
    ctx.fillStyle = '#228B22';
    ctx.fillRect(p.x, p.y, p.w, 5);
  }

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

  for (let e of enemies) {
    if (e.alive) {
      ctx.fillStyle = '#8B0000';
      ctx.fillRect(e.x, e.y, e.w, e.h);
      ctx.fillStyle = 'white';
      ctx.fillRect(e.x + 5, e.y + 8, 6, 6);
      ctx.fillRect(e.x + 19, e.y + 8, 6, 6);
      ctx.fillStyle = 'black';
      ctx.fillRect(e.x + 7, e.y + 10, 3, 3);
      ctx.fillRect(e.x + 21, e.y + 10, 3, 3);
    }
  }

  ctx.fillStyle = '#E52521';
  ctx.fillRect(player.x, player.y, player.w, player.h);
  ctx.fillStyle = '#1E90FF';
  ctx.fillRect(player.x + 4, player.y + 20, player.w - 8, player.h - 20);
  ctx.fillStyle = '#FFDAB9';
  ctx.fillRect(player.x + 5, player.y + 5, 20, 12);
  ctx.fillStyle = 'black';
  ctx.fillRect(player.x + 10, player.y + 8, 3, 4);
  ctx.fillRect(player.x + 18, player.y + 8, 3, 4);

  ctx.fillStyle = 'black';
  ctx.font = '20px Arial';
  ctx.fillText('Score: ' + score, 20, 30);

  if (gameOver) {
    ctx.fillStyle = 'rgba(0,0,0,0.6)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.font = '40px Arial';
    ctx.fillText('Game Over!', canvas.width/2 - 110, canvas.height/2);
    ctx.font = '24px Arial';
    ctx.fillText('Score: ' + score, canvas.width/2 - 60, canvas.height/2 + 50);
  }

  if (win) {
    ctx.fillStyle = 'rgba(0,0,0,0.6)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'white';
    ctx.font = '40px Arial';
    ctx.fillText('You Win!', canvas.width/2 - 90, canvas.height/2);
    ctx.font = '24px Arial';
    ctx.fillText('Score: ' + score, canvas.width/2 - 60, canvas.height/2 + 50);
  }
}

function gameLoop() {
  update();
  draw();
  requestAnimationFrame(gameLoop);
}
gameLoop();
</script>
</body>
</html>

Game Source: Mobile Mario Game

Creator: PrismCobra26

Libraries: none

Complexity: complex (285 lines, 8.1 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: mobile-mario-game-prismcobra26" to link back to the original. Then publish at arcadelab.ai/publish.