JS Gaming Introduction

Gaming Introduction

Video games have been an important part of computer systems since their early days. We will start by making the simplest possible video game.

Prerequisite

You must have a basic understanding of coding and have completed the Drawing lesson.

Back to the Basics

Like Python and C#, JS can be used to build powerful and modern games, thanks to modern graphical frameworks and libraries, like DirectX. The problem with learning how to build games in the modern world is that all the basics are missed. Things forgotten are things like the mathematics needed to draw a shape like a circle and understanding pixels and how they are handled.

Our goal here is not to build a modern 3D game. The goal is to show how Pong was made in the 70s and how games are made on a foundation level. We will focus on using the small screen on the hardware.

Game Objective

The player will need to catch falling eggs in a basket. For simplicity, the eggs are just a circle and the basket is just a rectangle.

Basket Drawing

The player, which is a basket in this case, is just a DrawLine(), or we can use DrawFillRect() to make a thicker line.

await BrainPad.Display.Clear(0);
await BrainPad.Display.DrawFillRect(1,10,60,20,3);
await BrainPad.Display.Show();

Basket Control

Buttons A and B will be used to control the player (basket). Instead of using the BtnDown() function, we will use DRead(). This is because we will need to know if the button is constantly down vs reading a button click. The B variable will be used for the basket’s position. We can create a subroutine that processes the basket.

In Python, we need to point out which variables we’ll be using inside the function we create so that it does not create new variables. So, we add the keyword ‘global‘. This ensures that we’re using the original variable we created at the beginning of our code.

var basketX = 10;

async function ProcessBasket() {
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonA, BrainPad.Pin.PullUp)) {
        basketX = basketX - 6;
    }
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonB, BrainPad.Pin.PullUp)) {
        basketX = basketX + 6;
    }
};


With the basket process in place, we can now create the game loop, to process and draw the “basket”.

while (true) {
    await BrainPad.Display.Clear(0);
    await BrainPad.Display.DrawFillRect(1, basketX, 60, 20, 3);
    await BrainPad.Display.Show();
    await ProcessBasket()
}

The entire code listing is here in case you have missed something.

var basketX = 10;

async function ProcessBasket() {
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonA, BrainPad.Pin.PullUp)) {
        basketX = basketX - 6;
    }
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonB, BrainPad.Pin.PullUp)) {
        basketX = basketX + 6;
    }
};

while (true) {
    await BrainPad.Display.Clear(0);
    await BrainPad.Display.DrawFillRect(1, basketX, 60, 20, 3);
    await BrainPad.Display.Show();
    await ProcessBasket()
}

Try what we have so far. Did you notice how the basket can leave the screen if you keep pushing one of the buttons? Any ideas on how we can keep the basket from leaving the screen?

Stay on Screen

Keeping the basket on the screen is somewhat easy. We will check the basket’s position and only change its position if the buttons are pressed and also if the basket is not leaving the screen. Notice how we used “and” in the previous sentence. The code will also be exactly just that.

Can you see why we did 0 & 110?

async function ProcessBasket() {
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonA, BrainPad.Pin.PullUp) && basketX > 0) {
        basketX = basketX - 6;
    }
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonB, BrainPad.Pin.PullUp) && basketX < 110) {
        basketX = basketX + 6;
    }
};

Falling Eggs

We’ll use a DrawCircle() function inside our while true loop and add egg variables eggY & eggX at the top of our code to track the egg’s x and y positions on the screen.

var eggY = 0;
var eggX = 20;

while (true){
    BrainPad.Display.Clear(0);
    BrainPad.Display.DrawCircle(1, eggX, eggY, 3); 
    BrainPad.Display.DrawFillRect(1, basketX, 60, 20, 3);   
    BrainPad.Display.Show();   
    ProcessBasket();
}

The eggs will always start at the top of the screen and fall down. The “down movement” means the Y-axis location will increase. Remember, the top of the screen is 0. eggY variable will keep track of the egg’s position as it’s falling.

Let’s create a function called ProcessEgg() to handle our egg. Inside the function let’s increment the eggY variable by +3. This will make our egg fall by 3 pixels every time the function is called.

async function ProcessEgg(){
    eggY = eggY + 3;
}

Also, we want to add some code to see when the egg reaches the bottom. We’ll use an if-statement to check and see if eggY is greater than 64. That means it has left the bottom of the screen. Once it does will send it back to the top by setting the eggY variable back to = 0.

async function ProcessEgg() {
    eggY = eggY + 3;
    if (eggY > 64) {
        eggY = 0;
    }
}

For the X-axis position, we will use our eggX variable. Let’s just keep it set at 20 for now. Here is the full code listing.

var basketX = 10;
var eggY = 0;
var eggX = 20;

void ProcessBasket(){
    if (!BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp) && basketX > 0){
        basketX = basketX - 6;
    }
    if (!BrainPad.Digital.Read(BrainPad.Pin.ButtonA, BrainPad.Pin.PullUp) && basketX < 110){
        basketX = basketX + 6;
    }
}
void ProcessEgg(){
    eggY = eggY + 3;
    if (eggY > 64) {
        eggY = 0;  
    }
}

