Why wont my ball object continously move up when i press space? - java

I am trying to recreate the mini game shooting gallery from Zelda: A link to the past with a triangle polygon that acts as the player and a circle that acts as the projectile and when the player presses space the projectile moves up at a quick speed, but my setOnKeyPressed method does not work, what am i missing?
Method spoken about
scene.setOnKeyPressed(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
Command movement = keyCommands.get(event.getCode());
if (movement != null){
movement.execute(player);
}
Command shoot = keyCommandsBall.get(event.getCode());
if (shoot != null){
shoot.execute(ball);
}
}
});
This is my main method
package videogameclass;
import java.util.HashMap;
import java.util.Map;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import static javafx.scene.paint.Color.color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
/**
*
* #author
*/
public class VideoGameClass extends Application {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
launch(args);
}
#Override
public void start(Stage stage) throws Exception
{
stage.setTitle("Target practice");
Group root = new Group();
Scene scene = new Scene(root, 320, 240);
stage.setScene(scene);
Circle circle1 = new Circle(20,20,10);
Sprite c = new Sprite(circle1);
circle1.setFill(Color.RED);
root.getChildren().add(c.getNode());
Circle circle2 = new Circle(80, 20, 10);
Sprite c2 = new Sprite(circle2);
circle2.setFill(Color.RED);
root.getChildren().add(c2.getNode());
Circle circle3 = new Circle(140, 20, 10);
Sprite c3 = new Sprite(circle3);
circle3.setFill(Color.RED);
root.getChildren().add(c3.getNode());
Rectangle rectangle = new Rectangle(15,40,25,10);
Sprite rec = new Sprite(rectangle);
rectangle.setFill(Color.BLACK);
root.getChildren().add(rec.getNode());
Rectangle rectangle2 = new Rectangle(75,40,25,10);
Sprite rec2 = new Sprite(rectangle2);
rectangle2.setFill(Color.BLACK);
root.getChildren().add(rec2.getNode());
Rectangle rectangle3 = new Rectangle(135,40,25,10);
Sprite rec3 = new Sprite(rectangle3);
rectangle3.setFill(Color.BLACK);
root.getChildren().add(rec3.getNode());
Rectangle rectangle4 = new Rectangle(195,40,25,10);
Sprite rec4 = new Sprite(rectangle4);
rectangle4.setFill(Color.BLACK);
root.getChildren().add(rec4.getNode());
Polygon t = new Polygon();
Sprite player = new Sprite(t);
root.getChildren().add(player.getNode());
t.getPoints().addAll(new Double[]{
25.0, 20.0,
45.0, 20.0,
35.0, 5.0,
25.0,20.0,
});
t.setTranslateX(125);
t.setTranslateY(200);
Circle circle4 = new Circle(t.getTranslateX()+35,t.getTranslateY(),5);
Sprite ball = new Sprite(circle4);
root.getChildren().add(ball.getNode());
Command moveLeft = new Command()
{
#Override
public void execute(Sprite sprite)
{
sprite.setVelocity(-2, 0);
//sprite.update();
}
};
Command moveRight = new Command()
{
#Override
public void execute(Sprite sprite)
{
sprite.setVelocity(2, 0);
//sprite.update();
}
};
Command moveUp = new Command(){
#Override
public void execute(Sprite sprite){
sprite.setVelocity(0,-2);
}
};
Command stopMoving = new Command()
{
#Override
public void execute(Sprite sprite)
{
sprite.setVelocity(0,0);
}
};
Map<KeyCode,Command> keyCommands = new HashMap<>();
keyCommands.put(KeyCode.LEFT, moveLeft);
keyCommands.put(KeyCode.RIGHT, moveRight);
Map<KeyCode,Command> keyCommandsBall = new HashMap<>();
keyCommands.put(KeyCode.SPACE, moveUp);
Map<KeyCode,Command> keyCommands2 = new HashMap<>();
keyCommands.put(KeyCode.F19, moveLeft);
keyCommands.put(KeyCode.F18, moveRight);
AnimationTimer animation = new AnimationTimer()
{
#Override
public void handle(long now){
c.setVelocity(1.0, 0);
c2.setVelocity(1.0,0);
c3.setVelocity(1.0,0);
ball.setVelocity(0, 0);
rec.setVelocity(-2.0,0);
rec2.setVelocity(-2.0,0);
rec3.setVelocity(-2.0,0);
rec4.setVelocity(-2.0,0);
c.update();
c2.update();
c3.update();
ball.update();//projectile
rec.update();
rec2.update();
rec3.update();
rec4.update();
player.update();
//move sprites
//check and handle collisions
if(c3.getNode().getTranslateX() > scene.getWidth() - 120){
c3.getNode().setTranslateX(-140);
}
if(c2.getNode().getTranslateX() > scene.getWidth() - 75){
c2.getNode().setTranslateX(c3.getNode().getTranslateX()-20);
}
if(c.getNode().getTranslateX() > scene.getWidth() - 10){
c.getNode().setTranslateX(c2.getNode().getTranslateX()-20);
}
if (rec.getNode().getTranslateX() < -40){
rec.getNode().setTranslateX(300);
}
if(rec2.getNode().getTranslateX() < -100){
rec2.getNode().setTranslateX(rec.getNode().getTranslateX()+30);
}
if(rec3.getNode().getTranslateX() < -135){
rec3.getNode().setTranslateX(rec2.getNode().getTranslateX()+30);
}
if(rec4.getNode().getTranslateX() < -225){
rec4.getNode().setTranslateX(rec3.getNode().getTranslateX()+30);
}
if(ball.getNode().getBoundsInParent().intersects(rec.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(rec2.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(rec3.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(rec4.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(c.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(c2.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
if(ball.getNode().getBoundsInParent().intersects(c3.getNode().getBoundsInParent())){
ball.setVelocity(0, 0);
}
}//end of handle
};
scene.setOnKeyPressed(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
Command movement = keyCommands.get(event.getCode());
if (movement != null){
movement.execute(player);
}
Command shoot = keyCommandsBall.get(event.getCode());
if (shoot != null){
shoot.execute(ball);
}
}
});
scene.setOnKeyReleased(new EventHandler<KeyEvent>()
{
#Override
public void handle(KeyEvent event)
{
Command movement = keyCommands2.get(event.getCode());
Command stop = stopMoving;
if (movement == null){
stop.execute(player);
}
}
});
animation.start();
stage.show();
}
}
This is my Sprite class
package videogameclass;
import javafx.scene.Node;
/**
*
* #author
*/
public class Sprite {
private final Node node;
private double dx, dy;
public Sprite(Node node){
this.node = node;
}
public Node getNode(){
return node;
}
public void setVelocity(double dx, double dy){
this.dx = dx;
this.dy = dy;
}
public double getVelocityX(){
return dx;
}
public double getVelocityY(){
return dy;
}
public void update(){
this.node.setTranslateX(this.node.getTranslateX()+dx);
this.node.setTranslateY(this.node.getTranslateY()+dy);
}
}
This is my Command method
package videogameclass;
public interface Command
{
public void execute(Sprite sprite);
}

Because there is nothing added to keyCommandsBall map.
I am not sure if this is a typo mistake, but I can sense this part of the code is wrong. All the commands added to keyCommands map only.
Map<KeyCode,Command> keyCommands = new HashMap<>();
keyCommands.put(KeyCode.LEFT, moveLeft);
keyCommands.put(KeyCode.RIGHT, moveRight);
Map<KeyCode,Command> keyCommandsBall = new HashMap<>();
keyCommands.put(KeyCode.SPACE, moveUp);
Map<KeyCode,Command> keyCommands2 = new HashMap<>();
keyCommands.put(KeyCode.F19, moveLeft);
keyCommands.put(KeyCode.F18, moveRight);
Update:
I felt a bit interesting in this game so I tried to put my hands in this. Indeed this was quite fun when playing ;)
But few suggestions for you from your code:
Try to reuse to code by either keeping things in loop or by calling a common method. That way you can reduce the code drastically.
Try to create constants for the fixed sizes and positions. That way you accidentally don't get messed up with some wrong values. (Though I have not included that in my example)
When providing a minimal example, try to keep all the code in a single class with all imports (even if it has multiple classes or interfaces). The main aim should be that others should directly copy the class and should be able to run the program.
Below is the output of the code that I worked on. (There is still so much scope to optimize this)
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class VideoGameClass extends Application {
private List<Sprite> circles = new ArrayList<>();
private List<Sprite> rectangles = new ArrayList<>();
private List<Sprite> all = new ArrayList<>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Group root = new Group();
Scene scene = new Scene(root, 320, 240, Color.BLACK);
stage.setTitle("Target practice");
stage.setScene(scene);
Sprite c1 = createCircle(20, 20);
Sprite c2 = createCircle(80, 20);
Sprite c3 = createCircle(140, 20);
Sprite rec1 = createRectangle(15);
Sprite rec2 = createRectangle(75);
Sprite rec3 = createRectangle(135);
Sprite rec4 = createRectangle(195);
Polygon t = new Polygon();
t.getPoints().addAll(new Double[]{
25.0, 20.0,
45.0, 20.0,
35.0, 5.0,
25.0, 20.0,
});
t.setFill(Color.WHITE);
t.setTranslateX(125);
t.setTranslateY(200);
Sprite player = new Sprite(t);
all.add(player);
Sprite ball = new Sprite(new Circle(t.getTranslateX() + 35, t.getTranslateY(), 5, Color.WHITE));
all.add(ball);
all.stream().map(Sprite::getNode).forEach(n -> root.getChildren().add(n));
Command moveLeft = sprite -> sprite.setVelocity(-2, 0);
Command moveRight = sprite -> sprite.setVelocity(2, 0);
Command moveUp = sprite -> sprite.setVelocity(0, -2);
Command stopMoving = sprite -> sprite.setVelocity(0, 0);
Map<KeyCode, Command> keyCommands = new HashMap<>();
keyCommands.put(KeyCode.LEFT, moveLeft);
keyCommands.put(KeyCode.RIGHT, moveRight);
Map<KeyCode, Command> keyCommandsBall = new HashMap<>();
keyCommandsBall.put(KeyCode.SPACE, moveUp);
Map<KeyCode, Command> keyCommands2 = new HashMap<>();
keyCommands.put(KeyCode.F11, moveLeft);
keyCommands.put(KeyCode.F12, moveRight);
AnimationTimer animation = new AnimationTimer() {
#Override
public void handle(long now) {
circles.forEach(c -> c.setVelocity(1.0, 0));
rectangles.forEach(r -> r.setVelocity(-2.0, 0));
all.forEach(Sprite::update);
//check and handle collisions
if (c3.getNode().getTranslateX() > scene.getWidth() - 120) {
c3.getNode().setTranslateX(-140);
}
if (c2.getNode().getTranslateX() > scene.getWidth() - 75) {
c2.getNode().setTranslateX(c3.getNode().getTranslateX() - 20);
}
if (c1.getNode().getTranslateX() > scene.getWidth() - 10) {
c1.getNode().setTranslateX(c2.getNode().getTranslateX() - 20);
}
if (rec1.getNode().getTranslateX() < -40) {
rec1.getNode().setTranslateX(300);
}
if (rec2.getNode().getTranslateX() < -100) {
rec2.getNode().setTranslateX(rec1.getNode().getTranslateX() + 30);
}
if (rec3.getNode().getTranslateX() < -135) {
rec3.getNode().setTranslateX(rec2.getNode().getTranslateX() + 30);
}
if (rec4.getNode().getTranslateX() < -225) {
rec4.getNode().setTranslateX(rec3.getNode().getTranslateX() + 30);
}
// Check for collision and stop the ball
Stream.of(circles, rectangles).flatMap(List::stream).forEach(sprite -> {
if (ball.getNode().getBoundsInParent().intersects(sprite.getNode().getBoundsInParent())) {
ball.setVelocity(0, 0);
}
});
}//end of handle
};
scene.setOnKeyPressed(event -> {
Command movement = keyCommands.get(event.getCode());
if (movement != null) {
movement.execute(player);
}
Command shoot = keyCommandsBall.get(event.getCode());
if (shoot != null) {
shoot.execute(ball);
}
});
scene.setOnKeyReleased(event -> {
Command movement = keyCommands2.get(event.getCode());
Command stop = stopMoving;
if (movement == null) {
stop.execute(player);
}
// Bring back the ball to the player
if(event.getCode() == KeyCode.Z){
ball.setVelocity(0, 0);
Circle ballNode = (Circle) ball.getNode();
ballNode.setTranslateX(0);
ballNode.setTranslateY(0);
ballNode.setCenterX(player.getNode().getTranslateX() + 35);
ballNode.setCenterY(player.getNode().getTranslateY());
}
});
animation.start();
stage.show();
}
private Sprite createCircle(double centerX, double centerY) {
Sprite circle = new Sprite(new Circle(centerX, centerY, 10, Color.RED));
circles.add(circle);
all.add(circle);
return circle;
}
private Sprite createRectangle(double x) {
Rectangle r = new Rectangle(x, 40, 25, 10);
r.setFill(Color.YELLOW);
Sprite rectangle = new Sprite(r);
rectangles.add(rectangle);
all.add(rectangle);
return rectangle;
}
class Sprite {
private final Node node;
private double dx, dy;
public Sprite(Node node) {
this.node = node;
}
public Node getNode() {
return node;
}
public void setVelocity(double dx, double dy) {
this.dx = dx;
this.dy = dy;
}
public void update() {
this.node.setTranslateX(this.node.getTranslateX() + dx);
this.node.setTranslateY(this.node.getTranslateY() + dy);
}
}
interface Command {
void execute(Sprite sprite);
}
}

Related

How can I create a feathered brush with JavaFX?

So I'm trying to create a very basic photo editor program in Java, using JavaFX. I got a brush and eraser working pretty well so far the following way:
package application;
import java.io.File;
import javax.imageio.ImageIO;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
public class EditorController {
private boolean eraser = false;
#FXML
private Canvas canvas;
#FXML
private ColorPicker colorPicker;
#FXML
private TextField brushSize;
#FXML
private TextField selectedTool;
private Point2D last = null;
public void initialize() {
GraphicsContext gc = canvas.getGraphicsContext2D();
canvas.setOnMouseReleased(e -> {last = null;});
canvas.setOnMouseClicked(e -> {
if (!eraser) {
double size = Double.parseDouble(brushSize.getText());
float mouseX = (float) e.getX();
float mouseY = (float) e.getY();
gc.fillOval(mouseX-(size/2), mouseY-(size/2), size, size);
}
});
canvas.setOnMouseDragged(e -> {
System.out.println(eraser);
double size = Double.parseDouble(brushSize.getText());
gc.setLineCap(StrokeLineCap.ROUND);
gc.setLineWidth(size);
float mouseX = (float) e.getX();
float mouseY = (float) e.getY();
if (last != null && !eraser) {
gc.strokeLine(last.getX(), last.getY(), mouseX, mouseY);
} else if (eraser) {
gc.clearRect(mouseX, mouseY, size, size);
}
last = new Point2D(mouseX, mouseY);
});
}
public void onSave() {
try {
Image snapshot = canvas.snapshot(null, null);
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File("paint.png"));
} catch (Exception e) {
System.out.println("Failed to save image: " + e);
}
}
public void onLoad() {
// not implemented yet
}
// not implemented yet
public void onUndo() { }
public void onRedo() { }
public void onSmaller() { }
public void onBigger() { }
public void onResetView() { }
public void onFitView() { }
public void onFillView() { }
public void onNewLayer() { }
public void onDeleteLayer() { }
public void onDuplicateLayer() { }
public void onGroupLayers() { }
public void onMergeLayers() { }
public void onAddMask() { }
public void onBrush() { eraser = false; selectedTool.setText("Brush"); }
public void onEraser() { eraser = true; selectedTool.setText("Eraser"); }
public void onExit() {
Platform.exit();
}
}
Now I want to have a feather/hardness value for the brush (like in photoshop) where I can draw a softer-looking line, but I'm not sure how to achieve it with JavaFX? Are there any tools within it for things like this?
So with a visual example: the brush on the left would be a feathered brush, the one on the right isn't (and that's what I have currently)
Simple drawing app.
It uses a radial gradient rendered to an image that is drawn on the canvas, but you could just draw the gradient directly onto the canvas. A gradient is a paint so you can set it directly as an argument to setFill on a graphics context.
The solution in my example probably won't exactly give you the solution you are looking for, but perhaps you could tweak it for what you need.
It was a quick app I put together for demo purposes, it could be structured better if a more functional drawing app was required.
The code which creates the "feathered brush" is based on a gradient:
private RadialGradient createSoftBrushGradient(Color primaryColor) {
return new RadialGradient(
0, 0,
.5, .5,
.5,
true,
CycleMethod.NO_CYCLE,
new Stop(0, primaryColor),
new Stop(1, Color.TRANSPARENT)
);
}
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class SprayPaint extends Application {
private final IntegerProperty brushDiameter = new SimpleIntegerProperty();
private final ObjectProperty<Image> brushImage = new SimpleObjectProperty<>();
private final ToggleGroup brushHardnessSelection = new ToggleGroup();
private final RadioButton hardBrushSelection = new RadioButton();
private final RadioButton softBrushSelection = new RadioButton();
private final Circle hardBrush = new Circle();
private final Circle softBrush = new Circle();
private final SnapshotParameters snapshotParams = new SnapshotParameters();
private final Canvas canvas = new Canvas(600, 450);
#Override
public void start(Stage stage) {
snapshotParams.setFill(Color.TRANSPARENT);
Pane controls = createControls();
StackPane canvasHolder = new StackPane(canvas);
canvasHolder.setStyle("-fx-border-color: gray;");
canvasHolder.setMaxSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
VBox layout = new VBox(
10,
controls,
canvasHolder
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
enableDrawing(canvas);
}
private void enableDrawing(Canvas canvas) {
EventHandler<MouseEvent> drawHandler = event -> {
Image brush = snapshotBrushImage();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(
brush,
event.getX() - brushDiameter.doubleValue() / 2,
event.getY() - brushDiameter.doubleValue() / 2
);
};
canvas.setOnMousePressed(drawHandler);
canvas.setOnMouseDragged(drawHandler);
}
private Image snapshotBrushImage() {
if (brushImage.get() == null) {
if (hardBrushSelection == brushHardnessSelection.getSelectedToggle()) {
brushImage.set(snapshot(hardBrush));
} else { // soft brush selected
brushImage.set(snapshot(softBrush));
}
}
return brushImage.get();
}
private Image snapshot(Circle brushNode) {
return brushNode.snapshot(snapshotParams, null);
}
private Pane createControls() {
hardBrush.radiusProperty().bind(
brushDiameter.divide(2.0)
);
softBrush.radiusProperty().bind(
brushDiameter.divide(2.0)
);
hardBrushSelection.getStyleClass().addAll("toggle-button", "left-pill");
hardBrushSelection.getStyleClass().remove( "radio-button");
StackPane hardBrushGraphic = new StackPane(hardBrush);
hardBrushGraphic.setMinSize(40, 40);
hardBrushSelection.setGraphic(hardBrushGraphic);
hardBrushSelection.setToggleGroup(brushHardnessSelection);
softBrushSelection.getStyleClass().addAll( "toggle-button", "right-pill");
softBrushSelection.getStyleClass().remove( "radio-button");
StackPane softBrushGraphic = new StackPane(softBrush);
softBrushGraphic.setMinSize(40, 40);
softBrushSelection.setGraphic(softBrushGraphic);
softBrushSelection.setToggleGroup(brushHardnessSelection);
hardBrushSelection.setSelected(true);
HBox brushSelectionPanel = new HBox(hardBrushSelection, softBrushSelection);
Slider brushDiameterSlider = new Slider(8, 40, 20);
brushDiameterSlider.setMajorTickUnit(4);
brushDiameterSlider.setMinorTickCount(0);
brushDiameterSlider.setShowTickMarks(true);
brushDiameter.setValue((int) Math.round(brushDiameterSlider.getValue()));
brushDiameterSlider.valueProperty().addListener((observable, oldValue, newValue) ->
brushDiameter.setValue((int) Math.round(newValue.doubleValue()))
);
Label diameterLabel = new Label();
diameterLabel.textProperty().bind(
brushDiameter.asString()
);
ColorPicker colorPicker = new ColorPicker();
hardBrush.fillProperty().bind(
colorPicker.valueProperty()
);
colorPicker.valueProperty().addListener((observable, oldColor, newColor) ->
softBrush.setFill(
createSoftBrushGradient(newColor)
)
);
colorPicker.setValue(Color.NAVY);
brushDiameter.addListener((observable, oldValue, newValue) ->
brushImage.set(null)
);
colorPicker.valueProperty().addListener((observable, oldValue, newValue) ->
brushImage.set(null)
);
brushHardnessSelection.selectedToggleProperty().addListener((observable, oldValue, newValue) ->
brushImage.set(null)
);
Button clear = new Button("Clear");
clear.setOnAction(e ->
canvas.getGraphicsContext2D().clearRect(
0, 0, canvas.getWidth(), canvas.getHeight()
)
);
HBox controlPanel = new HBox(
10,
colorPicker,
brushSelectionPanel,
new Label("Diameter: "),
brushDiameterSlider,
diameterLabel,
clear
);
controlPanel.setMinWidth(450);
controlPanel.setMinHeight(Pane.USE_PREF_SIZE);
return controlPanel;
}
private RadialGradient createSoftBrushGradient(Color primaryColor) {
return new RadialGradient(
0, 0,
.5, .5,
.5,
true,
CycleMethod.NO_CYCLE,
new Stop(0, primaryColor),
new Stop(1, Color.TRANSPARENT)
);
}
}

How to perform sequential animation

I'm creating a basic animation with javaFX, I have 5 rectangles in a row, I have two cirlces, red and blue. The red cricle is set on the first rectangle and the blue one is set on the fifth rectangle.
The idea is: If I click the first rectangle I want the red circle to travel to the fifth rectangle (by a translation) and once it gets there the blue rectangle (which is on the fifth rectangle) travels to the first one, in another word they exchange positions. I used the AnimationTimer class in my logic but the porblem is that the animation of both cirlces is synchronized when the pressedMouse event happens and that's not what I want, what I want is that the animation of the blue circle starts once the red circle's one is finished. I want to understand why is that happening? Is it kinda multiple thread? In case it is, when I run the program the red circle gets stuck in the middle however the blue one travels out of the bounds and hides, however if I comment one of the cirlce's position code for update (the update method) the app runs correctly, I hope I get an answer and I'm so thankful.
Another question is: How to make my animation looks smoother because it stops for a fraction of a second and moves again.
here is my code:
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class animation extends Application {
AnchorPane root = new AnchorPane();
//create a matrix from rectangle class to stock 5 rectangle objects
rectangle rect [] = new rectangle [5];
//isMoving gets the true value when the animation starts
private boolean isMoving = false;
private int traveledDistance = 30;
#Override
public void start(Stage primaryStage) {
//add 5 rectangles on the anchorpane
//rect[0], rect[2] and rect[4] have BURLYWOOD color
//rect[1], rect[3] have DARKBLUE color
for(int i = 0; i<5; i++)
{
if(i%2 == 0)
{
rect[i] = new rectangle();
rect[i].setFill(Color.BURLYWOOD);
}
else
{
rect[i] = new rectangle();
rect[i].setFill(Color.DARKBLUE);
}
//set all 5 rectangles as empty
rect[i].setRectEmpty(true);
//set all the 5 rectangles one after the other along the x axis
rect[i].setTranslateX(i*60);
//add the 5 rectangles to the parent
root.getChildren().add(rect[i]);
}
//instantiation of two circles (c and d) from cirlce class
circle c = new circle(Color.RED);
c.setName("redCircle");
circle d = new circle(Color.BLUE);
d.setName("blueCircle");
//set the position of the red circle centered relatively to rect[0]
//rect[0] is no longer empty as it contains the red circle
c.setTranslateX(30);
c.setTranslateY(30);
rect[0].setCircle(c);
rect[0].setCircleName(c.getName());
rect[0].setRectEmpty(false);
root.getChildren().add(c);
//set the position of the blue circle centered relatively to rect[4]
d.setTranslateX(4*60 +30);
d.setTranslateY(30);
rect[4].setCircle(d);
rect[4].setCircleName(d.getName());
root.getChildren().add(d);
displayedScene(primaryStage);
//when the parent is clicked
root.setOnMousePressed(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
//get the index of the clicked rectangle
int index = (int) event.getX()/60;
//if the clicked rectangle contains the red circle inside
if(!rect[index].isRectEmpty() && rect[index].getCircleName().equals("redCircle"))
{
Circle circle = rect[index].getCircle();
//update the postion of the red circle so that it occupies the last rectangle (rect[4])
update(index,5, circle);
//update the position of the blue circle so that it occupies the first rectangle(rect[0])
update(5,0, rect[4].getCircle());
}
}
});
}
//update method uses the AnimationTimer class
public void update(int initialPos, int lastPos, Circle circle)
{
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
updateCirclePosition(initialPos, lastPos, circle);
if(!isMoving)
{
this.stop();
}
}
};
timer.start();
}
public void updateCirclePosition(int initialPos, int lastPos, Circle circle)
{
int dx = 2;
if(initialPos>lastPos)
{
dx = -1*dx;
}
isMoving = true;
int distance = Math.abs((lastPos - initialPos)*60);
if(traveledDistance<distance-30)
{
circle.setTranslateX(circle.getTranslateX() + dx);
traveledDistance +=Math.abs(dx);
}
else{
isMoving = false;
traveledDistance = 30;
}
}
//load the Stage
public void displayedScene(Stage primaryStage)
{
Scene scene = new Scene(root, 300, 60);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
// circle class extends Circle
public class circle extends Circle
{
private String name;
public circle(Paint color) {
super(30, color);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
And here is rectangle class:
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
//rectangle class extends Rectangle
public class rectangle extends Rectangle {
private Circle circle;
private String circleName;
private boolean rectEmpty;
public rectangle() {
super(60, 60);
}
public Circle getCircle() {
return circle;
}
public void setCircle(Circle circle) {
this.circle = circle;
}
public boolean isRectEmpty() {
return rectEmpty;
}
public void setRectEmpty(boolean rectEmpty) {
this.rectEmpty = rectEmpty;
}
public String getCircleName() {
return circleName;
}
public void setCircleName(String circleName) {
this.circleName = circleName;
}
}
The following is an mre demonstrating the requested functionality.
Circles animation is done by animateCircles(). It uses TranslateTransition to translate a circle from one position to the other.
setOnFinished is used to start the next animation.
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Animation extends Application {
private static final double SQUARE_SIZE = 60, RADIUS = SQUARE_SIZE /2, ANIMATION_TIME = 1;
private final Pane root = new Pane();
private final Rectangle rect [] = new Rectangle [5];
private final Circle circles [] = new Circle[2];
private boolean isMoving = false, isSwapped = false;
#Override
public void start(Stage primaryStage) {
for(int i = 0; i<rect.length; i++) {
rect[i] = new Rectangle(SQUARE_SIZE, SQUARE_SIZE, i%2 == 0 ? Color.BURLYWOOD : Color.DARKBLUE);
//set all the 5 rectangles one after the other along the x axis
rect[i].setTranslateX(i*SQUARE_SIZE);
root.getChildren().add(rect[i]);
}
circles[0] = new Circle(RADIUS,Color.RED);
circles[1] = new Circle(RADIUS,Color.BLUE);
//set the position of the red circle centered to rect[0]
Point2D center = centerOf(rect[0]);
circles[0].setTranslateX(center.getX());
circles[0].setTranslateY(center.getY());
//set the position of the blue circle centered to rect[4]
center = centerOf(rect[4]);
circles[1].setTranslateX(center.getX());
circles[1].setTranslateY(center.getY());
root.getChildren().add(circles[0]);
root.getChildren().add( circles[1]);
Scene scene = new Scene(root, SQUARE_SIZE*rect.length, SQUARE_SIZE);
primaryStage.setScene(scene);
primaryStage.show();
root.setOnMousePressed(event -> animateCircles());
}
//return the center point
private Point2D centerOf(Rectangle rect) {
Bounds bounds = rect.getBoundsInParent();
double x = bounds.getMinX() + 0.5 * bounds.getWidth();
double y = bounds.getMinY() + 0.5 * bounds.getHeight();
return new Point2D(x, y);
}
private void animateCircles() {
if(isMoving) return;
TranslateTransition translateCircle0 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[0]);
translateCircle0.setToX( isSwapped ? centerOf(rect[0]).getX() : centerOf(rect[4]).getX());
TranslateTransition translateCircle1 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[1]);
translateCircle1.setToX( isSwapped ? centerOf(rect[4]).getX() : centerOf(rect[0]).getX());
translateCircle0.setOnFinished(e-> {
translateCircle1.play();
});
translateCircle1.setOnFinished(e-> {
isMoving = false;
isSwapped = ! isSwapped;
});
isMoving = true;
translateCircle0.play();
}
public static void main(String[] args) {
launch(args);
}
}
Alternatively you could implement animateCircles() using SequentialTransition:
private void animateCircles() {
if(isMoving) return;
TranslateTransition translateCircle0 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[0]);
translateCircle0.setToX( isSwapped ? centerOf(rect[0]).getX() : centerOf(rect[4]).getX());
TranslateTransition translateCircle1 = new TranslateTransition(Duration.seconds(ANIMATION_TIME), circles[1]);
translateCircle1.setToX( isSwapped ? centerOf(rect[4]).getX() : centerOf(rect[0]).getX());
SequentialTransition sequentialTransition = new SequentialTransition(translateCircle0, translateCircle1);
isMoving = true;
sequentialTransition.play();
sequentialTransition.setOnFinished(e-> {
isMoving = false;
isSwapped = ! isSwapped;
});
}

