Yunho Lee Final Project – Escape Room (Ver. p5js)

Inspiration:

The inspiration for the game is Escape Room, where participants have to solve several puzzles in order to escape from the room.

Content:

A player is stuck in a room where you need to solve 3 puzzles to escape through the exit. In order to find all clues, the player has to interact with the miniature of the room. The game requires both in-game actions, and real-life actions to clear.

Main Algorithms of game mechanics:

  1. Object Interactions: Each game object has a specific area where the message and the clue show up. The message shows up automatically, and the clue opens after the interaction with the object (change in the amount of light)
  2. Character Movement: The character sprite has 4 direction movements, each of them played when corresponding direction keys are pressed. The program remembers the last direction moved and stays looking in the same direction when there is no movement.
  3. UI: Its UI is made as simple as possible, but the timer does not stop when the game is paused to prevent CHEATERS.

Why Photosensors?

There are two reasons why I used photosensors for all of the interactions of the objects in the game. First, the size of the photosensor is very small that it is very easy to hide its location and to place it inside small miniature objects. Second, the photosensor is the most appropriate sensor that goes along with the concept of the game, escaping from a dark room with limited light sources.

Goals that I failed to accomplish:

  1. The collision between the player and the objects
  2. Miniature’s roof and sidewalls that have LED to change the brightness of the room according to the location of the player in the game

Game (Playable through interaction by SpaceBar instead of interacting with the miniature) :

P5js (Alert! Very Very Long)

let serial; // variable to hold an instance of the serialport library
let portName = "COM7"; // fill in your serial port name here

let Character = [];
let playerObj;

let lastkey;

let start = true;
let pause = false;
let fail = false;
let gameclear = false;

let quiz1clear = false;
let quiz2clear = false;
let quiz3clear = false;

let clueOn = true;
let hintOn = false;
let clueUI;

let answercheck = false;
let getinput = false;

let swipeCount = 0;

let roomObjects = [];

let solveNum = 0;
let timer = 300;

let exitdoor;
let userinput = "";

let carpetinput;
let bedinput;
let tableinput;
let glassinput;
let shelfinput;
let drawerinput;

function preload() {
  charSprite = loadImage("character.png");
  bg = loadImage("floor.jpg");
  table = loadImage("table.png");
  darkness = loadImage("darkness.png");
  bed = loadImage("bed.png");
  shelf = loadImage("shelf.png");
  exit = loadImage("exit.png");
  candle = loadImage("candle.png");
  desk = loadImage("desk.png");
  carpet = loadImage("carpet.png");
  wall1 = loadImage("wall1.jpg");
  pauseIcon = loadImage("pause.png");
  textbg = loadImage("textbox.png");
  x = loadImage("x.png");
  quiz1 = loadImage("quiz1.jpg");
  quiz2 = loadImage("quiz2.jpg");
  quiz3 = loadImage("quiz3.jpg");
  quiz2ans = loadImage("quiz2ans.jpg");
  quiz3ans = loadImage("quiz3ans.jpg");
  
  keypic = loadImage("key.jpg");
  quiz1hint = loadImage("quiz1hint.jpg");
  water = loadImage("water.jpg");
  
  lock = loadImage("lock.jpg");
  paper = loadImage("paper.jpg");
}

function setup() {
  createCanvas(500, 400);
  frameRate(60);
  
  //arduino serial
  serial = new p5.SerialPort(); // make a new instance of the serialport library
  serial.on("list", printList); // set a callback function for the serialport list event
  serial.on("connected", serverConnected); // callback for connecting to the server
  serial.on("open", portOpen); // callback for the port opening
  serial.on("data", serialEvent); // callback for when new data arrives
  serial.on("error", serialError); // callback for errors
  serial.on("close", portClose); // callback for the port closing

  serial.list(); // list the serial ports
  serial.open(portName); // open a serial port
  
  // create character
  let charw = charSprite.width/12;
  let charh = charSprite.height/8;
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j<4; j++) {
      Character[i+3*j] = charSprite.get((i+6)*charw, j*charh, charw, charh);
    }
  }
  
  //construct playerObj
  playerObj = new player(Character, darkness);
  
  // setup room objects
  roomObjects[0] = new roomObj (table, 180, 150, 0, 0);
  roomObjects[1] = new roomObj (bed, 0, 0, 0, 0);
  roomObjects[2] = new roomObj (shelf, 330, 0, 0, 0);
  roomObjects[3] = new roomObj (exit, 450, 200, 0, 0);
  roomObjects[4] = new roomObj (candle, 0, 100, 0, 0);
  roomObjects[5] = new roomObj (desk, 100, 0, 0, 0);
  roomObjects[6] = new roomObj (carpet, 100, 320, 0, 0);
  roomObjects[7] = new roomObj (wall1, 0, 240, 0, 0);
  roomObjects[8] = new roomObj (wall1, 225, -55, 0, 0);
  
  // setup scripts for each objects with clues
  tableUI = new scriptUI("There is a note on the table but it is very dusty... \n(Clean the table to show the hint.)", quiz1,175, 290, 130, 210);
  deskUI = new scriptUI("The drawer opened with the key! There is a note inside the drawer. \n(Pull the drawer to show the hint.)", quiz2,150, 180, 40, 60);
  shelfUI = new scriptUI("As I pour down the water, letters appeared on the paper!!!\n(Place the cup in front of the bookshelf to show the hint.)", quiz3,330, 380, 60, 85);
  
  // keys to obtain clues
  bedUI = new scriptUI("I see something below my bed... What is it? \n(Check under the bed with a flashlight to show what's there.)", keypic,0, 30, 0, 50);
  carpetUI  = new scriptUI("Found a letter on the carpet... \n(Lift the carpet to show the hint.)", quiz1hint, 120, 170, 300, 335);
  glassUI = new scriptUI("There is a glass of water. Where do I use it? \n(Take the cup to obtain the water.)", water, 0, 25, 90, 180);
  
  // UI when you don't have item to see the clue
  lockUI = new scriptUI("There is a cabinet that is hard locked. Maybe there is a key nearby to open it. \n(Press SPACE BAR to show/hide the image)", lock, 150, 180, 40, 60);
  paperUI = new scriptUI("There is a paper between books, but nothing is written on it! Maybe I can pour something to make it show? \n(Press SPACE BAR to hide/show the paper)", paper, 330, 380, 60, 85);
  // no hint but just there
  shelf2UI = new scriptUI("There are so many books on the bookshelf. I wander how many there are...", "empty", 420, 450, 75, 92);
  
  emptyUI = new scriptUI("","empty",0,0,0,0);
  
  // construct the exitdoor
  exitdoor = new exitdoorUI();
}

