Midterm Project: Ant Smasher + Posenet

For my midterm project, I created Ant Smasher, one of my favorite childhood games.

I spent a lot of time on it and am very proud of how it turned out. There’s:

    • multiple game settings
    • music and sound effects for squishing ants and losing
    • a swatter cursor
    • hover effects on the buttons
    • different types and speeds of generated ants
    • a timer
    • 3 lives that you can visually deplete
    • a visual effect as the ants eat the candy
    • custom designed start and end pages
    • a settings page to choose settings from

The ants splice off the list when they die or go offscreen, and everything resets (though your high score is stored) so you can keep playing again and again.

I also think I did a good job of writing OOP for the ants and more modular code so that I could easily reuse functions for buttons and text styles. The code is quite readable and makes more sense. I can definitely feel that I’ve improved, though in the future, I’d invest even more effort into planning upfront so that the code can be even more efficient.

//creates an ant object
class Ant {
  constructor(img, size, speed, n) {
    this.x = random(100, width)-100; //x position
    this.y = 0; //y position
    this.size = size; 
    this.speed = speed;
    this.img = img;
    this.n = n; //noise
    this.dead = false;
    this.timeDied = 100000;
  }
  
  display() {
    image(this.img, this.x, this.y, this.size, this.size)
  }
  
  move() {
    //if thing pressed
    if (noseX>this.x && noseX<this.x+this.size && noseY>this.y &&noseY<this.y+this.size) {
      
      this.speed = 0; //stop moving
      image(blood, this.x, this.y, this.size, this.size) //blood splat
      this.dead=true; //mark dead
      squish.play(); // audio
      score++
      
    } else {
      
      let randList = [-this.n, this.n];
      this.x+=random(randList);
      this.y += this.speed + random(randList);
    }
  }
  
}

The only thing I would add, and I honestly might do this if I just finish my other midterms first, are other themes: e.g. Spaceship Smasher, Rat Smasher, Campus Cat Smasher, Worm On A string Smasher, etc. Same concept, different sprites, and a way to change them in the settings, probably by using global variables. I just have to fix the settings page to make space for them.

Random other thoughts about what to add: multiple soundtracks, psychedelic video backgrounds, animated sprites, notification banners for when you reach certain milestones, a leaderboard, etc. I would have done these, but I didn’t think they would add much to the final product, and might just be a lot of work for slightly more distraction.

The final sketch is here. Play it with love.

Just for fun (and to procrastinate my other midterms), I made a version you can play with ML Pose Net on fullscreen. I fixed the scaling of the design elements and adjusted the gameplay settings so that they match this style of play, which is slower but so much more fun. Your nose plays the swatter. If you can play this on hard mode, you’re the GOAT.

Play on fullscreen only here. Best on 11 or 13 inch screens.

(I would have set it to innerWidth and innerHeight, but the proportions of the designs get wonky, and the ants go wild.)

All in all, I’m incredibly happy with how this turned out. The only thing I would add or change is that I wish I had invested more time and effort to learn about particle systems and vectors, which might have added even more complexity and cool stuff to my work.  There’s always more that I can think of to be ambitious about, but given my constraints, I’m thrilled with what I’ve made.

Midterm Project for Tarek Nabih And Aadil zakerya

Concept:

I am aware that none of you enjoyed losing when you played the snake game. In order to avoid seeing the “Game Over” warning, we all loved looking for shortcuts as children. However, as techies, I know you would want to make this “Snake” dance to your beats. In this midterm Project on the Snake Game in P5js, I will demonstrate this to you all.

The goal of the game is to develop the snake that you control using the keyboard’s arrow keys. If the snake strikes an edge or its own body, the game is ended. Collecting apples that are positioned at random on the game board will allow you to grow.

 

Setup:

According to this concept, the gaming board is divided into blocks. Block size and block count are set in separate variables.

The snake advances one block in the direction it is currently travelling in with each loop iteration. The direction is also predetermined in a separate variable and is only altered when the user clicks an arrow key. Nothing more than a few blocks with a unique color scheme make up the snake. Every snake block has a rectangle drawn for it with a background other than the canvas once the location of the blocks is saved in an array.

When the snake slams into its own body or an edge of the game board, the game is over. In both cases, just the head needs to be taken into account because the body always moves in lockstep with the head.

Movement:

When the user hits an arrow key, the snake shifts its direction of travel. The framework’s thekeyPressed() method makes it simple to intercept this event.

The Snake is traveling left if the value in the x-direction is negative, and upwards if the value is negative in the y-direction

 

Food processing:

The most challenging aspect of the overall process is undoubtedly food processing. By selecting random x and y values, the apple is positioned on the board. The apple is only ever placed on a position, though, if no other portion of the snake’s body is currently there. The apple will be placed at random in another location if the snake consumes it. Additionally, the snake expands by one block and the score rises as well.

 

Game scoring:

Last but not least, the start-stop-pause procedure must be built. For this, we use a normal event listener for a button that was previously placed in the HTML. A click then changes a variable that sets the state. This is queried in the game loop. If it is set to pause, most processing is skipped.

 

Code:

the current one goes as follows:

