<!
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-
scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>8-Bit Asteroids Mobile</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #000;
color: #0f0;
font-family: 'Courier New', Courier, monospace;
overflow: hidden; /* Prevent scrollbars */
touch-action: none; /* Prevent zooming/scrolling on touch
*/
}
canvas {
display: block;
}
#ui {
position: absolute;
top: 20px;
left: 20px;
font-size: 20px;
pointer-events: none;
}
#game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 30px;
text-align: center;
display: none;
background-color: #000;
border: 2px solid #0f0;
padding: 20px;
z-index: 10;
}
/* TOUCH CONTROLS STYLING */
.controls-area {
position: absolute;
bottom: 20px;
width: 100%;
height: 120px;
pointer-events: none; /* Let clicks pass through empty
spaces */
display: flex;
justify-content: space-between;
padding: 0 20px;
box-sizing: border-box;
}
.d-pad, .action-pad {
pointer-events: auto; /* Re-enable clicks for buttons */
display: flex;
gap: 10px;
}
.btn {
width: 70px;
height: 70px;
border: 2px solid #0f0;
border-radius: 50%;
background: rgba(0, 255, 0, 0.1);
color: #0f0;
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
-webkit-user-select: none;
}
.btn:active {
background: rgba(0, 255, 0, 0.4);
}
/* Layout adjustment for controls */
.d-pad { align-items: flex-end; }
.action-pad { align-items: flex-end; }
/* Specific positions */
#btn-thrust { margin-bottom: 40px; } /* Offset thrust button
*/
</style>
</head>
<body>
<div id="ui">SCORE: <span id="score">0</span></div>
<div id="game-over">
GAME OVER<br>
<span style="font-size: 16px">Tap Here to Restart</span>
</div>
<div class="controls-area">
<div class="d-pad">
<div id="btn-left" class="btn">◄</div>
<div id="btn-right" class="btn">►</div>
</div>
<div class="action-pad">
<div id="btn-shoot" class="btn">●</div>
<div id="btn-thrust" class="btn">▲</div>
</div>
</div>
<canvas id="gameCanvas"></canvas>
<script>
/** * CONFIGURATION */
const FPS = 60;
const FRICTION = 0.7;
const SHIP_SIZE = 20; // Slightly smaller for mobile screens
const TURN_SPEED = 360;
const SHIP_THRUST = 5;
const LASER_MAX = 10;
const LASER_SPD = 500;
const ROIDS_NUM = 4; // Fewer asteroids for small screens
const ROIDS_SIZE = 80;
const ROIDS_SPD = 50;
/** SETUP */
const canv = [Link]("gameCanvas");
const ctx = [Link]("2d");
const scoreEl = [Link]("score");
const gameOverEl = [Link]("game-over");
// Dimensions
[Link] = [Link];
[Link] = [Link];
// Game State
let level, roids, ship, score, gameOver;
// Input State
const input = {
left: false,
right: false,
thrust: false,
shoot: false
};
// Initialize
newGame();
setupTouchControls();
// Game Loop
setInterval(update, 1000 / FPS);
function newGame() {
gameOver = false;
[Link] = "none";
score = 0;
level = 0;
ship = newShip();
createAsteroidBelt();
}
function newShip() {
return {
x: [Link] / 2,
y: [Link] / 2,
a: 90 / 180 * [Link],
r: SHIP_SIZE / 2,
canShoot: true,
dead: false,
lasers: [],
rot: 0,
thrusting: false,
thrust: { x: 0, y: 0 }
}
}
function createAsteroidBelt() {
roids = [];
let x, y;
for (let i = 0; i < ROIDS_NUM + level; i++) {
do {
x = [Link]([Link]() * [Link]);
y = [Link]([Link]() * [Link]);
} while (distBetweenPoints(ship.x, ship.y, x, y) <
ROIDS_SIZE * 2 + ship.r);
[Link](newAsteroid(x, y, [Link](ROIDS_SIZE / 2)));
}
}
function newAsteroid(x, y, r) {
const lvlMult = 1 + 0.1 * level;
return {
x: x, y: y,
xv: [Link]() * ROIDS_SPD * lvlMult / FPS *
([Link]() < 0.5 ? 1 : -1),
yv: [Link]() * ROIDS_SPD * lvlMult / FPS *
([Link]() < 0.5 ? 1 : -1),
r: r,
a: [Link]() * [Link] * 2,
rot: ([Link]() - 0.5) * (ROIDS_SPD / 100)
};
}
function distBetweenPoints(x1, y1, x2, y2) {
return [Link]([Link](x2 - x1, 2) + [Link](y2 - y1,
2));
}
// --- INPUT HANDLING (TOUCH + KEYBOARD) ---
function setupTouchControls() {
// Helper to bind events
const bindBtn = (id, key) => {
const el = [Link](id);
// Touch events
[Link]('touchstart', (e) =>
{ [Link](); input[key] = true; handleInput(); });
[Link]('touchend', (e) =>
{ [Link](); input[key] = false; handleInput(); });
// Mouse events (for testing on PC)
[Link]('mousedown', (e) =>
{ [Link](); input[key] = true; handleInput(); });
[Link]('mouseup', (e) =>
{ [Link](); input[key] = false; handleInput(); });
};
bindBtn('btn-left', 'left');
bindBtn('btn-right', 'right');
bindBtn('btn-thrust', 'thrust');
// Shooting is slightly different (trigger once on press)
const shootBtn = [Link]('btn-shoot');
const shootStart = (e) => {
[Link]();
if(gameOver) return;
shootLaser();
};
[Link]('touchstart', shootStart);
[Link]('mousedown', shootStart);
// Game Over Restart
[Link]('touchstart', () =>
{ if(gameOver) newGame(); });
[Link]('click', () => { if(gameOver)
newGame(); });
}
// Process Input State
function handleInput() {
if(gameOver) return;
// Rotation
if([Link]) [Link] = TURN_SPEED / 180 * [Link] / FPS;
else if([Link]) [Link] = -TURN_SPEED / 180 * [Link] /
FPS;
else [Link] = 0;
// Thrust
[Link] = [Link];
}
function shootLaser() {
if ([Link] < LASER_MAX) {
[Link]({
x: ship.x + 4 / 3 * ship.r * [Link](ship.a),
y: ship.y - 4 / 3 * ship.r * [Link](ship.a),
xv: LASER_SPD * [Link](ship.a) / FPS,
yv: -LASER_SPD * [Link](ship.a) / FPS,
dist: 0
});
}
}
function update() {
if(gameOver) return;
// Draw Space
[Link] = "black";
[Link](0, 0, [Link], [Link]);
// Draw Ship
if (![Link]) {
[Link] = "#0f0";
[Link] = SHIP_SIZE / 15;
[Link]();
[Link](ship.x + 4/3 * ship.r * [Link](ship.a), ship.y
- 4/3 * ship.r * [Link](ship.a));
[Link](ship.x - ship.r * (2/3 * [Link](ship.a) +
[Link](ship.a)), ship.y + ship.r * (2/3 * [Link](ship.a) -
[Link](ship.a)));
[Link](ship.x - ship.r * (2/3 * [Link](ship.a) -
[Link](ship.a)), ship.y + ship.r * (2/3 * [Link](ship.a) +
[Link](ship.a)));
[Link]();
[Link]();
if ([Link]) {
[Link] = "#0f0";
[Link]();
[Link](ship.x - ship.r * (2/3 * [Link](ship.a) +
0.5 * [Link](ship.a)), ship.y + ship.r * (2/3 * [Link](ship.a) -
0.5 * [Link](ship.a)));
[Link](ship.x - ship.r * 6/3 * [Link](ship.a), ship.y
+ ship.r * 6/3 * [Link](ship.a));
[Link](ship.x - ship.r * (2/3 * [Link](ship.a) - 0.5 *
[Link](ship.a)), ship.y + ship.r * (2/3 * [Link](ship.a) + 0.5 *
[Link](ship.a)));
[Link]();
[Link]();
}
}
// Draw Lasers
for (let i = 0; i < [Link]; i++) {
[Link] = "#fff";
[Link]();
[Link]([Link][i].x, [Link][i].y, SHIP_SIZE / 15, 0,
[Link] * 2, false);
[Link]();
}
// Draw Asteroids
[Link] = "#0f0";
[Link] = SHIP_SIZE / 20;
for (let i = 0; i < [Link]; i++) {
let a = roids[i].a, r = roids[i].r, x = roids[i].x, y = roids[i].y;
[Link]();
[Link](x + r * [Link](a), y + r * [Link](a));
[Link](x + r * [Link](a + [Link]/2), y + r *
[Link](a + [Link]/2));
[Link](x + r * [Link](a + [Link]), y + r * [Link](a
+ [Link]));
[Link](x + r * [Link](a + 3*[Link]/2), y + r *
[Link](a + 3*[Link]/2));
[Link]();
[Link]();
}
// PHYSICS & LOGIC
ship.a += [Link];
ship.x += [Link].x;
ship.y += [Link].y;
if ([Link]) {
[Link].x += SHIP_THRUST * [Link](ship.a) / FPS;
[Link].y -= SHIP_THRUST * [Link](ship.a) / FPS;
} else {
[Link].x -= FRICTION * [Link].x / FPS;
[Link].y -= FRICTION * [Link].y / FPS;
}
// Screen Wrap
if (ship.x < 0 - ship.r) ship.x = [Link] + ship.r;
else if (ship.x > [Link] + ship.r) ship.x = 0 - ship.r;
if (ship.y < 0 - ship.r) ship.y = [Link] + ship.r;
else if (ship.y > [Link] + ship.r) ship.y = 0 - ship.r;
// Laser Logic
for (let i = [Link] - 1; i >= 0; i--) {
[Link][i].x += [Link][i].xv;
[Link][i].y += [Link][i].yv;
[Link][i].dist += [Link]([Link]([Link][i].xv,
2) + [Link]([Link][i].yv, 2));
if ([Link][i].dist > [Link] * 0.6) {
[Link](i, 1);
continue;
}
// Laser Wrap
if ([Link][i].x < 0) [Link][i].x = [Link];
else if ([Link][i].x > [Link]) [Link][i].x = 0;
if ([Link][i].y < 0) [Link][i].y = [Link];
else if ([Link][i].y > [Link]) [Link][i].y = 0;
}
// Asteroid Logic
for (let i = 0; i < [Link]; i++) {
roids[i].x += roids[i].xv;
roids[i].y += roids[i].yv;
roids[i].a += roids[i].rot;
if (roids[i].x < 0 - roids[i].r) roids[i].x = [Link] +
roids[i].r;
else if (roids[i].x > [Link] + roids[i].r) roids[i].x = 0 -
roids[i].r;
if (roids[i].y < 0 - roids[i].r) roids[i].y = [Link] +
roids[i].r;
else if (roids[i].y > [Link] + roids[i].r) roids[i].y = 0 -
roids[i].r;
}
// Collisions
for (let i = [Link] - 1; i >= 0; i--) {
for (let j = [Link] - 1; j >= 0; j--) {
if (distBetweenPoints(roids[i].x, roids[i].y,
[Link][j].x, [Link][j].y) < roids[i].r) {
[Link](j, 1);
let x = roids[i].x, y = roids[i].y, r = roids[i].r;
[Link](i, 1);
score += 100;
[Link] = score;
if (r > [Link](ROIDS_SIZE / 4)) {
[Link](newAsteroid(x, y, r / 2));
[Link](newAsteroid(x, y, r / 2));
}
if ([Link] === 0) { level++; newLevel(); }
break;
}
}
}
for (let i = 0; i < [Link]; i++) {
if (distBetweenPoints(ship.x, ship.y, roids[i].x, roids[i].y) <
ship.r + roids[i].r) {
gameOver = true;
[Link] = "block";
break;
}
}
}
function newLevel() { createAsteroidBelt(); }
[Link]('resize', function() {
[Link] = [Link];
[Link] = [Link];
});
</script>
</body>
</html>