Background Game

A background and flying object start the brain thinking…

  • Can we add keys movements?
  • How could we make another object?
  • How does something like this example turn into a game like Flappy Bird?

Learning Goals

The goals of this challenge are much simpler than the brainstorming mentioned above.

  • Can we determine which files are required to run the game and move them into our personal student repository?
  • Can we identify the different sections of code and know the different comment requirements? (YAML, HTML, JavaScript)
  • Can we locate and comment on the key code blocks in the game? As you do this, you may ask yourself:
    • What do we see when running the game?
    • Can we relate each code block to the visual elements it creates?

Code Transfer and Prep

You will need to get files into your repo, run make, the goto permalink /background to see the runtime.

Frontmatter Images

These lines are YAML key-value pairs used to define the images for the main objects in the game: the center object and the moving background. You will need to get these and code into your repo.

sprite: /images/platformer/sprites/flying-ufo.png
background: /images/platformer/backgrounds/alien_planet1.jpg

Move code to personal Repo

Obtain code and assets for the game using the VSCode terminal.

Obtain Code

Transfer markdown file from GitHub OCS repo to VSCode workspace.

mkdir -p hacks
wget https://raw.githubusercontent.com/Open-Coding-Society/pages/refs/heads/main/hacks/background.md -O hacks/background.md

Obtain Assets

Transfer assets referenced in the YAML section of the code to your VSCode workspace.

mkdir -p images/platformer/sprites
wget https://raw.githubusercontent.com/Open-Coding-Society/pages/refs/heads/main/images/platformer/sprites/flying-ufo.png -O images/platformer/sprites/flying-ufo.png
mkdir -p images/platformer/backgrounds 
wget https://raw.githubusercontent.com/Open-Coding-Society/pages/refs/heads/main/images/platformer/backgrounds/alien_planet1.jpg -O images/platformer/backgrounds/alien_planet1.jpg

Illustration of Transfer

flowchart TD
    %% GitHub Source
    subgraph GitHub["GitHub"]
        subgraph GH_Backgrounds["images/platformer/backgrounds/"]
            GH_planet[alien_planet1.jpg]
        end
        subgraph GH_Sprites["images/platformer/sprites/"]
            GH_ufo[flying-ufo.png]
        end
        subgraph GH_Hacks["hacks/"]
            GH_bgmd[background.md]
        end
    end

    %% Student Destination
    subgraph Student["VSCode"]
        subgraph SW_Backgrounds["images/platformer/backgrounds/"]
            SW_planet[alien_planet1.jpg]
        end
        subgraph SW_Sprites["images/platformer/sprites/"]
            SW_ufo[flying-ufo.png]
        end
        subgraph SW_Hacks["hacks/"]
            SW_bgmd[background.md]
        end
    end

    %% Transfer Arrows
    GH_bgmd -- "wget" --> SW_bgmd
    GH_ufo -- "wget" --> SW_ufo
    GH_planet -- "wget" --> SW_planet

Modify Code

Open each file in VSCode to verify the transfer. It is easiest to click on them in Source Control and then open them in Explorer.

Verify the location of the files and make minor changes to the YAML in the markdown file.

  • hacks/background.md
    • frontmatter changes
      • change “opencs” to “base”
      • remove 1st forward slashes in the image names
    • script/javascript changes
      • change name of object list from objects to gameObjects, there are two locations to change. This name is for clarity of ready code.
  • images/platformer/backgrounds/flying-ufo.png
  • images/platformer/backgrounds/alien_planet.jpg

Time to run the game by performing a make. The project has /background as permalink.

Code Overview

Now that you have the game running, let’s talk about the code.

Canvas of the Game

In order for anything to show up on your screen in the first place, we have to create a canvas for everything to be drawn on. The canvas is defined in HTML.

<canvas id="world"></canvas>

We now have a canvas, but there isn’t anything in it. We can write JavaScript code to modify the canvas. However, we have an HTML page, so we need to tell the page where the JavaScript code is using a script tag.

<canvas id="world"></canvas>
<script>
  // Our Javascript code is going to go here!
</script>

Remember that most HTML tags have a starting tag (<tagname>) and an ending tag (</tagname>), and everything in between is inside of those tags. All of our JavaScript code should be between the starting and ending script tags.


The code in the game needs to connect to the Document Object Model (DOM) element for the canvas. We have named the HTML element’s id world, and we will define a GameWorld class to manage the canvas.

We’re going to need to declare variables inside the GameWorld class using the keyword this.

this.canvas = document.getElementById("world");
// document.getElementById("world") finds the <canvas id="world"> element in the DOM.

this.ctx = canvas.getContext('2d');
// every canvas has a drawing context. for example, getContext('2d') returns the 2D drawing API (methods like drawImage, fillRect, clearRect).
// canvas is the DOM element; ctx is what draws pixels

Sizing the Canvas

Toward the bottom of the JavaScript <script> tag, just before the closing </script> tag, you will find the GameWorld class. We have defined this code to manage everything that writes to the canvas, aka game world.

We need to setup the GameWorld

  • The canvas is setup to match window screen. Look at all the this.canvas commands.
  • The ctx is used to draw into the canvas
  • The game objects are assigned to the this.gameObjects array. These game pieces are inserted into the GameWorld.
  • In object oriented programming language…
    • The GameWorld has-a Background
    • The GameWorld has-a Player
