Compare commits

...

5 commits

Author SHA1 Message Date
c34177b216 Format
All checks were successful
Check & deploy / deploy (push) Successful in 7s
2026-06-11 20:38:44 +01:00
2cb12f93e8 Fix typos 2026-06-11 20:37:03 +01:00
3640242506 Small clean up 2026-06-11 20:32:56 +01:00
3b807a968c Fix iOS cursor offset 2026-06-11 20:32:14 +01:00
3f73f30f2d De-cringify text 2026-06-11 20:31:52 +01:00
5 changed files with 59 additions and 26 deletions

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="hu"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@ -9,7 +9,7 @@
<meta name="theme-color" content="#A5402D" /> <meta name="theme-color" content="#A5402D" />
<link <link
href="https://fonts.googleapis.com/css?family=Raleway" href="https://fonts.googleapis.com/css?family=Raleway&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<link rel="stylesheet" href="css/main.css" /> <link rel="stylesheet" href="css/main.css" />
@ -21,9 +21,10 @@
<div id="startText"> <div id="startText">
<h1>Avoid</h1> <h1>Avoid</h1>
<p> <p>
the red balls by moving your cursor (or finger) on the screen. Try A relentless swarm is closing in on you. Guide your light across
eliminating the enemy balls by getting them to collide with each the screen with your cursor (or finger), stay out of reach, and
other. Good luck! outsmart your pursuers by luring them into crashing into one
another. How long can you survive?
</p> </p>
</div> </div>
<div id="endText"> <div id="endText">

View file

@ -38,6 +38,14 @@ class CanvasHandler {
return unit * Math.sqrt(this.width * this.height); return unit * Math.sqrt(this.width * this.height);
} }
clientToCanvas(clientX, clientY) {
const rect = this.canvas.getBoundingClientRect();
return [
((clientX - rect.left) / rect.width) * this.width,
((clientY - rect.top) / rect.height) * this.height,
];
}
get width() { get width() {
return this.canvas.width / window.devicePixelRatio; return this.canvas.width / window.devicePixelRatio;
} }

View file

