158 – Galactic Wing

For the rest of the year, I’m going to highlight some impressive final projects in Computer Programming and BC Calculus.
The computer programming students have had the majority of the term to work on their projects and one of the requirements was to write a blog post about their experience.
Here’s a crosspost of a pair of programmers who wrote a LOT of impressive code in processing. And here’s part 2, from the other student’s point of view.
You’re going to need to download the code and run it on your own computer. Totally worth it.

The Code Behind Galactic Wing

Game Designer, Lead Programmer/Developer, Sound Designer, and Music Producer – Nicklas Kenyon

Graphic Designer, Lead Artist,  and Programmer/Developer – Caleb Perry

Galactic Wing and Processing
Galactic Wing was written in Processing (It’s free at https://processing.org/download/).

Click to Download Game

Some of the code you see uses functions built into Processing.
For this review of my project I am going to explain the major parts of code, because the code itself is quite lengthy.

 

How the Code is Organized

Setup looks like this:

void setup(){
  size(1280,720);
  background(0);
  //loads images at the start of the game.
  loadimages();
  //Setup_FileRW creates the file reader, and then loads the save file.
  Setup_FileRW();
  //Assigns default or saved values before the game starts
  Setup_AssignValues();
  //Creates new instances of all lists that are requiered in the game.
  Setup_CreateLists();
  //If a Lists needs objects at the beginning of the game, those objects are added using the Setup_FillLists method
  Setup_FillLists();
  //Runs the initial setup of Music Players
  Setup_MusicPlayers();
}

Setup is composed of several separate setup packages so that the setup does not get cluttered. With all of the separate functions, I can keep better track of where things are, and when they happen. This same idea of organizing the code into several different functions and methods is used throughout the entire code.

Void draw also follows this organization

void draw(){
 //if the console isn't being used, continue running whichever gamemode is selected 
  if (consoleactive==false){ 
    //if the gamemode is 0, run the MainMenu package, which displays the main menu, and checks to see what the user wants to do.
    if (gamemode==0){
      MAINMENU();
    }
    //When campaign mode is made, gamemode 1 will run a campaign mode package.
    if (gamemode==1){
      CAMPAIGNMODE();
    }
    // If the gamemode is 2, the Chaos Mode package is run, which is a survival stlye gamemode
    // in which the player tries to dodge more and more asteroids as time goes on.
    if (gamemode==2){
      CHAOSMODE();
    }
    //if the gamemode is 3, the Hangar package is run, which brings up a view of the hangar, where the user can select and unlock ships.
    if (gamemode==3){
      HANGAR();
    }
    if (gamemode==4){
      //opens tribute mode
     TRIBUTEMODE(); 
    }
    if (gamemode!=3){
      //resets Hangar fade mode
      FadeHangarOff = false;
      //resets Opacity for all images to completely Opaque.
      tint(255,255);
    }
  }
  //runs the console package
  console_package();
  //Plays the music
  PlayMusic();
  //Runs the Character Package
  CHARACTER();
}

Each of the fully capitalized methods in void draw are what I like to call “packages” (i.e. “CAMPAIGNMODE()”).

Going into detail about each function would become monotonous and boring, so I will just touch on some other important aspects of the game.

The Save/Load System

The saving and loading of a save file is a game feature I am very proud of. Thankfully, Processing has some built in functions that make this a bit easier.

The code looks like this:

PrintWriter savefile;
int fivescores = 0;
BufferedReader savefile_reader;
String savefile_string;
PFont filefont;
int count = 0;
boolean FileIsRead = false;
// void Setup_File_RW() is in the "Setup" tab.

void SaveGame(){
  writesavefile();
}

void LoadGame(){
  if(FileIsRead==false){
    readsavefile();
    FileIsRead=true;
  }
  }

void writesavefile(){
  
  savefile = createWriter("savefile.txt");
  savefile.print("Gold="+Gold+"~");
  savefile.print("Rank="+Rank+"~");
  savefile.print("Experience="+Experience+"~");
  savefile.print("End of File~");
  println("Saved");
  savefile.flush();
  savefile.close();
}

void readsavefile(){
      try{
        savefile_string = savefile_reader.readLine();
        
      } catch (IOException e) {
         e.printStackTrace();
         savefile_string = null;
      }
      if (savefile_string == null) {
          //nothings gonna happens
      } 
      else {
        String[] savefile_list = split(savefile_string, "~");
        println("SFList: "+savefile_list.length);
        
        //Gold
        String SaveData_String_Gold = savefile_list[0];
        String[] SaveData_Array_Gold = split(SaveData_String_Gold,"=");
        ;
        Gold = int(SaveData_Array_Gold[1]);
        println("Gold: "+Gold);
        //Rank
        String SaveData_String_Rank = savefile_list[1];
        String[] SaveData_Array_Rank = split(SaveData_String_Rank,"=");
        //Rank = int(SaveData_Array_Rank[1]);
        println("Rank: "+Rank);
        //Experience
        String SaveData_String_Experience = savefile_list[2];
        String[] SaveData_Array_Experience = split(SaveData_String_Experience,"=");
        Experience = int(SaveData_Array_Experience[1]);
        println("Experience: "+Experience);
  }
}

As you can see, the save file takes user variables, such as Gold, and saves them. When the game starts, the save file is loaded.

This is what the save file looks like:

2014-06-01_02h34_57

 

This is a very simple way of storing information, but for the purposes of this game, it is sufficient.

 

The Command Console

The Command Console took forever.

At first, I was just checking an ArrayList of Strings. When I wanted to get more advanced and have the text scroll, and be able to view input history, I decided to make a “Input” class, that had a String variable and a “y” variable for the text’s y position in the history view.

Code:

class CommandInputs{
  //y position of the string in the console view
  int ypos;
  // the text that the user has input into the console.
  String inputtext;
  CommandInputs(int y, String text){
    ypos=y;
    inputtext=text;
  }
  void moveup(){
    //move the line up by the height of the font (i'm using font size 12, so I give a little bit of space by adding 14)
    ypos+= 14;
  } 
}

Here is a demo of the console:

Collision Detection

Currently, the game uses a radius based method of collision detection. Another, “pixel-perfect” method of collision detection is in the making, but is not functional enough to put into the game at this time.

A bit of sample code

//if an asteroid hits another asteroid, it bounces off
void check(Asteroid b){
  float d = dist(xpos, ypos, b.xpos, b.ypos);
    if (d <= ((diam+b.diam)/2)){
      if (diam<=b.diam){
      xspeed *= -1;
      }
    }
}

Loading Maps in Campaign Mode

As is the case in many 2D games, I decided to uses a pixel sheet to design and create my maps. The map reader gets the color information from each pixel in a map sheet, and then decides what object to place in that relative position based on the color.

Examples
Map_1

Map_2
Red pixels are stationary asteroids.
Green pixels are moving asteroids.

What about Gameplay?

There are two game modes in Galactic Wing; Chaos Mode and Campaign Mode.

Chaos mode is a survival style game mode in which you get one ship and try to stay alive, dodging asteroids and shooting gold as more and more asteroids enter your area of space.

Your ammo is replenished with each wave of asteroids.

 

Campaign mode is in early development but features a progression through different asteroid fields of increasing difficulty.

As you play, you will gain experience and be promoted to new ranks. The greater your rank, the more ships you can control.

 

As you play, you will gain gold, experience, and rank. With gold, you can buy new ships in the hangar.

The Controls

The controls for Galactic Wing are a bit unique. Instead of holding down the WASD keys to move, you press them to increase or decrease you x and y speeds.

To fire a laser, press the space bar.

If you manage to go into the hangar, you can press the “f” key to go back. The hangar is in development, so enter at your own risk.

To open the console, press the “/” key. When you are done typing in your command, press enter to submit it and close the console.

To go back to the main menu at any time, press the “m” key.

 

Second Post

What is Galactic Wing?

Galactic Wing is a Top Down Shooter that puts you in command of your very own fleet of weaponized space shuttles. You destroy asteroids and gold comets in order to earn gold. This gold can be spent in the hangar where you can upgrade all the ships in your fleet. The game comes with two game modes, Chaos Mode and Story/Campaign Mode. Chaos mode is where you simply have your fleet of ships with a constantle increasing number of asteroids in your vicinity; fight to survive. Campaign mode is where you have a combination of timed obstacle courses (still UC) and battles following a level type system. Its pretty much the best game processing has ever seen.

How I contributed to Galactic Wing

Galactic Wing was already in the making way before I was inducted into the project, so most of the core code was already there, however a hangar was yet to be constructed and there were no signs of any artwork. So my contribution to Galactic Wing was to create all the artwork for the game and to construct a hangar where the user could go to to buy new upgrades for his fleet.

What problems did I run into?

In order to not have variables for each and every ship(which would have been 40 variables in total), I decided to use class for the ships. this wasn’t really a problem, there were just some growing pains when trying to figure outt he weird way you have to use classes in processing

This is the code for the Ships class including the supporting method for the first hangar.

class Ships {
  boolean shipbought = false;//pretty self explainitory
  String shipname;
  int shipdamage;
  int shiparmor;
  int shipspeed;
  int engineerXmin;
  int engineerXmax;
  int shipprice;
  int Gold;
  Ships (String name, int damage, int armor, int speed,int Xmin,int Xmax,int price,int gold)
  {
    Gold = gold;
    shipprice = price;
    shipspeed = speed;
    shipdamage = damage;
    shiparmor = armor;
    shipname = name;
    engineerXmin = Xmin;
    engineerXmax = Xmax;
  }
  void displayh1() {
    if ((backgroundnum == 1)&&(engineerX > engineerXmin)&&(engineerX < engineerXmax))
    {//The following 70 lines of code are dedicated to displaying info about the ship, weather you can buy the ship(or if you've already bought the ship), and the price of the ship.
/*1*/ rectMode(CORNER);
      stroke(100);
      strokeWeight(15);
      fill(120,120,120,127);
      tint(255,127);
      rect(100,20,1000,520,40);
      tint(255,255);
      textFont(InfoTextHeader);
      textAlign(LEFT,CENTER);
      fill(0,0,200);
      text(""+ shipname +"",210,50);
      textFont(InfoTextTest);
      rectMode(CORNER);
      strokeWeight(3);
      fill(150);
      rect(210,200,803,15);
      rect(210,300,803,15);
      rect(210,400,803,15);
      noStroke();
      if(shipdamage < 33) {
      fill(red);
      rectMode(LEFT);
      }
      else if(shipdamage <66) {
        fill(yellow);
      }
      else if(shipdamage <=100) {
        fill(green);
      }
      rectMode(CORNER);
      //offset in the x and y by 2 to compensate for the stroke on the outlined boxes
      rect(212,202,float(shipdamage)/100*800,12);
      text("Damage: "+ shipdamage +"",250,150);
      if(shiparmor < 33) {
        fill(red);
      }
      else if (shiparmor <66) {
        fill(yellow);
      }
      else if(shiparmor <=100) {
        fill(green);
      }
      rect(212,302,float(shiparmor)/100*800,12);
      text("Armor: "+ shiparmor +"",250,250);
      if(shipspeed < 33){ 
        fill(red);
      }
      else if(shipspeed <66) {
        fill(yellow);
      }
      else if(shipspeed = shipprice) {
            Gold = Gold - shipprice;
            shipbought = true;
            s2.Gold = s1.Gold;//These 5 lines of code solves a problem where you would have your money instantly replenished to the default value whenever you bought a ship.
            s3.Gold = s1.Gold;
            s4.Gold = s1.Gold;
            s5.Gold = s1.Gold;
            s1.Gold = Money;
            println(""+ Money +"");
          }
          if((Gold <= shipprice)&&(shipbought == false)) {
            fill(50);
            rectMode(LEFT);
            rect(445,485,800,530);
            fill(200,0,0);
            text("INSUFFICIENT FUNDS",450,500);
          }
        }
      }
    }
  }

 

