> | | | > Games Master GUI

Games Master GUI

Posted on Wednesday 16 January 2013 | No Comments

The Games Master GUI is the graphical user interface that is displayed on a laptop that is connected to the Games Master Arduino. Its purpose is to display the scores of each LaserBot for everyone to see.

DESIGN

The Games Master GUI was designed using Processing. In Processing, there are quite a few examples on how to draw different things. Firstly,it was figured out how to draw text by looking at the Processing example "Words". The screen was to be split into two halves, one the left hand side would be LaserBot one's score, and on the right LaserBot two's score. 

First, in setup, the size of the screen needed to be stated.

void setup() {
  size(960,600);        // Processing screen size, dummy GUI for prototyping
}
Next, in the draw method, the text to be drawn needed to be called. The background colour was set, and then the text was aligned and the methods to write the text were called.
void draw() {
 background(102);
  textAlign(LEFT);
  drawType1(width * 0.10);
  textAlign(LEFT);
  drawType2(width * 0.72); 
}
In drawType1(), the font and size is selected, and the location on the screen is chosen. The text is then written. This is the same for drawType2(), except the text written is different.
void drawType1(float x) {
  textFont(createFont("Hall Fetica", 32));
  line(x, 0, x, 65);
  fill(255);
  text("LaserBot 1", x, 95);
  textFont(createFont("Hall Fetica", 24));
  fill(209);
  text("Score: " + robot1Score, x, 130);
}

void drawType2(float x) {
  textFont(createFont("Hall Fetica", 32));
  line(x, 0, x, 0);
  fill(255);
  text("LaserBot 2", x, 95);
  textFont(createFont("Hall Fetica", 24));
  fill(209);
  text("Score: " + robot2Score, x, 130);
}
Once the GUI was displaying text, it was decided to display the time as well. To do this, the Processing example "Clock" was used for reference. Another method drawTime() was written and called in the draw() method. It gets the correct time and just prints it in the same way as printing the text.
void drawTime(float x)
{
  textFont(createFont("Hall Fetica", 10));
  text("Time: " + hour() + ":" + minute() + ":" + second() + " ", x, 20);
}
Running the GUI displayed the time and text, as expected. However, it was a very boring GUI, so to jazz it up, a background image was added. Google images was used to find a suitable image and then the Processing example "BackgroundImage" was used for reference. To add an image, the image needs to be copied into the same folder as the Processing code. In the code, the image needs to be stated and then loaded in setup.
PImage bg;