Add constraints to lines in javaFX

How would I go about adding a constraint to how much a line can be dragged? I have a stick man and you can drag all his arms and legs, head and back about but I want them to stay the same length as they started off, so you can't stretch them longer or shorter than they should be, just move them up and down, side to side, in a circle etc. I guess i have to do something with the start/end x and y but im not sure how to set a set constraint to it and also still have it be draggable and stay the same length
private Line connectLines(Line line, Circle startNode, Circle endNode) {
line.startXProperty().bind(startNode.centerXProperty().add(startNode.translateXProperty()));
line.startYProperty().bind(startNode.centerYProperty().add(startNode.translateYProperty()));
line.endXProperty().bind(endNode.centerXProperty().add(endNode.translateXProperty()));
line.endYProperty().bind(endNode.centerYProperty().add(endNode.translateYProperty()));
return line;
}
//mouse pressed event
EventHandler<MouseEvent> mousePressed = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("pressed");
sceneX = e.getSceneX();
sceneY = e.getSceneY();
translateCircleX = ((Circle)(e.getSource())).getTranslateX();
translateCircleY = ((Circle)(e.getSource())).getTranslateY();
}
};
//mouse dragged event
EventHandler<MouseEvent> mouseDragged = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
System.out.println("dragged");
double offsetX = e.getSceneX() - sceneX;
double offsetY = e.getSceneY() - sceneY;
double newTranslateCircleX = translateCircleX + offsetX;
double newTranslateCircleY = translateCircleY + offsetY;
((Circle)(e.getSource())).setTranslateX(newTranslateCircleX);
((Circle)(e.getSource())).setTranslateY(newTranslateCircleY);
}
};
Here is an example. This example does not use Circle.setTranslate#. It uses Circle.setCenter#. It also uses Math.hypot to keep track of the Line length. If the line length becomes greater than or equal to 100, the change in the shape movements is subtracted.
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class TableViewDemo2 extends Application
{
double sceneX, sceneY;
Circle circle = new Circle(15, Color.RED);
Circle circle2 = new Circle(15, Color.BLUE);
Line line = new Line();
private Line connectLines(Line line, Circle startNode, Circle endNode)
{
line.startXProperty().bind(startNode.centerXProperty());
line.startYProperty().bind(startNode.centerYProperty());
line.endXProperty().bind(endNode.centerXProperty());
line.endYProperty().bind(endNode.centerYProperty());
return line;
}
//mouse pressed event
EventHandler<MouseEvent> mousePressed = new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
System.out.println("pressed");
sceneX = e.getSceneX();
sceneY = e.getSceneY();
Circle tempCircle = ((Circle) e.getSource());
tempCircle.toFront();
}
};
//mouse dragged event
EventHandler<MouseEvent> mouseDragged = new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
System.out.println(Math.hypot(line.getBoundsInLocal().getWidth(), line.getBoundsInLocal().getHeight()));
System.out.println("dragged");
double offSetX = e.getSceneX() - sceneX;
double offSetY = e.getSceneY() - sceneY;
Circle tempCircle = ((Circle) (e.getSource()));
tempCircle.setCenterX(tempCircle.getCenterX() + offSetX);
tempCircle.setCenterY(tempCircle.getCenterY() + offSetY);
if (Math.hypot(line.getBoundsInLocal().getWidth(), line.getBoundsInLocal().getHeight()) >= 100) {
tempCircle.setCenterX(tempCircle.getCenterX() - offSetX);
tempCircle.setCenterY(tempCircle.getCenterY() - offSetY);
}
sceneX = e.getSceneX();
sceneY = e.getSceneY();
}
};
#Override
public void start(Stage stage)
{
circle.setOnMouseDragged(mouseDragged);
circle2.setOnMouseDragged(mouseDragged);
Line returnLine = connectLines(line, circle, circle2);
StackPane root = new StackPane(new Pane(circle, circle2, returnLine));
stage.setTitle("TableView (o7planning.org)");
Scene scene = new Scene(root, 450, 300);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

JavaFX - Clickable line on canvas

I have this JavaFX application that lets you plot locations on a map and connect them.
I do this by drawing a map as a background image on a canvas and then drawing circles and lines on it. I have made the circles clickable by using the contains() method in the Circle class, but how can I make the lines clickable?
edit: Look at this example where I just draw a line and set an event handler:
Canvas canvas = new Canvas();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setLineWidth(5);
gc.strokeLine(100, 100, 200, 200);
canvas.setOnMouseClicked(event -> {
double x = event.getX(), y = event.getY();
});
My question is simply this: how do I finish the event handler so that it detects if the click is inside the line I just drew?
You should create a canvas and add the nodes (Circle, Line, etc) to it. Then you add mouse listeners to the nodes.
Example:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class DragNodes extends Application {
public static List<Circle> circles = new ArrayList<Circle>();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Canvas canvas = new Canvas(300, 300);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawShapes(gc);
Circle circle1 = new Circle(50);
circle1.setStroke(Color.GREEN);
circle1.setFill(Color.GREEN.deriveColor(1, 1, 1, 0.7));
circle1.relocate(100, 100);
Circle circle2 = new Circle(50);
circle2.setStroke(Color.BLUE);
circle2.setFill(Color.BLUE.deriveColor(1, 1, 1, 0.7));
circle2.relocate(200, 200);
Line line = new Line(circle1.getLayoutX(), circle1.getLayoutY(), circle2.getLayoutX(), circle2.getLayoutY());
line.setStrokeWidth(20);
Pane overlay = new Pane();
overlay.getChildren().addAll(circle1, circle2, line);
MouseGestures mg = new MouseGestures();
mg.makeDraggable(circle1);
mg.makeDraggable(circle2);
mg.makeDraggable(line);
root.getChildren().addAll(canvas, overlay);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
private void drawShapes(GraphicsContext gc) {
gc.setStroke(Color.RED);
gc.strokeRoundRect(10, 10, 230, 230, 10, 10);
}
public static class MouseGestures {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public void makeDraggable(Node node) {
node.setOnMousePressed(circleOnMousePressedEventHandler);
node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
orgTranslateX = p.getCenterX();
orgTranslateY = p.getCenterY();
} else {
Node p = ((Node) (t.getSource()));
orgTranslateX = p.getTranslateX();
orgTranslateY = p.getTranslateY();
}
}
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
p.setCenterX(newTranslateX);
p.setCenterY(newTranslateY);
} else {
Node p = ((Node) (t.getSource()));
p.setTranslateX(newTranslateX);
p.setTranslateY(newTranslateY);
}
}
};
}
}

