An Extension Of Yourself

The quickest way to shoot yourself in the foot when you think about starting a new venture is by jumping into a topic that you don’t a) know, b) care about, c) have time to invest in, or d) all of…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Building Breakout! With HTML Canvas and Javascript

A couple of months ago I started the Front-end development program at Turing School of Software and Design. The program consists of four different modules, each increasing in difficulty. Two weeks ago, I started the second module, which focuses almost exclusively on Javascript. Our first project was to build an old-school Atari game using HTML Canvas and Javascript (I ended up using a sort of hodge-podge of ES5 and ES6).

We decided it was important to set up our files in a way that wouldn’t come back to haunt us later. So, first we set variables for the canvas and context, which we set to “2d” in our index.js file. Next, we created files for all of the pieces of our game. These, included a general Gamepiece.js file, a Block.js file, a Paddle.js file, a Ball.js file, as well as one that would run the all of the functionality of our game (Game.js). Next, we created an Event Listener that calls the game-loop from from the Game.js file on click. We then set out creating classes in all of the files and exporting them to other files in which we knew they would be required. The following example is code from our Gamepiece class

So, cool, after a few console.logs we had everything hooked up.

We now had to create our game-loop in our Game.js file. This turned out to be a little trickier than I had originally hoped it would be. We knew we had to call requestAnimationFrame(), in order for our canvas to update 60 times a second. We had to then pass the game-loop function into the requestAnimationFrame(this.gameLoop). This should work. Well, we thought this would work. It did not. With the code as we had it, The context of game-loop (the one being passed into requestAnimationFrame()) was lost after the one gameloop() call. Frustrating. Approximately one million console.logs later we realized that we needed to set game-loop as a property in our constructor and bind “this” so it would remember the context:

Once we were past this hurdle, we decided to draw our blocks. Now, we knew we’d have to instantiate a bunch of new blocks. The question was how. We could hard-code their values in our constructor, but if we wanted to dynamically update them later (say for higher levels) this was a bad way to go about it. So we decided to use a nested for loop to set the x and y of each block as well as push them to an array of blocks which was set in our constructor:

We then called this function in our startGame() function so it would happen as soon as the user clicked on the canvas.

Once we had all of our blocks pushed up to the array, we used ForEach() to draw all of the blocks in said array:

It should be noted that the draw() function was created in the block class.

Once we had our blocks, we decided to move on to our paddle. We knew our paddle would never really need to be updated in the same way as our blocks. So, we instantiated a new paddle in our game constructor, and created a simple draw function. We then called that function in both our startGame() function as well as our Gameloop() because it has to redraw itself every time it moves.

With the paddle drawn it was time to make it move. This was done by creating a simple switch statement in the Paddle.js file. We used the left and right arrows for movement, so we called them in the switch statement as well (37, 39). We had to pass in the canvasWidth and the event so the paddle had a defined range of motion. Basically, the following code is saying if the paddle’s x position is less than the canvasWidth — the paddles width then move right, if the paddle’s x is greater than 0 then move left:

We then call this function, our paddle.draw(), and our paddle.erase() in an event listener in our index.js file. Our paddle is moving!!

We now have a moving paddle and an array of blocks. The only thing left to draw is the ball. Canvas only draws rectangles, sort of. There’s a way to draw circles, it just involves a little more math. So, we started by setting properties for its radius, its start angle and end angle. Next, we created a draw function in our ball class. To start, we had to call context.beginPath(), next we had to set the arc :

We then filled it with blue, and called:

We then had to instantiate a new ball in our game class, create a draw function and call it our gameloop(). We then had to create an erase function for the ball. This turned out to be just as annoying as drawing a circle in a language that only draws rectangles. Basically, we’re clearing a rectangle around the ball with the following code:

Moving on. (Sorry, my brain is so tired of calculating the radius of this ball.)

We now had to make our ball move. To do this, we created a move method in our ball class so the ball would move on its own. First, we set a dx and a dy to account for velocity in our constructor. Then we incremented the dx and dy in our move method:

We then chained this method in the drawBall() function in our Game.js file :

The ball moves! But there’s a problem. The ball will continue in the same direction forever. So, as soon as it goes past the width or height of the canvas it will never be seen again. We had to set up some parameters to change the balls direction if it reached the sides of the canvas. This was accomplished with a simple if statement that accounts for the balls x and y and its radius, as well as the left, right, and top of the canvas:

The ball is now bouncing all over the place, but its going through the blocks and the paddle which if you aren’t familiar with Breakout!, is not really the point. We decided to start with the paddle realizing that if we figured out how the collision detection worked for that, it would work basically the same way for the blocks. I’m just going to come out and say it. Canvas is finicky, and sometimes it makes me want to through my computer across the room. Anyway, the collision detection for the paddle was created in the paddle class (in the Paddle.js file) and chained onto the drawPaddle() function in the Game.js file. It required another if statement. It took into account the balls x, y, and radius. As well as the paddles x, y, width, and height. If the correct condition are met the balls dx and dy are reversed. Now, I’m told it’s possible to do all Object Oriented Programming without if statements, but I have yet to be converted. The if statement is as follows:

We then had to deal with the blocks. To be fair our initial assumption proved to mostly true. With a little finessing we were able to account for the the bottom and sides of the blocks. The only major difference is inside of the if statements that run if the conditions are met. These include erasing the block, splicing it out of the blocks array and incrementing score:

Our collision detection is working! I won’t bore you with the details surrounding the simple JavaScript required to dynamically update the score on the page (changing the innerhtml). But, I would like to end with the dynamic updating of levels and number of balls available to the user.

Updating levels in this game is quite simple. If the condition this.levelWon is true, the screen is populated with a “next level, click to play” message. Upon click, the paddles width is decremented and the balls speed is increased. In theory there is no end. As long as the user has balls remaining the level will update to infinity, although I can’t seem to get past level 4. The level up function is running in the gameloop() and is structured as follows:

Finally, we decided to give the user more than one life. It makes the game more fun and it would be almost impossible to beat two levels otherwise. We set the number of balls to three in our constructor:

and then created an if statement in our game class. If the balls y is lower than 600 (the height of our canvas) the user loses that ball and the property ball.inPlay was set to false. if ball.inPlay is false and this.balls is more than 0 then a new ball will be instantiated and drawn to the canvas. This function is called in our gameloop():

Now we have a fully functioning game! It was quite a process, but over the course of ten days I learned an incredible amount about class inheritance, scope, and the context of this. It’s also incredibly important to note that throughout this entire process we were (somewhat) adhering to test driven development. We used the mocha and chai libraries to build tests on all of our classes as we built them. If I could do it again I would also do this with the Game.js file, however it can be easy to forget about when one is learning so many concepts at once. I can say I have learned to, at minimum, appreciate TDD.

Add a comment

Related posts:

Still surprised when Godzilla ravages Tokyo

A group of shady billionaires put a racist fascist in charge of their data science firm. The firm went out and built some dumb personality quiz. (Pause here to think about every time you’ve ever let…

Dream

In the same night on 7/7/19 during another dream there was this part with this wide open land. And these great giant tall, tall horse like animals was running around, so free. One of these great…

BKEX Global will launch the function for Fund password

BKEX Global will launch the function for Fund password. BKEX Global will launch the function for fund password at 14:10 on July 9, 2019(Singapore time).