let count = 0;

// mouseClick interact with UI
function mousePressed(){
  // Start UI interaction
  if (start == true){
    if (mouseX >= 180 && mouseX <= 330) {
      if (mouseY >= 265 && mouseY <=305){
        start = false;  
      }
    }
  }
  // Pause UI interaction
  else{
    if (mouseX>=480 && mouseY <=20){
      pause = !pause;
    }
  }
  // unpause
  if (pause == true){
    if (mouseX >= 165 && mouseX <= 345) {
      if (mouseY >= 185 && mouseY <=225){
        pause = !pause;
      }
    }
    if (mouseX >= 165 && mouseX <= 345) {
      if (mouseY >= 265 && mouseY <=305){
        fail = true;
        pause = false;
      }
    }
  }
  // restart
  if (gameclear == true || fail == true){
    // rect(145, 250, 210, 50);
    if (mouseX >= 145 && mouseX <= 355){
      if (mouseY >= 250 && mouseY <= 300){
        quiz1clear = false;
        quiz2clear = false;
        quiz3clear = false;
        gameclear = false;
        fail = false;
        timer = 300;
        playerObj.posX = width/2+50;
        playerObj.posY = height/2+80;
        playerObj.lastkey = "Front";
        start = true;
        tableUI.isopen = false;
        deskUI.isopen = false;
        shelfUI.isopen = false;
        bedUI.isopen = false;
        glassUI.isopen = false;
        carpetUI.isopen = false;
        shelf2UI.isopen = false;
        lockUI.isopen = false;
        paperUI.isopen = false;
        userinput = "";
        swipeCount = 0;
        solveNum = 0;
      }
    }
  }
}

// gain input from keyboard and send it to the PlayerObj.
function keyPressed(){
  if (keyCode === 32){
    hintOn = !hintOn;
  }
  else if (keyCode === ENTER){
    answercheck = !answercheck;
  }
  else if (keyCode === BACKSPACE){
    userinput="";
  }
  // else if (clueOn == false){
  else{
  if (keyCode === LEFT_ARROW) {
    playerObj.direction = "Left";
    lastkey = "Left";
  } 
  else if (keyCode === RIGHT_ARROW) {
    playerObj.direction = "Right";
    lastkey = "Right";
  }
  else if (keyCode === UP_ARROW) {
    playerObj.direction = "Back";
    lastkey = "Back";
  }
  else if (keyCode === DOWN_ARROW) {
    playerObj.direction = "Front";
    lastkey = "Front";
  }
  }
}

function keyTyped() {
  if (getinput){
    let letter = key;
    if (letter.length == 1){
      userinput += letter;
    }  
  }
  
}