class GameWorld {
  static gameSpeed = 5;
  constructor(backgroundImg, spriteImg) {
    this.canvas = document.getElementById("world");
    this.ctx = this.canvas.getContext('2d');
    this.width = window.innerWidth;
    this.height = window.innerHeight;
    this.canvas.width = this.width;
    this.canvas.height = this.height;
    this.canvas.style.width = `${this.width}px`;
    this.canvas.style.height = `${this.height}px`;
    this.canvas.style.position = 'absolute';
    this.canvas.style.left = `0px`;
    this.canvas.style.top = `${(window.innerHeight - this.height) / 2}px`;

    this.objects = [
      new Background(backgroundImg, this),
      new Player(spriteImg, this)
    ];

Here are a couple of definitions that could be commented in the above code block.

canvas.width, canvas.height: set the drawing buffer size (pixel resolution of the canvas).

canvas.style.width, canvas.style.height: set the CSS size (how big it appears on screen). They set both equal so the drawing area matches the visible size.

Game Objects

In our game, everything that appears on the screen (like the alien planet background or the UFO sprite) can be thought of as a game object. Instead of writing separate code for each image, we create a class that acts like a blueprint. This blueprint is defined in the class GameObject

class GameObject {
  constructor(image, width, height, x = 0, y = 0, speedRatio = 0) {
    this.image = image;       // what picture to draw
    this.width = width;       // how wide to draw it
    this.height = height;     // how tall to draw it
    this.x = x;               // where it is horizontally
    this.y = y;               // where it is vertically
    this.speedRatio = speedRatio;
    this.speed = gameSpeed * this.speedRatio; // how fast it moves
  }
  update() {
    // gets filled in by subclasses (like Background)
  }
  draw(ctx) {
    ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
  }
}

Instead of hardcoding the background or sprite separately, we can now make them instances of the GameObject class. Look for the Background and Player classes in the <script> code.

Since the Background and Player classes use the keyword extends in their definition, they inherit the properties of GameObject. Inheritance is a key to OOP programming and using OOP language we say.

  • The Background is-a GameObject
  • The Player is-a GameObject

Background Object

Our background is a special game object: it has to move sideways forever to look like the player is moving. To make it seamless, we draw two copies of the background image side-by-side. As one scrolls off the screen, the other one takes its place.

class Background extends GameObject {
  constructor(image, gameWorld) {
    // Fill entire canvas
    super(image, gameWorld.width, gameWorld.height, 0, 0, 0.1);
  }
  update() {
    this.x = (this.x - this.speed) % this.width;
  }
  draw(ctx) {
    ctx.drawImage(this.image, this.x, this.y, this.width, this.height);
    ctx.drawImage(this.image, this.x + this.width, this.y, this.width, this.height);
  }
}}

The standard part of Background is it uses a super call to GameObject to define it’s this attributes like this.x, this.y, etc.

The special part is that it overrides or customizes the update() and draw() methods for its unique behavior.

update() moves the background a little bit every frame.

The % this.width (modulo) makes sure it “wraps around” so the background never disappears.

draw() paints two backgrounds so there’s no empty gap.

The Game Loop (Animation)

The most important part of a game is the gameLoop that keeps running continuously. This is where things get updated and redrawn, frame after frame.

gameLoop() {
  this.ctx.clearRect(0, 0, this.width, this.height);
  for (const obj of this.objects) {
    obj.update();
    obj.draw(this.ctx);
  }
  requestAnimationFrame(this.gameLoop.bind(this));
}
start() {
  this.gameLoop();
}

clearRect wipes the screen so old frames so they don’t overlap.

for cycles through every gameObject we defined with the GameWorld constructor.

update typically changes object positions.

draw places the image onto the canvas according to recent updates.

requestAnimationFrame tells the browser to “do this again on the next frame.”

That’s what makes the background look alive — it’s being redrawn 60 times per second!

OOP Model for Game

GameObject is the base class. Background and Player are subclasses (is-a relationship). GameWorld has-a Background and Player (composition).

classDiagram
    GameWorld "1" o-- "many" GameObject : has-many
    GameObject <|-- Background : is-a
    GameObject <|-- Player : is-a

    class GameWorld {
        +GameObject[] gameObjects
        +gameLoop()
        +start()
    }
    class GameObject {
        +image
        +width
        +height
        +x
        +y
        +speedRatio
        +speed
        +update()
        +draw(ctx)
    }
    class Background {
        +update()
        +draw(ctx)
    }
    class Player {
        +draw(ctx)
    }

Hacks (Objective Assessment)

The Player object has both standard and special methods in relation to GameObject. Be sure to make comments on both the standard and unique behaviors.

Discuss Player Object update

class Player extends GameObject {
      // code omitted
      update() {
        // Comment on what is going on here
      }
    }

Make comments throughout the Game code

Commenting is key to understanding. The code has been spaced with blank lines to separate distinct sections. Make comments in every section, working with your trio. Try to do this individually and then in live share. Use Chat to help verify your comments, but your teacher will expect you to be able to explain them.

Here are comment styles that will work in JavaScript:

  • Single line or end of line comment //
  • Multi line commets starting line /* ending line */

Make your own scene

There are backgrounds and sprites in images/platform. Locate them or find your own. Make a change to the Background and Sprite by modifying the YAML according to your located files.