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
Author: Andrew Brook, 2016-05-16

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

exemple d'application

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>
 2
Author: jewelsea, 2017-05-23 12:16:04