in my Application below, red blocks are falling from the top of the screen. My problem is, that after 3-5 minutes the animation begins to lag and I can't figure out why.
I used a profiler and found out, that it's not a memory leak but it must be a resource leak, I am pretty sure because the Cpu Load increases over time.
If you want to, you can just implement the necessary classes and compile and run the code, then you should probably see the problem. (after 3-5 minutes)
Thanks for your help and have a great day! :)
public class RBYDebug extends Application {
Group root = new Group();
ArrayList<Rectangle> entities = new ArrayList<>();
Random random = new Random();
int sW = 1600; //screen Width
int sH = 980; //screen Height
int dy = 4; //vertical move speed
public static void main(String[] args){
Application.launch(args);
}
public void start(Stage primaryStage){
//Setup Entities
for (int i = 0; i < 40; i++){
double x = random.nextDouble() * sW;
double y = i * 50;
Rectangle rect = new Rectangle(x, -y -50, 50, 50);
rect.setFill(Color.RED);
entities.add(rect);
}
//Root-Group setup
root.getChildren().setAll(entities);
//Setup Scene
Scene scene = new Scene(root, sW, sH, Color.BLACK);
//Stage setup
primaryStage.setTitle("RBY Debug");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
//Setup Timer
AnimationTimer timer = new AnimationTimer(){
public void handle(long now){
update();
}
};
timer.start();
}
public void update(){
for (Rectangle i : entities){
if (i.getY() <= sH){
i.setY(i.getY() + dy);
}
else {
i.setY(-i.getHeight());
}
}
root.getChildren().setAll(entities);
}
}
Related
I am new to JavaFX and I am creating a simple program. What I'm trying to achieve is to create a ball every 5 seconds that bounces off the walls and all balls should move every tens times per second (10 milliseconds). Also, feel free to leave other suggestions about my code.
Here's the source code:
public class Main extends Application {
#Override
public void start(Stage stage) {
//Sets the title, adds a group, and background color
BorderPane canvas = new BorderPane();
Scene scene = new Scene(canvas, 640, 480, Color.WHITE);
Circle ball = new Circle(10, Color.RED);
ball.relocate(0, 10);
canvas.getChildren().add(ball);
stage.setTitle("Title");
stage.setScene(scene);
stage.show();
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(20), new EventHandler<ActionEvent>() {
double dx = 5; //Step on x or velocity
double dy = 3; //Step on y
#Override
public void handle(ActionEvent t) {
//move the ball
ball.setLayoutX(ball.getLayoutX() + dx);
ball.setLayoutY(ball.getLayoutY() + dy);
Bounds bounds = canvas.getBoundsInLocal();
//If the ball reaches the left or right border make the step negative
if(ball.getLayoutX() <= (bounds.getMinX() + ball.getRadius()) ||
ball.getLayoutX() >= (bounds.getMaxX() - ball.getRadius()) ){
dx = -dx;
}
//If the ball reaches the bottom or top border make the step negative
if((ball.getLayoutY() >= (bounds.getMaxY() - ball.getRadius())) ||
(ball.getLayoutY() <= (bounds.getMinY() + ball.getRadius()))){
dy = -dy;
}
}
}));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}
Here is one way to do it as suggested in comments:
Create a custom object that holds the ball and its orientation information.
Add the newly created object in the list and the ball in canvas.
Loop through all balls and position them based on their orientation information.
When your desired time is reached add a new ball.
To keep it simple, you don't need any concurrent related stuff. Maybe using them will improve the implementation (but I am not touching that now). :-)
Here is a demo of using the points I mentioned.
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
public class CanvasBallCreation_Demo extends Application {
List<Ball> balls = new ArrayList<>();
BorderPane canvas;
double dx = 5; //Step on x or velocity
double dy = 3; //Step on y
double refresh = 20;//ms
double addBallDuration = 5000;//ms
double temp = 0;
SecureRandom rnd = new SecureRandom();
#Override
public void start(Stage stage) {
canvas = new BorderPane();
Scene scene = new Scene(canvas, 640, 480, Color.WHITE);
addBall();
stage.setTitle("Title");
stage.setScene(scene);
stage.show();
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(refresh), e->moveBalls()));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
private void addBall(){
Ball ball = new Ball();
balls.add(ball);
canvas.getChildren().add(ball.getBall());
}
private void moveBalls() {
temp = temp + refresh;
if(temp>addBallDuration){
temp = 0;
addBall();
}
Bounds bounds = canvas.getBoundsInLocal();
balls.forEach(obj -> {
Circle ball = obj.getBall();
double tx = obj.getTx();
double ty = obj.getTy();
ball.setLayoutX(ball.getLayoutX() + dx*tx);
ball.setLayoutY(ball.getLayoutY() + dy*ty);
//If the ball reaches the left or right border make the step negative
if (ball.getLayoutX() <= (bounds.getMinX() + ball.getRadius()) ||
ball.getLayoutX() >= (bounds.getMaxX() - ball.getRadius())) {
obj.setTx(-tx);
}
//If the ball reaches the bottom or top border make the step negative
if ((ball.getLayoutY() >= (bounds.getMaxY() - ball.getRadius())) ||
(ball.getLayoutY() <= (bounds.getMinY() + ball.getRadius()))) {
obj.setTy(-ty);
}
});
}
class Ball {
Circle ball = new Circle(10, Color.RED);
double tx = 1;
double ty = 1;
public Ball(){
// Placing the ball at a random location between 0,0 and 10,10 to generate random paths
ball.relocate(rnd.nextInt(10), rnd.nextInt(10));
}
public Circle getBall() {
return ball;
}
public double getTx() {
return tx;
}
public void setTx(double tx) {
this.tx = tx;
}
public double getTy() {
return ty;
}
public void setTy(double ty) {
this.ty = ty;
}
}
}
There are a few options, java.util.concurrent.TimeUnit or Thread.sleep.
You may want to look at both, and if you need two operations going at once (creating new balls, balls moving) I would look into using a new thread for each ball.
You can read more about using threads by using these articles, and your simple program actually seems like a great use case for learning about them.
https://www.geeksforgeeks.org/multithreading-in-java/
https://www.tutorialspoint.com/java/java_multithreading.htm
https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
I have 2 rectangles representing the paddles for my Pong game. I use W/S for one rectangle and UP/DOWN for the second rectangle. When I press W to move one rectangle and then press UP to move the second rectangle, the first rectangle will stop moving and then the second rectangle will move. How do I make it so both rectangles can move simultaneously?
GraphicsContext gc;
Rectangle player11;
Rectangle player22;
Circle ball;
private int y1;
private int p1y = 381;
private int y2;
private int p2y = 381;
AnimateObjects animate;
Canvas canvas;
private AnimationTimer timer = new AnimationTimer() {
public void handle(long now) {
// update paddle positions
p1y += y1;
p2y += y2;
if (p1y < 0) {
p1y = 0;
}
if (p2y < 0) {
p2y = 0;
}
player11.setY(p1y);
player22.setY(p2y);
}
};
public EventHandler<KeyEvent> keyReleased = new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
// set movement to 0, if the released key was responsible for the paddle
switch (event.getCode()) {
case W:
case S:
y1 = 0;
break;
case UP:
case DOWN:
y2 = 0;
break;
}
}
};
private EventHandler<KeyEvent> keyPressed = new EventHandler<KeyEvent>() {
public void handle(KeyEvent event) {
// start movement according to key pressed
switch (event.getCode()) {
case W:
y1 = -6;
break;
case S:
y1 = 6;
break;
case UP:
y2 = -6;
break;
case DOWN:
y2 = 6;
break;
}
}
};
public static void main(String[] args) {
launch();
}//main
public void start(Stage stage) {
stage.setTitle("Pong");
Group root = new Group();
canvas = new Canvas(1000, 800);
root.getChildren().add(canvas);
Scene scene = new Scene(root, Color.GREEN);
stage.setScene(scene);
player11 = new Rectangle(30, p1y, 20, 70);
player11.setFill(Color.RED);
player22 = new Rectangle(750, p2y, 20, 70);
player22.setFill(Color.BLUE);
root.getChildren().add(player11);
root.getChildren().add(player22);
scene.setOnKeyPressed(keyPressed);
scene.setOnKeyReleased(keyReleased);
ball = new Circle(10, Color.DARKSLATEBLUE);
root.getChildren().add(ball);
ball.relocate(500,350);
gc = canvas.getGraphicsContext2D();
animate = new AnimateObjects();
animate.start();
stage.show();
}//start
public class AnimateObjects extends AnimationTimer {
public void handle(long now) {
}//handle method in AnimateObjects class
}//AnimateObjects class
}//pong class
You need to capture KeyCodes. Use up, down, z, and x to control paddles. The right paddle will not move beyond the upper and lower bounds of the gameboard. The left paddle will. More comments in the code! Here is a working example.
import java.util.HashSet;
import java.util.Set;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class App extends Application
{
Rectangle leftPaddle, rightPaddle;
AnimationTimer gameLoop;
Set<KeyCode> input;
Pane gameBoard;
#Override
public void start(Stage primaryStage)
{
leftPaddle = new Rectangle(7, 100, Color.BLACK);
leftPaddle.setX(3);
leftPaddle.setY(0);
rightPaddle = new Rectangle(7, 100, Color.BLACK);
rightPaddle.setX(500 - 10);
rightPaddle.setY(0);
input = new HashSet(); //This set is used to keep up with keys that are currently being pressed.
gameBoard = new Pane(leftPaddle, rightPaddle);
VBox.setVgrow(gameBoard, Priority.ALWAYS);
gameBoard.setOnKeyPressed(event -> input.add(event.getCode()));//add keys that are currently being pressed to the set
gameBoard.setOnKeyReleased(event -> input.remove(event.getCode()));//remove keys from the set after they are released
gameLoop = new AnimationTimer()
{
#Override
public void handle(long l)
{
movePaddle();//Call method to move paddle based on certain keys in the set
//System.out.println("playing");
}
};
Button btnStartGame = new Button("Play");
btnStartGame.setMaxWidth(Double.MAX_VALUE);
btnStartGame.setOnAction((event) -> {
gameBoard.requestFocus();//Request gameboard focus to capture keyevents
gameLoop.start();
btnStartGame.setDisable(true);
});
VBox root = new VBox(gameBoard, btnStartGame);
Scene scene = new Scene(root, 500, 500);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
//Use to move paddles based on keycodes in set
private void movePaddle()
{
if (input.contains(KeyCode.UP)) {
rightPaddle.setY(rightPaddle.getY() - 10);
if (rightPaddle.getY() < 0) {
rightPaddle.setY(0);
}
}
else if (input.contains(KeyCode.DOWN)) {
rightPaddle.setY(rightPaddle.getY() + 10);
if (rightPaddle.getY() + rightPaddle.getHeight() > gameBoard.getHeight()) {
rightPaddle.setY(gameBoard.getHeight() - rightPaddle.getHeight());
}
}
if (input.contains(KeyCode.Z)) {
leftPaddle.setY(leftPaddle.getY() - 10);
}
else if (input.contains(KeyCode.X)) {
leftPaddle.setY(leftPaddle.getY() + 10);
}
}
}
I wanna move 3 Shapes at the same time in my scene. I already tried a lot with Threats but nothing worked so far. I hope you can help me, so I am able to see my 3 shapes moving from one side to the other side. Here is my Class:
public class TestingArea {
private Stage stage;
private Scene scene;
private Group root;
private ObservableList<Shape> shapes;
public TestingArea() {
shapeTest();
stage = new Stage();
stage.setScene(scene);
stage.show();
}
//This method is called by the main class, right after
//this stage here is shown
public void startAnimation() {
animationGo();
}
private void shapeTest() {
root = new Group();
shapes = FXCollections.observableArrayList();
int startX = 100;
int startY = 100;
int Size = 10;
//Here i just draw 3 triangles
Polygon poly = new Polygon(startX, startY, startX+Size*10, startY,
startX+Size*10/2, startY+Size*10);
for(Double doub: poly.getPoints()) {
System.out.println(doub);
}
int newStartX = poly.getPoints().get(2).intValue();
int newStartY = poly.getPoints().get(3).intValue();
Polygon poly2 = new Polygon(newStartX, newStartY
,newStartX+Size*10, newStartY
,newStartX+Size*10/2, startY+Size*10
);
int nextStartX = startX+Size*10/2;
int nextStartY = startY+Size*10;
Polygon poly3 = new Polygon(nextStartX, nextStartY
,nextStartX+Size*10, nextStartY
,nextStartX+Size*10/2, nextStartY+Size*10 );
//Add shapes to a List
shapes.add(poly);
shapes.add(poly2);
shapes.add(poly3);
//Add shapes to root
root.getChildren().add(poly);
root.getChildren().add(poly2);
root.getChildren().add(poly3);
scene = new Scene(root);
}
/**
* This method should move my 3 triangles 10 times and
* i wanna see every move.
*/
private void animationGo() {
for(int i = 0; i < 10; i++) {
for(Shape shape: shapes) {
double x = shape.getLayoutX();
shape.setLayoutX(x-10);
}
}
}
}
I am quite beginner in Java and need some help with my application.
I am would like to use drag and drop on custom made shapes with javafx canvas i.e multiple polygons that make up a bow tie.
I have created a method that draws a bow tie that looks like this:
public void joonistaBowTie(GraphicsContext gc) {
// Bowtie left side
gc.setFill(Color.RED);
double xpoints[] = { 242, 242, 200 };
double ypoints[] = { 245, 290, 270 };
int num = 3;
gc.fillPolygon(xpoints, ypoints, num);
// Bowtie right side
gc.setFill(Color.RED);
double xpoints1[] = { 160, 160, 200 };
double ypoints1[] = { 245, 290, 270 };
int num1 = 3;
gc.fillPolygon(xpoints1, ypoints1, num1);
// Bowtie middle part
gc.setFill(Color.RED);
gc.fillOval(190, 255, 20, 30);
}
I have moved that method into a separate class called BowTie.
I also have a main class that looks like this:
public class GraafikaNaide extends Application {
Bowtie bowtie;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JavaFX-iga joonistatud kloun");
Group root = new Group();
Canvas canvas = new Canvas(1000, 1000);
GraphicsContext gc = canvas.getGraphicsContext2D();
joonista(gc);
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private void joonista(GraphicsContext gc) {
Bowtie bowtie = new Bowtie();
bowtie.joonistaBowTie(gc);
}
I also found somewhat example on how to do drag and drop, but i just lack knowledge on how to implement this code to mine.
Could someone please help me with this?
Thanks
Using the link you provided, here is how you would incorporate it with your work:
public class GraafikaNaide extends Application
{
joonistaBowTie bowtie;
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
#Override
public void start(Stage primaryStage)
{
Canvas canvas = new Canvas(1000, 1000);
GraphicsContext gc = canvas.getGraphicsContext2D();
joonista(gc);
canvas.setOnMousePressed(canvasOnMousePressedEventHandler);
canvas.setOnMouseDragged(canvasOnMouseDraggedEventHandler);
Group root = new Group();
root.getChildren().add(canvas);
primaryStage.setTitle("JavaFX-iga joonistatud kloun");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
private void joonista(GraphicsContext gc)
{
joonistaBowTie bowtie = new joonistaBowTie();
bowtie.joinBowTie(gc);
}
EventHandler<MouseEvent> canvasOnMousePressedEventHandler = new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
orgSceneX = mouseEvent.getSceneX();
orgSceneY = mouseEvent.getSceneY();
orgTranslateX = ((Canvas)(mouseEvent.getSource())).getTranslateX();
orgTranslateY = ((Canvas) (mouseEvent.getSource())).getTranslateY();
}
};
EventHandler<MouseEvent> canvasOnMouseDraggedEventHandler = new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent mouseEvent)
{
double offsetX = mouseEvent.getSceneX() - orgSceneX;
double offsetY = mouseEvent.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
((Canvas) (mouseEvent.getSource())).setTranslateX(newTranslateX); //transform the object
((Canvas) (mouseEvent.getSource())).setTranslateY(newTranslateY);
}
};
}
Hope this helps.
Im working on a game in Java and having an issue (i believe its with the content pane) when rendering. I have a screen class which draws the background and all sprites to an Image. The frame then displays the image using a doubleBuffer. For some odd reason tho the image is rendering off the edge of the frame. You can see in the link below that the image is rendering 3 pixels to the left and 28 pixels above where it should be. Does anyone have any idea what could be causing this?
![enter image description here][1]
http://imageshack.us/photo/my-images/41/weirdg.png/
public class Game extends JFrame implements Runnable{
private static final long serialVersionUID = 1L;
//graphics
public BufferStrategy buffy;
BufferedImage image;
Screen screen;
public Boolean running = false;
public Boolean playerTurn = false;
public InputManager input;
public Level level;
//JButton b;
public static final int HEIGHT = 452;
public static final int WIDTH = 768;
public Game() {
super("GridWars");
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(WIDTH, HEIGHT));
drawPanel.setLayout(null);
drawPanel.setOpaque(false);
//drawPanel.setLocation(50,50);
setContentPane(drawPanel);
setResizable(false);
pack();
setLocationRelativeTo(null);
setVisible(true);
requestFocus();
createBufferStrategy(2);
//b = new JButton("this sucks");
//getContentPane().add(b);
//b.setBounds(300, 300, 100, 50);
buffy = getBufferStrategy();
image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
screen = new Screen(WIDTH, HEIGHT);
input = new InputManager(this);
level = new Level(WIDTH, HEIGHT, input, this);
}
public void start() {
running = true;
new Thread(this).start();
}
public void setup(){
}
public void run() {
final double TICKS = 30.0;
final double UPDATE_INTERVAL_NS = 1000000000 / TICKS;
double pastUpdateNS = System.nanoTime();
int updateCount = 0;
int frameCount = 0;
final double FRAPS = 60.0;
final double RENDER_INTERVAL_NS = 1000000000 / FRAPS;
double pastRenderNS = System.nanoTime();
int pastSecondNS = (int) (pastUpdateNS/1000000000);
while(running) {
double nowNS = System.nanoTime();
if(nowNS - pastUpdateNS >= UPDATE_INTERVAL_NS) {
update();
pastUpdateNS += UPDATE_INTERVAL_NS;
updateCount++;
}
float interp = Math.min(1.0f, (float) ((nowNS - pastUpdateNS) / UPDATE_INTERVAL_NS) );
render(interp);
pastRenderNS += RENDER_INTERVAL_NS;
frameCount++;
int thisSecondNS = (int) (pastUpdateNS/1000000000);
if (thisSecondNS > pastSecondNS) {
//System.out.println("TICKS: "+updateCount+" | FRAPS: "+frameCount);
updateCount = 0;
frameCount = 0;
pastSecondNS = thisSecondNS;
}
while( nowNS - pastRenderNS < RENDER_INTERVAL_NS && nowNS - pastUpdateNS < UPDATE_INTERVAL_NS) {
try { Thread.sleep(1); } catch(Exception e) {};
nowNS = System.nanoTime();
}
}
}
public void update() {
input.update();
level.update();
}
public void render(float interp) {
level.render(screen, interp);
image = screen.getImage();
Graphics g = buffy.getDrawGraphics();
g.drawImage(image, 0, 0, null, null);
//b.repaint();
g.dispose();
buffy.show();
}
public static void main(String[] args) {
Game game = new Game();
game.start();
}
}
The 0,0 coordinate of Graphics object you obtain from buffy.getDrawGraphics(); is exactly at top left corner of JFrame and it is ignoring frame decorations.
UPD I forgot one obvious option. JFrame.getInsets() provides information about decorations. You could simply shift your rendering.
You would make frame undecorated (setUndecorated(true)) and render/manage window controls yourself.
Or, and i think it is easier way, you would forget about direct rendering on JFrame, place Canvas on it, and use it instead. Canvas also contains createBufferStrategy method, so you need few simple changes.
JPanel drawPanel = new JPanel();
drawPanel.setLayout(new BorderLayout());
Canvas canvas = new Canvas();
canvas.setPreferredSize(new Dimension(WIDTH, HEIGHT));
drawPanel.add(canvas, BorderLayout.CENTER);
// some code skipped
canvas.setIgnoreRepaint(true); //important
canvas.createBufferStrategy(2);
buffy = canvas.getBufferStrategy();
I've created simple demo with similar render few days ago for another answer. Maybe it will helpful.