function draw(){
  background(220);
  image(bg, 0, 0, width, height, 700, 1200, 1000, 800);
  
  // print(carpetinput, " ", bedinput," ", tableinput);
  
  if (keyIsPressed == false){
    playerObj.direction = "Idle";
    playerObj.lastkey = lastkey;
  }
  // display room objects
  for (i=0; i<roomObjects.length; i++){
    roomObjects[i].display();
  }
  
  // display player object
  playerObj.move();
  playerObj.display();
  
  // if (frameCount%10 == 0){
  //   print(playerObj.posX+" "+playerObj.posY);
  // }
  clueUI = emptyUI;
  // player on the Object with clue => match clueUI Object's clue
  // table
  if (playerObj.posX >= 175 && playerObj.posX <= 290){
    if (playerObj.posY >= 130 && playerObj.posY <= 210){
      clueUI = tableUI;
      if (frameCount%10 == 0 && tableinput < 140){
        swipeCount++;
        // print(swipeCount);
      }
      if (swipeCount > 10) hintOn = true;
    }
  }
  //desk
  if(playerObj.posX >= 150 && playerObj.posX <= 180){
    if (playerObj.posY >= 40 && playerObj.posY <= 60){
      if (bedUI.isopen == false){
        clueUI = lockUI;
        if (frameCount%10 == 0 && deskinput > 250){
          hintOn = true;
        }
      }
      else clueUI = deskUI;
    }
    
  }
  //shelf
  if(playerObj.posX >= 330 && playerObj.posX <= 380){
    if (playerObj.posY >= 60 && playerObj.posY <= 85){
      if (glassUI.isopen == false){
        clueUI = paperUI;
        if (frameCount%10 == 0 && shelfinput < 150){
          hintOn = true;
        }
      }
      else clueUI = shelfUI;
    }
  }
  //bed
  if(playerObj.posX >= 0 && playerObj.posX <= 30){
    if (playerObj.posY >= 0 && playerObj.posY <= 50){
      clueUI = bedUI;
      if (frameCount%10 == 0 && bedinput > 250){
          hintOn = true;
        }
    }
  }
  //carpet
  if(playerObj.posX >= 120 && playerObj.posX <= 170){
    if (playerObj.posY >= 300 && playerObj.posY <= 335){
      clueUI = carpetUI;
      if (frameCount%10 == 0 && carpetinput > 170){
          hintOn = true;
        }
    }
  }
  //glass
  if(playerObj.posX >= 0 && playerObj.posX <= 25){
    if (playerObj.posY >= 90 && playerObj.posY <= 180){
      clueUI = glassUI;
      if (frameCount%10 == 0 && glassinput > 130){
          hintOn = true;
        }
    }
  }
  //shelf2
  if(playerObj.posX >= 420 && playerObj.posX <= 450){
    if (playerObj.posY >= 75 && playerObj.posY <= 92){
      clueUI = shelf2UI;
    }
  }
  //exit
  if(playerObj.posX >= 440 && playerObj.posX <= 451){
    if (playerObj.posY >= 176 && playerObj.posY <= 230){
      getinput = true;
    }
    else getinput = false;
  }else getinput = false;
  
  if(clueUI.inside == false) hintOn = false;
  
  if (tableUI.isopen && shelfUI.isopen){
    if (deskUI.isopen){
      if (!quiz1clear) exitdoor.quiz1();
      else if (!quiz2clear) {solveNum = 1; exitdoor.quiz2();}
      else if (!quiz3clear) {sdolveNum = 2; exitdoor.quiz3();}
      else gameclear = true;
    }
  }
  else exitdoor.notopen();
  
  // print(userinput);
  
  // clue UI display algorithm
  if (clueOn == true){
    clueUI.display();
    if (hintOn == true){
      clueUI.showclue();
    }
  }

  //UI font
  fill("white");
  textFont("Courier New", 50);
  
  // start UI
  if (start == true){
    image(bg, 0, 0, width, height, 700, 1200, 1000, 800);
    // rect(180, 175, 150, 50);
    if (mouseX >= 180 && mouseX <= 330) {
      if (mouseY >= 265 && mouseY <=305){
        fill("rgb(35,35,167)")  
      }
      else fill("white")
    }
    text("Start",180, 300);
    fill("white");
  }
  
  // pause UI
  if (pause == true){
    image(bg, 0, 0, width, height, 700, 1200, 1000, 800);
    // rect(165, 185, 180, 40);
    if (mouseX >= 165 && mouseX <= 345) {
      if (mouseY >= 185 && mouseY <=225){
        fill("rgb(38,38,160)");
      }
    }
    text("Resume",165, 220);
    fill("white");
    if (mouseX >= 165 && mouseX <= 345) {
      if (mouseY >= 265 && mouseY <=305){
        fill("rgb(38,38,160)");
      }
    }
    text("Restart",165, 300);
    fill("white");
  }
  image(pauseIcon,480,0);
  
  // fail UI
  if (fail){
    image(bg, 0, 0, width, height, 700, 1200, 1000, 800);
    text("Time Over!!", 90, 150);
    textFont("Courier New", 25);
    text("You found "+solveNum+" out of 3 clues!", 45, 210);
    textFont("Courier New", 50);
    if (mouseX >= 145 && mouseX <= 355){
      if (mouseY >= 250 && mouseY <= 300){
        fill("rgb(38,38,160)");
      }
    }
    text("Restart", 145, 290);
    fill("white");
    // rect(145, 250, 210, 50);
  }
  
  // Game clear UI
  if (gameclear){
    image(bg, 0, 0, width, height, 700, 1200, 1000, 800);
    text("Game Clear!!", 80, 130);
    textFont("Times New Roman", 28);
    textAlign(CENTER);
    text("You got all 3 Questions correct!! \nCongratulations!", 30, 170, 455, 300);
    textFont("Courier New", 50);
    textAlign(LEFT);
    if (mouseX >= 145 && mouseX <= 355){
      if (mouseY >= 250 && mouseY <= 300){
        fill("rgb(38,38,160)");
      }
    }
    text("Restart", 145, 290);
    fill("white");
  }
  // Timer UI
  if (timer%60 < 10){
    text(int(timer/60)+":0"+(timer%60), 200, 55);
  }
  else {
    text(int(timer/60)+":"+(timer%60), 200, 55);  
  }
  if (frameCount%75 == 0){
    if (!start && !gameclear){
      if (!fail) timer--;
    }
  }
  if (timer == 0){
    fail = true;
    timer = 300;
  }
}


