Week 4

This week, we added a collision function. Using the Rectangle class, we created another two rectangles to collide with. We also created an array to store all of out rectangles and made our code more extensible. If you are unfamiliar with arrays, this article would be a good read. Because of arrays, it now only requires one line of code to add a rectangle you can see and collide with. Let's take a look at the major changes we made.

The collision function

function checkCollision(rect1, rect2) {
	return  (rect1.x < rect2.x + rect2.width &&
			rect1.x + rect1.width > rect2.x &&
			rect1.y < rect2.y + rect2.height &&
			rect1.height + rect1.y > rect2.y);
}

This function takes two rectangles. It returns true if the two rectangles are colliding and false if they are not. Since our x and y variables refer to the left and top of the rectangle, the right and bottom are found by adding the width and height of the rectangle. I actually copied this code directly from MDN.

rectArray and main()

We now use an array called rectArray to store our variables. We now have three rectangles, and our main function looks much simpler.

var player = new Rectangle(10, 10, 20, 20);
var rect1 = new Rectangle(40, 40, 20, 10);
var rectArray = [];
rectArray.push(player);
rectArray.push(rect1);
rectArray.push(new Rectangle(50,60,10,10));

To add a rectangle, we can simply add it to this array and it will be rendered, updated, and collided with. In order to add this functionality, we modified main, and it now looks like this:

function main() {
	//clear screen
	ctx.fillStyle = "#FFF";
	ctx.fillRect(0,0,100,100);
	
	//update and render
	for (var i = 0; i < rectArray.length; i++) {
		rectArray[i].update();
		rectArray[i].render();
	}
}

We use the for loop to iterate through our array and update and render them. Here is the MDN article on for loops. It's important that we draw our objects after we clear the screen, otherwise it will simply write over what we've just drawn.

The Rectangle.update() function

update() {
	var prevx = this.x;
	var prevy = this.y;
	
	if (this.left == true) {
		this.x -= 1;
	}
	if (this.right == true) {
		this.x += 1;
	}
	for (var i = 0; i < rectArray.length; i++) {
		if (rectArray[i] != this && checkCollision(this, rectArray[i])) {
			this.x = prevx;
		}
	}
	
	if (this.up == true) {
		this.y -= 1;
	}
	if (this.down == true) {
		this.y += 1;
	}
	for (var i = 0; i < rectArray.length; i++) {
		if (rectArray[i] != this && checkCollision(this, rectArray[i])) {
			this.y = prevy;
		}
	}
}

Our Rectangle.update() function has grown quite a lot since last week because of the code we added to handle collision. We use variables prevx and prevy to store the positions before we move our rectangles, and if they collide on any axis, we move them back on the according axis. It's important that we keep our axes separate, because if we just checked and moved back after moving the rectangle on both axes, it would be impossible to "slide" along the surface of the recangle as we can here.

for (var i = 0; i < rectArray.length; i++) {
	if (rectArray[i] != this && checkCollision(this, rectArray[i])) {
		this.x = prevx;
	}
}

This code is important to understand. We iterate through every rectangle and test for collision with it. If it has collided, we move the rectangle back to where it was last frame on the x axis. Before checking collision with any rectangle, though, we have to make sure that rectArray[i] doesn't reference the same rectangle we're moving. If we didn't include that first part on that if statement, we would always be "colliding" with ourselves, and unable to move.