Final Project: Darkness Rhapsody – Qianyue Fan – Eric

The complete version is here: https://drive.google.com/open?id=1PJePD4xGHVjOKV4HEffk7oLUqKAOKHSd

Conception and Design:

Our final project is an interactive horror game, where users find their way out in a dark maze by pressing a button to hear the melody that shows the correct direction, and then move around with arrow keys. If the player is moving in the correct direction, the volume of the melody would go up and vice versa. Besides, there are monsters in the maze making finger-snapping sound, which works the same as the melodies. The player gets a jump scare if going the wrong way. In the beginning, we used only the keyboard for input, but later we changed the “L” key for “listen” into a big button connected to the computer controlled by Arduino. In that way, the user can use both hands together which improves operability.  Also, the button is placed in a wooden box with a religious symbol that stands for sound, which makes it more interesting and mysterious.

button and its box:

 

We thought about making the button into a necklace-like design so that it would look like an amulet and give users a more immersive experience, However, the button turned out to be oversized for an amulet. To match our title which is a parody of Bohemian Rhapsody, all of the melodies come from Queen songs. 

Fabrication and Production:

We expected the game to be calming and relaxing before, but we soon found that the unknown things behind the darkness make it quite a good choice to be a horror game.

 

The main point is on coding. It took me several days to learn creating customized functions and integrate them in a logical structure by myself.

tabs used

 We started from designing a maze map with several turnings and areas where monsters hide.

At first, I wrote functions to keep the player outside certain areas without using the picture of the map. But later I realized that I could let the program detect the color of the pixels nearby to form obstacles instead of making the maze all over again. For monsters, I still needed to mention their position because no one would bump into them if the areas were colored.

To edit the sound effect and make them work as expected, I drew boundaries on the map and fit the melodies and monster sound into the program one by one.

boudaries

I learned how to have stereo sound and 3D audio effect before these knowledges were taught in class. Since the effect of Processing library was not satisfying enough, I also used software like Adobe Audition and FL Studio to have a better listening experience. For the melodies from Queen, I either recorded some solo parts or played them with online virtual piano. As for the finger-snapping sound, it comes from the beginning of the song Killer Queen.

The whole process was sometimes scary, for I needed to test the trigger areas of the monsters and often got a surprise jump scare. I drew the inspiration of making jump scare from the game Five Nights at Freddy’s and used the designs of animatronics from the game, which turned out quite entertaining and effective.

What happens when you go the wrong way:

During the user test, almost everyone spoke highly of our project, and many of them were scared out of their wits. By the way, the horror effect worked as well in the IMA Show.

A student gets scared by the project on IMA Show:

After the user test, we added a tutorial in the hope that it would help users learn about the basic game controls. However, we ignored that we were actually explaining too much during the user test and thus overlooked the vagueness of game instructions. The tutorial itself was in fact not easy to understand, and people tended to skip instruction texts. We could have made a more detailed tutorial containing voice instructions and simpler maze design, but we did not make it due to the lack of time.

Conclusions:

The goal of our project was purely to scare horror-game lovers as much as possible to entertain them. It is also great fun to watch others play and gets scared. When you are highly focused, it is easy to scare you by having something weird jumping out of the screen, even if the thing itself is not quite terrifying. As a game with a lot of input, calculation and decision-making, it is a good match for my definition of interaction. The users need to pay a lot of attention and can be extremely nervous when moving around or standing at a crossroad, which shows the charm of a horror game. The only regret was the inefficiency of the tutorial. But after all, I made an amazing horror game that scares people and devoted a lot of time and effort to polish it up, using songs from the favourite band to make it more interesting. I hope I could learn more about game design to make the project a real work of art that attracts everyone and is easy to learn about.

code:

Arduino:

Processing:

Main Strcture:

import processing.serial.*;

Serial myPort;
int val;

