480 lines
12 KiB
JavaScript
480 lines
12 KiB
JavaScript
// Variables globales
|
|
let game;
|
|
let canvas;
|
|
let ctx;
|
|
|
|
class Game {
|
|
constructor() {
|
|
this.canvas = document.getElementById('gameCanvas');
|
|
this.ctx = this.canvas.getContext('2d');
|
|
this.width = this.canvas.width;
|
|
this.height = this.canvas.height;
|
|
|
|
// État du jeu
|
|
this.score = 0;
|
|
this.lives = 3;
|
|
this.level = 1;
|
|
this.gameRunning = false;
|
|
this.paused = false;
|
|
|
|
// Entités du jeu
|
|
this.player = null;
|
|
this.enemies = [];
|
|
this.bubbles = [];
|
|
this.platforms = [];
|
|
|
|
// Contrôles
|
|
this.keys = {};
|
|
}
|
|
|
|
init() {
|
|
// Créer le joueur
|
|
this.player = new Player(100, this.height - 100, this);
|
|
|
|
// Créer quelques plateformes
|
|
this.createPlatforms();
|
|
|
|
// Créer quelques ennemis
|
|
this.createEnemies();
|
|
|
|
// Écouter les événements clavier
|
|
this.setupControls();
|
|
|
|
// Démarrer la boucle du jeu
|
|
this.gameRunning = true;
|
|
this.gameLoop();
|
|
}
|
|
|
|
createPlatforms() {
|
|
this.platforms = [];
|
|
|
|
// Plateforme du sol
|
|
this.platforms.push({
|
|
x: 0,
|
|
y: this.height - 40,
|
|
width: this.width,
|
|
height: 40
|
|
});
|
|
|
|
// Plateformes flottantes
|
|
this.platforms.push({
|
|
x: 100,
|
|
y: this.height - 150,
|
|
width: 200,
|
|
height: 20
|
|
});
|
|
|
|
this.platforms.push({
|
|
x: 400,
|
|
y: this.height - 250,
|
|
width: 200,
|
|
height: 20
|
|
});
|
|
|
|
this.platforms.push({
|
|
x: 200,
|
|
y: this.height - 350,
|
|
width: 200,
|
|
height: 20
|
|
});
|
|
}
|
|
|
|
createEnemies() {
|
|
this.enemies = [];
|
|
// Créer quelques ennemis
|
|
this.enemies.push(new Enemy(300, this.height - 100, this));
|
|
this.enemies.push(new Enemy(500, this.height - 200, this));
|
|
}
|
|
|
|
setupControls() {
|
|
document.addEventListener('keydown', (e) => {
|
|
this.keys[e.key] = true;
|
|
|
|
// Sauter avec espace
|
|
if (e.key === ' ' && this.gameRunning && !this.paused) {
|
|
if (this.player) {
|
|
this.player.jump();
|
|
}
|
|
}
|
|
|
|
// Tirer une bulle avec Ctrl
|
|
if ((e.key === 'Control' || e.key === 'ControlLeft') && this.gameRunning && !this.paused) {
|
|
if (this.player) {
|
|
this.player.shootBubble();
|
|
}
|
|
}
|
|
});
|
|
|
|
document.addEventListener('keyup', (e) => {
|
|
this.keys[e.key] = false;
|
|
});
|
|
}
|
|
|
|
update() {
|
|
if (!this.gameRunning || this.paused) return;
|
|
|
|
// Mettre à jour le joueur
|
|
if (this.player) {
|
|
this.player.update();
|
|
}
|
|
|
|
// Mettre à jour les ennemis
|
|
for (let i = 0; i < this.enemies.length; i++) {
|
|
this.enemies[i].update();
|
|
|
|
// Vérifier si l'ennemi est hors jeu
|
|
if (this.enemies[i].y > this.height) {
|
|
this.enemies.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
// Mettre à jour les bulles
|
|
for (let i = 0; i < this.bubbles.length; i++) {
|
|
this.bubbles[i].update();
|
|
|
|
// Vérifier si la bulle est hors jeu
|
|
if (this.bubbles[i].y < -50 || this.bubbles[i].x > this.width + 50 || this.bubbles[i].x < -50) {
|
|
this.bubbles.splice(i, 1);
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
// Vérifier les collisions entre bulles et ennemis
|
|
for (let j = 0; j < this.enemies.length; j++) {
|
|
if (j < this.enemies.length && this.checkCollision(this.bubbles[i], this.enemies[j])) {
|
|
// Ennemi capturé dans une bulle
|
|
this.score += 100;
|
|
this.updateUI();
|
|
this.bubbles.splice(i, 1);
|
|
this.enemies.splice(j, 1);
|
|
i--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vérifier les collisions entre joueur et ennemis
|
|
if (this.player) {
|
|
for (let i = 0; i < this.enemies.length; i++) {
|
|
if (this.checkCollision(this.player, this.enemies[i])) {
|
|
this.playerHit();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vérifier si tous les ennemis sont éliminés
|
|
if (this.enemies.length === 0) {
|
|
this.nextLevel();
|
|
}
|
|
}
|
|
|
|
draw() {
|
|
// Effacer le canvas
|
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
|
|
// Dessiner le fond
|
|
this.ctx.fillStyle = '#00008B';
|
|
this.ctx.fillRect(0, 0, this.width, this.height);
|
|
|
|
// Dessiner les plateformes
|
|
this.ctx.fillStyle = '#8B4513';
|
|
for (let platform of this.platforms) {
|
|
this.ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
|
|
}
|
|
|
|
// Dessiner le joueur
|
|
if (this.player) {
|
|
this.player.draw();
|
|
}
|
|
|
|
// Dessiner les ennemis
|
|
for (let enemy of this.enemies) {
|
|
enemy.draw();
|
|
}
|
|
|
|
// Dessiner les bulles
|
|
for (let bubble of this.bubbles) {
|
|
bubble.draw();
|
|
}
|
|
}
|
|
|
|
checkCollision(obj1, obj2) {
|
|
return obj1.x < obj2.x + obj2.width &&
|
|
obj1.x + obj1.width > obj2.x &&
|
|
obj1.y < obj2.y + obj2.height &&
|
|
obj1.y + obj1.height > obj2.y;
|
|
}
|
|
|
|
playerHit() {
|
|
this.lives--;
|
|
this.updateUI();
|
|
|
|
if (this.lives <= 0) {
|
|
this.gameOver();
|
|
} else {
|
|
// Réinitialiser la position du joueur
|
|
if (this.player) {
|
|
this.player.x = 100;
|
|
this.player.y = this.height - 100;
|
|
this.player.velocityY = 0;
|
|
this.player.velocityX = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
nextLevel() {
|
|
this.level++;
|
|
this.updateUI();
|
|
|
|
// Réinitialiser les ennemis
|
|
this.createEnemies();
|
|
|
|
// Réinitialiser le joueur
|
|
if (this.player) {
|
|
this.player.x = 100;
|
|
this.player.y = this.height - 100;
|
|
this.player.velocityY = 0;
|
|
this.player.velocityX = 0;
|
|
}
|
|
}
|
|
|
|
gameOver() {
|
|
this.gameRunning = false;
|
|
setTimeout(() => {
|
|
alert(`Game Over! Score final: ${this.score}`);
|
|
}, 100);
|
|
}
|
|
|
|
updateUI() {
|
|
document.getElementById('score').textContent = this.score;
|
|
document.getElementById('lives').textContent = this.lives;
|
|
document.getElementById('level').textContent = this.level;
|
|
}
|
|
|
|
gameLoop() {
|
|
this.update();
|
|
this.draw();
|
|
if (this.gameRunning) {
|
|
requestAnimationFrame(() => this.gameLoop());
|
|
}
|
|
}
|
|
}
|
|
|
|
class Player {
|
|
constructor(x, y, game) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = 30;
|
|
this.height = 30;
|
|
this.velocityX = 0;
|
|
this.velocityY = 0;
|
|
this.speed = 5;
|
|
this.jumpPower = 12;
|
|
this.gravity = 0.5;
|
|
this.onGround = false;
|
|
this.game = game;
|
|
}
|
|
|
|
update() {
|
|
// Gestion des contrôles
|
|
this.velocityX = 0;
|
|
if (this.game.keys['ArrowLeft']) {
|
|
this.velocityX = -this.speed;
|
|
}
|
|
if (this.game.keys['ArrowRight']) {
|
|
this.velocityX = this.speed;
|
|
}
|
|
|
|
// Appliquer la gravité
|
|
this.velocityY += this.gravity;
|
|
|
|
// Mettre à jour la position
|
|
this.x += this.velocityX;
|
|
this.y += this.velocityY;
|
|
|
|
// Empêcher le joueur de sortir de l'écran
|
|
if (this.x < 0) this.x = 0;
|
|
if (this.x + this.width > this.game.width) this.x = this.game.width - this.width;
|
|
|
|
// Vérifier les collisions avec les plateformes
|
|
this.onGround = false;
|
|
for (let platform of this.game.platforms) {
|
|
if (this.x < platform.x + platform.width &&
|
|
this.x + this.width > platform.x &&
|
|
this.y + this.height <= platform.y + 10 &&
|
|
this.y + this.height + this.velocityY >= platform.y) {
|
|
this.y = platform.y - this.height;
|
|
this.velocityY = 0;
|
|
this.onGround = true;
|
|
}
|
|
}
|
|
|
|
// Empêcher le joueur de tomber sous l'écran
|
|
if (this.y > this.game.height) {
|
|
this.game.playerHit();
|
|
}
|
|
}
|
|
|
|
jump() {
|
|
if (this.onGround) {
|
|
this.velocityY = -this.jumpPower;
|
|
this.onGround = false;
|
|
}
|
|
}
|
|
|
|
shootBubble() {
|
|
// Créer une nouvelle bulle
|
|
let direction = 1;
|
|
if (this.game.keys['ArrowLeft']) direction = -1;
|
|
this.game.bubbles.push(new Bubble(this.x + this.width/2, this.y + this.height/2, direction, this.game));
|
|
}
|
|
|
|
draw() {
|
|
// Dessiner le joueur (cercle vert)
|
|
this.game.ctx.fillStyle = '#00FF00';
|
|
this.game.ctx.beginPath();
|
|
this.game.ctx.arc(this.x + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
|
|
this.game.ctx.fill();
|
|
|
|
// Dessiner les yeux
|
|
this.game.ctx.fillStyle = '#000000';
|
|
let eyeOffset = this.game.keys['ArrowLeft'] ? -5 : (this.game.keys['ArrowRight'] ? 5 : 0);
|
|
this.game.ctx.beginPath();
|
|
this.game.ctx.arc(this.x + this.width/2 - 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
|
|
this.game.ctx.arc(this.x + this.width/2 + 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
|
|
this.game.ctx.fill();
|
|
}
|
|
}
|
|
|
|
class Enemy {
|
|
constructor(x, y, game) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = 25;
|
|
this.height = 25;
|
|
this.velocityX = 2;
|
|
this.velocityY = 0;
|
|
this.gravity = 0.5;
|
|
this.game = game;
|
|
}
|
|
|
|
update() {
|
|
// Déplacement horizontal
|
|
this.x += this.velocityX;
|
|
|
|
// Appliquer la gravité
|
|
this.velocityY += this.gravity;
|
|
this.y += this.velocityY;
|
|
|
|
// Vérifier les collisions avec les plateformes
|
|
let onPlatform = false;
|
|
for (let platform of this.game.platforms) {
|
|
if (this.x < platform.x + platform.width &&
|
|
this.x + this.width > platform.x &&
|
|
this.y + this.height <= platform.y + 10 &&
|
|
this.y + this.height + this.velocityY >= platform.y) {
|
|
this.y = platform.y - this.height;
|
|
this.velocityY = 0;
|
|
onPlatform = true;
|
|
|
|
// Changer de direction si on arrive au bord de la plateforme
|
|
if (this.x <= platform.x || this.x + this.width >= platform.x + platform.width) {
|
|
this.velocityX *= -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Rebondir sur les murs
|
|
if (this.x <= 0 || this.x + this.width >= this.game.width) {
|
|
this.velocityX *= -1;
|
|
}
|
|
}
|
|
|
|
draw() {
|
|
// Dessiner l'ennemi (cercle rouge)
|
|
this.game.ctx.fillStyle = '#FF0000';
|
|
this.game.ctx.beginPath();
|
|
this.game.ctx.arc(this.x + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
|
|
this.game.ctx.fill();
|
|
|
|
// Dessiner les yeux
|
|
this.game.ctx.fillStyle = '#000000';
|
|
let eyeOffset = this.velocityX < 0 ? -5 : 5;
|
|
this.game.ctx.beginPath();
|
|
this.game.ctx.arc(this.x + this.width/2 - 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
|
|
this.game.ctx.arc(this.x + this.width/2 + 5 + eyeOffset, this.y + this.height/2 - 5, 3, 0, Math.PI * 2);
|
|
this.game.ctx.fill();
|
|
}
|
|
}
|
|
|
|
class Bubble {
|
|
constructor(x, y, direction, game) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.radius = 10;
|
|
this.velocityX = direction * 4;
|
|
this.velocityY = -3;
|
|
this.game = game;
|
|
}
|
|
|
|
update() {
|
|
this.x += this.velocityX;
|
|
this.y += this.velocityY;
|
|
|
|
// La bulle monte légèrement
|
|
this.velocityY -= 0.1;
|
|
|
|
// Limiter la vitesse verticale
|
|
if (this.velocityY < -5) this.velocityY = -5;
|
|
}
|
|
|
|
draw() {
|
|
// Dessiner la bulle (cercle transparent avec bordure)
|
|
this.game.ctx.strokeStyle = '#FFFFFF';
|
|
this.game.ctx.lineWidth = 2;
|
|
this.game.ctx.beginPath();
|
|
this.game.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
|
|
this.game.ctx.stroke();
|
|
|
|
// Ajouter un reflet
|
|
this.game.ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
|
|
this.game.ctx.beginPath();
|
|
this.game.ctx.arc(this.x - this.radius/3, this.y - this.radius/3, this.radius/4, 0, Math.PI * 2);
|
|
this.game.ctx.fill();
|
|
}
|
|
}
|
|
|
|
// Fonctions globales
|
|
function startGame() {
|
|
if (game) {
|
|
// Réinitialiser le jeu
|
|
game.score = 0;
|
|
game.lives = 3;
|
|
game.level = 1;
|
|
game.updateUI();
|
|
game.createPlatforms();
|
|
game.createEnemies();
|
|
if (game.player) {
|
|
game.player.x = 100;
|
|
game.player.y = game.height - 100;
|
|
game.player.velocityY = 0;
|
|
game.player.velocityX = 0;
|
|
}
|
|
game.bubbles = [];
|
|
game.gameRunning = true;
|
|
game.gameLoop();
|
|
} else {
|
|
// Créer un nouveau jeu
|
|
game = new Game();
|
|
game.init();
|
|
}
|
|
game.gameRunning = true;
|
|
game.paused = false;
|
|
}
|
|
|
|
function pauseGame() {
|
|
if (game && game.gameRunning) {
|
|
game.paused = !game.paused;
|
|
} |