S P A C E

So I took a whole weekend to make this game…

Description

In this simple game, the player controls a spacecraft whose mission is to collect StarBits, however, they must avoid colliding with meteors or they will risk loosing all the progress they have made. Score is displayed on the upper left corner. When the player collides with a meteor, the spaceship becomes red for a while and the score keeps decreasing until they get out of the way of the meteor.

Idea

I got inspiration from one of Daniel Shiffman’s Coding Challenges titled Starfield and from Super Mario Galaxy in which Mario collects this star shaped things called Starbits.

Process

After watching the video and playing a little of Mario Galaxy with a friend, I decided to write down the classes and functions that I thought I would need for this assignment. I did this mainly because last assignment was very chaotic for me in terms of knowing what I needed to do, so I decided to write how I was thinking of making the code rather than just go at it.

This is what I ended up with:

I decided to star with the background and do the star object first because I thought that after watching the video, I could do it in a breeze, after all Shiffman’s video is 13 minutes.

I took a whole day to figure out how to do just the background. Also, the field that I was making only appeared on the lower right hand corner. Like this:

So I texted on the Discord chat and Professor Shiloh and Professor Aaron came up with a solution, thank God.

Anyways, moving on.

After doing the Stars, I proceeded to make the spaceship. I did two versions of it: one controlled with the keyboard and one with the mouse. I wasn’t sure which one to leave because I felt like they had a different range of motion and deciding would rely on how the meteors and starbits.

Once the controls for the spaceship were done and the image of it was resized, I decided to make the meteors.

(Just a side note: I know that I am actually doing meteroids and not meteors, but meteors is just easier on my poor brain).

I think this was the hardest part of the process. The meteors were just not cooperating with me. When they were immobile and I just placed them at random positions, they looked beautiful, but then, when I started to make the functions to move them, it was just horrible. My idea was to do a similar mechanic to the stars and increase the meteor’s sizes as it moved through the screen. Nonetheless, when I tried to resize it, the more time I left it moving, the blurrier the image got.

Like this:

After spending almost a day in useless attempts to better this,  I decided to text on the Discord group and professor Aaron recommended using the scale() function along with changing the values I used to map the size of the meteor.

It worked, but I still had problems with the fact that the resizing was not stopping but getting way past the size that I wanted it to be. The problem was that I wasn’t aware that the map function did not restrain the value from continuing to increase, so I had to learn how to use constrain(). Furthermore, I also had to learn how to use the push/popMatrix() and the push/popStyle().

For the Starbits, I decided to use the star() function from the Processing reference. I had issues figuring out how to change the position of the star itself (every time I tried, what changes was the rotation or the size, but not the position). I don’t know if it is just me, but I found that some of the explanations given on the Processing reference could use a little more information about what the values passed down in the function represent.

After figuring out how to change the position of the Starbit, it was time to do the collisions. Professor Aaron taught me how to do it in an easier way than how I was doing it. There was a problem however with the values that the keyboard was giving. I printed them out and they were way too big in comparison with the location of the canvas. I later found out it was because the function was multiplying the value of the keyboard by two. I reused the same logic for detecting collisions to collect the Starbits. I then used the collision to change the tint of the spaceship to red when it got hit and to generate a new starbit in a random location when the player collected it.

When I put it all together, I didn’t really like the movement of the spaceship with the keyboard. It was too linear for the player to be able to avoid colliding with the meteors, so I opted for the mouse controls instead.

Finally, it was time to do the score. I had to watch a tutorial for the score and learn how to use the text() and its style functions. I placed conditions to increase the score when collecting the Starbit and decrease it when colliding with a meteor. I forgot to call the functions in the main which is why when I placed the code within the assignment, it wasn’t working.

I cried of happiness when I started to see the score change in the screen.

As a final touch, I decided to make the color of the score become red when the score dropped below 0.

Code for the Main Tab
// make variables and initialize them with the sizes of the array for the Stars
int sizeStars = 1000;

