
Here's what you'll be learning how to create in this course, and a bit about who you'll be learning it from. Pleasure to meet you!
This lecture is just a bit of set up and context for what you'll be learning in this review section, including one last reminder about the previous free Udemy course that this one builds upon.
Making an HTML file is a simple matter of creating a plain text file and saving it with the right extension. As a reminder, since you're programming you should make sure that your OS is set to show all file extensions, rather than hiding known types, since otherwise it will be tough to tell txt from html files, png images from jpg images, and so on.
Whereas other browsers may let you get away with a very basic (if technically incorrect) HTML wrapper for your game files, Firefox is picky and will complain with a warning in the console unless you provide it with a proper header. Since many people use Firefox and this doesn't take very long to do let's take a moment to form it properly.
You'll see how to open your console, print code to it from your program code, and also see an example of spotting an error message in it due to an issue in the programming.
To display graphics for your games we'll need a canvas to paint on. In this step you'll create that canvas with HTML and set up a way to use it from the JavaScript.
During this lecture you'll write code to display colored rectangles and circles.
For action to happen over time, rather than being computed all when the program stats, an interval time has to be set up. That's what you'll accomplish here. To show that it's working you'll also use a variable to position the ball, changing it on each update call to produce a steady horizontal motion.
Now you'll get the ball bouncing off the left and right sides, as well as moving vertically and bouncing off the top and bottom of the canvas.
There's a semi-mysterious giant rectangle being drawn every time the game updates. It deserves some explanation (maybe even a bit of demonstration?) for why it's there.
Time to separate the movement and rendering lines of code. Although this isn't technically necessary it can help reduce strange complications that would later arise due to positions being updated and drawn in varying order.
Drawing colored rectangles and circles currently takes multiple lines of code, but that just seems silly. Here we'll wrap those up into functions so that the code which needs to use them can be kept higher level.
It's time to hook up mouse movement to guide a player paddle. This will serve as the player's avatar within the game space.
If the ball continued bouncing off all four walls there wouldn't be much challenge to the game. Here we'll create the impression that the ball can fall off the bottom by resetting its position when it reaches that edge.
Whereas the game shown in the previous course kept the paddle up against the edge to keep the collision code simple, for this newer game it's time to get a little fancier and give it some space from the edge. When the ball and paddle overlap (well, technically when the ball's center passes within the paddle's boundaries) the ball will bounce.
Ball control is essential for these kinds of games. Here you'll review the most popular method used to steer the ball and then implement it.
Just a brief explanation to set up what you'll hear about in this section, which includes a little extra text display code to help you inspect positions and measure the size of each brick.
Here you'll set up a function to display text within the canvas area, use it to show the mouse position near the cursor, and verify that its output is consistent with what you know about your canvas.
At this point constants will be created to describe the bricks in a consistent way. You can use these values here to draw a lone blue brick in the corner of the canvas.
Now you'll compare a couple of different ways to leave visible gaps between the bricks. I'll make my case for subtracting the margin from the rectangles, rather than adding a margin between them as spacing.
Each brick will need its state kept separate in the code. You'll set up a way to toggle specific bricks on or off.
First this will be done inelegantly by giving each brick its own separate variable. Soon this will be replaced with an array to make the code more straightforward and easier to adjust.
By numbering your bricks starting from 0, rather than from 1, you'll establish a clearer pattern with how each bricks is positioned as a multiple of brick dimensions. This will also help clarify the relationship to the array usage coming up soon.
Instead of having a distinct variable to go with each brick you can keep all of the states as a group in an array. The advantages to doing this will become more clear as we begin using it to reduce redundancy in the code.
You can reduce code duplication in several places, even already, by using a for-loop in each place to deal with the bricks in sequence rather than doing so independently.
Now that bricks are handled systematically it's important to know how to check and modify specific bricks from the set. As evidence that the changes are reflected in the layout you'll also set up bricks to randomly be missing, each with its own 50-50 even change while being set up.
Although comments don't affect what code does, good use of comments can save you real time as the programmer. In many cases comments are about description or explanation. The usage I show here has more to do with maintaining structure by giving meaning to otherwise identical closing braces } which appear close together.
You're about to change the type of information being displayed as text by the mouse, but first I want to clarify what it's going to be.
Instead of showing a pixel x,y value by the mouse you can do a bit of division by brick dimension constants to find any given point's corresponding column and row.
By drawing several rows of bricks stacked on top of one another you'll create the initial version of the tile grid.
Accurate terminology in code can help keep ideas and reasoning clear. Column is a better label for the axis of bricks perpendicular to the row, rather than the term "count" which fit for a single line of bricks.
The array of brick states only has a different value per column, but you need a way to set and check the condition of any brick also if it's in a different row. The trick is to look at the array left-to-right, top-to-bottom, like words read on a page.
Note that whereas many programming languages support 2D arrays or ways of creating nested arrays - meaning arrays with more than one index, for example column and row - I've opted to show for these projects how to do it with a linear array. This is a slightly more universal approach, only involves one extra step, avoids common confusions with the order of access coordinates, and is helpful as a pattern to practice since it relates to other situations sometimes encountered in game and graphics programming.
Thanks to the orderly way in which the bricks are now handled in relation to their constants, you can change the number and size of bricks then have the game still work more or less the same.
In order to have the ball interact with bricks you'll need a way to compute which array index corresponds to a given column and row for a brick position on the canvas. The math for this is simple, but why it actually works is sometimes tricky for people to visualize when new to it.
Since you can find the pixel position of the mouse, get the column and row for a given pixel position, and the array index which corresponds to any column and row pair, you have all the information needed to vanish bricks when they're under your mouse cursor.
This takes the functionality which was used for the mouse pointer and does it for the ball's position instead. Even though it's technically not much different at a code level the effect makes the ball feel a lot more real within the virtual space than it did while passing through bricks without consequence.
A side effect of how the linear array positions match up to row and column pairs is that if the ball collides with the left or right edge of the screen it may interact improperly with a brick which is nearly across from it on the screen. This helps highlight why boundary checking needs to be kept separate for column and row, i.e. it's not sufficient to just determine whether the computed array index is valid in the array's bounds (from 0 to the last element).
The method here is very basic, but easy to do, and it already gives the ball a more solid feel within the game.
The ball has been starting within the brick wall, although that didn't really matter up until the ball's path started being affected by the bricks. I'll show here how to start the ball in the screen's center, below the grid... but I'll also explain why putting off this fix until a bit later will actually make it easier to work on improving the ball-brick collision.
A moment to highlight why the ball-brick collision code is about to get a little more complicated.
Even though the desired result is intuitive, the ways to accomplish it require a logical way of reasoning about different cases in terms of a ball's position or velocity relative to a brick when the two collide.
The movement function is taken over by various blocks of code handling different situations for the ball. To keep that code organized so that it's easier to read and work on let's break that code up into smaller functions with descriptive labels.
There are various approaches to determining which side of a brick is hit by a ball. I'll explain a method here which is appealing because in addition to using values easily available to the computer it also makes conceptual sense in the most common cases.
The previous step was just talking about it so that it'd make sense while doing it. In this step you're actually getting the side collision test working.
By teleporting the ball to the mouse whenever the mouse moves, in addition to giving the ball a consistent heading at that time, you can more easily reproduce collision cases to determine which if any still produce unwanted results.
This complication emerges as an effect of earlier corrective code. The simple workaround is shown here.
In this section you'll wrap up the remaining core gameplay for this project. After this one you'll move directly on to reworking it into the Racing practice game.
In order to tell whether the player has run out of bricks to hit you'll want a way to keep an accurate count of how many are left.
This seems like such a simple thing, but it impacts gameplay by giving the player an implicit mini-goal and establishes a range for what it means to do it well (minimal exit space upon entry) as opposed to poorly (huge or numerous openings into the top before getting the ball up there). The technical aspect of achieving this isn't complicated, but can be a tad awkward.
Rather than present the winning player with an empty space forever until they willingly let the ball pass it makes more sense to refresh bricks once they all get cleared. To avoid the ball becoming stuck inside the wall wait for the ball to return to the paddle before refilling the grid. We can also now let the ball start in the middle of the play area.
There needs to be some penalty for missing the ball. There's no concept in the game at this time about either limited lives or a scoring value, so for now missing the ball will cause the player's progress to be reset.
The bottom-most bricks in the grid cause a strange ball behavior. You'll learn why that is, and what we can do it about it.
Playing your game is an essential part of making it, in part since while doing so odds are good that you'll spot more things about it that ought to be changed.
If the ball hits the paddle near a wall sometimes the ball gets wedged partially off-screen. I'll explain what causes this to happen, then we'll fix it.
Another quick test run just to double check that the code is behaving as expected.
Very brief introduction to what you can expect in this next section.
Since the whole project will be reworked into the Racing game now is your chance to make a copy of it before proceeding.
The ball will become the car and the bricks will become the track, but there's no need for something in this Racing game which has much in common with the mouse paddle.
Since the bricks are becoming track sections the code naming should reflect that, or things will get needlessly confusing soon. Remember to use case-sensitive with different variations (lowercase, camelCase, and UPPER_CASE) any time you're doing find and replace in code.
By adjusting the tile dimensions as well as the row and column constants you can stretch the grid to cover the entire canvas. This will be useful for using the grid information for map layouts.
Rather than having for() loops set all track wall states on, off, or randomly, it makes sense to instead design ahead of time a layout which makes sense for your game. To do this we'll move away from using true/false boolean to indicate state and instead use 0's and 1's, which has the added advantage of being able to depict more information for a given tile.
Here are some simple layouts showing the connection between the array data and the game's environment.
I'll show you the layout I've designed for use. If you want to use this one but don't want to bother typing it in you can download the attached resources and grab it from my file.
It's often helpful to be able to place characters, items, or in this case cars (well, ball for now) to begin in specific places within the tile data. Here's a way to make it do just that.
Time to create a bit of art.
Here's how I create the car(s) graphics.
These lighting effects aren't done in code, they're actually just a simple part of the image file.
Having the image made only gets you partway there. Time to load it from code and get it displaying in-game.
This section will replace the ball that bounces with a car that drives.
First fix up labels in the code. In addition to keeping the code clear to reason about this also makes it easier for us to talk about.
Keeping an angle variable isn't very tricky, however writing an image function to spin an image around its center reveals this task to be a bit more involved than it seems.
This is a common usage of trigonometry calls for game programming, however it often trips up people new to doing it. The next two lectures will provide animations, illustrations, and explanation of what it's doing and why.
No talk here of identifies, proofs, or equations, instead it's all about a very practical and direct application of these numbers for programming motion toward an arbitrary direction.
I provide here a brief connection for how the trigonometry numbers are used in math outside of games as another way to explain visualizing what result they achieve and why.
Just tidying up, since there were aspects of the ball's bounce motion that don't relate to the car.
In addition to setting up keys to let the player steer the car, this section also addresses other related aspects of motion, such as friction. and what to do when hitting a wall.
Like mouse events these are set up in window.onload. Unlike mouse events, which listen for the canvas, keys listen for a relevant message from the document.
Of course these could be looked up, but they're easy to get locally, and it's good to know where these things come from.
The keys at this phase only work by tapping, but they should affect the car's angle and speed.
By storing hold state for each gameplay key you can treat keys in code more like buttons. Importantly, this means you can support much smoother drive, braking, and steering than could be done by typing.
Dampen the car's speed upon collision, rather than bouncing off walls with 100% of the speed brought into it. The way recorded in the video has a minor bug. As explained in the callout I added atop the video, this issue can be overcome by first subtracting the most recent movement prior to changing its speed.
When you take your foot off the gas a car should lose speed.
The map is laid out in a way that it'll work best for the cars to initially point north/up.
By taking this new course you'll program several classic game types that all incorporate 2D tile-based worlds. You'll code in JavaScript for HTML5 Canvas, so a text editor and ordinary web browser are all you need (an art program can be handy for a few sections but is not required). I've attached my code for each step so you'll never be stuck. At the end you'll learn even more ways to apply what you've learned. Also by completing the course you'll get a PDF of my complete 500-page textbook on game development: Hands-On Intro to Game Programming. The book contains over 100 exercises, a couple of more game types, and additional material with more detail about the projects you created in this course.
(HTML5 Logo in the course image is by W3C, licensed under Creative Commons Attribution 3.0 Unported. Background pattern for transition cards CC BY-SA 3.0 Subtle Patterns © Atle Mo. drawn by Paul Phönixweiß.)