18 April 2021

Hitchhiker's Controller to the Galaxy

For this project, I set out with the goal of creating a game controller for a classic piece of interactive fiction. I specifically wanted to make this controller be a book. The reason for this, is because Interactive Fiction and Choose Your Own Adventure books are very closely related, the primary difference between them being that one is digital, and one is analog. In creating this controller, I wanted to make a statement about that relationship, and bridge the gap between them.

The action of turning a page is a very natural affordance that everyone understands. Text responding and flashing on a digital display is also very universally and intuitively understood. With this project, I have taken the best of both - the signifiers and feedback offered by both is clear, and yet blurs the line between digital and analog. Playing the Hitchhiker's Guide to the Galaxy while using this controller should cause a player to ask themself, "Is this a piece of Interactive Fiction, or a Choose Your Own Adventure book?"

Conceptually, this was very simple to create! In practice though, it was a bit of a nightmare.

For starters, carving out a book to create a hollow space was extremely time consuming and delicate. Also, feeding the twelve wires through the back binding of the book was tedious, and required some forethought and planning in order to make sure wires ended up where they needed to be. The most difficult aspect of the project though, was getting the page contacts to work properly.

I re-applied each page at least three different times using tin foil, conductive tape, and anything else I could think of to try and get the page contacts working consistently. It was a massively frustrating ordeal, and I accidentally destroyed a few pages in the process. Eventually, I got them to a point where they mostly work, and at the very least, more consistently than ever before. I couldn't have known until I had already started this project that this would be such a massive challenge in making my game controller vision come to life, and I honestly kind of regret choosing to make something so novel for this project. Maybe next time I'll think a little less outside of the box.

With these problems in my mind, my question to you is: what suggestions do you have for making the "page gates" more consistent, but also flexible like a page should be? 



#include <Keyboard.h>

#include <Adafruit_CircuitPlayground.h>

#include <Adafruit_Circuit_Playground.h>


//.........................................................Pin Variables




int pinA1State = 0;

int pinA2State = 0;

int pinA3State = 0;

int pinA4State = 0;

int pinA5State = 0;

int pinA6State = 0;


//.........................................................Delay Variable

const int debounce = 100;


//.........................................................Page opening variables

const int openThreshHold = 650; //connection is analog, how low does signal need to be to be considered "open"


const int consecutiveThreshHold = 20; //how long player needs to hold page open


int consecutiveA1 = 0;

int consecutiveA2 = 0;

int consecutiveA3 = 0;

int consecutiveA4 = 0;

int consecutiveA5 = 0;

int consecutiveA6 = 0;


int consecutiveBookClosed = 0;

bool pageLock = false; //prevent player from putting in new command until book closed


//.........................................................Tracking Variables (where in HitchHikers Guide is the player)

bool dark = true;

bool standing = false;

bool haveRobe = false;

bool wearingRobe = false;

bool lookingInRobe = false;



void setup() {

  // .......................................................Initialize digital pins

  pinMode(13, OUTPUT);

  

  pinMode(A6, INPUT);

  pinMode(A5, INPUT);

  pinMode(A4, INPUT);

  pinMode(A3, INPUT);

  pinMode(A2, INPUT);

  pinMode(A1, INPUT);


//.........................................................Initialize Utilities

  Serial.begin(9600);


  Keyboard.begin();

}


//.........................................................Main loop: 1. see if a page is open 2. turn on dev light 3. check how long page has been open 4. if long enough execute a command 5. print to console

void loop() {


  ReadPins();


  ActivityLight();


  if (pageLock == false)

  {

    FindConsecutive();

  }

  else

  {

    WaitForPageClose();

  }


  CheckKeyboardAction();


  ConsolePrint();


  delay(debounce); 

}


//.........................................................Logic commands for the game, tracks where the player is and issues appropriate commands

void Interact()