NUM_ROWS = 20;
NUM_COLS = 20;
DIMENSION = 600;

MENU = 0;


TILE_WIDTH = DIMENSION/NUM_COLS;
TILE_HEIGHT = DIMENSION/NUM_ROWS;

SCORE = 0;

function preload(){
    main = loadImage('menu.jpg');
    headleft = loadImage('head_left.png');
    headup= loadImage('head_up.png');
    headright = loadImage('head_right.png');
    headdown= loadImage('head_down.png');
    
    myFont = loadFont('SnakeGameDemoRegular.ttf');
}

function setup(){
    createCanvas(DIMENSION,DIMENSION);
    //frameRate(12);

}

class Head{
    constructor(row, col){
        this.row = row;
        this.col = col;
        this.vc = 0;
        this.rc = 0;
        // this.headup=loadImage('head_up.png');
        // this.headleft = loadImage('head_left.png');
        this.key_handler = {UP: false, DOWN: false,LEFT: false, RIGHT: true};
        this.direction = '';
    }
    update(){
        if (this.key_handler.RIGHT){
            this.direction=RIGHT;
     
            this.headimg = image(headright,((this.col)*TILE_WIDTH)+45,((this.row)*TILE_HEIGHT)+15);
            this.vc = 1; 
            this.vr = 0;
            text("RIGHT",100,100);
           
            
        }
        if (this.key_handler.LEFT){
            this.direction=LEFT;
            this.headimg = image(headleft,(this.col)*TILE_WIDTH,(this.row)*TILE_HEIGHT);
            this.vc = -1; 
            this.vr = 0;
            text("left",100,100);
        }
        if (this.key_handler.UP){
           // this.direction=UP;
            this.headimg = image(headup,(this.col)*TILE_WIDTH,(this.row)*TILE_HEIGHT);
            this.vc = 0; 
            this.vr = -1;
        }
        if (this.key_handler.DOWN){
            //this.direction=DOWN;
            this.headimg = image(headdown,(this.col)*TILE_WIDTH,(this.row)*TILE_HEIGHT);
            this.vc = 0; 
            this.vr = 1;
        }


        if (this.key_handler.RIGHT){
            for(let x = (g.arr.length)-2; x<=1; x--){
                g[i].row = g[i-1].row
                g[i].col = g[i-1].col
                g[i].display()}
            g.arr[1].row = this.row + 1;
            g.arr[1].col = this.col + 1;
            g.arr[1].display();
            
            //this.headimg;
        }
        else if(this.key_handler.UP){
            for(let x = g.arr.length-2; x<=1; x--){
                g.arr[i].row = g.arr[i-1].row
                g.arr[i].col = g.arr[i-1].col
                g.arr[i].display();
        }
        g.arr[1].row = this.row + 1
        g.arr[1].col = this.col + 1
        g.arr[1].display();
        this.headimg;}

    else if(this.key_handler.DOWN){
        for(let x = g.arr.length-2; x<=1; x--){
            g.arr[i].row = g.arr[i-1].row
            g.arr[i].col = g.arr[i-1].col 
            g.arr[i].display()
        }
        g.arr[1].row = this.row + 1
        g.arr[1].col = this.col + 1
        g.arr[1].display();
        this.headimg
        }

    else if(this.key_handler.LEFT){
        for(let x = g.arr.length-2; x<=1; x--){
            g.arr[i].row = g.arr[i-1].row
            g.arr[i].col = g.arr[i-1].col
            g.arr[i].display()
        }
        g.arr[1].row = this.row + 1
        g.arr[1].col = this.col + 1
        g.arr[1].display()
        this.headimg
    }

        this.headimg;

        this.row += this.vr; 
        this.col += this.vc;

    }
    display(){
        this.update();
        this.headimg;
    }


}


class SnakeElement{
    constructor(col, row){
        this.row = row;
        this.col = col;
        //this.clr = clr; 
    }

    display(){
        text("HERE MEOW",100,100);
        if (y.key_handler.RIGHT){
            ellipse((this.col) * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30)}
        if (y.key_handler.UP){
            ellipse((this.col) * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30)}
        if (y.key_handler.DOWN){ 
            ellipse((this.col)  * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30)}
        if (y.key_handler.LEFT){
            ellipse((this.col)  * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30) }                       
        // if (this.clr == 'green'){
        //     fill(80, 153, 32) }
        // if (this.clr =='red'){
        //     fill(173, 48, 32)}
        // if (this.clr =='yellow'){
        //     fill( 251, 226, 76)}
        y.headimg;
    }
}

// class Snake extends Array{
 
//     constructor(){
//         this.push(y);
//         for(let i=0; i<3; i++){
//             this.push(SnakeElement(((NUM_COLS/2))*TILE_WIDTH ,(NUM_COLS/2)*TILE_WIDTH));
//         }
//         this.score = this.length(2-3;
//     }
//     addElem(elem) {
//         this.push(elem);
//     }
//     display(){
//         fill(0,0,0);
//         text("HERE",100,100);
//         this[0].display();
//     }
    
// }


