I am creating a Solitaire game using JavaFX. I want a Restart button to start a new game, but also a Reset button that will restart the same game i.e. the same scene to its original state.
Restart works seamlessly as I was able to call the start method but I am unable to get Reset working.
package guisolitaire;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class GUISolitaire extends Application {
Game game;
Pane root;
ScoreTimer timer = new ScoreTimer();
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas(1000, 800);
root = new Pane(canvas);
root.setStyle("-fx-background-color: green");
game = new Game(canvas.getGraphicsContext2D());
canvas.setOnMouseClicked(game::handleMouseClicked);
timer.restart();
timer.start();
Scene scene = new Scene(root, Color.GREEN);
primaryStage.setScene(scene);
primaryStage.setTitle("Solitaire");
primaryStage.show();
Label l = new Label(); // Timer label
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO, ae -> l.setText("Time elapsed: " + timer.s)),
new KeyFrame(Duration.seconds(1))
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
l.setTranslateX(800);
l.setTranslateY(765);
root.getChildren().add(l);
Button newGameButton = new Button();
newGameButton.setText("New Game");
newGameButton.setTranslateX(10);
newGameButton.setTranslateY(765);
root.getChildren().add(newGameButton);
EventHandler<ActionEvent> newGameEvent = ae -> {
timer.stop();
timer.restart();
start(primaryStage);
};
newGameButton.setOnAction(newGameEvent);
Button restart = new Button();
restart.setText("Restart");
restart.setTranslateX(100);
restart.setTranslateY(765);
root.getChildren().add(restart);
EventHandler<ActionEvent> resetGameEvent = ae -> {
primaryStage.setScene(scene);
primaryStage.show();
timer.stop();
timer.restart();
};
restart.setOnAction(resetGameEvent);
}
public static void main(String[] args) {
launch(args);
}
}
I have tried to reset primaryStage with the same scene and call the show method but that does not restart the game, it does not do anything.
Any ideas on how this may be able to be implemented?
Related
New to java and JavaFX so please bear with me
I need to do a presentation of 5 3d fruit models that show in a continuous loop, 15 seconds apart: fruit1 for 15 seconds then fruit2 for 15 seconds ad so on and so forth.. until fruit5 for 15 seconds and then back to fruit1 and continues until I hit the ESC key which should close the window.
I also understand that it's ideal to change the root group object that makes up the scene instead of changing the scene, so I changed that in my code
I understand that a timeline is needed in order to change something in the scene as it plays out, but I've tried with something similar to what this answer says but I don't get the logic of how to switch the root of the scene every 15seconds
UPDATE:
I gave up on the timeline option and I found the platform.run option as seen on this article which seems to work as I see the window updates iterating from the first fruit in the scenes array to second one but I'm not sure why it only runs once when I need it to run every 15 seconds which means that my scene switcher: nextSceneIndex() should go back and forth between 1 and 0.
UPDATE2:
I went back to the timeline suggestion and I implemented Sedrick's solution and it worked... I can't be happier :)
Here's my working code!
public void start(Stage stage) throws Exception {
BorderPane[] scenes = new BorderPane[]{createSceneApple(),createSceneCarrot(),createSceneTomato()};
Timeline tl = new Timeline();
tl.setCycleCount(Animation.INDEFINITE);
KeyFrame kf_fruit = new KeyFrame(Duration.seconds(10),
new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
if (index==0){
root.getChildren().setAll(scenes[0]);
index = 1;
}else if(index==1){
root.getChildren().setAll(scenes[1]);
index = 2;
}else if(index==2){
root.getChildren().setAll(scenes[2]);
index = 0;
}
}
});
tl.getKeyFrames().add(kf_fruit);
tl.play();
Scene scene = new Scene(root, windowWidth, windowHeight);
stage.setScene(scene);
stage.show();
}
Maybe you can get some ideas from here. This uses the code from the link I posted above. Timeline is used to loop through a list of Shape and info about that shape.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* JavaFX App
*/
public class App extends Application {
#Override
public void start(Stage stage) {
List<MyShape> shapes = new ArrayList();
shapes.add(new MyShape("Circle", "Shape.Circle", "More Circle Info", new Circle(25, Color.BLUE)));
shapes.add(new MyShape("Rectangle", "Shape.Rectangle", "More Rectangle Info", new Rectangle(100, 50, Color.RED)));
shapes.add(new MyShape("Line", "Shape.Line", "More Line Info", new Line(0, 0, 100, 100)));
TextField tf1 = new TextField();
TextField tf2 = new TextField();
TextArea ta1 = new TextArea();
VBox leftWindow = new VBox(tf1, tf2, ta1);
StackPane rightWindow = new StackPane(shapes.get(1).getShape());
AtomicInteger counter = new AtomicInteger();
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println(counter.get() % shapes.size());
MyShape currentShape = shapes.get(counter.getAndIncrement() % shapes.size());
tf1.setText(currentShape.getName());
tf2.setText(currentShape.getType());
ta1.setText(currentShape.getMoreInfo());
rightWindow.getChildren().set(0, currentShape.getShape());
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
BorderPane root = new BorderPane();
root.setLeft(new StackPane(leftWindow));
root.setRight(rightWindow);
var scene = new Scene(root, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Update:
If you only have two scenes, that simplifies some things. You basically need to set the initial view. You then need to switch out the view currently showing every two seconds. (I used two seconds so that you can see the views before they are switched out). I created my own version of createSceneCarrot and createSceneApple since I don't know your implementation.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
* JavaFX App
*/
public class App extends Application {
#Override
public void start(Stage stage) {
BorderPane[] scenes = new BorderPane[]{createSceneApple(),createSceneCarrot()};
StackPane root = new StackPane(scenes[0]);//Set initial view;
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(2), (ActionEvent event) -> {
if(root.getChildren().get(0).equals(scenes[0]))//If the first scene is loaded, load the second scene.
{
root.getChildren().set(0, scenes[1]);
}
else
{
root.getChildren().set(0, scenes[0]);
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
var scene = new Scene(root, 640, 640);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
public BorderPane createSceneApple()
{
BorderPane borderPane = new BorderPane();
TextField tf1 = new TextField("Rectangle 1");
TextField tf2 = new TextField("Rectangle Color: Blue");
TextArea ta1 = new TextArea("20x40");
VBox leftWindow = new VBox(tf1, tf2, ta1);
borderPane.setLeft(leftWindow);
StackPane rightWindow = new StackPane(new Rectangle(20, 40, Color.BLUE));
borderPane.setRight(rightWindow);
return borderPane;
}
public BorderPane createSceneCarrot()
{
BorderPane borderPane = new BorderPane();
TextField tf1 = new TextField("Circle 1");
TextField tf2 = new TextField("Circle Color: Blue");
TextArea ta1 = new TextArea("Radius: 50");
VBox leftWindow = new VBox(tf1, tf2, ta1);
borderPane.setLeft(leftWindow);
StackPane rightWindow = new StackPane(new Circle(50, Color.RED));
borderPane.setRight(rightWindow);
return borderPane;
}
}
I was testing the MonologFX from JFXtras (v8.0-r5), but I got stuck with it!
Can anyone tell me how to check what was the button in the dialog that was pressed by the user? I tried in many ways, but no luck at all.
package javafx_jfxtras_monologfx;
import javafx.application.Application;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
import jfxtras.labs.dialogs.MonologFX;
import jfxtras.labs.dialogs.MonologFXButton;
import jfxtras.labs.dialogs.MonologFX.Type;
public class JavaFX_JFXtras_MonologFX extends Application
{
#Override
public void start(Stage stage)
{
MonologFX m = new MonologFX();
m.setModal(true);
m.setType(Type.QUESTION);
m.setTitleText("JFXtras MonologFX");
m.setMessage("Do you want to continue?");
m.setPos(698, 450);
MonologFXButton mb1 = new MonologFXButton();
mb1.setType(MonologFXButton.Type.YES);
mb1.setLabel("Continue");
m.addButton(mb1);
MonologFXButton mb2 = new MonologFXButton();
mb2.setType(MonologFXButton.Type.NO);
mb2.setLabel("Exit");
m.addButton(mb2);
Button btn = new Button();
btn.setText("Click the Button");
btn.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
System.out.println("Hello :)");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
stage.setTitle("JavaFX - JFXtras MonologFX");
stage.setScene(scene);
stage.show();
m.show();
}
public static void main(String[] args)
{
launch(args);
}
}
But the controls in labs are experimental and Mark has not worked on this one for a long time. We don't take them out because someone may use them, but as of version 8u40 JavaFX has a dialog itself. https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Dialog.html|
I'm writing a small game. And i want a pause menu over the blured game menu to be shown when esc is pressed in the middle of the game.
What I do is that i make a new scene which has a stackPane wrapping the past root and the pause menu root and then i set the opacity of the past root 0.4 .
Then when the resume button in the pause menu is clicked i change the opacity back to 1 and set the past scene on stage but then its freezed. does anyone know why? can anyone help me achieve this?
Here is the part i make the new scene and then i put this on stage:
StackPane wrapper = new StackPane();
previousScene = main.getPrimaryStage().getScene();
previousScene.getRoot().setOpacity(.4);
vBox.setId("pausedWrapper");
wrapper.getChildren().add(previousScene.getRoot());
wrapper.getChildren().add(vBox);
scene = new Scene(wrapper, 1200, 700);
return scene;
Here is the part i change it back to where it was:
resumeGame.setOnAction(event -> {
System.out.println("game resumed!");
previousScene.getRoot().setOpacity(1);
main.getPrimaryStage().setScene(previousScene);
});
But then it does not work and the opacity does not change back to normal and the strange thing is when i check the sound on box the music is played but the box does not get checked like everything works but the view is freezed.
A node cannot be part of two different scene graphs. This happens in your code to the root of previousScene, because it is part of both previousScene and the new scene you create in your first block of code. Most likely what is happening is that it is removed from the first scene when you add it to the second (though it is hard to tell from the code you posted).
Consider instead using a Popup to display the pauseMenu on top of the existing window, or just use a modal Stage with undecorated StageStyle, as in the following SSCCE:
import javafx.animation.Animation;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Modality;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class PauseExample extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(50, 50, 50, 50);
rect.setFill(Color.CORAL);
TranslateTransition animation = createAnimation(rect);
Button pauseButton = new Button("Pause");
Pane pane = new Pane(rect);
pane.setMinSize(600, 150);
BorderPane root = new BorderPane(pane, null, null, pauseButton, new Label("This is\nthe main\nscene"));
pauseButton.setOnAction(e -> {
animation.pause();
root.setEffect(new GaussianBlur());
VBox pauseRoot = new VBox(5);
pauseRoot.getChildren().add(new Label("Paused"));
pauseRoot.setStyle("-fx-background-color: rgba(255, 255, 255, 0.8);");
pauseRoot.setAlignment(Pos.CENTER);
pauseRoot.setPadding(new Insets(20));
Button resume = new Button("Resume");
pauseRoot.getChildren().add(resume);
Stage popupStage = new Stage(StageStyle.TRANSPARENT);
popupStage.initOwner(primaryStage);
popupStage.initModality(Modality.APPLICATION_MODAL);
popupStage.setScene(new Scene(pauseRoot, Color.TRANSPARENT));
resume.setOnAction(event -> {
root.setEffect(null);
animation.play();
popupStage.hide();
});
popupStage.show();
});
BorderPane.setAlignment(pauseButton, Pos.CENTER);
BorderPane.setMargin(pauseButton, new Insets(5));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private TranslateTransition createAnimation(Rectangle rect) {
TranslateTransition animation = new TranslateTransition(Duration.seconds(1), rect);
animation.setByX(400);
animation.setCycleCount(Animation.INDEFINITE);
animation.setAutoReverse(true);
animation.play();
return animation;
}
public static void main(String[] args) {
launch(args);
}
}
I am trying to make an application where an object moves across the screen, only changing its x value. I am not getting any errors but it is not working when I run it. the objects are showing but the rectangle is not animating. I am not sure what to do
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private BorderPane root;
private Stage stage;
private Scene scene;
private AnchorPane anchorPane;
private Rectangle player;
private Circle circles;
private Button up, down;
private Timeline timer;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
root = new BorderPane();
scene = new Scene(root, 600, 500); //width and height of application
stage.setScene(scene);
stage.setTitle("Hoop runner"); //text for the title bar of the window
scene.getStylesheets().add("hoop.css");
anchorPane = new AnchorPane();
anchorPane.setMaxHeight(500);
anchorPane.setMinHeight(500);
anchorPane.setMinWidth(600);
anchorPane.setMaxWidth(600);
player = new Rectangle();
circles = new Circle();
up = new Button("˄");
down = new Button("˅");
root.setCenter(new VBox(new VBox(circles),
new HBox(player)));
root.setCenter(anchorPane);
player.setX(5);
player.setY(380);
player.setHeight(80);
player.setWidth(120);
anchorPane.getChildren().add(player);
timer = new Timeline(Animation.INDEFINITE);
timer.setCycleCount(Animation.INDEFINITE);
timer.getKeyFrames().add(new KeyFrame(Duration.INDEFINITE, event -> maverick()));
timer.play();
stage.show();
}
private void maverick() {
timer.play();
System.out.println("hi");
player.setX(player.getX() + 2);
timer.play();
}
public static void main(String[] args) {
launch(args);
}
}
Use setTranslateX instead of setX if you want to position or animate Nodes.
Change timer = new Timeline(Animation.INDEFINITE) to timer = new Timeline().
Change new KeyFrame(Duration.INDEFINITE, event -> maverick()) to new KeyFrame(Duration.millis(500), event -> maverick()) (for example).
A KeyFrame has to have a point in time. If, like the example, you use 500ms, then the KeyFrame's EventHandler will get called after 500ms. Since your TimeLine has a cycleCount of INDEFINITE, it will loop and execute the KeyFrame every 500ms.
Is there any way to translate somethings like pop up windows(that are not Node) in java FX ?
for example fade transition , translate transition or any timeline transition .... .
Thank's
Create a property and use a Timeline to "animate" the property. Register a listener with the property and update the window when its value changes.
For example:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TranslateWindowExample extends Application {
#Override
public void start(Stage primaryStage) {
Button moveButton = new Button("Move");
moveButton.setOnAction(event -> {
double currentX = primaryStage.getX() ;
DoubleProperty x = new SimpleDoubleProperty(currentX);
x.addListener((obs, oldX, newX) -> primaryStage.setX(newX.doubleValue()));
KeyFrame keyFrame = new KeyFrame(Duration.seconds(1), new KeyValue(x, currentX + 100));
Timeline animation = new Timeline(keyFrame);
animation.play();
});
StackPane root = new StackPane(moveButton);
Scene scene = new Scene(root, 250, 150);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Can you give an example of what do you call popup window ?
For instance, you can create a new stage on the top of your main stage.
You can also use some Tooltip class to add some text over the top of your window.
If you want more classical windows, you should have a look to Alert class.
Anthony