//serial functions

// get the list of ports:
function printList(portList) {
  // portList is an array of serial port names
  for (let i = 0; i < portList.length; i++) {
    // Display the list the console:
    print(i + " " + portList[i]);
  }
}

function serverConnected() {
  print("connected to server.");
}

function portOpen() {
  print("the serial port opened.");
}

function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  let inString = serial.readLine();
  if (inString.length > 0) {
    let inputVals = split(inString, ","); // split the string on the commas
    if (inputVals.length == 6) {
      // if there are 6 elements
      carpetinput =  inputVals[0];
      bedinput =     inputVals[1];
      tableinput =   inputVals[2];
      glassinput =   inputVals[3];
      shelfinput =   inputVals[4];
      drawerinput =  inputVals[5];
      // print(carpetinput, bedinput, tableinput, glassinput, shelfinput, drawerinput);
    }
  }
  serial.write("0");
}

function serialError(err) {
  print("Something went wrong with the serial port. " + err);
}

function portClose() {
  print("The serial port closed.");
}

class player{
  constructor(Character, darkness){
    this.sprite = Character;
    this.darkness = darkness;
    this.posX = width/2+50;
    this.posY = height/2+80;
    this.direction = "Idle";
    this.lastkey = "Front";
    this.speed = 2;
    this.frame = 1;
  }
  
  display(){
    image(this.sprite[this.frame], this.posX, this.posY);
    image(darkness, 0, 0, width, height, 475-this.posX, 380-this.posY, 500, 400);
  }
  
  // Controlls Player's Movement according to the keyboard input
  move(){
    if (this.direction == "Idle"){
      if (this.lastkey == "Front"){
        this.frame = 1;
      }
      else if (this.lastkey == "Left"){
        this.frame = 4
      }
      else if (this.lastkey == "Right"){
        this.frame = 7
      }
      else if (this.lastkey == "Back"){
        this.frame = 10
      }
    }
    else if (this.direction == "Left"){
      if (frameCount % 9 == 0){
      if (this.frame < 3 || this.frame > 4){
        this.frame = 3;
      }
      else {
        this.frame++;
      }
    }
    if (this.posX > 10){
      this.posX -= this.speed;
    }
    }
    else if (this.direction == "Right"){
      if (frameCount % 9 == 0){
      if (this.frame < 6 || this.frame > 7){
        this.frame = 6;
      }
      else {
        this.frame++;
      }
      
      }
      if (this.posX < 450){
        this.posX += this.speed;
      }
    }
    else if (this.direction == "Front"){
      if (frameCount % 9 == 0){
      if (this.frame > 1){
        this.frame = 0;
      }
      else {
        this.frame++;
      }
      
    }
      if (this.posY < 350){
        this.posY += this.speed;
      }
    }
    else if (this.direction == "Back"){
      if (frameCount % 9 == 0){
      if (this.frame < 9 || this.frame > 10){
        this.frame = 9;
      }
      else {
        this.frame++;
      }
    }
      if (this.posY > 10){
        this.posY -= this.speed;
      }
    }
    
    this.xbox = this.posX + this.sprite[0].width;
    this.ybox = this.posY + this.sprite[0].height;
  }
  
  // collide() {
  //   if (this.lastkey == "Front"){
  //     this.frame = 1;
  //   }
  //   else if (this.lastkey == "Left"){
  //     this.frame = 4
  //   }
  //   else if (this.lastkey == "Right"){
  //     this.frame = 7
  //   }
  //   else if (this.lastkey == "Back"){
  //     this.frame = 10
  //   }
  // }
  // more functions to be added
}

// class of room Objects
class roomObj {
  constructor(image, xPos, yPos, actionX, actionY){
    this.image = image;
    this.xPos = xPos;
    this.yPos = yPos;
    // action Area for objects with specific interaction
    this.actionX = xPos + actionX;
    this.actionY = yPos + actionY;
  }
  
  display() {
    image (this.image, this.xPos, this.yPos);
  }
}

class scriptUI {
  constructor(scriptText, clueimg, x1, x2, y1, y2){
    this.scriptText = scriptText;
    this.clueimg = clueimg;
    this.isopen = false;
    this.inside = false;
    this.x1 = x1;
    this.x2 = x2;
    this.y1 = y1;
    this.y2 = y2;
  }
  
