xxxxxxxxxx
267
// Header
/*
* Hütchen-Spiel Implementation
* Date: 10.11.2024
* A simple ball shooting game with specific measurements and bouncing physics
* Scale: 1 meter = 80 pixels
*/
// Constants for conversion and physics
const METER_TO_PIXELS = 80;
const CM_TO_PIXELS = METER_TO_PIXELS / 100;
const GRAVITY = 0.4;
const BOUNCE_FACTOR = 0.7; // Coefficient of restitution
const FRICTION = 0.99; // Ground friction
const GROUND_Y = 500;
let ball = {
size: 20 * CM_TO_PIXELS,
x: 0,
y: 0,
velocityX: 0,
velocityY: 0,
isLaunched: false
};
let catapult = {
baseX: 9 * METER_TO_PIXELS,
baseY: GROUND_Y,
width: 40,
minLength: 0.3 * METER_TO_PIXELS,
maxLength: 1 * METER_TO_PIXELS,
currentLength: 0.3 * METER_TO_PIXELS,
angle: 0
};
let target = {
x: 4 * METER_TO_PIXELS,
y: GROUND_Y - ball.size/2,
size: ball.size,
velocityX: 0
};
let hole = {
x: 2.5 * METER_TO_PIXELS,
y: GROUND_Y,
size: 40
};
let obstacle = {
x: 7 * METER_TO_PIXELS,
y: GROUND_Y - (50 * CM_TO_PIXELS),
width: 20,
height: 50 * CM_TO_PIXELS
};
let slope = {
x: 0,
y: GROUND_Y,
width: 1 * METER_TO_PIXELS,
height: 50 * CM_TO_PIXELS,
getY: function(x) {
if (x < 0 || x > this.width) return GROUND_Y;
let progress = x / this.width;
return GROUND_Y - (1 - progress) * this.height;
}
};
let flagpole = {
x: 3 * METER_TO_PIXELS,
y: GROUND_Y,
height: 1 * METER_TO_PIXELS,
flagWidth: 30,
flagHeight: 20
};
let gameStats = {
hits: 0,
misses: 0
};
function setup() {
createCanvas(10 * METER_TO_PIXELS, 600);
resetBall();
}
function draw() {
background(220);
// Draw ground and borders (light blue)
fill(173, 216, 230);
rect(0, GROUND_Y, width, height - GROUND_Y);
rect(0, 0, 20, GROUND_Y);
// Draw slope (light blue)
fill(173, 216, 230);
triangle(
slope.x, slope.y,
slope.x + slope.width, slope.y,
slope.x, slope.y - slope.height
);
// Draw hole
fill(0);
ellipse(hole.x, hole.y, hole.size);
// Draw obstacle (red wall)
fill(255, 0, 0);
rect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
// Draw flagpole
stroke(0);
line(flagpole.x, flagpole.y, flagpole.x, flagpole.y - flagpole.height);
fill(255, 255, 0);
triangle(
flagpole.x, flagpole.y - flagpole.height,
flagpole.x + flagpole.flagWidth, flagpole.y - flagpole.height + flagpole.flagHeight/2,
flagpole.x, flagpole.y - flagpole.height + flagpole.flagHeight
);
// Draw catapult (green)
let ballX = catapult.baseX - cos(catapult.angle) * catapult.currentLength;
let ballY = catapult.baseY - sin(catapult.angle) * catapult.currentLength;
stroke(34, 139, 34);
fill(34, 139, 34);
triangle(
catapult.baseX - 20, catapult.baseY,
catapult.baseX + 20, catapult.baseY,
ballX, ballY
);
// Draw target (red ball)
fill(255, 0, 0);
noStroke();
ellipse(target.x, target.y, target.size);
// Update ball physics if launched
if (ball.isLaunched) {
// Update position
ball.x += ball.velocityX;
ball.y += ball.velocityY;
ball.velocityY += GRAVITY;
// Ground collision with slope
let groundY = GROUND_Y;
if (ball.x < slope.width) {
groundY = slope.getY(ball.x);
}
if (ball.y + ball.size/2 > groundY) {
ball.y = groundY - ball.size/2;
ball.velocityY = -ball.velocityY * BOUNCE_FACTOR;
ball.velocityX *= FRICTION;
// Stop ball if velocity is very low
if (Math.abs(ball.velocityY) < 0.5) {
ball.velocityY = 0;
ball.y = groundY - ball.size/2;
}
}
// Wall collisions
if (ball.x - ball.size/2 < 20) { // Left wall
ball.x = 20 + ball.size/2;
ball.velocityX = -ball.velocityX * BOUNCE_FACTOR;
}
if (ball.x + ball.size/2 > width) { // Right wall
ball.x = width - ball.size/2;
ball.velocityX = -ball.velocityX * BOUNCE_FACTOR;
}
// Obstacle collision
if (ball.x + ball.size/2 > obstacle.x &&
ball.x - ball.size/2 < obstacle.x + obstacle.width &&
ball.y + ball.size/2 > obstacle.y &&
ball.y - ball.size/2 < obstacle.y + obstacle.height) {
// Determine which side of the obstacle was hit
let dx = (ball.x < obstacle.x + obstacle.width/2) ? obstacle.x - (ball.x + ball.size/2) : (obstacle.x + obstacle.width) - (ball.x - ball.size/2);
let dy = (ball.y < obstacle.y + obstacle.height/2) ? obstacle.y - (ball.y + ball.size/2) : (obstacle.y + obstacle.height) - (ball.y - ball.size/2);
if (Math.abs(dx) < Math.abs(dy)) {
ball.x += dx;
ball.velocityX = -ball.velocityX * BOUNCE_FACTOR;
} else {
ball.y += dy;
ball.velocityY = -ball.velocityY * BOUNCE_FACTOR;
}
}
// Target collision
let d = dist(ball.x, ball.y, target.x, target.y);
if (d < (ball.size + target.size) / 2) {
// Transfer momentum to target
target.velocityX = ball.velocityX * 0.8;
target.x += target.velocityX;
// Keep target on ground
target.y = GROUND_Y - target.size/2;
// Apply friction to target
target.velocityX *= FRICTION;
// Check if target reached hole
let dHole = dist(target.x, target.y, hole.x, hole.y - target.size/2);
if (dHole < hole.size/2) {
gameStats.hits++;
resetGame();
}
}
// Update target position with friction
if (Math.abs(target.velocityX) > 0.01) {
target.x += target.velocityX;
target.velocityX *= FRICTION;
}
// Reset if ball stops moving
if (Math.abs(ball.velocityX) < 0.1 && Math.abs(ball.velocityY) < 0.1 &&
ball.y + ball.size/2 >= GROUND_Y - 1) {
gameStats.misses++;
resetBall();
}
} else {
ball.x = ballX;
ball.y = ballY;
}
// Draw player's ball (green)
fill(34, 139, 34);
ellipse(ball.x, ball.y, ball.size);
// Draw score
fill(0);
textSize(20);
text(`Hits: ${gameStats.hits} Misses: ${gameStats.misses}`, 20, 30);
// Update catapult stretch based on mouse position when not launched
if (!ball.isLaunched) {
let mouseDistance = dist(mouseX, mouseY, catapult.baseX, catapult.baseY);
catapult.currentLength = constrain(mouseDistance, catapult.minLength, catapult.maxLength);
catapult.angle = atan2(catapult.baseY - mouseY, catapult.baseX - mouseX);
}
}
function mousePressed() {
if (!ball.isLaunched) {
let power = map(catapult.currentLength, catapult.minLength, catapult.maxLength, 5, 15);
ball.velocityX = -cos(catapult.angle) * power;
ball.velocityY = -sin(catapult.angle) * power;
ball.isLaunched = true;
}
}
function resetBall() {
ball.isLaunched = false;
catapult.currentLength = catapult.minLength;
catapult.angle = 0;
ball.velocityX = 0;
ball.velocityY = 0;
}
function resetGame() {
resetBall();
target.x = 4 * METER_TO_PIXELS;
target.y = GROUND_Y - target.size/2;
target.velocityX = 0;
}