Some product links are affiliate links which means
if you something we'll receive a small commission.
Youtube Link
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./style.css" />
<title>Pong Game</title>
</head>
<body>
<div class="container">
<h1 class="header">Pong Game</h1>
<canvas id="pingpong" width="650" height="480"></canvas>
<p class="usage">
<b>Usage</b> <br />
<br />
Play <br />
Up( <a class="key">Arrow Up</a> ) / Down( <a class="key">Arrow Down</a> ) <br />
</p>
</div>
<script src="./index.js"></script>
</body>
</html>
css
body {
background-color: #000;
}
.container {
padding: 2em;
font-family: VT323, sans-serif;
text-align: center;
}
.usage {
color: #fff;
font-size: 20px;
}
a.key {
border: 1px solid #ccc;
padding: 0 5px;
display: inline-block;
margin: 3px 2px;
background: #e3e3e3;
border-radius: 4px;
color: #000;
}
canvas {
display: block;
margin: 0 auto;
border: 10px solid #fff;
}
.header {
text-shadow: 0 0 5px #f562ff, 0 0 15px #f562ff, 0 0 25px #f562ff, 0 0 20px #f562ff, 0 0 30px #890092, 0 0 80px #890092, 0 0 80px #890092;
color: #fccaff;
text-shadow: 0 0 5px #ffa500, 0 0 15px #ffa500, 0 0 25px #ffa500, 0 0 20px #ffa500, 0 0 30px #ff0000, 0 0 80px #ff8d00, 0 0 80px #ff0000;
color: #fff6a9;
text-align: center;
font-size: 80px;
}
js
function Game(canvasId) {
this.game = this;
this.id = canvasId;
this.canvas = document.getElementById(this.id);
this.context = this.canvas.getContext("2d");
this.width = 0;
this.height = 0;
setTimeout(function () {
document.addEventListener("keydown", this.game.fireKeyDown);
document.addEventListener("keyup", this.game.fireKeyUp);
}, 50);
this.timer = setInterval(function () {
game.tick();
}, 10);
window.onkeydown = function (e) {
return !(e.keyCode == 32);
};
function intersects(circle, rect) {
var circleDistance = {};
circleDistance.x = Math.abs(circle.x - rect.left);
circleDistance.y = Math.abs(circle.y - rect.top);
if (circleDistance.x > rect.width / 2 + circle.rad) {
return false;
}
if (circleDistance.y > rect.height / 2 + circle.rad) {
return false;
}
if (circleDistance.x <= rect.width / 2) {
return true;
}
if (circleDistance.y <= rect.height / 2) {
return true;
}
var cornerDistance_sq = Math.pow(circleDistance.x - rect.width / 2, 2) + Math.pow(circleDistance.y - rect.height / 2, 2);
return cornerDistance_sq <= Math.pow(circle.r, 2);
}
function Ball(boundWidth, boundHeight) {
this.x = Math.random() * (boundWidth / 5) + (2 * boundWidth) / 5;
this.y = Math.random() * (boundHeight / 2) + (2 * boundHeight) / 4;
this.dx = Math.random() > 0.5 ? -1 : 1;
this.dy = Math.random() > 0.5 ? -1 : 1;
this.rad = 10;
this.color = "#fff";
this.speed = 2;
this.set = function (newX, newY) {
this.x = newX;
this.y = newY;
};
this.setD = function (_dx, _dy) {
this.dx = _dx;
this.dy = _dy;
};
this.randomDiretion = function () {
this.dx = Math.random() > 0.5 ? -1 : 1;
this.dy = Math.random() > 0.5 ? -1 : 1;
};
this.update = function () {
var nextX = this.x + this.dx;
var nextY = this.y + this.dy;
if (nextX < this.rad || nextX > boundWidth - this.rad) {
this.dx *= -1;
}
if (nextY < this.rad || nextY > boundHeight - this.rad) {
this.dy *= -1;
}
this.x += this.dx * this.speed;
this.y += this.dy * this.speed;
};
this.draw = function (context) {
var oldStyle = context.fillStyle;
context.beginPath();
context.fillStyle = this.color;
context.arc(this.x, this.y, this.rad, 0, Math.PI * 2, true);
context.closePath();
context.fill();
context.fillStyle = oldStyle;
};
this.collide = function (racket) {
if (!racket) return false;
var rect = {
left: racket.x,
top: racket.y,
width: racket.width,
height: racket.height,
};
if (intersects(this, rect)) {
var incidencePercent = (2 * (this.y - rect.top)) / rect.height;
this.dx *= -1;
this.dy += incidencePercent;
this.dy = Math.min(this.dy, 2);
this.dy = Math.max(this.dy, -2);
return true;
} else {
return false;
}
};
}
function Racket(x, size, color, boundHeight) {
this.x = x;
this.y = boundHeight / 2;
this.width = 10;
this.height = size;
this.left = this.x - this.width / 2;
this.right = this.x - this.width / 2;
this.dy = 0;
this.direction = 0;
this.draw = function (context) {
context.fillStyle = color;
context.fillRect(this.left, this.y - this.height / 2, this.width, this.height);
};
this.accelate = function (dy) {
this.dy += dy;
};
this.call = 0;
this.update = function () {
if (++this.call % 10 == 0) {
this.dy *= 0.7;
this.call = 0;
}
if (Math.abs(this.dy) < 1e-10) this.dy = 0;
this.y = this.y + this.dy / 10;
if (this.y - this.height / 2 < 0 || this.y + this.height / 2 > boundHeight) {
this.y = Math.max(this.y, this.height / 2);
this.y = Math.min(this.y, boundHeight - this.height / 2);
this.dy = 0;
}
};
}
this.init = function (stageName) {
this.stage = stageName;
if (!this.balls) this.balls = [];
this.racket1 = this.racket2 = null;
};
this.state = function (stageName) {
var width = this.context.canvas.width;
var height = this.context.canvas.height;
this.init(stageName);
if (stageName == "PLAY_SOLO" || stageName == "PLAY_DUO") {
this.balls = [];
this.racket1_score = 0;
this.racket2_score = 0;
this.racket1 = new Racket((1 * width) / 10, height / 5, "#fff", height);
this.racket2 = new Racket((9 * width) / 10, height / 5, "#fff", height);
this.pushBall(1, "#fff");
} else if (stageName == "GAME_OVER") {
this.balls = [];
}
};
this.draw = function (context) {
var width = context.canvas.width;
var height = context.canvas.height;
context.clearRect(0, 0, width, height);
for (let i in this.balls) {
this.balls[i].draw(context);
}
var defaultColor = "#FFFFFF";
context.font = "40px VT323";
context.fillStyle = defaultColor;
context.textAlign = "center";
if (this.stage === "SELECT_COMPUTER") {
context.fillText("COMPUTER LEVER", width / 2, height / 4);
context.fillText("1. easy", width / 2, height / 4 + 80);
context.fillText("2. medium", width / 2, height / 4 + 160);
context.fillText("3. hard", width / 2, height / 4 + 240);
} else if (this.stage == "GAME_OVER") {
context.fillStyle = "rgba(255,255,255,0.5)";
context.textAlign = "center";
context.font = "100px VT323";
context.fillText(this.racket1_score, width / 4, height / 2 + 40);
context.fillText(this.racket2_score, (3 * width) / 4, height / 2 + 40);
context.font = "40px VT323";
context.fillStyle = defaultColor;
context.fillText("GAME OVER", width / 2, height / 3 - 60);
context.fillStyle = "#f7ca18";
context.fillText(this.game_result_text, width / 2, height / 3);
context.fillStyle = defaultColor;
context.fillText("press SPACE to back", width / 2, (4 * height) / 5);
} else if (this.stage == "PLAY_SOLO" || this.stage == "PLAY_DUO") {
context.strokeStyle = "rgba(255,255,255,0.5)";
context.beginPath();
context.moveTo(width / 2, 0);
context.lineTo(width / 2, height);
context.stroke();
context.closePath();
context.fillStyle = "rgba(255,255,255,0.5)";
context.textAlign = "center";
context.font = "100px VT323";
context.fillText(this.racket1_score, width / 4, height / 2 + 40);
context.fillText(this.racket2_score, (3 * width) / 4, height / 2 + 40);
context.font = "40px VT323";
var hud = {
PLAY_SOLO: ["COM", "YOU"],
PLAY_DUO: ["PLAYER1", "PLAYER2"],
};
for (var i in hud) {
context.fillText(hud[this.stage][0], (1 * width) / 4, height / 4 + 40);
context.fillText(hud[this.stage][1], (3 * width) / 4, height / 4 + 40);
}
this.racket1.draw(context);
this.racket2.draw(context);
}
};
this.keypress_table = {};
this.fireKeyDown = function (event) {
var _this = game;
_this.keypress_table[event.code.toString()] = true;
if (_this.stage == "GAME_OVER") {
switch (event.code) {
case "Space":
_this.state("SELECT_COMPUTER");
_this.start();
break;
}
} else if (_this.stage == "SELECT_COMPUTER") {
switch (event.key) {
case "1":
_this.computer_level = 1;
_this.state("PLAY_SOLO");
break;
case "2":
_this.computer_level = 2;
_this.state("PLAY_SOLO");
break;
case "3":
_this.computer_level = 3;
_this.state("PLAY_SOLO");
break;
case "Escape":
_this.state("SELECT_COMPUTER");
break;
}
}
};
this.fireKeyUp = function (event) {
game.keypress_table[event.code.toString()] = false;
};
this.update = function () {
if (this.stage == "PLAY_SOLO") {
if (this.keypress_table["ArrowUp"]) if (this.racket2) this.racket2.accelate(-5);
if (this.keypress_table["ArrowDown"]) if (this.racket2) this.racket2.accelate(+5);
if (this.stage == "PLAY_SOLO" && this.racket1) {
var accuracy = parseInt(this.computer_level || 1) - 1;
var isDiffrentPosition = this.balls && this.racket1.y != this.balls[0];
if (Math.random() < [0.3, 0.5, 0.9][accuracy] && isDiffrentPosition) this.racket1.accelate(5 * (this.balls[0].y < this.racket1.y ? -1 : 1));
var tickSometime = Math.random() < 0.1;
if (tickSometime) {
if (this.racket1.y <= this.racket1.height / 2 + 10) this.racket1.accelate(+40);
if (this.racket1.y >= this.context.canvas.height - this.racket1.height / 2 - 10) this.racket1.accelate(-40);
}
}
}
for (var i in this.balls) {
this.balls[i].collide(this.racket1);
this.balls[i].collide(this.racket2);
this.balls[i].update();
if (this.racket1 && this.racket2) {
if (this.balls[i].x < this.racket1.left - this.racket2.width) {
this.scoring(0, +1);
this.popAndNewBall(i);
} else if (this.balls[i].x > this.racket2.right + this.racket2.width) {
this.scoring(+1, 0);
this.popAndNewBall(i);
}
}
}
if (this.stage == "PLAY_SOLO") {
if (this.racket1_score > 3) {
if (this.stage == "PLAY_SOLO") this.game_result_text = "COMPUTER WIN";
return "GAME_OVER";
}
if (this.racket2_score > 3) {
if (this.stage == "PLAY_SOLO") this.game_result_text = "PLAYER WIN";
return "GAME_OVER";
}
}
if (this.racket1) this.racket1.update();
if (this.racket2) this.racket2.update();
return "UPDATED";
};
this.tick = function () {
if (this.update() == "GAME_OVER") {
this.state("GAME_OVER");
}
this.draw(this.context);
};
this.timer = 0;
this.start = function () {
this.init("SELECT_COMPUTER");
for (var i = 0; i < 5; ++i) {
this.pushBall(Math.floor(Math.random() * 5), "#" + Math.random().toString(16).substring(2, 6));
}
};
this.stop = function () {
clearInterval(this.timer);
};
this.pushBall = function (ball_number, color, delay) {
if (!delay) delay = 0;
var width = this.context.canvas.width;
var height = this.context.canvas.height;
for (var i = 0; i < ball_number; ++i) {
var ball = new Ball(width, height);
ball.rad = 12;
ball.color = color;
ball.speed = 4;
if (delay) {
ball.setD(0, 0);
setTimeout(function () {
ball.randomDiretion();
}, delay);
}
this.balls.push(ball);
}
};
this.popAndNewBall = function (ball_index) {
this.balls = [];
this.pushBall(1, "#fff", 1000);
};
this.scoring = function (score1, score2) {
this.racket1_score += score1;
this.racket2_score += score2;
};
}
var game = new Game("pingpong");
game.start();
game.pushBall(1);