dark-forest-pong / index.html
Rausda6's picture
Update index.html
a695645 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gravity Pong</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<style>
.gravity-slider {
-webkit-appearance: none;
width: 100%;
height: 8px;
border-radius: 4px;
background: #4b5563;
outline: none;
}
.gravity-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #3b82f6;
cursor: pointer;
}
canvas {
display: block;
margin: 0 auto;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
<div class="w-full max-w-4xl">
<h1 class="text-4xl font-bold text-center mb-2 text-blue-400">Gravity Pong</h1>
<p class="text-center text-gray-300 mb-6">Play Pong with a twist - navigate through a solar system's gravity!</p>
<div id="game-container" class="relative mb-6"></div>
<div class="bg-gray-800 p-4 rounded-lg shadow-lg">
<div class="flex flex-col space-y-4">
<div>
<label class="block text-sm font-medium text-gray-300 mb-2">Gravity Strength</label>
<input type="range" min="0" max="2" step="0.1" value="1" class="gravity-slider" id="gravitySlider">
<div class="flex justify-between text-xs text-gray-400">
<span>Weak</span>
<span>Normal</span>
<span>Strong</span>
</div>
</div>
<div class="flex justify-between">
<div>
<p class="text-sm font-medium text-gray-300">Player Score: <span id="playerScore" class="text-blue-400">0</span></p>
</div>
<div>
<p class="text-sm font-medium text-gray-300">AI Score: <span id="aiScore" class="text-red-400">0</span></p>
</div>
</div>
<div class="pt-2">
<p class="text-xs text-gray-400 text-center">Use <span class="font-bold">W/S</span> or <span class="font-bold">↑/↓</span> keys to move your paddle</p>
</div>
</div>
</div>
</div>
<script>
let ball;
let playerPaddle;
let aiPaddle;
let playerScore = 0;
let aiScore = 0;
let gravityStrength = 1;
let planets = [];
let sun;
function setup() {
const container = document.getElementById('game-container');
const canvas = createCanvas(800, 500);
canvas.parent('game-container');
// Initialize game objects
ball = new Ball();
playerPaddle = new Paddle(true);
aiPaddle = new Paddle(false);
// Create solar system
sun = {
x: width/2,
y: height/2,
radius: 40,
mass: 1000
};
planets = [
{ x: width/2 + 120, y: height/2, radius: 15, mass: 100, angle: 0, distance: 120, speed: 0.02 },
{ x: width/2 - 180, y: height/2, radius: 20, mass: 150, angle: PI, distance: 180, speed: 0.015 },
{ x: width/2, y: height/2 + 100, radius: 10, mass: 50, angle: PI/2, distance: 100, speed: 0.025 }
];
// Set initial ball position (offset from center to avoid immediate sun interaction)
ball.reset(100, height/2);
// Setup slider
const slider = document.getElementById('gravitySlider');
slider.addEventListener('input', function() {
gravityStrength = parseFloat(this.value);
});
}
function draw() {
background(23, 23, 32);
// Draw star (sun)
drawSun();
// Update and draw planets
updatePlanets();
// Draw gravity fields
drawGravityFields();
// Update and display game objects
ball.update();
ball.display();
playerPaddle.update();
playerPaddle.display();
aiPaddle.update();
aiPaddle.display();
// Check collisions
checkPaddleCollision(ball, playerPaddle);
checkPaddleCollision(ball, aiPaddle);
// AI movement
aiMovement();
// Display scores
document.getElementById('playerScore').textContent = playerScore;
document.getElementById('aiScore').textContent = aiScore;
}
function drawSun() {
// Sun glow effect
for (let i = 0; i < 3; i++) {
let alpha = 50 - i * 15;
fill(255, 204, 0, alpha);
noStroke();
ellipse(sun.x, sun.y, sun.radius * 2 + i * 20);
}
// Sun core
fill(255, 204, 0);
stroke(255, 153, 0);
strokeWeight(2);
ellipse(sun.x, sun.y, sun.radius * 2);
// Sun surface details
fill(255, 153, 0, 100);
noStroke();
for (let i = 0; i < 10; i++) {
let angle = random(TWO_PI);
let r = random(sun.radius * 0.7, sun.radius);
let x = sun.x + cos(angle) * r;
let y = sun.y + sin(angle) * r;
ellipse(x, y, random(3, 8));
}
}
function updatePlanets() {
for (let planet of planets) {
// Update position based on orbit
planet.angle += planet.speed;
planet.x = sun.x + cos(planet.angle) * planet.distance;
planet.y = sun.y + sin(planet.angle) * planet.distance;
// Draw planet
fill(100, 200, 255);
noStroke();
ellipse(planet.x, planet.y, planet.radius * 2);
// Add some details
fill(70, 170, 220);
ellipse(planet.x - planet.radius/3, planet.y - planet.radius/3, planet.radius/2);
}
}
function drawGravityFields() {
noFill();
stroke(255, 100);
strokeWeight(1);
// Sun gravity field
ellipse(sun.x, sun.y, sun.radius * 8);
// Planet gravity fields
for (let planet of planets) {
ellipse(planet.x, planet.y, planet.radius * 6);
}
}
function aiMovement() {
// Simple AI to follow the ball
let aiPaddleCenter = aiPaddle.y + aiPaddle.height / 2;
let ballFutureY = ball.y + ball.ySpeed * 3; // Predict ball position
if (aiPaddleCenter < ballFutureY - 10) {
aiPaddle.move(5);
} else if (aiPaddleCenter > ballFutureY + 10) {
aiPaddle.move(-5);
}
}
function keyPressed() {
if (keyCode === UP_ARROW || key === 'w' || key === 'W') {
playerPaddle.move(-10);
} else if (keyCode === DOWN_ARROW || key === 's' || key === 'S') {
playerPaddle.move(10);
}
}
function keyReleased() {
if ((keyCode === UP_ARROW || key === 'w' || key === 'W') ||
(keyCode === DOWN_ARROW || key === 's' || key === 'S')) {
playerPaddle.move(0);
}
}
function checkPaddleCollision(ball, paddle) {
if (ball.x - ball.radius < paddle.x + paddle.width &&
ball.x + ball.radius > paddle.x &&
ball.y - ball.radius < paddle.y + paddle.height &&
ball.y + ball.radius > paddle.y) {
// Calculate angle based on where ball hits paddle
let intersectY = (ball.y - (paddle.y + paddle.height/2));
let normalizedIntersectY = intersectY/(paddle.height/2);
let bounceAngle = normalizedIntersectY * PI/4;
// Reverse direction and add angle
ball.xSpeed = paddle.isLeft ? abs(ball.xSpeed) : -abs(ball.xSpeed);
ball.ySpeed = ball.maxSpeed * sin(bounceAngle);
// Add some randomness
ball.ySpeed += random(-0.5, 0.5);
// Ensure speed stays within bounds
let speed = sqrt(ball.xSpeed * ball.xSpeed + ball.ySpeed * ball.ySpeed);
if (speed > ball.maxSpeed) {
ball.xSpeed = ball.xSpeed * ball.maxSpeed / speed;
ball.ySpeed = ball.ySpeed * ball.maxSpeed / speed;
}
// Add visual effect
paddle.hitEffect();
}
}
class Ball {
constructor() {
this.radius = 10;
this.reset(100, height/2);
this.maxSpeed = 8;
this.color = [255, 255, 255];
}
reset(x, y) {
this.x = x;
this.y = y;
this.xSpeed = random(4, 6) * (random() > 0.5 ? 1 : -1);
this.ySpeed = random(-2, 2);
this.trail = [];
}
update() {
// Apply gravity from sun
this.applyGravity(sun);
// Apply gravity from planets
for (let planet of planets) {
this.applyGravity(planet);
}
{
const dx = this.x - sun.x;
const dy = this.y - sun.y;
const dist = Math.sqrt(dx*dx + dy*dy);
const deflectRadius = sun.radius * 0.6; // ring just outside the sun
if (dist < deflectRadius && dist > 0) {
// unit outward normal
const nx = dx / dist, ny = dy / dist;
// reflect velocity across the normal
const vdotn = this.xSpeed * nx + this.ySpeed * ny;
this.xSpeed -= 2 * vdotn * nx;
this.ySpeed -= 2 * vdotn * ny;
// push the ball to the ring boundary so it doesn't immediately retrigger
this.x = sun.x + nx * deflectRadius;
this.y = sun.y + ny * deflectRadius;
}
}
// Update position
this.x += this.xSpeed;
this.y += this.ySpeed;
// Store position for trail
this.trail.push({x: this.x, y: this.y});
if (this.trail.length > 20) {
this.trail.shift();
}
// Check wall collisions
if (this.y - this.radius < 0 || this.y + this.radius > height) {
this.ySpeed *= -1;
}
// Check scoring
if (this.x - this.radius < 0) {
aiScore++;
this.reset(width - 100, height/2);
} else if (this.x + this.radius > width) {
playerScore++;
this.reset(100, height/2);
}
}
applyGravity(body) {
// Calculate distance to gravitational body
let dx = body.x - this.x;
let dy = body.y - this.y;
let distance = sqrt(dx * dx + dy * dy);
// Calculate gravitational force (inverse square law)
let force = (body.mass * gravityStrength) / (distance * distance);
// Normalize direction vector
if (distance > 0) {
dx /= distance;
dy /= distance;
}
// Apply force to ball's velocity
this.xSpeed += dx * force * 0.5;
this.ySpeed += dy * force * 0.5;
// Limit speed
let speed = sqrt(this.xSpeed * this.xSpeed + this.ySpeed * this.ySpeed);
if (speed > this.maxSpeed) {
this.xSpeed = this.xSpeed * this.maxSpeed / speed;
this.ySpeed = this.ySpeed * this.maxSpeed / speed;
}
}
display() {
// Draw trail
for (let i = 0; i < this.trail.length; i++) {
let alpha = map(i, 0, this.trail.length, 50, 255);
let size = map(i, 0, this.trail.length, this.radius * 0.3, this.radius);
fill(this.color[0], this.color[1], this.color[2], alpha);
noStroke();
ellipse(this.trail[i].x, this.trail[i].y, size * 2);
}
// Draw ball
fill(this.color[0], this.color[1], this.color[2]);
stroke(200);
strokeWeight(1);
ellipse(this.x, this.y, this.radius * 2);
// Add some shine
fill(255, 255, 255, 150);
noStroke();
ellipse(this.x - this.radius/3, this.y - this.radius/3, this.radius/2);
}
}
class Paddle {
constructor(isLeft) {
this.width = 15;
this.height = 100;
this.x = isLeft ? 30 : width - 30 - this.width;
this.y = height/2 - this.height/2;
this.speed = 0;
this.isLeft = isLeft;
this.color = isLeft ? [100, 200, 255] : [255, 100, 100];
this.hitAlpha = 0;
}
move(amount) {
this.speed = amount;
}
update() {
this.y += this.speed;
this.y = constrain(this.y, 0, height - this.height);
// Fade hit effect
if (this.hitAlpha > 0) {
this.hitAlpha -= 5;
}
}
display() {
// Glow effect when hit
if (this.hitAlpha > 0) {
fill(this.color[0], this.color[1], this.color[2], this.hitAlpha);
noStroke();
rect(this.x - 5, this.y - 5, this.width + 10, this.height + 10, 5);
}
// Paddle
fill(this.color[0], this.color[1], this.color[2]);
stroke(255, 255, 255, 100);
strokeWeight(1);
rect(this.x, this.y, this.width, this.height, 5);
// Paddle details
fill(255, 255, 255, 50);
noStroke();
rect(this.x + 3, this.y + 10, this.width - 6, this.height - 20, 3);
}
hitEffect() {
this.hitAlpha = 100;
}
}
</script>
</body>
</html>