  display() {
    if (playerObj.posX >= this.x1 && playerObj.posX <= this.x2){
      if (playerObj.posY >= this.y1 && playerObj.posY <= this.y2){
        this.inside = true;
        image(textbg, 25, 250);
        textFont("Courier New", 18);
        text(this.scriptText, 35, 260, 445, 360);
      }
      else this.inside = false;
    }
  }
  
  showclue() {
    if (playerObj.posX >= this.x1 && playerObj.posX <= this.x2){
      if (playerObj.posY >= this.y1 && playerObj.posY <= this.y2){
    if (this.clueimg != "empty"){
      image(this.clueimg, 25, 60);
      this.isopen = true;
    }
  }}}
}

class exitdoorUI{
  constructor(){
    this.quizNum = 1;
    this.answer1 = 151;
    this.answer2 = 102;
    this.answer3 = 6511;
  }
  
  notopen(){
    if (playerObj.posX >= 440 && playerObj.posX <= 451){
      if (playerObj.posY >= 176 && playerObj.posY <= 230){
        image(textbg, 25, 250);
        textFont("Courier New", 20);
        text("You are locked inside the room until you solve all the problems. There are 3 clues... (Search the room for the clues!!)", 35, 260, 455, 360);
      }
    }
  }
  
  quiz1(){
    textFont("Courier New", 20);
    if (playerObj.posX >= 440 && playerObj.posX <= 451){
      if (playerObj.posY >= 176 && playerObj.posY <= 230){
        if (!answercheck){
          image(textbg, 25, 250);
          textFont("Courier New", 20);
          text("Q1: The answer is : " + userinput, 35, 280);
          image(quiz1, 25, 100);
        }
        else if (userinput != this.answer1){
          image(textbg, 25, 250);
          text("Hmm... Why don't you try again. \nPress Enter to Continue...", 35, 260, 455, 360);
          userinput = "";
        }
        else if (userinput == this.answer1){
          // image(textbg, 25, 250);
          // text("Your answer to Question 1 is correct! \nPress Enter to Continue...", 35, 260, 455, 360);
          quiz1clear = true;
          userinput = "";
          answercheck = false;
        }
      }
    }
  }
  quiz2(){
    textFont("Courier New", 20);
    if (playerObj.posX >= 440 && playerObj.posX <= 451){
      if (playerObj.posY >= 176 && playerObj.posY <= 230){
        if (!answercheck){
          image(textbg, 25, 250);
          textFont("Courier New", 20);
          text("Your answer to Question 1 is correct! \nQ2: The answer is : " + userinput, 35, 260, 455, 360);
          image(quiz2ans, 25, 100);
        }
        else if (userinput != this.answer2){
          image(textbg, 25, 250);
          text("Hmm... Why don't you try again. \nPress Enter to Continue...", 35, 260, 455, 360);
          userinput = "";
        }
        else if (userinput == this.answer2){
        //   image(textbg, 25, 250);
        //   text("Your answer to Question 2 is correct! \nPress Enter to Continue...", 35, 260, 455, 360);
          quiz2clear = true;
          userinput = "";
          answercheck = false;
        }
      }
    }
  }
  quiz3(){
    textFont("Courier New", 20);
    if (playerObj.posX >= 440 && playerObj.posX <= 451){
      if (playerObj.posY >= 176 && playerObj.posY <= 230){
        if (!answercheck){
          image(textbg, 25, 250);
          textFont("Courier New", 20);
          text("Your answer to Question 2 is correct! \nQ3: The answer is : " + userinput, 35, 260, 455, 360);
          image(quiz3ans, 25, 100);
        }
        else if (userinput != this.answer3){
          image(textbg, 25, 250);
          text("Hmm... Why don't you try again. \nPress Enter to Continue...", 35, 260, 455, 360);
          userinput = "";
        }
        else if (userinput == this.answer3){
          // image(textbg, 25, 250);
          // text("Your answer to Question 3 is correct! \nPress Enter to Continue...", 35, 260, 455, 360);
          quiz3clear = true;
          answercheck = false;          
        }
      }
    }
  }
}

 

Arduino

#include <Servo.h>

Servo servo;

int carpet, bed, glass, shelf, table, drawer;

void setup() {
  servo.attach(9);
  Serial.begin(9600);
  while (Serial.available() <= 0) {
    Serial.println("0,0,0,0,0,0"); // send a starting message
    delay(300);              // wait 1/3 second
  }
}
void loop() {
  while (Serial.available() > 0) {
    read the incoming byte:
    int lock = Serial.read();
    servo.write(lock);

    byte CarpetButton = digitalRead(2);
    
    if (CarpetButton == HIGH) carpet = 1;
    else carpet = 0;
    
    table = analogRead(A0);
    glass = analogRead(A1);
    shelf = analogRead(A2);
    drawer = analogRead(A3);
    bed = analogRead(A4);
    carpet = analogRead(A5);
    
    Serial.print(carpet);
    Serial.print(",");
    Serial.print(bed);
    Serial.print(",");
    Serial.print(table);
    Serial.print(",");
    Serial.print(glass);
    Serial.print(",");
    Serial.print(shelf);
    Serial.print(",");
    Serial.print(drawer);
    Serial.println();
  }
}

 

