/*
Shield Defense Base: By Michael Downey
A small little game where you control a slippery moving tank that
generates a shield above it, and try to defend the base beneath
you against a meteor shower. As the game goes on, the sky gets
darker, the meteors speed up, and the impact deals more damage.
However, faster meteors give a higher score. When the base is
destroyed, your final score is displayed.
*/
//declare all objects
int numberOfRocks = 5;
int numberOfStars = 20;
Rock[] meteorShower= new Rock[numberOfRocks];
Base base;
//Collision[] explosions= new Collision[numberOfRocks];
Star[] stars= new Star[numberOfStars];
Tank tank;
Timer timer;
//declare all global variables
float difficultyTimer;
float score = 0;
int scoreDisplay = 0;
int finalScore = 0;
//declare all values related to the base globally to ensure that other classes can use them
float baseLeft = 0;
float baseTop = 500;
float baseRight = 800;
float baseBottom = 600;
float baseHealth = 500; //health of the military base
//declare shield collision values globally to ensure that other classes
float shieldLeft;
float shieldTop;
float shieldRight;
float shieldBottom;
//declare booleans for movement and game over
boolean leftPressed = false;
boolean rightPressed = false;
boolean gameOver = false;
void setup() {
//set up the window size, the frame rate, and the main background color
size(800, 600);
frameRate(60);
rectMode(CORNERS);
noStroke();
//Activates the game objects
createMeteorShower();
//createExplosions();
createStars();
base=new Base();
tank=new Tank();
timer=new Timer();
}
void createMeteorShower() {
for (int i=0; i<meteorShower.length; i++) {
meteorShower[i]=new Rock();
}
}
//explosions were going to be a seperate class, but creating them in the same class as the meteors proved mroe efficient
/*void createExplosions() {
for (int i=0; i<explosions.length; i++) {
explosions[i]=new Collision();
}
}*/
void createStars() {
for (int i=0; i<stars.length; i++) {
stars[i]=new Star();
}
}
void draw() {
//updates all of the major game objects
timer.update();
for (int i=0; i<stars.length; i++) {
stars[i].update();
}
for (int i=0; i<meteorShower.length; i++) {
meteorShower[i].update();
}
tank.update();
//prevents the base health from going below zero
if (baseHealth < 0) {
baseHealth = 0;
}
//draw the background
background(0, 0, 30);
for (int i=0; i<stars.length; i++) {
stars[i].display();
}
fill(0, 200, 255, 250 - difficultyTimer / 100); //The brightness of the background is an indicator of the meteor shower's average speed
noStroke();
rectMode(CORNERS);
rect(0, 0, 800, 600);
//display major game objects
base.display();
tank.display();
for (int i=0; i<meteorShower.length; i++) {
meteorShower[i].display();
}
//display the score
fill(0 + difficultyTimer / 100);
textSize(32);
scoreDisplay = int(score);
text("Score:" + scoreDisplay, 0, 32);
//Activates game over and reveals final score
if (baseHealth <= 0) {
fill(0);
gameOver = true;
rectMode(CORNERS);
rect(0, 0, 800, 600);
fill(255);
textSize(64);
finalScore = int(score);
text("GAME OVER! Score:" + finalScore, 25, 300);
}
}
void keyPressed() {
//detect if the player has pressed left or right
switch(key) {
case 'a':
leftPressed = true;
break;
case 'd':
rightPressed = true;
break;
}
}
void keyReleased() {
//detect if the player has pressed left or right
switch(key) {
case 'a':
leftPressed = false;
break;
case 'd':
rightPressed = false;
break;
}
}class Base {
int colorVariation = 0; //variable for animating base lights
int variationIncrement = 2; //controls the incrementation of the color shift
Base() {
init();
}
void init() {
}
void update() {
}
void display() {
//draw the base itself
noStroke();
rectMode(CORNERS);
fill(150,150,150,255);
rect(baseLeft, baseTop, baseRight, baseBottom);
//draw the base's lights
ellipseMode(RADIUS);
fill(128 + colorVariation,0,0);
ellipse(80,550,5,5);
ellipse(160,550,5,5);
ellipse(240,550,5,5);
ellipse(320,550,5,5);
ellipse(400,550,5,5);
ellipse(480,550,5,5);
ellipse(560,550,5,5);
ellipse(640,550,5,5);
ellipse(720,550,5,5);
//control the light "animation"
colorVariation = colorVariation + variationIncrement;
if (colorVariation > 50) {
variationIncrement = -2;
}
if (colorVariation < -50) {
variationIncrement = 2;
}
//draw the base's health bar
fill(255,0,0);
rect(275,565,525,585);
fill(0,255,0);
rect(275,565,275 + baseHealth / 2,585);
}
}/*class Bullet {
Bullet() {
init();
}
void init() {
}
void update() {
}
void display() {
}
}*/
//There were plans to use a superweapon, but gameplay tests without it seemed simple and fun enough to determine it wasn't needed, leaving this bullet a remnant./*class Collision {
Collision() {
init();
}
void init() {
}
void update() {
}
void display() {
}
}*/
//Initial plans were to code the base explosions into this collision class, however, it was decided that coding the explosions into the rocks worked just as wellclass Rock {
//declare necessary PVectors
PVector position = new PVector();
PVector oldPosition = new PVector();
PVector velocity = new PVector();
//declare necessary variables
float rockLeft, rockRight, rockTop, rockBottom;
float rockDamage;
int explosionCounter;
boolean rockBoomBase = false;
boolean rockBoomShield = false;
Rock() {
init();
}
void init() {
//set initial rock values.
position.x = random(0, 800);
position.y = 0 - random(200);
velocity.y = random(.5, 2) + difficultyTimer / 600;
}
void update() {
//checks for game over
if (gameOver == false) {
//move the rock downwards
position.add(velocity);
//reconfirms the rock's position for collisions
rockLeft = position.x;
rockTop = position.y;
rockRight = position.x + 50;
rockBottom = position.y + 50;
//checks to see if the rock has impacted the base
if (rockBottom > baseTop) {
rockBoomBase = true;
rockBoomShield = false;
explosionCounter = 0;
rockDamage = 2 + velocity.y;
oldPosition.x = position.x + 25;
oldPosition.y = position.y + 50;
init();
baseHealth = baseHealth - rockDamage;
}
//checks to see if the rock has impacted the shield
if (rockRight > shieldLeft && rockLeft < shieldRight && rockBottom > shieldTop && rockTop < shieldBottom) {
rockBoomShield = true;
rockBoomBase = false;
score = score + 5 * velocity.y;
explosionCounter = 0;
oldPosition.x = position.x + 25;
oldPosition.y = position.y + 50;
init();
}
}
}
void display() {
//draws the rock
noStroke();
ellipseMode(CORNERS);
fill(147, 83, 0, 255);
ellipse(position.x, position.y, position.x + 50, position.y + 50);
triangle(position.x, position.y - 10, position.x + 35, position.y + 10, position.x, position.y + 20);
triangle(position.x + 10, position.y + 15, position.x + 35, position.y, position.x + 60, position.y + 15);
triangle(position.x + 9, position.y + 15, position.x + 34, position.y + 30, position.x + 59, position.y + 15);
triangle(position.x, position.y + 25, position.x + 20, position.y + 55, position.x + 50, position.y + 25);
triangle(position.x, position.y + 25, position.x - 5, position.y + 55, position.x + 30, position.y + 50);
//checks for base explosion and draws it
if (rockBoomBase == true) {
fill(255, 150, 0, 255 - explosionCounter * 5);
ellipseMode(CENTER);
ellipse(oldPosition.x, oldPosition.y, 15 + explosionCounter * 2, 15 + explosionCounter * 2);
explosionCounter++;
}
//checks for shield break and draws it
if (rockBoomShield == true) {
fill(147, 83, 0, 255 - explosionCounter * 5);
ellipseMode(CENTER);
ellipse(oldPosition.x, oldPosition.y, 5 + explosionCounter * 2, 5 + explosionCounter * 2);
explosionCounter++;
}
}
}/*class Shield {
Shield() {
init();
}
void init() {
}
void update() {
}
void display() {
}
}*/
//Originally, the shield and the tank would be seperate classes, but making them into the same class was more efficientclass Star {
//declares variables
PVector position = new PVector();
float transparency;
int lengthIncrease;
Star() {
init();
}
void init() {
//sets the position and transparency of the star
position.x = random(800);
position.y = random(600);
transparency = random(200,255);
lengthIncrease = 1;
}
void update() {
//controls animation parameters
lengthIncrease = lengthIncrease + 1;
transparency = transparency - 10;
if (transparency < -50) {
init();
}
}
void display() {
//draws the star
stroke(255,255,255,transparency);
strokeWeight(random(2,6));
line(position.x,position.y,position.x + 1 * lengthIncrease,position.y);
line(position.x,position.y,position.x - 1 * lengthIncrease,position.y);
line(position.x,position.y,position.x,position.y + 1 * lengthIncrease);
line(position.x,position.y,position.x,position.y - 1 * lengthIncrease);
}
}class Tank {
//declares necessary pVectors
PVector position = new PVector();
PVector velocity = new PVector();
//declares animation variables
int glowVariation = 200;
int variationIncrement = 5;
//declare shield position
float shieldPositionLeft;
float shieldPositionRight;
float shieldPositionTop;
float shieldPositionBottom;
Tank() {
init();
}
void init() {
//sets tank starting point
position.x = 400;
position.y = 500;
velocity.x = 0;
}
void update() {
//checks for game over
if (gameOver == false) {
//update the shield position
shieldPositionLeft = position.x - 50;
shieldPositionRight = position.x + 50;
shieldPositionTop = position.y - 70;
shieldPositionBottom = position.y - 60;
//updates the shield collision
shieldLeft = shieldPositionLeft;
shieldRight = shieldPositionRight;
shieldTop = shieldPositionTop;
shieldBottom = shieldPositionBottom;
//alter the tank's velocity, increasing and decreasing speed with button presses, as opposed to basic left button moves left and right button moves right
if (leftPressed == true) {
velocity.x--;
}
if (rightPressed == true) {
velocity.x++;
}
if (velocity.x > 10) {
velocity.x = 10;
}
if (velocity.x < -10) {
velocity.x = -10;
}
position.add(velocity);
if (position.x > 800) {
position.x = 800;
}
if (position.x < 0) {
position.x = 0;
}
}
}
void display() {
//change the glow on the glowing parts
glowVariation = glowVariation + variationIncrement;
if (glowVariation > 200) {
variationIncrement = -5;
}
if (glowVariation < 50) {
variationIncrement = 5;
}
//display the tank
ellipseMode(CENTER);
fill(0, 255, 255);
ellipse(position.x, position.y - 36, 32, 32);
fill(255, 255, 255, 0 + glowVariation);
ellipse(position.x, position.y - 36, 36, 36);
rectMode(CENTER);
fill(105, 160, 15, 255);
rect(position.x, position.y - 25, 36, 36);
ellipseMode(CENTER);
fill(110, 40, 35, 255);
ellipse(position.x - 18, position.y - 7, 20, 20);
ellipse(position.x + 18, position.y - 7, 20, 20);
//display the shield near the tank itself
rectMode(CORNERS);
fill(0, 255, 255);
rect(shieldPositionLeft, shieldPositionTop, shieldPositionRight, shieldPositionBottom);
fill(255, 255, 255, 0 + glowVariation);
rect(shieldPositionLeft - 2, shieldPositionTop - 2, shieldPositionRight + 2, shieldPositionBottom + 2);
}
}class Timer {
Timer() {
init();
}
void init() {
}
void update() {
difficultyTimer++; //increments the timer
}
void display() {
}
}