Code Übersicht
/***********************************************************************
Spiritus Runner - 2D Runner
Gestartet am: 22.03.2023
Mitglieder: Nicolò Tonarelli, Mika Pfeifer, Andrea Krauss
Hauptentwickler Processing: Nicolò
Hauptentwickler Website: Andrea Krauss & Mika Pfeifer
*************************************************************************/
//************************************************************************************************************************************
/*************************************************************************************************************************************
** **
** MAIN **
** **
** Hier befinden sich alle Funktionen / Variablen, die für das grobe Spielsystem verantwortlich sind **
** **
**************************************************************************************************************************************/
//************************************************************************************************************************************
import processing.sound.*;
// Schriftarten
PFont mono;
// Verschiedene Spielzustände
int gameState = 0; // 0 = Menü, 1 = Spiel, 2 = settings, 3 = Shop, 4 = GameOver, 5 = Animation
// Menü Variablen / Settings
int buttonWidth = 250;
int buttonHeight = 100;
int textSize = 50;
int textHeight = textSize/2;
color menuThemeColor = #F5DC19;
// Zeit / Tickers etc.
int ticker = 0;
int tickerMemory;
// Bilder
PImage background;
PImage coinPreview;
PImage left;
PImage up;
// Sounds
SoundFile backgroundMusic;
SoundFile animationMusic;
SoundFile hoverSound;
SoundFile gameOverSound;
SoundFile cashSound;
SoundFile victorySound;
SoundFile jumpSound;
SoundFile damageSound;
SoundFile levelChangeSound;
SoundFile coinSound;
// Array wo die Musik Dateien beinhaltet
SoundFile[] music = new SoundFile[4];
// Buttons
Button playButton;
Button shopButton;
Button quitButton;
Button settingsButton;
Button settingsDelete;
Button backButton;
// Shop Buttons
Button shopItem1;
Button shopItem2;
Button shopItem3;
// Shop Preise
int shopItem1Price = 100;
int shopItem2Price = 300;
int shopItem3Price = 50;
// Wert für ob der Game Over ein Sieg war
boolean isVictory = false;
// Liste an Bildern von Spielerlaufanimation
ArrayList<PImage> playerImages = new ArrayList<PImage>();
/*/************************************************************************************************************************************
** **
** SETUP (Startvariablen werden gesetzt) **
** **
//************************************************************************************************************************************/
void setup() {
fullScreen(); //Vollbild
// Bilder werden geladen
background = loadImage("images/background.jpeg");
coinPreview = loadImage("images/coin.png");
left = loadImage("images/debug/left.png");
up = loadImage("images/debug/up.png");
// Fonts
mono = createFont("fonts/pixel.ttf", 128);
// Sounds werden geladen
backgroundMusic = new SoundFile(this, "sounds/background-music.wav");
hoverSound = new SoundFile(this, "sounds/hover.wav");
animationMusic = new SoundFile(this, "sounds/animation-music.wav");
gameOverSound = new SoundFile(this, "sounds/gameover.wav");
cashSound = new SoundFile(this, "sounds/cash.wav");
victorySound = new SoundFile(this, "sounds/victory.wav");
jumpSound = new SoundFile(this, "sounds/jump.wav");
damageSound = new SoundFile(this, "sounds/damage.wav");
levelChangeSound = new SoundFile(this, "sounds/levelchange.wav");
coinSound = new SoundFile(this, "sounds/coin.wav");
// Die 4 Muskdateien werden geladen
for(int i = 1; i <= 4; i++) {
music[i-1] = new SoundFile(this, "sounds/music"+i+".wav");
}
// Programminhalte werden generiert
createScreen(true);
// Settingsdelete Button wird geladen
settingsDelete = new Button("Spielstand löschen", width/2, height/2, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize); // Settingsbutton
// Spielerbilder werden in die ArrayList geladen
for (int i = 1; i <= 5; i++) { // For Schleife von 1 - 5
PImage tempImage = loadImage("images/player"+i+".png"); // Temporäres Bild
tempImage.resize(int(tempImage.width*2/3), int(tempImage.height*2/3)); // Bild wird resized
playerImages.add(tempImage); // Bild wird zur Liste hinzugefügt
}
}
/*/************************************************************************************************************************************
** **
** DRAW (Spielstoppuhr um 1 höher und createScreen()) **
** **
//************************************************************************************************************************************/
void draw() {
ticker++; // Zeitrechnung für Animationen & Systeme
createScreen(false); // Programminhalte werden generiert
}
/*/************************************************************************************************************************************
** **
** createScreen (Funktion, die alle States / Spielzustände verwaltet **
** Je nach boolean gameStateSwitch werden Startwerte gesetzt oder nur Funktionen ausgeführt **
** **
//************************************************************************************************************************************/
void createScreen(boolean gameStateSwitch) {
switch(gameState) {
case 0: ///////////////////////////////////////////////////////////////////////// GAMESTATE: MENU
if (!gameStateSwitch) return; // -> Funktion nur 1x ausgeführt
// Menü Background
background.resize(1920, 1080); // Resize
image(background, 0, 0); // Menühintergrund Bild
restartMusic(); // Nur Menümusik wird gestartet
createButtons(); // Buttons werden gezeichnet
drawMainMenu(); // Menütitel werden gezeichnet
loadGameData(); // Daten aus data.txt werden geladen
loadScores(); // Münzen & Highscore werden angezeigt
break;
case 1: ///////////////////////////////////////////////////////////////////// GAMESTATE: GAME
if (gameStateSwitch) { // (Nur 1x mal)
background(#FFFFFF); // Hintergrundfarbe weiss
ticker = 0; // Stoppuhr auf 0
tickerMemory = ticker; // Stopppuhrspeicher auf ticker
setupGame(); // Spielstartwerte
animationMusic.stop(); // Erklärungsmusik wird gestoppt
}
gameHandler(); // Spielfunktionen
break;
case 2: ///////////////////////////////////////////////////////////////////////////// SETTINGS
if (!gameStateSwitch) return; // nur 1x mal
// Menü Background
background.resize(1920, 1080);
image(background, 0, 0);
// Settingsbutton wird angezeigt
settingsDelete.draw(1, true); // settingsDelete-Button wird im Knopfzustand 1 (unberührt) gezeichnet
// Menü Musik wird geändert
backgroundMusic.stop();
animationMusic.play();
animationMusic.loop();
// Zurück/Back Button wird angezeigt
backButton.draw(1, true); // Zurückbutton im Knopfzustand 1 gezeichnet
break;
case 3: /////////////////////////////////////////////////////////////////////////////// SHOP
if (gameStateSwitch) { // nur 1x mal
background(#FFFFFF);
backgroundMusic.stop();
animationMusic.play();
animationMusic.loop();
backButton.draw(1, true); // Backbuttobn im Zustand 1 gezeichnet
}
break;
case 4: /////////////////////////////////////////////////////////////////////////// GAME OVER
if (gameStateSwitch) { // nur 1xmal
saveGameData();
ticker = 0; // Stoppuhr auf 0
tickerMemory = ticker; // Stoppuhrspeicher auf Stoppuhr (0)
if (isVictory) { // Falls man gewonnen hat
victorySound.play(); // Siegmusik wird abgespielt
} else gameOverSound.play(); // GameOvermusik wird abgespielt
}
currentMusic.stop(); // Spielmusik wird gestoppt
drawGameOver(); // GameOver wird gezeichnet
if (ticker >= 100) { // Nach 100 Frames
if (!gameOverSound.isPlaying()) { // GameOverSound gestoppt
backButton.draw(1, true); // Backbutton wird angezeigt
}
}
break;
case 5: /////////////////////////////////////////////////////////////// ANIMATIONS LOADING SCREEN
// VOREINSTELLUNGEN UND TIMER STARTEN
if (gameStateSwitch) { // nur 1x
frameRate(200); // Frame Rate auf 200
tickerMemory = ticker;
backgroundMusic.stop();
animationMusic.play();
animationMusic.loop();
//backgroundMusic.stop();
}
// KÄSTCHEN ANIMATION
if (ticker - 570 < tickerMemory) { // Nach 570 Frames Loading Screen
fill(menuThemeColor);
noStroke();
square(int(random(0, width)), int(random(0, height)), int(random(0, 400)));
fill(#FFFFFF);
textSize(textSize*2);
text("Loading...", width/2, height/2);
}
// Erste mal Hintergrund färben (gelb)
if (ticker - 571 == tickerMemory) { // Nach 571 Frames Fenster färben
background(menuThemeColor);
}
if (ticker - 600 == tickerMemory) { // Nach 600 Frames Steuerung angzeigen
background(menuThemeColor);
stroke(#FFFFFF); // Linie
strokeWeight(5);
line(620, 230, 1290, 230);
stroke(#FFFFFF); // Linie
strokeWeight(5);
line(0, height-100, width, height-100);
stroke(#FFFFFF); // Linie
strokeWeight(5);
line(0, 100, width, 100);
fill(#FFFFFF); // Steuerungstext
text("Steuerung", width/2, height/5*1);
textSize(textSize); // Springen Text
text("[Leertaste]: \u2191 - Springen", width/2, height/6*2);
text("[Pfeiltaste]: \u2193 - Ducken", width/2, height/6*3);
text("[Ziel]: \u2193 - Laufe durch alle Levels!", width/2, height/6*4);
text("[!]: \u2193 - Meide Objekte & Lehrer!", width/2, height/6*5);
}
if (ticker - 1400 == tickerMemory) { // Nach 1400 Frames Lets Go anzeigen
background(menuThemeColor);
fill(#FFFFFF);
text("Lets go!", width/2, height/2);
}
if (ticker - 1750 == tickerMemory) { // Nach 1750 Frames Bildschirm färben
background(menuThemeColor);
}
if (ticker - 1900 == tickerMemory) { // Nach 1900 Frames State wechseln
gameState = 1;
frameRate(60);
animationMusic.stop();
createScreen(true);
}
break;
}///////////////////////////////////////////////////////////////////////////////////////
}
/*/************************************************************************************************************************************
** **
** Funktion, die zu Beginn des Menü geladen wird **
** **
//************************************************************************************************************************************/
void restartMusic() {
backgroundMusic.play();
backgroundMusic.loop();
backgroundMusic.amp(0.1);
animationMusic.stop();
gameOverSound.stop();
}
/*/************************************************************************************************************************************
** **
** Startbuttons (Buttons werden geladen) **
** **
//************************************************************************************************************************************/
void createButtons() {
playButton = new Button("PLAY", width/2, height/4*1, menuThemeColor, #D3C02D, #FFF079, buttonWidth, buttonHeight, 0, textSize);
playButton.draw(1, true);
shopButton = new Button("SHOP", width/2, height/4*2, menuThemeColor, #D3C02D, #FFF079, buttonWidth, buttonHeight, 0, textSize);
shopButton.draw(1, true);
quitButton = new Button("QUIT", width/2, height/4*3, menuThemeColor, #D3C02D, #FFF079, buttonWidth, buttonHeight, 0, textSize);
quitButton.draw(1, true);
shopItem1 = new Button("Doppelsprung (100$)", width/2, height/4*1, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize);
//shopItem1.draw(1, true);
shopItem2 = new Button("x2 Leben (300$)", width/2, height/4*2, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize);
//shopItem2.draw(1, true);
shopItem3 = new Button("Mütze kaufen (50$)", width/2, height/4*3, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize);
//shopItem3.draw(1, true);
settingsButton = new Button("SETTINGS", 130, height-120, menuThemeColor, #D3C02D, #FFFFFF, 200, 50, 0, 30);
settingsButton.draw(1, true);
backButton = new Button("BACK", 1840, 1030, menuThemeColor, #D3C02D, #FFFFFF, 200, 50, 0, 30);
}
/*/************************************************************************************************************************************
** **
** Score & Highscore werden angezeigt **
** **
//************************************************************************************************************************************/
void loadScores() {
fill(menuThemeColor);
textSize(textSize/2);
textAlign(RIGHT);
text(highscore + " Highscore", 1700, 40);
fill(menuThemeColor);
textSize(textSize/2);
textAlign(RIGHT);
text(coins + " $", 1700, 40+(textSize/1.5));
coinPreview.resize(70, 70);
image(coinPreview, 1710, 15);
}
/*/************************************************************************************************************************************
** **
** Hauptmenü Titel wird gezeichnet **
** **
//************************************************************************************************************************************/
void drawMainMenu() {
stroke(menuThemeColor);
strokeWeight(10);
line(100, 555, 660, 555);
stroke(menuThemeColor);
strokeWeight(10);
line(1295, 555, 1770, 555);
fill(menuThemeColor);
textSize(textSize*2);
text("SPIRITUS", width/10*2, height/2);
fill(menuThemeColor);
textSize(textSize*2);
text("RUNNER", width/10*8, height/2);
}
/*/************************************************************************************************************************************
** **
** Game Over wird gezeichnet **
** **
//************************************************************************************************************************************/
void drawGameOver() {
if(isVictory) { background(menuThemeColor); } else { background(255,0,0); } // Je Nach GameOverTyp wird die Hintergrundfarbeanders
textAlign(CENTER);
fill(#FFFFFF);
textSize(200);
text("Game Over", width/2, height/2);
textAlign(CENTER);
fill(#FFFFFF);
textSize(50);
text("Score: " + score + " Highscore: " + highscore, width/2, 650);
}
//***************************************************************************************************************************
/********************************************************g*******************************************************************
** **
** BUTTONS / KNÖPFE **
** **
** Hier befindet sich die Klasse für Buttons, die uns das erstellen von Knöpfen vereinfacht **
** **
****************************************************************************************************************************/
//***************************************************************************************************************************
/*/************************************************************************************************************************************
** **
** Button Klasse, die berechnet ob der Button gedrückt wird und den Button erleuchten lässt **
** **
//************************************************************************************************************************************/
class Button {
String buttonName; // Button Name & Text
int posX; // X-Kooordinate
int posY; // Y-Koordinate
color buttonColor; // Buttonfarbe normal (Buttonzustand 1)
color buttonHoverColor;// Buttonfarbe wenn die Maus übergeht (Buttonzustand 2)
color clickedColor; // Buttonfarbe angeklickt (Buttonzustand 3)
int buttonWidth; // Boxlänge
int buttonHeight; // Boxhöhe
color textColor; // Textfarbe
int textSize; // Schriftartengrösse
// Klassenkonstruktor der die Werte setzt
Button(String buttonName, int posX, int posY, color buttonColor, color buttonHoverColor, color clickedColor, int buttonWidth, int buttonHeight, color textColor, int textSize) {
this.buttonName = buttonName;
this.posX = posX;
this.posY = posY;
this.buttonWidth = buttonWidth;
this.buttonHeight = buttonHeight;
this.buttonColor = buttonColor;
this.buttonHoverColor = buttonHoverColor;
this.clickedColor = clickedColor;
this.textColor = textColor;
this.textSize = textSize;
}
// mode 1 = normal, mode 2 = hovered, mode 3 = clicked
// Funktion um den Button anzeigen zu lassen
void draw(int mode, boolean playSound) {
color finalColor = color(0, 0, 0); // Buttonhintergrundfarbe zu der gewechselt wird
color finalStrokeColor = color(0, 0, 0); // Buttonrandfarbe zu der gewechselt wird
switch(mode) { // Je nach Buttonzustand werden andere Farben gewählt
case 1: //////////////////////////////// KNOPFZUSTAND 1
finalColor = this.buttonColor;
finalStrokeColor = this.buttonColor;
break;
case 2: //////////////////////////////// KNOPFZUSTAND 2
finalColor = this.buttonColor;
finalStrokeColor = this.buttonHoverColor;
break;
case 3: //////////////////////////////// KNOPFZUSTAND 3
finalColor = this.clickedColor;
finalStrokeColor = this.clickedColor;
if (playSound) hoverSound.play(); // Falls playSound true ist, wird ein Sound abgespielt
break;
}
// Buttonbox wird gezeichnet
stroke(finalStrokeColor);
fill(finalColor);
rectMode(CENTER);
rect(this.posX, this.posY, this.buttonWidth, this.buttonHeight);
// Buttontext wird gezeichnet
fill(this.textColor);
textFont(mono);
textSize(this.textSize);
textAlign(CENTER);
text(this.buttonName, this.posX, this.posY+textSize/2);
}
// Funktion, die schaut, ob die Maus auf dem Button ist
boolean mouseOnIt() {
int x = mouseX;
int y = mouseY;
if (! (x > (posX-buttonWidth/2) && x < (posX+buttonWidth/2) && y > (posY-buttonHeight/2) && y < (posY+buttonHeight/2)) ) { // Falls sich die Maus darauf befindet, wird der Button angeleuchtet
this.draw(1, true); // Button wird angeleuchtet, da die Maus sich über ihm befindet
}
return (x > (posX-buttonWidth/2) && x < (posX+buttonWidth/2) && y > (posY-buttonHeight/2) && y < (posY+buttonHeight/2));
}
// Funktion, die schaut, ob die Maus auf dem Button ist mit gameState Bedingung
boolean mouseOnIt(int gameStateNeeded) {
int x = mouseX;
int y = mouseY;
boolean correctSection = (x > (posX-buttonWidth/2) && x < (posX+buttonWidth/2) && y > (posY-buttonHeight/2) && y < (posY+buttonHeight/2)); // Falls sich die Maus darauf befindet, wird der Button angeleuchtet
if (!correctSection) {
if (gameState == gameStateNeeded) this.draw(1, true); // Button wird angeleuchtet, da die Maus sich über ihm befindet
}
return ((correctSection) && gameStateNeeded == gameState);
}
}
/*/************************************************************************************************************************************
** **
** Funktion, die das Klicken auf Buttons verwaltet **
** **
//************************************************************************************************************************************/
void menuClickHandler(boolean clicked) {
if (clicked) println("X:", mouseX, "Y:", mouseY); // DEBUG
if (!clicked) { ////////////////////////////////////// FALLS DIE MAUS NUR ÜBER DEN BUTTON GEHT ABER NICHT DRÜCKT /////////////////////
if (playButton.mouseOnIt(0)) playButton.draw(2, true);
if (shopButton.mouseOnIt(0)) shopButton.draw(2, true);
if (quitButton.mouseOnIt(0)) quitButton.draw(2, true);
if (settingsButton.mouseOnIt(0)) settingsButton.draw(2, true);
if (settingsDelete.mouseOnIt(2)) settingsDelete.draw(2, true);
if (backButton.mouseOnIt(2) || backButton.mouseOnIt(3)) backButton.draw(2, true);
if (shopItem1.mouseOnIt(3)) shopItem1.draw(2, true);
if (shopItem2.mouseOnIt(3)) shopItem2.draw(2, true);
if (shopItem3.mouseOnIt(3)) shopItem3.draw(2, true);
} else { ////////////////////////////////////// FALLS DIE MAUS AUF DEN BUTTON DRÜCKT ////////////////////////////////////////////////
if (shopItem1.mouseOnIt(3)) { // SHOP ITEM 1
buyItem(1); // Item 1 wird gekauft
gameState=0; // Gamestate wird gewechselt
createScreen(true); // Gamestate wird aktualisiert
return;
} else if (shopItem2.mouseOnIt(3)) { // SHOP ITEM 2
buyItem(2); // Item 2 wird gekauft...
gameState=0; // `
createScreen(true);// `
return;
} else if (shopItem3.mouseOnIt(3)) { // SHOP ITEM 3
buyItem(3); // `
gameState=0; // `
createScreen(true);// `
return;
}
if (playButton.mouseOnIt(0)) { // PLAY BUTTON
playButton.draw(3, true);
gameState = 5; // Animation wird gestartet
createScreen(true); // Gamestate wird aktualisiert
return;
}
if (shopButton.mouseOnIt(0)) { // SHOP BUTTON (IM HAUPTMENÜ)
shopButton.draw(3, true);
gameState = 3; // Es wird zu Shopstate gewechselt
createScreen(true);
delay(100);
return;
}
if (settingsButton.mouseOnIt(0)) { // SETTINGS BUTTON
settingsButton.draw(3, true);
gameState = 2;
createScreen(true);
return;
}
if (settingsDelete.mouseOnIt(2)) { // SPIELSTAND LÖSCHEN BUTTON
settingsButton.draw(3, true);
deleteGameData(); // data.txt wird gelöscht
loadGameData(); // neues data.txt wird erstellt
gameState = 0;
createScreen(true);
return;
}
if (backButton.mouseOnIt(2) || backButton.mouseOnIt(3) || backButton.mouseOnIt(4)) { // ZUERECK ZUM MENU BUTTON
gameState = 0;
createScreen(true);
return;
}
if (quitButton.mouseOnIt(0)) { // QUIT BUTTON
exit(); // Spiel wird geschlossen
return;
}
}
}
void buyItem(int item) {
loadGameData(); // Münzen werden aus der Datei geladen
switch(item) { // Switch-Statement für welchen Gegenstand man versucght zu kaufen
case 1:
if (coins >= shopItem1Price && !boughtItem1) { // Überprüfung ob man genug Geld hat & Gegenstand noch nicht gekauft wurde
coins-=shopItem1Price; // Preis wird abgezogen
boughtItem1 = true; // Item 1 ist nun gekauft
saveGameData(); // Daten werden in Datei gespeichert
cashSound.play(); // Kaufsound wird abgespielt
}
break;
case 2:
if (coins >= shopItem2Price && !boughtItem2) { // Überprüfunng ob man genug Geld hat & `
coins-=shopItem2Price; //
boughtItem2 = true; //
saveGameData(); //
cashSound.play(); //
}
break;
case 3:
if (coins >= shopItem3Price && !boughtItem3) { // Überprüfunng ob man genug Geld hat & `
coins-=shopItem3Price; //
boughtItem3=true; //
saveGameData(); //
cashSound.play(); //
}
break;
}
}
//***************************************************************************************************************************
/****************************************************************************************************************************
** **
** Steuerung **
** **
** Hier befinden sich alle Funktionen die was mit Tasten & Berührungen zutun haben **
** **
****************************************************************************************************************************/
//***************************************************************************************************************************
int lastButtonClick; // Knopfcooldown
/*/************************************************************************************************************************************
** **
** Falls Linksklick gemacht wird **
** **
//************************************************************************************************************************************/
void mousePressed() {
if (lastButtonClick + 1f < frameCount) { // Falls der Buttoncooldown vorbei ist (Dazu da, das nicht durch das schnelle State wechseln mehrere Buttons aufeinmal gedrückt werden)
lastButtonClick=frameCount; // Cooldown wird zurückgesetzt
menuClickHandler(true); // Es wird geschaut, ob ein Button gedrückt wurde
}
}
void mouseMoved() {
menuClickHandler(false); // Überprüfen ob die Maus über einem Knopf ist
}
/*/************************************************************************************************************************************
** **
** Falls eine Taste gedrückt wird **
** **
//************************************************************************************************************************************/
void keyPressed() {
if (key == 'd') { // Falls d gedrückt wird, wird der Debugmode aktiviert/deaktiviert
showHitboxes = showHitboxes ? false : true; // Variable anhängig von ob showHitboxes true oder false ist (Conditional)
}
if(gameState==1)
// Spieler springen lassen, wenn er springen kann und er Leertaste drückt
if (key == ' ' && player.canJump && gameState==1) {
player.jump();
jumpCount++; // Sprungcount (für Doppelsprungextra) um 1 höher
}
if (keyCode == DOWN && player.canJump) player.crouch = true; // Bei Pfeiltaste runter geht der Spieler in die Knie
}
/*/************************************************************************************************************************************
** **
** Falls eine Taste losgelassen wird **
** **
//************************************************************************************************************************************/
void keyReleased() {
if(!(gameState==1)) return; // Fehlervemidung da der player zu Beginn noch null ist
// Sprungstatus zurücksetzen, wenn der Spieler die Leertaste loslässt und sich am Boden befindet
//if (key == ' ' && player.y == height - player.playerImg.height/2) {
// player.canJump = true;
//}
if (key == ' ' && boughtItem1 && jumpCount < 1) player.canJump=true;
if (keyCode == DOWN) {
player.crouch = false;
}
}
//***************************************************************************************************************************
/********************************************************g*******************************************************************
** **
** GAME **
** **
** Hier befinden sich alle Funktionen die während des Spiels ausgeführt werden **
** **
****************************************************************************************************************************/
//***************************************************************************************************************************
// Münzenbild
PImage coinImage;
int score; // Punktzahl
boolean canJump; // Sprungstatus des Spielers (ob der wieder springen kann)
boolean showHitboxes; // Debugmode (Zeigt die Kollisionsboxen an, kann mit 'd' aktiviert/deaktiviert werden)
PImage bg1, bg2, bg3, bg3R; // Hintergründe
PImage accessoirHelmet; // Mützenbild
float bg1Speed = 0.2; // Hintergrund 1 Geschwindigkeit
float bg2Speed = 0.5; // Hintergrund 2 Geschwindigkeit
float bg3Speed = 1.0; // Hintergrund 3 Geschwindigkeit
float bg1Offset = 0; // Hintergrund 1 offset
float bg2Offset = 0; // Hintergrund 2 offset
float bg3Offset = 0; // Hintergrund 3 offset
int gameTicker; // Stoppuhr (für Timer, Funktionen, Cooldowns...)
int tickSaveTheme; // Stoppuhrspeicher (für Timer, Funktionen, Cooldowns...)
int herzen; // Anzahl an Herzen
int levelTime = 20; // Zeit, die ein Level benötigt
int theme; // Aktuelle ID vom Level (Mensa, Zimmer, Gym...)
int jumpCount = 0; // Anzahl bereits gemachte Sprünge (um Doppelsprung zu berechnen)
int currentMusicNumber; // Aktuelle Musik ID
SoundFile currentMusic; // Aktuelle Musik Datei
ArrayList<Obstacle> obstacles = new ArrayList<Obstacle>(); // Liste von allen Gegern, Münzen & Objekten
ArrayList<Integer> alreadyThemesBeen = new ArrayList<Integer>(); // Liste von den Levels die man schon durchquert hat
Player player; // Spielerobjekt
/*/************************************************************************************************************************************
** **
** Funktion, die die ganzen Werte, Bilder etc. zu Spielbeginn ladet **
** **
//************************************************************************************************************************************/
void setupGame() {
// Hintergrundbilder werden geladen und resized
bg1 = loadImage("images/themes/tree_layer.png");
bg1.resize(width, height);
bg2 = loadImage("images/themes/mountain_layer.png");
bg2.resize(width, height);
bg3 = loadImage("images/themes/wallpaper.png");
bg3.resize(width, height);
bg3R = loadImage("images/themes/wallpaper.png");
bg3.resize(width, height);
// Geschwindigkeit Hintergründe
bg1Speed = 5;
bg2Speed = 3;
bg3Speed = 1;
// Timer / Stoppuhren zurückgesetzt
gameTicker = 0;
tickSaveTheme = 0;
jumpCount = 0;
// Annzahl Herzen
herzen = 10;
if (boughtItem2) herzen*=2; // Falls x2 Herzen gekauft wurde (im Shop)
// Zufälliges Level wird ausgewählt
theme = Math.round(random(1, 5));
alreadyThemesBeen.clear(); // Liste zurückgesetzt
alreadyThemesBeen.add(theme); // Neues Level zur Liste
score = 0; // Score zurückgesetzt
showHitboxes = false; // Debugmode deaktiviert
isVictory = false; // Sieg wird deaktiviert
// Zufällige Musik wird ausgewählt und abgespielt
currentMusicNumber = int(random(1, 4));
currentMusic = music[currentMusicNumber-1];
// Spielerobjekt wird definiert
player = new Player(width/2, height/2, 0.0, 0.6, 1, 4, true);//(float x, float y, float speed, float gravity, int currentImage, int maxImages, boolean canJump)
// Falls Item 3 im Shop gekauft wurde, wird der Helm geladen
if (boughtItem3) {
accessoirHelmet = loadImage("./images/extra/helmets/" + Math.round(random(1, 6)) + ".png");
}
}
/*/************************************************************************************************************************************
** **
** Funktion, die jeden Frame ausgeführt wird und alle Spielabläufe verwaltet **
** **
//************************************************************************************************************************************/
void gameHandler() {
gameTicker++; // Stoppuhr um 1 höher
background(#FFFFFF); // Hintergrund zeichnen
player.move(); // Spieler bewegen
moveBackgrounds(); // Verschiebung des hintersten Hintergrundbild von rechts nach links
if (player.y >= height - player.playerImg.height/2) { // Spieler Spring Animation, falls er am Springen ist / über dem Boden ist
player.displayJump();
}
player.display(); // Spieler wird gezeichnet
displayPoints(); // Punktzahl anzeigen
obstacleHandler(); // Hindernis bewegen und zeichnen
levelChanger(); // Nach einiger Zeit wird das Level gewechselt / Victory (Game over) aktiviert
spawnObstacles(); // Gegner werden erschaffen
spawnCoin(); // Münzen werden erschaffen
drawHearts(); // Herzensanzeige wird gezeichnet
checkIfMusicEnded(); // Musik wird neugstartet falls gestoppt
}
void checkIfMusicEnded() {
if (!(currentMusic.isPlaying())) {
int newMusicNumber = int(random(1, 4));
do {
newMusicNumber=int(random(1, 4));
} while (newMusicNumber==currentMusicNumber);
currentMusicNumber = newMusicNumber;
currentMusic = music[currentMusicNumber-1];
currentMusic.play();
}
}
/*/************************************************************************************************************************************
** **
** Funktion, die das Level ändert oder Game Over (bei Sieg) aktiviert **
** **
//************************************************************************************************************************************/
void levelChanger() {
if (tickSaveTheme / 60f + levelTime < ticker / 60f ) { // Alle 'levelTime' Sekunden (60 Frames) wird das Level gewechselt
if (alreadyThemesBeen.size() == 5) { // Falls alle Level durchgespielt wurden
isVictory=true;
currentMusic.stop();
doGameOver(); // Statewechsel / Game Over aktiviert
return;
}
int n;
do { // Solange kein neues Level gefunden wurde, wird der Random Integer neugeneriert
n = Math.round(random(1, 5));
} while (alreadyThemesBeen.contains(n));
theme = n;
alreadyThemesBeen.add(theme);
tickSaveTheme = ticker;
}
}
/*/************************************************************************************************************************************
** **
** Funktion, die die Hintergründe verschiebt **
** **
//************************************************************************************************************************************/
void moveBackgrounds() {
bg3Offset -= bg3Speed;
bg3Offset = bg3Offset % bg3.width;
for (float x = bg3Offset; x > -width; x -= bg3.width) { // Hintergrund wird bewegt sobald er das Fenster verlässt
image(bg3, x, 0);
image(bg3R, width+bg3Offset, 0);
if (showHitboxes) showDebugRect(color(255, 0, 0), int(x), 0, bg3.width, bg3.height);
if (showHitboxes) showDebugRect(color(255, 0, 0), int(width+bg3Offset), 0, bg3R.width, bg3R.height);
}
// Verschiebung des zweiten Hintergrundbild von rechts nach links
bg2Offset -= bg2Speed;
bg2Offset = bg2Offset % bg2.width;
for (float x = bg2Offset; x > -width; x -= bg2.width) { // Hintergrund wird bewegt sobald er das Fenster verlässt
image(bg2, x, 0);
image(bg2, width+bg2Offset, 0);
if (showHitboxes) showDebugRect(color(0, 255, 0), int(x), 0, bg2.width, bg2.height);
if (showHitboxes) showDebugRect(color(0, 255, 0), int(width+bg2Offset), 0, bg2.width, bg2.height);
}
// Verschiebung des dritten Hintergrundbild von rechts nach links
bg1Offset -= bg1Speed;
bg1Offset = bg1Offset % bg1.width;
for (float x = bg1Offset; x > -width; x -= bg1.width) { // Hintergrund wird bewegt sobald er das Fenster verlässt
image(bg1, x, 0);
image(bg1, width+bg1Offset, 0);
if (showHitboxes) showDebugRect(color(0, 255, 0), int(x), 0, bg1.width, bg2.height);
if (showHitboxes) showDebugRect(color(0, 0, 255), int(width+bg1Offset), 0, bg2.width, bg2.height);
}
}
/*/************************************************************************************************************************************
** **
** Score wird angezeigt **
** **
//************************************************************************************************************************************/
void displayPoints() {
textAlign(RIGHT);
textSize(32);
fill(255);
text("Score: " + score, width - 20, 40);
}
/*/************************************************************************************************************************************
** **
** Anzahl Herzen wird mit dem Symbol \u2764 gezeichnet **
** **
//************************************************************************************************************************************/
void drawHearts() {
String finalHeartText = "";
for (int i = 0; i < herzen; i++) finalHeartText += "\u2764"; // Von 0 - i (For Schleife) wird dem endgültigen String 'finalHeartText' das Herzsymbol hinzugefügt
fill(255, 0, 0);
textSize(80);
textAlign(LEFT);
text(finalHeartText, 40, 80); // Finale String wird angezeigt
}
/*/************************************************************************************************************************************
** **
** Funktion die deen Game Over Screen einleitet **
** **
//************************************************************************************************************************************/
void doGameOver() {
gameState = 4; // State wird gewechselt
createScreen(true);
currentMusic.stop(); // Spielmusik wird gestoppt
}
/*/************************************************************************************************************************************
** **
** Funktion fürs Debuggen, die Ränder von Rechtecken zeichnet **
** **
//************************************************************************************************************************************/
void showDebugRect(color c, int x, int y, int w, int h) {
stroke(c);
strokeWeight(3);
noFill();
rectMode(CORNER);
rect(x, y, w, h);
}
/*/************************************************************************************************************************************
** **
** Spielerobjekt das den Sprung, Bewegung & Animation vom Spieler verwaltet **
** **
//************************************************************************************************************************************/
class Player {
float x, y, speed, gravity; // X, Y, Sprungeschwindigkeit, Schwerkraft
int currentImage; // Aktuelles Bild der Animation
int maxImages; // Wie viele versch. Spielerversionen es gibt (Animation)
PImage playerImg; // Aktuelles Spielerbild was angezeigt wird
boolean canJump; // Boolean für ob der Spieler gerade springen kann
boolean crouch; // Ob der Spieler gerade am Crouchen ist
// Konstruktor
Player(float x, float y, float speed, float gravity, int currentImage, int maxImages, boolean canJump) {
this.x = x;
this.y = y;
this.speed = speed;
this.gravity = gravity;
this.currentImage = currentImage;
this.maxImages = maxImages;
this.canJump = canJump;
crouch = false;
this.playerImg = playerImages.get(0);
}
void display() {
if (ticker % 6 == 0) currentImage = currentImage+1>=maxImages ? 1 : currentImage+1; // Alle 6 Frames (wenn der frameCount durch 6 teilbar ist) wird das Spielerbild der Animation mit den nächsten getauscht
this.playerImg = playerImages.get(currentImage); // Neues Spielerbild wird gesetzt
if (crouch) this.playerImg = playerImages.get(4); // Falls der Spieler am Schleichen ist, wird der Schleichbild angezeigt
// Falls der Spieler den Hut gekauft hat im Shop wird er hier angezeigt
imageMode(CENTER);
if (boughtItem3) image(accessoirHelmet, this.x, this.y-25);
image(this.playerImg, this.x, this.y);
imageMode(CORNER);
}
// Falls man Leertaste drückt
void jump() {
this.speed = -20; // Spielersprung (wirkt sich auf Y-Koordinate des Spielers aus) wird auf -20 gesetzt -> Spieler geht hoch
if (!boughtItem1 || jumpCount >= 1) this.canJump = false; // Spieler kann nicht erneut springen, bis er den Boden erreicht hat
jumpSound.play(); // Sprungsound
}
void move() {
this.y += this.speed; // Spielersprung wirkt sich auf Y aus
this.speed += this.gravity; // Schwerkraft wirkt sich auf Spielersprung aus -> Spieler geht wieder runter
this.speed = constrain(this.speed, -10, 10); // Sprungkraft wird begrenzt
if (this.x < width/2) this.x++; // Falls der Spieler nicht in der Mitte ist, wird er sich dahin wieder bewegen (NICHT IN VERWENDUNG)
if (this.x < 0) { // Falls der Spieler aus dem Fenster geht kriegt er Schaden und wird wieder verschoben
herzen--;
this.x = width/2;
this.y = 500;
}
}
void displayJump() { // Sprung wird angezeigt
this.y = height - this.playerImg.height/2; // Y-Koordinate wird nochmals korrigiert
this.speed = 0; // Sprungkraft auf 0
this.canJump = true; // Spieler kann erneut springen, da er den Boden erreicht hat
jumpCount=0; // jumpCount auf 0
}
}
//***************************************************************************************************************************
/********************************************************g*******************************************************************
** **
** HIGHSCORE **
** **
** Hier befindet sich die Klasse für Buttons, die uns das erstellen von Knöpfen vereinfacht **
** **
****************************************************************************************************************************/
//***************************************************************************************************************************
int highscore = 0;
int coins = 0;
boolean boughtItem1, boughtItem2, boughtItem3 = false;
/*/************************************************************************************************************************************
** **
** Funktion, die die Scores & Shop Daten in der Datei speichert **
** **
//************************************************************************************************************************************/
void saveGameData() {
// Öffne die "data.txt" Datei im Schreibmodus
PrintWriter writer = createWriter("data.txt");
// Schreibe den Highscore und die Coins in die Datei
writer.println(highscore);
writer.println(coins);
writer.println(boughtItem1);
writer.println(boughtItem2);
writer.println(boughtItem3);
// Schließe die Datei
writer.close();
}
/*/************************************************************************************************************************************
** **
** Funktion, die die Daten aus der Datei ladet **
** **
//************************************************************************************************************************************/
void loadGameData() {
// Versuche die "data.txt" Datei zu öffnen
BufferedReader reader = createReader("data.txt");
if (reader != null) {
try {
// Lese den Highscore aus der Datei
String scoreStr = reader.readLine();
// Konvertiere den String in eine Zahl
highscore = Integer.parseInt(scoreStr);
// Lese die Coins aus der Datei
String coinsStr = reader.readLine();
// Konvertiere den String in eine Zahl
coins = Integer.parseInt(coinsStr);
// Schließe die Datei
String boughtItem1Str = reader.readLine();
boughtItem1 = Boolean.parseBoolean(boughtItem1Str);
String boughtItem2Str = reader.readLine();
boughtItem2 = Boolean.parseBoolean(boughtItem2Str);
String boughtItem3Str = reader.readLine();
boughtItem3 = Boolean.parseBoolean(boughtItem3Str);
reader.close();
}
catch (IOException e) {
println("Fehler beim Lesen der Datei: " + e.getMessage());
}
} else {
// Wenn die Datei nicht existiert, erstelle sie und schreibe den Highscore (0) und die Coins (0) hinein
saveGameData();
}
}
/*/************************************************************************************************************************************
** **
** Funktion, die die Daten Datei löscht **
** **
//************************************************************************************************************************************/
void deleteGameData() {
// Lösche die "data.txt" Datei, falls sie vorhanden ist
File file = new File(sketchPath("data.txt"));
if (file.exists()) {
file.delete();
}
// Setze den Highscore und die Coins auf 0
highscore = 0;
coins = 0;
}
//***************************************************************************************************************************
/********************************************************g*******************************************************************
** **
** LEVELS **
** **
** Hier werden die Hintergrundbilder und Gegner der Levels verwalten **
** **
****************************************************************************************************************************/
//***************************************************************************************************************************
void spawnObstacles() {
switch(theme) {
case 1: // DRAUSSEN / AUSSERHALB SCHULE
if (ticker-tickSaveTheme <= 1) { // nur 1x-mal
bg3 = loadImage("images/themes/building.jpg");
bg3R = loadImage("images/themes/building-r.jpg");
bg2 = loadImage("images/themes/mountain_layer.png");
bg1 = loadImage("images/themes/tree_layer.png");
}
spawnOutsideElements();
break;
case 2: // GANG
if (ticker-tickSaveTheme <= 1) { // nur 1x-mal
bg3 = loadImage("images/themes/gang.jpeg");
bg3R = loadImage("images/themes/gang-r.jpeg");
bg2 = loadImage("images/themes/gang_layer.png");
bg1 = loadImage("images/themes/gang_floor.png");
}
if (ticker % 60 == 0 && random(0, 100) <= 50) { // Alle 60 Frames mit 50% Chance wird ein Lehrer erschaffen
spawnRandomTeacher();
} else if (ticker % 180 == 0 && random(0, 100) < 30) {
spawnTable(); // Alle 180 Frames mit 30% Chance wird falls kein Lehrer erschaffen wird ein Tisch erschaffen
}
break;
case 3: // KLASSENZIMMER
if (ticker-tickSaveTheme <= 1) { // nur 1x-mal
bg3 = loadImage("images/themes/klassenzimmer.jpg");
bg3R = loadImage("images/themes/klassenzimmer-r.jpg");
bg2 = loadImage("images/themes/klassenzimmer_layer.png");
bg1 = loadImage("images/themes/klassenzimmer_floor.png");
}
spawnFurniture(); // Möbel wird erschaffen
break;
case 4:// KRAFTRAUM
if (ticker-tickSaveTheme <= 1) { // nur 1x-mal
bg3 = loadImage("images/themes/kraftraum.jpeg");
bg3R = loadImage("images/themes/kraftraum-r.jpeg");
bg2 = loadImage("images/themes/gym_layer.png");
bg1 = loadImage("images/themes/gym_floor.png");
}
spawnGymElements(); // Gymelemente werden erschaffen
break;
case 5: // MENSA
if (ticker-tickSaveTheme <= 1) { // nur 1x-mal
bg3 = loadImage("images/themes/mensa.jpeg");
bg3R = loadImage("images/themes/mensa-r.jpeg");
bg2 = loadImage("images/themes/mensa_layer.png");
bg1 = loadImage("images/themes/mensa_floor.png");
}
spawnFood(); // Essensgegner werden erschaffen
break;
}
}
void spawnTable() {
if (Math.round(random(1, 2)) == 1) { // 50% Chance
Enemy tisch = new Enemy("Tisch", "images/enemys/objects/depressionTable.png", int(random(2, 8))); // Tischelement das Schaden macht
tisch.spawn(); // Tisch erschaffen
} else {
Solid tisch = new Solid("Tisch", "images/enemys/objects/lehrerpult.png", int(random(4, 15))); // Tischelement das solide ist
tisch.spawn(); // Tisch erschaffen
}
}
void spawnFood() {
if ((frameCount % 100 == 0) && random(0, 100) > 30) { // Delay & Chance
Enemy food = new Enemy("Essen", "images/enemys/food/food-" + Math.round(random(0, 97)) + ".png", 10); // Enemyelement
food.resizeFactor(2.2, 2.2); // Bild wird resized
food.obstacleY+=10; // Y-Wert korrigiert
food.spawn(); // Wird erschaffen
}
if (frameCount % 60*50 == 0 && int(random(0, 10)) <= 2) spawnRandomTeacher(); // Das gleiche auch hier
if (frameCount % 60*30 == 0 && int(random(0, 10)) <= 1) spawnWoodStair(); // Das gleiche auch hier
}
//
// Ab hier keine Kommentare mehr, da das Prinzip überall gleich ist
//
void spawnWoodStair() {
for (int i = 0; i < 3; i++) {
for (int b=0; b < i+1; b++) {
Solid holzKiste = new Solid("Holzkiste", "images/enemys/objects/kiste.png", 6);
holzKiste.spawn();
holzKiste.resizeFactor(0.6, 0.6);
holzKiste.obstacleX = holzKiste.obstacleX + ((holzKiste.obstacleImg.width+200)*i);
holzKiste.obstacleY = holzKiste.obstacleY - (holzKiste.obstacleImg.height*b);
}
}
}
void spawnFurniture() {
String[] possFurnitureElements = new String[]{"bookshelf.png", "lehrerpult.png", "schrank.png", "schrank2.png", "schrank3.png"};
if (frameCount % 120 == 0 && int(random(0, 100)) < 50) {
String fString = possFurnitureElements[int(random(0, possFurnitureElements.length))];
Enemy furniture = new Enemy("Möbel", "images/enemys/objects/" + fString, int(random(4, 10)));
furniture.spawn();
}
}
void spawnGymElements() {
String[] possGymElements = new String[]{"bank", "beinpresse", "beinstrecke", "hantelbank", "kabelzug", "langhantel", "laufband", "laufrad", "multigym"};
if (ticker % 40 == 0 && random(0, 100) < 40) {
if (!(int(random(1, 10)) == 1)) {
Obstacle element = new Enemy("Gymelement", "images/enemys/objects/"+possGymElements[int(random(0, possGymElements.length))]+".png", 6);
element.resizeFactor(0.9, 0.9);
element.spawn();
} else if (random(1, 100) < 30) {
String teacherImageName = "sport";
String teacherName = "Sportlehrer";
Obstacle teacherObject = new Enemy(teacherName, "/images/enemys/teachers/" + teacherImageName + ".png", 6);
teacherObject.spawn();
teacherObject.showName();
} else if ((random(1, 100) > 50)) {
spawnWoodStair();
} else {
PowerUp trampolin = new PowerUp("Trampolin", "/images/enemys/objects/trampolin.png", 6, 0);
trampolin.resizeFactor(0.06, 0.06);
trampolin.spawn();
}
}
}
void spawnOutsideElements() {
if (ticker % 80 == 0 && random(0, 100) < 40) {
if (int(random(1, 2)) == 1) {
if (int(random(1, 5)) == 1) {
Obstacle holzKiste = new Enemy("Holzkiste", "images/enemys/objects/kiste.png", 6);
holzKiste.resize(50, 50);
holzKiste.spawn();
holzKiste.obstacleX = holzKiste.obstacleX + int(random(0, 300));
} else {
Obstacle holzKiste = new Enemy("Holzkiste", "images/enemys/objects/kiste.png", 6);
holzKiste.resize(50, 50);
holzKiste.spawn();
}
} else spawnRandomTeacher();
}
if (ticker % 110 == 0 && random(0, 100) < 10) {
Obstacle bus = new Enemy("Bus", "images/enemys/objects/bus.png", int(random(10, 15)));
bus.resizeFactor(0.8, 0.8);
bus.spawn();
}
}
void spawnRandomTeacher() {
// Hier wird ein Randombild ausgewählt
String[] teachers = new String[]{"abwart-Abwart", "bio-Biolehrer", "chemie-Chemielehrer", "geografie-Geografielehrer", "info-Informatiklehrer", "mathe-Mathematiklehrer", "musik-Musiklehrer", "rektor-Rektor", "rektor-Prorektor", "sport-Sportlehrer"};
String teacher = teachers[int(random(0, teachers.length))];
String teacherImageName = teacher.split("-")[0];
String teacherName = teacher.split("-")[1];
Obstacle teacherObject = new Enemy(teacherName, "/images/enemys/teachers/" + teacherImageName + ".png", 6);
teacherObject.spawn();
}
//***************************************************************************************************************************
/********************************************************g*******************************************************************
** **
** Obstacles **
** **
** Hier befinden sich alle Obstacle Funktionen & Klassen die von der Hauptklasse Obstacle erben **
** **
****************************************************************************************************************************/
//***************************************************************************************************************************
/*/************************************************************************************************************************************
** **
** Code der für die Berührungen & Hitboxen der Objetke verantwortlich ist) **
** **
//************************************************************************************************************************************/
void obstacleHandler() {
for (int i = 0; i < obstacles.size(); i++) { // For Schleife durch alle Elemente
Obstacle currentObstacle = obstacles.get(i); // Obstacle in der Schleife
if (currentObstacle != null) {
// Hitboxes Obstacle nur zum Debuggen
if (showHitboxes) showDebugRect(color(255, 0, 0), int(currentObstacle.obstacleX), int(currentObstacle.obstacleY), currentObstacle.obstacleImg.width, currentObstacle.obstacleImg.height);
if (showHitboxes) showDebugRect(color(255, 0, 0), int(player.x-player.playerImg.width/2), int(player.y-player.playerImg.height/2), player.playerImg.width, player.playerImg.height);
currentObstacle.obstacleX -= currentObstacle.obstacleSpeed; // Obstacle-X wird mit Obastclespeed verändert
if (currentObstacle.obstacleX < -100) obstacles.remove(currentObstacle); // Falls das Obstacle aus dem Fenster geht, wird es gelöscht
image(currentObstacle.obstacleImg, currentObstacle.obstacleX, currentObstacle.obstacleY);
if (currentObstacle.showName) { // Falls der boolean true ist, wird über dem Objekt der Name angezeigt
textSize(10);
fill(#FFFFFF);
textAlign(CENTER);
text(currentObstacle.obstacleName, currentObstacle.obstacleX, currentObstacle.obstacleY-30);
}
// DEBUGMODE
if (showHitboxes) {
showDebugRect(color(255, 255, 255), int(player.x-player.playerImg.width/2), int(player.y+player.playerImg.height/2), player.playerImg.width, 1);
showDebugRect(color(255, 255, 255), int(currentObstacle.obstacleX), int(currentObstacle.obstacleY), currentObstacle.obstacleImg.width, 1);
text(currentObstacle.obstacleImg.width + "x"+ currentObstacle.obstacleImg.height, currentObstacle.obstacleX, currentObstacle.obstacleY-20);
textAlign(LEFT);
fill(255, 0, 0);
textSize(10);
text("canJump: " + player.canJump, 40, 100);
text("gameTicker: " + gameTicker, 40, 100+20);
text("tickSaveTheme: " + tickSaveTheme, 40, 100+40);
text("herzen: " + herzen, 40, 100+60);
text("levelTime: " + levelTime, 40, 100+80);
text("theme: " + theme, 40, 100+100);
text("obstacles length: " + obstacles.size(), 40, 100+120);
text("alreadyThemesBeen length: " + alreadyThemesBeen.size(), 40, 100+140);
text("player gravity: " + player.gravity, 40, 100+160);
text("player x: " + player.x, 40, 100+180);
text("player y: " + player.y, 40, 100+200);
text("player speed: " + player.speed, 40, 100+220);
text("player crouch: " + player.crouch, 40, 100+240);
text("player animation: " + player.currentImage, 40, 100+260);
text("framecount: " + frameCount, 40, 100+280);
text("framerate: " + frameRate, 40, 100+300);
text("jumpcount: " + jumpCount, 40, 100+320);
}
// Kollisionserkennung
if (player.x + player.playerImg.width/2 >= currentObstacle.obstacleX && player.x <= currentObstacle.obstacleX + currentObstacle.obstacleImg.width &&
player.y + player.playerImg.height/2 >= currentObstacle.obstacleY) {
if (player.y+player.playerImg.height/2-10 <= currentObstacle.obstacleY && currentObstacle instanceof Solid) { // Falls der Spieler über dem Objekt ist und das Objekt solide ist
player.y-=10; // Spieler wird hochgedrückt / Steht auf dem Objekt
player.canJump = true; // Spieler kann wieder springen
if (showHitboxes) { // DEBUGMODE
imageMode(CENTER);
image(up, player.x, player.y);
imageMode(CORNER);
}
}
//else if (currentObstacle instanceof Solid) {
// player.canJump = false;
// if (player.x+player.playerImg.width/2+5 >= currentObstacle.obstacleX) {
// player.x-=currentObstacle.obstacleSpeed*2-1;
// player.canJump = true;
// if (showHitboxes) {
// imageMode(CENTER);
// image(left, player.x+player.playerImg.width/2+5, player.y);
// imageMode(CORNER);
// }
// } else player.canJump = false;
//}
currentObstacle.collide();
}
}
}
}
// Funktion die jede 100 Frames mit 50% Chance einen Coin erschafft
void spawnCoin() {
if (!(ticker % 100 == 0 && random(0, 100) > 50)) return;
Obstacle c = new Coin("Coin", "images/coin.png", 5);
c.resize(30, 30);
c.spawn();
}
abstract class Obstacle {
String obstacleName; // Name
String obstacleImage;// Bilddateipfad
PImage obstacleImg; // Bild
int obstacleX; // X
int obstacleY; // Y
int obstacleSpeed; // Geschwindigkeit
boolean showName = false; // Wert ob der Name angezeigt werden soll
Obstacle(String obstacleName, String obstacleImage, int obstacleSpeed) { //////////////////////////////////////////////////////////////////////// VORLAGE / MUTTERKLASSE / ABSTRAKTE KLASSE
this.obstacleName = obstacleName;
this.obstacleImage = obstacleImage;
this.obstacleX = width+50;
this.obstacleSpeed = obstacleSpeed;
obstacleImg = loadImage(obstacleImage);
int imgHeight = obstacleImg.height;
this.obstacleY = height-imgHeight;
if (showHitboxes) { // Falls Debugmode aktiv ist, wird der Name angezeigt
this.showName = true;
this.obstacleName = obstacleImage;
}
}
void spawn() { // .spawn fügt das Obstacle der Liste zu
obstacles.add(this);
}
void resize(int w, int h) { // Obstaclebild resizen
obstacleImg.resize(w, h);
this.obstacleY = height-h;
}
void resizeFactor(float w, float h) { // Obstaclebild mit float-multiplizieren
obstacleImg.resize(int(obstacleImg.width*w), int(obstacleImg.height*h));
this.obstacleY = height-obstacleImg.height;
}
void showName() { //
showName = true;
}
abstract void collide(); // Abstrakte Funktion / Vorlage für alle Obstacles
}
//
// Ab hier kommen die verschiedenen Obstacletypen die von der Hauptklasse erben
//
class Enemy extends Obstacle { //////////////////////////////////////////////////////////////////////// ENEMY
Enemy(String obstacleName, String obstacleImage, int obstacleSpeed) {
super(obstacleName, obstacleImage, obstacleSpeed);
}
void collide() {
obstacles.remove(this);
herzen--;
damageSound.play();
if (herzen <= 0) {
loadGameData();
coins+=score;
if (highscore < score) highscore = score;
saveGameData();
obstacles.clear();
gameState = 4;
frameRate(60);
createScreen(true); //DEBUG
}
}
}
class PowerUp extends Obstacle { //////////////////////////////////////////////////////////////////////// POWER UP
/*
Effect 0 = Spieler wird hochgeschossen
*/
int effect;
PowerUp(String obstacleName, String obstacleImage, int obstacleSpeed, int effect) {
super(obstacleName, obstacleImage, obstacleSpeed);
this.effect = effect;
}
void collide() {
obstacles.remove(this); // Obstacle wird nicht mehr angezeigt
switch(effect) {
case 0:
player.speed = -600; // Spieler wird hochgespickt
player.canJump = true; // Spieler kann nochmals springen
break;
}
}
}
class Solid extends Obstacle {
Solid(String obstacleName, String obstacleImage, int obstacleSpeed) {
super(obstacleName, obstacleImage, obstacleSpeed);
}
void collide() {
}
}
class Coin extends Obstacle { //////////////////////////////////////////////////////////////////////// COIN
Coin(String obstacleName, String obstacleImage, int obstacleSpeed) {
super(obstacleName, obstacleImage, obstacleSpeed);
}
void collide() {
score++; // Score um 1 höher
coinSound.play();
obstacles.remove(this);
}
}
//***************************************************************************************************************************
/********************************************************g*******************************************************************
** **
** QUELLEN & BEMERKUNGEN **
** ** **
****************************************************************************************************************************/
//***************************************************************************************************************************
/*
Menü Musik: https://youtu.be/uIfD2BKaD2k
Background Image: spiritus.ch
Coin Texture: https://opengameart.org/content/gold-cointoken (cc0)
Animation Music: Ultimate 8-bit Electro Gaming Music Mix 2020 - Chiptune Music Mix
Game Over Sound bei Verloren: https://freesound.org/people/EVRetro/sounds/533034/
Game Over Sound bei Gewonnen: https://freesound.org/people/Mrthenoronha/sounds/518305/
Kaufsound: https://freesound.org/people/kiddpark/sounds/201159/
Lehrerbilder: https://opengameart.org/content/pixel-game-characters
Spielerbilder: https://opengameart.org/content/character-sprite-walk-animation
Schrankbild: https://opengameart.org/content/cupboard-0
Bücherregalbild: https://opengameart.org/content/bookshelf-3
Busbild: https://opengameart.org/content/school-bus
Inspiration Gymelemente: https://www.vhv.rs/viewpic/iTohbmx_fitness-clipart-gym-tool-gym-equipment-pixel-art/
Tischbild: https://opengameart.org/content/table-to-do-the-only-thing-worth-it-in-life
Möbel als Inspiration: https://opengameart.org/content/bad-kitchen-tileset
Musik:
- https://opengameart.org/content/quirky-fast-dnb-8bit-drums-and-bass-lmfao
- https://opengameart.org/content/8bit-theme-upbeat-overworld
- https://opengameart.org/content/boss-battle-5-8-bit
- https://opengameart.org/content/8-bit-theme-moving-right-along
Coinsound: https://freesound.org/people/rigor789/sounds/341979/
Damagesound: https://freesound.org/people/Taira%20Komori/sounds/214209/
Jumpsound: https://freesound.org/people/Prof.Mudkip/sounds/422087/
*/
// Bemerkungen
/*
- Die Kollisionsboxen können manchmal ungenau sein, was das Spiel vereinfachen soll.
- Es gibt eine Schleichfunktion, die aber nicht gebaucht werden muss, da wir sie letztendlich doch unnötig gefunden haben
- Die Hintergrundgrafiken sind meist entweder von der Spiritus.CH Galerie oder selbstgemacht
- Nicht alle Möbel & Objekte sind vom Internet, sondern manche wurden auch selber gezeichnet
*/