Yunho Lee Final Project Game Progress Report 5/5

Accomplishment:

Mostly done with the programming part over the gameplay algorithms, except for the communication of the Arduino circuit with p5js. The game is totally playable through keyboard controls: spacebar – item interaction, arrow keys – player movement.

The collection of assets is completed through free assets images and self-made images through Clip Studio Paint and Adobe Photoshop.

In progress:

  1. Thumbnail of the game (a background picture to be used for the start menu)
  2. Create Circuit Map before constructing the interactive structures.

Objectives left:

  1. Algorithm for collision with objects to restrict character movement
  2. Connection with Circuit
  3. Wood miniature of the room in the game
  4. Test on several people to investigate the game’s difficulty (of the puzzle). => Anyone who sees this article, don’t be afraid to try out my game!! (It’s made to be very difficult to clear in a 5-minute time limit.)

Arduino & P5js communication exercise – Yunho & Kashyapa

EX1

The work didn’t get saved by mistake.

EX2

let serial; // variable to hold an instance of the serialport library
let portName = "COM3"; // fill in your serial port name here
let xPos=0;
let yPos=240;
let onOff=0;
let val;

function setup() {
  createCanvas(640, 480);
  serial = new p5.SerialPort(); // make a new instance of the serialport library
  serial.on("list", printList); // set a callback function for the serialport list event
  serial.on("connected", serverConnected); // callback for connecting to the server
  serial.on("open", portOpen); // callback for the port opening
  serial.on("data", serialEvent); // callback for when new data arrives
  serial.on("error", serialError); // callback for errors
  serial.on("close", portClose); // callback for the port closing

  serial.list(); // list the serial ports
  serial.open(portName); // open a serial port
}

function draw() {
  background(255);
  val = map(mouseX, 0, width, 0, 255);
}

// get the list of ports:
function printList(portList) {
  // portList is an array of serial port names
  for (let i = 0; i < portList.length; i++) {
    // Display the list the console:
    print(i + " " + portList[i]);
  }
}

function serverConnected() {
  print("connected to server.");
}

function portOpen() {
  print("the serial port opened.");
}

function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  let inString = serial.readLine();
  serial.write(val);
}

function serialError(err) {
  print("Something went wrong with the serial port. " + err);
}

function portClose() {
  print("The serial port closed.");
}

/*
Arduino code
void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  pinMode(5, OUTPUT);
  while (Serial.available() <= 0) {
    Serial.println("0,0"); // send a starting message
    delay(300);              // wait 1/3 second
  }
}
void loop() {
  while (Serial.available() > 0) {
    // read the incoming byte:
    int inByte = Serial.read();
    analogWrite(5,inByte);
    Serial.println();
  }
}
*/

 

EX3

let serial; // variable to hold an instance of the serialport library
let portName = "COM7"; // fill in your serial port name here

let velocity;
let gravity;
let position;
let acceleration;
let wind;
let drag = 0.99;
let mass = 50;
let windVal;

function setup() {
  createCanvas(640, 360);
  noFill();
  position = createVector(width/2, 0);
  velocity = createVector(0,0);
  acceleration = createVector(0,0);
  gravity = createVector(0, 0.5*mass);
  wind = createVector(0,0);
  
  serial = new p5.SerialPort(); // make a new instance of the serialport library
  serial.on("list", printList); // set a callback function for the serialport list event
  serial.on("connected", serverConnected); // callback for connecting to the server
  serial.on("open", portOpen); // callback for the port opening
  serial.on("data", serialEvent); // callback for when new data arrives
  serial.on("error", serialError); // callback for errors
  serial.on("close", portClose); // callback for the port closing

  serial.list(); // list the serial ports
  serial.open(portName); // open a serial port
}

// get the list of ports:
function printList(portList) {
  // portList is an array of serial port names
  for (let i = 0; i < portList.length; i++) {
    // Display the list the console:
    print(i + " " + portList[i]);
  }
}

function serverConnected() {
  print("connected to server.");
}

function portOpen() {
  print("the serial port opened.");
}

function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  let inString = serial.readLine();
  print(inString);
  if (inString.length > 0){
    windVal = map(inString, 0, 1023, -3, 3);
  }
}

function serialError(err) {
  print("Something went wrong with the serial port. " + err);
}

function portClose() {
  print("The serial port closed.");
}

function draw() {
  background(255);
  
  wind.x = windVal;
  
  applyForce(wind);
  applyForce(gravity);
  velocity.add(acceleration);
  velocity.mult(drag);
  position.add(velocity);
  acceleration.mult(0);
  ellipse(position.x,position.y,mass,mass);
  
  if (position.y > height-mass/2 - 25){
    serial.write(255);
  }
  else serial.write(0);
  
  if (position.y > height-mass/2) {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      position.y = height-mass/2;
    }
}