class Snake{
    constructor(){
        this.arr = [];
        this.arr.push(y);
        for(let i=0; i<3; i++){
            let x = new SnakeElement(((NUM_COLS/2))*TILE_WIDTH ,(NUM_COLS/2)*TILE_WIDTH);
            this.arr.push(x);
        }
        this.score = this.arr.length-3;
        //text(this.score,200,200);
    }
    addElem(elem){
        this.arr.push(elem);
    }
    display(){
        this.arr[0].display();
    }
}













let y = new Head(NUM_ROWS/2,NUM_COLS/2);
let g =  new Snake();



function draw(){

    
    
    if (MENU==0){
        background(main);
        textSize(50);
        textFont(myFont);
        fill('white');
        text("SNEK GAME",240,100);


        textSize(30);
        
        fill('white');
        text('START', 450, 240);
        text('Instructions',450, 290);
    }

    if (MENU==1){
        
        if (frameCount%12==0){
            background('black');
            g.display();
        }
        //h.update();
        
        
    }

    if(MENU==2){
        background('white');
    }
    
    print(mouseX, mouseY);


    
    


}



function mouseClicked(){
    if(MENU==0){
        if((mouseX < 516 && mouseX > 448)&&(mouseY < 238 && mouseY > 218)){
            MENU=1;
        }
        if((mouseX < 583 && mouseX > 450)&&(mouseY<291 && mouseY>265)){
            MENU=2;
        }
    }
}

function keyPressed() {
    if (keyCode === UP_ARROW) {
      y.key_handler.UP = true;
      y.key_handler.DOWN = false;
      y.key_handler.LEFT = false;
      y.key_handler.RIGHT = false;

    } 
    if (keyCode === DOWN_ARROW) {
        y.key_handler.UP = false;
        y.key_handler.DOWN = true;
        y.key_handler.LEFT = false;
        y.key_handler.RIGHT = false;
     
    }
    if (keyCode === LEFT_ARROW) {
        y.key_handler.UP = false;
        y.key_handler.DOWN = false;
        y.key_handler.LEFT = true;
        y.key_handler.RIGHT = false;
      
    } 
    
    if (keyCode === RIGHT_ARROW) {
        y.key_handler.UP = false;
        y.key_handler.DOWN = false;
        y.key_handler.LEFT = false;
        y.key_handler.RIGHT = true;
 
    }
    
  }

Reflections:

Currently, our code is not working, we are still debugging to figure out the current problem with it then we are gonna upgrade our program. We are trying to make the snake move in animation and not just be moving in the standard way of a moving square. we hope to make it colorful as well not just black and white as most snake games are these days.

 

 

 

 

Midterm Project: Scuba Diving

For the midterm project, I decided on making a scuba diving game where the player will be encounter sea treasures and sea monsters in a continuous stream. The player will accumulate points by colliding with sea treasures but when a collision is made with a sea monster, the player’s initial life points of 3 is reduced by 1. After the life points reach zero, the game will end and the player will have to start again.

This idea was inspired by a game a made with the python kivy module. In that version, the player moves a track and accumulates points by staying on the underwater track for as long as possible. A screenshot is included below:

The game will look something like this:

Female scuba diver swimming under water Stock Photo

I downloaded scuba diver sprites and extracted the various stances. I set up a background image for the game and created movements with the mouse and keyboard.

You can click at the on any position to make the sprite move or you can move by pressing the arrow keys on the keyboard. The code is below:

function keyPressed() {
if (keyCode === DOWN_ARROW) {
posy += speed;
}if (keyCode === LEFT_ARROW) {
posx -= speed;
}if (keyCode === RIGHT_ARROW) {
posx += speed;
}if (keyCode === UP_ARROW) {
posy -= speed;
}
}

function mousePressed(){
posx = mouseX;
posy = mouseY;
}

The sketch is embedded below:

 

Improvements:

  • Change the sprites based on the different movements
  • Make the transition between sprites smooth
  • Add monster and treasure sprites
  • Let the monster and treasure sprites flow continuously from right to left randomly and with different speeds
  • Add sound when collisions are with treasure and monsters
  • Display an interface for starting and ending the game

Midterm Progress

For the midterm project, I will be drawing inspiration from the Snake game on old Nokia phones. The aesthetics of the game will be the same as the one seen below.

However, I want to increase the complexity of program which will randomly generate a moving maze. This is to incorporate a sense of randomness to the game.

Firstly I will have to use the lessons learned in the OOP lectures, to create the Snake and the food class. The snake class will also have a sub class depicting each body component (for length of snake). I have started work on the snake class and body classes.

After this is done we will implement a maze class where certain walls will be in a fixed position and other walls will appear and disappear randomly. I want to make this more frequent when the hallway is further away from snakes head.

Midterm Progress

Concept

For my midterm project, I really wanted to learn to work with sprites and add movement to my characters. An idea for the game came to my mind when I looked at different sprite sheets on the internet and found pony characters. I chose them because their movements include flying,jumping as well as walking, which seemed really interesting to work with. You can see an example of the sprite below:

pony sprite | Pony, Sprite, Mlp wingsMy Little Gauntlet — Yet another update. All the manes and tails...

