Semplice gioco, con un limite da 500 righe(questo gioco ne ha circa 407), sviluppato con la libreria grafica javafx; IDE: IntelliJ IDEA
Sconfiggi il nemico con dei proiettili, mirando e sparando con il mouse e muovendoti in 8 direzioni! Attenzione: il nemico non starà fermo a guardare! Utilizza lo sprint e non avvicinarti troppo al nemico, se vuoi vivere a lungo...

Il gioco è composto da 9 classi(esclusa GameApplication) e una interfaccia:
- Player
- rappresenta il
Playercon cui l'utente interagisce
- rappresenta il
- Enemy
- rappresenta il nemico da sconfiggere
- GameUpdate
- rappresenta il cuore del gioco, dove si ha il loop e si controllano tutti gli aspetti legati all'esecuzione del gioco
- Projectile
- rappresentano i colpi che ha a disposizione il
Playere l'Enemy
- rappresentano i colpi che ha a disposizione il
- Character
- rappresenta un personaggio astratto
- CharacterPlayable
- rappresenta un personaggio astratto giocabile
- Entity
- rappresenta ogni oggetto, personaggio, ecc... qualsiasi cosa del gioco
- Collider
- rappresenta il collider che ha ogni oggetto e personaggio
- ScreenSettings
- rappresenta le caratteristiche per la finestra di gioco
- GameScene
- Rappresenta la scena del gioco
- GameApplication
- rappresenta la classe di inizio del gioco
In questa classe imposto lo scheletro dell'applicazione;
imposto il Group, al quale attccherò tutti i componenti del gioco, la Scene con la mia classe GameScene per settare il mondo di gioco e infine dato che estendo la classe Application ho uno stage predefinito, nel quale setto la scena,la faccio apparire a schermo e istanzio l'oggetto GameUpdate, il quale inizierà il cosiddetto loop del gioco, cioè un metodo che viene chiamato ad ogni frame.
public class GameApplication extends Application {
//8 righe
@Override
public void start(Stage stage) throws Exception {
//Parent root = FXMLLoader.load(getClass().getResource("Prova.fxml"));
Group root = new Group();
GameScene gameScene = new GameScene(root,GameScene.screenWidth, GameScene.screenHeight, Color.BLACK);
//stage.setFullScreen(true);
stage.setScene(gameScene);
stage.show();
GameUpdate gameUpdate = new GameUpdate(root);
gameUpdate.startGameLoop(gameScene, root); //corpo del gioco
stage.setResizable(false);
stage.setOnCloseRequest(event -> System.exit(0)); //quando chiudo con la x, termina anche l'esecuzione, lo stato !=0 indica un
// uscita non normale
}
public static void main(String[] args){
launch(); //lavora con i thread
}
}Imposto la scena con dei parametri fissati da me nell'interfaccia ScreenSettings, richiamando il costruttore di Scene.
public class GameScene extends Scene implements ScreenSettings{
public GameScene(Parent parent, double width, double height, Paint colorBackground) {
super(parent,width, height, colorBackground);
}
}Settaggi per la grandezza della scena.
public interface ScreenSettings{
//static e final sono ridondanti per le interfacce
//cioè tutte le immagini (player, alberi, ecc...) saranno 16x16 pixel
//dobbiamo scegliere una risoluzione giusta, perchè ad esempio il nostro 16x16 pixel con la 1920x1080 risulterà piccolo
//quindi scegliamone una molto minore, ma ci servirà scalarla poichè il monitor avrà la risoluzione alta
int initialSizeTile = 16; //sarà la dimensione standard dei vari assets
int scale = 3; //16*3=48
int maxScreenWidth = 16;
int maxScreenHeight = 12;
int sizeTile = initialSizeTile*scale; //48x48, con questo ci faremo l'immagine del player e tutte le altre PERò
//dell'imageView ovvero del contenitore
// che deve essere 48x48
int screenWidth = sizeTile * maxScreenWidth; //768 pixel
int screenHeight = sizeTile * maxScreenHeight; //576 pixel
}Il metodo startGameLoop() aggiunge come "figlie" i vari componenti grafici al gruppo root e fa iniziare l'esecuzione del Thread con il metodo start(), il quale richiama run() in automatico, ovvero l'esecuzione del Thread; nel metodo run() ci sarà il loop del gioco, il codice che va aeseguito ogni frame, così da aggiornaere movimenti, vita, ecc...; questo ciclo l'ho creato usando il concetto di deltatime ovvero il tempo trascorso tra un fotogramma (frame) e l’altro durante l’esecuzione del gioco, serve a rendere il comportamento del gioco indipendente dal frame rate (messo come costante 60 FPS) e l'ho calcolato grazie al tempo di creazione del Thread, così so di quanto spostare un immagine a schermo.
I due metodi principali sono gameMethodMovementHandler() e gameMethodAttackHandler() che gestiscono rispettivamente il movimento e l'attacco del Player con dei gestori degli eventi.
kill_Enemy() e player_Died() sono rispetivamente per la morte del nemico e del player.
L'attacco del Player si basa su dove era il cursore al momento del click.
Il movimento avviene con WASD o con le frecce e in 8 versi.
public class GameUpdate implements Runnable{
private Player plr;
Thread currentThread; //per tenere conto del tempo del processo
GameScene gameScene;
final float FPS = 60; //Frame Per Second
Enemy enemy;
public GameUpdate(Group root){
currentThread = new Thread(this);
plr = new Player();
enemy = new Enemy();
}
//inizializzare le varie cose
public void startGameLoop(GameScene gameScene, Group root){
plr.setRoot(root);
root.getChildren().addAll(plr.vBox,plr.cld.ret, plr.imgView); //per far si che l'immagine stia sopra al rettangolo
this.gameScene = gameScene;
root.getChildren().addAll(enemy.vBox, enemy.cld.ret, enemy.imgView);
currentThread.start(); //chiama implicitamente run, eseguo il processo
plr.setEnemy(enemy);
}
@Override
public void run() { //processo in esecuzione
/*
Il deltaTime rappresenta il tempo trascorso tra due frame consecutivi, e serve a rendere il movimento indipendente
dal frame rate. In parole semplici: se il gioco va a 30 FPS o 60 FPS, il player si muoverà alla stessa velocità percepita.
* */
double deltatime = 0;
long currentTime; //tempo corrente del processo
long lastUpdate = System.currentTimeMillis();
//creo un Set di KeyCode, per salvare tutti gli eventi relativi all'input da tastiera
Set<KeyCode> keysPressed = new HashSet<>();
//gestisco gli eventi e aggiungo/rimuovo il codice del tasto premuto nel mio Set
//event -> è la dichiarazione di un parametro che la lambda riceve (in questo caso, è un oggetto KeyEvent).
//keysPressed.add(event.getCode()); è il corpo della lambda.
//Non c'è bisogno di dichiarare esplicitamente event da parte tua, perché è fornito automaticamente dal metodo setOnKeyPressed
//EventHandler<KeyEvent> è l'interfaccia che viene implementata per gestire l'evento,
// ma l'oggetto che viene passato alla lambda è di tipo KeyEvent (che è l'oggetto che contiene i dettagli dell'evento di tastiera
//quindi tutto questo: event -> keysPressed.add(event.getCode()) è un EventHandler<KeyEvent>
if(plr.progressBar.getProgress() > 0.1) gameScene.setOnKeyPressed((event) -> keysPressed.add(event.getCode()));
if(plr.progressBar.getProgress() > 0.1) gameScene.setOnKeyReleased(event -> keysPressed.remove(event.getCode()));
//gameLoop
while(currentThread.isAlive()){
currentTime = System.currentTimeMillis();
//(currentTime - lastUpdate) è il tempo passato tra due frame
//1000 / FPS → è la durata ideale di un frame, sempre in millisecondi (es: 1000 / 60 = 16.67ms).
/*Quindi stai dicendo:
“Quanto tempo è passato rispetto a quanto dovrebbe passare?”
È un valore relativo, utile per capire quando è il momento di aggiornare il gioco.
* */
//System.out.println(currentTime);
deltatime += (currentTime - lastUpdate) / (1000.0 / FPS);
lastUpdate = currentTime;
//System.out.println(lastUpdate);
if(deltatime >= 1){ //un secondo
//System.out.println(deltatime);
if(plr.progressBar.getProgress() > 0.1) gameMethodMovementHandler(deltatime, this.gameScene, keysPressed);
if(plr.progressBar.getProgress() > 5.551115123125783E-17) gameMethodAttackHandler(deltatime);
if(enemy != null && enemy.progressBar.getProgress() <= 0.1) { kill_Enemy(); }//perchè è 1.1368683772161603E-13
if(plr != null && plr.progressBar.getProgress() <= 0.1) player_Died();
System.out.println(plr.progressBar.getProgress());
enemy.attack(deltatime, plr);
if(!enemy.attack_flag) enemy.shot(deltatime,plr, enemy.p);
deltatime--;
}
}
}
private void kill_Enemy(){
if(this.enemy != null && this.enemy.cld.ret != null) {
Platform.runLater(() -> {
plr.root.getChildren().remove(enemy.vBox);
enemy.vBox = null;
});
Platform.runLater(() -> {
plr.root.getChildren().remove(enemy.cld.ret);
enemy.cld.ret = null;
});
Platform.runLater(() -> {
plr.root.getChildren().remove(enemy.imgView);
enemy.imgView = null;
});
System.gc(); //richiama il garbage collector
}
}
private void player_Died(){
if(this.plr != null && this.plr.cld.ret != null) {
Platform.runLater(() -> {
plr.root.getChildren().remove(plr.vBox);
plr.vBox = null;
});
Platform.runLater(() -> {
plr.root.getChildren().remove(plr.cld.ret);
plr.cld.ret = null;
});
Platform.runLater(() -> {
plr.root.getChildren().remove(plr.imgView);
plr.imgView = null;
});
System.gc(); //richiama il garbage collector
}
}
private void gameMethodMovementHandler(double deltaTime, GameScene gameScene, Set<KeyCode> keysPressed) {
if(enemy != null && plr != null && plr.cld != null && enemy.cld != null) plr.cld.collision_Detected(enemy.cld.ret, true);
//gestisco l'evento, la penultima condizione degli if è per non far andare fuori mappa, l'ultima condizione è per la collisione
if ( (keysPressed.contains(plr.forward) || keysPressed.contains(plr.forwardArrow)) && plr.y >0
) {
plr.dir_forward = true;
plr.changeImage("Images/Back_Pg.png");
if(plr.cld.fr) plr.moveUp(deltaTime);
}
else{
plr.dir_forward = false;
}
if ( (keysPressed.contains(plr.backward) || keysPressed.contains(plr.backwardArrow)) && plr.y < (ScreenSettings.screenHeight-ScreenSettings.sizeTile)
) {
plr.dir_backward = true;
plr.changeImage("Images/Front_Pg.png");
if(plr.cld.br) plr.moveDown(deltaTime);
}
else{
plr.dir_backward = false;
}
if ( (keysPressed.contains(plr.leftward) || keysPressed.contains(plr.leftwardArrow)) && plr.x > 0
) {
plr.dir_leftward = true;
plr.changeImage("Images/Left_Side_Pg.png");
if(plr.cld.dx) plr.moveLeft(deltaTime);
}
else{
plr.dir_leftward = false;
}
if ( (keysPressed.contains(plr.rightward) || keysPressed.contains(plr.rightwardArrow)) && plr.x < (ScreenSettings.screenWidth-ScreenSettings.sizeTile)
) {
plr.dir_rightward = true;
plr.changeImage("Images/Right_Side_Pg.png");
if(plr.cld.sx) plr.moveRight(deltaTime);
}
else{
plr.dir_rightward = false;
}
// Sprint
// Sprint attivato quando il tasto è tenuto premuto
if (keysPressed.contains(plr.sprint)) {
plr.sprintStatus(deltaTime); // Attiva o continua lo sprint
plr.sprint(deltaTime);
} else {
plr.walk(deltaTime);
}
}
private void gameMethodAttackHandler(double deltatime){
if(plr.progressBar.getProgress() > 5.551115123125783E-17) {
gameScene.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent mouseEvent) {
double getSceneX = mouseEvent.getSceneX();
double getSceneY = mouseEvent.getSceneY();
plr.setDestinationAttack(getSceneX, getSceneY);
plr.normal_attack(deltatime);
}
});
}
if(plr.attack_flag && plr.progressBar.getProgress() > 0.1){
plr.shot(deltatime);
}
}
}Questa classe rappresenta il Player con cui interagisce l'utente.
I metodi principali sono quelli del movimento moveUp(),moveDown(), moveLeft() e moveRight() e quelli che riguardano lo sprint del personaggio; un altro principale metodo è shot(), il quale si basa nello sparare dei proiettili, implementato con una lista di Projectile così da poterne sparare più di uno; se il proiettile colpisce il nemico, a quest'ultimo gli diminuisce la vita e gli aumenta la velocità; se colpisce il proiettile del nemico, il proiettile del player si distrugge.
public class Player extends CharacterPlayable{
//per lo sprint
protected double timeSprint;
protected double timeReCharge;
protected static final double SPRINT_TIME_DURATION = 400.0; // durata dello sprint
protected static final double RECHARGE_TIME_DURATION = 400.0; // tempo di ricarica
protected boolean isSprinting;
//attacco
private List<Projectile> projectiles;
Image attackImage;
boolean attack_flag;
double xDest, yDest;
protected Group root;
private Enemy enemy;
public Player(){
x = ScreenSettings.screenWidth/2.0;
y = ScreenSettings.screenHeight/2.0;
progressBar = new ProgressBar(1.0); //1 = 100%, 0.5 = 50%
vBox = new VBox(progressBar);
vBox.setSpacing(10);
vBox.setLayoutX(this.x);
vBox.setLayoutY(this.y - 20);
this.projectiles = new ArrayList<>();
attack_flag = false;
xDest = 0;
yDest = 0;
timeSprint = 0.0;
timeReCharge = 0.0;
isSprinting = false;
dir_forward = false;
dir_backward = false;
dir_rightward = false;
dir_leftward = false;
dir_forward_oblq_right = false;
dir_forward_oblq_left = false;
dir_backward_oblq_right = false;
dir_backward_oblq_left = false;
speed = 2.5;
strength = 2.46793;
img = new Image(getClass().getResourceAsStream("Images/Front_Pg.png"), ScreenSettings.sizeTile, // requestedWidth
ScreenSettings.sizeTile, // requestedHeight
false, false); //preserveRatio = false e disattivando il smoothing a livello di Image
//Questo dice a JavaFX: "Scala esattamente a 48x48, non interpolare, non mantenere le proporzioni"
imgView = new ImageView(img);
//imposto la grandezza dell'immagine
imgView.setFitWidth(ScreenSettings.sizeTile);
imgView.setFitHeight(ScreenSettings.sizeTile);
imgView.setLayoutX(x);
imgView.setLayoutY(y);
attackImage = new Image(getClass().getResourceAsStream("Images/ProvaAttacco.png"), ScreenSettings.sizeTile, // requestedWidth
ScreenSettings.sizeTile, // requestedHeight
false, false);
cld = new Collider(x, y,ScreenSettings.sizeTile,ScreenSettings.sizeTile);
}
//implementazione metodi movimento
@Override
protected void moveUp(double deltaTime) {
y -= speed * deltaTime ;
//playerView.setY(y);
Platform.runLater(() -> { imgView.setLayoutY(y); cld.ret.setY(y); vBox.setLayoutY(y-20);});
}
@Override
protected void moveDown(double deltaTime) {
y += speed * deltaTime ;
// playerView.setY(y);
Platform.runLater(() -> {
imgView.setLayoutY(y); cld.ret.setY(y); vBox.setLayoutY(y-20);});
}
@Override
protected void moveLeft(double deltaTime) {
x -= speed * deltaTime ;
//playerView.setX(x);
Platform.runLater(() -> {
imgView.setLayoutX(x); cld.ret.setX(x); vBox.setLayoutX(x);});
}
@Override
protected void moveRight(double deltaTime) {
x += speed * deltaTime;
//playerView.setX(x);
Platform.runLater(() -> {
imgView.setLayoutX(x); cld.ret.setX(x); vBox.setLayoutX(x);});
}
@Override
protected void sprintStatus(double deltatime){
if (!isSprinting && this.timeReCharge <= 0) {
// Se non stiamo sprintando e il tempo di ricarica è finito
isSprinting = true; //si attiva sprint
this.timeSprint = SPRINT_TIME_DURATION;
System.out.println("Sprint attivato! Durata: " + SPRINT_TIME_DURATION + " secondi.");
} else if (isSprinting) {
System.out.println("Già sprintando!");
} else if (this.timeReCharge > 0) {
System.out.println("Non puoi sprintare, il tempo di ricarica non è finito.");
}
}
protected void sprint(double deltatime){
if (this.isSprinting) {
this.speed = 4;
this.timeSprint -= deltatime; // Diminuisci il tempo dello sprint
System.out.println(this.timeSprint);
if (this.timeSprint <= 0) {
this.walk(deltatime);
this.isSprinting = false;
this.timeReCharge = Player.RECHARGE_TIME_DURATION; // Inizia la ricarica
System.out.println("Sprint finito! Inizia la ricarica.");
}
}
}
@Override
protected void walk(double deltaTime){
speed = 2.5;
// Se il tasto di sprint non è premuto, gestisci la ricarica
if (this.timeReCharge > 0) {
this.timeReCharge -= deltaTime;
System.out.println("Sto ricaricando");
if (this.timeReCharge <= 0) {
System.out.println("Ricarica completa. Puoi sprintare di nuovo!");
}
}
}
@Override
protected void normal_attack(double deltatime) {
if(this.progressBar.getProgress() > 5.551115123125783E-17) {
Projectile p = new Projectile(attackImage, imgView.getLayoutX(), imgView.getLayoutY(), xDest, yDest);
this.attack_flag = true;
projectiles.add(p);
Platform.runLater(() -> {
root.getChildren().addAll(p.cld.ret, p.imgView);
});
System.out.println("Attacco");
}
}
protected void setDestinationAttack(double xDest, double yDest){
this.xDest = xDest;
this.yDest = yDest;
}
protected void setRoot(Group root){
this.root = root;
}
protected void setEnemy(Enemy enemy){this.enemy = enemy;}
protected void shot(double deltaTime){
//teorema di pitagora
// double dx = xDest - xAttack;
// double dy = yDest - yAttack;
// double distance = Math.sqrt(dx * dx + dy * dy); //ipotenusa, vettore direzione
// //calcolo le componenti
// double directionX = dx / distance; //coseno dell'angolo tra x e l'ipotenusa
// double directionY = dy / distance; //seno dell'angolo tra y e l'ipotenusa
Iterator<Projectile> iterator = projectiles.iterator();
while(iterator.hasNext()){
Projectile p = iterator.next();
p.journey(deltaTime, p.speed);
if(p.isArrived(xDest, yDest)){
Platform.runLater(() -> {
root.getChildren().removeAll(p.cld.ret, p.imgView);
});
iterator.remove();
}else if ((enemy != null) && (enemy.cld.ret != null) ) {
if ( p.cld.ret.intersects(enemy.cld.ret.getBoundsInLocal())) { //se colpisce il nemico
if (enemy.health > 1) { //perchè non so il perchè non va negativo e si ferma a circa 1e-13
enemy.speed += 0.2;
enemy.health -= (enemy.initial_Health * p.normal_damage);
System.out.println(enemy.health);
Platform.runLater(() -> {
enemy.progressBar.setProgress(enemy.progressBar.getProgress() - p.normal_damage);
});
}
Platform.runLater(() -> {
root.getChildren().removeAll(p.cld.ret, p.imgView);
});
iterator.remove();
} else if(p.cld.ret.intersects(enemy.p.cld.ret.getBoundsInLocal())){ // se colpisce il proiettile del nemico
Platform.runLater(() -> {
root.getChildren().removeAll(p.cld.ret, p.imgView);
});
iterator.remove();
}
}
}
if(projectiles.isEmpty()) attack_flag = false; //così ne posso sparare di più
}
}Rappresenta il nemico da abbattere; il metodo movement() fa muovere il nemico in un percorso fisso; il metodo shot() rappresenta l'attacco del nemico che quando si distrugge o arriva a destinazione o colpisce il player, ne spara subito un altro; se colpisce il player, gli fa fare un piccolo rimbalzo.
public class Enemy extends Character{
Image attackImage;
boolean attack_flag;
Projectile p;
final double REBOUND = 2.56784;
private boolean goingDown;
public Enemy(){
goingDown = true;
this.x = (ScreenSettings.screenWidth/2.0) + 100.0;
this.y = (ScreenSettings.screenHeight/2.0) - 100.0;
progressBar = new ProgressBar(1.0); //1 = 100%, 0.5 = 50%
vBox = new VBox(progressBar);
vBox.setSpacing(10);
vBox.setLayoutX(this.x);
vBox.setLayoutY(this.y - 20);
dir_forward = false;
dir_backward = false;
dir_rightward = false;
dir_leftward = false;
dir_forward_oblq_right = false;
dir_forward_oblq_left = false;
dir_backward_oblq_right = false;
dir_backward_oblq_left = false;
this.health = this.initial_Health;
speed = 1.5;
strength = 4;
img = new Image(getClass().getResourceAsStream("Images/Front_Enemy_c.png"), ScreenSettings.sizeTile, // requestedWidth
ScreenSettings.sizeTile, // requestedHeight
false, false); //preserveRatio = false e disattivando il smoothing a livello di Image
//Questo dice a JavaFX: "Scala esattamente a 48x48, non interpolare, non mantenere le proporzioni"
imgView = new ImageView(img);
//imposto la grandezza dell'immagine
imgView.setFitWidth(ScreenSettings.sizeTile);
imgView.setFitHeight(ScreenSettings.sizeTile);
imgView.setLayoutX(x);
imgView.setLayoutY(y);
cld = new Collider(x, y,ScreenSettings.sizeTile,ScreenSettings.sizeTile);
attackImage = new Image(getClass().getResourceAsStream("Images/ProvaAttaccoEnemy.png"), ScreenSettings.sizeTile, // requestedWidth
ScreenSettings.sizeTile, // requestedHeight
false, false);
attack_flag = true;
}
protected void attack(double deltatime, Player plr){
if(attack_flag && plr.progressBar.getProgress() > 0.1 && this.progressBar.getProgress() > 0.1){
attack_flag = false;
Projectile p = new Projectile(attackImage, this.x, this.y, plr.x, plr.y);
Platform.runLater(() -> { plr.root.getChildren().addAll(p.cld.ret,p.imgView); });
this.p = p;
shot(deltatime, plr, this.p);
}
}
protected void shot(double deltaTime, Player plr, Projectile p){
if(this.progressBar.getProgress() > 0.1 ) movement(deltaTime);
if(this.progressBar.getProgress() > 0.1 && !attack_flag && plr.progressBar.getProgress() > 0.1) { //finchè è in vita
p.journey(deltaTime, p.speed);
plr.cld.collision_Detected(p.cld.ret, false);
if (p.isArrived(plr.x, plr.y)) {
Platform.runLater(() -> { plr.root.getChildren().removeAll(p.cld.ret, p.imgView); } );
attack_flag = true;
}
if(p.cld.ret.intersects(plr.cld.ret.getBoundsInLocal())){
if(plr.cld.dx && plr.x > 0){
Platform.runLater(() -> {
plr.imgView.setX(plr.imgView.getX() - (plr.speed * this.REBOUND));
plr.cld.ret.setLayoutX(plr.cld.ret.getLayoutX() - (plr.speed * this.REBOUND));
plr.progressBar.setTranslateX(plr.progressBar.getTranslateX() - (plr.speed * this.REBOUND * 0.4));
plr.vBox.setTranslateX(plr.vBox.getTranslateX() - (plr.speed * this.REBOUND * 0.4));
});
}
if(plr.cld.sx && plr.x < (ScreenSettings.screenWidth-ScreenSettings.sizeTile)){
Platform.runLater(() -> {
plr.imgView.setX(plr.imgView.getX() + (plr.speed * this.REBOUND));
plr.cld.ret.setLayoutX(plr.cld.ret.getLayoutX() + (plr.speed * this.REBOUND));
plr.progressBar.setTranslateX(plr.progressBar.getTranslateX() + (plr.speed * this.REBOUND * 0.4));
plr.vBox.setTranslateX(plr.vBox.getTranslateX() + (plr.speed * this.REBOUND * 0.4));
});
}
if(plr.cld.br && plr.y < (ScreenSettings.screenHeight-ScreenSettings.sizeTile)){
Platform.runLater(() -> {
plr.imgView.setY(plr.imgView.getY() + (plr.speed * this.REBOUND));
plr.cld.ret.setLayoutY(plr.cld.ret.getLayoutY() + (plr.speed * this.REBOUND));
plr.progressBar.setTranslateY(plr.progressBar.getTranslateY() + (plr.speed * this.REBOUND* 0.4));
plr.vBox.setTranslateY(plr.vBox.getTranslateY() + (plr.speed * this.REBOUND* 0.4));
});
}
if(plr.cld.fr && plr.y >0){
Platform.runLater(() -> {
plr.imgView.setY(plr.imgView.getY() - (plr.speed * this.REBOUND));
plr.cld.ret.setLayoutY(plr.cld.ret.getLayoutY() - (plr.speed * this.REBOUND));
plr.progressBar.setTranslateY(plr.progressBar.getTranslateY() - (plr.speed * this.REBOUND* 0.4));
plr.vBox.setTranslateY(plr.vBox.getTranslateY() - (plr.speed * this.REBOUND* 0.4));
});
}
Platform.runLater(() -> { plr.root.getChildren().removeAll(p.cld.ret, p.imgView); } );
Platform.runLater(() -> { plr.progressBar.setProgress(plr.progressBar.getProgress() - 0.2); });
attack_flag = true;
}
}
if(this.progressBar.getProgress() < 0.1 && !p.isArrived(plr.x, plr.y)){
Platform.runLater(() -> { plr.root.getChildren().removeAll(p.cld.ret, p.imgView); } );
}
}
public void movement(double deltaTime){
double maxDestY = ScreenSettings.screenHeight - ScreenSettings.sizeTile;
double minDestY = 0; // oppure un valore di partenza
if (goingDown) {
Platform.runLater(() -> {
this.changeImage("Images/Front_Enemy_c.png");
});
this.y += this.speed * deltaTime;
if (this.y >= maxDestY) {
this.y = maxDestY;
goingDown = false;
}
} else {
Platform.runLater(() -> {
this.changeImage("Images/Back_Enemy_c.png");
});
this.y -= this.speed * deltaTime;
if (this.y <= minDestY) {
this.y = minDestY;
goingDown = true;
}
}
double finalY = this.y;
Platform.runLater(() -> {
this.imgView.setLayoutY(finalY);
this.cld.ret.setY(finalY);
this.vBox.setLayoutY(finalY - 20);
});
}
}Rappresenta il proiettile usato dal nemico e dal player;
il metodo journey() rappresenta il movimento del proiettile e isArrived() se è arrivato o no a destinazione.
public class Projectile extends Entity{
double directionX, directionY;
double xDest, yDest;
protected static final double margine = 2.0;
protected double normal_damage = 0.2; //in percentuale
protected final double speed = 2.5;
public Projectile(Image img, double x, double y, double xDest, double yDest) {
this.imgView = new ImageView(img);
this.imgView.setFitWidth(ScreenSettings.sizeTile);
this.imgView.setFitHeight(ScreenSettings.sizeTile);
this.x = x;
this.y = y;
this.imgView.setLayoutX(this.x);
this.imgView.setLayoutY(this.y);
this.xDest = xDest;
this.yDest = yDest;
double dx = this.xDest - x;
double dy = this.yDest - y;
double distance = Math.sqrt(dx * dx + dy * dy);
directionX = dx / distance;
directionY = dy / distance;
cld = new Collider(this.x, this.y, ScreenSettings.sizeTile, ScreenSettings.sizeTile);
}
protected void journey(double deltaTime, double speed){
x += deltaTime * speed * directionX;
y += deltaTime * speed * directionY;
Platform.runLater(() -> {
imgView.setLayoutX(x);
imgView.setLayoutY(y);
cld.ret.setX(x);
cld.ret.setY(y);
});
}
public boolean isArrived(double xDest, double yDest){
double dx = this.xDest - x;
double dy = this.yDest - y;
return Math.sqrt(dx * dx + dy * dy) <= margine;
}
}Rappresenta il Collider, ovvero un oggetto invisibile messo intorno agli oggetti per gestire le collisioni; se block è true allora abilita il collider ad essere "rigido" ovvero di non andare all'interno di un altro collider, mentre se è false serve solo per capire se due oggetti si toccano.
public class Collider {
Rectangle ret ;
protected double x, y;
boolean dx, sx, fr, br;
public Collider(double x, double y, double width, double height){
dx = sx = fr = br = true;
this.x = x;
this.y = y;
ret = new Rectangle( this.x, this.y, width, height); //posizione e grandezza
ret.setArcHeight(10d);
ret.setArcWidth(10d);
//ret.setStroke(Color.RED);
}
protected void collision_Detected( Rectangle ret2, boolean block){
if( this.ret != null && ret2 != null) {
if (this.ret.intersects(ret2.getBoundsInParent())) {
//uso un gap per evitare che il pg si blocchi e quindi avvenga una sovrapposizione delle condizioni di collisione
//restituisce la coordinata X dell'angolo superiore sinistro del rettangolo rispetto al parent.
double deltaX = this.ret.getX() - ret2.getX();
double deltaY = this.ret.getY() - ret2.getY();
//esempi quando mi trovo a sinistra del nemico:
//es. 436 - 484 = -48 che è la larghezza delle immagini
// System.out.println(e1.cld.ret.getX()+" "+ret2.cld.ret.getX());
// System.out.println(e1.cld.ret.getY()+" "+ret2.cld.ret.getY());
//es. 214.049 - 188 = 26,049
//quindi dato che il val ass di deltax è > di deltay mi si attiva la collisione da sinistra
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX < 0) {
// Collisione da sinistra: correggi posizione
this.sx = false;
//mi imposta la posizione x del colider del player alla distanza del collider del nemico - la larghezza che è 48
if(block)
Platform.runLater(() -> {
this.ret.setX(ret2.getX() - this.ret.getWidth());
});
System.out.println("Collisione da sinistra!");
} else {
// Collisione da destra: correggi posizione
this.dx = false;
if(block) Platform.runLater(() -> {
this.ret.setX(ret2.getX() + ret2.getWidth());
});
System.out.println("Collisione da destra!");
}
} else {
if (deltaY < 0) {
// Collisione dall'alto: correggi posizione
this.br = false;
if(block) Platform.runLater(() -> {
this.ret.setY(ret2.getY() - this.ret.getHeight());
});
System.out.println("Collisione dall'alto!");
} else {
// Collisione dal basso: correggi posizione
this.fr = false;
if(block) Platform.runLater(() -> {
this.ret.setY(ret2.getY() + ret2.getHeight());
});
System.out.println("Collisione dal basso!");
}
}
} else this.dx = this.sx =this.fr = this.br = true;
}
}
}Rappresenta un personaggio generico.
public abstract class Character extends Entity{
//caratteristiche
protected ProgressBar progressBar;
protected VBox vBox;
protected final double initial_Health = 2450.431;
protected double health;
protected double strength;
protected double speed;
//direzioni: 8 totali
protected boolean dir_forward;
protected boolean dir_backward;
protected boolean dir_rightward;
protected boolean dir_leftward;
protected boolean dir_forward_oblq_right;
protected boolean dir_forward_oblq_left;
protected boolean dir_backward_oblq_right;
protected boolean dir_backward_oblq_left;
protected final void changeImage(String pathImage){
Platform.runLater(() -> { imgView.setImage(new Image(getClass().getResourceAsStream(pathImage), ScreenSettings.sizeTile, // requestedWidth
ScreenSettings.sizeTile, // requestedHeight
false, false));});
}
}Rappresenta un personaggio giocabile.
public abstract class CharacterPlayable extends Character{
//comandi
protected final KeyCode forward = KeyCode.W;
protected final KeyCode backward = KeyCode.S;
protected final KeyCode leftward = KeyCode.A;
protected final KeyCode rightward = KeyCode.D;
protected final KeyCode forwardArrow = KeyCode.UP;
protected final KeyCode backwardArrow = KeyCode.DOWN;
protected final KeyCode leftwardArrow = KeyCode.LEFT;
protected final KeyCode rightwardArrow = KeyCode.RIGHT;
protected final KeyCode sprint = KeyCode.SHIFT;
//movimento
protected abstract void moveUp(double deltaTime);
protected abstract void moveDown(double deltaTime);
protected abstract void moveLeft(double deltaTime);
protected abstract void moveRight(double deltaTime);
protected abstract void sprintStatus(double deltatime);
protected abstract void walk(double deltaTime);
protected abstract void normal_attack(double deltatime);
}Rappresenta la classe più generica di ogni oggetto all'interno del gioco.
public abstract class Entity {
//immagine
protected Image img;
protected ImageView imgView;
//posizione
protected double x;
protected double y;
//Collider
protected Collider cld;
}