Week 13: Final Project Documentation & User Testing

Concept

Fruitfall Frenzy is an entertaining and straightforward catching game where players aim to accumulate points by catching fruits while avoiding junk food to prevent point deductions. To achieve victory, players must collect fruits and reach a score of 15 or more. However, failing to do so and catching 10 junk foods with a score of 0 results in a loss for the player.

link to the game: https://editor.p5js.org/Javeria/sketches/e-4vC95i_

Implementation

In terms of interaction, I’ve incorporated two buttons on the breadboard to facilitate the control of the basket’s left and right movements. The Arduino integration is seamlessly implemented, with the readSerial() function acting as the communication bridge between p5.js and Arduino. The Arduino code is designed to read the status of two switches and relay these values to p5.js. On the other hand, p5.js takes charge of displaying the game graphics, playing sounds, and managing the overall game logic.

p5js Code:

let basket;
let fruits = [];
let junkFoods = [];
let score = 0;
let gameOver = false;
let startPage;
let front;
let instructions;
let gifImage;
let win;
let lose;
let junkFoodsCaught = 0;
let gameStarted = false;
let instructionMode = false;
let customFont;
let gameWon = false;
let switch1State, switch2State;
let startmusic;
let winmusic;
let lossmusic;
let gamemusic;
let fruitmusic;
let junkfoodmusic;
let currentMusic;

let basketImg, backgroundImage;
let fruitImages = [];
let junkFoodImages = [];

function preload() {
  customFont = loadFont('Sunday Happy.ttf');
  startPage = loadImage('1.png');
  instructions = loadImage('2.png');
  basketImg = loadImage('basket.png');
  backgroundImage = loadImage('background.png');
  win = loadImage('win.png'); // Add this line
  lose = loadImage('lose.gif'); // Add this line
  startmusic = loadSound ('start.mp3');
  winmusic  = loadSound ('win.mp3');
  lossmusic  = loadSound ('loss.mp3');
  gamemusic  = loadSound ('game.mp3');
  fruitmusic  = loadSound ('fruit.mp3');
  junkfoodmusic  = loadSound ('junkfood.mp3');

  // Load different fruit images
  for (let i = 1; i <= 5; i++) {
    fruitImages.push(loadImage('fruit' + i + '.png'));
  }

  // Load different junk food images
  for (let i = 1; i <= 2; i++) {
    junkFoodImages.push(loadImage('junkfood' + i + '.png'));
  }
}

function setup() {
  createCanvas(620, 650);
  setupGame();
  currentMusic = startmusic;
  currentMusic.play();
}

function setupGame() {
  basket = new Basket(basketImg);

  // Limit the initial number of falling elements
  const initialFruitsCount = 2; // Adjust this value as needed
  const initialJunkFoodsCount = 2; // Adjust this value as needed

  for (let i = 0; i < initialFruitsCount; i++) {
    fruits.push(createRandomFruit());
  }

  for (let i = 0; i < initialJunkFoodsCount; i++) {
    junkFoods.push(createRandomJunkFood());
  }
}

function drawGame() {
  // Draw background image
  image(backgroundImage, 0, 0, width, height);

  basket.display();
  basket.move();

  // Add new fruits and junk foods based on certain conditions
  if (frameCount % 60 === 0 && fruits.length + junkFoods.length < 5) {
    // Check if there's enough space for a new fruit
    if (fruits.length < 2) {
      fruits.push(createRandomFruit());
    }

    // Check if there's enough space for a new junk food
    if (junkFoods.length < 2) {
      junkFoods.push(createRandomJunkFood());
    }
  } }
 //<-- Add this closing bracket

