Objection! A Real Ace Attorney Experience – Bryan Feng – Inmi Lee


CONCEPTION AND DESIGN

Original diagram for the storyline

This project started with my previous experience when playing video games, especially visual novels that rely solely on mouse-clicking for option selection and story progression (i.e. my favorite game Phoenix Wright: Ace Attorney Trilogy). What if the player can physically present the evidence just like the characters do in the game? Building upon my previous experience during the midterm project and my understanding of the thinking stage of interactivity explained by Crawford in The Art of Interactive Design, I decided to build an arcade machine-like project, using Arduino sensors and buttons as the input, and Processing pictures and music as the output. The player uses the button to progress and present the magnet-attached evidence to react to the characters’ dialogues and make objections at different stages of the game. During the user-testing session, due to the fact that I spent too much time coding and didn’t get to build a physical working prototype, most advice I got was on the practical functioning of the sketches. In addition to that, some users suggested adding more sound effects and making the opening section shorter for a better experience for the players. I made the changes accordingly and changed the Arduino input sensor design, which I will talk more about in the production part.

Preliminary concept sketch for the project (when I still believed RFID could work)

FABRICATION AND PRODUCTION

RFID testing

Considering that my project requires the user to present a specific item according to the story (i.e. presenting the correct evidence to advance the plot), the actuators I use must be able to detect what exactly the user is presenting, and the only thing that can achieve this correspondence is RFID (Radio Frequency Identification) tags and readers – at least that’s what I thought at that time. I bought the RFID kit online and made sure the reader and the tags all worked using the RC522 module. However, the problem arose when I tried to use serial communication to send the data read by the RFID reader to Processing as I couldn’t figure out what exactly was detected from the RFID tags and didn’t have enough time to figure it out. Later, I tried to switch to using joysticks to imitate the mouse-clicking and different sections of the screen to represent different evidence, but it looked way too much like the original mouse-clicking and the readings of the joystick weren’t stable enough, so I gave up on that idea. During the user-testing session, I was

Final circuit using reed switches

inspired by Kevin’s feedback and decided to use reed switches and magnets to achieve the original effects, except this time different reed switches represent different evidence, and placing the evidence (magnet attached) next to the reed sensor close the circuit. The digital readings of the reed switches were stable and the serial communication worked out.

After making sure the key interaction component of my project worked, I moved on to the Processing coding and material fabrication. For the screen components, I (aggressively) rewrote the original storyline and made sure its originally 4-hour-long content could fit into the 10-minute game, and used character stand-ups and background scenes from the original game. I used arrays for images so that each time the user pushed a button the program showed one image, and added different soundtracks from the original game at different stages of the project using example codes from the class. I used 3D printing for the evidence models and used existing files

3D printing

shared by other creators on Thingiverse for the more complex models (i.e. the attorney’s badge, the Thinker, and the glass shards). For the physical structure, I designed the arcade machine model blueprints and used MakerCase to generate the open box with grids that would be used to hold the evidence models.

Presentation version

CONCLUSIONS

In the end, my project basically achieved what I had hoped for: users who had already played the original game had fun with the new interactive experience of presenting physical evidence and smashing the button to object, and those who hadn’t played were able to engage with the different

IMA show

phases of the game and become interested in the original game. However, some users proposed that the evidence given to the player at the start of the game could be obtained by players searching and investigating on their own, which was actually part of the gameplay of the original game (and got cut in my version due to the time-restriction). If given more time, I would work more on the RFID mechanism and figure out how the readers and tags work, and add another phase of the game with the user searching the crime scene (3D printed floor plans of the recreated crime scenes) to get crucial evidence.

Final version

This is my first time designing and building a complete project from scratch on my own, so I ran into a lot LOT of problems throughout the process. However, I also got a much clearer understanding of how serial communication works and how to use Processing to provide visual and auditory feedback to users. As a perfectionist with a low tolerance for frustration and OCD (which forced me to recount all 243 images for Processing three times in a row every time I adjusted the array until I drove myself crazy), I have learned how to remain calm when encountering a problem to troubleshoot and change tactics when necessary to find a more maneuverable direction, and I believe that these skills will play an indispensable role in the future.

 

Demonstration of partial gameplay


DISASSEMBLY

Disassemble the project after IMA show

APPENDIX

Final version closeup

Sketch for Arduino:

int buttonBlue = 2;
int buttonWhite = 3;
const int reed_1 = 5;
const int reed_2 = 6;
const int reed_3 = 7;
const int reed_4 = 8;
const int reed_5 = 9;
const int reed_6 = 10;
const int reed_7 = 11;
const int reed_8 = 12;

