How to avoid a 'machine gun' effect to the bullets in my game? - javafx

Some days ago i asked this:
How to have multiple instances on the screen of the same sprite at the same time with javafx2
and partially solved the question elaborating the suggestion of jewelsea.
I have this obstacle now: when a key is pressed to 'fire' bullets, weapon shoot bullets as fast as a machine gun..
I would like to limit the amount of bullets that the weapon of the hero of my game can shoot..for example to decide to shoot a bullet every 0.5 secs or just when a key is pressed and not to have always a machine gun effect...
In my game the part of program that controls the 'fire' effect is like this:
scene.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event2) {
if (event2.getCode()==KeyCode.F); { .........
Before i've tried also using setOnKeyPressed and setOnKeyReleased with the same results..
So what could i try to shoot just a bullet also keeping press the 'F' key or to limit the bullets in number?
Thank you in advance and good bye!

I've done this by using a Timeline as a timer and starting it and stopping it on key pressed and key released:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class KeyEventTest extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Scene scene = new Scene(root, 400, 400);
Duration firingInterval = Duration.millis(500);
Timeline firing = new Timeline(
new KeyFrame(Duration.ZERO, event -> fire()),
new KeyFrame(firingInterval));
firing.setCycleCount(Animation.INDEFINITE);
scene.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.F && firing.getStatus() != Animation.Status.RUNNING) {
firing.playFromStart();
}
});
scene.setOnKeyReleased(event -> {
if (event.getCode() == KeyCode.F) {
firing.stop();
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void fire() {
// dummy implementation:
System.out.println("Fire!");
}
public static void main(String[] args) {
launch(args);
}
}
It's fairly easy to adapt this to additionally limit the number of bullets on the screen at any time, etc.

Related

JavaFX : How to close a sub window without getting focus on main window

I am trying to close a sub window programatically after certain time. This sub window initOwner is set with main stage. But on closing this sub window, the main window is getting focused. Is there any way to close the sub window(programatically) without gaining focus on main window?
Below is the quick demo of my issue. I tried all the possible ways to close the window. Steps to reproduce:
After starting the application, click the button to open the sub
window. This sub window will close automatically after 10seconds.
Meanwhile open any other application (notepad, outlook, browser.. or
whatever). While you are working on that application, when the sub
window is closed, the main stage gets focus and comes in front of my
current application. This is quite annoying to my client.
Note: I cannot remove initOwner(), as I always want to keep my sub window on top of the main window.
Update : Based on the comments, I tried running the demo with different jdk versions (u91, u121 & u211) and in Windows 10. In all three cases , the moment the sub window is closed, the main stage is coming to front. I even tried in a differnt system but the results are same :(
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.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
public class OwnerStage_Demo extends Application {
#Override
public void start(Stage stage) throws Exception {
Button button = new Button("Open Window");
button.setOnAction(e -> {
Stage stg = new Stage();
stg.setScene(new Scene(new StackPane(), 300, 300));
stg.initOwner(stage);
stg.show();
// Window will close automatically after 10secs.
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10000), x -> {
//stg.close();
//stg.hide();
stg.fireEvent(new WindowEvent(stg, WindowEvent.WINDOW_CLOSE_REQUEST));
}));
timeline.setCycleCount(1);
timeline.play();
});
VBox root = new VBox(button);
root.setSpacing(10);
Scene sc = new Scene(root, 600, 600);
stage.setScene(sc);
stage.show();
}
public static void main(String... a) {
Application.launch(a);
}
}
Update : Attached the gif demonstrating the issue.
How a window gains the focus depends on a platform (OS + JRE).
The platform processes focused window that is why the window may have different behavior on different OS after calling focus request.
There is no way to achieve required behaviour with pure JFX because of the restriction you had set:
Note: I cannot remove initOwner(), as I always want to keep my sub window on top of the main window.
com.sun.javafx.tk.quantum.WindowStage
if (!isPopupStage && owner != null && owner instanceof WindowStage) {
WindowStage ownerStage = (WindowStage)owner;
ownerStage.requestToFront();
}
What you can do is to imitate owner window <- child window relationship without initializing real owner.
Source:
public class PlainZStage extends Stage {
public PlainZStage(final Window owner) {
init(owner, this::focusedChanged);
}
private void init(final Window owner, final ChangeListener<Boolean> listener) {
showingProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(final ObservableValue<? extends Boolean> observable, final Boolean oldValue,
final Boolean newValue) {
owner.getScene().getRoot().setDisable(newValue);
if (newValue) {
owner.focusedProperty().addListener(listener);
} else {
owner.focusedProperty().removeListener(listener);
showingProperty().removeListener(this);
}
}
});
}
private void focusedChanged(final ObservableValue<? extends Boolean> source, final Boolean oldValue,
final Boolean newValue) {
if (newValue && isShowing()) {
toFront();
}
}
}
Usage:
button.setOnAction(e -> {
final Stage stg = new PlainZStage(stage);
stg.setScene(new Scene(new StackPane(), 300, 300));
stg.show();
// Window will close automatically after 10secs.
final Timeline timeline = new Timeline(new KeyFrame(Duration.millis(10000), x -> {
stg.close();
}));
Alternatively, you could combine JFX&SWING to filter focus events but you will face pure architectural evil :)

How can I remove my javafx program from the taskbar

I need remove my javafx app from the taskbar. I tried StageStyle.UTILITY. This is works but I need both UNDECORATED and UTILITY stage styles or another solvings.
Thank you for your replies.
Sorry you've been waiting so long for some sort of an answer on this, the following is mainly for people who come to this in the future hoping to discover a way of achieving this.
Let me start of by saying I wouldn't consider the following a solution but more of a workaround.
Assigning more than one initStyle to a stage is not possible however hiding the application from the task-bar and assigning an initStyle other than utility to the stage that is shown is.
To achieve this one must create two stages, the stage they want the user to see, and an another stage that will be considered the parent of the main stage and will be of initStyle.UTILITY this will prevent the icon from showing in the task-bar.
Below you can see the hello world example from oracles documentation modified to allow for an undecorated window with no icon (Note if one wanted to achieve a transparent/decorated window they could do so by changing the style of mainStage).
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class MultipleStageStyles extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.initStyle(StageStyle.UTILITY);
primaryStage.setOpacity(0);
primaryStage.setHeight(0);
primaryStage.setWidth(0);
primaryStage.show();
Stage mainStage = new Stage();
mainStage.initOwner(primaryStage);
mainStage.initStyle(StageStyle.UNDECORATED);
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
mainStage.setScene(new Scene(root, 300, 250));
mainStage.show();
}
}