//make variable of class spaceship
Spaceship spacecraft;

//make variable of class meteor
Meteor meteor;

//make variable of class Starbit
StarBit starBit;

//make array of class Star
Star[] stars = new Star[sizeStars];

//make variable of class score
Score score;

void setup()
{
  //set size and background
  size(1000, 700);
  background(0);

  //call constructor for spacecraft
  spacecraft = new Spaceship();

  //call constructor for meteor
  meteor = new Meteor();

  //call constructor for Starbit
  starBit = new StarBit();

  //call constructor for score
  score = new Score();

  //create a Star object for each element in the array
  for (int i = 0; i < sizeStars; i++)
  {
    stars[i]= new Star();
  }
}

void draw()
{
  background(0);

  //display the Starbit using the function
  starBit.display();

  //update the position of the spacecraft
  spacecraft.updatePosition();

  //show the spacecraft
  spacecraft.show();

  //update the position of the meteor
  meteor.updatePosition();
  //show the meteor
  meteor.show();

  //check if there has been a collision to decrease score
  score.collided();

  //check if player collected Starbits to increase score
  score.collected();

  // check if the spacecraft changed tint from collision
  spacecraft.changeTint();

  //check if player collected Starbit to create a new Starbit
  starBit.createStarBit();

  //display current score
  score.display();

  //change position of the stars in the canvas
  translate(width/2, height/2);

  //update the position of the stars and show them in the canvas
  for (int i = 0; i < sizeStars; i++)
  {
    stars[i].updatePosition();
    stars[i].show();
  }
}
Meteor Class
class Meteor
{
  //create varaible to hold image of the meteor
  PImage meteor;
  //create variables for the x, y and z position of the stars
  float x, y, z;
  //create variable to hold the direction (speed) at which the meteor will move
  float xDir, yDir;

  //constructor
  Meteor()
  {
    //load image of the meteor to the variable
    meteor = loadImage("meteor.PNG");

    //initialize the position of the meteor at random location
    x = random(width);
    //y can be left at 0 because the image size is 0 at the beginning
    y = 0;

    //assign random speed in the y direction
    yDir = random(7, 20);

    /*assign random speed in the x position depending on whether the value of x is
     less than the width*/
    if (x < width/2) {
      xDir = random(7, 20);
    } else {
      xDir = random(-20, -7);
    }

    //testing at the center
    //x = width/2;
    //y = height/2;

    //assign random value to z
    z = random(width);
  }

  //function to reset the values of Meteor
  void reset()
  {
    x = random(width);
    y = 0;
    z = width;

    yDir = random(7, 20);
    if (x < width/2) {
      xDir = random(7, 20);
    } else {
      xDir = random(-20, -7);
    }
  }

  //function to update the position of the stars as they move
  void updatePosition()
  {
    //add speed to x and y 
    x += xDir;
    y += yDir;
    //changing the z position
    z = z - 10;

    // re-set the positions
    if (z < 0)
    {
      reset();
    }
  }

  //show the meteor in the canvas

  void show()
  {
    //make variable that increases the size of the meteor as it travels through the screen
    float size = map(z, 0, width, 0.8, 0.02);
    //limit how bit the size can get 
    size = constrain(size, 0.02, 0.8);

    //show the image
    pushMatrix();

    //translate to position of meteor so that it scales at the center of the image
    translate(x, y);
    //scale the meteor to its current size
    scale(size);
    //show the meteor

    //center the image 
    pushStyle();
    imageMode(CENTER);
    image(meteor, 0, 0);
    popStyle();

    popMatrix();
  }
}
Spaceship Class
class Spaceship
{
  //create variable to hold image of the Spaceship
  PImage spaceship;
  //create variables for the x, y position of the spacecraft
  int x, y;
  int speed = 5;
  boolean collided, collected;

  //constructor
  Spaceship()
  {
    //initialize the x and y position at the center
    x = width/2;
    y = height/2;

    //load image of the meteor to the variable
    spaceship = loadImage("SpaceShip.PNG");
  }

