Comment puis-je implémenter une autre classe dans mon interface graphique JavaFX?
Je travaille sur un petit jeu d'aventure en tant que projet final pour ma classe AP Java
depuis un moment maintenant, et j'essaie d'implémenter les classes que j'ai créées pour l'interface graphique JavaFX
que j'avais construite avec Scene Builder
.
Mais pour une raison quelconque, l'interface graphique refuse de reconnaître ma méthode startAdventure()
, dans la classe Adventure, qui commence fondamentalement le tout. L'interface graphique est lancée, mais elle n'affiche pas correctement ce qu'on lui dit d'afficher dans startAdventure()
.
startAdventure()
semble ne pas être appelé à tout cela jusqu'à la fermeture de l'interface graphique, ce qui entraîne une exception de pointeur null car startAdventure()
est censé manipuler les zones de texte et autres dans l'interface graphique.
Je m'excuse à l'avance si je ne fournis pas assez d'informations, si je ne comprends pas les suggestions, etc. Je suis un peu défié en matière de codage, et je suis complètement nouveau pour poster des choses sur ce site.
Voici à quoi ressemble le début de startAdventure()
dans ma classe Adventure (text et inputText sont des variables correctement initialisées de mon AdventureGUI.fxml
fichier, qui a été fait en utilisant Scene Builder
):
public void startAdventure()
{
//welcome user
text.setText("Welcome to the land of euphoria!" + "\n");
//gets users name
text.appendText("What's your name?" + "\n");
p.playerName = inputText.getText();
text.appendText("Welcome, " + p.playerName + "\n");
//sets start location
p.setNewLoc("START");
Et voici à quoi ressemble ma classe principale:
public class RunAdventure extends Application
{
@Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("AdventureGUI.fxml"));
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.setTitle("Adventure");
primaryStage.show();
}
public static void main(String[] args)
{
Adventure game = new Adventure();
launch(args);
game.startAdventure();
}
} //end class
Si cela fonctionnait correctement, l'interface graphique se lancerait et à l'intérieur de la zone de texte, elle afficherait du texte commençant par " Bienvenue au pays de l'euphorie!".
Cependant, dans son état actuel, l'interface graphique est lancée et n'a aucun texte dans la zone de texte.
Encore une fois, nouveau à tout ici, alors faites-moi savoir tout de suite si vous pensez qu'il y a quelque chose d'autre que vous avez besoin de moi pour aider le processus de suggestion.
Edit: Quand j'ai essayé de changer l'ordre du jeu where.startAdventure () était, en le mettant juste avant primaryStage.show() dans la méthode start, une longue liste d'exceptions s'est produite. Voici le changement que j'ai fait:
@Override
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("AdventureGUI.fxml"));
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.setTitle("Adventure");
Adventure game = new Adventure();
game.startAdventure();
primaryStage.show();
}
Et voici les exceptions qui se sont produites:
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:894)
at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:56)
at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:158)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at Classwork.FinalExamProject.Adventure.startAdventure(Adventure.java:102)
at Classwork.FinalExamProject.RunAdventure.start(RunAdventure.java:24)
at com.sun.javafx.application.LauncherImpl$8.run(LauncherImpl.java:837)
at com.sun.javafx.application.PlatformImpl$7.run(PlatformImpl.java:335)
at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:301)
at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:298)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:298)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Je dois également mentionner que la classe Adventure sert également de contrôleur
EDIT (5/20): D'accord, j'ai j'ai presque tout ce qui fonctionne dans mon programme, mais je rencontre maintenant un nouveau problème. Dans mon aventure, j'ai une fonction qui permet au joueur de combattre divers monstres s'ils sont dans un endroit donné. Mon problème est qu'une fois que j'arrive à cet endroit dans mon programme, le programme cesse de répondre. Après avoir fait du débogage, j'ai compris que c'était une boucle while que j'avais qui faisait tout dans la méthode doBattle() pendant que le joueur.isAlive et ennemi.isAlive. Le seul problème est, une fois que je supprime le while en boucle, le programme saute juste après que le monstre soit mort ou que le joueur soit mort, alors qu'il est censé vous donner le choix d'attaquer le monstre ou non à plusieurs reprises jusqu'à ce que les entités soient mortes. Je sais que je bourre beaucoup trop de choses dans cette question, mais je ne sais pas où la mettre, et jusqu'à présent, j'ai reçu un soutien écrasant de tout le monde ici. Je ne peux pas assez vous remercier, mais je ne sais pas comment résoudre ce problème par moi-même. Voici le code de la Méthode doBattle ():
public void doBattle(Monster enemy)
{
//boolean fled = false;
//Greets player into battle by telling what monster they are fighting
text.appendText("\n" + "A wild " + enemy.getName() + " has appeared!" + "\n" );
mobImagePane.setImage(enemy.getImage());
//System.out.print("THIS RAN"); //debug
while ( p.getHealth() > 0 && enemy.getHealth() > 0 ) //while enemy and player are alive
{
//Prompts user to attack or run
text.appendText("Attack " + enemy.getName() + "? (Y/N) " + "\n");
inputText.setOnAction(event ->
{
String fightChoice = inputText.getText();
fightChoice = fightChoice.toUpperCase();
//String fightChoice = this.choice;
if (fightChoice.equals("Y"))//if they want to fight
{
//Player turn
enemy.setHealth(enemy.getHealth() - p.getDamage()); //Sets the monsters health as their current health minus the players damage
text.appendText("You attack " + enemy.getName() + " for " + p.getDamage() + " damage!" + "\n" + "Enemy health is " + enemy.getHealth() + "\n");
//Monster turn
p.setHealth(p.getHealth() - enemy.getDamage()); //Sets the players health as their current health minus the monsters damage
text.appendText("The " + enemy.getName() + " hit you for " + enemy.getDamage() + " damage!" + "\n" + "Your health is " + p.getHealth() + "\n"); //prints how much damage the monster does to the player
if (p.health < 20.0) {
text.appendText("Your health is low, you should return home and restore health!" + "\n");
}
//checks if the player or monster is dead
this.checkLife();
enemy.checkLife();
} else {
if (fightChoice.equals("N")) // if they don't want to fight
{
mobImagePane.setImage(null);
text.appendText("You fled from the fight!" + "\n");
this.setNewLoc("TOWN"); // brings you back to town
fled = true;
//JOptionPane.showMessageDialog(null, "You are now in " + this.currentLoc.getName() + "\n" + this.currentLoc.getDescription());
//String move2 = JOptionPane.showInputDialog(null,"Which way do you want to go? (N, E, S, W)");
//makeMove(move2);
//break;
} else // they don't make any sense
{
text.appendText("Unrecognized command" + "\n");
}
}
//}
//when someone dies
if (!fled) {
if (p.alive) // if you are still standing
{
//print results (money earned, health remaining)
mobImagePane.setImage(null);
p.wallet += enemy.getLoot();
playerInfo.setText(p.getPlayerName() + "\n" + "Health: " + p.getHealth() + "\n" + "Wallet: " + p.getWallet() + "\n");
text.setText("You shrekt the " + enemy.getName() + "\n" + "You got $" + enemy.getLoot() + " for winning!" + "\n" + "You now have $" + p.wallet + "\nYour health is " + p.getHealth() + "\n");
} else //if you died
{
mobImagePane.setImage(null);
text.setText("You have been shrekt by the " + enemy.getName() + "\n" + "GAME OVER" + "\n");
text.appendText("\nPlay again? (Y/N)" + "\n");
inputText.setOnAction(event2 -> {
String answer = inputText.getText();
answer.toUpperCase();
//String answer = this.choice;
if (answer.equals("Y")) //if they want to play again
{
text.appendText("Alright! Let's go!" + "\n");
this.reset();
} else //if they want to quit
{
text.appendText("Wow. What a skrub, okay bye." + "\n");
System.out.close();
}
});
}
}
});
}
} //end doBattle
1 answers
(Certaines) les choses qui vont mal et comment les réparer
Comme codé, votre méthode startAdventure()
n'est appelée qu'après l'arrêt du système JavaFX, ce qui n'est évidemment pas ce que vous voulez.
Le lancer méthode javadoc indique:
La méthode de lancement ne revient pas tant que l'application n'est pas sortie, soit via un appel à
Platform.exit
ou que toutes les fenêtres de l'application ont été fermées.
Vous invoquez startAdventure()
au mauvais moment, vous devez l'invoquer dans la méthode start()
de votre application plutôt qu'après l'appel de la méthode launch()
.
game.startAdventure();
primaryStage.show();
De plus, le lanceur Java crée une instance de votre application, vous n'avez donc pas besoin d'en créer une explicitement. Lisez le Cycle de vie de l'application JavaFX pour comprendre ce processus. Donc, votre méthode principale devrait être:
public static void main(String[] args) {
launch(args);
}
une Certaine spéculation de ma part
De plus, votre création d'un nouveau Adventure()
est un peu suspect, bien qu'il soit difficile de le dire avec certitude sans autre code fourni. Adventure peut être un contrôleur, donc vous n'avez peut-être pas besoin de créer un nouvel objet Adventure, mais vous pouvez plutôt obtenir le contrôleur du chargeur comme indiqué dans:
Et, s'il s'agit d'un contrôleur, alors le code pour démarrer l'aventure pourrait se produire dans la méthode initialize()
du contrôleur, qui sera automatiquement invoquée par le FXMLLoader.
Au fur et à mesure que l'application se développe, il peut être avantageux de créer une approche de style MVC, où votre classe Adventure n'est que des données de modèle et une logique qui sont transmises à un AdventureView qui est un contrôleur qui relie le modèle et la vue FXML. Bien que la discussion des techniques détaillées sur la façon d'accomplir cela soit en dehors de la portée de cette réponse, vous pourriez obtenir quelques idées de la réponse des paramètres de passage liée plus tôt.
Réponses à questions complémentaires
Text et inputText sont des variables correctement initialisées de mon AdventureGUI.fichier fxml, qui a été fait en utilisant Scene Builder
Les variables de votre instance de la classe Adventure ne seront pas initialisées par le FXMLLoader si vous créez votre classe adventure en utilisant new
, à la place, le FXMLLoader créera sa propre instance d'Adventure et l'initialisera, et cela sera séparé de l'instance que vous avez créée. Comme le texte le champ n'est pas initialisé pour votre instance, vous vous retrouvez avec une NullPointerException .
De plus, text et inputText ont été déclarés dans la classe Adventure comme ceci: '@FXML private TextArea text; @FXML private TextArea inputText; '
Oui, ils doivent être créés par le FXMLLoader, ce qui ne se produira pas si vous écrivez simplement new Adventure()
.
Donc, pour résoudre tout cela, je dois créer une méthode initialize() dans ma classe controller? Comment pourrais-je aller sur faire cela?
Option A (laissez le FXMLLoader initialiser implicitement votre aventure)
Renommer startAdventure()
à initialize()
.
public void start(Stage primaryStage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("AdventureGUI.fxml"));
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.setTitle("Adventure");
primaryStage.show();
}
Option B (initialiser explicitement votre aventure)
public void start(Stage primaryStage) throws Exception
{
FXMLLoader loader = new FXMLLoader(
getClass().getResource("AdventureGUI.fxml")
);
Parent root = loader.load();
Adventure adventure = loader.<Adventure>getController();
adventure.startAdventure();
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.setTitle("Adventure");
primaryStage.show();
}
Exemple Demande
aventure / AdventureApp.java
package adventure;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class AdventureApp extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
"AdventureGUI.fxml"
)
);
Parent root = loader.load();
stage.setScene(new Scene(root));
stage.setTitle("Adventure");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
aventure/Aventure.java
package adventure;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
public class Adventure {
@FXML
private TextArea text;
@FXML
private TextField inputText;
private Player player = new Player();
public void initialize() {
text.setText("Welcome to the land of euphoria!\n");
text.appendText("What's your name?\n");
Platform.runLater(() -> inputText.requestFocus());
inputText.setOnAction(event -> {
player.setName(inputText.getText());
text.appendText("Welcome, " + player.getName() + "\n");
});
player.setLocation("START");
}
}
aventure/Joueur.java
package adventure;
public class Player {
private String name;
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
aventure/Aventure.java
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="200.0" spacing="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="adventure.Adventure">
<children>
<TextArea fx:id="text" editable="false" prefHeight="200.0" prefWidth="200.0" wrapText="true" VBox.vgrow="ALWAYS" />
<TextField fx:id="inputText" minHeight="-Infinity" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>