while (true){
    BrainPad.Display.Clear(0);
    BrainPad.Display.DrawCircle(1, eggX, eggY, 3);
    BrainPad.Display.DrawFillRect(1, basketX, 60, 20, 3);
    BrainPad.Display.Show();   
    ProcessBasket();
    ProcessEgg();
}

That is starting to look like a game. Now we need to change where the egg falls down from the top. We want it coming down from a random location. Thankfully, random is a built-in feature in most programming languages. Let’s create a variable to hold our random number, and place it near the top with our other variables. Inside our ProcessEgg() function we’ll generate a random number every time our egg leaves the bottom of the screen.

function getRandomArbitrary(min, max) {
    return Math.random() * (max - min) + min;
}


async function ProcessEgg(){
    eggY = eggY + 3;
    if (eggY > 64) {
        eggY = 0;
        eggX = getRandomArbitrary(0,127);
    }  
}

Keeping Score

The basket is moving and the egg is falling but where is the score? This is done by comparing the position of the egg to the basket. We only need to do this when the egg reaches the bottom so we can use the if-statement inside our ProcessEgg() function to check.

Create a variable to hold our score set it to 0, and place it with our other variables at the top. Increase the score by one inside the if-statement that we use to check to see if our egg and basket are in the same spot.

var score = 0;

void ProcessEgg(){
    eggY = eggY + 3;
    if (eggY > 64) {
        //Check for collision
        if (eggX > basketX && eggX < basketX + 20){
            score = score + 1;
        }
        //New Egg
        eggY = 0;
        eggX = rnd.Next(120);
    }  
}

We finally can show off our score! Let’s place it in the top left corner.

while (true) {
    await BrainPad.Display.Clear(0);
    await BrainPad.Display.DrawCircle(1, eggX, eggY, 3);
    await BrainPad.Display.DrawFillRect(1, basketX, 60, 20, 3);
    await BrainPad.Display.DrawText(score.toString(), 1, 0, 0);
    await BrainPad.Display.Show();
    await ProcessBasket();
    await ProcessEgg();
}

Making Noise!

How about some tone when the egg is caught and a different tone when lost? We can use System.Beep() to generate simple tones.

async function ProcessEgg() {
    eggY = eggY + 3;
    if (eggY > 64) {
        //Check for collision
        if (eggX > basketX && eggX < basketX + 20) {
            score = score + 1;
            await BrainPad.System.Beep(BrainPad.Pin.Piezo, 1000, 100);
        }
        else {
            await BrainPad.System.Beep(BrainPad.Pin.Piezo, 256, 100);
        }
        //New Egg
        eggY = 0;
        eggX = getRandomArbitrary(0, 127);
    }
}

Final Results

A playable game with under 50 lines of code! Not bad considering commercial games are usually thousands or millions of lines of code. Here is the complete code with all the parts we’ve shown above:

import { SerialUSB } from './serialusb.js';
import * as due from './duelink.js';

let BrainPad = new due.DUELinkController(new SerialUSB());
await BrainPad.Connect();


var basketX = 10;
var eggY = 0;
var eggX = 20;

async function ProcessBasket() {
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonA, BrainPad.Pin.PullUp) && basketX > 0) {
        basketX = basketX - 6;
    }
    if (!await BrainPad.Digital.Read(BrainPad.Pin.ButtonB, BrainPad.Pin.PullUp) && basketX < 110) {
        basketX = basketX + 6;
    }
};

var score = 0;

function getRandomArbitrary(min, max) {
    return Math.random() * (max - min) + min;
}

async function ProcessEgg() {
    eggY = eggY + 3;
    if (eggY > 64) {
        //Check for collision
        if (eggX > basketX && eggX < basketX + 20) {
            score = score + 1;
            await BrainPad.System.Beep(BrainPad.Pin.Piezo, 1000, 100);
        }
        else {
            await BrainPad.System.Beep(BrainPad.Pin.Piezo, 256, 100);
        }
        //New Egg
        eggY = 0;
        eggX = getRandomArbitrary(0, 127);
    }
}


while (true) {
    await BrainPad.Display.Clear(0);
    await BrainPad.Display.DrawCircle(1, eggX, eggY, 3);
    await BrainPad.Display.DrawFillRect(1, basketX, 60, 20, 3);
    await BrainPad.Display.DrawText(score.toString(), 1, 0, 0);
    await BrainPad.Display.Show();
    await ProcessBasket();
    await ProcessEgg();
}

What is Next?

Modify the code to change how the game works, or completely make a new game.


BrainStorm

Do video games really detect collisions properly? Or is it just checking for location? Things get more advanced in 3D space and advanced computing but will the answer be going the easy route? What about “falling” objects? Did we do proper gravity physics calculations? It was a simple addition. Can you think of video games where proper physics calculations are a must?

Content Licensing
Newsletter

Twitter Feed
Hot off the press!