JavaFX animation does not work in a Stack Pane - java

I am working on a class assignment, a pong game.
Below is the requirement.
A rectangular paddle moves back and forth via mouse drag along the bottom of the pane;
If the ball connects with the paddle, then it bounces at a 90-degree angle back into the pane space;
The mouse and keyboard actions do not work properly, no response to mouse and keys.
I can't figure out the problem. Any helps will be really appreciated.
PongForOne.java
public class PongForOne extends Application {
#Override
public void start(Stage primaryStage) {
BallPane ball = new BallPane();
PaddlePane paddle = new PaddlePane();
StackPane stkPane = new StackPane();
stkPane.getChildren().addAll(paddle,ball);
paddle.setOnMouseDragged(e -> {
PaddlePane.paddle.setX(e.getX());
});
paddle.setOnKeyPressed(e -> {
switch(e.getCode()){
case LEFT: PaddlePane.paddle.setX(PaddlePane.paddle.getX() - 10); break;
case RIGHT: PaddlePane.paddle.setX(PaddlePane.paddle.getX() + 10); break;
default: break;
}
});
Scene scene = new Scene(stkPane, 300, 400);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
ball.requestFocus();
paddle.requestFocus();
}
public static void main(String[] args) {
launch(args);
}
}
BallPane.java
public class BallPane extends Pane {
private final double radius = 20;
private double x = radius, y = radius;
private double dx = 1, dy = 1;
private int numBallHitsPaddle = 0;
private int numBallMissPaddle = 0;
private final Circle circle = new Circle(x,y, radius);
private final Text text= new Text (x, y, Integer.toString(numBallHitsPaddle));
private final Timeline animation;
public BallPane(){
circle.setFill(Color.GREEN);
getChildren().addAll(circle, text);
animation = new Timeline(new KeyFrame(Duration.millis(5), e -> moveBall()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
}
public void play(){
animation.play();
}
public void pause(){
animation.pause();
}
public void increaseSpeed(){
animation.setRate(animation.getRate() + 0.1);
}
public void decreaseSpeed(){
animation.setRate(
animation.getRate() > 0 ? animation.getRate() - 0.1 : 0);
}
public void colorChange(){
circle.setFill(Color.color(Math.random(), Math.random(), Math.random()));
}
public DoubleProperty rateProperty(){
return animation.rateProperty();
}
public boolean ballHitsPaddle(){
boolean status = false;
if (x + radius > PaddlePane.getPaddleX() && x - radius < PaddlePane.getPaddleX() + PaddlePane.getPaddleWidth() && y + radius == PaddlePane.getPaddleY()){
status = true;
}
return status;
}
/**
* defines ball's movement
* changes color every time when ball hits paddle and counts the number of hit
*/
public void moveBall(){
if (x < radius || x > getWidth() - radius){
dx *= -1;
}
if ( y > getHeight() - radius){
x = getWidth() / 2;
y = 50;
dx *= -1;
numBallMissPaddle++;
}
if (y < radius || ballHitsPaddle()){
if(ballHitsPaddle()) {
text.setText(Integer.toString(++numBallHitsPaddle));
colorChange();
}
dy *= -1;
}
x += dx;
y += dy;
circle.setCenterX(x);
circle.setCenterY(y);
text.setX(x - 4);
text.setY(y + 3);
}
}
PaddlePane.java
public class PaddlePane extends Pane{
private static final double width = 40, hight = 5;
public static Rectangle paddle = new Rectangle(width, hight);
public PaddlePane(){
paddle.xProperty().bind(widthProperty().divide(2).subtract(width / 2));
paddle.yProperty().bind(heightProperty().subtract(100));
getChildren().add(paddle);
}
public static double getPaddleX(){
return paddle.getX();
}
public static double getPaddleY(){
return paddle.getY();
}
public static double getPaddleWidth(){
return paddle.getWidth();
}
public static double getPaddleHeight(){
return paddle.getHeight();
}
public void colorChange(){
paddle.setFill(Color.color(Math.random(), Math.random(), Math.random()));
}
}

Related

GameScreen shows Black Screen

I'm currently programming a game where you have to avoid asteroids. I had to deal with some messed up coordinates and I had to guess the coordinates for sprites. I now have arbitrary units that describe my world. Unfortunately, my game screen does not work fully. When I want to render my asteroids the game screen shows a black screen.
public class GameScreen extends Screen {
private OrthographicCamera cam;
private Spaceship spaceship;
private Asteroids asteroids;
private Background bg;
#Override
public void create() {
// TODO Auto-generated method stub
bg = new Background();
spaceship = new Spaceship();
asteroids = new Asteroids();
float aspectratio = 16/10;
cam = new OrthographicCamera(100, 100 * aspectratio);
cam.position.set(cam.viewportWidth / 2f, cam.viewportHeight / 2f, 0);
cam.update();
}
#Override
public void render(SpriteBatch batch) {
// TODO Auto-generated method stub
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
bg.render(batch);
spaceship.render(batch);
batch.end();
}
The shown code above works just fine and shows me this:
When I add the render method for the asteroids in the GameScreen class the GameScreen is just black:
#Override
public void render(SpriteBatch batch) {
// TODO Auto-generated method stub
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
bg.render(batch);
spaceship.render(batch);
batch.end();
}
Asteroid Class:
public class Asteroid {
private Vector2 p;
private Vector2 v;
private float mass;
private float radius;
public final float maxV = 200;
public final float minV = 50;
public final float rMin = 10;
public final float rMax = 30;
public final float minX = MyGdxGame.WIDTH;
public final float minY = MyGdxGame.HEIGHT;;
float alpha = MathUtils.random(0, 360);
public Asteroid(float maxX, float maxY, List<Asteroid> asteroids) {
do {
radius = MathUtils.random(rMin, rMax);
masse = radius * radius * radius;
float alpha = MathUtils.random(0, 360);
p = new Vector2(MathUtils.random(minX, maxX), MathUtils.random(minY,
maxY));
float vBetrag = MathUtils.random(minV, maxV);
v = new Vector2(vBetrag * MathUtils.cosDeg(alpha),
vBetrag * MathUtils.cosDeg(alpha));
} while (ueberlappMit(asteroids));
}
private boolean ueberlappMit(List<Asteroid> asteroids) {
for(Asteroid a: asteroids){
if(abstand(a) < radius + a.radius + 10){ //!
return true;
}
}
return false;
}
public void update(float deltaT, float xMin, float xMax, float yMin, float yMax) {
p.x += v.x * deltaT;
p.y += v.y * deltaT;
while(p.x > xMax)
{
p.x -= (xMax - xMin);
}
while(p.x < xMin)
{
p.x += (xMax - xMin);
}
while(p.y > yMax)
{
p.y -= (yMax - yMin);
}
while(p.y < yMin)
{
p.y += (yMax - yMin);
}
}
public float abstand(Asteroid a2) {
return p.dst(a2.p);
}
Asteroids Class:
public class Asteroids extends Entity {
private final int numberofAsteroids = 150;
private float xMin, xMax, yMin, yMax;
private List<Asteroid> asteroids = new ArrayList<Asteroid>();
private final int cyclicBoundaryConditionsMultiple = 2;
public Asteroids() {
xMin = MyGdxGame.WIDTH * (-cyclicBoundaryConditionsMultiple);
xMax = MyGdxGame.WIDTH * (cyclicBoundaryConditionsMultiple);
yMin = MyGdxGame.HEIGHT * (-cyclicBoundaryConditionsMultiple);
yMax = MyGdxGame.HEIGHT * (cyclicBoundaryConditionsMultiple);
for (int i = 0; i < anzahl; i++) {
Asteroid a = new Asteroid( xMax, yMax, asteroids);
asteroids.add(a);
}
}
#Override
public void update() {
for (Asteroid a : asteroids) {
a.update(Gdx.graphics.getDeltaTime(), xMin, xMax, yMin, yMax);
}
for (int i = 0; i < numberofAsteroids; i++) {
Asteroid a1 = asteroids.get(i);
for (int j = i + 1; j < anzahl; j++) {
Asteroid a2 = asteroids.get(j);
float abstand = a1.abstand(a2);
if (abstand < a1.getRadius() + a2.getRadius()) {
calculateCollision(a1, a2);
}
}
}
}
}
#Override
public void render(ShapeRenderer renderer) {
for (Asteroid a : asteroids) {
System.out.println("RENDER A");
renderer.setColor(0, 0, 0, 1);
renderer.circle(a.getP().x, a.getP().y, a.getRadius());
}
}
Thx alot for your help Guys, i solved the problem. The method private boolean ueberlappMit(List asteroids) checks if the asteroids overlap and if its the case the Asteroids shoud be created again. The Problem was that by selecting a too high radius the Game got stuck in the do while loop in the Asteroid class.

Java Eclipse game - restart game

I have made a game in Eclipse neon with the language Java, but I was wondering how I can make the game so that if the user presses r he or she could go back to the first screen where it says press spacebar to start. here are my 3 files of code if you need it to figure out the solution.
//This is the game file (main)
import processing.core.PApplet;
public class Game extends PApplet{
public static void main(String[] args) {
// TODO Auto-generated method stub
PApplet.main("Game");
}
James james1;
Wall wall1;
Wall wall2;
Wall wall3;
public void settings() {
size(500, 700);
}
public void setup() {
frameRate(40);
james1 = new James(this, 250, 650, 50);
wall1 = new Wall(this, 0);
wall2 = new Wall(this, 250);
wall3 = new Wall(this, -250);
}
public void draw() {
if(key == ' ') {
clear();
background(0, 255, 255);
boolean loser = wall1.col(james1.getX(), james1.getY()) || wall2.col(james1.getX(), james1.getY()) ||wall3.col(james1.getX(), james1.getY());
james1.show();
move();
if (!loser){
wall1.update();
wall2.update();
wall3.update();
}
else {
reset();
}
wall1.display();
wall2.display();
wall3.display();
}
else {
background(255, 0, 255);
textSize(45);
text("Press Space to Start!", 50, 350);
}
}
public void reset() {
clear();
frameRate(0);
textSize(40);
text("You Lose, R to restart", 25, 550);
}
public void restart() {
}
public void move() {
james1.setX(mouseX);
if (mouseX <= 25) {
james1.setX(25);
}
else if (mouseX >= 475) {
james1.setX(475);
}
}
}
//This is the character file (James)
import processing.core.PApplet;
public class James {
PApplet p;
private float x;
private float y;
private float size;
public James(PApplet np, float nx, float ny, float nsize) {
p = np;
x = nx;
y = ny;
size = nsize;
}
public void show() {
p.fill(255,20,147);
p.ellipse(x, y, size, size);
p.fill(128,128,0);
p.ellipse(x - 10, y - 10, size - 30, size - 30);
p.ellipse(x + 10, y - 10, size -30, size - 30);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getSize() {
return size;
}
public void setX(float gx) {
x = gx;
}
public void setY(float gy) {
y = gy;
}
public void setSize(float gSize) {
size = gSize;
}
}
//This is obstacle class file (walls)
import processing.core.PApplet;
public class Wall {
PApplet p;
private float x;
private float y;
private float w;
private float xw;
private float yw;
private float h;
private float wh;
private float xspeed;
private float yspeed;
public Wall (PApplet np, float ny) {
p = np;
x = 0;
y = ny;
wh = 0;
xw = p.random(0, 450);
yw = p.random(0, 450);
w = p.random(0, 450);
h = 30;
yspeed = 10;
}
public void display() {
p.rect(0, y, w, h);
p.rect(w + 100, y, 500-(w+100), h);
}
public void update() {
if(y > p.height) {
y = 0;
w = p.random(0,450);
xw = p.random(0, 450);
yw = p.random(0, 450);
}
y = yspeed + y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getW() {
return w;
}
public float getXW() {
return xw;
}
public float getYW() {
return yw;
}
public float getWH() {
return wh;
}
public float getH() {
return h;
}
public float getxSpeed() {
return xspeed;
}
public float getySpeed() {
return yspeed;
}
public void setX(float gx) {
x = gx;
}
public void setYW(float gyw) {
yw = gyw;
}
public void setW(float gw) {
w = gw;
}
public void setY(float gy) {
y = gy;
}
public void setH(float gh) {
h = gh;
}
public void setxSpeed(float gxSpeed) {
xspeed = gxSpeed;
}
public void setySpeed(float gySpeed) {
yspeed = gySpeed;
}
public boolean col(float ballX, float ballY) {
if (y + h >= ballY - 25 && y <= ballY + 25 ) {
if(ballX - 25 <= w || ballX+25 >= w+100) {
return true;
}
}
return false;
}
}
Hope that wasn't too long for just one goal ;)
Copy everything from method setup to method restart and then call restart from method setup.
You get something like this:
public void setup()
{
restart();
}
public void restart()
{
frameRate(40);
james1 = new James(this, 250, 650, 50);
wall1 = new Wall(this, 0);
wall2 = new Wall(this, 250);
wall3 = new Wall(this, -250);
}
Now just call restart whenever you want the game to restart. In your example: when the user presses the 'r' button.
In games, it really is a good idea to have a Map class that handles these kind of things.
You can just basically copy setup and paste it with a new method name, or even use that same method to make it so when pressed "r", it just calls that method back again. But just for it to be cleaner and much more readable. you can do this.
public void setup() {
if(key.r) { //of course this key.r will have to be created as a KeyListener.
restart();
}
}
public void restart() {
frameRate(40);
james1 = new James(this, 250, 650, 50);
wall1 = new Wall(this, 0);
wall2 = new Wall(this, 250);
wall3 = new Wall(this, -250);
}
You can either create a new class for the KeyInput or do it in the same class, but it should be a little like this:
public boolean[] keys = new boolean[120];
public boolean r;
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
f3 = (key == KeyEvent.VK_F3);
}
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
public void keyTyped(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
}
Have a nice day!
:D

Java JFrame Bouncing Ball Physics Simulation- Ball Motion Conflicts Upon Creation of Creation

So, right now, I'm able to create multiple balls and update them all properly through a Vector of objects and a for loop to update each object independently. The problem is that for every ball after the first, the ball that is created seems to affect the momentum and position of the other balls, causing all the balls to abruptly change their flight paths.
Code for creating frame and managing ball creation based on mouselistener:
public class FrameCreation extends JFrame{
static Vector<BallCreate> ballObjects = new Vector<BallCreate>();
static Timer timer;
Point m1;
Point m2;
static JFrame frame1;
static mouseHandler mouse;
public static void main(String args[]){
FrameCreation frame = new FrameCreation();
frame1 = new JFrame("Phyiscs Test");
frame1.setVisible(true);
frame1.setSize(520,530);
frame1.setDefaultCloseOperation(frame1.EXIT_ON_CLOSE);
mouse = frame.new mouseHandler();
frame1.addMouseListener(mouse);
}
public class mouseHandler extends JPanel implements MouseListener{
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
m1 = e.getPoint();
System.out.println("Mouse pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
m2 = e.getPoint();
Thread queryThread = new Thread(){
double vX = m2.getX() - m1.getX();
double vY = m2.getY() - m1.getY();
public void run(){
createBall(m1.getX(),m1.getY(),vX,vY);
}
};
queryThread.start();
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
public void createBall(double posX, double posY, double vX, double vY){
BallCreate newBall = new BallCreate(posX,posY,50,vX,vY);
ballObjects.add(newBall);
frame1.add(newBall);
frame1.revalidate();
newBall.repaint();
}
}
Code for drawing and updating balls:
public class BallCreate extends JPanel {
Timer timer;
int diam;
Color color;
double vX, vY, posX, posY;
final double G = 30;
public BallCreate(double posX, double posY, int diam, double vX, double vY){
this.posX = posX;
this.posY = posY;
this.vX = vX;
this.vY = vY;
this.diam = diam;
color = getRandomColor();
timer = new Timer(100, new MovementUpdate());
timer.start();
}
public void drawing(){
repaint();
}
public void motionUpdate(){
for(BallCreate ball : FrameCreation.ballObjects){
double t = 0.1;
double dX = ball.vX*t;
double dY = (ball.vY + G*t)*t;
double v2Y = G*t + ball.vY;
System.out.println("Ball v2y: " + ball.vY);
ball.posX = ball.posX + dX;
ball.posY = ball.posY + dY;
vY = v2Y;
drawing();
wallCollisionCheck();
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for(BallCreate ball : FrameCreation.ballObjects){
g.setColor(ball.color);
//System.out.println("pos X: " + posX);
//System.out.println("pos Y: " + posY);
g.fillOval((int)ball.posX, (int)ball.posY,
ball.diam ,ball.diam);
}
}
public void wallCollisionCheck(){
for(BallCreate ball : FrameCreation.ballObjects){
double botBound = 500-(ball.posY + ball.diam);
if((botBound <=0 && vY>=0)|| (ball.posY <=0 && ball.vY <=0 )){
//System.out.println("Collision Noted");
//System.out.println("Prev vY: " + ball.vY);
ball.vY = ball.vY * -0.65;
//System.out.println("Post vY: " + ball.vY);
ball.posY = 460;
}
if((ball.posX <= 0 && ball.vX <= 0) || ((Math.abs(500 - ball.posX) <= 40)&& ball.vX >=0)){
ball.vX = ball.vX * -0.65;
}
drawing();
}
}
public Color getRandomColor(){
int R = (int)(255*Math.random());
int G = (int)(255*Math.random());
int B = (int)(255*Math.random());
Color c = new Color(R, G , B);
return c;
}
public class MovementUpdate implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
for(BallCreate ball : FrameCreation.ballObjects){
ball.motionUpdate();
}
}
}
}
I realize that the problem is slightly vague, but it's simply hard to describe without being able to show an image of what is happening. Any help is appreciated, and clarification of code can be done if necessary. Thanks.

JavaFX Circle must follow cursor

I am trying to build a game myself using JavaFX. It features a circle that must follow my cursor (using a smooth translation animation, so it will not go directly to the location of my cursor)
For now I have written this piece of code
root.setOnMouseMoved(event -> {
TranslateTransition tt = new TranslateTransition(Duration.millis(2000), circle);
x[0] = event.getSceneX();
y[0] = event.getSceneY();
location.setText(x[0] + ", " + y[0]);
if (x[0] != oldX[0] || y[0] != oldY[0]) {
tt.stop();
tt.setToX(event.getSceneX());
tt.setToY(event.getSceneY());
oldX[0] = x[0];
oldY[0] = y[0];
}
tt.play();
});
The location.setText(..) is just a label to see whether the coords are recognized by the program. And in fact they are: for each pixel my cursor moves it updated these numbers instantly.
However, my circle will only go to the location of my cursor when it does not move. I want the shape to follow my cursor on the go as well but it just won't.
So my problem is this: how can I make my circle follow my mouse, even when it is moving?
Use an AnimationTimer for the movement. You may also want to check Mike's Blog about a usage example.
Read The Nature of Code by Daniel Shiffman, especially the Chapter Vectors, 1.10 Interactivity with Acceleration. The webpage has a running example, easily to convert to JavaFX.
Here's the code from the book implemented in JavaFX:
Walker.java
import java.util.Random;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
public class Walker extends Pane {
private static Random random = new Random();
PVector location;
PVector velocity;
PVector acceleration;
float topspeed;
double width = 30;
double height = width;
double centerX = width / 2.0;
double centerY = height / 2.0;
double radius = width / 2.0;
Circle circle;
public Walker() {
location = new PVector(random.nextDouble() * width, random.nextDouble() * height, 0);
velocity = new PVector(0, 0, 0);
topspeed = 4;
circle = new Circle(radius);
circle.setCenterX(radius);
circle.setCenterY(radius);
circle.setStroke(Color.BLUE);
circle.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.3));
getChildren().add(circle);
}
public void step(PVector mouse) {
PVector dir = PVector.sub(mouse, location);
dir.normalize();
dir.mult(0.5);
acceleration = dir;
velocity.add(acceleration);
velocity.limit(topspeed);
location.add(velocity);
}
public void checkBoundaries() {
if (location.x > Settings.SCENE_WIDTH) {
location.x = 0;
} else if (location.x < 0) {
location.x = Settings.SCENE_WIDTH;
}
if (location.y > Settings.SCENE_HEIGHT) {
location.y = 0;
} else if (location.y < 0) {
location.y = Settings.SCENE_HEIGHT;
}
}
public void display() {
relocate(location.x - centerX, location.y - centerY);
}
}
Main.java
import java.util.ArrayList;
import java.util.List;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
Pane playfield;
List<Walker> allWalkers = new ArrayList<>();
PVector mouse = new PVector(0,0,0);
#Override
public void start(Stage primaryStage) {
// create containers
BorderPane root = new BorderPane();
StackPane layerPane = new StackPane();
// playfield for our walkers
playfield = new Pane();
layerPane.getChildren().addAll(playfield);
root.setCenter(layerPane);
Scene scene = new Scene(root, Settings.SCENE_WIDTH, Settings.SCENE_HEIGHT);
primaryStage.setScene(scene);
primaryStage.show();
// add 1 walker
addWalker();
// capture mouse position
scene.addEventFilter(MouseEvent.ANY, e -> {
mouse.set(e.getX(), e.getY(), 0);
});
// process all walkers
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
// move
allWalkers.forEach((walker) -> walker.step(mouse));
// check border
allWalkers.forEach(Walker::checkBoundaries);
// update in fx scene
allWalkers.forEach(Walker::display);
}
};
loop.start();
}
/**
* Add single walker to list of walkers and to the playfield
*/
private void addWalker() {
Walker walker = new Walker();
allWalkers.add(walker);
playfield.getChildren().add(walker);
}
public static void main(String[] args) {
launch(args);
}
}
Settings.java
public class Settings {
public static double SCENE_WIDTH = 800;
public static double SCENE_HEIGHT = 600;
}
PVector.java (you can get the full source from the Processing source code)
public class PVector {
public double x;
public double y;
public double z;
public PVector(double x, double y, double z) {
super();
this.x = x;
this.y = y;
this.z = z;
}
public void normalize() {
double m = mag();
if (m != 0 && m != 1) {
div(m);
}
}
public void div(double value) {
x /= value;
y /= value;
z /= value;
}
public void mult(double value) {
x *= value;
y *= value;
z *= value;
}
public void add(PVector v) {
x += v.x;
y += v.y;
z += v.z;
}
public void sub(PVector v) {
x -= v.x;
y -= v.y;
z -= v.z;
}
public void limit(float max) {
if (mag() > max) {
normalize();
mult(max);
}
}
public double mag() {
return Math.sqrt(x * x + y * y + z * z);
}
public static PVector sub(PVector v1, PVector v2) {
return sub(v1, v2, null);
}
public static PVector sub(PVector v1, PVector v2, PVector target) {
if (target == null) {
target = new PVector(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
} else {
target.set(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
return target;
}
public void set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
So using the tips Roland gave me I have created this piece of code which works just the way I want to be (like I described in my question)
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
TranslateTransition tt = new TranslateTransition(Duration.millis(250), circle);
Point mouse = MouseInfo.getPointerInfo().getLocation();
x[0] = mouse.getX();
y[0] = mouse.getY();
location.setText(x[0] + ", " + y[0]);
tt.setToX(x[0]);
tt.setToY(y[0]);
tt.play();
}
};
timer.start();
The major change is the use of the AnimationTimer instead of an event. This caused me to change the way I retrieve the location of the mouse, now I use the awt.MouseInfo to get the X and Y of my cursor.

Javafx canvas not clearing correctly.

I've written a java implementation of Craig Reynolds Boids. I recently updated each object to be represented by a .png image. Ever since I've been having the display issue in the image.
What's the best way to fix the issue?
I've tried using a Polygon but when one of my coordinates is a negative the triangle doesn't display properly.
Main Class:
public void paint(final GraphicsContext g) {
new AnimationTimer() {
#Override
public void handle(long now) {
flock.updateBoidsPostion();
g.clearRect(0, 0, width, height);
flock.drawBoids(g);
}
}.start();
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Boids Flocking Algorithm");
Group root = new Group();
Canvas canvas = new Canvas(width, height);
GraphicsContext gc = canvas.getGraphicsContext2D();
root.getChildren().add(canvas);
primaryStage.setScene(new Scene(root));
primaryStage.show();
paint(gc);
}
Flock:
/**
* Paint each boid comprising the flock the canvas.
* #param g
*/
public void drawBoids(GraphicsContext g) {
for(Boid aBoid : boids) {
aBoid.draw(g);
}
}
Boid:
public void draw(GraphicsContext g) {
//coordinates for the tip of the boid
int x = (int)this.position.xPos;
int y = (int)this.position.yPos;
//Calculate a angle representing the direction of travel.
Rotate r = new Rotate(angle, x, y);
g.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy());
g.drawImage(image, x, y);
}
The problem with your code is that you rotate the GraphicsContext, not the image. Or at least you don't rotate the GraphicsContext back after you rotated it.
I was curious about the link you mentioned, i. e. the Boids Pseudocode.
Here's a quick implementation. Drag the rectangle to have the flock follow it.
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
// Boids implementation in JavaFX
// Pseudo code by Conrad Parker: http://www.kfish.org/boids/pseudocode.html
public class Main extends Application {
int numBoids = 50;
double boidRadius = 10d;
double boidMinDistance = boidRadius * 2d + 5; // +5 = arbitrary value
double initialBaseVelocity = 1d;
double velocityLimit = 3d;
double movementToCenter = 0.01; // 1% towards center
List<Boid> boids;
static Random rnd = new Random();
double sceneWidth = 1024;
double sceneHeight = 768;
Pane playfield;
Rectangle rectangle;
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
playfield = new Pane();
playfield.setPrefSize(sceneWidth, sceneHeight);
Text infoText = new Text( "Drag the rectangle and have the flock follow it");
root.setTop(infoText);
root.setCenter(playfield);
Scene scene = new Scene(root, sceneWidth, sceneHeight, Color.WHITE);
//scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
// create boids
createBoids();
// add boids to scene
playfield.getChildren().addAll(boids);
double w = 20;
double h = 20;
rectangle = new Rectangle( w, h);
rectangle.relocate(sceneWidth / 2 - w/2, sceneHeight / 4 - h/2);
playfield.getChildren().add(rectangle);
MouseGestures mg = new MouseGestures();
mg.makeDraggable(rectangle);
// animation loop
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
boids.forEach(Boid::move);
boids.forEach(Boid::updateUI);
}
};
loop.start();
}
private void createBoids() {
boids = new ArrayList<>();
// margin from top/left/bottom/right, so we have the boids initially more in the center
double marginX = sceneWidth / 4;
double marginY = sceneHeight / 4;
for (int i = 0; i < numBoids; i++) {
// random position around the center
double x = rnd.nextDouble() * (sceneWidth - marginX * 2) + marginX;
double y = rnd.nextDouble() * (sceneHeight - marginY * 2) + marginY;
// initial random velocity depending on speed
double v = Math.random() * 4 + initialBaseVelocity;
Boid boid = new Boid(i, x, y, v);
boids.add(boid);
}
}
// Rule 1: Boids try to fly towards the centre of mass of neighbouring boids.
public Point2D rule1(Boid boid) {
Point2D pcj = new Point2D(0, 0);
for( Boid neighbor: boids) {
if( boid == neighbor)
continue;
pcj = pcj.add( neighbor.position);
}
if( boids.size() > 1) {
double div = 1d / (boids.size() - 1);
pcj = pcj.multiply( div);
}
pcj = (pcj.subtract(boid.position)).multiply( movementToCenter);
return pcj;
}
// Rule 2: Boids try to keep a small distance away from other objects (including other boids).
public Point2D rule2(Boid boid) {
Point2D c = new Point2D(0, 0);
for( Boid neighbor: boids) {
if( boid == neighbor)
continue;
double distance = (neighbor.position.subtract(boid.position)).magnitude();
if( distance < boidMinDistance) {
c = c.subtract(neighbor.position.subtract(boid.position));
}
}
return c;
}
// Rule 3: Boids try to match velocity with near boids.
public Point2D rule3(Boid boid) {
Point2D pvj = new Point2D(0, 0);
for( Boid neighbor: boids) {
if( boid == neighbor)
continue;
pvj = pvj.add( neighbor.velocity);
}
if( boids.size() > 1) {
double div = 1d / (boids.size() - 1);
pvj = pvj.multiply( div);
}
pvj = (pvj.subtract(boid.velocity)).multiply(0.125); // 0.125 = 1/8
return pvj;
}
// tend towards the rectangle
public Point2D tendToPlace( Boid boid) {
Point2D place = new Point2D( rectangle.getBoundsInParent().getMinX() + rectangle.getBoundsInParent().getWidth() / 2, rectangle.getBoundsInParent().getMinY() + rectangle.getBoundsInParent().getHeight() / 2);
return (place.subtract(boid.position)).multiply( 0.01);
}
public class Boid extends Circle {
int id;
Point2D position;
Point2D velocity;
double v;
// random color
Color color = randomColor();
public Boid(int id, double x, double y, double v) {
this.id = id;
this.v = v;
position = new Point2D( x, y);
velocity = new Point2D( v, v);
setRadius( boidRadius);
setStroke(color);
setFill(color.deriveColor(1, 1, 1, 0.2));
}
public void move() {
Point2D v1 = rule1(this);
Point2D v2 = rule2(this);
Point2D v3 = rule3(this);
Point2D v4 = tendToPlace(this);
velocity = velocity
.add(v1)
.add(v2)
.add(v3)
.add(v4)
;
limitVelocity();
position = position.add(velocity);
constrainPosition();
}
private void limitVelocity() {
double vlim = velocityLimit;
if( velocity.magnitude() > vlim) {
velocity = (velocity.multiply(1d/velocity.magnitude())).multiply( vlim);
}
}
// limit position to screen dimensions
public void constrainPosition() {
double xMin = boidRadius;
double xMax = sceneWidth - boidRadius;
double yMin = boidRadius;
double yMax = sceneHeight - boidRadius;
double x = position.getX();
double y = position.getY();
double vx = velocity.getX();
double vy = velocity.getY();
if( x < xMin) {
x = xMin;
vx = v;
}
else if( x > xMax) {
x = xMax;
vx = -v;
}
if( y < yMin) {
y = yMin;
vy = v;
}
else if( y > yMax) {
y = yMax;
vy = -v;
}
// TODO: modification would be less performance consuming => find out how to modify the vector directly or create own Poin2D class
position = new Point2D( x, y);
velocity = new Point2D( vx, vy);
}
public void updateUI() {
setCenterX(position.getX());
setCenterY(position.getY());
}
}
public static Color randomColor() {
int range = 220;
return Color.rgb((int) (rnd.nextDouble() * range), (int) (rnd.nextDouble() * range), (int) (rnd.nextDouble() * range));
}
public static class MouseGestures {
class DragContext {
double x;
double y;
}
DragContext dragContext = new DragContext();
public void makeDraggable( Node node) {
node.setOnMousePressed( onMousePressedEventHandler);
node.setOnMouseDragged( onMouseDraggedEventHandler);
node.setOnMouseReleased( onMouseReleasedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if( event.getSource() instanceof Circle) {
Circle circle = ((Circle) (event.getSource()));
dragContext.x = circle.getCenterX() - event.getSceneX();
dragContext.y = circle.getCenterY() - event.getSceneY();
} else {
Node node = ((Node) (event.getSource()));
dragContext.x = node.getTranslateX() - event.getSceneX();
dragContext.y = node.getTranslateY() - event.getSceneY();
}
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if( event.getSource() instanceof Circle) {
Circle circle = ((Circle) (event.getSource()));
circle.setCenterX( dragContext.x + event.getSceneX());
circle.setCenterY( dragContext.y + event.getSceneY());
} else {
Node node = ((Node) (event.getSource()));
node.setTranslateX( dragContext.x + event.getSceneX());
node.setTranslateY( dragContext.y + event.getSceneY());
}
}
};
EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
}
};
}
public static void main(String[] args) {
launch(args);
}
}
A 3D version is just a matter of using Points3D instead of Points2D and Spheres and Boxes instead of Circles and Rectangles.
I also suggest you read the excellent book The Nature of Code by Daniel Shiffman, especially the chapter Autonomous Agents. It deals in detail with the Boids.

Categories