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

Python, C# and JavaScript 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.

Python

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

C#

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

JS

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 versus 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.

Python

basketX = 10

def ProcessBasket():
    global basketX   

    if (BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp)) == 0:
        basketX = basketX - 6

    if (BrainPad.Digital.Read(BrainPad.Pin.ButtonB,BrainPad.Pin.PullUp)) == 0:
        basketX = basketX + 6 

C#

var basketX = 10;

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

JS

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”.

Python

while True:
    BrainPad.Display.Clear(0)
    BrainPad.Display.DrawFillRect(1,basketX,60,20,3)
    BrainPad.Display.Show() 
    ProcessBasket()

C#

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

JS

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.

Python

basketX = 10

def ProcessBasket():
    global basketX   

    if (BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp)) == 0:
        basketX = basketX - 6

    if (BrainPad.Digital.Read(BrainPad.Pin.ButtonB,BrainPad.Pin.PullUp)) == 0:
        basketX = basketX + 6 

while True:
    BrainPad.Display.Clear(0)
    BrainPad.Display.DrawFillRect(1,basketX,60,20,3)
    BrainPad.Display.Show() 
    ProcessBasket()

C#

var basketX = 10;

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

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

JS

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?

Python

def ProcessBasket():
    global basketX   

    if ((BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp)==0) and basketX > 0):
        basketX = basketX - 6

    if ((BrainPad.Digital.Read(BrainPad.Pin.ButtonB,BrainPad.Pin.PullUp)==False) and basketX < 110):
        basketX = basketX + 6 

C#

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;
    }
}

JS

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.

Python

eggY = 0
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()

C#

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();
}

JS

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. The “down movement” means the Y-axis location will increase. Remember, the top of the screen is 0. The 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.

Python

def ProcessEgg():
    global eggY
    eggY = eggY + 3

C#

void ProcessEgg(){
    eggY = eggY + 3;
}

JS

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 can send it back to the top by setting eggY variable back to = 0.

Python

def ProcessEgg():
    global eggY
    eggY = eggY + 3
    if eggY > 64:
        eggY = 0

C#

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

JS

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.

Python

basketX = 10
eggY = 0
eggX = 20

def ProcessBasket():
    global basketX   

    if ((BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp)==0) and basketX > 0):
        basketX = basketX - 6

    if ((BrainPad.Digital.Read(BrainPad.Pin.ButtonB,BrainPad.Pin.PullUp)==0) and basketX < 110):
        basketX = basketX + 6 
        
def ProcessEgg():
    global eggY
    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()

C#

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();
}

JS

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 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.

Python

import random

def ProcessEgg():
    global eggY
    global eggX
    eggY = eggY + 3
    if eggY > 64:
        eggY = 0
        eggX = random.randint(0,128)

C#

var rnd = new Random();

void ProcessEgg(){
    eggY = eggY + 3;
    if (eggY > 64) {
        eggY = 0;
        eggX = rnd.Next(127);
    }  
}

JS

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.

Python

score = 0

def ProcessEgg():
    global eggY
    global eggX
    global basketX
    global score

    eggY = eggY + 3
    if eggY > 64:
        # Check for collision
        if eggX > basketX and eggX < basketX + 20:
            score = score + 1

        # new egg
        eggY = 0
        eggX = random.randint(0,120)

C#

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);
    }  
}

JS

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.

Python

while True:
    BrainPad.Display.Clear(0)
    BrainPad.Display.DrawCircle(1,eggX,eggY,3)
    BrainPad.Display.DrawFillRect(1,basketX,60,20,3)
    BrainPad.Display.DrawTextScale(score,1,0,0,3,2)
    BrainPad.Display.Show() 
    ProcessBasket()
    ProcessEgg()
    BrainPad.System.Wait(10)

C#

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

JS

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.

Python

def ProcessEgg():
    global eggY
    global eggX
    global basketX
    global score

    eggY = eggY + 3
    if eggY > 64:
        # Check for collision
        if eggX > basketX and eggX < basketX + 20:
            score = score + 1
            BrainPad.System.Beep(BrainPad.Pin.Piezo,1000,100)
        else:
            BrainPad.System.Beep(BrainPad.Pin.Piezo,256,100)
        # new egg
        eggY = 0
        eggX = random.randint(0,120)

C#

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

JS

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:

Python

from DUELink.DUELinkController import DUELinkController
availablePort = DUELinkController.GetConnectionPort()
BrainPad = DUELinkController(availablePort)

import random

basketX = 10
eggY = 0
eggX = 20
score = 0

def ProcessBasket():
    global basketX   

    if ((BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp)==0) and basketX > 0):
        basketX = basketX - 6

    if ((BrainPad.Digital.Read(BrainPad.Pin.ButtonB,BrainPad.Pin.PullUp)==0) and basketX < 110):
        basketX = basketX + 6 
        
def ProcessEgg():
    global eggY
    global eggX
    global basketX
    global score

    eggY = eggY + 3
    if eggY > 64:
        # Check for collision
        if eggX > basketX and eggX < basketX + 20:
            score = score + 1
            BrainPad.System.Beep(BrainPad.Pin.Piezo,1000,100)
        else:
            BrainPad.System.Beep(BrainPad.Pin.Piezo,256,100)
        # new egg
        eggY = 0
        eggX = random.randint(0,120)
        

while True:
    BrainPad.Display.Clear(0)
    BrainPad.Display.DrawCircle(1,eggX,eggY,3)
    BrainPad.Display.DrawFillRect(1,basketX,60,20,3)
    BrainPad.Display.DrawTextScale(score,1,0,0,3,2)
    BrainPad.Display.Show() 
    ProcessBasket()
    ProcessEgg()
    BrainPad.System.Wait(10)

C#

using GHIElectronics.DUELink;
var availablePort = DUELinkController.GetConnectionPort();
var BrainPad = new DUELinkController(availablePort);

var basketX = 10;
var eggY = 0;
var eggX = 20;
var rnd = new Random();
var score = 0;

void ProcessBasket(){
    if (!BrainPad.Digital.Read(BrainPad.Pin.ButtonA,BrainPad.Pin.PullUp) && basketX > 0){
        basketX = basketX - 6;
    }
    if (!BrainPad.Digital.Read(BrainPad.Pin.ButtonB, BrainPad.Pin.PullUp) && basketX < 110){
        basketX = basketX + 6;
    }
}
void ProcessEgg(){
    eggY = eggY + 3;
    if (eggY > 64) {
        //Check for collision
        if (eggX > basketX && eggX < basketX + 20){
            score = score + 1;
            BrainPad.System.Beep(BrainPad.Pin.Piezo, 1000, 100);
        }
        else {
            BrainPad.System.Beep(BrainPad.Pin.Piezo, 256, 100);
        }
        //New Egg
        eggY = 0;
        eggX = rnd.Next(127);
    }   
}

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

JS

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 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!