function draw() {
  background(250);

  if (gameWon) {
    // Player wins
    currentMusic.stop(); // Stop the current music
    winmusic.play();    // Play the win music
    currentMusic = winmusic;
    drawWinPage();
     currentMusic.stop(); // Stop the current music
    winmusic.play();    // Play the win music
    currentMusic = winmusic; // Update currentMusic
  } else if (gameOver) {
    // Player loses
    drawLosePage();
    currentMusic.stop(); // Stop the current music
    lossmusic.play();   // Play the lose music
    currentMusic = lossmusic; // Update currentMusic
  } else {
    if (instructionMode) {
      // Display instructions
      image(instructions, 0, 0, width, height);
      fill(255, 204, 0, 180);
      noStroke();
      rect(535, 524, 70, 35);
      fill(0);
      textSize(30);
      textFont(customFont);
      text("Back", 542, 549);
       if (currentMusic !== startmusic) {
        currentMusic.stop(); // Stop the current music if it's not start music
        startmusic.play();  // Play the start music
        currentMusic = startmusic; // Update currentMusic
      }

      
    } else {
      // Display the start page
      image(startPage, 0, 0, width, height);
      fill(255, 204, 0, 180);
      noStroke();
      rect(228, 335, 150, 35);
      fill(0);
      textSize(30);
      textFont(customFont);
      text("Start Game", 237, 359);
      fill(255, 204, 10, 180);
      noStroke();
      rect(227, 391, 150, 35);
      fill(0);
      textSize(30);
      textFont(customFont);
      text("Instructions", 235, 417);
      

      // Draw game elements if the game has started
      if (gameStarted) {
        drawGame();
         if (currentMusic !== gamemusic) {
          currentMusic.stop(); // Stop the current music if it's not game music
          gamemusic.play();   // Play the game music
          currentMusic = gamemusic; // Update currentMusic
      }
    }
  }
}
}



function drawWinPage() {
  // Draw the win background image
  image(win, 0, 0, width, height);
  fill(255, 204, 0, 180);
      noStroke();
      rect(247, 266, 140, 35);
      fill(0);
      textSize(30);
      textFont(customFont);
      text("Restart", 272, 290);
  
  
  if (mouseIsPressed && mouseX > 247 && mouseX < 387 && mouseY > 266 && mouseY < 301){
    
  
    restartGame();
   }
  
}

function drawLosePage() {
  // Draw the lose background image
  image(lose, 0, 0, width, height);
  fill(255, 204, 0, 180);
      noStroke();
      rect(247, 592, 150, 35);
      fill(0);
      textSize(30);
      textFont(customFont);
      text("Restart", 280, 616);
  
  
   if (mouseIsPressed && mouseX > 247 && mouseX < 397 && mouseY > 592 && mouseY < 627) {
    restartGame();
   }
  

  
  
}
function restartGame() {
  gameOver = false;
  gameStarted = false;
  score = 0;
  junkFoodsCaught = 0;
  gameWon = false;
  setupGame();
}

function mousePressed() {
  if (!gameStarted) {
    if (instructionMode) {
      // Handle mouse click to return to the main menu
      if (mouseX > 535 && mouseX < 605 && mouseY > 524 && mouseY < 559) {
        instructionMode = false;
      }
    } else {
      if (mouseX > 228 && mouseX < 378) {
        if (mouseY > 335 && mouseY < 370) {
          // Start the game
          gameStarted = true;
          setupGame(); // Call setupGame when the game starts
        } else if (mouseY > 391 && mouseY < 426) {
          // Show instructions
          instructionMode = true;
        }
      }
    }
  }
}


class Basket {
  constructor(img) {
    this.width = 200;
    this.height = 150;
    this.x = width / 2 - this.width / 2;
    this.y = height - this.height - 10;
    this.img = img;
  }

  display() {
    image(this.img, this.x, this.y, this.width, this.height);
  }

  move() {
    if (keyIsDown(LEFT_ARROW) && this.x > 0) {
      this.x -= 5;
    }
    if (keyIsDown(RIGHT_ARROW) && this.x < width - this.width) {
      this.x += 5;
    }
  }

  moveLeft() {
    if (this.x > 0) {
      this.x -= 5;
    }
  }

  moveRight() {
    if (this.x < width - this.width) {
      this.x += 5;
    }
  }
}


class Fruit {
  constructor(img) {
    this.x = random(width);
    this.y = 0;
    this.diameter = 120;
    this.img = img;
  }

  display() {
    image(this.img, this.x - this.diameter / 2, this.y - this.diameter / 2, this.diameter, this.diameter);
  }

  fall() {
    this.y += 5;
  }

  intersects(basket) {
    let halfBasket = basket.width / 2;
    return (
      this.x > basket.x - halfBasket &&
      this.x < basket.x + basket.width + halfBasket &&
      this.y > basket.y &&
      this.y < basket.y + basket.height
    );
  }