void setup() {
  size(960,600);        // Processing screen size, dummy GUI for prototyping
  bg = loadImage("Robot Skull.jpg");
  noStroke();            //  We don’t want an outline or Stroke on our graphics
}
The method noStroke() is to ensure that there are no outlines on the graphics. For the background image to load, the size of the GUI needs to be exactly the same size as the image. In the draw() method, the background code needs to be updated to represent the new background image.
void draw() {
 background(bg);
This GUI can be seen below.
Games Master GUI


ANIMATION

To improve the GUI further, animation was added. Processing has examples for animation but it was not suited to what was wanted. A site for gif animation on Processing was found and used. An animation library needed to be downloaded and copied into the libraries folder of Processing. There was then an example called "gifDisplay" which was used for reference. An animation of an explosion was found here. An explosion was wanted to simulate a robot being hit. The same animation would be used twice to show which robot had been hit. 

Firstly, the library needs to be imported into the code. Also, the gif images need to be stated.

import gifAnimation.*; //load animation library

Gif player1Gif;
Gif player2Gif;
In setup(), the images needed to be loaded and a frame rate chosen. Also, the animation that had been chosen had a loop built into it, so to only play once the repeat function was ignored in the animation. 
  frameRate(100); //frame rate for animation
  player1Gif = new Gif(this, "explosion_animation_gif.gif"); //load animation file
  player1Gif.ignoreRepeat(); //ensure animation only occurs once
  player2Gif = new Gif(this, "explosion_animation_gif.gif"); //load animation file
  player2Gif.ignoreRepeat(); //ensure animation only occurs once
In the draw() method, the position of the animation needed to be chosen. One animation was placed under the text for LaserBot 1 and the other was placed under LaserBot 2.
  //draw animations
  image(player1Gif, 10, height/2 - player1Gif.height/2);
  image(player2Gif, 680, height/2 - player2Gif.height/2);
To play the animation, the following line of code was added in where the GUI is detecting which robot has been hit (described in the next section).
//play animation of explosion
player1Gif.play();
Therefore, the GUI was setup so that if a LaserBot 1 hit LaserBot 2, LaserBot 1's score would be incremented and LaserBot 2's explosion animation would run (or vice versa). When a player won the game, a variable called 'won' was updated which would cause both animations to run continuously (by typing playerXGif.loop()) and at the bottom of the screen it would state who had won the game. To create the text at the bottom, a method exactly the same as drawType1() was created, except the text was changed to "Player X wins" and the position of where this text was drawn was changed. The final GUI can be seen below.
Animated Games Master GUI


PARSING CODE RECEIVED FROM ARDUINO

In the circle network setup, for the robots to send their code to the Games Master, they have to pass it along. Robot 1 has to pass its score to robot 2, who then passes is to the Games Master, while robot 2 can pass it to the Games Master straight away. The score packet that is sent is in the form [G210] (see post blah). 

In Processing, [G210] will be received and needs to be parsed to be able to increment the correct robots score. This is done in the serialEvent() method. Firstly, variables need to be stated. Score needs to be saved for each robot so variables are needed for that. Also, the packet received is made up of characters, so the score characters need to be concatenated together and then parsed to an Integer, so variables are needed for that. 
String score = "";
int robot1Score = 0;
int robot2Score = 0;
The method serialEvent(), which is being called every time there is a Serial message from the Arduino, contains all the code to parse the incoming message. First, the message is read in. If the input isn't null, the message is split up into separate characters and the first two characters are checked that they match '[G'. If they do match, the next character is read and stored as the robot ID and the next two characters after that are stored as scores. These two scores are then concatenated together and changed to an Integer. If the robot ID is equal to 1, then robot 1's score is incremented, else if the ID is 2, robot 2's score is incremented. Depending on what score has been sent, variables that identify the sensors (side or rear) are updated so that iPad GUI's can be updated to show which part of the robot has been hit. Also in this method, the animations are called to play to simulate the robot being hit. Finally, a variable called won is used to ensure that the scores can only be incremented up to a certain value (e.g. 50) and that the game will then end. The code for this serialEvent() method can be seen below.

//PROCESSING CODE
void serialEvent(Serial arduinoPort) 
//overrides  serial event, so that Arduino can echo received messages
// (serial monitor in Arduino IDE not available as processing is using serial port)
{
  input = arduinoPort.readStringUntil('\n');
  print(input);
  
  if (input != null){
    //should be [
    char bracketStart = input.charAt(1);
    //should be G
    char gamesMaster = input.charAt(2);
    //should be either 1 or 2
    char robotID = input.charAt(3);
    
    if (bracketStart == '[')
    {
      if (gamesMaster == 'G')
      {
        char score1 = input.charAt(4);
        char score2 = input.charAt(5);
        score = "";
        
        //concatenate the 2 numbers together for the score
        score += score1;
        score += score2;
        
        //convert to an int
        robotScore = Integer.parseInt(score);
          
        //delay for sensor hit lights  
        sensorMillis = millis();
        
        //delay for events
        if (won == false){
        if((millis() - serialDelay) > 5000){
        
        //increment the correct robots score
        if (robotID == '1')
        {
          robot1Score = robot1Score + robotScore;
          //if score is 5, side sensor has been hit
          if (robotScore == 5)
          {
            sideSensorHit2 = 1;
          }
           //if score is 10, rear sensor has been hit
          else if (robotScore == 10)
          {
            rearSensorHit2 = 1;
          }
          //play animation of explosion
          player2Gif.play();
        }
        else if (robotID == '2')
        {
          robot2Score = robot2Score + robotScore;
                    //if score is 5, side sensor has been hit
          if (robotScore == 5)
          {
            sideSensorHit1 = 1;
          }
           //if score is 10, rear sensor has been hit
          else if (robotScore == 10)
          {
            rearSensorHit1 = 1;
          }
          //play animation of explosion
          player1Gif.play();
        }
      }
        }
     }
    }
    input = "";
    serialDelay = millis();
  }
}
That concludes all the code needed for the Games Master GUI.

Leave a Reply

Theme MXS. Powered by Blogger.