Hello, I have just finished the ‘Adding features to our balls demo’, could anyone make some comments of my code? And I also put my answers to the bonus point questions in the code.
/*
my answers
* For a bonus point, let us know which keys the specified keycodes map to.
> 65 maps to A, 68 maps to D, 87 maps to W, and 83 maps to S. (ASCII code)
* For another bonus point, can you tell us why we've had to set let _this = this; in the position it is in? It is something to do with function scope.
> The `this` in `onkeydown` points to a different object than the `this` in `setControls`. (But I do not know which object the `this` in `onkeydown` points to, could you help with me?)
*/
// setup canvas
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let width = canvas.width = window.innerWidth;
let height = canvas.height = window.innerHeight;
let paragraph = document.querySelector('p');
let ballCount = 0;
// function to populate paragraph
function countBall() {
paragraph.textContent = 'Ball count: ' + ballCount;
}
// function to generate random number
function random(min, max) {
let num = Math.floor(Math.random() * (max - min)) + min;
return num;
}
// define Shape constructor
function Shape(x, y, velX, velY, exists) {
this.x = x;
this.y = y;
this.velX = velX;
this.velY = velY;
this.exists = exists;
}
// define EvilCircle constructor
function EvilCircle(x, y, exists) {
Shape.call(this, x, y, 20, 20, exists);
this.color = 'white';
this.size = 10;
}
EvilCircle.prototype = Object.create(Shape.prototype);
Object.defineProperty(EvilCircle.prototype, 'constructor', {
value: EvilCircle,
enumerable: false,
writable: false
});
// define evilcircle draw method
EvilCircle.prototype.draw = function () {
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.stroke();
}
// define evilcircle checkBounds method
EvilCircle.prototype.checkBounds = function() {
if ((this.x + this.size) >= width) {
this.x -= this.size;
}
if ((this.x - this.size) <= 0) {
this.x += this.size;
}
if ((this.y + this.size) >= height) {
this.y -= this.size;
}
if ((this.y - this.size) <= 0) {
this.y += this.size;
}
};
// define evilcircle setControls method
EvilCircle.prototype.setControls = function() {
let _this = this;
window.onkeydown = function(e) {
if(e.keyCode === 65)
_this.x -= _this.velX;
else if(e.keyCode === 68)
_this.x += _this.velX;
else if(e.keyCode === 87)
_this.y -= _this.velY;
else if(e.keyCode === 83)
_this.y += _this.velY;
}
};
// define evilcircle collisionDetect method
EvilCircle.prototype.collisionDetect = function() {
for (let j = 0; j < balls.length; j++) {
if (balls[j].exists) {
let dx = this.x - balls[j].x;
let dy = this.y - balls[j].y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + balls[j].size)
balls[j].exists = false;
}
}
};
// define Ball constructor
function Ball(x, y, velX, velY, exists, color, size) {
Shape.call(this, x, y, velX, velY, exists);
this.color = color;
this.size = size;
}
Ball.prototype = Object.create(Shape.prototype);
Object.defineProperty(Ball.prototype, 'constructor', {
value: Ball,
enumerable: false,
writable: true
});
// define ball draw method
Ball.prototype.draw = function () {
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fill();
};
// define ball update method
Ball.prototype.update = function () {
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;
};
// define ball collision detection
Ball.prototype.collisionDetect = function () {
for (let j = 0; j < balls.length; j++) {
if (!(this === balls[j]) && balls[j].exists) {
let dx = this.x - balls[j].x;
let dy = this.y - balls[j].y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.size + balls[j].size) {
balls[j].color = this.color = 'rgb(' + random(0, 255) + ',' + random(0, 255) + ',' + random(0, 255) + ')';
}
}
}
};
// define array to store balls and populate it
let balls = [];
while (balls.length < 25) {
let size = random(10, 20);
let ball = new Ball(
// ball position always drawn at least one ball width
// away from the adge of the canvas, to avoid drawing errors
random(0 + size, width - size),
random(0 + size, height - size),
random(-7, 7),
random(-7, 7),
true,
'rgb(' + random(0, 255) + ',' + random(0, 255) + ',' + random(0, 255) + ')',
size
);
balls.push(ball);
}
// create evil circle
let evilcircle = new EvilCircle(
random(20, width - 20),
random(20, height - 20),
true,
);
evilcircle.setControls();
// define loop that keeps drawing the scene constantly
function loop() {
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.fillRect(0, 0, width, height);
evilcircle.draw();
evilcircle.checkBounds();
evilcircle.collisionDetect();
ballCount = 0;
for (let i = 0; i < balls.length; i++) {
if(balls[i].exists) {
balls[i].draw();
balls[i].update();
balls[i].collisionDetect();
++ballCount;
}
}
countBall();
requestAnimationFrame(loop);
}
loop();