That’s why my game concept will be around the theme of the cartoon “My little pony” 😁 I am planning to make a game similar to vertical jumping games, where my rainbow pony(character of Rainbow dash) will fly in the sky, and maintain the weather and clear the skies. The player earns bonus points for every cloud cleared and loses points when the character hits a bolt of lightning.

Process

As the first step into my game, I started working on backgrounds and an algorithm that changes the background and indicates a change of level. So I created a dummy variable that increases with time and that leads to a change. I also added background music to my sketch.

Next steps

1. Create classes for clouds and lightning and locate them into my canvas

2. Make my pony move

3. Work on the point counting system which will then lead to the change of levels(background)

[Tetris] Update 1 2022/10/3 — Game Interface & Block Display

Concept

For this project, I created a Tetris. Tetris is a simple game, in which users move or rotate blocks to fill a game field. Once a horizontal line is created with blocks, blocks on the line will be removed and the user will gain a certain amount of points. Users will gain points if:

  1. Removes blocks by making a horizontal line of blocks.
  2. Uses hard drop
  3. Extra points if multiple lines are removed at once

Although I am using a high-resolution screen, I wanted to give a “retro” appearance to the game (8bit). To achieve this effect, I used 8-bit style fonts and images.

Tetris (NES) - online game | RetroGames.cz

Code

Coding the game was indeed complex because the game required not only game logic, which I partially implemented, but also a friendly user interface. For this reason, I used 3 js. files, each responsible of running the code (sketch.js), displaying game interface (Page.js), and running game (Game.js).

Before making an actual “gaming” part of the project, I made the game interface of the game first. For this project, I used Press Start 2P font downloaded from https://fonts.google.com/specimen/Press+Start+2P.

1. sketch.js

<2022/10/3> UPDATE 1

This file manages how different pages will be displayed based on the user input. Depending on the user input, the game will show the main page, game page, instruction page, interrupt page, or game.

function draw() {
  background(0);
  if (!escapePressed) {
    //if user did not press ESC
    if (currentPageIndex == 0) {
      //MAIN PAGE
      displayMain();
      if (mySelectorMain != null) {
        //draw selector object
        mySelectorMain.drawSelector();
      } else {
        //if selector is new, create new selector object
        mySelectorMain = new selector(150, 340, 21);
      }
    } else if (currentPageIndex == 1) {
      //START GAME selected
      startGame(); //start game based on Game.js
    } else if (currentPageIndex == 2) {
      //HOW TO PLAY selected
      displayInstructions(); //display instructions
    }
  } else {
    if (currentPageIndex == 1) {
      //when user is in the middle of the game
      displayReturn("EXIT GAME AND RETURN TO MAIN?");
      if (mySelectorESC != null) {
        //draw selector object
        mySelectorESC.drawSelector();
      } else {
        //if selector is new, create new selector object
        mySelectorESC = new selector(230, 310, 21);
      }
    } else if (currentPageIndex == 2) {
      //when user is reading instructions
      displayReturn("QUIT READING AND RETURN TO MAIN?");
      if (mySelectorESC != null) {
        //draw selector object
        mySelectorESC.drawSelector();
      } else {
        //if selector is new, create new selector object
        mySelectorESC = new selector(230, 310, 21);
      }
    }
  }
}

Code above will display appropriate pages based on the current index of the game and ESC input(0: MAIN PAGE, 1: GAME, 2: INSTRUCTION). If ESC is pressed, the game will display RETURN TO HOME PAGE with appropriate texts.

//When keyboard input is given
function keyPressed() {
  if (keyCode == DOWN_ARROW) {
    if (currentPageIndex == 0) {
      /*Main page. Move selector accordingly. Selector will move 
      to HOW TO PLAY when it was previously at START GAME, and vice versa*/
      if (mySelectorMain.y == 340) mySelectorMain.y = 400;
      else mySelectorMain.y = 340;
    } else if (
      (currentPageIndex == 1 || currentPageIndex == 2) &&
      escapePressed
    ) {
      /*if ESC is pressed in the middle of the game or in the instruction,
      move selector accordingly.Selector will move to NO when it was previously 
      at YES, and vice versa*/
      if (mySelectorESC.y == 310) mySelectorESC.y = 370;
      else mySelectorESC.y = 310;
    }
  }
  if (keyCode == UP_ARROW) {
    if (currentPageIndex == 0) {
      /*Main page. Move selector accordingly. Selector will move 
      to HOW TO PLAY when it was previously at START GAME, and vice versa*/
      if (mySelectorMain.y == 340) mySelectorMain.y = 400;
      else mySelectorMain.y = 340;
    } else if (
      (currentPageIndex == 1 || currentPageIndex == 2) &&
      escapePressed
    ) {
      /*if ESC is pressed in the middle of the game or in the instruction,
      move selector accordingly.Selector will move to NO when it was previously 
      at YES, and vice versa*/
      if (mySelectorESC.y == 310) mySelectorESC.y = 370;
      else mySelectorESC.y = 310;
    }
  }
  if (keyCode == ENTER) {
    if (currentPageIndex == 0) {
      //user selects START GAME. Change currentPageIndex to 1
      if (mySelectorMain.y == 340) currentPageIndex = 1;
      //user selects HOW TO PLAY. Change currentPageIndex to 2
      else currentPageIndex = 2;
    } else if (
      (currentPageIndex == 1 || currentPageIndex == 2) &&
      escapePressed
    ) {
      /*ESC is pressed in the middle of the game or in the instruction.
      If user selects YES, change currentPageIndex to 0 (returning to MAIN PAGE);
      if user selects NO, simply return to the current page*/
      if (mySelectorESC.y == 310) {
        //YES
        currentPageIndex = 0;
        escapePressed = false;
      } else {
        //NO
        //user selects NO
        escapePressed = false;
      }
    }
    //Reset selector objects' position (DEFAULT: First option)
    mySelectorMain = null;
    mySelectorESC = null;
  }
  //If ESC is pressed, change escapePressed to true;
  if (keyCode == ESCAPE) {
    if (currentPageIndex != 0) escapePressed = true;
  }
}

