first commit
This commit is contained in:
parent
d858b258b2
commit
4e88f8add2
|
@ -0,0 +1,266 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pong in WebGL</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
font-family: Arial, sans-serif;
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
#score {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="score">Player: 0 | Opponent: 0</div>
|
||||
<canvas id="gameCanvas"></canvas>
|
||||
|
||||
<script>
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const gl = canvas.getContext('webgl');
|
||||
const scoreDisplay = document.getElementById('score');
|
||||
|
||||
if (!gl) {
|
||||
alert('WebGL not supported');
|
||||
throw new Error('WebGL not supported');
|
||||
}
|
||||
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 a_position;
|
||||
uniform vec2 u_resolution;
|
||||
|
||||
void main() {
|
||||
vec2 zeroToOne = a_position / u_resolution;
|
||||
vec2 zeroToTwo = zeroToOne * 2.0;
|
||||
vec2 clipSpace = zeroToTwo - 1.0;
|
||||
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(1, 1, 1, 1);
|
||||
}
|
||||
`;
|
||||
|
||||
function createShader(gl, type, source) {
|
||||
const shader = gl.createShader(type);
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
console.error(gl.getShaderInfoLog(shader));
|
||||
gl.deleteShader(shader);
|
||||
return null;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
function createProgram(gl, vertexShader, fragmentShader) {
|
||||
const program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
|
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
||||
console.error(gl.getProgramInfoLog(program));
|
||||
gl.deleteProgram(program);
|
||||
return null;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
|
||||
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
|
||||
const program = createProgram(gl, vertexShader, fragmentShader);
|
||||
|
||||
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
|
||||
const resolutionUniformLocation = gl.getUniformLocation(program, 'u_resolution');
|
||||
|
||||
const positionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
|
||||
const paddleWidth = 20;
|
||||
const paddleHeight = 100;
|
||||
const ballSize = 15;
|
||||
|
||||
let paddle1Y = canvas.height / 2 - paddleHeight / 2;
|
||||
let paddle2Y = canvas.height / 2 - paddleHeight / 2;
|
||||
let balls = [{ x: canvas.width / 2, y: canvas.height / 2, speedX: 5, speedY: 5 }];
|
||||
let playerScore = 0;
|
||||
let opponentScore = 0;
|
||||
let speedIncrement = 0.2;
|
||||
let powerups = [];
|
||||
let bricks = [];
|
||||
let opponentInverted = false;
|
||||
|
||||
function spawnPowerup() {
|
||||
powerups.push({
|
||||
x: Math.random() * (canvas.width - 50) + 25,
|
||||
y: Math.random() * (canvas.height - 50) + 25,
|
||||
type: Math.floor(Math.random() * 3) // 0: multi-ball, 1: invert opponent, 2: bricks
|
||||
});
|
||||
}
|
||||
|
||||
function spawnBricks() {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
bricks.push({
|
||||
x: canvas.width / 2 - 100 + i * 40,
|
||||
y: canvas.height / 2 - 20,
|
||||
width: 30,
|
||||
height: 10
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
paddle1Y = e.clientY - paddleHeight / 2;
|
||||
paddle1Y = Math.max(0, Math.min(canvas.height - paddleHeight, paddle1Y));
|
||||
});
|
||||
|
||||
function drawRect(x, y, width, height) {
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
|
||||
x, y,
|
||||
x + width, y,
|
||||
x, y + height,
|
||||
x, y + height,
|
||||
x + width, y,
|
||||
x + width, y + height,
|
||||
]), gl.STATIC_DRAW);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
function update() {
|
||||
// Clear canvas
|
||||
gl.clearColor(0, 0, 0, 1);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.useProgram(program);
|
||||
gl.enableVertexAttribArray(positionAttributeLocation);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform2f(resolutionUniformLocation, canvas.width, canvas.height);
|
||||
|
||||
// Draw paddles
|
||||
drawRect(10, paddle1Y, paddleWidth, paddleHeight);
|
||||
drawRect(canvas.width - paddleWidth - 10, paddle2Y, paddleWidth, paddleHeight);
|
||||
|
||||
// Update and draw balls
|
||||
balls.forEach((ball, index) => {
|
||||
ball.x += ball.speedX;
|
||||
ball.y += ball.speedY;
|
||||
|
||||
if (ball.y <= 0 || ball.y + ballSize >= canvas.height) {
|
||||
ball.speedY *= -1;
|
||||
}
|
||||
|
||||
if (ball.x <= paddleWidth + 10 && ball.y + ballSize >= paddle1Y && ball.y <= paddle1Y + paddleHeight) {
|
||||
ball.speedX *= -1;
|
||||
ball.speedX += speedIncrement;
|
||||
ball.speedY += (Math.random() - 0.5) * 2;
|
||||
}
|
||||
|
||||
if (ball.x + ballSize >= canvas.width - paddleWidth - 10 && ball.y + ballSize >= paddle2Y && ball.y <= paddle2Y + paddleHeight) {
|
||||
ball.speedX *= -1;
|
||||
ball.speedX -= speedIncrement;
|
||||
ball.speedY += (Math.random() - 0.5) * 2;
|
||||
}
|
||||
|
||||
if (ball.x <= 0) {
|
||||
opponentScore++;
|
||||
balls.splice(index, 1);
|
||||
} else if (ball.x + ballSize >= canvas.width) {
|
||||
playerScore++;
|
||||
balls.splice(index, 1);
|
||||
}
|
||||
|
||||
drawRect(ball.x, ball.y, ballSize, ballSize);
|
||||
});
|
||||
|
||||
if (balls.length === 0) {
|
||||
balls.push({ x: canvas.width / 2, y: canvas.height / 2, speedX: 5, speedY: 5 });
|
||||
}
|
||||
|
||||
// Draw and check powerups
|
||||
powerups.forEach((powerup, index) => {
|
||||
drawRect(powerup.x, powerup.y, 20, 20);
|
||||
|
||||
balls.forEach((ball) => {
|
||||
if (
|
||||
ball.x < powerup.x + 20 &&
|
||||
ball.x + ballSize > powerup.x &&
|
||||
ball.y < powerup.y + 20 &&
|
||||
ball.y + ballSize > powerup.y
|
||||
) {
|
||||
if (powerup.type === 0) {
|
||||
balls.push({ x: ball.x, y: ball.y, speedX: -ball.speedX, speedY: -ball.speedY });
|
||||
} else if (powerup.type === 1) {
|
||||
opponentInverted = !opponentInverted;
|
||||
} else if (powerup.type === 2) {
|
||||
spawnBricks();
|
||||
}
|
||||
|
||||
powerups.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Draw and check bricks
|
||||
bricks.forEach((brick, index) => {
|
||||
drawRect(brick.x, brick.y, brick.width, brick.height);
|
||||
|
||||
balls.forEach((ball) => {
|
||||
if (
|
||||
ball.x < brick.x + brick.width &&
|
||||
ball.x + ballSize > brick.x &&
|
||||
ball.y < brick.y + brick.height &&
|
||||
ball.y + ballSize > brick.y
|
||||
) {
|
||||
ball.speedY *= -1;
|
||||
bricks.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Opponent AI
|
||||
const targetY = balls[0].y - paddleHeight / 2;
|
||||
paddle2Y += (opponentInverted ? -1 : 1) * (targetY - paddle2Y) * 0.1;
|
||||
paddle2Y = Math.max(0, Math.min(canvas.height - paddleHeight, paddle2Y));
|
||||
|
||||
// Update score
|
||||
scoreDisplay.textContent = `Player: ${playerScore} | Opponent: ${opponentScore}`;
|
||||
|
||||
// Spawn powerups occasionally
|
||||
if (Math.random() < 0.01) {
|
||||
spawnPowerup();
|
||||
}
|
||||
|
||||
requestAnimationFrame(update);
|
||||
}
|
||||
|
||||
update();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue