Pong för tanter
<!DOCTYPE html> <html> <head> <title>Pong från AINNOVA</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <style> body { margin: 0; padding: 10px; background: #f0f4f8; font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; touch-action: manipulation; overflow: hidden; } .game-container { position: relative; width: 100%; max-width: 600px; display: flex; flex-direction: column; align-items: center; } .logo-small { width: 80px; height: auto; margin-bottom: 5px; } canvas { border: 2px solid #007BBF; background: #000; width: 100%; max-height: 70vh; touch-action: none; } #startScreen { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(255, 255, 255, 0.95); padding: 15px; border-radius: 8px; text-align: center; width: 90%; max-width: 400px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); z-index: 10; } .button-container { display: flex; flex-direction: column; width: 100%; margin-top: 10px; } button { padding: 10px; margin: 5px 0; background: #007BBF; color: white; border: none; border-radius: 5px; font-size: 16px; width: 100%; cursor: pointer; } .score-display { font-size: 20px; margin: 5px 0; font-weight: bold; color: #007BBF; } h2 { font-size: 18px; margin: 5px 0; color: #333; text-align: center; } #status { color: #007BBF; font-size: 16px; margin-top: 5px; text-align: center; } @media (min-width: 400px) { .button-container { flex-direction: row; justify-content: center; } button { margin: 0 5px; width: auto; min-width: 120px; } } </style> </head> <body> <div class="game-container"> <img class="logo-small" src="https://valingeveckan.wordpress.com/wp-content/uploads/2025/04/namnlos-design-4-1.png" alt="AINNOVA Logo"> <h2>Pong – En demo från AINNOVA</h2> <div class="score-display" id="scoreDisplay">0 - 0</div> <canvas id="pong"></canvas> <div id="status"></div> <div id="startScreen"> <h1>Välkommen till Pong!</h1> <p>På dator: Använd W/S eller upp/ner-pil för vänster paddel.<br> På mobil: Tryck och dra upp/ner på vänster sida.<br> Två spelare? Höger paddel använder I/K (dator) eller dra på höger sida.<br> Mot datorn? Din paddel är större för att göra det lättare!<br> Första till 5 poäng vinner!</p> <div class="button-container"> <button onclick="startGame(false)">2 Spelare</button> <button onclick="startGame(true)">Mot datorn</button> </div> </div> </div>
<script> const canvas = document.getElementById('pong'); const ctx = canvas.getContext('2d'); const startScreen = document.getElementById('startScreen'); const scoreDisplay = document.getElementById('scoreDisplay'); const status = document.getElementById('status');
// Spelinställningar
const PADDLE_WIDTH = 10;
const WINNING_SCORE = 5;
let scale = 1;
// Spelobjekt
let leftPaddle = { x: 0, y: 160, height: 80, speed: 5 };
let rightPaddle = { x: 0, y: 170, height: 60, speed: 4 };
let ball = { x: 300, y: 200, size: 8, dx: 4, dy: 4 };
let leftScore = 0, rightScore = 0;
let gameState = 'start';
let vsComputer = false;
let animationFrameId = null;
let lastTime = 0;
const frameRate = 60;
const frameDelay = 1000 / frameRate;
// Initiera spelet
function initGame() {
setCanvasSize();
window.addEventListener('resize', setCanvasSize);
// Tangentbordsstyrning
document.addEventListener('keydown', function(e) {
e.preventDefault();
const key = e.key.toLowerCase();
if (key === 'w' || key === 'arrowup') keys.up = true;
if (key === 's' || key === 'arrowdown') keys.down = true;
if (key === 'i') keys.i = true;
if (key === 'k') keys.k = true;
if (key === ' ' && gameState === 'playing') {
gameState = 'paused';
status.textContent = 'Pausad – tryck mellanslag eller skärmen för att fortsätta';
} else if (key === ' ' && gameState === 'paused') {
gameState = 'playing';
status.textContent = 'Spelet är igång!';
gameLoop(performance.now());
}
});
document.addEventListener('keyup', function(e) {
const key = e.key.toLowerCase();
if (key === 'w' || key === 'arrowup') keys.up = false;
if (key === 's' || key === 'arrowdown') keys.down = false;
if (key === 'i') keys.i = false;
if (key === 'k') keys.k = false;
});
// Touchkontroller
canvas.addEventListener('touchstart', handleTouch);
canvas.addEventListener('touchmove', handleTouch);
canvas.addEventListener('touchend', e => e.preventDefault());
canvas.addEventListener('mousedown', handleMouse);
canvas.addEventListener('mousemove', handleMouse);
}
let keys = { up: false, down: false, i: false, k: false };
function handleTouch(e) {
e.preventDefault();
if (gameState === 'paused') {
gameState = 'playing';
status.textContent = 'Spelet är igång!';
gameLoop(performance.now());
return;
}
const touch = e.touches[0] || e.changedTouches[0];
const rect = canvas.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return; // Undvik division med noll
const touchX = (touch.clientX - rect.left) / rect.width * canvas.width;
const touchY = (touch.clientY - rect.top) / rect.height * canvas.height;
if (touchX < canvas.width / 2) {
leftPaddle.y = touchY - leftPaddle.height / 2;
} else if (!vsComputer) {
rightPaddle.y = touchY - rightPaddle.height / 2;
}
leftPaddle.y = Math.max(0, Math.min(canvas.height - leftPaddle.height, leftPaddle.y));
rightPaddle.y = Math.max(0, Math.min(canvas.height - rightPaddle.height, rightPaddle.y));
}
function handleMouse(e) {
if (gameState !== 'playing') return;
const rect = canvas.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return; // Undvik division med noll
const mouseX = (e.clientX - rect.left) / rect.width * canvas.width;
const mouseY = (e.clientY - rect.top) / rect.height * canvas.height;
if (mouseX < canvas.width / 2) {
leftPaddle.y = mouseY - leftPaddle.height / 2;
} else if (!vsComputer) {
rightPaddle.y = mouseY - rightPaddle.height / 2;
}
leftPaddle.y = Math.max(0, Math.min(canvas.height - leftPaddle.height, leftPaddle.y));
rightPaddle.y = Math.max(0, Math.min(canvas.height - rightPaddle.height, rightPaddle.y));
}
function setCanvasSize() {
const maxWidth = Math.min(window.innerWidth - 20, 600);
const maxHeight = Math.min(window.innerHeight * 0.7, 400);
canvas.width = maxWidth;
canvas.height = maxWidth * 2 / 3;
if (canvas.height > maxHeight) {
canvas.height = maxHeight;
canvas.width = maxHeight * 3 / 2;
}
scale = canvas.width / 600;
// Justera positioner och storlekar
leftPaddle.x = 0;
leftPaddle.height = 80 * scale;
leftPaddle.speed = 5 * scale;
rightPaddle.x = canvas.width - PADDLE_WIDTH * scale;
rightPaddle.height = 60 * scale;
rightPaddle.speed = 4 * scale;
ball.size = 8 * scale;
ball.dx = 4 * scale;
ball.dy = 4 * scale;
resetBall();
}
function startGame(computer) {
vsComputer = computer;
startScreen.style.display = 'none';
gameState = 'playing';
leftScore = 0;
rightScore = 0;
updateScore();
status.textContent = 'Spelet är igång! Använd W/S, pilar eller dra på skärmen.';
resetBall();
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
lastTime = performance.now();
gameLoop(lastTime);
}
function resetBall() {
ball.x = canvas.width / 2;
ball.y = canvas.height / 2;
ball.dx = (Math.random() > 0.5 ? 1 : -1) * 4 * scale;
ball.dy = (Math.random() - 0.5) * 4 * scale;
leftPaddle.y = canvas.height / 2 - leftPaddle.height / 2;
rightPaddle.y = canvas.height / 2 - rightPaddle.height / 2;
}
function updateScore() {
scoreDisplay.textContent = leftScore + ' - ' + rightScore;
}
function gameLoop(timestamp) {
if (gameState === 'gameover') {
drawGameOver();
return;
}
if (gameState !== 'playing') {
if (gameState === 'paused') {
drawPaused();
}
animationFrameId = requestAnimationFrame(gameLoop);
return;
}
if (timestamp - lastTime < frameDelay) {
animationFrameId = requestAnimationFrame(gameLoop);
return;
}
lastTime = timestamp;
updateGame();
drawGame();
animationFrameId = requestAnimationFrame(gameLoop);
}
function updateGame() {
// Flytta paddlar (tangentbord)
if (keys.up && leftPaddle.y > 0) {
leftPaddle.y -= leftPaddle.speed;
}
if (keys.down && leftPaddle.y < canvas.height - leftPaddle.height) {
leftPaddle.y += leftPaddle.speed;
}
if (!vsComputer) {
if (keys.i && rightPaddle.y > 0) {
rightPaddle.y -= rightPaddle.speed;
}
if (keys.k && rightPaddle.y < canvas.height - rightPaddle.height) {
rightPaddle.y += rightPaddle.speed;
}
} else {
// Dator-AI (med 30% chans att missa)
if (rightPaddle.y + rightPaddle.height / 2 < ball.y - 10 && Math.random() > 0.3) {
rightPaddle.y += rightPaddle.speed * 0.8;
} else if (rightPaddle.y + rightPaddle.height / 2 > ball.y + 10 && Math.random() > 0.3) {
rightPaddle.y -= rightPaddle.speed * 0.8;
}
}
// Flytta bollen
ball.x += ball.dx;
ball.y += ball.dy;
// Kollision med tak och golv
if (ball.y <= 0 || ball.y >= canvas.height - ball.size) {
ball.dy *= -1;
}
// Kollision med paddlar
if (
ball.x <= leftPaddle.x + PADDLE_WIDTH * scale &&
ball.y + ball.size >= leftPaddle.y &&
ball.y <= leftPaddle.y + leftPaddle.height
) {
ball.dx = Math.abs(ball.dx);
}
if (
ball.x + ball.size >= rightPaddle.x &&
ball.y + ball.size >= rightPaddle.y &&
ball.y <= rightPaddle.y + rightPaddle.height
) {
ball.dx = -Math.abs(ball.dx);
}
// Poäng
if (ball.x < 0) {
rightScore++;
updateScore();
resetBall();
}
if (ball.x > canvas.width) {
leftScore++;
updateScore();
resetBall();
}
// Vinnare
if (leftScore >= WINNING_SCORE || rightScore >= WINNING_SCORE) {
gameState = 'gameover';
}
}
function drawGame() {
// Rensa canvas
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Rita mittlinje
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.lineWidth = 2 * scale;
ctx.beginPath();
ctx.moveTo(canvas.width / 2, 0);
ctx.lineTo(canvas.width / 2, canvas.height);
ctx.stroke();
// Rita paddlar
ctx.fillStyle = '#007BBF';
ctx.fillRect(leftPaddle.x, leftPaddle.y, PADDLE_WIDTH * scale, leftPaddle.height);
ctx.fillRect(rightPaddle.x, rightPaddle.y, PADDLE_WIDTH * scale, rightPaddle.height);
// Rita boll
ctx.fillStyle = 'white';
ctx.fillRect(ball.x, ball.y, ball.size, ball.size);
}
function drawGameOver() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = Math.round(20 * scale) + 'px Arial';
ctx.textAlign = 'center';
if (leftScore >= WINNING_SCORE) {
ctx.fillText('Tanten vann!', canvas.width / 2, canvas.height / 2 - 20 * scale);
} else if (vsComputer) {
ctx.fillText('Datorn vann!', canvas.width / 2, canvas.height / 2 - 20 * scale);
} else {
ctx.fillText('Spelare 2 vann!', canvas.width / 2, canvas.height / 2 - 20 * scale);
}
ctx.font = Math.round(16 * scale) + 'px Arial';
ctx.fillText('Tryck på Starta för att spela igen', canvas.width / 2, canvas.height / 2 + 20 * scale);
startScreen.style.display = 'block';
startScreen.querySelector('h1').textContent = 'Spelet är slut!';
}
function drawPaused() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = Math.round(20 * scale) + 'px Arial';
ctx.textAlign = 'center';
ctx.fillText('Pausad', canvas.width / 2, canvas.height / 2);
}
// Starta spelet
initGame();
</script> </body> </html>