1. Oktober 2013
Als Endaufgabe habe ich das Pong mit dem guten alten Handy Game 'Space Impact' gemischt. Herausgekommen ist das, was passiert wenn der Weltraum auf ein Pong auftrifft – oder eben 'Space impacts Pong'. Die dazugehörigen Daten sind in diesem Zip File zu finden: Space_Impact Ebenfalls ist der Code hier direkt einsehbar:
import java.util.*; //import all java classes, mainly because I need the class of Iterator
import ddf.minim.*;
import megamu.shapetween.*; //import tween-class
Minim minim;
AudioSample laserBeamSnd;
AudioSample alienBlastSnd;
AudioSample collisionSnd;
AudioSample up;
AudioPlayer ambience;
Timer timer;
Timer timer2;
Tween ani;
SpaceShip spaceship;
Landscape landscape1;
Landscape landscape2;
Landscape landscape3;
Counter counter;
ArrayList<Shot> shots;
ArrayList<AlienShip> aliens;
ArrayList<NewLife> myLives;
final static int GAME_MODE_MENU = 0;
final static int GAME_MODE_PLAY = 1;
final static int GAME_MODE_GAMEOVER = 2;
final static int SHIP_TYPE_DRONE = 0;
final static int SHIP_TYPE_DESTROYER = 1;
final static int NORMAL_SHOT = 0;
final static int RED_SHOT = 1;
PFont fontB;
PFont fontT;
PShape life;
PImage backgroundImage;
PImage startScreen;
PImage startShip;
float bgh;
int shipsizeDrone = 24; //18px height for drones
int shipsizeDestroyer = 47;
int scorePerFrame = 3; //each frame you survive you earn 3 pts.
int scoreForDrone = 1200;
int scoreForDestroyer = 5000;
int gameGUIbar_h = 45; //height of display the icon bar on top
int lives = 3;
float startShipPos = 0;
int gameMode = GAME_MODE_MENU;
float alienAbundance = 0.3; //Abundance of aliens, value 0 - 10
float lifeAbundance = 0.03; //Abundance of aliens, value 0 - 10
int timeToIncrease = 10000; //after 10 sec alienAbundace is increased.
boolean shieldActive = false;
boolean pleaseIncrease = false; //increase Speed
void setup() {
size(900, 600);
background(20);
fontB = loadFont("NeutraDisp-Bold-48.vlw");
fontT = loadFont("NeutraText-Book-48.vlw");
startScreen = loadImage("startscreen.jpg");
startShip = loadImage("startship.png");
// sound init
ani = new Tween(this, 90, Tween.FRAMES, Shaper.COSINE);
minim = new Minim(this);
ambience = minim.loadFile("space_ambience.mp3");
up = minim.loadSample("1up.mp3");
laserBeamSnd = minim.loadSample("laserbeam.mp3");
alienBlastSnd = minim.loadSample("alien_blast.mp3");
collisionSnd = minim.loadSample("collision_gameover.mp3");
// if AudioPlayer used...
// hasVolume = player.hasControl(Controller.VOLUME); //check if machine has Volume controller
// if(hasVolume =
// laserBeamSnd.setVolume(0.1);
smooth();
}
void draw() {
// updateScene();
background(20);
drawScene();
}
void drawScene() {
switch(gameMode)
{
case GAME_MODE_MENU:
drawMenu();
break;
case GAME_MODE_PLAY:
drawGame();
break;
case GAME_MODE_GAMEOVER:
drawGameOver();
break;
}
}
void drawMenu() {
float x = lerp( -width, 0, ani.position() ); //lerp tween
//Images not really dynamic, bigger screens or other screen-relations will distort the image
image(startScreen, 0, 0, width, height);
// pushMatrix();
image(startShip, x, 0, width, height);
// popMatrix();
if (millis() < 300) {
ani.start(); //start tween
}
// println(x);
//textFont auskommentiert, da genauere Arbeitsweise in Layoutprogramm möglich...
// pushStyle();
// fill(255);
// textFont(fontB, 45);
// text("SPACE IMPACTS PONG", 30, 200);
//
// textFont(fontT, 25);
//
// text("\'p\' : Start Game", 80, 300);
// text("Arrow Up/Down : Move Up/Down", 80, 350);
// text("Arrow Right : Shield Against Rebounced Shots", 80, 400);
//
// popStyle();
}
// ------------------------------ DRAW GAME ---------------------------------------
void drawGame() {
//draw background ------------------
image(backgroundImage, 0, 0, width, bgh);
landscape3.showLand();
landscape2.showLand();
landscape1.showLand();
//draw icon-bar -------------------
initUI(); //calls UserInterface function in other tab
//check if shield gets inactive again
if (timer!=null) {
if (timer.isFinished()) { //check if shieldactive still true?
shieldActive = false;
}
}
//check if incrase of Aliens
if (timer2!= null) {
if (timer2.isFinished()) {
alienAbundance += 0.03; //increase Aliens over Time (here every 10 secs)
// increaseAlien speed
pleaseIncrease = true;
println(alienAbundance);
timer2.start();
}
}
//draw score -------------
drawCount();
//draw ships and shots ------------------
spaceship.draw(shieldActive);
float createAlien = random(0, 10); //number between 0 and 10
float createLife = random(0, 10);
if (createAlien < alienAbundance) {
float shiptype = random(0, 1);
int type;
float aHeight;
if (shiptype > 0.5) { //50:50 if ship 1 or ship 2
type = 1;
aHeight = shipsizeDestroyer;
}
else {
type = 0;
aHeight = shipsizeDrone;
}
aliens.add(new AlienShip(width, int(random(0+gameGUIbar_h+15, height-15)), aHeight, type)); //generate new alien instances randomly
// println("Wueh, new Alien arrived");
}
if (createLife < lifeAbundance) {
myLives.add(new NewLife(width, int(random(0+gameGUIbar_h+15, height-15)), 45)); // generate new life, 45 = size
}
// println(shots.size());
for (Iterator<NewLife> it = myLives.iterator(); it.hasNext(); ) { //check if Lives are on screen
NewLife nl = it.next();
nl.update();
if (nl.outOfScreen()) { //check whether the life has passed you without collecting
it.remove(); //then remove it from the ArrayList
}
int returnHealth = nl.checkCollision(spaceship._pos.x, spaceship._pos.y, spaceship._width, spaceship._height); //check if collision between spaceship and life
if (returnHealth == NewLife.COLLISION) {
it.remove();
up.trigger(); //play 1up sound
lives += 1; //incrase lives
}
}
for (Iterator<Shot> it = shots.iterator(); it.hasNext(); ) { //check if thre are Shot instances, go to next
Shot s = it.next();
//check timer to delete red shots after a while
if (s._timer != null) {
if (s._timer.isFinished()) {
it.remove();
}
}
s.update();
if (s.outOfScreen()) { //check whether the shot has gone out of the window to erase it from the ArrayList
it.remove(); //then remove it from the ArrayList
// println(shots.size());
}
int returnStatus = s.killedYourself(spaceship._pos.x, spaceship._pos.y, spaceship._width, spaceship._height, shieldActive); //number if rebounce/suicide/nothing
switch (returnStatus)
{
case Shot.REBOUNCE:
// change shot direction
s._dir.x *= -1; //change X vector to -1
// set shot on shield, no overlap
s._pos.x = spaceship._pos.x + spaceship._width/2 + s._width + spaceship._shieldwidth;
//change color of shot -> now special shot!
s._sType = RED_SHOT; // = 1
//increase speed of red shot
s._dir.mult(4);
s.timerStart();
break;
case Shot.SUICIDE:
if (lives <=1 ) { //if last life has gone ->game over
it.remove(); //delete shot from ArrayList
gameMode = GAME_MODE_GAMEOVER;
break;
}
else { //still lives there
lives -= 1; //reduce by 1
println(lives + " " + shots);
break;
}
case Shot.NOTHING:
break;
}
}
for (Iterator<AlienShip> it = aliens.iterator(); it.hasNext();) {
AlienShip alien = it.next();
alien.update();
//code not finished, at the moment, only one alienship gets increased speed...
// if(pleaseIncrease){
// alien._speed -= 3; //x vector -3 dazu
// pleaseIncrease = false;
// print(alien);
// }
// //check if speed has to increase
if (alien.outOfScreen()) { //check whether the alien has passed you without causing harm
it.remove(); //then remove it from the ArrayList
}
int returnDamage = alien.checkCollision(spaceship._pos.x, spaceship._pos.y, spaceship._width, spaceship._height); //check if collision between spaceship and alien
switch(returnDamage)
{
case AlienShip.COLLISION:
// println("NOOOOOO, I'm too young to die!");
it.remove(); //remove the colliding alienship from list
if (lives<=1) { //if last life has gone ->game over
collisionSnd.trigger();
gameMode = GAME_MODE_GAMEOVER;
break;
}
else { //if spare lives
lives -= 1;
collisionSnd.trigger();
break;
}
case AlienShip.NOTHING:
break;
}
for (Iterator<Shot> it2 = shots.iterator(); it2.hasNext(); ) { //iterate through all the shots to check if an alien got Hit
Shot shot = it2.next();
int returnInfo = alien.checkHit(shot._pos.x, shot._pos.y, shot._width, shot._height, shot._sType); //check if Hit, Parameters: shot_posX and Y, size and type (normal/red)
switch(returnInfo)
{
case AlienShip.HIT_DRONE:
// println("HITMAN");
it.remove();
it2.remove();
// spiele sound
alienBlastSnd.trigger();
counter.update(scoreForDrone); // + to counter
break;
case AlienShip.HIT_DESTROYER:
it.remove();
it2.remove();
// spiele sound
alienBlastSnd.trigger();
counter.update(scoreForDestroyer); // +scoreForDestroyer to counter
break;
case AlienShip.HIT_DRONE_RED:
it.remove();
alienBlastSnd.trigger();
counter.update(scoreForDrone);
break;
case AlienShip.HIT_DESTROYER_RED:
it.remove();
alienBlastSnd.trigger();
counter.update(scoreForDestroyer);
break;
case AlienShip.REBOUNCE:
//spiele rebounce sound
// change shot direction
shot._dir.x *= -1; //change X vector to -1
shot._dir.y = (random(-3.5, 3.5)); //randomly changes Y-Direction
// set shot exactly on alien ship edge
shot._pos.x = alien._pos.x - alien._width/2 - shot._width;
case AlienShip.NOTHING:
break;
}
}
}
}
void drawGameOver() {
int endCount = counter.endCount();
pushStyle();
fill(255);
textAlign(CENTER);
textFont(fontB, 45);
text("Game Over", width/2, 200);
textFont(fontT, 30);
text("Your Score: " + endCount, width/2, 300);
textFont(fontT, 22);
text("Back to Menu: 'Q'", width/2, 400);
popStyle();
// ambience.close();
// // always stop Minim before exiting.
// minim.stop();
}
// ------------------------------ KEYS ---------------------------------------
void keyPressed() {
switch (key) {
case ' ':
//play sound
laserBeamSnd.trigger();
//add shot to arraylist
shots.add(new Shot(int(spaceship._pos.x)+spaceship._width/2+2, int(spaceship._pos.y), 12, gameGUIbar_h, NORMAL_SHOT)); // Start by adding one element
// println(spaceship._pos.y);
break;
case 'e':
// end
gameMode = GAME_MODE_GAMEOVER;
break;
case 'p':
// start game
gameMode = GAME_MODE_PLAY;
initGameScene();
break;
case 'q':
// end
gameMode = GAME_MODE_MENU;
break;
}
switch (keyCode) {
case RIGHT:
println("right");
shieldActive = true;
timer = new Timer(3000);
timer.start();
}
}
void initGameScene() {
spaceship = new SpaceShip(10, (height-gameGUIbar_h)/2, 33, gameGUIbar_h); //new Spaceship at X= 10, Y=Height/2 and height 30 and size of gui-bar
shots = new ArrayList<Shot>(); // Create an empty ArrayList of the class type Shot
aliens = new ArrayList<AlienShip>(); //Create empty ArrayList für the Alienships
myLives = new ArrayList<NewLife>();
counter = new Counter();
//call landscape instances to show images
String path1 = "landscape/landscape_level1.svg";
landscape1 = new Landscape(path1, 1400, 55, 5);
String path2 = "landscape/landscape_level2.svg";
landscape2 = new Landscape(path2, 1400, 99, 1.2);
String path3 = "landscape/landscape_level3.svg";
landscape3 = new Landscape(path3, 1400, 186, 0.2);
life = loadShape("icons/heart.svg"); //load heart icon
lives = 3;
backgroundImage = loadImage("landscape/background.jpg");
bgh = width/1.6666;
ambience.play();
timer2 = new Timer(timeToIncrease);
timer2.start();
}
class AlienShip {
final static int NOTHING = 0;
final static int HIT_DRONE = 1;
final static int COLLISION = 2;
final static int REBOUNCE = 3;
final static int HIT_DESTROYER = 4;
final static int HIT_DRONE_RED = 5;
final static int HIT_DESTROYER_RED = 6;
PShape _droneShape;
PShape _destroyerShape;
PShape _destroyerShieldShape;
PVector _pos;
PVector _dir;
float _height;
float _width;
int _type; //defines type of ship
boolean _bounced = false;
int _speed = -2; //start speed destroyer
//Constructor
AlienShip(int x, int y, float h, int type) {
_pos = new PVector(x, y); //set position
_dir = new PVector(0, 0); //no direction at the beginning
_height = h;
if (type == 0) { //if drone
_width = h*(57/19);
}
else if (type == 1) { //if destroyer
_width = h*(1.8); //1.8 = (89/47)
}
_type = type; // 0= drone, 1 = destroyer
_droneShape = loadShape("drone.svg"); //57x19
_destroyerShieldShape = loadShape("destroyer_shield.svg"); //89x47
_destroyerShape = loadShape("destroyer.svg"); //89x47
}
void update() {
calcPos(); //get's new Position of the AlienShip
if (_type == 0) { //if drone
pushStyle();
shapeMode(CENTER);
shape(_droneShape, _pos.x, _pos.y, _width, _height);
popStyle();
}
else if (_type == 1) { //if destroyer
pushStyle();
shapeMode(CENTER);
if (_bounced) { //remove shield
shape(_destroyerShape, _pos.x, _pos.y, _width, _height);
}
else {
shape(_destroyerShieldShape, _pos.x, _pos.y, _width, _height);
}
popStyle();
}
}
void calcPos() { //update the PVectors for the positioning
if (_type == 1) { //if destroyer
_pos.add(_speed, 0, 0);
}
else {
_pos.add(_speed -2, 0, 0);
}
}
boolean outOfScreen() {
if ((_pos.x + _width) < 0) {
// println("Alien passed, Huh!");
return true;
}
else {
return false;
}
}
int checkHit(float shotX, float shotY, float shotWidth, float shotHeight, int shotType) {
if (shotX + shotWidth/2 >= _pos.x - _width/2 && shotX - shotWidth/2 <= _pos.x + _width/2 &&//check if shot has reached alien-x
shotY + shotHeight/2 >= _pos.y - _height/2 && shotY - shotHeight/2 <= _pos.y + _height/2) {
if (_type == 1 && _bounced == false) { //if AlienShip belongs to Destroyers and has not bounced yet
_bounced = true; //sets bounced to true
return REBOUNCE; //returns 3
}
else {
if (shotType != 0) { //if red shot
if (_type == 0) { //if drone
return HIT_DRONE_RED; //returns 1
}
else {
return HIT_DESTROYER_RED; //returns 4
}
}
else {
if (_type == 0) { //if drone
return HIT_DRONE; //returns 1
}
else {
return HIT_DESTROYER; //returns 4
}
}
}
}
else {
return NOTHING; //returns 0
}
}
int checkCollision(float shipX, float shipY, float shipWidth, float shipHeight) {
if (shipX + shipWidth/2 >= _pos.x - _width/2 && shipX - shipWidth/2 <= _pos.x + _width/2 &&//check if shot has reached alien-x
shipY + shipHeight/2 >= _pos.y - _height/2 && shipY - shipHeight/2 <= _pos.y + _height/2) {
return COLLISION; //returns 2
}
else {
return NOTHING;
}
}
}
class Counter {
int _counter;
//constructor
Counter() {
_counter = 0; // set counter to 0
}
int update(int plus){
_counter = _counter + plus; //add to counter
// println(_counter);
return _counter;
}
int endCount(){
return _counter;
}
}
class Landscape
{
PShape _landscape;
PShape _landscape2ndPart;
PVector _pos;
String _file;
int _landH; //height of file
int _landW; //width of file
float _speed;
//constructor
Landscape (String filepath, int w, int h, float speed) {
_file = filepath;
_landH = h;
_landW = w;
_landscape = loadShape(_file);
_landscape2ndPart = loadShape(_file);
_pos = new PVector(0, 0);
_speed = speed;
}
void showLand() {
if(_pos.x +_landW <= 0){
_pos.x = 0;
}
shapeMode(CORNER);
pushMatrix();
translate(_pos.x, 0);
shape(_landscape, 0, height-_landH, _landW, _landH);
popMatrix();
//seamless transition to second landscape
pushMatrix();
translate(_pos.x, 0);
shape(_landscape2ndPart, _landW, height-_landH, _landW, _landH);
popMatrix();
_pos.add(-_speed, 0, 0);
}
}
class NewLife extends AlienShip
{
PShape _newLife;
//constructor
NewLife(int x, int y, float h) {
super(x, y, h, 0);
_type = 0;
_width = h;
_height = _width;
_newLife = loadShape("icons/newlife.svg");
}
void update() {
calcPos(); //get's new Position of the Life
pushStyle();
shapeMode(CENTER);
shape(_newLife, _pos.x, _pos.y, _width, _height);
popStyle();
}
void calcPos() { //update the PVectors for the positioning
if (_type == 1) { //if destroyer
_pos.add(-2, 0, 0);
}
else {
_pos.add(-4, 0, 0);
}
}
//how the hell can I access function in the superclass???
// boolean outOfScreen() {
// if ((_pos.x + _width) < 0) {
// // println("Alien passed, Huh!");
// return true;
// }
// else {
// return false;
// }
// }
}
class Shot
{
Timer _timer;
PShape _shotShape;
PShape _shotShape2;
PVector _pos;
PVector _dir;
float _height;
float _width;
int _h;
int _sType;
final static int SUICIDE = 0;
final static int REBOUNCE = 1;
final static int NOTHING = 2;
//Constructor
Shot(float x, float y, float size, int gameGUIbar_h, int shottype) {
_pos = new PVector(x, y-13); //set position
_dir = new PVector(0, 0); //no direction at the beginning
_height = size;
_width = size*(33/7);
_dir = new PVector(5, 0, 0);
_shotShape = loadShape("shot.svg"); //33x7
_shotShape2 = loadShape("shot2.svg");
_h = gameGUIbar_h;
_sType = shottype; //at start always 0 = normal shot
}
void update() {
calcPos(); //get's new Position of the Spaceship
pushMatrix();
// rotate(_dir.heading()); //rotates cordinates to angle of direction vector
// rectMode(CENTER);
// rect(_pos.x, _pos.y, _width, _height);
shapeMode(CENTER);
if (_sType == 0) {
shape(_shotShape, _pos.x, _pos.y, _width, _height);
}
else {
shape(_shotShape2, _pos.x, _pos.y, _width, _height);
}
popMatrix();
}
void calcPos() { //update the PVectors for the positioning
_pos.add(_dir);
// println(_dir);
// test if shot hits top or bottom
if (_pos.y - _height/2 <= _h || _pos.y +_height/2 >= height) //falls an Oberkante oder an unterkante
{
_dir.y *= -1;
if (_pos.y - _height/2 <= _h) {
_pos.y = _h + _height/2;
}
else if (_pos.y +_height/2 >= height) {
_pos.y = height - _height/2;
}
}
//red shot can't leave canvas
if (_sType != 0) {
if (_pos.x - _width/2 <= 0) {
_dir.x *= -1;
_pos.x = _width/2;
}
else if (_pos.x + _width/2 >= width ) {
_dir.x *= -1;
_pos.x = width - _width/2;
}
}
}
boolean outOfScreen() {
if ((_pos.x - _width) > width || (_pos.x < 0)) {
// println("out!");
return true;
}
else {
return false;
}
}
int killedYourself(float shipX, float shipY, float shipWidth, float shipHeight, boolean shieldActive) {
// println("UK " + (shipY + shipHeight/2) + " OK: " + (shipY - shipHeight/2) + " UKs" + (_pos.y + _height/2) + " OKs " + (_pos.y - _height/2));
// println("RK " + (shipX + shipWidth/2) + " LK " + (shipX - shipWidth/2) + " RKs " + _pos.x + " LKs " + (_pos.x + _width/2));
// if(shieldActive){
// shipWidth += 19;
// }
if (_sType != 0) { //red shot does not kill the player!
return NOTHING;
}
if (shipX + shipWidth/2 >= _pos.x && shipX - shipWidth/2 <= _pos.x + _width/2 && //check if shot has reached Spaceship X
shipY + shipHeight/2 >= _pos.y - _height/2 && shipY - shipHeight/2 <= _pos.y + _height/2) {
if (shieldActive) {
println("bounced back");
return REBOUNCE; //returns 1
}
println("suicide");
return SUICIDE; //returns 0
}
else {
return NOTHING; //returns 2
}
}
void timerStart() {
_timer = new Timer(11000); //red shot should dissolve after 11000ms
_timer.start();
}
}
class SpaceShip
{
PShape _shipShape;
PShape _shipShapeShield;
PVector _pos;
PVector _dir;
float _width;
float _height;
float _shieldheight;
float _shieldwidth;
int _movingstep = 8; //per Arrow pressed ships moves by this number
color _color;
int _h;
//Constructor
SpaceShip(int x, int y, float size, int gameGUIbar_h) {
_pos = new PVector(x, y); //set position
_dir = new PVector(0, 0); //no direction at the beginning
_height = size;
_shieldheight = size*1.7878; // 59/33
_shieldwidth = 19;
_width = size*2.33333; // 77/33
_color = color(255);
_shipShape = loadShape("spaceship.svg"); //77x33
_shipShapeShield = loadShape("shield.svg"); //59height
_h = gameGUIbar_h;
}
//draws Spaceship
void draw (boolean shieldActive) {
calcPos(); //get's new Position of the Spaceship
pushStyle();
shapeMode(CENTER);
if (shieldActive) {
shape(_shipShapeShield, _pos.x +10.9, _pos.y + 3.5, _shieldwidth, _shieldheight); //manual adjustment of the shield to the ship... naja
}
shape(_shipShape, _pos.x+_width/2, _pos.y, _width, _height);
// noStroke();
// rectMode(CENTER);
// rect(_pos.x, _pos.y, _width, _height);
popStyle();
}
void calcPos() { //updates the position and direction of Spaceship
if (keyPressed) {
if (keyCode == UP && _pos.y - _height/2 > _h ) {
_pos.add(0, -_movingstep, 0);
// println("UP");
}
else if (keyCode == DOWN && _pos.y + _height/2 < height) {
_pos.add(0, _movingstep, 0);
// println("DOWN");
}
}
}
void shoot() {
// shot = new Shot
}
}
class Timer {
int savedTime; // When Timer started
int totalTime; // How long Timer should last
Timer(int tempTotalTime) {
totalTime = tempTotalTime;
}
// Starting the timer
void start() {
// When the timer starts it stores the current time in milliseconds.
savedTime = millis();
}
// The function isFinished() returns true if 5,000 ms have passed.
// The work of the timer is farmed out to this method.
boolean isFinished() {
// Check how much time has passed
int passedTime = millis()- savedTime;
if (passedTime > totalTime) {
return true;
} else {
return false;
}
}
}
//UI
public void initUI() {
pushStyle();
fill(50);
rectMode(CORNER);
rect(0, 0, width, gameGUIbar_h);
stroke(120);
line(0, gameGUIbar_h, width, gameGUIbar_h);
popStyle();
for (int i = 0; i < lives; i++) {
pushStyle();
shapeMode(CENTER);
shape(life, 25*(i+1), gameGUIbar_h/2, gameGUIbar_h - 9, gameGUIbar_h - 9); //small manual adaption to Y-position ...
popStyle();
}
}
public void drawCount()
{
int actualScore = counter.update(scorePerFrame);
pushStyle();
textFont(fontT, 25);
text("Score:", width-250, gameGUIbar_h - 15);
textAlign(RIGHT);
text(actualScore, width-20, gameGUIbar_h - 15);
popStyle();
}