  intersectsBasket(basket) {
    let halfBasket = basket.width / 2;
    return (
      this.x > basket.x - halfBasket &&
      this.x < basket.x + basket.width + halfBasket &&
      this.y + this.diameter / 2 > basket.y &&
      this.y - this.diameter / 2 < basket.y + basket.height
    );
  }
}


class JunkFood extends Fruit {
  constructor(img) {
    super(img);
    this.diameter = 60;
   
  }

  // Override the intersectsBasket method
  intersectsBasket(basket) {
    let halfBasket = basket.width / 2;
    return (
      this.x > basket.x - halfBasket &&
      this.x < basket.x + basket.width + halfBasket &&
      this.y + this.diameter / 2 > basket.y &&
      this.y - this.diameter / 2 < basket.y + basket.height
    );
  }
}

function drawGame() {
  // Draw background image
  image(backgroundImage, 0, 0, width, height);

  basket.display();
  basket.move();

  if (frameCount % 60 === 0) {
    fruits.push(createRandomFruit());
    junkFoods.push(createRandomJunkFood());
  }

  for (let i = fruits.length - 1; i >= 0; i--) {
    fruits[i].display();
    fruits[i].fall();

    if (fruits[i].intersects(basket)) {
      score += 1;
      fruits.splice(i, 1);
    } else if (fruits[i].y > height) {
      fruits.splice(i, 1);
    }
  }

  for (let i = junkFoods.length - 1; i >= 0; i--) {
    junkFoods[i].display();
    junkFoods[i].fall();

    if (junkFoods[i].intersects(basket)) {
      score -= 1; // Deduct score if junk food touches the basket
      junkFoods.splice(i, 1);
    } else if (junkFoods[i].y > height) {
      junkFoods.splice(i, 1);
    }
  }

  textSize(20);
  fill(0);
  text("Score: " + score, 20, 30);

  // Check win condition
  if (score >= 10) {
    gameWon = true;
    drawWinPage();
  }

  // Check lose condition
  if (score < 0 || junkFoodsCaught >= 10) {
    gameOver = true;
    drawLosePage();
  }
}

function createRandomFruit() {
  let randomFruitImg = random(fruitImages);
  let fruit = new Fruit(randomFruitImg);

  for (let existingFruit of fruits) {
    while (fruit.intersects(existingFruit) || fruit.intersectsBasket(basket)) {
      fruit = new Fruit(randomFruitImg);
    }
  }

  return fruit;
}

function createRandomJunkFood() {
  let randomJunkFoodImg = random(junkFoodImages);
  let junkFood = new JunkFood(randomJunkFoodImg);

  for (let existingFruit of fruits) {
    while (junkFood.intersects(existingFruit) || junkFood.intersectsBasket(basket)) {
      junkFood = new JunkFood(randomJunkFoodImg);
    }
  }
  for (let existingJunkFood of junkFoods) {
    while (junkFood.intersects(existingJunkFood) || junkFood.intersectsBasket(basket)) {
      junkFood = new JunkFood(randomJunkFoodImg);
    }
  }

  return junkFood;
}
function keyPressed() {
  // Handle key presses (this function only detects if a key is pressed at the moment)
}
function readSerial(data) {
  ////////////////////////////////////
  //READ FROM ARDUINO HERE
  ////////////////////////////////////

  if (data != null) {
    // make sure there is actually a message
    // split the message
    let fromArduino = split(trim(data), ",");
    console.log(fromArduino);
    // if the right length, then proceed
    if (fromArduino.length == 2) {
      // only store values here
      // do everything with those values in the main draw loop
      
      // We take the string we get from Arduino and explicitly
      // convert it to a number by using int()
      // e.g. "103" becomes 103
      switch1State = int(fromArduino[0]);
      switch2State = int(fromArduino[1]);
      
      if (switch1State) {
        basket.moveLeft();
      } 
      
      if (switch2State) {
        basket.moveRight();
      }
    }

    //////////////////////////////////
    //SEND TO ARDUINO HERE (handshake)
    //////////////////////////////////
    let sendToArduino = "\n";
    writeSerial(sendToArduino);
  }
}


function keyPressed(){
  setUpSerial();
}

Arduino Code:

const int switch1Pin = 4;  
const int switch2Pin = 8;  