  //function to update the position of the spacecraft by tracking the mouse position
  void updatePosition()
  {
    //assign the position of the mouse to the x and y variables of the spaceship
    x = mouseX;
    y = mouseY;
  }

  //show the spaceship in the canvas
  void show()
  {
    //place the cursors at the center of the image
    imageMode(CENTER);
    //draw the spacecraft where the mouse is located
    spaceship.resize(200, 200);
    image(spaceship, x, y );
  }

  //returns true if the player had "collected" the starbit or false if they haven't
  boolean checkCollect()
  {
    //make variable to calculate distance between the spaceship and the Starbit
    float distance = dist(mouseX, mouseY, starBit.xPos, starBit.yPos);
    //radius of space that the starBit occupies
    float radius = 30;

    /*if distance between the starbit and the spaceship is smaller than the radius, 
     then the player has collected the starbit*/
    if (distance < radius)
    {
      return true;
    } 
    //else, player hasn't collected the starbit
    else
    {
      return false;
    }
  }
  //returns true if the player has "collided" with a meteor or false if they haven't
  boolean checkCollide()
  {
    //make variable to calculate distance between the spaceship and the meteor
    float distance = dist(mouseX, mouseY, meteor.x, meteor.y);
    //radius of space that the meteor occupies
    float radius = 100;

    /*if distance between the meteor and the spaceship is smaller than the radius, 
     then the player has collided with the meteor*/
    if (distance < radius)
    {
      return true;
    } 
    //else, player hasn't collided with the meteor
    else
    {
      return false;
    }
  }

  //change tint of the spaceship to red if the collision happened
  void changeTint()
  {
    collided = checkCollide();
    //collected = checkCollect();

    if (collided == true)
    {
      pushStyle();
      tint(255, 0, 0);
      image(spaceship, mouseX, mouseY);
      popStyle();
    }
  }
}
Star Class
class Star
{
  //create variables for the x, y and z position of the stars
  float x, y, z;

  //constructor
  Star()
  {
    //initialize the position of the stars at random locations
    x = random(-width, width);
    y = random(-height, height);
    z = random(width);
  }

  //function to update the position of the stars as they move
  void updatePosition()
  {
    //decrease value of z
    z = z - 20;

    //reset the star's position
    if (z < 1)
    {
      x = random(-width, width);
      y = random(-height, height);
      z = width;
    }
  }

  //show the star in the canvas
  void show()
  {
    fill(255);
    noStroke();

    //divide the x and y position by x and map it with the width and height to move the stars
    float newXpos = map(x/z, 0, 1, 0, width);
    float newYpos = map(y/z, 0, 1, 0, height);

    //increase radius of the star as the get closer to the end of the canva
    float radius = map(z, 0, width, 10, 0);

    //use a circle to draw the stars
    circle(newXpos, newYpos, radius);
  }
}
StarBit Class
class StarBit
{
  //make variables for x and y position of the Starbit
  int xPos, yPos;
  /*radius1 is for how big the starBit will be, radius2 is for the dents between the 
   points of the StarBit*/
  float radius1 = 20;
  float radius2 = 40;
  //make variable for the number of points in the starBit
  int numPoints = 5;
  //make variables to hold the red, green and blue colors of the rgb
  int r, g, b;

  //make bolean to check if StarBit was collected
  boolean collected;

  //constructor
  StarBit()
  {
    /*assign random x and y positions to the starbits (the 35 is so that they don't)
     appear outside the canva*/
    xPos = round(random(35, width-35));
    yPos = round(random(35, height-35));

    //assign random values for the rgb 
    r = round(random(255));
    g = round(random(255));
    b = round(random(255));
  }

  //create a new starbit
  void newStarBit()
  {
    xPos = round(random(35, width-35));
    yPos = round(random(35, height-35));

    r = round(random(255));
    g = round(random(255));
    b = round(random(255));
  }