function applyForce(force){
  // Newton's 2nd law: F = M * A
  // or A = F / M
  let f = p5.Vector.div(force, mass);
  acceleration.add(f);
}

function keyPressed(){
  if (keyCode==LEFT_ARROW){
    wind.x=-1;
  } 
  if (keyCode==RIGHT_ARROW){
    wind.x=1;
  }
  if (key==' '){
    mass=random(15,80);
    position.x=width/2;
    position.y=-mass;
    velocity.mult(0);
  }
}

/*
Arduino code
void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  while (Serial.available() <= 0) {
    Serial.println("0"); // send a starting message
    delay(300);              // wait 1/3 second
  }
}
void loop() {
  while (Serial.available() > 0) {
    // read the incoming byte:
    int inByte = Serial.read();
    analogWrite(2, inByte);
    int WindPower = analogRead(A0);
    Serial.println(WindPower);
    
  }
}
*/

 

Yunho Lee – Final Project Idea

I am planning to make a game that can be controlled through joysticks, sensors, and buttons. My final goal is to make an RPG type of map where characters can move around the map, farm items, and use those items to solve a specific puzzle. Solving the puzzle will require interaction with the sensors (for example, blows off dust by blowing at the sound sensor) The main goal of the game will be to escape from a room, by finding all the clues and items.

It will require several OOP for stages, characters,  items, UIs, and so on.

I hope I can make a “save” function, but I am not sure if I will have time to accomplish that.

Dev & Yunho Reading Questions – Design meets Disabilities

  1. Think of an example of an invisible design.
  2. Think of an example of a positive design.
  3.  Some people might not want their disabilities to be shown to other people. Will these designs also be helpful for them?
  4. Do you think it is appropriate for a single design to accommodate everyone with a particular disability?
  5. What might be the effects of designs on inclusion if we keep making more emphasis on simplicity?

Yunho Lee Analog&Digital Switch – Christmas Tree Decoration

Concept and Inspiration

The inspiration for the project is Christmas tree decorations that have LED light bulbs blinking to make the tree look pretty.

There are one analog and one digital input that the circuit includes.

Digital – The red digital button switch chooses the blinking mode of the LED lights. (1 – off, 2 – on, 3 – blinks)

Analog – The potentiometer’s input decides the speed of the blinking of the LED lights and also the speed of the carol being played from the speaker.

How I implemented the Carol to be played in the speaker – First, I made an array of melodies where “0” indicates MUTE and from 1 to 11 each means a note. As time passes, the index will move on to the next element in the melodies array, playing the music as it is written in the melodies array.

The code I used is inserted below.

  • I have bad connections with the blue lightbulbs, so it sometimes turns off in the video.
  • I was going to make mode #4 that automatically turns the light bulb on and off depending on the light in the room, but I think I broke my light sensor while bending it, and was not able to make it happen.
#include "pitches.h"

int melody[] = {
  5,5,5,6,5,5,3,3,3,0,0,0,5,5,5,6,5,5,3,3,3,0,0,0,9,9,0,0,9,9,7,7,7,0,0,0,8,8,0,0,8,8,5,5,5,0,0,0,6,6,0,0,6,6,8,8,7,7,6,6,5,5,5,6,5,5,3,3,3,0,0,0,6,6,0,0,6,6,8,8,7,7,6,6,5,5,5,6,5,5,3,3,3,0,0,0,9,9,0,0,9,9,11,11,9,9,7,7,8,8,8,8,0,0,10,10,10,10,0,0,8,8,5,5,3,3,5,5,5,4,2,2,1,1,1,1,1,1,0,0,0,0,0,0
};

int notes[] = {
  NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_F5
};

byte prevButtonState = LOW;
byte patternNum = 0;
unsigned long timer = 0;
unsigned long timer2 = 0;
unsigned int index = 0;

bool redOn = LOW;
bool yellowOn = LOW;
bool greenOn = LOW;
bool blueOn = LOW;