void setup() {
  Serial.begin(9600);
  pinMode(switch1Pin, INPUT_PULLUP);
  pinMode(switch2Pin, INPUT_PULLUP);
  

  while (Serial.available() <= 0 ){
    Serial.println("0,0");
    delay(300);
  }
}

void loop() {


  while(Serial.available()) {
    if (Serial.read() == '\n') {
      int switch1State = digitalRead(switch1Pin);
      delay(5);
      int switch2State = digitalRead(switch2Pin);
      
      Serial.print(switch1State);
      Serial.print(',');
      Serial.println(switch2State);
    }
  }


  
}

 

Future improvements

To enhance both the engagement and challenge levels of the game, I’m considering  incorporating additional features. These may include the gradual escalation of difficulty as the game progresses and the introduction of power-ups to provide players with unique advantages.

User Testing 

In the testing phase, I asked Nafiha to give my game a try. Impressively, she seamlessly utilized the buttons to navigate the basket without any hesitation. Her feedback highlighted that the controls were straightforward, requiring no additional instructions. Taking her suggestion into account, I modified the game dynamics by initially decreasing the number of falling objects and contemplating a gradual increase over time, resulting in a more balanced and enjoyable gameplay experience.

https://drive.google.com/file/d/1_36pcfyrhlTWxp_WqHmETfE6l8cfDicl/view?usp=sharing

 

Week 13 – Final Project Documentation

Concept

Reaction Game Based, or RGB, is a rhythm tool/game used to test your reaction time. Three randomized dots appear on the screen with two characteristics: number (1, 2, 3) and color (red, green, blue). Your objective is to hit each dot according to its assigned resistor, clearing the screen of all dots and obtaining the biggest score possible under the time limit of 40 seconds. After that, the user is presented with their score and their estimated age according to their reaction time.

Implementation

For the interaction, three force-sensitive resistors were used in order to hit the dots, along with a buzzer, which is responsible for playing a sound every time the user hits a resistor. As for the Arduino code, each input was declared, and the basic serial communication was established with P5.js, sending the data from each resistor. With each tap on the resistor, a sound is also played by the buzzer.

int fsrPin1 = A0;
int fsrPin2 = A1;
int fsrPin3 = A2;
int buzzerPin = 2;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int fsrValue1 = analogRead(fsrPin1);
  int fsrValue2 = analogRead(fsrPin2);
  int fsrValue3 = analogRead(fsrPin3);

  // send FSR values to p5.js
  Serial.print(fsrValue1);
  Serial.print(",");
  Serial.print(fsrValue2);
  Serial.print(",");
  Serial.println(fsrValue3);

  // force sensitive resistor hits
  checkHit(fsrValue1, 1);
  checkHit(fsrValue2, 2);
  checkHit(fsrValue3, 3);
}

void checkHit(int fsrValue, int noteNumber) {
  if (fsrValue > 130) { // FSR threshold to activate sound
    Serial.println("Hit detected!");

    Serial.print("Note,");
    Serial.println(noteNumber);

    // buzzer sound on tap
    tone(buzzerPin, 1000, 100); // frequency and duration
  }
}

As for P5.js, the most important variables are the ones that keep track of the player’s score and the notes. The function “checkHit” is also important to determine whether a note has been successfully hit based on the input from force-sensitive resistors (FSRs). It iterates through the FSR values and compares them to predefined thresholds. If the FSR value exceeds its threshold and the associated note is not marked as already hit, it signifies a successful hit. The function then updates the score, initiates visual effects on the ellipses like glowing, sets a cooldown for the corresponding FSR, and checks if all notes are hit. If all notes are hit, it triggers the generation of new notes. Regarding serial communication, a p5.web-serial.js file provided in class by Michael Ang and Aaron Sherwood is responsible for the communication between Arduino and p5.js.

