AI 상대와의 퐁 게임
Wikiprompt, 무료 프롬프트 백과사전에서
AI 상대와의 퐁 게임 클래식 퐁 게임을 생성하는 프롬프트로, HTML, CSS, JavaScript를 사용하여 사용자 및 AI 제어 패들을 구현합니다.
프롬프트 내용저장
🌐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pong · user vs AI</title>
<style>
* {
box-sizing: border-box;
user-select: none;
}
body {
margin: 0;
min-height: 100vh;
background: linear-gradient(145deg, #0b0e1a 0%, #1a1f2f 100%);
display: flex;
justify-content: center;
align-items: center;
font-family: 'Segoe UI', Roboto, system-ui, sans-serif;
}
.game-wrapper {
background: #0f1322;
padding: 1.5rem 2rem 2rem;
border-radius: 3rem;
box-shadow: 0 20px 30px rgba(0,0,0,0.6), inset 0 0 0 1px rgba(255,255,255,0.06);
}
canvas {
display: block;
margin: 0 auto;
background: #0a0d18;
border-radius: 2rem;
box-shadow: 0 0 0 2px #2e3a5e, 0 15px 25px rgba(0,0,0,0.7);
width: 800px;
height: 500px;
}
.controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1.2rem;
color: #b7c6e8;
font-weight: 500;
letter-spacing: 0.5px;
padding: 0 0.5rem;
}
.keys {
background: #1d253a;
padding: 0.4rem 1rem;
border-radius: 3rem;
font-size: 0.9rem;
border: 1px solid #3f4d73;
box-shadow: inset 0 2px 3px rgba(0,0,0,0.4);
}
.keys span {
background: #0f1422;
padding: 0.2rem 0.8rem;
border-radius: 2rem;
margin: 0 0.2rem;
border: 1px solid #4c5b82;
color: #dbe5ff;
}
.score-badge {
background: #1d253a;
padding: 0.4rem 1.6rem;
border-radius: 3rem;
font-size: 1.3rem;
font-weight: 700;
color: #eaf0ff;
border: 1px solid #3f4d73;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.5);
letter-spacing: 2px;
}
.ai-note {
font-size: 0.8rem;
opacity: 0.7;
background: #1a2133;
padding: 0.3rem 1rem;
border-radius: 2rem;
}
</style>
</head>
<body>
<div class="game-wrapper">
<canvas id="pongCanvas" width="800" height="500"></canvas>
<div class="controls">
<div class="keys">
<span>▲</span> <span>▼</span> move
</div>
<div class="score-badge" id="scoreDisplay">0 : 0</div>
<div class="ai-note">AI · right</div>
</div>
</div>
<script>
(function() {
const canvas = document.getElementById('pongCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('scoreDisplay');
// ---------- GAME CONSTANTS ----------
const W = 800, H = 500;
const PADDLE_WIDTH = 14;
const PADDLE_HEIGHT = 90;
const BALL_SIZE = 14; // square ball (classic feel)
const PADDLE_SPEED = 6.2; // user paddle speed
const AI_SPEED = 4.6; // AI paddle speed (slightly slower, human-like)
const INITIAL_BALL_SPEED = 4.2;
const MAX_BALL_SPEED = 9.5;
const SPEED_INCREASE = 0.15; // per paddle hit
// ---------- GAME STATE ----------
let leftPaddleY = H / 2 - PADDLE_HEIGHT / 2;
let rightPaddleY = H / 2 - PADDLE_HEIGHT / 2;
let ballX = W / 2 - BALL_SIZE / 2;
let ballY = H / 2 - BALL_SIZE / 2;
let ballSpeedX = INITIAL_BALL_SPEED * (Math.random() > 0.5 ? 1 : -1);
let ballSpeedY = (Math.random() * 2 - 1) * 2.5;
let leftScore = 0;
let rightScore = 0;
// ---------- INPUT ----------
let upPressed = false;
let downPressed = false;
// ---------- HELPER: RESET BALL ----------
function resetBall(direction = 1) {
ballX = W / 2 - BALL_SIZE / 2;
ballY = H / 2 - BALL_SIZE / 2;
// random vertical component, but not too steep
ballSpeedY = (Math.random() * 2 - 1) * 3.0;
// direction: 1 -> to the right (AI), -1 -> to the left (user)
ballSpeedX = INITIAL_BALL_SPEED * direction;
// ensure not perfectly horizontal
if (Math.abs(ballSpeedY) < 0.8) ballSpeedY = 1.2 * (Math.random() > 0.5 ? 1 : -1);
}
// ---------- AI: follow ball ----------
function updateAI() {
// ball center
const ballCenterY = ballY + BALL_SIZE / 2;
const paddleCenterY = rightPaddleY + PADDLE_HEIGHT / 2;
// only move if ball is moving towards AI (right) or if ball is on right side
// classic: always follow, but with max speed
const diff = ballCenterY - paddleCenterY;
// dead zone to avoid jitter
if (Math.abs(diff) > 4) {
if (diff > 0) {
rightPaddleY = Math.min(H - PADDLE_HEIGHT, rightPaddleY + AI_SPEED);
} else if (diff < 0) {
rightPaddleY = Math.max(0, rightPaddleY - AI_SPEED);
}
}
// extra: if ball is moving away, still track slowly (but we already do)
// clamp just in case
rightPaddleY = Math.max(0, Math.min(H - PADDLE_HEIGHT, rightPaddleY));
}
// ---------- COLLISION & PHYSICS ----------
function updateBall() {
// move ball
ballX += ballSpeedX;
ballY += ballSpeedY;
// top / bottom walls
if (ballY <= 0) {
ballY = 0;
ballSpeedY = -ballSpeedY;
} else if (ballY + BALL_SIZE >= H) {
ballY = H - BALL_SIZE;
ballSpeedY = -ballSpeedY;
}
// paddle collision (left paddle)
// left paddle rect
const leftRect = {
x: 0,
y: leftPaddleY,
w: PADDLE_WIDTH,
h: PADDLE_HEIGHT
};
// right paddle rect
const rightRect = {
x: W - PADDLE_WIDTH,
y: rightPaddleY,
w: PADDLE_WIDTH,
h: PADDLE_HEIGHT
};
// ball rect
const ballRect = {
x: ballX,
y: ballY,
w: BALL_SIZE,
h: BALL_SIZE
};
// helper: AABB collision
function rectsOverlap(r1, r2) {
return !(r2.x > r1.x + r1.w || r2.x + r2.w < r1.x || r2.y > r1.y + r1.h || r2.y + r2.h < r1.y);
}
// LEFT paddle collision (only if ball moving left)
if (ballSpeedX < 0 && rectsOverlap(ballRect, leftRect)) {
// adjust position to avoid sticking
ballX = leftRect.x + leftRect.w;
// reflect horizontal
ballSpeedX = -ballSpeedX;
// add slight speed up
ballSpeedX = Math.sign(ballSpeedX) * Math.min(Math.abs(ballSpeedX) + SPEED_INCREASE, MAX_BALL_SPEED);
// add spin based on hit position
const hitOffset = (ballY + BALL_SIZE/2) - (leftPaddleY + PADDLE_HEIGHT/2);
ballSpeedY = hitOffset * 0.18;
// clamp vertical speed
ballSpeedY = Math.max(-7, Math.min(7, ballSpeedY));
}
// RIGHT paddle collision (only if ball moving right)
if (ballSpeedX > 0 && rectsOverlap(ballRect, rightRect)) {
// adjust position
ballX = rightRect.x - BALL_SIZE;
ballSpeedX = -ballSpeedX;
ballSpeedX = Math.sign(ballSpeedX) * Math.min(Math.abs(ballSpeedX) + SPEED_INCREASE, MAX_BALL_SPEED);
const hitOffset = (ballY + BALL_SIZE/2) - (rightPaddleY + PADDLE_HEIGHT/2);
ballSpeedY = hitOffset * 0.18;
ballSpeedY = Math.max(-7, Math.min(7, ballSpeedY));
}
// scoring (left/right out)
if (ballX + BALL_SIZE < 0) {
// right scores
rightScore++;
updateScore();
resetBall(-1); // ball goes to left (towards user)
} else if (ballX > W) {
// left scores
leftScore++;
updateScore();
resetBall(1); // ball goes to right (towards AI)
}
// extra safety: keep ball within vertical bounds (already done)
}
// ---------- UPDATE SCORE UI ----------
function updateScore() {
scoreDisplay.textContent = `${leftScore} : ${rightScore}`;
}
// ---------- USER PADDLE MOVEMENT ----------
function updateUserPaddle() {
if (upPressed) {
leftPaddleY = Math.max(0, leftPaddleY - PADDLE_SPEED);
}
if (downPressed) {
leftPaddleY = Math.min(H - PADDLE_HEIGHT, leftPaddleY + PADDLE_SPEED);
}
}
// ---------- DRAW EVERYTHING ----------
function draw() {
ctx.clearRect(0, 0, W, H);
// background grid (subtle)
ctx.fillStyle = '#0a0d18';
ctx.fillRect(0, 0, W, H);
// center dashed line
ctx.strokeStyle = '#2f3c5f';
ctx.lineWidth = 3;
ctx.setLineDash([12, 18]);
ctx.beginPath();
ctx.moveTo(W/2, 0);
ctx.lineTo(W/2, H);
ctx.stroke();
ctx.setLineDash([]); // reset
// center circle
ctx.beginPath();
ctx.arc(W/2, H/2, 40, 0, Math.PI * 2);
ctx.strokeStyle = '#2f3c5f';
ctx.lineWidth = 3;
ctx.stroke();
// left paddle (user) - cyan glow
ctx.shadowColor = '#4cc9f0';
ctx.shadowBlur = 18;
ctx.fillStyle = '#8ecae6';
ctx.fillRect(0, leftPaddleY, PADDLE_WIDTH, PADDLE_HEIGHT);
ctx.shadowBlur = 0;
// right paddle (AI) - red/orange glow
ctx.shadowColor = '#ff9e5e';
ctx.shadowBlur = 18;
ctx.fillStyle = '#f4a261';
ctx.fillRect(W - PADDLE_WIDTH, rightPaddleY, PADDLE_WIDTH, PADDLE_HEIGHT);
ctx.shadowBlur = 0;
// ball (white with glow)
ctx.shadowColor = '#ffffff';
ctx.shadowBlur = 20;
ctx.fillStyle = '#f8f9fa';
ctx.fillRect(ballX, ballY, BALL_SIZE, BALL_SIZE);
ctx.shadowBlur = 0;
// small reflection (optional)
ctx.fillStyle = 'rgba(255,255,255,0.1)';
ctx.fillRect(ballX + 2, ballY + 2, BALL_SIZE - 4, BALL_SIZE - 4);
}
// ---------- GAME LOOP ----------
function gameLoop() {
// update user paddle
updateUserPaddle();
// update AI
updateAI();
// update ball physics + collisions
updateBall();
// draw
draw();
requestAnimationFrame(gameLoop);
}
// ---------- EVENT LISTENERS ----------
window.addEventListener('keydown', (e) => {
// prevent arrow scrolling
if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === ' ') {
e.preventDefault();
}
if (e.key === 'ArrowUp') upPressed = true;
if (e.key === 'ArrowDown') downPressed = true;
});
window.addEventListener('keyup', (e) => {
if (e.key === 'ArrowUp') upPressed = false;
if (e.key === 'ArrowDown') downPressed = false;
});
// prevent page move on touch / space (extra)
window.addEventListener('keydown', (e) => {
if (e.key === ' ' || e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
}
}, { passive: false });
// initial score display
updateScore();
// start the loop
gameLoop();
})();
</script>
</body>
</html>
전체 프롬프트를 보려면 로그인하세요
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
사용법
이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개