CS 106 Winter 2018

Assignment 02: Input and Output


Question 1 Platformer

A platformer is a style of video game in which you control a character as you navigate a course of different-height platforms and other obstacles. The iconic game in this genre is the side scroller Super Mario Bros.

Many platformers (and indeed, many video games in general) are tile-based—behind the scenes, the graphical elements are broken down into a small set of tiles, which are reassembled like puzzle pieces into different configurations to define the game's levels.

In this question, you'll write a small sketch that can manipulate tiles to draw one screen from a hypothetical platformer game. Proceed as follows:

  1. Create a new sketch called Platformer inside of an A02/ directory. Write a setup() function that sets the size of the sketch window to 640×576. (This is exactly four times the resolution of the original Nintendo Gameboy.)

  2. Usually, all the graphical assets in a tile-based game travel together in a single tileset image. Download a copy of the image Tiles.png and add it to the sketch. This tileset incorporates four variations of a background image and simple platformer tiles, inspired by Gameboy-era art. The original is in the public domain, courtesy of GrafxKid on OpenGameArt.

  3. At the top of the sketch declare a global variable of type PImage, and in setup() load Tiles.png and store it in that image variable.

  4. Create a draw() function that draws the spring-themed background image in the top-left quadrant of Tiles.png, scaled so that it covers the entire sketch window. Use the built-in function copy() to extract and draw the relevant rectangle from Tiles.png. Note that the top-left pixel of the background has coordinates (16,16), and the background has the same dimensions as the Gameboy screen: 160×144. Also, because copy() will fill the sketch window, you don't need to use background().

  5. You may notice that the background image looks a bit blurry. That's because Processing is trying to smooth it out as it scales it up by a factor of four on the way from the source image to the sketch window. Fortunately, there's a solution. Add a call to noSmooth() (with no arguments) at the bottom of setup(). That will make the sketch look more like classic, chunky pixel art.

  6. We're going to want to start drawing individual tiles to the sketch window. In order to do that, let's make sure we agree on how the tileset and sketch window are divided into grids.

    The sketch window is divided into a 10×9 grid of cells, each one of size 64×64 pixels. Here's a copy of the background image, with superimposed x and y grid coordinates. Click to download a PDF version.



    The platformer tiles all live in Tiles.png, in a little cluster to the right of the background image. With the exception of the big tree in the upper-left, they're all 16×16 pixel tiles. We'll simply number them consecutively from top-left to bottom-right:



    Drawing one piece of a level will consist of copying a 16×16 tile out of the tileset, scaling it by a factor of four, and pasting it into a 64×64 grid cell in the sketch window.

    (You don't have to modify your sketch to complete this step, you just need to agree with the grids described above. So maybe nod your head in agreement.)

  7. Write a helper function that takes three integers as parameters: a tile number from 0 to 17, according to the numbering in the second image above, and the x and y coordinates of a cell number in the sketch window, from (0,0) to (9,8) as in the first image above. The function should use copy() to transfer the numbered game tile into the corresponding cell in the sketch window. It does not need to return a value. For example, copying Tile 14 into Cell (6,7) would yield this result:



    Test your helper function to convince yourself that it works. Try adding a couple of different calls to your function at the end of draw(), verifying that you get the expected tile in the expected cell.

    You'll need a way to turn a tile number into something more like x and y values for the horizontal and vertical position of that tile in the tileset. There's an elegant trick here: if the tile number is num, you can use the values of num % 3 and num / 3 as the starting point for getting the correct x and y positions to copy pixels from in the tileset.

  8. Next, you're going to add functionality to draw a level from a description stored in an external text file. Add a global variable that holds an array of strings. In setup(), initialize that variable by reading a text file called level.txt from your data/ folder. Download this sample level.txt file for testing purposes. The file contains a sequence of lines, each one made up of three integers. The first two integers are the x and y coordinates of a cell in the sketch window. The third integer is the number of the game tile to put in that cell.

  9. Tie the previous step into the rest of the game. In your draw() function, loop over all the lines in the text file (using the array from the previous step). For each line, extract the three numbers and use your helper function to draw the tile in the right spot in the sketch window.

    We make one important exception to this process. When drawing the big tree, it's annoying and error-prone to have to specify all four tiles in a 2×2 group every time. Instead, we use Tile 3 by itself to drop in the whole tree. When you see a request for Tile 3, draw the whole tree, with its bottom-left tile in the location associated with Tile 3. (You can assume that the input file contains no requests for Tiles 0, 1, or 4, or the blank Tile 16 for that matter.)

    For example, when rendering the test level.txt file provided above, the contents of your sketch window should look exactly like this:



  10. Replace the provided level.txt with a new, original level of your own design. The level should use at least 18 placed tiles, and at least 7 distinct tile types. It should make logical use of the tiles: ideally, adjacent tiles should "fit together" as they were designed to do, creating a plausible game screen. We might award a modest bonus to especially well designed levels (it's hard to say for sure, because this is a narrowly defined artistic domain).

  11. The preceding steps are collectively worth about 90% of the marks for this question. For the final 10%, you must implement one additional feature: when the user presses the space key, rotate between the seasons. Tiles.png contains four distinct background images and four copies of the tiles; the space key should cycle through them repeatedly. If you've set up your code correctly, this feature does not require significant changes. You can introduce a couple of global variables that act as a global offset for the origin in all your copy() operations. Then you just need a keyPressed() function that modifies those global variables.

There are many, many opportunities here to extend the core requirements and create a submission that's more artistic or more game-like. We invite you to explore creative extensions. You can submit a sketch with as many additional features you like, as long as the core requirements above are satisfied, and will award bonus marks for creative and attractive extensions. Include a comment in your code indicating what you added.

Submission

When you are ready to submit, please follow these steps.

  1. Please ensure that any sketches you submit compile and run. It's better to submit a sketch that runs smoothly but implements fewer required features than one that has broken code for all features. If you get partway into a feature but can't make it work, comment it out so that the sketch works correctly without it.

  2. If necessary, review the Code Style Guide and use Processing's built-in auto format tool. You do not need to use the precise coding style outlined in the guide, but whatever style you use, your code must be clear, concise, consistent, and commented.

  3. If necessary, review the How To Submit document for a reminder on how to submit to LEARN.

  4. Make sure to include a comment at the top of all source files containing your name and student ID number.

  5. Create a zip file called A02.zip containing the entire A02 with its subfolder Platformer.

  6. Upload A02.zip to LEARN. Remember that you can (and should!) submit as many times as you like. That way, if there's a catastrophe, you and the course staff will still have access to a recent version of your code.

  7. If LEARN isn't working, and only if LEARN isn't working, please email your ZIP file to the course account (see the course home page for the address). In this case, you must mail your ZIP file before the deadline. Please use this only for emergencies, not "just in case". Submissions received after the deadline may receive feedback, but their marks will not count.