function checkHit() {
  for (let i = 0; i < fsrValues.length; i++) {
    if (resistorCooldowns[i] <= 0) {
      for (let j = notes.length - 1; j >= 0; j--) {
        let note = notes[j];
        if (fsrValues[i] > fsrThresholds[i] && !note.hit) {
          let expectedNote = i + 1; // expected note based on resistor index
          if (note.number === expectedNote) {
            console.log("Hit detected for note " + expectedNote + "!");
            score += 10;
            glowingFrames = glowingDuration;
            note.hit = true;
            resistorCooldowns[i] = cooldownDuration; // set cooldown for the resistor
            if (allNotesHit()) {
              setTimeout(generateNotes, 1000); // wait for 1 second before generating new notes
            }
            break; // exit the loop once a hit is detected
          }
        }
      }
    }
  }

Future improvements

Future changes to the project would depend on different purposes that it could offer, be it simply a reaction time test, a game focused on fun, or maybe a combination of both. Nonetheless, I believe that more colors could be added, and possibly music. Also, I would have liked to see different modes and dynamics for the notes, testing the user in different ways. Other than that, I am proud of offering a fun yet quite useful experience for the user.

User testing

User testing was conducted with three different people, and it went generally well. Most of them managed to figure out the controls and objectives of the game pretty much instantly, and they had no difficulty in obtaining quite high scores despite no prior knowledge of the game.

However, in one instance the controls did not seem as obvious, and the participant struggled a little bit to figure out the resistor and the timer.

Overall, the experience is working well, and not a lot of explanation is needed. Something that could be improved would be maybe utilizing different buttons other than the force-sensitive resistors. The reason is that the force-sensitive resistors do not work perfectly. Maybe they look less obvious than three huge buttons would, and it is a bit difficult to work with the force thresholds. Have too much necessary force for them, and you have to smash the table in order to register a hit, but if you have too little, they will register even if you barely touch them. Making it more obvious that each resistor is assigned to a number would also help.

Week 11 – Arduino and P5.js

1 – Make something that uses only one sensor on Arduino and makes the ellipse in p5 move on the horizontal axis, in the middle of the screen, and nothing on arduino is controlled by p5

Code:

const int potPin = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int potValue = analogRead(potPin);

  // send the potentiometer value to the serial port
  Serial.println(potValue);

  delay(50);
}

2 – Make something that controls the LED brightness from p5

Code:

int ledPin = 6;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  while (Serial.available() > 0) {
    // read the incoming brightness value from p5.js
    int brightness = Serial.parseInt();

    // adjust the LED brightness based on the received value
    analogWrite(ledPin, brightness);
  }
}

3 – Take the gravity wind example and make it so every time the ball bounces one led lights up and then turns off, and you can control the wind from one analog sensor

int potPin = A0;
int ledPin = 6;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(potPin, INPUT);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, LOW);
  while (Serial.available()) {
    digitalWrite(LED_BUILTIN, HIGH);
    int ambientBrightness = Serial.parseInt();
    if (Serial.read() == '\n') {
      int sensorReading = analogRead(potPin);
      Serial.println(sensorReading);
    }
    if (ambientBrightness >= 350 && ambientBrightness <= 360) {
      digitalWrite(ledPin, HIGH);
    } else {
      digitalWrite(ledPin, LOW);
    }
  }
  int sensorReading = analogRead(potPin);
  Serial.println(sensorReading);
}

Week 12 – Finalized Project Concept

Concept

For my final project, I have decided to change my idea. Now, I have settled on a rhythm music game, akin to Guitar Hero. Using force sensitive resistors (FSR), the user will have to quickly press the sensors to match the combinations that are being shown on the screen, almost like drums but using your hands. The design of the game on P5.js will be minimalistic, but it will also contain many colors and bright lights when the sensors are pressed. The more the user progresses, the more points will be accumulated, until the game eventually ends and restarts. To add more replayability, the rhythm of the sensors will be random.

Arduino

The Arduino will contain force sensitive resistors for inputs to capture the user’s input for the game. Each sensor will have an analog input assigned to it, which I will be able to manipulate with code. A cardboard box will also most likely be used around the breadboard to deliver a more aesthetically pleasing experience. Below is a schematic with the sensors for the board:

P5.js

The P5 program will be responsible for displaying the game, including the score and the notes that are necessary to be hit. The music could also be played by P5 instead of a buzzer on the breadboard, but that still needs to be decided. In terms of code, P5 will receive the information from the sensors and know when to activate a hit or miss in the notes. Below is a low-fidelity drawing of the game:

 

Final Project Proposal: Feed Flynn

I. Introduction