Code above deals with user inputs and how the game will react to each  input. For this version of the game, user has an option to select different options using up/down arrows and enter. Variables with name including “selector” are objects of triangles that is shown next to the options. It will be further explained in PAGE.JS section.

2. Page.js

<2022/10/3> UPDATE 1

This file manages how each page will visually displayed to the user. It is where all positions and alignments of window/texts are shown. The page starts with preload() to load fonts and images that will be used through out the game.

function preload() {
  font = loadFont("assets/8Bit.ttf"); //load textfont to p5js
  blockImg = loadImage("assets/block.png");  //load single block unit image to p5js
}

Most of the codes in this page manages positions and alignments of the visual elements for each page (MAIN, GAME, INSTRUCTION, and INTTERUPTION).

//MAIN Page
function displayMain() {
  currentPageIndex = 0;

  //display main title of the game: TETRIS
  setFontStyle(titleFontSize);
  text("TETRIS", width / 2, 250);

  //display options
  setFontStyle(normalFontSize);
  text("START GAME", width / 2, 350);
  text("HOW TO PLAY", width / 2, 410);
}

//Instruction Page
function displayInstructions() {
  setFontStyle(titleFontSize - 40);
  text("HOW TO PLAY", width / 2, 95);

  createWindow(width / 2, 320, 530, 370);

  setFontStyle(normalFontSize);
  text(
    "↑ : ROTATE BLOCKS\n\n\n← : MOVE BLOCKS LEFT\n\n\n→ : MOVE BLOCKS RIGHT\n\n\n↓ : SOFT DROP\n\n\nSPACE : HARD DROP",
    width / 2,
    180
  );

  setFontStyle(normalFontSize - 7);

  if (millis() - currentMillis > 800) {
    currentMillis = millis();
    blink = !blink;
  }

  if (blink) {
    fill(0);
    stroke(0);
  } else {
    fill(255);
    stroke(255);
  }

  text("PRESS ESC TO RETURN", width / 2, 560);
}

//Game
function displayGameBackground(score, level, lines) {
  createWindow(width / 4 + 25, height / 2, width / 2 + 6, height - 60 + 6);

  setFontStyle(normalFontSize);
  text("——— NEXT ———", 462.5, 50);

  //Next blocks
  createWindow(400, 125, 106, 106);
  createWindow(525, 125, 106, 106);

  //Score, level, line cleared
  createWindow(462.5, 385, 231, 376);

  setFontStyle(normalFontSize);
  text("SCORE", 462.5, 245);
  text(score, 462.5, 290); //display score
  text("LEVEL", 462.5, 358.3);
  text(level, 462.5, 403.3); //display level of the round
  text("LINES", 462.5, 481.7);
  text(lines, 462.5, 526.7); //display total # of lines cleared in the round
}

//Return to Main Question Page
function displayReturn(_text) {
  //Create window
  createWindow(width / 2, height / 2, 400, 300);

  setFontStyle(normalFontSize);

  //Text Question
  text(_text, width / 2, 220, 350);

  //Options
  text("YES", width / 2, 320);
  text("NO", width / 2, 380);
}
//default setting of the font
function setFontStyle(size) {
  strokeWeight(1);
  textFont(font);  //set font style
  fill(255);
  textWrap(WORD);  //wrap text
  textAlign(CENTER);  //align text to center
  textSize(size);  //change text size
}

//create a rectangular window where texts will be displayed
function createWindow(x, y, w, h) {
  fill(0);
  stroke(255);  //white border
  strokeWeight(6);  //border weight of the rectangle
  rectMode(CENTER);  //rectangle is centered
  rect(x, y, w, h);
}

setFontStlye(size) and createWindow(x,y,w,h) are functions, where size is textSize, x and y are coordinates of the rectangle’s/window’s center, and w and h are width and height of the window.

3. Game.JS

<2022/10/3> UPDATE 1

This file contains all the game logics and runs Tetris game. The game consists of 2 main parts: block objects and game field array. Block objects are used to display blocks in motion. Game field array is what the program use to visualize the game. Once blocks cannot further be moved, it will be stored into game field array, so no further calculations will be needed in the future.

