Stationery War

An endless runner game made in Unity that uses Arduino as controller

Details

Stationery War an interactive game which the player will use a plane-like controller built with stationeries to control a virtual plane. When we were a kid, we have tried to use stationeries to build something like a plane or a car, maybe also weapons and architecture. and with this project, we want to bring back our child memory and combine it with new technologies.

Hardware

For the base design of the controller, we attached a ruler and the breadboard on top of a highlighter, which looks like the airplane we built as a child using stationeries, while 2 solenoids are attached to the bottom of the ruler which looks like 2 machine guns of the plane.

Software 

The game is built in Unity3D using version 2019.4.14f1. It is an 3d infinite-level game, which requires the player to control the spaceship to destroy in-coming enemy planes and avoid architecture at the same time. To control the movement of the spaceship, rotate the plane-like controller to do vertical and horizontal movement. Push the button to fire bullets to attack enemy planes.

Connections between Arduino and Unity 

In Arduino, there is a function called sentSensorData(), and it will send 4 values, which are separated by a ; mark. The first 3 values will be the xyz rotation of the accelerometer and the last value will be a 0/1 boolean value to indicate whether the button is pressed or not. Arduino prints the value in the serial monitor and sends it to unity. The source code for the Arduino part is shown below:

                
        
// MPU 6050   --->   Arduino
//
//  VCC   ----->   3.3v
//  GND   ----->   GND
//  SCL   ----->   A5 pin
//  SDA   ----->   A4 pin

#include <Wire.h>
const int MPU = 0x68; // I2C address of the MPU-6050
int32_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ, button;
// value returned is in interval [-32768, 32767] so for normalize multiply GyX and others for gyro_normalizer_factor
// float gyro_normalizer_factor = 1.0f / 32768.0f;

int led1Pin = 0;
int led2Pin = 1;
int solenoid1Pin = 2;
int solenoid2Pin = 3;
int buttonPin = 7;
int buttonInterval = 100;

void setup()
{
  pinMode(led1Pin, OUTPUT);
  pinMode(led2Pin, OUTPUT);
  pinMode(solenoid1Pin, OUTPUT);
  pinMode(solenoid2Pin, OUTPUT);
  pinMode(buttonPin, INPUT);

  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B); // PWR_MGMT_1 register
  Wire.write(0);    // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  Serial.begin(57600);
  Serial.flush();
}

int checkButton()
{
  unsigned long currentMillis = millis();
  static unsigned long previousMillis = 0;
  int buttonState = digitalRead(buttonPin);
  int currentState = digitalRead(solenoid1Pin);

  if (currentMillis - previousMillis >= buttonInterval && buttonState == HIGH)
  {
    previousMillis = currentMillis;
    if (currentState == LOW)
    {
      digitalWrite(led1Pin, HIGH);
      digitalWrite(led2Pin, HIGH);
      digitalWrite(solenoid1Pin, HIGH);
      digitalWrite(solenoid2Pin, HIGH);
    }
    else
    {
      digitalWrite(led1Pin, LOW);
      digitalWrite(led2Pin, LOW);
      digitalWrite(solenoid1Pin, LOW);
      digitalWrite(solenoid2Pin, LOW);
    }
  }

  return buttonState;
}

void sentSensorData()
{
  //read MPU450's accelerometer and sent it to serial port
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 14, true); // request a total of 14 registers

  //accelerometer
  AcX = Wire.read() << 8 | Wire.read(); // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
  AcY = Wire.read() << 8 | Wire.read(); // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ = Wire.read() << 8 | Wire.read(); // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  button = checkButton();
  Serial.print(AcX);
  Serial.print(";");
  Serial.print(AcY);
  Serial.print(";");
  Serial.print(AcZ);
  Serial.print(";");
  Serial.print(button);
  Serial.println(";");
  //Serial.print(int16_t(5)); Serial.print(";"); Serial.print(int16_t(10)); Serial.print(";"); Serial.print(int16_t(15)); Serial.println(""); //temp

  //gyroscope
  //Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  //GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  //GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  //GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  //Serial.print(GyX); Serial.print(";"); Serial.print(GyY); Serial.print(";"); Serial.print(GyZ); Serial.println("");
}

void loop()
{
    sentSensorData();
    Serial.flush();
    delay(20);
} 
      
    

For the serial connection between Arduino and Unity, we used an add-on called Ardity, which allows us to simplify the process of setting up the serial connections. When Unity received the signal value from the serial port. Unity will first parse the value inputted by dividing sent value by the ‘;’ mark and store those values into 4 different variables. Then it performs updates on the player's avatar game object’s transform position and rotation.


Creation date: December, 2020
My role in this project: Arduino to Unity programming, game design

Language used

Coding languages used on this project:

C++ (Arduino)

%

C# (Unity)

%