“Feed Flynn” is an immersive and engaging arcade-style game designed to entertain and challenge players. This game combines intuitive arcade button controls with a visually appealing graphic design ( which I designed YALL) to create an enjoyable gaming experience.

Some pictures:

Figure 1: My little Retro Style Flynn

Figure 2: My Game logo

II. Project Overview

A. Concept

The game revolves around Flynn, the main character, navigating through a world filled with edible objects and obstacles. The primary objective is to maneuver Flynn using arcade buttons, collecting burgers and donuts for points while avoiding collision with books, which results in point deductions. Flynn also has the ability to shoot bullets to eliminate books, adding an element of strategy and focus to the gameplay.

B. Features

  1. Arcade-Style Controls: Utilize physical or on-screen arcade buttons to move Flynn left and right within the game environment.
  2. Score System: Accumulate points by collecting burgers and donuts while avoiding collisions with books. Each successful collection adds points, while collisions deduct points.
  3. Shooting Mechanism: Implement Flynn’s ability to shoot bullets to eliminate books, allowing players to strategically clear obstacles.

Final Project – Idea Finalization

In my last post, I’ve already shared most of what my  game may be and how it’ll work. However, there have been more adjustments and decisions made and I would like to share my final project idea.

I have finalized the idea as the bubble shooter game (shown in the image above). The required functions are simple- the player should be able to choose directions and shoot bubbles. So, I would like to clarify how I will be using different components of the arduino and its sensors.

My bubble shooter game will have a gauge, that will increase as the player manages to remove certain number of lines. I plan to show this on the LCD screen.

Once this gauge is full, the player will be able to punch on the force sensor, which will work as an item that will break additional 3-5 lines of bubbles.

The player will be able to adjust the direction of bubbles being shot with the potentiometer and the shot will be made with a push button.

As of now, getting the graphics on p5js is the biggest issue and the primary goal so I’ve been focusing on it. I plan to get the graphics done and work on the different parts of the arduino, and connect them.

final project – finalized concept

concept:

Because I already made a game for my midterm project, I wanted to make my final project more of an artistic immersive experience. Specifically, I wanted to build off of the musical instrument that we made recently for one of our assignments. Instead of an instrument, however, this will be more focused on being a soundscape, with dynamic visualizations. However, instead of musical notes, I want to use sounds. The user will generate sounds based on physical input. Essentially, I want the user to be able to switch between two soundscapes – day and night – which will be based on the light of the room. And then by using force sensors and/or pressing buttons, the user will be able to generate sound.

hardware:

a light sensor, force sensor, buttons

p5.js code:

I hope to use the p5.js that produces visualizers similar to  this and this, except based on user input from the Arduino.

arduino code:

The Arduino IDE will collect the data from the sensors, i.e. how much light in the room to define the colours, and the force sensors/buttons mapping to the sound being produced.

Final Project Progress #2

Finalized concept for the project:

As I was thinking about my idea (and as Spotify Wrapped came out), I remembered last year’s Spotify’s Wrapped element where the application generated a gradient based on the top music moods. Here is an image of what I am referring to:

Therefore, I decided to stick to the colors but to change the purpose and the idea of the game. Instead of making it a game, I want my project to be a personalized experience for the user. The users will be prompted to input colors that represent different emotions to them, such as happiness, sadness, anger, etc. The input will happen with the help of 3 potentiometers, where each potentiometer will represent Red, Green and Blue. After the input is done, a personal color or a gradient will be generated, that perhaps with a click of a button can be downloaded and then sent to the user’s personal device.

Arduino Program:

Arduino setup:

  • 3 potentiometers (I saw we have bigger potentiometers in the IM Lab, perhaps I could use those)
  • buttons (for progressing through the game and the download of the result)

Arduino Program:

The Arduino program will receive the values from the potentiometers and map them to the color range from 0 to 255. It will also receive the input from the buttons and send them to P5js.

P5 Program:

P5 will be responsible for displaying the correct screens and stages of the experience (such as in the midterm project). It will also be responsible for providing real-time feedback on the color being generated by receiving the values from potentiometers and Arduino. P5 will store the input colors and use them to generate the final result.

 

Final Project Proposal – Twisted Twister!