Game logic follow procedures listed below:

  • Create an empty game field array. (0: empty space, 1: something is present)
board = [];
for (let i = 0; i < boardHeight + 1; i++) {
  board[i] = [];
  for (let j = 0; j < boardWidth; j++) {
    board[i][j] = 0;
  }
}
  • User selects START GAME. Game Starts based on SCRIPT.JS
else if (currentPageIndex == 1) { 
//START GAME selected 
startGame(); 
}
  • Load background layout of the game (shapes and texts) based on PAGE.JS
function startGame() {
  displayGameBackground(0, 1, 0, 1);
  //Code is partially shown for demonstration
}
  • Generate, display, and move block object. Types of the block is randomly generated from the array that contains 7 different block types. Up until now, block will move only vertically down every time interval. When the block reaches bottom, or cannot be further moved, a new random block will be generated.
let blockTypeArr = ["I", "J", "L", "O", "S", "T", "Z"]; //Block Type Array

//GAME
function startGame() {
  displayGameBackground(0, 1, 0, 1); //display background interface

  if (generateBlock) {
    //If new block is needed, create a random shaped block
    myBlock = new block(int(random(0, 11)), 10, random(blockTypeArr), 4);
    generateBlock = false;
  }

  myBlock.drawBlock();
  myBlock.blockDown();
  displayBoard();
}

Most of the game logic is handled by block class below. For more, read the commented lines.

//block object for tetris blocks
class block {
  constructor(_x, _y, _type, speed) {
    this.x = _x;
    this.y = _y;
    this.type = _type;
    this.shapeArr = [];
    this.speed = 1;
  }
  
  /*this class method will fill shapeArr based on the
  type of the block. shapeArr is a 4*4 array, in which
  0: empty, 1: block*/
  shapeType(_type) {
    let shapeArr = [];
    /*example L block
      0 0 0 0
      0 1 0 0
      0 1 0 0
      0 1 1 0*/
    for (let i = 0; i < 4; i++) {
      shapeArr[i] = [0, 0, 0, 0];
    }
    if (_type == "I") {
      shapeArr[0][0] = 1;
      shapeArr[1][0] = 1;
      shapeArr[2][0] = 1;
      shapeArr[3][0] = 1;
    } else if (_type == "J") {
      shapeArr[1][2] = 1;
      shapeArr[2][2] = 1;
      shapeArr[3][1] = 1;
      shapeArr[3][2] = 1;
    } else if (_type == "L") {
      shapeArr[1][1] = 1;
      shapeArr[2][1] = 1;
      shapeArr[3][1] = 1;
      shapeArr[3][2] = 1;
    } else if (_type == "O") {
      shapeArr[2][1] = 1;
      shapeArr[2][2] = 1;
      shapeArr[3][1] = 1;
      shapeArr[3][2] = 1;
    } else if (_type == "S") {
      shapeArr[2][1] = 1;
      shapeArr[2][2] = 1;
      shapeArr[3][0] = 1;
      shapeArr[3][1] = 1;
    } else if (_type == "T") {
      shapeArr[2][1] = 1;
      shapeArr[3][0] = 1;
      shapeArr[3][1] = 1;
      shapeArr[3][2] = 1;
    } else if (_type == "Z") {
      shapeArr[2][1] = 1;
      shapeArr[2][2] = 1;
      shapeArr[3][2] = 1;
      shapeArr[3][3] = 1;
    }
    this.shapeArr = shapeArr;
  }

  //Checks if block can be moved in a given direction
  validMove(dX, dY) {
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) { 
if ( /*ignores all empty spaces. 1s must always be within the boundary of game field and must not overlap with non-empty, or 1s, when moved*/ this.shapeArr[i][j] == 1 && (board[this.y + i + 1][this.x + j] != 0 || this.y + i + dY >= boardHeight ||
            this.x + j + dX < 0 || this.x + j + dX >= boardWidth)
        ) {
          return false;
        }
      }
    }
    return true;
  }

  //Move block down for every time interval
  blockDown() {
    if (this.validMove(0, 1)) {
      if (millis() - counter > 400 / this.speed) {
        this.y++;
        counter = millis();
      }
    } else {
      //Once block cannot be moved down further, update game field
      for (let i = 0; i < 4; i++) {
        for (let j = 0; j < 4; j++) {
          if (this.shapeArr[i][j] == 1) board[this.y + i][this.x + j] = 1;
        }
      }
      //generate new block
      generateBlock = true;
    }
  }

  //Move Block left or right. left when direction is -1, right when direction is 1
  blockLeftRight(dX) {
    if (direction == -1) {
    }
  }

  //draw blocks. for each 1s in the shapeArr, block image will be placed
  drawBlock() {
    this.shapeType(this.type);
    blockImg = blockImg.get(0, 0, 20, 20);
    push();
    translate(25, 30);
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) {
        if (this.shapeArr[i][j] == 1)
          image(blockImg, blockSize * (this.x + j), blockSize * (this.y + i));
      }
    }
    pop();
  }
}

