This is my first post, so please forgive any mistakes I make. Using Java, I've made a recreation of the classic arcade game, Snake. The game mechanics, title screen, and end screen all function properly and look correct; however, my game board currently looks horrendous.
I implemented the game's board by making a TilePane that holds a certain width and height of Rectangles. For some reason, I can clearly see a separation between each Rectangle where the background of the Region containing the TilePane peeps through. To understand how I formatted the board, here's the code where I place each Rectangle in the TilePane:
private final Rectangle[][] board; // board containing the Rectangles
public Board(int width, int height) {
// grid to display each Rectangle
TilePane playableGrid = new TilePane(Orientation.VERTICAL);
playableGrid.setPrefRows(height);
this.setBackground(Background.EMPTY);
// initialize the board of Rectangles
board = new Rectangle[width][height];
// places each Rectangle in the board and playableGrid
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
board[x][y] = new Rectangle(24, 24);
playableGrid.getChildren().add(board[x][y]);
} // for
} // for
this.getChildren().add(playableGrid); // add the playableGrid in the VBox
} // Board
Also, here's a link showing how the problem: https://imgur.com/a/d25iY3i. Any help would be greatly appreciated! I fiddled around with the Stroke property found in the Shape class, but I could not find any solution.
Edit: I'll provide a minimal reproducible example of the code to display the problem. Also, I forgot to mention that I'm using XMing to run the Application on a Windows laptop. Here's a quick example that you should be able to copy and paste into a class (it shows one extra Rectangle on the right, not sure why):
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Example extends Application {
public static void main(String[] args) {
Application.launch(Example.class, args);
}
public Example() {}
#Override
public void start(Stage stage) {
/** Setup TilePane where game would be played */
TilePane playableGrid = new TilePane(Orientation.VERTICAL);
playableGrid.setPrefRows(25);
for (int x = 0; x < 25; x++) {
for (int y = 0; y < 25; y++) {
// The playableGrid consists of 25 rows of Rectangles
Rectangle rectangle = new Rectangle(20, 20);
rectangle.setFill(Color.CADETBLUE);
playableGrid.getChildren().add(rectangle);
} // for
} // for
/** In my game, a Region with a specific background holds the playableGrid */
VBox root = new VBox();
root.setMinSize(500, 500);
root.setBackground(new Background(new BackgroundFill(Color.AZURE,
CornerRadii.EMPTY, Insets.EMPTY)));
root.getChildren().add(playableGrid);
/** Setup scene */
Scene scene = new Scene(root);
/** Setup stage */
stage.setScene(scene);
stage.setOnCloseRequest(event -> Platform.exit());
stage.sizeToScene();
stage.show();
}
}
Clearly, you can see the background of the VBox holding the TilePane.
Related
I've managed to move the camera using mouse drag. However, the problem is that once I've moved the camera and released the mouse press, the camera returns back to the origin.
I want the camera to remain in the changed position instead so that when I use the mouse again to move the camera, it moves from that position to wherever instead of from the origin.
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Sphere;
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
Scene scene = new Scene(group, WIDTH, HEIGHT);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
scene.setOnMouseDragged(event -> {
camera.setTranslateY(anchorY - event.getSceneY());
camera.setTranslateX(anchorX - event.getSceneX());
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I've tried googling to find a solution but couldn't find much on camera movement with javafx
First of all, your scene does not have depth buffer enabled . These constructors enable 3d features in a scene instance Scene(Parent root,double width,double height,boolean depthBuffer) and Scene(Parent root,double width,double height,boolean depthBuffer,SceneAntialiasing antiAliasing) both with depthBufer boolean set to true. Second ,if you want to translate a node ; translate it getting its current coordinates and then add new coords . that will avoid reset coords . I divided the dragged result by 10 because it brought too high values
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
// for 3d Scene constructor must have zbuffer= true and SceneAntialiasing
Scene scene = new Scene(group, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
// translating camera from its current coords pluss event
scene.setOnMouseDragged(event -> {
camera.setTranslateY(camera.getTranslateY() + ((anchorY - event.getSceneY()) / 10));
camera.setTranslateX(camera.getTranslateX() + ((anchorX - event.getSceneX()) / 10));
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I don't know in how far Google can help you fix your logic problems. I did no further analysis of your code but at least each time when you press the mouse button your camera position is reset to zero by design. Your changes are not cumulative.
My task is to write a program that makes a circle ball that gradually fades away as the ball moves to the right. But it's not working, I can make the ball move when the mouse is dragged but the opacity is the same. Can you guys help me? I don't know how to convert the opacity value into double
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
public class Project3 extends Application
{
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Pane root = createRootPane();
Scene scene1 = new Scene(root);
primaryStage.setScene(scene1);
primaryStage.setTitle(" Hai Vo ");
primaryStage.show();
}
public Pane createRootPane()
{
Circle ball = new Circle (100,50,25);
Pane root = new Pane(ball);
root.setMinSize(300,300);
root.setOnMouseDragged (
event ->
{
double x = event.getX();
ball.setCenterX(event.getX());
ball.setCenterY(event.getY());
ball.opacityProperty().bind(ball.centerXProperty());
double opacity = ball.opacityProperty();
ball.setOpacity(opacity);
} );
return root;
}
}
First, move the binding out of the event handler. The binding will ensure the opacity is always updated when the centerX property updates.
Second, don't set bound values; the last two lines of the event handler just set the opacity to its current value anyway.
Third, the opacity should be between 0 and 1. You want it to be 1 when centerX is 0 and 0 when centerX is 300 (or, generally, the width of the pane).
What you need is (in pseudocode)
opacity = 1 - ball.centerX / root.width
= (ball.centerX / root.width) * (-1) + 1
which you can express in bindings with
ball.centerXProperty()
.divide(root.widthProperty())
.multiply(-1)
.add(1)
So put together, you need:
public Pane createRootPane() {
Circle ball = new Circle (100,50,25);
Pane root = new Pane(ball);
ball.opacityProperty().bind(
ball.centerXProperty()
.divide(root.widthProperty())
.multiply(-1)
.add(1)
);
root.setMinSize(300,300);
root.setOnMouseDragged (
event ->
{
ball.setCenterX(event.getX());
ball.setCenterY(event.getY());
} );
return root;
}
I'm trying to draw 10,000 circles in JavaFX but it seems like its not working and I'm not even able to draw a single circle. Actually it trows me an error:
This is the code that I currently have:
public class RandomCircles extends Application {
private Random randomNumbers;
private int count;
private final double MAX_X = 600;
private final double MAX_Y = 300;
private final int FINAL_CIRCLES = 10000;
public void start(Stage primaryStage){
Circle initCircle = new Circle();
initCircle.setStroke(Color.BLACK);
initCircle.setStrokeWidth(3);
initCircle.setRadius(1);
for(count = 0; count <= FINAL_CIRCLES; count++){
initCircle.setCenterX(randomNumbers.nextInt((int) MAX_X));
initCircle.setCenterY(randomNumbers.nextInt((int) MAX_Y));
}
Group baseDemo = new Group(initCircle);
// Scene scene = new Scene(baseDemo, MAX_X, MAX_Y);
Scene scene = new Scene(baseDemo);
scene.setFill(Color.WHITE);
scene.getWidth();
primaryStage.setTitle("10,000");
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
launch(args);
}
}
Can somebody also tell me if using the setCenterX/Y is the right approach to create the circles in random locations?
Thanks.
UPDATE: To the person who though of my post as a duplicate, it is not. My problem comes from my logic that I implemented in my code not from a NullPointerException(not really) error. , which was wrong. Some guy already helped me to solve it.
After fixing the runtime error you are getting, your code only draws one circle. That's because you only add one circle to your scene graph. The for loop basically does nothing. The last X and Y coordinates for the circle's center are used to draw the single, solitary circle. You need to add ten thousand circles.
In the code below, I changed 10_000 to 100 (one hundred) since 10_000 has too many overlapping circles in the stage dimensions that you set. I also increased each circle's radius.
import java.util.Random;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class RandomCircles extends Application {
private Random randomNumbers = new Random();
private int count;
private final double MAX_X = 600;
private final double MAX_Y = 300;
private final int FINAL_CIRCLES = 100;
public void start(Stage primaryStage){
Group baseDemo = new Group();
for(count = 0; count <= FINAL_CIRCLES; count++){
Circle initCircle = new Circle();
initCircle.setStroke(Color.BLACK);
initCircle.setStrokeWidth(3);
initCircle.setRadius(5);
initCircle.setCenterX(randomNumbers.nextInt((int) MAX_X));
initCircle.setCenterY(randomNumbers.nextInt((int) MAX_Y));
baseDemo.getChildren().add(initCircle);
}
Scene scene = new Scene(baseDemo);
scene.setFill(Color.WHITE);
scene.getWidth();
primaryStage.setTitle("100");
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Change this line:
private Random randomNumbers;
to this:
private Random randomNumbers = new Random();
Your code is assuming the the Random object will be allocated like the other member variables, but it is an object and must be created with new.
I want my blue circle go from one light blue circle to another, but only does half.
The coordinates appear being the same:
initial_lightblue: 211.7,230.5
blue: 193.7,239.1
final_lighblue: 193.7,239.1
My code
TranslateTransition transition = new TranslateTransition();
transition.setToX(c.getX());
transition.setToY(c.getY());
transition.setNode(view);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
Any sugestions?
From documentation
TranslateTransition : This Transition creates a move/translate animation that spans its duration. This is done by updating the
translateX, translateY and translateZ variables of the node at regular
interval.
So it's going to use the translate properties in order to relocate the actual node inside your pane. With that being said if you locate your node at x=50 and y=50 by setting its layoutX and layoutY properties their translation values will be 0, so if you actually try to set the end coordinates of the TranslateTransition to be ex. x = 100 y = 100 then its going to move your Node to the x = 150 and y = 150 and not to the (x,y) = (100,100) cause its going to change the translation property (x and y) from 0 to 100 which will move the node eventually to (x,y) = 150,150.
With that been said here is an example :
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Test extends Application {
private Circle c1;
private Circle c2;
#Override
public void start(Stage stage) throws Exception {
AnchorPane pane = new AnchorPane();
// Set only the radius
c1 = new Circle(5);
c1.setFill(Color.BLUE);
// Let's translate the c1 to the location we want
c1.setTranslateX(50);
c1.setTranslateY(60);
// The same for circle2
c2 = new Circle(5);
c2.setFill(Color.RED);
c2.setTranslateX(120);
c2.setTranslateY(200);
pane.getChildren().addAll(c1, c2);
pane.setOnMouseClicked(e -> {
startAnimation();
});
stage.setScene(new Scene(pane, 400, 500));
stage.show();
}
private void startAnimation() {
TranslateTransition transition = new TranslateTransition();
transition.setNode(c1);
transition.setToX(c2.getTranslateX());
transition.setToY(c2.getTranslateY());
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
}
public static void main(String[] args) {
launch(args);
}
}
P.S : In your code example you are referring to circles but you call c.getX() to actually take the x of the circle, that is quite strange cause the Circle class doesn't have method getX() I am quessing you are referreing to getCenterX() or those are not Circles .
I am trying to create a program in Java that is sort of like a board game. I have gameTiles, currently with only one color as I try to get the layout right. I want the tiles to appear about halfway of the width of the window and extend to the bottom, maybe anywhere from 9x9 or 11x11 different tiles. I have tried to use the grid layout to get these to be close together, not necessarily touching but close enough to look like a board game. However, no matter what I try, the tiles are space so far apart, and change shape when I resize window. I have been using the GridLayout manager to try to achieve this. Here is my code.
import java.awt.GridLayout;
import javax.swing.JFrame;
public class GameWindow extends JFrame {
public GameWindow(String title){
super(title);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1200,400);
}
/**
* #param args
*/
public static void main(String[] args) {
GameWindow gameWindow = new GameWindow("Game");
gameWindow.setLayout(new GridLayout(0,2));
GameTile greenTile = new GameTile(0,true,0,10);
GameTile greenTile2 = new GameTile(0,true,0,10);
gameWindow.add(greenTile);
gameWindow.add(greenTile2);
gameWindow.setVisible(true);
}
}
This is the GameWindow.java file. The GameTile.java I have so far (which is still mainly not finished just for testing) is as follows:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
/**
* #author Zachary Parks
*
*/
public class GameTile extends JPanel {
private Color color;
private Color[] colors = {Color.BLUE, Color.YELLOW, Color.BLACK};
private int score, multiplier,initialX,initialY;
private boolean positiveEffect;
public GameTile(int colorTile, boolean effect, int initX, int initY){
color = colors[colorTile];
score = getScore(color);
multiplier = getMultiplier(color);
positiveEffect = effect;
initialX = initX;
initialY = initY;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Image image = null;
try {
image = ImageIO.read(new File("greentile.png"));
} catch (IOException e) {
e.printStackTrace();
}
g.drawImage(image,initialX,initialY,this);
}
private int getMultiplier(Color color2) {
return 0;
}
private int getScore(Color color2) {
return 0;
}
/**
* Method that returns the data from the tile in
* array of int. 0 index = added score, 1 index = tile effect score
* #return
*/
public int[] getData() {
int [] scoreData = null;
scoreData[0] = score*multiplier;
return null;
}
}
A lot of the code is still in progress, like the GameTile's properties, all I'm trying at this point is get the tiles next to each other.
This is what I am getting:
To add tiles like a grid. A tileSize variable is great to have. Let's say the image/tile is 32 pixels.
public static final tileSize = 32;
With this, we can now add tiles using a for loop:
for(int x = 0; x < SCREEN_WIDTH / GameTile.tileSize; x++) { // loop through as many tiles fit the x-axis.
for(int y = 0; y < SCREEN_HEIGHT / GameTile.tileSize; y++) { // do the same with y-axis
GameTile greenTile = new GameTile(0,true, x * GameTile.tileSize , y * GameTile.tileSize);
gameWindow.add(greenTile);
}
}
SCREEN_WIDTH and SCREEN_HEIGHT is the size of your JFrame.
Keep in mind this loops through the whole screen, you wanted the half but it's easily configurable.
Please format your code next time (tab in), much easier to read and help you.
I highly recommend moving image = ImageIO.read(new File("greentile.png")); into the constructor, right now you're loading the image every framerate for every gameTile which will cause performance drop.
Also do not have a JPanel for every GameTile. Instead keep it in your main drawing class and loop through all GameTiles using an ArrayList
public static ArrayList<GameTile> gameTiles = new ArrayList<GameTile>();
protected void paintComponent(Graphics g) {
for(int i = 0; i < gameTiles.size(); i++) {
gameTiles.get(i).draw(g);
}
So instead of adding a JPanel to the JFrame for every gameTile, we draw gameTiles at the specified coordinates. Good luck!
To answer your question in the comment field: the code will look similar to this
public class GameWindow extends JPanel {
public static ArrayList<GameTile> gameTiles = new ArrayList<GameTile>();
public static void main(String[] args) {
new GameWindow();
}
public GameWindow() {
JFrame frame = new JFrame();
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.setVisible(true);
// add all the tiles (I just use x and y as parameters here)
gameTiles.add(new GameTile(10, 10));
gameTiles.add(new GameTile(50, 10));
}
public void paintComponent(Graphics g) {
for(int i = 0; i < gameTiles.size(); i++) {
gameTiles.get(i).draw(g);
}
}
}
And inside your GameTile class, remove the extends JPanel. And rename the paintComponent as draw or something alike.
However, no matter what I try, the tiles are space so far apart, and change shape when I resize window
A GridLayout expands each cell to fill the space available to the component. If you only have two cells each cell will take up half the width of the frame.
So the solution is to wrap your tile panel into another panel that will respect the preferred size of the tiles. So don't set the layout of the frame, set the layout of a panel holding the tiles:
Something like:
JPanel tilePanel = new JPane( new GridLayout(0, 2, 5, 5) );
tilePanel.add( new GameTile(...) );
tilePanel.add( new GameTile(...) );
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add(tilePanel, new GridBagConstraints() );
frame.add(wrapper, BorderLayout.CENTER );
The above code will cause the tiles to be centered in the frame.
frame.add(wrapper, BorderLayout.LINE_END);
The above will cause the tiles to display on the right of the frame vertically centered.