import processing.sound.*;
SoundFile file1;
SoundFile file2;
SoundFile file3;
SoundFile file4;
SoundFile file5;
SoundFile file6;
SoundFile file7;
SoundFile win;
SoundFile mons;
SoundFile scream;
SoundFile tut1;
SoundFile tut2;
SoundFile tut3;

int sentinel = 0;
int rad = 10;
int PosX= rad;
int PosY = 750 – rad;
int PosXt= 720;
int PosYt = 770;
float speed = 4;
color b = color(0);
PImage map;
PImage map2;
PImage tardis;
PImage freddy;
PImage chicken;
PImage spring;
PImage foxy;
PImage toyBonnie;
PImage BB;
float size = 0.6;

void setup() {
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[ 2 ], 9600);

size(1440, 790);
map = loadImage(“map.jpg”);
map2 = loadImage(“map2.jpg”);
tardis = loadImage(“tardis.jpg”);
freddy = loadImage(“freddy.jpg”);
chicken = loadImage(“chicken.jpg”);
spring = loadImage(“springtrap.jpg”);
foxy = loadImage(“foxy.jpg”);
toyBonnie = loadImage(“toyBonnie.jpg”);
BB = loadImage(“BB.jpg”);
file1 = new SoundFile(this, “sent1.aif”);
file2 = new SoundFile(this, “sent2.aif”);
file3 = new SoundFile(this, “sent3.aif”);
file4 = new SoundFile(this, “sent4.aif”);
file5 = new SoundFile(this, “sent5.aif”);
file6 = new SoundFile(this, “sent6.wav”);
file7 = new SoundFile(this, “sent7.wav”);
win = new SoundFile(this, “Seven Seas Of Rhye.wav”);
mons = new SoundFile(this, “knock.aif”);
scream = new SoundFile(this, “scream.wav”);
tut1 = new SoundFile(this, “breakFree.wav”);
tut2 = new SoundFile(this, “underPressure.wav”);
tut3 = new SoundFile(this, “noStop.wav”);
}

void draw() {
if (sentinel == 0) {
initScreen();
} else if (sentinel == 1) {
gameScreen();
} else if (sentinel == 2) {
bearjump();
} else if (sentinel == 3) {
chickenjump();
} else if (sentinel == 4) {
springtrap();
} else if (sentinel == 5) {
foxjump();
} else if (sentinel == 6) {
gameOverScreen();
} else if (sentinel == 7) {
winningScreen();
} else if (sentinel == 8) {
warnScreen();
} else if (sentinel == 9) {
infoScreen();
} else if (sentinel == 10) {
tutorial();
} else if (sentinel == 11) {
rabbitjump();
} else if (sentinel == 12) {
bbjump();
} else if (sentinel == 13) {
TutOverScreen();
} else if (sentinel == 14) {
TutContinue();
}
}