//visually display the gameboard.
function displayBoard() {
  blockImg = blockImg.get(0, 0, 20, 20);
  push();
  translate(25, 30);
  for (let i = 0; i < boardHeight; i++) {
    for (let j = 0; j < boardWidth; j++) {
      if (board[i][j] == 1) image(blockImg, blockSize * j, blockSize * i);
    }
  }
  pop();
}

Future Improvements

There are tons of more things to implement in this game:

    1. Moving blocks horizontally using key inputs.
    2. Rotating blocks using key inputs.
    3. This means more logic must be added to the movement validation method.
    4. Add conditions that will check if game is over or not.
    5. Add conditions that will check if user had made a horizontal line using blocks. Then, the program must be able to remove that line and shift whole game filed down by the number of removed lines unit(s).
    6. The game will not reset even if user returns to the main page in the middle of the game. This can be fixed by resetting every variable at the start of each game.

Midterm Project: Design and Storyboarding

PURPOSE & GOALS 

The largest project I ever took on as an IMA student was a digital ethnography about Chinese adoption. The project involved conducting interviews and participant observation for 15+ individuals. Using the information I had gathered, I created a multipage interactive website with various p5 sketches embedded into it. With visual elements that complimented text and quotes from myself and other interviewees, the ethnography addressed 3 important aspects of the adoptee experience, family, culture, and self-identification. One of my goals for the site was to embed a game that helped show the user what it’s like dealing with biculturalism, but this was never completed. Thus, this game will be a continuation of my past project that addresses bicultural identity for Chinese adoptees.

 

But first, what is Biculturalism? 

Biculturalism is describes the mix of cultures that an individual comes to know, either being a person of multiple races, or growing up in a culture that is different from that of their family. In the case of adoptees, they were born into the culture of their motherland, but are often taken away from their birthplace at a young age and have to grow up in the culture of their family (usually Western). I note that the experience is quite different from a Chinese or Asian person growing up outside their home country, as the added layer of an interracial or multinational family complicates the child’s access to both cultures.

 

As a result, adoptees often grow up very distant from Chinese culture, yet feel like they can never be accepted by the Western culture they were brought up in either. Adoptees are often labelled “whitewashed” if they do not like Chinese food, or don’t know any Mandarin, or they are labelled “ungrateful” if they have more Chinese friends than they do White friends, or are eager to learn about their heritage. The duality of these two boxes that adoptees are meant to fit into creates a feeling of Otherness and “Alienness”, on a level that is far more nuanced than the traditional Asian American experience.

 

INSPIRATION 

Personally, I am not a gamer. I have never been one, and I don’t think I’ll ever become one. Most of the time I just don’t find the characters/plot that compelling, and rarely do they leave me with something to think about after the game is finished. Hence, if I were to play a game, I enjoy ones that are less focused on action and more on storytelling. One of my favorite games is an interactive roleplaying game called Dys4ia (Youtube video of gameplay can be found here). Dys4ia documents the experience of trans woman and video game designer Anna Anthropy. The game has multiple levels, each level deals with a different challenge of being a trans woman, such as buying new clothes, being misgendered, starting testosterone, etc. Her game is an example of how video games can be used as a method of digital storytelling, telling the story of marginalized identities. She also shows us the capabilities of media art being self-reflective, allowing the user to enter into a new perspective that is not their own and to learn about the struggles and experiences of others in a way that is vulnerable, intimate, and creative.

 

DESIGN

Inspired by Anthropy’s work, hoping to create an experience that not only educates the user about topics that are rarely represented, but also encourages them to question their understanding of what biculturalism is. In contrast to Anthropy’s game, my game will be much more text heavy, with some narration at the beginning of each “level”. In total, I plan to make 4 mini-games, or levels that symbolize aspects of my experience with Chinese and American culture as an adoptee.

To figure out exactly what I wanted to cover in the game, I began by creating a Mindmap that includes some of my thoughts and reflections about my own bicultural identity. The map is shown below:

After completing the map, the next step was to figure out what the game play will look like.

 

STORYBOARD 

I think figuring out how to represent the map into game form was the most difficult part. I knew that I didn’t need to create something super complicated to communicate the story, so the goal was to come up with games that while not novel in creativity, help demonstrate a complex metaphor. I also think it’s important to note that the “levels” are not an advancement of one another, but rather showcase the different phases of my journey as an adoptee. It’s really a cycle that is ongoing, and I find myself returning to these experiences at different moments in my life.  Hence, each mini-game, while has an objective, does not involve any “winning” per say. Some actions cause the game to restart, and yet the player has the option to replay or continue to the next game at any time they wish. A “scene by scene” representation of the game is shown below:

I realize that this project is very ambitious, and I don’t know if I will be able to accomplish four mini-games, but this is my vision as of now.

Midterm Progress

Project Idea

While I was tutoring as a coding instructor, I often used this game as a fun activity for beginners. In this game, we are supposed to move an object (Harvard’s logo) from one location to another while avoiding all the difficulties (other university’s logo). Thus, I decided to create a modified version of this game as my midterm project. 

In my adaptation of the game, the goal is to help the main character, who happens to be John Wick, survive as long as possible. However, the catch is there will be other characters who will be chasing him, and as soon as they get a hold of Wick, the game will be over. Thus, the theme of the game is to help Wick survive. 