void setup() {
  pinMode(2, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(12, INPUT);
  Serial.begin(9600);
}

void loop() {
  byte buttonState  = digitalRead(12);

  int lightVal = analogRead(A0);

  int mappedPotValue = map(lightVal, 0, 1023, 150, 500);
  Serial.println(mappedPotValue);
  
  if (buttonState == HIGH && prevButtonState == LOW) {
    if (patternNum<2){
      patternNum++;
    }
    else patternNum = 0;
  }
  prevButtonState = buttonState;

// Music play code
  if (millis() > timer) {
    timer = millis() + mappedPotValue;
    if (melody[index] != 0){
      tone(5, notes[melody[index]-1], mappedPotValue+20);
    }
    else noTone(5);
    index++;
    if (index == 144) index = 0;
  }
// Light bulbs Control
  if (patternNum == 0){
    redOn = LOW;
    yellowOn = LOW;
    greenOn = LOW;
    blueOn = LOW;
  }
  else if (patternNum == 1){
    redOn = HIGH;
    yellowOn = HIGH;
    greenOn = HIGH;
    blueOn = HIGH;
  }
  else if (patternNum == 2){
    if (millis() > timer2) {
      timer2 = millis() + mappedPotValue;
      redOn = !redOn;
      yellowOn = !yellowOn;
      greenOn = !greenOn;
      blueOn = !blueOn;
    }
  }
  else {
    redOn = LOW;
    yellowOn = LOW;
    greenOn = LOW;
    blueOn = LOW;
  }

  digitalWrite(8, redOn);
  digitalWrite(7, yellowOn);
  digitalWrite(4, greenOn);
  digitalWrite(2, blueOn);
}

 

Yunho Lee Hand-Free Switch Assignment

Concept and how the switch operates

Concept: In most of the human living places in the United Arab Emirates, seawater is one of the main sources of daily water. I designed the switch so that it can let the user know when the sea water level inside the water tank is above a certain point.

How the Switch operates: Saltwater has Na+ (Sodium ions) and Cl- (Chloride ions) that help the water conduct electricity. Therefore, the two metal rods connect the two sides of the circuit through the saltwater’s conductivity, turning the LED on when the water level rises enough to hit both rods.

Yunho Lee Midterm Project – Golem Hunters

Game theme and Concept

  1. Genre – First Person Shooter (FPS) game
  2. The main goal of the game – get a higher score by shooting as much as golems as possible using the limited amount of bullets provided

Implementation

  1. Structure of the overall program
    1. Setup()
      1. set variables to avoid unexpected bugs
      2. set frameRate to 60 to make sure all computers display the same result
      3. Divide the PNG file into frames of each sprite (Gun, Golems)
      4. Create a pool of Golems that respawn when killed
    2. mousePressed()
      1. Pause Button
      2. Resume Button
      3. Click to Shoot – Only when you have enough Ammo
      4. Start Button
    3. draw()
      1. make objects move according to the movement of the mouse cursor
      2. display Golems
      3. Display Cross in the middle of the screen
      4. Display PauseUI / StartUI when the game is paused/starts
    4. GunClass
      1. display() – display the sprite images by frames
      2. move() – Shooting motion when the mouse is clicked
    5. Golem
      1. display() – display the sprite image by frames
      2. alive() – the movement of golem when it is moving across the screen
      3. deadMotion() – movement of golem when it is shot
      4. respawn() – waits for a few frames and resets itself in a random position
  2. Techniques Used
    1. Instead of adding an extra golem whenever a golem is killed, I decided to reset the golem that is killed so that there is less use of data and reduce the amount of trash data by recycling resources. – Advantage of OOP.
    2. By transposing the golems the same amount as the movement of the background image, the game gives a strong conception of being in the middle of the scene, looking at the screen from the first-person’s point of view.

Here below is my final version of the midterm project game! Enjoy!

(Recommended to Play in Fullscreen Version)

https://editor.p5js.org/yl4643/full/Rr46vntt5

 

Yunho Lee Midterm Progress

As a midterm project, I decided to make an FPS (First-person shooting) game. This is an upgraded version of the OOP assignment.

In order to make a plan of overall programming, I decided to write down the algorithms needed for each object in the game.

Algorithm

 

Class Zombie – create an array of zombies

Spawn() – spawn a zombie in a random position on the screen

hitbox() – give a range of hitbox

Die() – when shoot() is called clear all zombies that includes (middle, middle) coordinates inside their hitbox & displays death frames from the sprite

Walk() – zombies walk around the map with random speed & random distance in X-direction & Takes frames from sprite

 

Class GunClass – shoots when clicked

drawBullet() & fly() – draws a bullet and make it fly to the shooting direction

shoot() – displays shooting motion of the gun sprite when it is clicked

 

Class UI – shows the score and the time left of the game

Timeover() – stops the game and shows the UI that shows the score and restart button

Restart() – restarts the game

Pause() – pauses the game

Resume() – resumes the game

 

I have progressed until implementing the shooting motion of the gun and will be adding the zombies soon. The prototype is below.

Yunho Lee Text Visualization Assignment – Text Rain

My first intention was to display simultaneously the input by users to the screen. The task could be solved by referring to the materials from the class of the example of creating a “Letter” Object and adding an extra “Letter” object in the Array of Letters. The result is the following.

function keyTyped() {
let lettercheck = key;
if (lettercheck.length == 1){
myString += lettercheck;
mycharArr = [];
let x=25;
let y=80;
for (let i=0; i<myString.length; i++){
if(x >= 560) {
x = 25;
y += 25;
}
mychar = new letter(x,y,myString[i])
mycharArr.push(mychar);
x += 15;
}
}

The next step was to make letters fall when the screen is clicked. I decided to do this by adding a boolean in letters to change to true when the screen is clicked.

function mousePressed(){
for (let i=0; i<myString.length; i++){
mycharArr[i].isfall = true;
}
}

I included BACKSPACE to delete the last input made by users, Enter to reset the position of the letters after falling, and TAB to clear the screen and reset the input by the user.

The final result is the following.