admin管理员组文章数量:1355625
I'm currently working on a concurrent implementation of multiplayer Tetris in JavaFX where each game is run on a separate Stage. Each game has its own set of inputs so you can play simultaneously as focus is switched between the two stages whenever an appropriate input for one is received. For the most part, this works well - thanks to a very helpful answer on a previous question: Is there a way to switch the focus window upon a key press in JavaFX I was able to modify my input handling to include focus switching like I wanted. This is my current implementation:
public void handleInput(KeyEvent event) {
switch(event.getCode()) {
case UP:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.rotate(MESH);
}
break;
case LEFT:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.move(false, MESH);
}
break;
case RIGHT:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.move(true, MESH);
}
break;
case DOWN:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.slam(MESH);
}
With there being alternate cases for WASD that apply the same thing but for stageList.get(1) and gameList.get(1). Where the items in gameList are of type MainBoard - the class in which this method resides that act as the classes for running each individual game. Tetr is the Tetromino class which handles methods for the individual Tetris blocks. My issue is that the down input crashes the game every time, by this I mean it causes both windows to not respond for a few seconds before closing, leaving only the startboard open, and I'm unsure why. The method tetr.slam worked just fine before I implemented the dynamic focus switching and was instead manually switching focus by clicking. Additionally, the equivalent S input for the other MainBoard still works fine. I've included the method for tetr.slam below even if I don't believe that's where the issue lies:
public void slam(int MESH[][]) {
while (!fall(MESH)) {
fall(MESH);
}
}
And the definitions for MESH and fall(MESH):
public int[][] MESH = new int [StartBoard.XMAX/StartBoard.SIZE][StartBoard.YMAX/StartBoard.SIZE];
public boolean fall(int MESH[][]) {
for (Rectangle i: rectList) {
if (i.getY() + StartBoard.SIZE >= StartBoard.YMAX ||
MESH[(int)i.getX()/StartBoard.SIZE][((int)i.getY() + StartBoard.SIZE)/StartBoard.SIZE] == 1) {
return true;
}
}
for (Rectangle i: rectList) {
i.setY(i.getY() + StartBoard.SIZE);
}
return false;
}
Where MESH is a variable of MainBoard which it passes into its Tetromino. I'm really stumped on the issue here, if anyone has any ideas I would be very appreciative. Edit: I'm adding in the StartBoard code which creates the two threads on which the two MainBoards are created and run, in case concurrency is the issue.
startButton.setOnAction(new EventHandler <ActionEvent>()
{
public void handle(ActionEvent event)
{
playerVal.set(0);
startTask();
}
});
}
public void startTask() {
//Creates runnable
Runnable task = new Runnable() {
public void run() {
runTask();
}
};
//Run task in bgThread
Thread bgThread = new Thread(task);
Thread bgThread2 = new Thread(task);
//Terminate running thread if application exists
bgThread.setDaemon(true);
bgThread2.setDaemon(true);
//Start thread
bgThread.start();
bgThread2.start();
}
/**
* Sets up new stages for a Tetris game.
*/
public void runTask() {
Platform.runLater(new Runnable()
{
@Override
public void run()
{
Pane localPane = new Pane();
Scene localScene = new Scene(localPane, XMAX + 150, YMAX);
Stage inner = new Stage(){{setScene(localScene); }};
inner.initOwner(stage);
//inner.initModality(Modality.WINDOW_MODAL);
int val = playerVal.getAndIncrement();
inner.setX((XMAX + 150) * val );
inner.setY(YMAX / 2 - 150);
stageList.add(inner);
inner.show();
MainBoard localMainBoard = new MainBoard(val);
gameList.add(localMainBoard);
System.out.println("*****START******");
try {
localMainBoard.start(inner);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
I'm currently working on a concurrent implementation of multiplayer Tetris in JavaFX where each game is run on a separate Stage. Each game has its own set of inputs so you can play simultaneously as focus is switched between the two stages whenever an appropriate input for one is received. For the most part, this works well - thanks to a very helpful answer on a previous question: Is there a way to switch the focus window upon a key press in JavaFX I was able to modify my input handling to include focus switching like I wanted. This is my current implementation:
public void handleInput(KeyEvent event) {
switch(event.getCode()) {
case UP:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.rotate(MESH);
}
break;
case LEFT:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.move(false, MESH);
}
break;
case RIGHT:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.move(true, MESH);
}
break;
case DOWN:
if (!StartBoard.stageList.get(0).isFocused()) {
StartBoard.stageList.get(0).requestFocus();
StartBoard.gameList.get(0).handleInput(event);
} else {
tetr.slam(MESH);
}
With there being alternate cases for WASD that apply the same thing but for stageList.get(1) and gameList.get(1). Where the items in gameList are of type MainBoard - the class in which this method resides that act as the classes for running each individual game. Tetr is the Tetromino class which handles methods for the individual Tetris blocks. My issue is that the down input crashes the game every time, by this I mean it causes both windows to not respond for a few seconds before closing, leaving only the startboard open, and I'm unsure why. The method tetr.slam worked just fine before I implemented the dynamic focus switching and was instead manually switching focus by clicking. Additionally, the equivalent S input for the other MainBoard still works fine. I've included the method for tetr.slam below even if I don't believe that's where the issue lies:
public void slam(int MESH[][]) {
while (!fall(MESH)) {
fall(MESH);
}
}
And the definitions for MESH and fall(MESH):
public int[][] MESH = new int [StartBoard.XMAX/StartBoard.SIZE][StartBoard.YMAX/StartBoard.SIZE];
public boolean fall(int MESH[][]) {
for (Rectangle i: rectList) {
if (i.getY() + StartBoard.SIZE >= StartBoard.YMAX ||
MESH[(int)i.getX()/StartBoard.SIZE][((int)i.getY() + StartBoard.SIZE)/StartBoard.SIZE] == 1) {
return true;
}
}
for (Rectangle i: rectList) {
i.setY(i.getY() + StartBoard.SIZE);
}
return false;
}
Where MESH is a variable of MainBoard which it passes into its Tetromino. I'm really stumped on the issue here, if anyone has any ideas I would be very appreciative. Edit: I'm adding in the StartBoard code which creates the two threads on which the two MainBoards are created and run, in case concurrency is the issue.
startButton.setOnAction(new EventHandler <ActionEvent>()
{
public void handle(ActionEvent event)
{
playerVal.set(0);
startTask();
}
});
}
public void startTask() {
//Creates runnable
Runnable task = new Runnable() {
public void run() {
runTask();
}
};
//Run task in bgThread
Thread bgThread = new Thread(task);
Thread bgThread2 = new Thread(task);
//Terminate running thread if application exists
bgThread.setDaemon(true);
bgThread2.setDaemon(true);
//Start thread
bgThread.start();
bgThread2.start();
}
/**
* Sets up new stages for a Tetris game.
*/
public void runTask() {
Platform.runLater(new Runnable()
{
@Override
public void run()
{
Pane localPane = new Pane();
Scene localScene = new Scene(localPane, XMAX + 150, YMAX);
Stage inner = new Stage(){{setScene(localScene); }};
inner.initOwner(stage);
//inner.initModality(Modality.WINDOW_MODAL);
int val = playerVal.getAndIncrement();
inner.setX((XMAX + 150) * val );
inner.setY(YMAX / 2 - 150);
stageList.add(inner);
inner.show();
MainBoard localMainBoard = new MainBoard(val);
gameList.add(localMainBoard);
System.out.println("*****START******");
try {
localMainBoard.start(inner);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
Share Improve this question edited Apr 1 at 18:11 Kyle Nickson asked Mar 28 at 10:19 Kyle NicksonKyle Nickson 11 silver badge1 bronze badge 7 | Show 2 more comments1 Answer
Reset to default 0I remember your original question, and I kinda questioned how you were using Threads
. You appeared to create a thread to do background work, but in that thread, all of your code was in Platform.runLater
. I would suggest avoiding threads. I think using something from the Animation API is better.
I created this example that hopefully can help you. This example uses two different TimeLines
as game loops. It adds a listener to the stages to handle changing the focus from one stage to the other. After starting the two games, you can press CTRL +A to switch between both stages. You can also press the up and down arrows to speed up or slow down the ball, depending on which stage has focus.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.security.Key;
import java.util.HashSet;
import java.util.Set;
public class App extends Application {
@Override
public void start(Stage primaryStage)
{
ObservableList<Screen> screens = Screen.getScreens();
Rectangle2D bounds = screens.getFirst().getVisualBounds();
VBox root1 = setUpScene();
Scene scene1 = new Scene(root1, 800, 800);
primaryStage.setX((bounds.getMaxX() / 2.0) - scene1.getWidth() - 10);
primaryStage.setY(100);
primaryStage.setScene(scene1);
primaryStage.show();
VBox root2 = setUpScene();
var scene2 = new Scene(root2, 800, 800);
Stage stage2 = new Stage();
stage2.setX((bounds.getMaxX() / 2.0) + 10);
stage2.setY(100);
stage2.setScene(scene2);
stage2.show();
EventHandler<KeyEvent> handler = e -> {
if (e.isControlDown()) {
if (e.getCode() == KeyCode.A)
{
if(primaryStage.isFocused())
{
stage2.requestFocus();
root2.setStyle("-fx-background-color: black;");
root2.requestFocus();
root1.setStyle(null);
}
else
{
primaryStage.requestFocus();
root1.setStyle("-fx-background-color: black;");
root1.requestFocus();
root2.setStyle(null);
}
}
}
};
// add key-pressed handler to all involved windows
primaryStage.addEventFilter(KeyEvent.KEY_PRESSED, handler);
stage2.addEventFilter(KeyEvent.KEY_PRESSED, handler);
Platform.runLater(primaryStage::requestFocus);
}
public static void main(String[] args) {
launch();
}
public void moveBall(Circle ball, DoubleProperty ballVelocity)
{
ball.setCenterX(ball.getCenterX() + ballVelocity.getValue());
}
public void checkWallCollision(Circle ball, DoubleProperty leftBound, DoubleProperty rightBound, DoubleProperty ballVelocity)
{
//If the ball hits the wall, change its direction
if(ball.getCenterX() + ball.getRadius() >= rightBound.getValue() || ball.getCenterX() - ball.getRadius() <= leftBound.getValue())
{
ballVelocity.setValue(ballVelocity.getValue() * -1);
}
}
public VBox setUpScene()
{
VBox root = new VBox();
Circle ball = new Circle(100, 375, 10, Color.GREEN);
Timeline gameLoop;
DoubleProperty leftBound = new SimpleDoubleProperty(0);
DoubleProperty rightBound = new SimpleDoubleProperty(750);
DoubleProperty ballVelocity = new SimpleDoubleProperty(5);
Pane middleRoot = new Pane(ball);
rightBound.bind(middleRoot.widthProperty());
middleRoot.setStyle("-fx-background-color: yellow;");
middleRoot.setPrefSize(750, 750);
gameLoop = new Timeline(
new KeyFrame(Duration.millis(25), (ActionEvent event) -> {
System.out.println(ballVelocity);
moveBall(ball, ballVelocity);
checkWallCollision(ball, leftBound, rightBound, ballVelocity);
}));
gameLoop.setCycleCount(Timeline.INDEFINITE);
Button btnStart = new Button("Start");
btnStart.setOnAction((event) -> {
if(gameLoop.getStatus() == Timeline.Status.STOPPED)
{
gameLoop.play();
}
});
Button btnReset = new Button("Reset");
btnReset.setOnAction((event) -> {
if(gameLoop.getStatus() == Timeline.Status.RUNNING)
{
gameLoop.stop();
ball.setCenterX(100);
}
});
HBox bottomRoot = new HBox(btnStart, btnReset);
root.getChildren().add(middleRoot);
root.getChildren().add(bottomRoot);
root.setOnKeyPressed(event -> {
if (root.isFocused())
{
if(event.getCode() == KeyCode.UP && Math.abs(ballVelocity.getValue()) < 20)
{
if(ballVelocity.getValue() > 0)
{
ballVelocity.setValue(ballVelocity.get() + 1);
}
else if(ballVelocity.getValue() < 0)
{
ballVelocity.setValue(ballVelocity.get() - 1);
}
}
else if(event.getCode() == KeyCode.DOWN && Math.abs(ballVelocity.getValue()) > 1)
{
if(ballVelocity.getValue() > 0)
{
ballVelocity.setValue(ballVelocity.get() - 1);
}
else if(ballVelocity.getValue() < 0)
{
ballVelocity.setValue(ballVelocity.get() + 1);
}
}
}
});
return root;
}
}
本文标签: javafxMethod Breaks After Implementing Focus SwitchingStack Overflow
版权声明:本文标题:javafx - Method Breaks After Implementing Focus Switching - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744043502a2581100.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
slam
methodwhile (!fall(MESH))
goes into an infinite loop, but that is just a guess. – jewelsea Commented Mar 28 at 12:12java.util.concurrent
as a tag, so threading could be an issue, but there is no concurrent code in your question. – jewelsea Commented Mar 28 at 12:13