{

  if (dark == true)

  {

    dark = false;

    Keyboard.print("Turn on the light");

    Keyboard.write(KEY_RETURN);

  }


  else if (standing == false)

  {

    standing = true;

    Keyboard.print("Stand");

    Keyboard.write(KEY_RETURN);

  }


  else if (haveRobe == true)

  {

    wearingRobe = true;

    Keyboard.print("Put on robe");

    Keyboard.write(KEY_RETURN);

  }


  else

  {

    Keyboard.print("Look");

    Keyboard.write(KEY_RETURN);

  }

}


void Look()

{

  if (wearingRobe == true)

  {

    Keyboard.print("Look in Robe");

    Keyboard.write(KEY_RETURN);

    lookingInRobe = true;

  }

  else

  {

    Keyboard.print("Look");

    Keyboard.write(KEY_RETURN);

  }

}


void Take()

{

  if (haveRobe == false)

  {

    Keyboard.print("Take robe");

    Keyboard.write(KEY_RETURN);

    haveRobe = true;

  }

  else if (lookingInRobe = true)

  {

    lookingInRobe = true;

    Keyboard.print("Take pills");

    Keyboard.write(KEY_RETURN);

  }

  else

  {

    Keyboard.print("Take");

    Keyboard.write(KEY_RETURN);

  }

}


//.........................................................Player has to close book to open a page again


void WaitForPageClose()

{

  if (pinA1State >= openThreshHold && pinA2State >= openThreshHold && pinA3State >= openThreshHold && pinA4State >= openThreshHold && pinA5State >= openThreshHold && pinA6State >= openThreshHold)

  {

    consecutiveBookClosed++;

  }


  else

  {

    consecutiveBookClosed = 0;

  }


  if (consecutiveBookClosed >= consecutiveThreshHold)

  {

    pageLock = false;

  }

}


//.........................................................Check if a keyboard action should be performed

void CheckKeyboardAction()

{

  if (consecutiveA1 >= consecutiveThreshHold)

  {

    Interact();

    pageLock = true;

    consecutiveA1 = 0;

  }


  if (consecutiveA2 >= consecutiveThreshHold)

  {

    Look();

    pageLock = true;

    consecutiveA2 = 0;

  }


  if (consecutiveA3 >= consecutiveThreshHold)

  {

    Take();

    pageLock = true;

    consecutiveA3 = 0;

  }


  if (consecutiveA4 >= consecutiveThreshHold)

  {

    Keyboard.print("Go outside");

    Keyboard.write(KEY_RETURN);

    pageLock = true;

    consecutiveA4 = 0;

  }


  if (consecutiveA5 >= consecutiveThreshHold)

  {

    Keyboard.print("Panic");

    Keyboard.write(KEY_RETURN);

    pageLock = true;

    consecutiveA5 = 0;

  }


  if (consecutiveA6 >= consecutiveThreshHold)

  {

    Keyboard.print("Wait");

    Keyboard.write(KEY_RETURN);

    pageLock = true;

    consecutiveA6 = 0;

  } 

}


//.........................................................A page has to be held open for a consecutive ammount of time (prevent accidental commands)

void FindConsecutive()

