Final Project: Space Explorer

Concept

Many of us are fascinated with space and all the heavenly objects floating around far away from us. Our Earth is one of many objects in space. I wanted to make an interactive environment where we could look at these planets and admire their beauty, while also learning a few facts about them.  Hence, I used WEBGL in p5Js to create a 3D space where I could spawn planets and stars which one can explore.

Implementation

My implementation of the project is divided into p5Js and Arduino, and has very simple controls, aesthetic visuals, with immersive audio.

Interactive Design

The design of the project is simple. The visual component of the project features a dark space with the nine planets in the solar system (including Pluto) and the Sun. I tried to make it to scale as much as possible but still remaining reasonable with user experience.With regards to interactivity, I created a controller that lets the user move around in 3D space and look around. Here are the features of the controller:

  1. Movement: (Joystick) the user can move around in a plane in a 3D space.
  2. Look Around: (Joystick) the user can pan the camera to look at different angles in the space.
  3. Zoom: (momentary Switch) the user can zoom in and out. This provides the third dimensional movement in the z-axis. After a certain point of zooming in, the controls and the visuals are mirrored.
  4. Pointer to Earth: (Switch) this helps the user in navigation and directional feedback. This also increases the spatial awareness; the idea of where the player is relative to Earth.
  5. Information: (Switch) this will provide the user with some facts about the planet they are closest to.

Arduino

The Arduino provides controller access for the movement and changes in the p5Js. It takes inputs from the joystick and switches, processes it, and sends it to the p5Js as a suitable string of information.

// Movement using joystick
const int x_pos = A0;
const int y_pos = A1;

// zoom in and out
const int z_in = 12;
const int z_out = 11;

// Camera movement
const int camera_x = A5;

// pointer to earth button
const int pointer = 2;

// information about the planet
const int info = 7;

// LED
const int led = 5;

const int n = 7; // number of button inputs the user provides

int send_list[n];

int input_pins[n];

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

  pinMode(x_pos, INPUT);
  pinMode(y_pos, INPUT);

  pinMode(camera_x, INPUT);

  pinMode(z_in, INPUT);
  pinMode(z_out, INPUT);

  pinMode(pointer, INPUT);

  pinMode(info, INPUT);

  pinMode(led, OUTPUT);

  input_pins[0] = x_pos;
  input_pins[1] = y_pos;
  input_pins[2] = camera_x;
  input_pins[3] = z_in;
  input_pins[4] = z_out;
  input_pins[5] = pointer;
  input_pins[6] = info;

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

void get_user_input() {
  send_list[0] = analogRead(x_pos);
  send_list[1] = analogRead(y_pos);
  send_list[2] = analogRead(camera_x);
  for (int i = 3; i < n; i++) {
    send_list[i] = 0;
    if (digitalRead(input_pins[i]) == HIGH) {
      send_list[i] = 1;
    }
  }
}

void send_input() {
  String to_send = "";
  for (int i = 0; i < n-1; i++) {
    to_send += String(send_list[i]) + ",";
  }
  to_send += String(send_list[n-1]) + "\0";

  Serial.println(to_send);
}

void loop() {
  while (Serial.available() > 0) {
    // get user input and parse to send it to p5js.
    get_user_input();
    send_input();

    // receive response from p5js
    int response = Serial.read();
    analogWrite(led, response);
  }
}

It also receives a confirmation from p5Js that the devices is connected. This can be visually seen from the LED which lights up when the connection is made.

The code first gets all the input and creates an array of them.

p5Js

My project is p5Js heavy. The code lets the user connect to Arduino and receive control information to make changes in the canvas. It creates multiple planets which are basically spheres with different sizes, positions, and textures onto them. Each planet then rotates on its axis and are tilted to some degree. The planets are made stationary relative to the Sun, to make navigation and finding the planets easier.

I used the WEBGL library, to create the 3D space and made the camera movable and responsive of the players movement. This created the effect/ feature of moving in the 3D environment.

The code features multiple classes: Player, Camera, Sun, Planet, Info, Star which creates most of the functionality and visuals in the project. I separated out the Arduino interaction into a separate file, arduino.js to make debugging easier.

Communication

The communication between Arduino and P5Js is intuitive. When the Arduino senses change in the controller, it extract the values of the state the controller is in. These values are then stored into an the send_list array. All the values in this array is then converted into a string separated by commas. This string is then sent to P5Js for further processing.

void get_user_input() {
  send_list[0] = analogRead(x_pos);
  send_list[1] = analogRead(y_pos);
  send_list[2] = analogRead(camera_x);
  for (int i = 3; i < n; i++) {
    send_list[i] = 0;
    if (digitalRead(input_pins[i]) == HIGH) {
      send_list[i] = 1;
    }
  }
}

void send_input() {
  String to_send = "";
  for (int i = 0; i < n-1; i++) {
    to_send += String(send_list[i]) + ",";
  }
  to_send += String(send_list[n-1]) + "\0";

  Serial.println(to_send);
}

The P5Js then receives this string, and parses it to retrieve all the individual values of all the input devices in the controller. These are then stored in separate variable and used to control the objects on the canvas .

function readSerial(data) {
  if (data != null) {    
    
    // get the information from the arduino
    let nums = data.split(",");
    let len = nums.length;
    
    // parsing the received string to get the useful numbers
    let x = 0;
    for (let i = 0; i < len; i++) {
      user_input[x] = parseInt(nums[i], 10);
      x++;
    }
    
    // continuing the conversation between p5js and arduino
    writeSerial("10");
  }
}

After the information is received, P5Js then sends in a confirmation to Arduino to send in more information. This confirmation also determines the brightness of the LED which lights up when P5Js reciprocates back to the Arduino.

Code I am proud of

The hardest part of the entire project was to figure out the movement in 3D space. I had to first learn the orientation of the camera, then move objects using some arrow keys, and make the movement consistent with where the camera was looking at.

This was very confusing. The problem was, when you move without incorporating the camera into the movement, you would always move the same way, no matter where the camera is pointing towards. For instance, you press UP arrow to move forward. Then you change the camera angle to look behind. When you press UP arrow again, instead of you moving forward, you would move backward.

This problem was overcome by having an angle attribute to the player which would map to the camera as such:

class Camera {
  constructor() {
    // distance between the camera and the virtual player (invisible)
    this.distance = 400;
  }
  
  // creates the camera and determines the position, where the camera is looking at and at what orientation.
  make() {
    camera(this.distance * cos(player.angle) + player.pos.x, this.distance * sin(player.angle) + player.pos.y, this.distance/2, player.pos.x, player.pos.y, player.pos.z, 0, 0, -1);
  }
}

Notice in the above code, the angle is an attribute to the player. It controls where the camera is looking at and where the player moves, as seen in the code below.

// we are in the move() function in the Player class
this.pos.x += move_mag * cos(theta + this.angle - PI/2);
this.pos.y += move_mag * sin(theta + this.angle - PI/2);

Credit: I got the idea of the solution through videos of JS64 Youtube Channel.

Future Improvement

There are a lot of things that I could do with this project. I realised that this project is sort of incomplete, although it serves it purpose of being educational, interactive, and aesthetically pleasing. However, to make it more engaging, I could turn it into a game where, I introduce a character who has to search for more inhabitable planets in the vast expanse of space. I could also link it with my Midterm game, as the theme of space travel is similar.

This project only incorporates the solar system, albeit not entirely since it lacks asteroids, moons and comets. I could expand it more to have more planets and better the spatial awareness so that the player is not lost.

Leave a Reply