Issue
So basically i am making a game in which the circle will move in the four directions. When the "S" or "RIGHT" key is pressed once, the circle will move till it reaches the maxX of the pane. And when i press the "A" key or "LEFT", the circle will move till it reaches minX of pane. The "UP" or "W" and "DOWN" or "Z" keys also follow similar logic. If it get to maxY of pane it stops till another key is pressed and vice versa.
Here is the code for the controller class
@FXML
private Pane board;
private BooleanProperty wPressed = new SimpleBooleanProperty();
private BooleanProperty aPressed = new SimpleBooleanProperty();
private BooleanProperty zPressed = new SimpleBooleanProperty();
private BooleanProperty sPressed = new SimpleBooleanProperty();
private BooleanBinding keyPressed = wPressed.or(aPressed).or(zPressed).or(sPressed);
private Circle circle = new Circle(20,40,20);
private Rectangle[][] grid;
private int size = 960;
private int spots = 16;
private int squareSize = size / spots;
@FXML
void handleB(ActionEvent event) {
}
AnimationTimer timer = new AnimationTimer() {
double deltaX = 2;
double deltaY = 2;
@Override
public void handle(long timestamp) {
double rectX = circle.getLayoutX();
Bounds bounds = board.getBoundsInLocal();
double maxX = bounds.getMaxX();
double maxY = bounds.getMaxY();
double minX = bounds.getMinX();
double minY = bounds.getMinY();
if (circle.getLayoutX() >= ( maxX- circle.getRadius())) {
deltaX = 0;
//System.out.println(rect.getLayoutX());
}
if (circle.getLayoutY() >= (maxY - circle.getRadius())) {
deltaY = 0;
}
if (circle.getLayoutX() <= ( minX + circle.getRadius())) {
deltaX = 0;
//System.out.println(rect.getLayoutX());
}
if (circle.getLayoutY() <= ( minY + circle.getRadius())) {
deltaY = 0;
}
if(wPressed.get()) {
circle.setLayoutY(circle.getLayoutY() - deltaY);
}
if(zPressed.get()){
circle.setLayoutY(circle.getLayoutY() + deltaY);
}
if(aPressed.get()){
circle.setLayoutX(circle.getLayoutX() - deltaX);
}
if(sPressed.get()){
circle.setLayoutX(circle.getLayoutX() + deltaX);
}
}
};
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
grid = new Rectangle[spots][spots];
for (int i = 0; i < size; i += squareSize) {
for (int j = 0; j < size; j += squareSize) {
Rectangle r = new Rectangle(i, j, squareSize, squareSize);
grid[i / squareSize][j / squareSize] = r;
// Filling each grid with the cell image
r.setFill(Color.GRAY);
r.setStroke(Color.BLACK);
board.getChildren().add(r);
}
}
board.getChildren().addAll(circle);
movementSetup();
keyPressed.addListener(((observableValue, aBoolean, t1) -> {
if(!aBoolean){
timer.start();
} else {
timer.stop();
}
}));
}
private void movementSetup() {
board.setOnKeyPressed(e -> {
if(e.getCode() == KeyCode.UP || e.getCode() == KeyCode.W ) {
zPressed.set(false);
sPressed.set(false);
aPressed.set(false);
wPressed.set(true);
}
if(e.getCode() == KeyCode.A || e.getCode() == KeyCode.LEFT) {
zPressed.set(false);
sPressed.set(false);
wPressed.set(false);
aPressed.set(true);
}
if(e.getCode() == KeyCode.Z || e.getCode() == KeyCode.DOWN) {
wPressed.set(false);
aPressed.set(false);
sPressed.set(false);
zPressed.set(true);
}
if(e.getCode() == KeyCode.S || e.getCode() == KeyCode.RIGHT) {
wPressed.set(false);
aPressed.set(false);
zPressed.set(false);
sPressed.set(true);
}
});
}
}
** I did the pane in scenebuilder which i inserted it into an anchorpane as well**
The fxml file is below (ui.fxml)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<AnchorPane maxHeight="1000.0" maxWidth="1200.0" minHeight="0.0" minWidth="0.0" prefHeight="1000.0" prefWidth="1200.0" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx.game.Controller">
<children>
<Pane fx:id="board" maxHeight="960.0" maxWidth="960.0" minHeight="0.0" minWidth="0.0" prefHeight="960.0" prefWidth="960.0">
<children>
<Button layoutX="367.0" layoutY="934.0" mnemonicParsing="false" onAction="#handleB" text="Button" />
</children>
</Pane>
</children>
</AnchorPane>
Lastly the main class
package fx.game;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("ui.fxml"));
stage.setTitle("Hello World");
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Am really bad at game development I might be doing it completely wrong Anyone who can give me a better solution will be appreciated
Solution
From my limited understanding and testing, board.setOnKeyPressed
isn't responding to key events, I used the Scene
instead. You should also be taking into account onKeyReleased
.
I don't know why you're starting and stoping the timer
, just start it and on each, evaluate the state and make the changes that are required.
Your movement logic seems to be off as well. Get the circles current location, apply any movement to it as required, then determine if the new position is outside the acceptable bounds, adjust the position as needed and then update the circles position.
I also found that the Bounds
the board
would update based on the position of it's children, which made it hard to do bounds checking on, instead, I calculate the expected size based on the squareSize
and the number of spots
For example...
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class App extends Application {
private BooleanProperty wPressed = new SimpleBooleanProperty();
private BooleanProperty aPressed = new SimpleBooleanProperty();
private BooleanProperty zPressed = new SimpleBooleanProperty();
private BooleanProperty sPressed = new SimpleBooleanProperty();
private BooleanBinding keyPressed = wPressed.or(aPressed).or(zPressed).or(sPressed);
private Circle circle = new Circle(20, 20, 20);
private Rectangle[][] grid;
private int size = 960;
private int spots = 16;
private int squareSize = size / spots;
AnimationTimer timer = new AnimationTimer() {
double deltaX = 2;
double deltaY = 2;
@Override
public void handle(long timestamp) {
double rectX = circle.getLayoutX();
Bounds bounds = board.getBoundsInLocal();
double width = squareSize * spots;
double height = squareSize * spots;
double diameter = circle.getRadius() * 2;
double x = circle.getLayoutX();
double y = circle.getLayoutY();
if (wPressed.get()) {
y -= deltaY;
}
if (zPressed.get()) {
y += deltaY;
}
if (aPressed.get()) {
x -= deltaX;
}
if (sPressed.get()) {
x += deltaX;
}
if (x >= (width - diameter)) {
x = width - diameter;
}
if (x < 0) {
x = 0;
}
if (y > (height - diameter)) {
y = height - diameter;
}
if (y < 0) {
y = 0;
}
circle.setLayoutX(x);
circle.setLayoutY(y);
}
};
private Pane board = new AnchorPane();
@Override
public void start(Stage stage) {
stage.setTitle("Dice Game");
setup();
Scene scene = new Scene(board);
movementSetup(scene);
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
private void movementSetup(Scene scene) {
scene.setOnKeyPressed(e -> {
zPressed.set(false);
sPressed.set(false);
aPressed.set(false);
wPressed.set(false);
if (e.getCode() == KeyCode.UP || e.getCode() == KeyCode.W) {
wPressed.set(true);
} else if (e.getCode() == KeyCode.A || e.getCode() == KeyCode.LEFT) {
aPressed.set(true);
}else if (e.getCode() == KeyCode.Z || e.getCode() == KeyCode.DOWN) {
zPressed.set(true);
}else if (e.getCode() == KeyCode.S || e.getCode() == KeyCode.RIGHT) {
sPressed.set(true);
}
});
}
protected void setup() {
grid = new Rectangle[spots][spots];
for (int i = 0; i < size; i += squareSize) {
for (int j = 0; j < size; j += squareSize) {
Rectangle r = new Rectangle(i, j, squareSize, squareSize);
grid[i / squareSize][j / squareSize] = r;
// Filling each grid with the cell image
r.setFill(Color.GRAY);
r.setStroke(Color.BLACK);
board.getChildren().add(r);
}
}
board.getChildren().addAll(circle);
timer.start();
}
public static void main(String[] args) {
launch();
}
}
nb: I'm a complete JavaFX noob, so there's probably better ways to approach this issue and maybe having a look at things like Introduction to JavaFX for Game Development (or other "javafx game development blogs" might provide better ideas
Answered By - MadProgrammer
Answer Checked By - Candace Johnson (JavaFixing Volunteer)