Another problem was trying to use a totally public variable to draw your gold value from. This was issue because you had to pass a variable through the class, which would then be translated to another variable that would actually be used in the proceeding code. So, in order to fix that issue I created a variable called money that would be public and another variable called gold which would be seen only inside the 3 classes for each hangar. So at the end of the frame the class would make their money count equal to each other across the board first. Then outside the class I would call the variable for gold of the hangar the gold was spent in  (ex. “Ships.gold” or “sx.Gold” for each individual ship, where x is the ship number) and would make that equal to money so that upon the next variable passing, the Money variable would be updated to the value each hangar’s gold value was last seen at. It seems simple now but at the time I just couldn’t figure it out.

I have since found a few much simpler ways of doing this.

 

Another problem that I had was having the background display the correct image. This issue arose when I had multiple parameters inside the draw() sequence that led to different backgrounds than the one I wanted being displayed, or if the program came to the correct background to be displayed, it would instead just display a black image. Processing didn’t seem to like that, so instead what I did was I made a main variable called background that was an empty PImage. This would be set using the parameters talked about before only this time I would would do this process outside of the draw(). So inside the draw() i had only one background command but the image variable passed through that background command would be the thing to change change.

So this isn’t the real code, but it looked something like this:

PImage background;
PImage hangar1;
PImage hangar2;

void setup() {
size(1280,720);
hangar1 = image(*image directory*);
hangar2 = image(*image directory*);

void draw() {
checkhangar();
background(background);

void checkhangar() {
if(guy is in hangar 1) {
  background = hangar1;
  }
if(guy is in hangar 2) {
  background = hangar2;
  }
}

I’m not really sure why the first method that was malfunctioning wasn’t working, but either way that’s how I fixed the issue.

Pixel art in Photoshop

Making the ships for Galactic Wing was one of the most fun things I’ve done in Photoshop because it was both gratifying and informative. It was informative because in order to mae the ships small enough while still making them look sharp, I needed to learn how to make pixel art.

Each ship in galactic wing had to be 50 x 50 in order to work with collision so I made a template in 50 x 50 in order to go through the whole process.

 

 

 

 

 

 

 

 

 

 

 

 

 

Whence we create a ship in the 50×50 state we need to scale it so we can display it in the hangar while keeping is crisp and not blurry beyond recognition.

So we go to Image > Image Size, then we will put in the desired resolution. Then in the drop down box in the bottom of the window we will select the option “Nearest Neighbor (preserve hard edges)” then press OK.

 

dan

 

Leave a Reply

Your email address will not be published. Required fields are marked *