@ -1,14 +1,18 @@
class InputHandler { class InputHandler {
constructor(idOfWatchedArea) { constructor(canvasHandler) {
const elem = document.getElementById(idOfWatchedArea); const elem = canvasHandler.canvas;
const handleMouse = ({ offsetX, offsetY }) => const handleMouse = ({ clientX, clientY }) =>
this.setPointerPosition([offsetX, offsetY]); this.setPointerPosition(canvasHandler.clientToCanvas(clientX, clientY));
const handleTouch = ({ touches }) =>
this.setPointerPosition([touches[0].clientX, touches[0].clientY]); const handleTouch = (event) => {
event.preventDefault();
const { clientX, clientY } = event.touches[0];
this.setPointerPosition(canvasHandler.clientToCanvas(clientX, clientY));
};
elem.addEventListener("mousemove", handleMouse); elem.addEventListener("mousemove", handleMouse);
elem.addEventListener("touchmove", handleTouch); elem.addEventListener("touchmove", handleTouch, { passive: false });
this.pointerPosition = [0, 0]; this.pointerPosition = [0, 0];
} }

View file

@ -1,7 +1,8 @@
const enemySpawnFrequency = 4.5; const enemySpawnFrequencyPerSecond = 4.5;
const maxDeltaTimeInMs = 50;
const canvasHandler = new CanvasHandler("canvas"); const canvasHandler = new CanvasHandler("canvas");
const inputHandler = new InputHandler("canvas"); const inputHandler = new InputHandler(canvasHandler);
let game; let game;
function newGame() { function newGame() {
@ -27,11 +28,16 @@ class Circle {
this.position = add(this.position, limit(deltaPosition, maxDistance)); this.position = add(this.position, limit(deltaPosition, maxDistance));
} }
} }
draw(canvasHandler) { draw(canvasHandler) {
canvasHandler.drawCircle(this.position, this.size, this.color); canvasHandler.drawCircle(this.position, this.size, this.color);
} }
isTouching(other) { isTouching(other) {
return magnitude(subtract(this.position, other.position)) < 2 * this.size; return (
magnitude(subtract(this.position, other.position)) <
this.size + other.size
);
} }
} }
@ -46,8 +52,8 @@ class Game {
); );
this.circleHolder = [this.player]; this.circleHolder = [this.player];
this.spawnInterval = 1000 / enemySpawnFrequency; this.spawnInterval = 1000 / enemySpawnFrequencyPerSecond;
this.previousSpawn = this.previousTimestamp = 0; this.previousSpawn = this.previousTime = 0;
this.score = 0; this.score = 0;
this.menuBox = document.getElementById("menuBox").style; this.menuBox = document.getElementById("menuBox").style;
@ -55,16 +61,19 @@ class Game {
"none"; "none";
this.isActive = true; this.isActive = true;
window.requestAnimationFrame(this.nextFrame.bind(this)); this.boundNextFrame = this.nextFrame.bind(this);
window.requestAnimationFrame(this.boundNextFrame);
} }
nextFrame(timestamp) { nextFrame(timestamp) {
if (this.isActive) { if (this.isActive) {
canvasHandler.fillCanvas("rgba(165, 64, 45, 0.1)"); canvasHandler.fillCanvas("rgba(165, 64, 45, 0.1)");
this.spawnEnemyIfNeeded(timestamp); this.spawnEnemyIfNeeded(timestamp);
this.moveCircles(timestamp); this.moveCircles(timestamp);
window.requestAnimationFrame(this.nextFrame.bind(this)); window.requestAnimationFrame(this.boundNextFrame);
} }
} }
spawnEnemyIfNeeded(timestamp) { spawnEnemyIfNeeded(timestamp) {
if (timestamp - this.previousSpawn > this.spawnInterval) { if (timestamp - this.previousSpawn > this.spawnInterval) {
this.circleHolder.push( this.circleHolder.push(
@ -84,31 +93,42 @@ class Game {
this.score++; this.score++;
} }
} }
moveCircles(timestamp) { moveCircles(timestamp) {
const deltaTime = this.getDeltaTime(timestamp); const deltaTime = this.getDeltaTime(timestamp);
const collided = new Set();
for (let i = 0; i < this.circleHolder.length; i++) { for (let i = 0; i < this.circleHolder.length; i++) {
this.circleHolder[i].move(deltaTime); this.circleHolder[i].move(deltaTime);
this.circleHolder[i].draw(canvasHandler); this.circleHolder[i].draw(canvasHandler);
for (let j = i + 1; j < this.circleHolder.length; j++) { for (let j = i + 1; j < this.circleHolder.length; j++) {
if (this.circleHolder[i].isTouching(this.circleHolder[j])) { if (this.circleHolder[i].isTouching(this.circleHolder[j])) {
if (this.circleHolder[i] == this.player) this.showEndScreen(); if (this.circleHolder[i] === this.player) {
else { this.showEndScreen();
this.circleHolder.splice(i, 1); return;
this.circleHolder.splice(j - 1, 1); }
collided.add(i).add(j);
} }
} }
} }
if (collided.size > 0) {
this.circleHolder = this.circleHolder.filter(
(_, index) => !collided.has(index)
);
} }
} }
getDeltaTime(timestamp) { getDeltaTime(timestamp) {
const tempPreviousTime = this.previousTime; const tempPreviousTime = this.previousTime;
this.previousTime = timestamp; this.previousTime = timestamp;
return timestamp - tempPreviousTime; return Math.min(timestamp - tempPreviousTime, maxDeltaTimeInMs);
} }
showEndScreen() { showEndScreen() {
document.getElementById("endText").style.display = "block"; document.getElementById("endText").style.display = "block";
document.getElementById("endScore").innerHTML = `Your score: ${this.score}`; document.getElementById("endScore").innerHTML = `Your score: ${this.score}`;
document.getElementById("startButton").textContent = "Play again";
this.menuBox.display = "flex"; this.menuBox.display = "flex";
this.isActive = false; this.isActive = false;
} }