{

  if (pinA1State <= openThreshHold && pinA2State >= openThreshHold && pinA3State >= openThreshHold && pinA4State >= openThreshHold && pinA5State >= openThreshHold && pinA6State >= openThreshHold)

  {

    consecutiveA1++;

    consecutiveA2 = 0;

    consecutiveA3 = 0;

    consecutiveA4 = 0;

    consecutiveA5 = 0;

    consecutiveA6 = 0;

  }


  else if (pinA1State >= openThreshHold && pinA2State <= openThreshHold && pinA3State >= openThreshHold && pinA4State >= openThreshHold && pinA5State >= openThreshHold && pinA6State >= openThreshHold)

  {

    consecutiveA1 = 0;

    consecutiveA2++;

    consecutiveA3 = 0;

    consecutiveA4 = 0;

    consecutiveA5 = 0;

    consecutiveA6 = 0;

  }


  else if (pinA1State >= openThreshHold && pinA2State >= openThreshHold && pinA3State <= openThreshHold && pinA4State >= openThreshHold && pinA5State >= openThreshHold && pinA6State >= openThreshHold)

  {

    consecutiveA1 = 0;

    consecutiveA2 = 0;

    consecutiveA3++;

    consecutiveA4 = 0;

    consecutiveA5 = 0;

    consecutiveA6 = 0;

  }


  else if (pinA1State >= openThreshHold && pinA2State >= openThreshHold && pinA3State >= openThreshHold && pinA4State <= openThreshHold && pinA5State >= openThreshHold && pinA6State >= openThreshHold)

  {

    consecutiveA1 = 0;

    consecutiveA2 = 0;

    consecutiveA3 = 0;

    consecutiveA4++;

    consecutiveA5 = 0;

    consecutiveA6 = 0;

  }


  else if (pinA1State >= openThreshHold && pinA2State >= openThreshHold && pinA3State >= openThreshHold && pinA4State >= openThreshHold && pinA5State <= openThreshHold && pinA6State >= openThreshHold)

  {

    consecutiveA1 = 0;

    consecutiveA2 = 0;

    consecutiveA3 = 0;

    consecutiveA4 = 0;

    consecutiveA5++;

    consecutiveA6 = 0;

  }


  else if (pinA1State >= openThreshHold && pinA2State >= openThreshHold && pinA3State >= openThreshHold && pinA4State >= openThreshHold && pinA5State >= openThreshHold && pinA6State <= openThreshHold)

  {

    consecutiveA1 = 0;

    consecutiveA2 = 0;

    consecutiveA3 = 0;

    consecutiveA4 = 0;

    consecutiveA5 = 0;

    consecutiveA6++;

  }

}


//.........................................................Arduino's built in LED lights up if any page is detected as open

void ActivityLight()

{

  if (pinA1State <= openThreshHold || pinA2State <= openThreshHold || pinA3State <= openThreshHold || pinA4State <= openThreshHold || pinA5State <= openThreshHold || pinA6State <= openThreshHold){

    digitalWrite(13, HIGH); 

  } 

  else{

  digitalWrite(13, LOW);

  }

}


//.........................................................Read pins, which correlate to page states (open/closed)

void ReadPins()

{

  pinA1State = analogRead(A1);

  pinA2State = analogRead(A2);

  pinA3State = analogRead(A3);

  pinA4State = analogRead(A4);

  pinA5State = analogRead(A5);

  pinA6State = analogRead(A6);

}


//.........................................................Print status of most important variables to the console

void ConsolePrint()

  {

    Serial.print("Pin States ");

    Serial.print(" A1.");

    Serial.print(pinA1State);

    Serial.print(" A2.");

    Serial.print(pinA2State);

    Serial.print(" A3.");

    Serial.print(pinA3State);

    Serial.print(" A4.");

    Serial.print(pinA4State);

    Serial.print(" A5.");

    Serial.print(pinA5State);

    Serial.print(" A6.");

    Serial.print(pinA6State);

    

    Serial.println("---");


    Serial.print("Consecutives");

    Serial.print(" A1.");

    Serial.print(consecutiveA1);

    Serial.print(" A2.");

    Serial.print(consecutiveA2);

    Serial.print(" A3.");

    Serial.print(consecutiveA3);

    Serial.print(" A4.");

    Serial.print(consecutiveA4);

    Serial.print(" A5.");

    Serial.print(consecutiveA5);

    Serial.print(" A6.");

    Serial.print(consecutiveA6);


    Serial.print("---");


    Serial.print(" Page locked?");

    Serial.print(pageLock);


    Serial.println(" ");

    

  }


No comments:

Post a Comment

Note: Only a member of this blog may post a comment.