void initScreen() {
background(0, 0, 0);
textAlign(CENTER);
fill(#F5453B);
textSize(70);
text(“Darkness Rhapsody”, width/2, height/2);
textSize(15);
text(“Click to start”, width/2, height-30);
}

void warnScreen() {
background(0, 0, 0);
textAlign(CENTER);
fill(#F5453B);
textSize(70);
text(“Warning:”, width/2, height/3);
textSize(50);
text(“This game contains jumpscares that”, width/2, height/3 + 60);
text(“some may find disturbing.”, width/2, height/3 + 120);
textSize(30);
text(“Click to go on”, width/2, height/3 + 200);
}

void infoScreen() {
background(255);
textAlign(CORNER);
fill(#CB7C2B);
textSize(40);
text(“How to play:”, width/10, height/4);
text(“Use arrowkeys to move;”, width/10, height/4+70);
text(“The music shows the direction of the exit,”, width/10, height/4+130);
text(“Press the button to listen.”, width/10, height/4+190);
text(“Monsters make finger-snapping sound.”, width/10, height/4+260);
text(“Stay away from them.”, width/10, height/4+320);
textAlign(CENTER);
text(“Click to continue”, width/2, height/4+400);
}

void gameScreen() {
loadPixels();
map.loadPixels();
flashlight();
fill(0);
direction();
musicsound();
ellipse(PosX, PosY, rad*2, rad*2);
monsterSound();
decide();
}

void gameOverScreen() {
background(0);
textAlign(CENTER);
fill(255);
textSize(30);
text(“Game Over”, height/2, width/2 – 20);
textSize(15);
text(“Click to Restart”, height/2, width/2 + 10);
}

void winningScreen() {
winning();
background(255);
textAlign(CENTER);
fill(0);
textSize(30);
text(“Congratulations! You’ve found your way out!”, width/2, height/2);
textSize(15);
text(“Click to go back”, width/2, height/2 + 40);
}

void tutorial() {
loadPixels();
map2.loadPixels();
flashlightT();
fill(0);
directionT();
tutorialSound();
ellipse(PosXt, PosYt, rad*2, rad*2);
tuMonSound();
tuDecide();
tuText();
}

void TutOverScreen() {
background(255);
textAlign(CENTER);
fill(#F5453B);
textSize(70);
text(“Told you not to go there…”, width/2, height/2);
textSize(15);
text(“Click to restart”, width/2, height-30);
}

void TutContinue() {
background(255);
textAlign(CORNER);
fill(#CB7C2B);
textSize(40);
text(“Great! You’ve finished the tutorial.”, width/10, height/4);
text(“Now let’s keep moving.”, width/10, height/4+70);
text(“Click to continue”, width/2, height/4+400);
}

void mouseClicked() {
if (sentinel == 0) {
sentinel = 8;
} else if (sentinel == 6 ||sentinel == 7 || sentinel == 13) {
win.stop();
sentinel = 0;
PosX= rad;
PosY = 750 – rad;
PosXt = 720;
PosYt = 770;
} else if (sentinel == 8) {
sentinel = 9;
} else if (sentinel == 9) {
sentinel = 10;
} else if (sentinel == 10) {
sentinel = 14;
}else if (sentinel == 14) {
sentinel = 1;
}
}

Change between Game Stages:

void decide(){
if (PosX >= 120 && PosY >= 650 && PosX <= 300){
sentinel = 2;
}
else if (PosX >= 0 && PosX <= 100 && PosY <= 180){
sentinel = 3;
}
else if (PosX > 550 && PosX <= 650 && PosY <= 180){
sentinel = 4;
}
else if (PosX >= 950 && PosX <= 1350 && PosY <= 600 && PosY >= 250){
sentinel = 5;
}
else if (PosX >= width && PosY <= 100){
sentinel = 7;
}
}

Move around:

void direction(){
//control
if (keyPressed){
if (key == CODED) {
if ((get(PosX,PosY-10) == color(b) || PosY-rad <= 0) && keyCode == UP){
PosY-=0;
}else if ((get(PosX,PosY+10) == color(b) || PosY+rad >= height) && keyCode == DOWN){
PosY+=0;
}else if ((get(PosX-10,PosY) == color(b) || PosX-rad <= 0) && keyCode == LEFT){
PosX-=0;
}else if ((get(PosX+10,PosY) == color(b) || PosX+rad >= width && PosY >= 100) && keyCode == RIGHT){
PosX+=0;
}else if(keyCode == UP) {
PosY -= speed;
} else if (keyCode == DOWN){
PosY += speed;
} else if (keyCode == LEFT){
PosX -= speed;
} else if (keyCode == RIGHT){
PosX += speed;
}
}
}
}

Flashlight Effect:

void flashlight(){

// We must also call loadPixels() on the PImage since we are going to read its pixels. map.loadPixels();
for (int x = 0; x < map.width; x++ ) {
for (int y = 0; y < map.height; y++ ) {

// Calculate the 1D pixel location
int loc = x + y*map.width;

// Get the R,G,B values from image
float r = red (map.pixels[loc]);
float g = green(map.pixels[loc]);
float b = blue (map.pixels[loc]);

// Calculate an amount to change brightness
// based on proximity to the mouse
float distance = dist(x, y, PosX, PosY);

// The closer the pixel is to the mouse, the lower the value of “distance”
// We want closer pixels to be brighter, however, so we invert the value using map()
// Pixels with a distance of 50 (or greater) have a brightness of 0.0 (or negative which is equivalent to 0 here)
// Pixels with a distance of 0 have a brightness of 1.0.
float adjustBrightness = map(distance, 0, 50, 8, 0);
r *= adjustBrightness;
g *= adjustBrightness;
b *= adjustBrightness;

// Constrain RGB to between 0-255
r = constrain(r, 0, 255);
g = constrain(g, 0, 255);
b = constrain(b, 0, 255);

// Make a new color and set pixel in the window
color c = color(r, g, b);
pixels[loc] = c;
}
}

updatePixels();
}

Make the Monster Jump (one example):

void foxjump() {
mons.stop();
imageMode(CENTER);
translate(width/2, height/2);
if (size < 1.6) {
scream.play();
scale(size);
image(foxy, 0, 0, foxy.width, foxy.height);
size += 0.2;
image(foxy, 0, 0);
}
if (size >= 1.6 && scream.isPlaying() == false){
delay(1000);
sentinel = 6;
size = 1;
}
}

Play the Music When the Button is Pressed:

void musicsound() {

while (myPort.available() > 0) {
val = myPort.read();
println(val);
}

if (val == 1) {
if (PosX <= 100 && PosY >= 250) {
file1.amp(map(PosY, height, 250, 0.01, 1));
file1.play();
//delay(6000);
}
if (PosX >= 0 && PosX <= 550 && PosY <= 250) {
file2.pan(1); // map(PosX, 0,550,1,0)
file2.amp(map(PosX, 0, 550, 0, 1));
file2.play();
//delay(6000);
}
if (PosX > 550 && PosX <= 650 && PosY >= 150 && PosY <= 350) {
file3.amp(map(PosY, 150, 500, 0, 1));
file3.play();
//delay(3000);
} else if (PosX > 550 && PosX <= 800 && PosY > 350 && PosY <= 500) {
file4.amp(map(PosX, 550, 800, 0, 1));
file4.pan(map(PosX, 550, 800, 1, 0));
file4.play();
//delay(3000);
} else if (PosX > 800 && PosY > 350 && PosY <= height) {
file5.amp(map(dist(PosX, PosY, width, 500), dist(800, height, width, 500), 0, 0, 1));
file5.pan(map(PosX, 800, width, 1, 0.5));
file5.play();
} else if (PosX <= width && PosX >= 750 && PosY <= 350 && PosY >= 150) {
file6.amp(map(dist(PosX, PosY, 750, 150), dist(width, 350, 750, 150), 0, 0, 1));
file6.pan(-1); // map(PosX, width, 750, -1, 0)
file6.play();
} else if (PosX <= width && PosX >= 750 && PosY <= 150 && PosY >= 0) {
file7.amp(map(dist(PosX, PosY, width, 0), dist(750, 150, width, 75), 0, 0.1, 1));
file7.pan(map(PosX, width, 750, 1, 0.1));
file7.play();
} else if (sentinel == 7) {
win.play();
}
}
}

Make Monster’s sound Loop as Background:

void monsterSound() {
boolean isPlaying = false;
boolean wasPlaying = false;
// freddy
if (PosX <= 100 && PosY >= 550) {
isPlaying = true;
mons.pan(1);
mons.amp(map(PosY, height, 550, 1, 0));

//chica
} else if (PosX <= 100 && PosY < 450 ) {
isPlaying = true;
mons.pan(0);
mons.amp(map(PosY, 450, 200, 0, 1));

//chica 2
} else if (PosX >= 100 && PosX <= 250 && PosY <= 250) {
isPlaying = true;
mons.pan(-1);
mons.amp(map(PosX, 100, 250, 1, 0));

//springtrap 1
} else if (PosX >= 400 && PosX <= 650 && PosY <= 250) {
isPlaying = true;
mons.pan(map(PosX, 400, 650, 1, 0));
mons.amp(map(PosX, 400, 650, 0, 1));

//springtrap 2
} else if (PosX > 550 && PosX <= 650 && PosY <= 350) {
isPlaying = true;
mons.pan(0);
mons.amp(map(PosY, 250, 350, 1, 0));

//foxy 1 (L shape) area: 950,250,400,350
} else if (PosX >= 650 && PosX <= 950 && PosY >= 350 && PosY <= 600) {
isPlaying = true;
mons.pan(1);
mons.amp(map(PosX, 650, 950, 0, 1));

//foxy 2
} else if (PosX >= 800 && PosX <= width && PosY > 600 && PosY <= height) {
isPlaying = true;
mons.pan(map(PosX, 800, width, 1, -1));
mons.amp(map(PosY, 600, height, 1, 0.3));

//foxy 3
} else if (PosX >= 1350 && PosX <= width && PosY <= 600 && PosY >= 250) {
isPlaying = true;
mons.pan(-1);
mons.amp(map(PosX, 1350, width, 1, 0.3));

// foxy 4
} else if (PosX >= 750 && PosX <= width && PosY <= 350 && PosY >= 150) {
isPlaying = true;
mons.pan(map(PosX, 750, width, 1,-1));
mons.amp(map(PosY, 350,150, 1,0.3));
} else {
isPlaying = false;
}
if (isPlaying == true && wasPlaying == false) {
if (!mons.isPlaying()) {
mons.play();
}
} else {
mons.stop();
}
wasPlaying = isPlaying;
}

Recitation 11: Workshops by Qianyue Fan

In this recitation, I went to Tristan’s workshop about object oriented programming in order to consolidate the knowledge points that I learned in class. Since we are making a game for the final project, we are likely to apply a lot of functions and OOP can help organize them.

For the exercise, I made a simple interactive program that generates colourful bouncing balls every time the mouse is clicked.

code:

video:

Recitation 10: Media Controller by Qianyue Fan

In the recitation today, I  use Arduino to control the appearance of the picture of “Mona Lisa” in Processing. There are three potentiometers that respectively control blur, tint and size. Arduino reads the analogue signals from the potentiometers, and the values are then mapped in Processing. The first sensor value adjusts the blur degree ranging from 0 to 10; the second changes the green value in RGB color from 0 to 255; the last one changes the size of the picture from 0.5 to 1.5 times the original size of the picture.

original picture:

circuit:

video:

main part of Processing code:

For the way that technology is used, my project is based on the open-source software and related code-sharing communities. The progress in computer and the decrease in the price of digital hardware also made the use of both Arduino and Processing possible. Processing itself includes a QuickTime­based interface and a Java­-based scripting language, benefiting from the works of predecessors in digital technology.

Recitation 9: Final Project Process by Qianyue Fan

Since we had five people in the group, each of us critiqued four other projects.

Luke wants to make a “Magic Brush” that allows users with physical disabilities to create artworks without difficulty. The device will include various sensors of Arduino to receive sound, light and movement of the body, and turn the received data into certain styles of strokes and colors to draw beautiful paintings on the canvas in Processing. We considered it a good idea for it helps people explore the endless possibilities of art. Not only disabled people but also everyone can find some inspiration in it. The project so far is just a basic frame, and we suggested providing various art styles for users to choose from, and hoped that users could save the current picture they paint. Also, there can be some ready examples to show some amazing works done with the program, because users may have no idea about it in the very beginning.

Eugene will design a game based on Avengers. It is an arcade game for two players that includes three Marvel superheroes and Thanos. Superheroes cannot move but can decide when to attack, and Thanos must keep moving to dodge the ranged attacks. Thanos wins if staying alive in one minute, otherwise, the superheroes win. The design is simple but exciting. To make it more playable, we suggested adding ultimate attacks with fancy effects and taking the distance between both sides into consideration to improve gaming experience.

Yixuan would like to make a device to provide immersive experience in a room. Users will see a scene in the natural environment and can interact with flower-like objects. When doing so, the device plays the sound of Bianzhong, a traditional Chinese musical instrument. However, we agreed that it is difficult to make it eye-catching and attract users, so it needs further adjustments. As for the feeling of traditional Chinese culture, we thought that more instruments could be added.

Gloria will create a game to promote environment protection. Two players control a salvage ship and a whale to remove all the plastic garbage in the ocean. This is a very unique game for it encourages cooperation instead of competition. We noticed that making different pictures to show the motion of the ship and the whale might require a lot of work, but there were not any problems for the rest of the game.

Most of us share a similar definition of interaction and believe that game is the perfect way for it. But the immersive project is also in line with my definition of interaction, where the activity between at least two actors who affect others in a cycle of receiving input, processing and giving output, and it arouses people’s emotional or mental reactions.

 For our project, others appreciated its focus on hearing and the 3D audio effect, but they also pointed out that it would be a complicated task to make the dark maze and the limited illumination. Whether to move the background has not been decided as well. Based on the feedback, we will move up the time for the start of the project to figure out these big problems first. Besides, the use of Arduino may lack creativity, so we are considering adding signal light or some other parts to strengthen the interaction part. The 3D audio seems like something geek, but it is actually easy to make and we may add more effects to move the sound not only left and right but also forward and backward.  

Final Project Essay by Qianyue Fan

Project Title

Darkness Rhapsody.

Project statement of purpose

During my research, I have drawn inspiration from the project  Roskilde Festival playground, which creates changing mazes of light and color, along with an interactive button that changes the background music. Based on this project, I want to design a maze game mainly with processing, where players can hardly see the maze itself and need to listen to where the sound comes from to find the right path. It mimics the experience of eyesight loss and forces players to focus on the sounds that they ignore in daily life. It is a game designed for everyone to explore and reflect on their sense of hearing. Instead of instant feedback that we usually get from games, this one intends to slow down the quick pace in life and provoke thoughts on ourselves.

Project Plan

The project will be in the form of an interactive game using both Arduino and Processing. The goal is to find the way out in a dark maze with deadly monsters.

The Arduino will be used for controlling the movement of the character and we plan to use joysticks for this.

The game interface will be in an abstract art style, and it only uses black, white and red color. Around the character there is limited illumination that allows the player to see a very small area around.

The player needs to wear headphones to identify the direction of the sound. The sound of the monster remains the same all through the levels, while the tone showing the exit changes when the player reaches different places. 3D audio effects will be used, and we are still considering what songs to choose.

The monster starts moving regularly in an area once the player crosses the boundary, and the player loses when bumping into the monster. Monsters look like red polygons, but the player won’t see them until getting very close.

In the next three weeks, we are going to figure out these things:

First week: Deciding whether to realize the effect of walking by moving the background or just use a still background and move the character; Design the maze;

Second week: Connect Arduino and Processing to make them work as we expected;   Choose the sounds; Coding and editing sounds;

Third week: User test.

Context and Significance

In my previous research and analysis, I defined interaction as the activity between at least two actors who affect others in a cycle of receiving input, processing and giving output, and it arouses people’s emotional or mental reactions. From my point of view, game is one of the most interactive activities that fit the definition. The uniqueness of our project lies in its lack of immediate feedback and requirement on both physical and mental focus. Meanwhile, we add some features like monsters and the risk of losing to make it entertaining enough. This will be a game accessible to everyone to have an experience of a mix of quietness and excitement.