Board games have always captured my interest, and for my final project, I’ve decided to reimagine the classic multiplayer game, Twister, by transforming it into an engaging solo experience. In this adapted version, I aim to introduce interactive elements, to enhance the traditional Twister setup.

Game Components:

In the standard version of the game, participants spin a wheel to determine a color and a designated body part (hands or feet). Subsequently, players must position the indicated body part onto the corresponding colored circle on the Twister mat. The objective is to avoid losing balance, and if the player falls down, they lose. For the physical setup in my version, I plan to craft the Twister mat using acrylic sheets. Under each colored circle, I will integrate force sensors to detect whether a player has placed their palm or foot on the designated spot. This modification eliminates the need for multiple players while retaining the essence of the original game.

Spinning wheel

 

 

 

 

 

 

Digital Interface with p5js:

Utilizing the p5js interface, I intend to create a virtual spinner that dictates the next move for the player. p5js will not only control the wheel spinning but also manage the game score and determine win/loss conditions and the start & end of the game.

Additional Gameplay Elements:

To introduce a new challenge, I plan to incorporate ultrasonic sensors. In the classic game there is one option on the wheel that if landed on, the users have to keep their body part in the air. In this version, When the wheel lands on a specific option, players must extend their hands or feet to a particular distance, as detected by the sensors. 

Wheel Spinning Mechanism: Considering the physical nature of the game that actively uses the player’s hands, I’m exploring two potential options for spinning the wheel through p5js instead of directly clicking it on the laptop. One approach involves a switch on the Twister mat that, when pressed, triggers the p5js interface to spin the wheel. Alternatively, I’m considering a voice-activated mechanism where saying the word ‘spin’ initiates the wheel rotation.

Twister mat with acrylic sheets

Arduino Circuit: The Arduino circuit will be straightforward, connecting switches and force sensors to the Twister mat. However, I’m intrigued by the possibility of incorporating flex sensors/motors to introduce more complexity and fun to the game. These additions could potentially respond to the player’s movements, making the experience even more interactive.

Considerations:

While developing the game, I recognize the need to redefine the losing condition for a single-player setup. Unlike the traditional game where a player falls to signify defeat, I aim to explore alternative practical criteria that maintain the challenge and excitement without relying on physical falls in a solo context. By combining physical and digital elements, along with creative twists to the gameplay, I hope to transform Twister into a captivating and entertaining game experience.

Week 12: Final Project Proposal

Finalized Concept:

For my final project of the semester, I want to incorporate my love for music, inspired by an idea I had worked on before in a different class last semester. In Making Music, we had an assignment to create a drum kit out of “everyday” sounds. It was really interesting to listen to music tracks created purely out of “everyday” sounds. For my project I want to create an interactive drum kit, where the user presses on a button to choose one of the multiple themes available. Then the user can use their fingers to hit the “drum plates”, which are going to be placed on force sensors. Depending on which theme they are on, they would get different sounds from each plate.I will include one regular drum kit and multiple unusual ones. Additionally, I want to include a feature where a user can record up to 10 seconds of music as well. When the recording is complete it is attached to previous users’ recordings so that at the end of the night, I have a recording of different people playing different drum kits each using their own style. I also would like to include a feature that allows users to listen to the cumulative recording at any point. My idea is to mix everyday sounds, to sound like unusual drum kits, and allow users to improvise for a bit and mashup their improvisation later on to have a piece of music that is reflective of our different personalities. My idea is to implement it as a virtual studio, each room in the studio has a different theme and a different drum kit.

Input from Arduino: Force sensors, button

Output from Arduino: RGB LED that displays different colors depending on what theme they are on

Arduino code:

The Arduino code will read values from the sensors and send them to p5js, it will also change the color of the RGB LED by receiving a value from p5js and acting accordingly

Input from P5js: Space bar to record a short performance, if possible. Enter to return to homepage.

Output from P5js: Sounds, screen display

P5js code:

The P5js code will control the display of the themes by reading the switch values sent from the Arduino. It will also trigger sound files according to the data sent from the force sensors by the Arduino. Additionally, it would control the recording process and the playback of the recordings.

Schematic: 

Arduino schematic

This is the initial design of my home p5js screen, but I have not designed the other themed screens yet, so I am not sure if I would stick with the same vibe for my project. I might change it if necessary when I am implementing the other screens as well:

Homepage initial design