Adding more JComponents to JFrame

Single keys (in code KeyboardButtons) extends JComponent. When i'm trying to add the to main JFrame, i can do that for single key, when trying to add another one, the first one is not showing.
Can you please look at the code a tell me where the problem is?
MainFrame.java:
package keyboard;
import java.awt.*;
import javax.swing.JFrame;
public class MainFrame extends JFrame {
public MainFrame() throws HeadlessException {
setTitle("Keyboard");
setSize(1024, 768);
}
public static void main(String[] args) throws InterruptedException {
JFrame mainWindow = new MainFrame();
mainWindow.setDefaultCloseOperation(EXIT_ON_CLOSE);
Point left5 = new Point(210, 210);
Point left4 = new Point(410, 110);
Point left3 = new Point(580, 120);
Point left2 = new Point(680, 200);
Point left1 = new Point(800, 500);
Keyboard kb = new Keyboard(left1, left2, left3, left4, left5);
KeyboardButton[] buttons = kb.registerKeys();
Container c = mainWindow.getContentPane();
c.add(buttons[0]);
c.add(buttons[1]);
mainWindow.setVisible(true);
}
}
KeyboardButton.java:
package keyboard;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.*;
import javax.swing.JComponent;
public class KeyboardButton extends JComponent implements MouseListener {
Polygon polygon;
boolean isActive;
final Color ACTIVE_COLOR = Color.red;
final Color INACTIVE_COLOR = Color.blue;
public KeyboardButton(Polygon p) {
polygon = p;
addMouseListener(this);
}
private void checkMousePosition(MouseEvent e) {
if (polygon.contains(e.getX(), e.getY())) {
setState(true);
}
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
checkMousePosition(e);
System.out.println(this + " pressed");
}
public void mouseReleased(MouseEvent e) {
setState(false);
System.out.println(this + " released");
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(isActive ? ACTIVE_COLOR : INACTIVE_COLOR);
g.drawPolygon(polygon);
}
void setState(boolean state) {
isActive = state;
repaint();
}
}
Keyboard.java:
package keyboard;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import javax.swing.JComponent;
public class Keyboard extends JComponent {
Point[] leftFingers;
Point leftCenter = new Point(300, 600);
public Keyboard(Point left1, Point left2, Point left3, Point left4, Point left5) {
leftFingers = new Point[5];
leftFingers[0] = left1;
leftFingers[1] = left2;
leftFingers[2] = left3;
leftFingers[3] = left4;
leftFingers[4] = left5;
}
public KeyboardButton[] registerKeys() {
Polygon[] polygons = generateKeyPolygons(calculateBordersOfKeys(calculateCentersBetweenEachTwoFingers(leftFingers)));
KeyboardButton[] buttons = new KeyboardButton[5];
for (int i = 0; i < polygons.length; i++) {
buttons[i] = new KeyboardButton(polygons[i]);
}
return buttons;
}
private Point[] calculateBordersOfKeys(Point[] fingers) {
Point[] centers = calculateCentersBetweenEachTwoFingers(fingers);
Point[] result = new Point[6];
result[0] = calculateCentralSymmetry(centers[0], fingers[0]);
System.arraycopy(centers, 0, result, 1, centers.length);
result[5] = calculateCentralSymmetry(centers[3], fingers[4]);
return result;
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.red);
g.drawOval(leftCenter.x - 25, leftCenter.y - 25, 50, 50);
for (int i = 0; i < leftFingers.length; i++) {
g.drawOval(leftFingers[i].x, leftFingers[i].y, 10, 10);
}
}
private Polygon[] generateKeyPolygons(Point[] borders) {
Polygon[] polygons = new Polygon[5];
for (int i = 0; i < borders.length - 1; i++) {
Polygon p = new Polygon();
p.addPoint(leftCenter.x, leftCenter.y);
p.addPoint(borders[i].x, borders[i].y);
p.addPoint(borders[i + 1].x, borders[i + 1].y);
polygons[i] = p;
}
return polygons;
}
private Point[] calculateCentersBetweenEachTwoFingers(Point[] fingers) {
Point[] centers = new Point[4];
for (int i = 0; i < fingers.length - 1; i++) {
centers[i] = new Point(((fingers[i].x + fingers[i + 1].x) / 2), ((fingers[i].y + fingers[i + 1].y) / 2));
}
return centers;
}
private Point calculateCentralSymmetry(Point toReflected, Point center) {
Point reflection = new Point();
if (toReflected.x > center.x) {
reflection.x = center.x - Math.abs(center.x - toReflected.x);
} else {
reflection.x = center.x + Math.abs(center.x - toReflected.x);
}
if (toReflected.y > center.y) {
reflection.y = center.y - Math.abs(center.y - toReflected.y);
} else {
reflection.y = center.y + Math.abs(center.y - toReflected.y);
}
return reflection;
}
}
Try to use another LayoutManager, or for what it seems to me it looks like you are trying to manualy paint shapes on the screen, i'd suggest painting them all on one layer. (have one JComponent's paintComponent() method which calls to KeyboardButton.paint() and other painting methods, then you can just add that one JComponent)

Categories

Resources