class Ball {
constructor(x, y, radius, speedX, speedY, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.speedX = speedX;
this.speedY = speedY;
this.color = color;
}
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
move() {
this.x += this.speedX;
this.y += this.speedY;
}
reset(canvas) {
this.x = canvas.width / 2 + (Math.random() - 0.5) * (canvas.width / 4);
this.y = canvas.height / 3;
const baseSpeed = canvas.width / 200;
this.speedX = baseSpeed * (Math.random() > 0.5 ? 1 : -1);
this.speedY = baseSpeed * (Math.random() > 0.5 ? 1 : -1);
}
}
class Paddle {
constructor(width, height, x, y, color) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
this.color = color;
}
draw(ctx) {
ctx.beginPath();
ctx.rect(this.x, this.y, this.width, this.height);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
}
class Game {
constructor(canvasId) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext("2d");
this.scoreEl = document.getElementById("score");
this.livesEl = document.getElementById("lives");
this.score = 0;
this.lives = 5;
this.gameOver = false;
this.canvas.width = 400;
this.canvas.height = 600;
this.balls = [
new Ball(this.canvas.width / 2, this.canvas.height / 2, 10, 2, -2, "#ff4757"),
new Ball(this.canvas.width / 3, this.canvas.height / 3, 10, -2, 2, "#feca57"),
];
this.paddle = new Paddle(90, 14, (this.canvas.width - 90) / 2, this.canvas.height - 20, "#000000");
this.canvas.addEventListener("mousemove", this.movePaddle.bind(this));
this.canvas.addEventListener("touchmove", this.movePaddle.bind(this));
window.addEventListener("resize", this.resize.bind(this));
this.resize();
}
resize() {
const oldWidth = this.canvas.width;
const oldHeight = this.canvas.height;
this.canvas.width = this.canvas.clientWidth;
this.canvas.height = this.canvas.width * 1.5;
if (oldWidth === 0) return;
const scaleX = this.canvas.width / oldWidth;
const scaleY = this.canvas.height / oldHeight;
this.paddle.x *= scaleX;
this.paddle.width *= scaleX;
this.paddle.y *= scaleY;
this.paddle.height *= scaleY;
this.balls.forEach((ball) => {
ball.x *= scaleX;
ball.y *= scaleY;
ball.radius *= scaleX;
ball.speedX *= scaleX;
ball.speedY *= scaleY;
});
}
movePaddle(e) {
const rect = this.canvas.getBoundingClientRect();
let root = document.documentElement;
let newX = e.clientX - rect.left - root.scrollLeft;
if (e.type === "touchmove") {
newX = e.touches[0].clientX - rect.left - root.scrollLeft;
}
this.paddle.x = newX - this.paddle.width / 2;
if (this.paddle.x < 0) {
this.paddle.x = 0;
}
if (this.paddle.x + this.paddle.width > this.canvas.width) {
this.paddle.x = this.canvas.width - this.paddle.width;
}
}
checkCollisions() {
this.balls.forEach((ball) => {
// Ściany boczne
if (ball.x + ball.radius > this.canvas.width || ball.x - ball.radius < 0) {
ball.speedX = -ball.speedX;
}
// Sufit
if (ball.y - ball.radius < 0) {
ball.speedY = -ball.speedY;
}
// Odbicie od paletki
if (ball.y + ball.radius > this.paddle.y && ball.x > this.paddle.x && ball.x < this.paddle.x + this.paddle.width) {
let intersect = (ball.x - (this.paddle.x + this.paddle.width / 2)) / (this.paddle.width / 2);
let bounceAngle = intersect * (Math.PI / 3);
let speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) + this.canvas.width / 8000 + this.score / 500;
ball.speedX = speed * Math.sin(bounceAngle);
ball.speedY = -speed * Math.cos(bounceAngle);
this.score++;
this.scoreEl.textContent = this.score;
}
// Spadnięcie piłki
if (ball.y + ball.radius > this.canvas.height) {
this.lives--;
this.livesEl.textContent = this.lives;
ball.reset(this.canvas);
if (this.lives === 0) {
this.gameOver = true;
setTimeout(() => {
this.resetGame();
}, 3000);
}
}
});
}
resetGame() {
this.score = 0;
this.lives = 5;
this.scoreEl.textContent = this.score;
this.livesEl.textContent = this.lives;
this.balls.forEach((ball) => ball.reset(this.canvas));
this.gameOver = false;
}
update() {
if (this.gameOver) return;
this.balls.forEach((ball) => ball.move());
this.checkCollisions();
}
draw() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (this.gameOver) {
this.ctx.fillStyle = "black";
this.ctx.font = "bold 40px Arial";
this.ctx.textAlign = "center";
this.ctx.fillText("Koniec gry!", this.canvas.width / 2, this.canvas.height / 2 - 40);
this.ctx.font = "24px Arial";
this.ctx.fillText("Wynik: " + this.score, this.canvas.width / 2, this.canvas.height / 2);
} else {
this.balls.forEach((ball) => ball.draw(this.ctx));
this.paddle.draw(this.ctx);
}
}
gameLoop() {
this.update();
this.draw();
requestAnimationFrame(this.gameLoop.bind(this));
}
start() {
this.gameLoop();
}
}
const game = new Game("gameCanvas");
game.start();
* {
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
background-color: #ffffff;
color: #000000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: #12c2e9;
background: -webkit-linear-gradient(to right, #12c2e9, #c471ed, #f64f59);
background: linear-gradient(to right, #12c2e9, #c471ed, #f64f59);
}
.game-container {
background-color: #ffffff;
border: none;
border-radius: 10px;
padding: 20px;
text-align: center;
width: 90%;
max-width: 400px;
}
.game-title {
font-size: 3.5rem;
font-weight: bold;
color: #000000;
margin-bottom: 10px;
}
.game-ui {
display: flex;
justify-content: space-between;
font-size: 1.2rem;
margin-bottom: 10px;
}
#gameCanvas {
background-color: #f0f0f0;
border-radius: 6px;
border: 6px solid #000000;
width: 100%;
}
.game-instructions {
margin-top: 10px;
font-size: 0.9rem;
color: #333333;
}
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pong</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="game-container">
<div class="game-title">Pong</div>
<div class="game-ui">
<div class="score">Wynik: <span id="score">0</span></div>
<div class="lives">Życia: <span id="lives">5</span></div>
</div>
<canvas id="gameCanvas"></canvas>
<div class="game-instructions">Kieruj rakietką za pomocą myszki lub dotyku ekranu</div>
</div>
<script src="js/script.js"></script>
</body>
</html>