added games
This commit is contained in:
612
games/fussball.html
Normal file
612
games/fussball.html
Normal file
@@ -0,0 +1,612 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Elfmeterschießen Simulator</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #222;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#game-container {
|
||||
position: relative;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||
border: 4px solid #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
background: linear-gradient(to bottom, #87CEEB 0%, #87CEEB 30%, #2E8B57 30%, #228B22 100%);
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
#ui-overlay {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
#score-board {
|
||||
font-size: 18px;
|
||||
margin-top: 5px;
|
||||
font-weight: bold;
|
||||
color: #ffeb3b;
|
||||
}
|
||||
|
||||
#message {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 48px;
|
||||
font-weight: 900;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
text-shadow: 0 0 10px rgba(0,0,0,0.5);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.instruction {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="game-container">
|
||||
<canvas id="gameCanvas" width="800" height="600"></canvas>
|
||||
|
||||
<div id="ui-overlay">
|
||||
<h1>Elfmeter Simulator</h1>
|
||||
<div id="score-board">Tore: 0 | Versuche: 0</div>
|
||||
</div>
|
||||
|
||||
<div id="message">TOOR!</div>
|
||||
<div class="instruction">Klicke irgendwo ins Tor, um zu schießen!</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const messageEl = document.getElementById('message');
|
||||
const scoreEl = document.getElementById('score-board');
|
||||
|
||||
// Zuschauer-Daten (statisch)
|
||||
const spectators = [];
|
||||
|
||||
// Spielzustand
|
||||
let score = 0;
|
||||
let attempts = 0;
|
||||
let isBallFlying = false;
|
||||
let showMessageTimer = null;
|
||||
let frameCount = 0; // Für Animationen
|
||||
|
||||
// Ball Physik
|
||||
const startBallY = 520;
|
||||
const startBallX = 400;
|
||||
const ball = {
|
||||
x: startBallX,
|
||||
y: startBallY,
|
||||
z: 0,
|
||||
radius: 15,
|
||||
targetX: 0,
|
||||
targetY: 0,
|
||||
speed: 0
|
||||
};
|
||||
|
||||
// Torwart (Verbessert)
|
||||
const goalie = {
|
||||
x: 400,
|
||||
baseY: 230, // Bodenlinie
|
||||
y: 230, // Aktuelle Y Position
|
||||
width: 50, // Körperbreite
|
||||
height: 90,
|
||||
speed: 1.5,
|
||||
direction: 1,
|
||||
diveTargetX: 0,
|
||||
diveTargetY: 0,
|
||||
isDiving: false,
|
||||
angle: 0 // Rotation für Hechtsprung
|
||||
};
|
||||
|
||||
// Tordimensionen
|
||||
const goal = {
|
||||
topY: 130,
|
||||
bottomY: 230,
|
||||
topLeftX: 250,
|
||||
topRightX: 550,
|
||||
bottomLeftX: 230,
|
||||
bottomRightX: 570
|
||||
};
|
||||
|
||||
// Hilfsfunktionen
|
||||
function initSpectators() {
|
||||
for (let y = 55; y < 220; y += 20) {
|
||||
for (let x = 10; x < canvas.width; x += 15 + Math.random() * 10) {
|
||||
spectators.push({
|
||||
x: x,
|
||||
y: y,
|
||||
color: `hsl(${Math.random() * 360}, 70%, 60%)`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resetBall() {
|
||||
isBallFlying = false;
|
||||
ball.x = startBallX;
|
||||
ball.y = startBallY;
|
||||
ball.z = 0;
|
||||
ball.radius = 15;
|
||||
|
||||
// Torwart zurücksetzen
|
||||
goalie.isDiving = false;
|
||||
goalie.x = 400;
|
||||
goalie.y = goalie.baseY;
|
||||
goalie.angle = 0;
|
||||
}
|
||||
|
||||
function showStatus(text, color) {
|
||||
messageEl.innerText = text;
|
||||
messageEl.style.color = color;
|
||||
messageEl.style.opacity = 1;
|
||||
|
||||
clearTimeout(showMessageTimer);
|
||||
showMessageTimer = setTimeout(() => {
|
||||
messageEl.style.opacity = 0;
|
||||
resetBall();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function updateScore() {
|
||||
scoreEl.innerText = `Tore: ${score} | Versuche: ${attempts}`;
|
||||
}
|
||||
|
||||
// --- Zeichnen ---
|
||||
|
||||
function drawStands() {
|
||||
ctx.fillStyle = "#555";
|
||||
ctx.fillRect(0, 50, canvas.width, 180);
|
||||
|
||||
ctx.fillStyle = "#777";
|
||||
for (let y = 60; y < 230; y += 20) {
|
||||
ctx.fillRect(0, y, canvas.width, 10);
|
||||
}
|
||||
|
||||
for (const spectator of spectators) {
|
||||
ctx.fillStyle = spectator.color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(spectator.x, spectator.y, 5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
ctx.fillStyle = "#333";
|
||||
ctx.fillRect(0, 0, canvas.width, 50);
|
||||
ctx.strokeStyle = "#222";
|
||||
ctx.lineWidth = 5;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 50);
|
||||
ctx.lineTo(canvas.width, 50);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawPitch() {
|
||||
ctx.strokeStyle = "rgba(255, 255, 255, 0.2)";
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(100, 600);
|
||||
ctx.lineTo(200, 230);
|
||||
ctx.lineTo(600, 230);
|
||||
ctx.lineTo(700, 600);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.fillStyle = "white";
|
||||
ctx.beginPath();
|
||||
ctx.arc(400, 480, 3, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function drawGoal() {
|
||||
ctx.strokeStyle = "white";
|
||||
ctx.lineWidth = 8;
|
||||
ctx.lineJoin = "round";
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(goal.topLeftX + 20, goal.topY - 10);
|
||||
ctx.lineTo(goal.topRightX - 20, goal.topY - 10);
|
||||
ctx.lineTo(goal.bottomRightX - 10, goal.bottomY);
|
||||
ctx.moveTo(goal.topLeftX + 20, goal.topY - 10);
|
||||
ctx.lineTo(goal.bottomLeftX + 10, goal.bottomY);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.strokeStyle = "rgba(255, 255, 255, 0.3)";
|
||||
ctx.lineWidth = 1;
|
||||
for (let i = goal.topLeftX; i < goal.topRightX; i += 20) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(i, goal.topY);
|
||||
ctx.lineTo(i + (i - 400) * 0.2, goal.bottomY);
|
||||
ctx.stroke();
|
||||
}
|
||||
for (let j = goal.topY; j < goal.bottomY; j += 15) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(goal.topLeftX, j);
|
||||
ctx.lineTo(goal.topRightX, j);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.strokeStyle = "white";
|
||||
ctx.lineWidth = 8;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(goal.bottomLeftX, goal.bottomY);
|
||||
ctx.lineTo(goal.topLeftX, goal.topY);
|
||||
ctx.lineTo(goal.topRightX, goal.topY);
|
||||
ctx.lineTo(goal.bottomRightX, goal.bottomY);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
function drawGoalie() {
|
||||
ctx.save();
|
||||
|
||||
// Position setzen (Mitte des Torsos ca.)
|
||||
let drawX = goalie.x;
|
||||
let drawY = goalie.y - 45; // Etwas höher als die Füße
|
||||
|
||||
ctx.translate(drawX, drawY);
|
||||
ctx.rotate(goalie.angle); // Rotation für Hechtsprung
|
||||
|
||||
// Farben
|
||||
const skinColor = "#ffccaa";
|
||||
const shirtColor = "#FFD700"; // Gelb
|
||||
const shortsColor = "#111"; // Schwarz
|
||||
|
||||
// --- Zeichne Körperteile (Menschlicher) ---
|
||||
|
||||
// Kopf
|
||||
ctx.fillStyle = skinColor;
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, -35, 12, 0, Math.PI * 2); // Relativ zur Mitte
|
||||
ctx.fill();
|
||||
|
||||
// Körper (Trikot)
|
||||
ctx.fillStyle = shirtColor;
|
||||
// Abgerundetes Rechteck für Torso
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(-18, -25, 36, 45, 5);
|
||||
ctx.fill();
|
||||
|
||||
// Hose
|
||||
ctx.fillStyle = shortsColor;
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(-18, 15, 36, 15, 5);
|
||||
ctx.fill();
|
||||
|
||||
// Gliedmaßen Animation
|
||||
ctx.lineCap = "round";
|
||||
ctx.lineJoin = "round";
|
||||
|
||||
// Beine
|
||||
ctx.strokeStyle = skinColor; // Beine Hautfarbe oder Socken? Sagen wir schwarz für Stutzen
|
||||
ctx.lineWidth = 10;
|
||||
|
||||
ctx.strokeStyle = "#111"; // Stutzen/Hose Verlängerung
|
||||
ctx.beginPath();
|
||||
if (goalie.isDiving) {
|
||||
// Beine strecken beim Sprung
|
||||
ctx.moveTo(-10, 30); ctx.lineTo(-15, 60); // Linkes Bein
|
||||
ctx.moveTo(10, 30); ctx.lineTo(15, 60); // Rechtes Bein
|
||||
} else {
|
||||
// Beine leicht gebeugt beim Stehen (Idle Animation)
|
||||
const kneeBend = Math.sin(frameCount * 0.1) * 2;
|
||||
ctx.moveTo(-10, 30); ctx.lineTo(-12, 55 + kneeBend);
|
||||
ctx.moveTo(10, 30); ctx.lineTo(12, 55 + kneeBend);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Arme (Trikot Farbe Ärmel + Haut)
|
||||
// Wir zeichnen vereinfacht ganze Arme in Hautfarbe mit gelben Ärmeln
|
||||
|
||||
// Ärmel
|
||||
ctx.strokeStyle = shirtColor;
|
||||
ctx.lineWidth = 12;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-15, -20); ctx.lineTo(-25, -5); // Links
|
||||
ctx.moveTo(15, -20); ctx.lineTo(25, -5); // Rechts
|
||||
ctx.stroke();
|
||||
|
||||
// Unterarme / Hände
|
||||
ctx.strokeStyle = skinColor;
|
||||
ctx.lineWidth = 10;
|
||||
ctx.beginPath();
|
||||
|
||||
if (goalie.isDiving) {
|
||||
// Arme weit strecken zum Ball
|
||||
// Wenn wir nach rechts springen (angle > 0), ist "oben" relativ zur Rotation
|
||||
// Da wir das ganze Canvas rotiert haben, zeichnen wir einfach Arme nach "oben" (über den Kopf)
|
||||
ctx.moveTo(-25, -5); ctx.lineTo(-35, -40); // Arme hochreißen
|
||||
ctx.moveTo(25, -5); ctx.lineTo(35, -40);
|
||||
|
||||
// Handschuhe (Weiß)
|
||||
ctx.strokeStyle = "white";
|
||||
ctx.lineWidth = 14;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-35, -40); ctx.lineTo(-37, -45);
|
||||
ctx.moveTo(35, -40); ctx.lineTo(37, -45);
|
||||
ctx.stroke();
|
||||
|
||||
} else {
|
||||
// Arme bereit halten (Idle)
|
||||
const armWave = Math.cos(frameCount * 0.1) * 3;
|
||||
ctx.moveTo(-25, -5); ctx.lineTo(-35 - armWave, 10);
|
||||
ctx.moveTo(25, -5); ctx.lineTo(35 + armWave, 10);
|
||||
|
||||
// Handschuhe
|
||||
ctx.strokeStyle = "white";
|
||||
ctx.lineWidth = 14;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-35 - armWave, 10); ctx.lineTo(-37 - armWave, 15);
|
||||
ctx.moveTo(35 + armWave, 10); ctx.lineTo(37 + armWave, 15);
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function drawBall() {
|
||||
ctx.save();
|
||||
ctx.translate(ball.x, ball.y);
|
||||
|
||||
if (isBallFlying) {
|
||||
const rotation = Date.now() / 100;
|
||||
ctx.rotate(rotation);
|
||||
}
|
||||
|
||||
ctx.fillStyle = "white";
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, ball.radius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = "black";
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, ball.radius * 0.5, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
|
||||
for(let i=0; i<5; i++) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(
|
||||
Math.cos(i * 2 * Math.PI / 5) * ball.radius * 0.7,
|
||||
Math.sin(i * 2 * Math.PI / 5) * ball.radius * 0.7,
|
||||
ball.radius * 0.2, 0, Math.PI*2
|
||||
);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
if (!isBallFlying || ball.z < 0.1) {
|
||||
ctx.restore();
|
||||
ctx.fillStyle = "rgba(0,0,0,0.3)";
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(ball.x + 5, ball.y + ball.radius + 2, ball.radius, ball.radius * 0.3, 0, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
} else {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Logik ---
|
||||
|
||||
function update() {
|
||||
frameCount++;
|
||||
|
||||
// Torwart Idle Bewegung (Automatisch hin und her)
|
||||
if (!isBallFlying && !goalie.isDiving) {
|
||||
goalie.x += goalie.speed * goalie.direction;
|
||||
|
||||
// Leichte Wippbewegung im Stand
|
||||
goalie.y = goalie.baseY + Math.sin(frameCount * 0.2) * 2;
|
||||
goalie.angle = 0; // Aufrecht stehen
|
||||
|
||||
if (goalie.x > goal.bottomRightX - 40 || goalie.x < goal.bottomLeftX + 40) {
|
||||
goalie.direction *= -1;
|
||||
}
|
||||
}
|
||||
else if (isBallFlying && goalie.isDiving) {
|
||||
// HECHT-LOGIK
|
||||
|
||||
// X Bewegung
|
||||
const dx = goalie.diveTargetX - goalie.x;
|
||||
goalie.x += dx * 0.08; // Sanfteres Gleiten
|
||||
|
||||
// Y Bewegung (Sprung)
|
||||
const dy = goalie.diveTargetY - goalie.y;
|
||||
goalie.y += dy * 0.08;
|
||||
|
||||
// Rotation berechnen (Hecht)
|
||||
// Wenn er weit zur Seite muss (>50px vom Zentrum), dreht er sich
|
||||
const distFromCenter = goalie.diveTargetX - 400;
|
||||
let targetAngle = 0;
|
||||
|
||||
if (Math.abs(distFromCenter) > 40) {
|
||||
// 90 Grad (PI/2) nach rechts oder links
|
||||
// Wir nehmen etwas weniger als 90 Grad für realistischeren Sprung (ca 70 Grad)
|
||||
targetAngle = (distFromCenter > 0) ? 1.2 : -1.2;
|
||||
} else {
|
||||
// Bei zentralen Bällen nur leicht neigen oder aufrecht bleiben
|
||||
targetAngle = (distFromCenter > 0) ? 0.2 : -0.2;
|
||||
}
|
||||
|
||||
// Winkel interpolieren
|
||||
goalie.angle += (targetAngle - goalie.angle) * 0.1;
|
||||
}
|
||||
|
||||
// Ball Bewegung
|
||||
if (isBallFlying) {
|
||||
ball.z += 0.05;
|
||||
const progress = ball.z;
|
||||
|
||||
ball.x = startBallX + (ball.targetX - startBallX) * progress;
|
||||
ball.y = startBallY + (ball.targetY - startBallY) * progress;
|
||||
|
||||
ball.radius = 15 - (7 * progress);
|
||||
|
||||
if (progress >= 1) {
|
||||
isBallFlying = false;
|
||||
checkResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkResult() {
|
||||
// Verbesserte Kollisionserkennung für den neuen Körper
|
||||
// Wir prüfen die Distanz zum Zentrum des Torsos
|
||||
// Da er sich dreht, ist eine einfache Box schwer, wir nehmen eine etwas größere Kreis-Distanz an
|
||||
|
||||
// Torso Position anpassen für Hitbox
|
||||
let hitX = goalie.x;
|
||||
let hitY = goalie.y - 40; // Körpermitte
|
||||
|
||||
const distX = Math.abs(ball.x - hitX);
|
||||
const distY = Math.abs(ball.y - hitY);
|
||||
|
||||
// Hitbox
|
||||
let caught = false;
|
||||
|
||||
// Körpertreffer
|
||||
if (distX < 45 && distY < 45) caught = true;
|
||||
|
||||
// Optional: Wenn er hechtet, deckt er mehr Breite aber weniger Höhe ab
|
||||
// Das simulieren wir einfachheitshalber über den distX Parameter oben, der schon relativ generisch ist.
|
||||
|
||||
if (caught) {
|
||||
showStatus("GEHALTEN!", "#ff4444");
|
||||
return;
|
||||
}
|
||||
|
||||
// Tor Check
|
||||
const inGoalX = ball.x > goal.topLeftX && ball.x < goal.topRightX;
|
||||
const inGoalY = ball.y > goal.topY && ball.y < goal.bottomY;
|
||||
|
||||
if (inGoalX && inGoalY) {
|
||||
score++;
|
||||
showStatus("TOOOOR!", "#44ff44");
|
||||
} else {
|
||||
showStatus("VORBEI!", "#aaaaaa");
|
||||
}
|
||||
|
||||
updateScore();
|
||||
}
|
||||
|
||||
function gameLoop() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
drawStands();
|
||||
|
||||
// Boden
|
||||
ctx.fillStyle = "#2E8B57";
|
||||
ctx.fillRect(0, 230, canvas.width, canvas.height - 230);
|
||||
|
||||
drawPitch();
|
||||
drawGoal();
|
||||
|
||||
// Torwart zeichnen (Reihenfolge wichtig für Tiefe)
|
||||
// Wenn Ball hinten ist (z nahe 1), ist Ball HINTER Torwart? Nein, Ball fliegt auf Tor zu.
|
||||
// Im echten Leben steht Torwart VOR der Linie.
|
||||
// Also: Ball (wenn weit weg) -> Torwart -> Ball (wenn nah)
|
||||
// Hier einfachheitshalber: Torwart vor Netz, Ball davor.
|
||||
|
||||
drawGoalie();
|
||||
drawBall();
|
||||
|
||||
update();
|
||||
requestAnimationFrame(gameLoop);
|
||||
}
|
||||
|
||||
// Input Handler
|
||||
function handleInput(x, y) {
|
||||
if (isBallFlying || messageEl.style.opacity == "1") return;
|
||||
|
||||
isBallFlying = true;
|
||||
attempts++;
|
||||
updateScore();
|
||||
|
||||
ball.targetX = x;
|
||||
ball.targetY = y;
|
||||
|
||||
// KI Entscheidung
|
||||
goalie.isDiving = true;
|
||||
|
||||
// Fehlerquote
|
||||
const errorMargin = (Math.random() * 200) - 100;
|
||||
|
||||
// Ziel für den Torwart setzen
|
||||
goalie.diveTargetX = ball.targetX + errorMargin;
|
||||
|
||||
// Er soll auch springen (Y-Achse)!
|
||||
// Ziel-Y ist Ballhöhe, aber begrenzt durch Boden
|
||||
// Wir addieren etwas Offset, da er "hoch" springen muss um an hohe Bälle zu kommen
|
||||
let jumpHeight = ball.targetY;
|
||||
if (jumpHeight > goalie.baseY) jumpHeight = goalie.baseY; // Nicht in den Boden graben
|
||||
|
||||
goalie.diveTargetY = jumpHeight + 40; // +40 weil seine Koordinaten bei den Füßen sind
|
||||
|
||||
// Begrenzen
|
||||
if (goalie.diveTargetX < goal.bottomLeftX - 50) goalie.diveTargetX = goal.bottomLeftX - 50;
|
||||
if (goalie.diveTargetX > goal.bottomRightX + 50) goalie.diveTargetX = goal.bottomRightX + 50;
|
||||
}
|
||||
|
||||
canvas.addEventListener('mousedown', (e) => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const clickX = (e.clientX - rect.left) * scaleX;
|
||||
const clickY = (e.clientY - rect.top) * scaleY;
|
||||
handleInput(clickX, clickY);
|
||||
});
|
||||
|
||||
canvas.addEventListener('touchstart', (e) => {
|
||||
e.preventDefault();
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const scaleX = canvas.width / rect.width;
|
||||
const scaleY = canvas.height / rect.height;
|
||||
const touch = e.touches[0];
|
||||
const clickX = (touch.clientX - rect.left) * scaleX;
|
||||
const clickY = (touch.clientY - rect.top) * scaleY;
|
||||
handleInput(clickX, clickY);
|
||||
}, {passive: false});
|
||||
|
||||
initSpectators();
|
||||
updateScore();
|
||||
gameLoop();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user