  //make shape of starbit (taken from Processing refence)
  void star(float x, float y, float radius1, float radius2, int npoints)
  {
    float angle = TWO_PI / npoints;
    float halfAngle = angle/2.0;

    beginShape();
    for (float a = 0; a < TWO_PI; a += angle) {
      float sx = x + cos(a) * radius2;
      float sy = y + sin(a) * radius2;

      fill(r, g, b);
      vertex(sx, sy);
      sx = x + cos(a+halfAngle) * radius1;
      sy = y + sin(a+halfAngle) * radius1;
      vertex(sx, sy);
    }
    endShape(CLOSE);
  }

  //display the starBit
  void display()
  {
    pushMatrix();
    //make the star rotate
    translate(xPos, yPos);
    rotate(frameCount/ -100.0);
    //draw the starbit
    star(0, 0, radius1, radius2, numPoints);
    popMatrix();
  }

  //create starBit if the current starBit had been collected
  void createStarBit()
  {
    //asign value returned by spacecraft.checkCollect()to variable
    collected = spacecraft.checkCollect();
    if (collected == true)
    {
      newStarBit();
    }
  }
}
Score Class
class Score
{
  //make variable to hold the score
  int score;
  //make variable that will be used to display the score
  String s;
  //variables to check for staBit collection and meteor collisions
  boolean collected, collide;

  //constructor  
  Score()
  {
    //initialize score to 0
    score = 0;
    s = "Score: " + score;
  }

  //increase score if Starbit has been collected
  void collected()
  {
    //assign value returned by spacecraft.checkCollect()
    collected = spacecraft.checkCollect();
    //increase score if starbit was collected
    if (collected == true)
    {
      score ++;
    }

    //update s with the new score
    s = "Score: " + score;
  }
  //decrease score if there was a collision with a meteor
  void collided()
  {
    //assign value returned by spacecraft.checkCollide();
    collide = spacecraft.checkCollide();

    //if collided, decrease score until player gets away from the meteor
    if (collide == true)
    {
      score --;
    }

    //update s with the new score
    s = "Score: " + score;
  }

  //display the score
  void display()
  {
    //if score is below 0, show the text in red
    if (score < 0)
    {
      textSize(20);
      textAlign(LEFT);
      fill(255, 0, 0);
      text(s, 10, 20);
    } 
    //else show them in white
    else 
    {
      textSize(20);
      textAlign(LEFT);
      fill(255);
      text(s, 10, 20);
    }
  }
}

 

Result:

 

Final Thoughts

I really like how this assignment turned out, but I must admit it was very difficult. I feel like most of this project was me doing something, seen it work when tested, then seen it failed once I implemented it in the actual code and asking for help. To be honest, I feel like most of the time I was asking for help because I had no idea of what I was doing wrong or how to start debugging.

I think one of my biggest problem was keeping track of whether I was calling the functions or not. I made a lot of classes and a lot of versions of the project because I was scared of messing up what I had, yet that also made it hard because I had one place in which I was testing the score, one in which I was testing the collisions, etc, and when I placed them together, it was very easy to forget to add stuff to the main tab. I guess this is something that I will fix with practice.

Another thing that was difficult was centering the images and keeping track of the points of reference used. At some point it became very confusing to know which point of reference was been used by which object. Another thing was that I didn’t really understood the difference between push/popMatrix and push/popStyle. Now I know, thank to Prof. Aaron. This made me realize that a lot of my problems could be fixed more easily if only I knew the correct syntax.

What I think was what made me very slow in my process, however, was coming up with the logic of the code. It was very extremely difficult to visualize what I was doing and think of other ways to accomplish something when my initial idea failed. I also think that this is something that will become easier the more I practice, but man it was hard (and at times discouraging), but still, after taking a break I feel like I was able to solve many issues by myself.

I am extremely happy with this mini game and after completing it I had a lot of fun playing (I even sent it to my parents so they could play).

This is the link to the GitHub with the code. If you decide to play it, I hope you enjoy it!

Leave a Reply