void setup() {
  Serial.begin(9600);
  pinMode(buttonBlue, INPUT);
  pinMode(buttonWhite, INPUT);
}

void loop() {

  int buttonState_1 = digitalRead(buttonBlue);
  int buttonState_2 = digitalRead(buttonWhite);
  int reedState_1 = digitalRead(reed_1);
  int reedState_2 = digitalRead(reed_2);
  int reedState_3 = digitalRead(reed_3);
  int reedState_4 = digitalRead(reed_4);
  int reedState_5 = digitalRead(reed_5);
  int reedState_6 = digitalRead(reed_6);
  int reedState_7 = digitalRead(reed_7);
  int reedState_8 = digitalRead(reed_8);

  Serial.print(buttonState_1);
  Serial.print(",");
  Serial.print(buttonState_2);
  Serial.print(",");
  Serial.print(reedState_1);
  Serial.print(",");
  Serial.print(reedState_2);
  Serial.print(",");
  Serial.print(reedState_3);
  Serial.print(",");
  Serial.print(reedState_4);
  Serial.print(",");
  Serial.print(reedState_5);
  Serial.print(",");
  Serial.print(reedState_6);
  Serial.print(",");
  Serial.print(reedState_7);
  Serial.print(",");
  Serial.print(reedState_8);

  Serial.println();

  delay(2);
}

Sketch for Processing:

import processing.serial.*;
Serial serialPort;

import processing.sound.*;
SoundFile crime;
SoundFile suspect;
SoundFile court;
SoundFile objection;
SoundFile pursuit;
SoundFile end;
SoundFile crisis;
SoundFile win;
SoundFile phoenixObjection;
SoundFile milesObjection;

PImage title;
PImage evidence;
PImage[] opening = new PImage[48];
PImage[] start = new PImage[243];

int count;
int preValue;
int white;
int preWhite;

//choice determination boolean
boolean wrongBadge;
boolean correctBadge;
boolean wrongA;
boolean correctA;
boolean wrongB;
boolean correctB;
boolean wrongC;
boolean correctC;

int NUM_OF_VALUES_FROM_ARDUINO = 10;
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];

void setup() {
  size(1200, 800);

  //screen displays
  title = loadImage("title.png");
  evidence = loadImage("evidence.png");
  for (int i = 0; i < opening.length; i++) {
    opening[i] = loadImage("opening"+i+".png");
  }
  for (int i = 0; i < start.length; i++) {
    start[i] = loadImage("start"+i+".png");
  }

  //sound files for background soundtracks
  crime = new SoundFile(this, "crime.wav");
  suspect = new SoundFile(this, "suspect.wav");
  court = new SoundFile(this, "court.wav");
  objection = new SoundFile(this, "objection.wav");
  pursuit = new SoundFile(this, "pursuit.wav");
  end = new SoundFile(this, "end.wav");
  crisis = new SoundFile(this, "crisis.wav");
  win = new SoundFile(this, "win.wav");
  phoenixObjection = new SoundFile(this, "phoenixObjection.mp3");
  milesObjection = new SoundFile(this, "milesObjection.mp3");

  //serial communication
  printArray(Serial.list());
  serialPort = new Serial(this, "/dev/cu.usbmodem14201", 9600);
}

