<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pong</title>
<link rel="icon" type="image/png" href="favicon.png">
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Script -->
<script src="script.js"></script>
</body>
</html>
body {
margin: 0;
background-color: rgb(39, 39, 39);
display: flex;
justify-content: center;
font-family: "Courier New", Courier, monospace;
}
canvas {
margin-top: 25px;
z-index: 10;
}
.game-over-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 500px;
height: 700px;
background-color: rgb(56, 56, 56);
margin-top: -4px;
z-index: 11;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: whitesmoke;
}
button {
cursor: pointer;
color: rgb(0, 0, 0);
background-color: rgb(195, 195, 195);
border: none;
height: 50px;
width: 200px;
border-radius: 5px;
font-size: 20px;
font-family: "Courier New", Courier, monospace;
}
button:hover {
filter: brightness(80%);
}
button:active {
transform: scale(0.95);
}
button:focus {
outline: none;
}
/* Montior and Larger */
@media screen and (min-width: 1800px) {
canvas {
margin-top: 100px;
}
.game-over-container {
margin-top: -19px;
}
}
/* Large Smartphone (Vertical) */
@media screen and (max-width: 500px) {
canvas {
width: 100%;
height: 700px;
margin-top: 50px;
}
.game-over-container {
width: 100%;
height: 700px;
}
}
// 캔버스
const { body } = document;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = 500;
const height = 700;
const screenWidth = window.screen.width;
const canvasPosition = screenWidth / 2 - width / 2;
const isMobile = window.matchMedia('(max-width: 600px)');
const gameOverEl = document.createElement('div');
// 막대기
const paddleHeight = 10;
const paddleWidth = 50;
const paddleDiff = 25;
let paddleBottomX = 225;
let paddleTopX = 225;
let playerMoved = false;
let paddleContact = false;
// 공
let ballX = 250;
let ballY = 350;
const ballRadius = 5;
// 속도
let speedY;
let speedX;
let trajectoryX;
let computerSpeed;
// Change Mobile Settings
if (isMobile.matches) {
speedY = -2;
speedX = speedY;
computerSpeed = 4;
} else {
speedY = -1;
speedX = speedY;
computerSpeed = 3;
}
// Score
let playerScore = 0;
let computerScore = 0;
const winningScore = 7;
let isGameOver = true;
let isNewGame = true;
// 캔버스 랜더링.
function renderCanvas() {
// 캔버스 배경.
context.fillStyle = 'black';
context.fillRect(0, 0, width, height);
// 막대기 색
context.fillStyle = 'white';
// 플레이어 막대기 (하단)
context.fillRect(paddleBottomX, height - 20, paddleWidth, paddleHeight);
// 컴퓨터 막대기 (상단)
context.fillRect(paddleTopX, 10, paddleWidth, paddleHeight);
// 중앙라인(점선)
context.beginPath();
context.setLineDash([4]);
context.moveTo(0, 350);
context.lineTo(500, 350);
context.strokeStyle = 'grey';
context.stroke();
// 공
context.beginPath();
context.arc(ballX, ballY, ballRadius, 2 * Math.PI, false);
context.fillStyle = 'white';
context.fill();
// 점수
context.font = '32px Courier New';
context.fillText(playerScore, 20, canvas.height / 2 + 50);
context.fillText(computerScore, 20, canvas.height / 2 - 30);
}
// 캔버스 생성.
function createCanvas() {
canvas.width = width;
canvas.height = height;
body.appendChild(canvas);
renderCanvas();
}
// Reset Ball to Center
function ballReset() {
ballX = width / 2;
ballY = height / 2;
speedY = -3;
paddleContact = false;
}
// Adjust Ball Movement
function ballMove() {
// Vertical Speed
ballY += -speedY;
// Horizontal Speed
if (playerMoved && paddleContact) {
ballX += speedX;
}
}
// Determine What Ball Bounces Off, Score Points, Reset Ball
function ballBoundaries() {
// Bounce off Left Wall
if (ballX < 0 && speedX < 0) {
speedX = -speedX;
}
// Bounce off Right Wall
if (ballX > width && speedX > 0) {
speedX = -speedX;
}
// Bounce off player paddle (bottom)
if (ballY > height - paddleDiff) {
if (ballX > paddleBottomX && ballX < paddleBottomX + paddleWidth) {
paddleContact = true;
// Add Speed on Hit
if (playerMoved) {
speedY -= 1;
// Max Speed
if (speedY < -5) {
speedY = -5;
computerSpeed = 6;
}
}
speedY = -speedY;
trajectoryX = ballX - (paddleBottomX + paddleDiff);
speedX = trajectoryX * 0.3;
} else if (ballY > height) {
// Reset Ball, add to Computer Score
ballReset();
computerScore++;
}
}
// Bounce off computer paddle (top)
if (ballY < paddleDiff) {
if (ballX > paddleTopX && ballX < paddleTopX + paddleWidth) {
// Add Speed on Hit
if (playerMoved) {
speedY += 1;
// Max Speed
if (speedY > 5) {
speedY = 5;
}
}
speedY = -speedY;
} else if (ballY < 0) {
// Reset Ball, add to Player Score
ballReset();
playerScore++;
}
}
}
// Computer Movement
function computerAI() {
if (playerMoved) {
if (paddleTopX + paddleDiff < ballX) {
paddleTopX += computerSpeed;
} else {
paddleTopX -= computerSpeed;
}
}
}
function showGameOverEl(winner) {
// 캔버스 숨기고
canvas.hidden = true;
// Container
gameOverEl.textContent = '';
gameOverEl.classList.add('game-over-container');
// 제목
const title = document.createElement('h1');
title.textContent = `${winner} 이겼습니다!!`;
// 재도전 버튼
const playAgainBtn = document.createElement('button');
playAgainBtn.setAttribute('onclick', 'startGame()');
playAgainBtn.textContent = '다시 플레이 하시겠습니까?';
// 할당.
gameOverEl.append(title, playAgainBtn);
body.appendChild(gameOverEl);
}
// 둘중 하나가 승리점수를 얻은지 확인하고 얻으면 게임 종료.
function gameOver() {
if (playerScore === winningScore || computerScore === winningScore) {
isGameOver = true;
// 승자 결정.
const winner = (playerScore === winningScore) ? 'Player 1' : 'Computer';
showGameOverEl(winner);
}
}
// 매 프레임마다 불리게 된다.
function animate() {
renderCanvas();
ballMove();
ballBoundaries();
computerAI();
gameOver();
if (!isGameOver) {
window.requestAnimationFrame(animate);
//무제한 호출이 아니라 60 제한 호출로 부드러운 애니메이션.
}
}
// 게임을 시작하고 모두 리셋한다.
function startGame() {
if (isGameOver && !isNewGame) {
body.removeChild(gameOverEl);
canvas.hidden = false;
}
isGameOver = false;
isNewGame = false;
playerScore = 0;
computerScore = 0;
ballReset();
createCanvas();
animate();
canvas.addEventListener('mousemove', (e) => {
playerMoved = true;
// Compensate for canvas being centered
paddleBottomX = e.clientX - canvasPosition - paddleDiff;
if (paddleBottomX < paddleDiff) {
paddleBottomX = 0;
}
if (paddleBottomX > width - paddleWidth) {
paddleBottomX = width - paddleWidth;
}
// 마우스 숨기기.
canvas.style.cursor = 'none';
});
}
// On Load
startGame();
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.