Files
2026-08-21 16:25:40 +02:00

367 lines
11 KiB
HTML

<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pac-Man Grand Format - Contrôles Fluides</title>
<!-- Chargement de Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body
class="bg-slate-950 text-slate-100 flex flex-col items-center justify-center min-h-screen select-none"
>
<h1 class="text-3xl font-extrabold mb-3 tracking-widest text-yellow-400">
PAC-MAN
</h1>
<!-- Tableau de bord (Score / Vies) -->
<div
class="flex justify-between w-[800px] mb-2 px-2 font-mono text-base text-slate-300"
>
<div>
Score : <span id="score" class="text-yellow-400 font-bold">0</span>
</div>
<div>Vies : <span id="lives" class="text-red-500 font-bold">3</span></div>
</div>
<!-- Conteneur du jeu (800x800) -->
<div
class="relative bg-black p-4 rounded-2xl shadow-2xl border-2 border-blue-600"
>
<canvas id="gameCanvas" width="800" height="800" class="block"></canvas>
</div>
<p class="text-xs text-slate-400 mt-4">
Utilisez les touches fléchées **← ↑ ↓ →** (ou **Z Q S D**) pour guider
Pac-Man.
</p>
<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const tileSize = 40;
const speed = 4;
const map = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1],
[1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1],
[1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 2, 1, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 2, 1, 1, 1, 1],
[0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0],
[1, 1, 1, 1, 2, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 2, 1, 1, 1, 1],
[0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0],
[1, 1, 1, 1, 2, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1],
[0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0],
[1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1],
[1, 2, 2, 1, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 1, 2, 2, 1],
[1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1],
[1, 2, 2, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 2, 1, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
let score = 0;
let lives = 3;
let pacman = {
x: 9 * tileSize + tileSize / 2,
y: 16 * tileSize + tileSize / 2,
radius: tileSize / 2 - 4,
dx: 0,
dy: 0,
nextDx: 0,
nextDy: 0,
};
let ghosts = [
{
x: 9 * tileSize + tileSize / 2,
y: 9 * tileSize + tileSize / 2,
color: "#ef4444",
dx: speed,
dy: 0,
},
{
x: 10 * tileSize + tileSize / 2,
y: 9 * tileSize + tileSize / 2,
color: "#ec4899",
dx: -speed,
dy: 0,
},
{
x: 9 * tileSize + tileSize / 2,
y: 10 * tileSize + tileSize / 2,
color: "#06b6d4",
dx: 0,
dy: -speed,
},
{
x: 10 * tileSize + tileSize / 2,
y: 10 * tileSize + tileSize / 2,
color: "#f97316",
dx: 0,
dy: speed,
},
];
window.addEventListener("keydown", (e) => {
switch (e.key) {
case "ArrowLeft":
case "q":
case "Q":
pacman.nextDx = -speed;
pacman.nextDy = 0;
break;
case "ArrowRight":
case "d":
case "D":
pacman.nextDx = speed;
pacman.nextDy = 0;
break;
case "ArrowUp":
case "z":
case "Z":
pacman.nextDx = 0;
pacman.nextDy = -speed;
break;
case "ArrowDown":
case "s":
case "S":
pacman.nextDx = 0;
pacman.nextDy = speed;
break;
}
});
function isWall(pixelX, pixelY) {
let c = Math.floor(pixelX / tileSize);
let r = Math.floor(pixelY / tileSize);
if (r === 8 || r === 10 || r === 12) {
if (pixelX < 0 || pixelX >= canvas.width) return false;
}
if (r < 0 || r >= map.length || c < 0 || c >= map[0].length)
return true;
return map[r][c] === 1;
}
function checkCollisionBox(x, y, radius) {
const corners = [
{ x: x - radius, y: y - radius },
{ x: x + radius, y: y - radius },
{ x: x - radius, y: y + radius },
{ x: x + radius, y: y + radius },
];
for (let corner of corners) {
if (isWall(corner.x, corner.y)) return true;
}
return false;
}
function isAtIntersection(x, y) {
return (
(x - tileSize / 2) % tileSize === 0 &&
(y - tileSize / 2) % tileSize === 0
);
}
function update() {
if (pacman.x < -tileSize / 2) {
pacman.x = canvas.width + tileSize / 2;
} else if (pacman.x > canvas.width + tileSize / 2) {
pacman.x = -tileSize / 2;
}
// Changement de direction immédiat si la voie est libre dans la nouvelle direction désirée
if (pacman.nextDx !== 0 || pacman.nextDy !== 0) {
// Demi-tour instantané autorisé
if (pacman.nextDx === -pacman.dx && pacman.nextDy === -pacman.dy) {
pacman.dx = pacman.nextDx;
pacman.dy = pacman.nextDy;
}
// Changement normal si on passe par une intersection ou si l'axe change proprement
else if (
!checkCollisionBox(
pacman.x + pacman.nextDx,
pacman.y + pacman.nextDy,
pacman.radius,
)
) {
// Si on change d'axe (ex: on va verticalement et on veut aller horizontalement), on s'assure d'être aligné sur la grille
if (
(pacman.dx !== 0 && pacman.nextDy !== 0) ||
(pacman.dy !== 0 && pacman.nextDx !== 0)
) {
if (isAtIntersection(pacman.x, pacman.y)) {
pacman.dx = pacman.nextDx;
pacman.dy = pacman.nextDy;
}
} else {
pacman.dx = pacman.nextDx;
pacman.dy = pacman.nextDy;
}
}
}
// Avancer Pac-Man s'il n'y a pas de mur devant lui
if (
!checkCollisionBox(
pacman.x + pacman.dx,
pacman.y + pacman.dy,
pacman.radius,
)
) {
pacman.x += pacman.dx;
pacman.y += pacman.dy;
} else {
pacman.dx = 0;
pacman.dy = 0;
}
let gridX = Math.floor(pacman.x / tileSize);
let gridY = Math.floor(pacman.y / tileSize);
if (
gridY >= 0 &&
gridY < map.length &&
gridX >= 0 &&
gridX < map[0].length
) {
if (map[gridY][gridX] === 2) {
map[gridY][gridX] = 0;
score += 10;
document.getElementById("score").innerText = score;
}
}
ghosts.forEach((ghost) => {
if (ghost.x < -tileSize / 2) ghost.x = canvas.width + tileSize / 2;
if (ghost.x > canvas.width + tileSize / 2) ghost.x = -tileSize / 2;
if (isAtIntersection(ghost.x, ghost.y)) {
const possibleDirs = [
{ dx: speed, dy: 0 },
{ dx: -speed, dy: 0 },
{ dx: 0, dy: speed },
{ dx: 0, dy: -speed },
];
const oppositeDx = -ghost.dx;
const oppositeDy = -ghost.dy;
let validDirs = possibleDirs.filter((d) => {
if (d.dx === oppositeDx && d.dy === oppositeDy) return false;
let nextX = ghost.x + d.dx * 4;
let nextY = ghost.y + d.dy * 4;
return !checkCollisionBox(nextX, nextY, tileSize / 2 - 4);
});
if (validDirs.length === 0) {
validDirs = [{ dx: oppositeDx, dy: oppositeDy }];
}
let chosen =
validDirs[Math.floor(Math.random() * validDirs.length)];
ghost.dx = chosen.dx;
ghost.dy = chosen.dy;
}
if (
checkCollisionBox(
ghost.x + ghost.dx,
ghost.y + ghost.dy,
tileSize / 2 - 4,
)
) {
ghost.dx = -ghost.dx;
ghost.dy = -ghost.dy;
}
ghost.x += ghost.dx;
ghost.y += ghost.dy;
let dist = Math.hypot(pacman.x - ghost.x, pacman.y - ghost.y);
if (dist < tileSize / 2) {
lives--;
document.getElementById("lives").innerText = lives;
if (lives <= 0) {
alert("GAME OVER ! Score final : " + score);
document.location.reload();
} else {
pacman.x = 9 * tileSize + tileSize / 2;
pacman.y = 16 * tileSize + tileSize / 2;
pacman.dx = 0;
pacman.dy = 0;
pacman.nextDx = 0;
pacman.nextDy = 0;
}
}
});
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let r = 0; r < map.length; r++) {
for (let c = 0; c < map[r].length; c++) {
if (map[r][c] === 1) {
ctx.fillStyle = "#2563eb";
ctx.fillRect(c * tileSize, r * tileSize, tileSize, tileSize);
} else if (map[r][c] === 2) {
ctx.fillStyle = "#fde047";
ctx.beginPath();
ctx.arc(
c * tileSize + tileSize / 2,
r * tileSize + tileSize / 2,
6,
0,
Math.PI * 2,
);
ctx.fill();
}
}
}
ctx.fillStyle = "#facc15";
ctx.beginPath();
let angle = 0.2;
if (pacman.dx > 0) angle = 0.2;
if (pacman.dx < 0) angle = 1.2;
if (pacman.dy > 0) angle = 0.7;
if (pacman.dy < 0) angle = 1.7;
ctx.arc(
pacman.x,
pacman.y,
pacman.radius,
angle * Math.PI,
(angle + 1.6) * Math.PI,
);
ctx.lineTo(pacman.x, pacman.y);
ctx.fill();
ghosts.forEach((ghost) => {
ctx.fillStyle = ghost.color;
ctx.beginPath();
ctx.arc(ghost.x, ghost.y - 4, tileSize / 2 - 4, Math.PI, 0, false);
ctx.lineTo(ghost.x + tileSize / 2 - 4, ghost.y + tileSize / 2);
ctx.lineTo(ghost.x - tileSize / 2 + 4, ghost.y + tileSize / 2);
ctx.fill();
});
}
function loop() {
update();
draw();
setTimeout(loop, 1000 / 45);
}
loop();
</script>
</body>
</html>