void draw() {
  background(0);
  getSerialData();

  //push button to continue setting
  if (arduino_values[0] == 1 && preValue == 0) {
    count++;
  }
  if (arduino_values[1] == 1 && preWhite == 0) {
    white++;
  }

  //default page
  if (count == 0) {
    image(title, 0, 0);
  }

  //game start
  if (count > 0 && count < 9) { //opening scene
    if (crime.isPlaying() == false) {
      crime.loop();
      image(opening[count-1], 0, 0);
    } else {
      image(opening[count-1], 0, 0);
    }
  } else if (count > 9 && count < 49) { //detention scene
    if (suspect.isPlaying() == false) {
      crime.stop();
      suspect.loop();
      image(opening[(count-1)], 0, 0);
    } else {
      image(opening[(count-1)], 0, 0);
    }
  } else if (count > 48 && count < 143) { //trial start
    if (court.isPlaying() == false) {
      suspect.stop();
      court.loop();
      image(start[(count-49)], 0, 0);
    } else {
      image(start[(count-49)], 0, 0);
    }
  } else if (count > 142 && count < 186) { //first stage
    if (objection.isPlaying() == false) {
      court.stop();
      phoenixObjection.play();
      objection.loop();
      image(start[(count-49)], 0, 0);
    } else {
      image(start[(count-49)], 0, 0);
    }
  } else if (count > 185 && count < 203) { //second stage
    if (pursuit.isPlaying() == false) {
      objection.stop();
      phoenixObjection.play();
      pursuit.loop();
      image(start[(count-49)], 0, 0);
    } else {
      image(start[(count-49)], 0, 0);
    }
  } else if (count > 202 && count < 228) { //third stage
    if (crisis.isPlaying() == false) {
      pursuit.stop();
      milesObjection.play();
      crisis.loop();
      image(start[(count-49)], 0, 0);
    } else {
      image(start[(count-49)], 0, 0);
    }
  } else if (count > 227 && count < 258) { //fourth stage
    if (end.isPlaying() == false) {
      crisis.stop();
      phoenixObjection.play();
      end.loop();
      image(start[(count-49)], 0, 0);
    } else {
      image(start[(count-49)], 0, 0);
    }
  } else if (count > 257) { //post trial
    if (win.isPlaying() == false) {
      end.stop();
      win.loop();
      image(start[(count-49)], 0, 0);
    } else {
      image(start[(count-49)], 0, 0);
    }
  }

  //badge presenting
  if (count == (opening.length + 7) && arduino_values[2] == 0) {
    wrongBadge = true;
  } else if (count == (opening.length + 7) && arduino_values[2] == 1) {
    correctBadge = true;
  }
  if (wrongBadge == true) {
    image(start[(count-49)], 0, 0);
    if (count == (opening.length + 10) && arduino_values [0] == 1 && preValue == 0) {
      count = count - 3;
      wrongBadge = false;
    }
  }
  if (correctBadge == true) {
    count = count + 4;
    image(start[(count-49)], 0, 0);
    correctBadge = false;
  }

  //A presenting
  if (count == (opening.length + 92) && arduino_values[6] == 0 && arduino_values[9] == 0) {
    wrongA = true;
  } else if (count == (opening.length + 92) && (arduino_values[6] == 0||arduino_values[9] == 0)) {
    correctA = true;
  }
  if (wrongA == true) {
    image(start[(count-49)], 0, 0);
    if (count == (opening.length + 94) && arduino_values [0] == 1 && preValue == 0) {
      count = count - 3;
      wrongA = false;
    }
  }
  if (correctA == true) {
    count = count + 3;
    image(start[(count-49)], 0, 0);
    correctA = false;
  }

  //B presenting
  if (count == (opening.length + 135) && arduino_values[4] == 0) {
    wrongB = true;
  } else if (count == (opening.length + 135) && arduino_values[4] == 1) {
    correctB = true;
  }
  if (wrongB == true) {
    image(start[(count-49)], 0, 0);
    if (count == (opening.length + 137) && arduino_values [0] == 1 && preValue == 0) {
      count = count - 3;
      wrongB = false;
    }
  }
  if (correctB == true) {
    count = count + 3;
    image(start[(count-49)], 0, 0);
    correctB = false;
  }

  //C presenting
  if (count == (opening.length + 176) && arduino_values[3] == 0) {
    wrongC = true;
  } else if (count == (opening.length + 176) && arduino_values[3] == 1) {
    correctC = true;
  }
  if (wrongC == true) {
    image(start[(count-49)], 0, 0);
    if (count == (opening.length + 179) && arduino_values [0] == 1 && preValue == 0) {
      count = count - 4;
      wrongC = false;
    }
  }
  if (correctC == true) {
    count = count + 4;
    image(start[(count-49)], 0, 0);
    correctC = false;
  }

  //evidence page
  if ((white % 2) == 1) {
    image(evidence, 0, 0);
  }

  preValue = arduino_values[0];
  preWhite = arduino_values[1];

  //reset game after finish
  if (count > 290) {
    resetGame();
  }
}

void resetGame() {
  count = 0;
  white = 0;
  wrongBadge = false;
  correctBadge = false;
  wrongA = false;
  correctA = false;
  wrongB = false;
  correctB = false;
  wrongC = false;
  correctC = false;
  win.stop();
}

// the helper function
void getSerialData() {
  while (serialPort.available() > 0) {
    String in = serialPort.readStringUntil( 10 );  // 10 = '\n'  Linefeed in ASCII
    if (in != null) {
      print("From Arduino: " + in);
      String[] serialInArray = split(trim(in), ",");
      if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
        for (int i=0; i<serialInArray.length; i++) {
          arduino_values[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

 

Wiring circuit diagram (I really don’t know why Tinkercad doesn’t have reed switches)
Part of the appointments made for this project (special thanks for Inmi, Shengli, Kevin, Amelia, and Dalin for your help!)
My cat Kenny for remote emotional support

Leave a Reply

Your email address will not be published. Required fields are marked *