Javafx darken background

I have FXML application with 10 circles in AnchorPane. I want to hover mouse on one circle and make other 9 and background to darken.
The best I could do was some basic FadeTransition, which only made them disappear, not darken, plus I cant figure out how to select all children of node except one that I have mouse on. Selecting all children except one manually seems not really efficient for more objects.
I tried to google it up, but I just cant find anything.
Please, post a link to thread related to similar problem or sample code. Any help would be really appreciated.
You can use the following sample. Please note that there are some assumptions made, such as every node in the scene graph is a Shape object and that every shape has a Color object associated with the fill. The sample code is sufficient to derive other solutions related specifically to your use case.
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class SelectionApp extends Application {
private Pane root = new Pane();
private Parent createContent() {
root.setPrefSize(800, 600);
root.getChildren().add(new Rectangle(800, 600, Color.AQUA));
for (int i = 0; i < 10; i++) {
Circle circle = new Circle(25, 25, 25, Color.GREEN);
// just place them randomly
circle.setTranslateX(Math.random() * 700);
circle.setTranslateY(Math.random() * 500);
circle.setOnMouseEntered(e -> select(circle));
circle.setOnMouseExited(e -> deselect(circle));
root.getChildren().add(circle);
}
return root;
}
private void select(Shape node) {
root.getChildren()
.stream()
.filter(n -> n != node)
.map(n -> (Shape) n)
.forEach(n -> n.setFill(darker(n.getFill())));
}
private void deselect(Shape node) {
root.getChildren()
.stream()
.filter(n -> n != node)
.map(n -> (Shape) n)
.forEach(n -> n.setFill(brighter(n.getFill())));
}
private Color darker(Paint c) {
return ((Color) c).darker().darker();
}
private Color brighter(Paint c) {
return ((Color) c).brighter().brighter();
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
primaryStage.setTitle("Darken");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to add a shortcut event in javafx with combination of Ctrl + P +X

table.setOnKeyPressed(new EventHandler<KeyEvent>() {
// final KeyCombination kb = new KeyCodeCombination(KeyCode.P, KeyCombination.CONTROL_DOWN);
// final KeyCombination k = new KeyCodeCombina
public void handle(KeyEvent key) {
if (key.getCode() == KeyCode.P && key.isControlDown()) {
//My Code
}
}
});
I want to invoke the event with the shortcut keycombination of Ctrl+P+X
It is actually a little hard to understand what Ctrl+P+X means. I am going to assume it means that you press ctrl, then you press p, then you press x (potentially releasing the p before you press the x). I'll also assume that the order matters, e.g. press ctrl, then press x then press p would not count. Anyway a bit of speculation on my part, perhaps not exactly what you want, but hopefully you will get the gist of the provided solution and be able to adapt it to your situation.
The solution monitors both key presses and releases so that it can keep track of the state of key presses to determine if the key combination triggers.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.time.LocalTime;
public class KeyCombo extends Application {
KeyCombination ctrlP = KeyCodeCombination.keyCombination("Ctrl+P");
KeyCombination ctrlX = KeyCodeCombination.keyCombination("Ctrl+X");
#Override
public void start(Stage stage) throws Exception {
Label lastPressedLabel = new Label();
TextField textField = new TextField();
BooleanProperty pDown = new SimpleBooleanProperty(false);
textField.setOnKeyPressed(event -> {
if (ctrlP.match(event)) {
pDown.set(true);
}
if (pDown.get() && ctrlX.match(event)) {
pDown.set(false);
lastPressedLabel.setText(
LocalTime.now().toString()
);
}
});
textField.setOnKeyReleased(event -> {
if (!event.isControlDown()) {
pDown.set(false);
}
});
VBox layout = new VBox(10,
new Label("Press Ctrl+P+X"),
textField,
lastPressedLabel
);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you can, I'd advise trying to use a simpler control scheme, e.g. just Ctrl+P or Ctrl+X (which is directly supported by the key code combination event matching), rather than using a composite control scheme of Ctrl+P+X.

Make JavaFX wait and continue with code

Basically I am trying to make a short effect using JavaFX. I have the shape of a heart (added together from two circles and a polygon) that I can vary in size using the double value p. "Standart Size" would be p = 1.0;.
I am trying to add a pumping effect to the heart. I have the method pumpOnce():
public void pumpOnce(){
p = p + 1;
initHeart();
//Here goes what ever it takes to make stuff working!!
p = p - 1;
initHeart();
}
initHeart() draws the heart based on p.
I have found out that Thread.sleep(); or similar methods will not work due to the thread philosophy in JavaFX.
But what can I use instead?
The JavaFX animations are probably the way to go, but the "thread philosophy" in JavaFX isn't hard to work with if you want to roll your own, or do other, more complicated things in background threads.
The following code will pause and change the value in a label (full disclosure, I'm reusing code I wrote for another question):
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.xml.datatype.Duration;
public class DelayWithTask extends Application {
private static Label label;
public static void main(String[] args) { launch(args); }
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
label = new Label();
label.setText("Waiting...");
StackPane root = new StackPane();
root.getChildren().add(label);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
delay(5000, () -> label.setText("Hello World"));
}
public static void delay(long millis, Runnable continuation) {
Task<Void> sleeper = new Task<Void>() {
#Override
protected Void call() throws Exception {
try { Thread.sleep(millis); }
catch (InterruptedException e) { }
return null;
}
};
sleeper.setOnSucceeded(event -> continuation.run());
new Thread(sleeper).start();
}
}
The basic JavaFX background tool is the Task, any JavaFX application that actually does anything will probably be littered with these all over. Learn how to use them.
Dave's solution is great for general purpose off thread based work in JavaFX.
If you wish to use the animation facilities of JavaFX, the solutions below demonstrate this using a Timeline or a ScaleTransition. The timeline implements a discrete scale of the UI element, so every quarter of a second the UI element is scaled larger or back to it's original size. The scale transition implements a smooth scale of the UI element, so the UI element gradually gets larger then smaller using an interpolated scale factor with the default easing interpolator.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BeatingHeart extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) {
ImageView heart = new ImageView(HEART_IMAGE_LOC);
animateUsingTimeline(heart);
// animateUsingScaleTransition(heart);
StackPane layout = new StackPane(heart);
layout.setPrefWidth(heart.getImage().getWidth() * 2);
layout.setPrefHeight(heart.getImage().getHeight() * 2);
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private void animateUsingTimeline(ImageView heart) {
DoubleProperty scale = new SimpleDoubleProperty(1);
heart.scaleXProperty().bind(scale);
heart.scaleYProperty().bind(scale);
Timeline beat = new Timeline(
new KeyFrame(Duration.ZERO, event -> scale.setValue(1)),
new KeyFrame(Duration.seconds(0.5), event -> scale.setValue(1.1))
);
beat.setAutoReverse(true);
beat.setCycleCount(Timeline.INDEFINITE);
beat.play();
}
private void animateUsingScaleTransition(ImageView heart) {
ScaleTransition scaleTransition = new ScaleTransition(
Duration.seconds(1), heart
);
scaleTransition.setFromX(1);
scaleTransition.setFromY(1);
scaleTransition.setFromZ(1);
scaleTransition.setToX(1.1);
scaleTransition.setToY(1.1);
scaleTransition.setToZ(1.1);
scaleTransition.setAutoReverse(true);
scaleTransition.setCycleCount(Animation.INDEFINITE);
scaleTransition.play();
}
private static final String HEART_IMAGE_LOC =
"http://icons.iconarchive.com/icons/mirella-gabriele/valentine/128/Heart-red-icon.png";
// icon obtained from: http://www.iconarchive.com/show/valentine-icons-by-mirella-gabriele/Heart-red-icon.html
// icon license: Free for non-commercial use, commercial use not allowed.
}

Resources