<!DOCTYPE html>
<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();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.