Commit 8b7efe2d authored by Ahmet Rüchan Arican's avatar Ahmet Rüchan Arican
Browse files

Initial commit

parents
# <span style="color: #34A09D">Penalty Champs Retro⚽(PiS, SoSe2021)</span>
Ein kleines Elfmeterschießen-Minispiel im Retro-Stil wie es Papa noch kennt
Autor: Ahmet-Rüchan Arican, 5341696
***
## Kurzbeschreibung
Das Spiel handelt hierbei um das klassische Elfmeterschießen, wie wir es im echten Fußball kennen. [Mehr Infos](https://de.wikipedia.org/wiki/Elfmeterschie%C3%9Fen "Wikipedia-Artikel") \
Nach der Wahl des Teams geht es auch schon los:\
Das Tor ist in 6 Flächen unterteilt: **links**, **rechts** und die **Mitte** jeweils **oben** und **unten**. In einer dieser Flächen schießen sie.
Der Torwart springt dabei in eine rein zufällig gewählte Position. Trifft der Nutzer, so bekommt er einen Punkt. Ansonsten
kriegt der Gegner einen.\
Nachdem der Spieler 5 Schüsse absolviert hat, wird gewechselt, d. h. nun ist der **Nutzer** der **Torwart** und der
**Computer** der **Schütze**, ihre Rolle sehen sie auch im Scoreboard.\
Wenn nach diesen 5 Schüssen (also insgesamt 10) immer noch kein Sieger feststeht, wird in die Playoff-Runde
gegangen. Das bedeutet der Nutzer wechselt immer ab, jeweils ein Schuss und anschließend einmal parieren. Somit
wird weiter gemacht bis ein Sieger feststeht. (141 Wörter)
***
## Screenshot zum Projekt
(*Die rot gekennzeichneten Grenzen können minimal von der Realität abweichen*)
![](app/src/main/resources/Screenshot%20Game2.jpg)
***
## Bedienungshinweise
Zu aller erst wählt der Nutzer im Startbildschirm ein Team aus, indem er, wie es dort steht, die Taste *F* bzw *f* für das
linke Team (Fenerbahce) oder *G* bzw *g* für das rechte Team (Galatasaray) drückt.
Nun muss der Spieler auf das Tor klicken und der Ball geht in die gewählte Richtung\
**!!Achtung: Nach einem Mal klicken kann der Schuss nicht zurückgezogen oder geändert werden!!** Falls sie die
Maus trotzdem währen dem Schuss bewegen wird der Prozess übersprungen und sie sehen nur, ob sie einen Score
bekommen haben oder nicht\
Falls der Nutzer gerade der Schütze ist und außerhalb dieses Bereichs, also ins Aus schießt, kriegt der Gegner einen
Punkt. Falls er der Torwart ist und sie ins Aus klicken, dann bleibt der Torwart nur stehen und der Gegner führt sein
Schuss durch.\
Das Tor ist wie in dem [Screenshot](#screenshot-zum-projekt) unterteilt.\
Am Ende des Spiels muss der Nutzer nun, wenn er nochmal spielen will, irgendeine beliebige Taste drücken
oder wenn er das Spiel beenden will die Taste *0* drücken (**Achtung:** die Zahl 0, nicht der Buchstabe O).
***
## Übersicht auf die Dateien und die Lines of Code
### Dateiübersicht
\Readme.md
\gradlew
\gradlew.bat
\settings.gradle
\app\build.gradle
\app\core.jar
\app\src\main\java\Projekt\Penalty\DI.java
\app\src\main\java\Projekt\Penalty\GameEngine.java
\app\src\main\java\Projekt\Penalty\Goalkeeper.java
\app\src\main\java\Projekt\Penalty\Penalty.java
\app\src\main\java\Projekt\Penalty\Shooter.java
\app\src\main\java\Projekt\Penalty\Team.java
\app\src\main\resources\Trophy.png
\app\src\main\resources\ball2.png
\app\src\main\resources\FB.png
\app\src\main\resources\GS.png
\app\src\main\resources\net11.png
\app\src\main\resources\Pitch.jpg
\app\src\main\resources\Saving Goalie.png
\app\src\main\resources\Scoreboard.png
\app\src\main\resources\Screenshot Game2.jpg
\app\src\main\resources\Stadium2.jpg
\app\src\main\resources\Standing Goalie.png
\app\src\test\java\Projekt\Penalty\GameEngineTest.java
\gradle\wrapper\gradle-wrapper.jar
\gradle\wrapper\gradle-wrapper.properties
### Lines of Code
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Java 6 88 25 477
-------------------------------------------------------------------------------
SUM: 6 88 25 477
-------------------------------------------------------------------------------
## Quellen
(Alle im Stand von 29.06.2021.)
### Bilder
- https://www.pngkey.com/png/detail/2-26292_chain-link-fence-texture-png-seamless-transparent-chain.png
- https://pngtree.com/freepng/oblique-oblique-posture-dummy-limb_3921864.html
- https://pngtree.com/freepng/dummy-limb-cartoon-cartoon-limb_3921866.html
- https://png.pngtree.com/png-vector/20190912/ourmid/pngtree-modren-ball-icon-vector-png-image_1726872.jpg
- https://upload.wikimedia.org/wikipedia/commons/f/f6/Galatasaray_Sports_Club_Logo.png
- https://upload.wikimedia.org/wikipedia/commons/f/f6/Galatasaray_Sports_Club_Logo.png
- https://www.pngkey.com/png/full/811-8117820_trophy-transparent-background-trophy-png.png
- https://banner2.cleanpng.com/20180601/qtl/kisspng-fenerbahe-s-k-fenerbahe-men-s-basketball-euro-fb-5b11829113ed76.5359636415278741930816.jpg
- https://static1.s123-cdn-static-a.com/uploads/4379155/800_5f8afaae91012.png
- https://image.freepik.com/free-photo/green-grass-field-background-soccer-football-sports-green-lawn-pattern-texture-background-close-up_64749-2270.jpg
### Inspirationen und Inhalte
- https://processing.org/reference/loadImage_.html
- https://www.youtube.com/watch?v=hsAiIytWf-I
- https://stackoverflow.com/
- https://www.baeldung.com/
- https://www.w3schools.com/java/
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java application project to get you started.
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
* User Manual available at https://docs.gradle.org/7.0/userguide/building_java_projects.html
*/
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
implementation 'org.junit.jupiter:junit-jupiter:5.7.0'
// Use JUnit test framework.
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.0'
// This dependency is used by the application.
implementation 'com.google.guava:guava:30.0-jre'
implementation files('core.jar')
}
application {
// Define the main class for the application.
mainClass = 'Projekt.Penalty.DI'
}
test {
useJUnitPlatform();
}
File added
package Projekt.Penalty;
import processing.core.PApplet;
import processing.core.PImage;
public class DI extends PApplet {
Penalty ge = new GameEngine();
PImage net, ball, goalie, saveGoalie, fb, gs, pitch, stadium, scoreboard, trophy;
boolean setup, clubSelected, loopCancelerVar;
int xBallSpeed = 450, yBallSpeed = 550;
public static void main(String[] args) {
DI x = new DI();
x.runSketch(new String[]{});
}
//Fenstergröße
public void settings() {
size(900, 600);
}
//Bilder werden hochgeladen
public void setup() {
rectMode(CENTER);
ellipseMode(CENTER);
imageMode(CENTER);
textAlign(CENTER);
trophy = loadImage("Trophy.png");
scoreboard = loadImage("Scoreboard.png");
stadium = loadImage("Stadium2.jpg");
net = loadImage("net11.png");
goalie = loadImage("Standing Goalie.png");
saveGoalie = loadImage("Saving Goalie.png");
ball = loadImage("ball2.png");
fb = loadImage("FB.png");
gs = loadImage("GS.png");
pitch = loadImage("Pitch.jpg");
setField();
}
//Das Spielfeld wird eingerichtet und sorgt im Hintergrund für weiter anstehende Ambience
private void setField() {
image(stadium, 450, 250, 900, 600);
image(net, 450, 250, 500, 300);
image(pitch, 450, 550, 900, 300);
stroke(240);
line(0, 400, 900, 400);
line(200, 100, 250, 200);
line(700, 100, 650, 200);
line(250, 200, 250, 400);
line(650, 200, 650, 400);
noFill();
strokeWeight(6);
rect(450, 250, 500, 300);
fill(255);
ellipse(450, 550, 20, 20);
image(scoreboard, 450, 50, 300, 100);
image(fb, 350, 30, 50, 50);
image(gs, 550, 30, 50, 50);
textSize(32);
text(ge.getScore1() + " : " + ge.getScore2(), 450, 40);
textSize(20);
text(ge.getCurrentPlayer() == 1 ? "Shooter" : "Goalkeeper", 450, 70);
}
//Das Spielfeld wird nach dem Schuss zurückgesetzt: Ball auf Mittelpunkt / Torwart in Mitte
public void resetField() {
image(ball, xBallSpeed = 450, yBallSpeed = 550, 40, 40);
image(goalie, 450, 305, 275, 276);
}
public void draw() {
if (setup) {
if (ge.isGameOver()) {
String winner = ge.getScore1() > ge.getScore2() ? "Fenerbahce" : "Galatasaray";
String message = "The Champions: " + winner;
int color2 = winner.equals("Fenerbahce") ? color(0, 0, 139) : color(200, 0, 0);
fill(255, 255, 51);
rect(225, 300, 460, 600);
fill(color2);
rect(675, 300, 460, 600);
image(trophy, 160, 300, 200, 220);
image(trophy, 740, 300, 200, 220);
fill(0);
textSize(48);
text(message, width / 2f, 75);
textSize(22);
text("to play again: press any key", 200, 540);
text("to Quit: press 0", 200, 570);
if (ge.getScore1() > ge.getScore2()) {
image(fb, 450, 300, 350, 350);
} else {
image(gs, 450, 325, 350, 425);
}
if (keyPressed) {
if (key == '0') {
System.exit(0);
} else {
resetStats();
setup();
}
}
} else {
setField();
if (mouseButton == LEFT && !loopCancelerVar) {
saveGoal(ge.getAngle());
shoot(ge.getShootPos());
} else {
resetField();
}
}
} else {
background(0);
image(fb, 200, 300, 200, 200);
image(gs, 650, 300, 200, 220);
text("Choose one Team you wanna play\nPress 'F' for Fenerbahce(left)\nand 'G' for Galatasaray(right)", width / 2f, 100);
if (keyPressed) {
if (key == 'f' || key == 'F') {
ge.setMyTeam(Team.FENERBAHCE);
} else {
ge.setMyTeam(Team.GALATASARAY);
}
setup = true;
clubSelected = true;
}
}
}
//Berechnet Koordinaten vom Ball wie weit es jeweils in der DI gehen soll
public void ballMove(int pos) {
switch (pos) {
case 0 -> {
if (xBallSpeed >= 270 && yBallSpeed >= 180) {
xBallSpeed -= 10;
yBallSpeed -= 20;
} else {
loopCancelerVar = true;
}
}
case 1 -> {
if (yBallSpeed >= 180) {
yBallSpeed -= 15;
} else {
loopCancelerVar = true;
}
}
case 2 -> {
if (xBallSpeed <= 620 && yBallSpeed >= 180) {
xBallSpeed += 10;
yBallSpeed -= 20;
} else {
loopCancelerVar = true;
}
}
case 3 -> {
if (xBallSpeed >= 260 && yBallSpeed >= 100) {
xBallSpeed -= 15;
yBallSpeed -= 15;
} else {
loopCancelerVar = true;
}
}
case 4 -> {
if (yBallSpeed >= 320) {
yBallSpeed -= 20;
xBallSpeed -= 5;
} else {
loopCancelerVar = true;
}
}
case 5 -> {
if (xBallSpeed <= 635 && yBallSpeed >= 320) {
xBallSpeed += 15;
yBallSpeed -= 15;
} else {
loopCancelerVar = true;
}
}
default -> {
if (xBallSpeed >= 100 && yBallSpeed >= 360) {
xBallSpeed -= 15;
yBallSpeed -= 10;
} else {
loopCancelerVar = true;
}
}
}
}
//Die Interaktionsmethode -> unten mehr Details
public void mouseClicked() {
loopCancelerVar = false;
if (clubSelected) {
ge.checkGameStatus();
ge.setRandomPos();
//Bei Maus-Klick wird jeweils Schuss gesetzt in AL und DI
if (mouseX > 200 && mouseX <= 350) {
if (mouseY >= 100 && mouseY <= 250) {
ge.setPosition(0);
} else if (mouseY > 250 && mouseY <= 400) {
ge.setPosition(3);
} else {
ge.outClicked();
}
} else if (mouseX > 350 && mouseX <= 550) {
if (mouseY > 0 && mouseY <= 250) {
ge.setPosition(1);
} else if (mouseY > 250 && mouseY <= 400) {
ge.setPosition(4);
} else {
ge.outClicked();
}
} else if (mouseX > 550 && mouseX <= 700) {
if (mouseY > 0 && mouseY <= 250) {
ge.setPosition(2);
} else if (mouseY > 250 && mouseY <= 400) {
ge.setPosition(5);
} else {
ge.outClicked();
}
} else {
ge.outClicked();
}
//Punkteaktualisierung -- Bei einem Schuss ins Aus kriegt der Gegner einen Punkt
ge.updateScore();
//Tor wird "leergeräumt" sodass Tor-Array wieder leer ist
ge.freshGoal();
}
}
//Die Animation des Balles wird hier dargestellt
public void shoot(int pos) {
ballMove(pos);
image(ball, xBallSpeed, yBallSpeed, 40, 40);
}
//Rotation des Torwarts
public void saveGoal(int in) {
pushMatrix();
if (in == 315 || in == 45) {
translate(450, 350);
} else translate(450, 400);
rotate(radians(in));
translate(-450, -400);
image(saveGoalie, 450, 288, 140, 276);
popMatrix();
}
//Bei einem Neustart müssen alle Statistiken zurückgesetzt
public void resetStats() {
loopCancelerVar = false;
setup = false;
clubSelected = false;
xBallSpeed = 450;
yBallSpeed = 550;
ge.resetStats();
}
}
\ No newline at end of file
package Projekt.Penalty;
import java.util.Arrays;
import java.util.Random;
public class GameEngine implements Penalty {
Goalkeeper goalkeeper = new Goalkeeper();
Shooter shooter = new Shooter();
private Team myTeam;
private int[] goal = new int[6];
private boolean playOff, gameOver;
private int currentPlayer = shooter.getValue();
private int enemy = goalkeeper.getValue();
private int score1, score2, round, generalRound, changedPlayer; //round: prüft auf 5 Schüsse pro Runde ;; generalRound: Sorgt dafür das 2x 5 Schüsse gemacht werden
public void setMyTeam(Team in) {
assert in != null : "must enter a valid Team";
myTeam = in;
}
public int getCurrentPlayer() {
return this.currentPlayer;
}
public int getShootPos() {
return shooter.getPosition();
}
public int getScore1() {
return this.score1;
}
public int[] getGoal() {
return goal;
}
public int getScore2() {
return this.score2;
}
public boolean isGameOver() {
return gameOver;
}
public void setShot(int pos) {
assert pos < 6 : "Entry is out of range";
this.goal[pos] += currentPlayer;
}
public void setGoalkeeper(int pos) {
assert pos < 7 : "invalid Position"; //die Zahl 6 ist absichtlich mit eingebunden als default-Wert bei einem ins Aus klicken
this.goal[pos] += enemy;
}
public int getAngle() {
return goalkeeper.getAngle();
}
public void setPosition(int pos) {
assert pos < 7 : "position out of range";
if (currentPlayer == 1) {
shooter.setPosition(pos);
} else {
goalkeeper.setPosition(pos);
setGoalkeeper(pos);
}
}
//Ist für die Zufalls-Entscheidung des Gegners zuständig
public void setRandomPos() {
int pos = randomizer();
if (currentPlayer == 1) {
goalkeeper.setPosition(pos);
setGoalkeeper(pos);
} else {
shooter.setPosition(pos);
}
}
//Methode wird aufgerufen, wenn Nutzer ins Aus klickt, also außerhalb des Wertebereichs
public void outClicked() {
shooter.setPosition(currentPlayer == shooter.getValue() ? 6 : 2);
goalkeeper.setPosition(1);
}
//Methode gibt true zurück wenn aktuelle Position vom Schuss nicht vom Torhüter gedeckt wurde
public boolean checkGoal() {
return Arrays.stream(goal).anyMatch(n -> n != 0);
}
public void checkGameStatus() {
//Falls wir in der Playoff-Runde sind muss nach jedem Schuss gewechselt werden
if (playOff) {
changePlayer();
changedPlayer++;
} else {
//Wechselt Spieler nach 5 Schüssen
if (round == 5 || round == 10) {
generalRound++;
changePlayer();
}
//Prüft, ob nach 10 Schüssen ein Gewinner steht oder unentschieden ist
if (generalRound == 2 && round % 2 == 0) {
if (checkWinner()) {
gameOver = true;
} else {
playOff = true;
}
}
//Runde wid erhöht
round++;
}
//prüft in der Playoff-Runde nach 2 Schüssen ob es ein Gewinner gibt
if (changedPlayer % 2 == 0 && changedPlayer != 0) {
if (checkWinner()) gameOver = true;
}
}
//Der Score wird erhöht jeweils für oder gegen den Spieler
public void entryScore() {
switch (myTeam) {
case FENERBAHCE -> {
if (checkGoal()) {
if (currentPlayer == 1) {
score1++;
} else {
score2++;
}
} else {
if (currentPlayer == 1) {
score2++;
} else {
score1++;
}
}
}
case GALATASARAY -> {
if (checkGoal()) {
if (currentPlayer == 1) {
score2++;
} else {
score1++;
}
} else {
if (currentPlayer == 1) {
score1++;
} else {
score2++;
}
}
}
}
}
public void updateScore() {
if (shooter.getPosition() != 6) {
setShot(shooter.getPosition());
entryScore();
} else {
if (currentPlayer == 1) {
if (myTeam == Team.FENERBAHCE) {
score2++;
} else {
score1++;
}
} else {
if (myTeam == Team.FENERBAHCE) {
score1++;
} else {
score2++;
}
}
}
}
//Methode gibt true wenn es einen Gewinner gibt
public boolean checkWinner() {
return score1 > score2 || score2 > score1;
}