diff --git a/bouncing-balls-vim-motions/README.md b/bouncing-balls-vim-motions/README.md new file mode 100644 index 00000000..09f103a7 --- /dev/null +++ b/bouncing-balls-vim-motions/README.md @@ -0,0 +1,11 @@ +This is a simple game that is intended to help a user +become accoustomed to using vim motions to control a pointer. + + +The game consists of several balls generated at random that bounce across the screen. +When the pointer is collides with a ball the ball is destroyed. + +H- moves LEFT +J- UP +K- DOWN +L- RIGHT diff --git a/bouncing-balls-vim-motions/index.html b/bouncing-balls-vim-motions/index.html new file mode 100644 index 00000000..4071373f --- /dev/null +++ b/bouncing-balls-vim-motions/index.html @@ -0,0 +1,17 @@ + + + + + + Bouncing balls + + + + +

bouncing balls

+

+ + + + + \ No newline at end of file diff --git a/bouncing-balls-vim-motions/main.js b/bouncing-balls-vim-motions/main.js new file mode 100644 index 00000000..be363567 --- /dev/null +++ b/bouncing-balls-vim-motions/main.js @@ -0,0 +1,199 @@ +// setup canvas +const canvas = document.querySelector('canvas'); +const ctx = canvas.getContext('2d'); + +const width = canvas.width = window.innerWidth; +const height = canvas.height = window.innerHeight; + +const para = document.querySelector('p'); + +// function to generate random number +function random(min, max) { + const num = Math.floor(Math.random() * (max - min + 1)) + min; + return num; +} + +// function to generate random color +function randomRGB() { + return `rgb(${random(0, 255)},${random(0, 255)},${random(0, 255)})`; +} + +class Shape { + constructor(x, y, velX, velY, exists) { + this.x = x; + this.y = y; + this.exists = true; + this.velX = velX; + this.velY = velY; + } +} + +class EvilCircle extends Shape { + constructor(x, y) { + super(x, y, 20, 20); + this.color = "white"; + this.size = 30; + window.addEventListener("keydown", (e) => { + switch (e.key) { + case "h": + this.x -= this.velX; + break; + case "l": + this.x += this.velX; + break; + case "k": + this.y -= this.velY; + break; + case "j": + this.y += this.velY; + break; + } + }); + } + draw() { + ctx.beginPath(); + ctx.lineWidth = 3; + ctx.strokeStyle = this.color; + ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI); + ctx.stroke(); + } + checkBounds() { + if ((this.x + this.size) >= width) { + this.x = width - this.size; + } + + if ((this.x - this.size) <= 0) { + this.x = this.size; + } + + if ((this.y + this.size) >= height) { + this.y = height - this.size; + } + + if ((this.y - this.size) <= 0) { + this.y = this.size; + } + } + + collisionDetect() { + for (const ball of balls) { + if (ball.exists) { + const dx = this.x - ball.x; + const dy = this.y - ball.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < this.size + ball.size) { + ball.exists = false; + } + } + } + } + + +} + +class Ball extends Shape { + constructor(x, y, velX, velY, color, size) { + super(x, y, velX, velY); + this.color = color; + this.size = size; + } + + + draw() { + ctx.beginPath(); + ctx.fillStyle = this.color; + ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI); + ctx.fill(); + } + + update() { + if ((this.x + this.size) >= width) { + this.velX = -(this.velX); + } + + if ((this.x - this.size) <= 0) { + this.velX = -(this.velX); + } + + if ((this.y + this.size) >= height) { + this.velY = -(this.velY); + } + + if ((this.y - this.size) <= 0) { + this.velY = -(this.velY); + } + + this.x += this.velX; + this.y += this.velY; + } + + collisionDetect() { + for (const ball of balls) { + if (!(this === ball) && ball.exists) { + const dx = this.x - ball.x; + const dy = this.y - ball.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < this.size + ball.size) { + ball.color = this.color = randomRGB(); + this.velX = -(this.velX); + this.velY = -(this.velY); + } + } + } + } + +} + +const balls = []; + +while (balls.length < 25) { + const size = random(10, 20); + const ball = new Ball( + // ball position always drawn at least one ball width + // away from the edge of the canvas, to avoid drawing errors + random(0 + size, width - size), + random(0 + size, height - size), + random(-7, 7), + random(-7, 7), + randomRGB(), + size + ); + + balls.push(ball); +} + +const evilCircle = new EvilCircle(random(0, width), random(0, height)); + +let count = 0; + +function loop() { + ctx.fillStyle = "rgba(0, 0, 0, 0.25)"; + ctx.fillRect(0, 0, width, height); + + for (const ball of balls) { + if (ball.exists) { + ball.draw(); + ball.update(); + ball.collisionDetect(); + } + } + count = 0; + for (const ball of balls) { + if (ball.exists) { + count++; + } + } + para.textContent = `Ball count: ${count}`; + if (count > 0) { + evilCircle.draw(); + evilCircle.checkBounds(); + evilCircle.collisionDetect(); + } + + requestAnimationFrame(loop); + } + + +loop(); \ No newline at end of file diff --git a/bouncing-balls-vim-motions/style.css b/bouncing-balls-vim-motions/style.css new file mode 100644 index 00000000..130e9764 --- /dev/null +++ b/bouncing-balls-vim-motions/style.css @@ -0,0 +1,33 @@ +html, body { + margin: 0; +} + +html { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + height: 100%; +} + +body { + overflow: hidden; + height: inherit; +} + +h1 { + font-size: 2rem; + letter-spacing: -1px; + position: absolute; + margin: 0; + top: -4px; + right: 5px; + + color: transparent; + text-shadow: 0 0 4px white; +} + +p { + position: absolute; + margin: 0; + top: 35px; + right: 5px; + color: #aaa; +}