Coding

For now, I have implemented “MyCharacter”, which is related to the main character, as well as “SuppCharacter” class, which is related to other characters who chase the main character. Each class is inherited from a primary class called “Character”. In the MyCharacter class, there are five methods (display, update, border_check, up_collision_check and movement), while the latter class contains three methods (display, update and border_check). As of now, when the primary character collides with other characters, no effect other than a piece of string is visible on the console. As per the necessity, I intend to implement more methods as well as modify existing methods to refine the game. 

In the MyCharacter class, the display method displays images loaded into the system, the update method updates (x, y) attributes of the class, the border_check method keeps the main character within the canvas while the up_collision_check and movement methods check if the main character has collided with other characters or not. In the same way, the methods in the SuppCharacter follow similar algorithms. 

The piece of code that I am particularly proud of is the up_collision_check method(), which contains nested if-conditionals. It is relatively short in length, but trying out different mathematical approaches took a while, and ultimately I came up with this algorithm. 

up_collision_check(other)
{
  // X coordinates verification
  if ( (this.x < (other.x + other.size)) && (this.x > other.x)) 
  {
    // Y coordinates verification
    if ((this.y < (other.y + other.size)) && ((this.y + this.char_h) > other.y))
    {
      print("Collision");
      return true;
    }      
  }   
}

Future Iteration

Over the weekend, I plan on adding the following functionalities to the game:

  1. Start screen that displays (a) instruction on how to play the game, (2) Start button and other setting features as deemed necessary 
  2. Implement a levels-based approach, where the level of difficulty goes on increasing. 
  3. Introduce more side characters to the game
  4. Include a variety of sound effects for different actions like collision, completion of a level and so forth
  5. Include a timer to determine the completion of a level 

Reflection

Overall, I am glad about the way the project is going on so far. The program is already 300 lines in length, and as I implement more methods, I may need to divide the project into multiple .js files. Also, I am expecting to make the project more efficient by reducing redundancies and changing algorithms if needed. 

Midterm Progress – Week 5

Concept

For my midterm project, I would like to work on a cool shooting game. However, I am yet to decide on the theme or path it should take. I was thinking about having a global warming theme where there could be say enemies to global warming spawning randomly and you need to take them down. As for the main character it could be say humans or plants. As far as the theme is concerned I would still like to keep myself flexible and see which ideas come on the way. As of now simple implementation of the objects have started being implemented using basic shapes, for example the circle drawn can move throughout the screen using AWSD keys.

Code

I have started implementing the logic of the game using basic shapes. As I progress, I shall incorporate images, sounds, and animations where possible to make the game more interesting and interactive. So far among the most difficult challenges I have faced were the logic to some parts of the game implementation such as the movement and restrictions of the shape within the canvas. I also faced a challenge with inheritance of classes for which I spent a considerable amount of time but haven’t figured out yet.

update() {
    this.y += this.vy;
    this.x += this.vx;
    
     //Input control for movement
    if (this.key_handler.a) {
      this.vx = -this.control_speed;
    } else if (this.key_handler.d) {
      this.vx = this.control_speed;
    } else {
      this.vx = 0;
    }

    if (this.key_handler.w) {
      this.vy = -this.control_speed;
    } else if (this.key_handler.s) {
      this.vy = this.control_speed;
    } else {
      this.vy = 0;
    }
    
    // print('x = ' + (this.x));
    // print('y = ' + (this.y));
    // print('r = ' + (this.radius));
    
    //Restricting movement to screen bounds
    if (this.x - this.radius < 0) {
      this.x = this.radius;
    }
    if (this.x + this.radius > SCREEN_WIDTH) {
      this.x = SCREEN_WIDTH - this.radius;
    }
    if (this.y - this.radius < 0) {
      this.y = this.radius;
    }
    if (this.y + this.radius > SCREEN_HEIGHT) {
      this.y = SCREEN_HEIGHT - this.radius;
    }
  }

Reflections / Improvements

I am looking forward to implementing a lot of things, from the start and end windows, to the score counter and display, audio and other visual effects. I am also looking forward to learning more about classes and inheritance of classes in p5js as it will make the code more efficient and easier to navigate.

Midterm Progress

Project idea – “Know your playlist”

The idea for this project came from my love for music, lately for rock music. My idea for the project would be a quiz type game, where I would play a song and the player would need to guess it correctly. I have some ideas for the design of it, however they are just rough sketches at the moment.

The game would work the following way:

  • A 5 to 10 second clip of the song would play and then the player would need to answer – The options would include: 4 different album images each with the name of the song and artist.
  • The player would need to answer correctly, if not the game will skip to the next question and take away a life (each play-through will have 3 lives)
  • After the game is completed (around 10 questions), the game will reveal the player’s score, and if they get all answers correctly a special message will appear (TBD)

coding

At the moment, I have no working code for the game, but I am thinking of using various arrays to keep songs from repeating themselves. Alongside that, in order to keep the design of the game tidy, I might need to use different object oriented functions for the picture + the name of the song. I am not fully sure on how will the project work and what exactly will be in the code, but I am working on finding it.