Reputation: 75
First of all, I want to shoot a plane with a cannon. I've setted this Timeline for the trajectory, but I don't see the bullet on my Scene. It's very likely that my trajectory's code isn't correct. I tried to look on the internet about formula for projectile motion, but I understand nothing about physics;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Game_1 extends Application {
private final double gravity = 9.81;
private Timeline timeline;
private ImageView plane;
private Circle circle;
private AnchorPane ap;
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Test");
Group group = new Group();
Scene scene = new Scene(group, 600, 350);
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
}
private void shoot() {
double x = 65.0f;
double y = 408;
double speed = 200;
double t = 2;
double angle = -45;
double dx = Math.cos(angle) * speed;
double dy = Math.sin(angle) * speed;
circle = new Circle(x, y, 5, Color.BLACK);
double x2 = x + dx * t;
double y2 = (Math.tan(angle) * y - (gravity / (2 * Math.pow(speed, 2) * Math.cos(angle))) * Math.pow(x, 2));
timeline = new Timeline();
KeyValue xKV = new KeyValue(circle.centerXProperty(), x2);
KeyValue yKV = new KeyValue(circle.centerYProperty(), y2, new Interpolator() {
@Override
protected double curve(double t) {
return y + dy * t - 0.5 * gravity * t * t;
}
});
KeyFrame xKF = new KeyFrame(Duration.seconds(t), xKV);
KeyFrame yKF = new KeyFrame(Duration.seconds(t), yKV);
timeline.getKeyFrames().addAll(xKF, yKF);
ap.getChildren().add(circle);
timeline.play();
collision();
}
private void collision() {
circle.boundsInParentProperty().addListener((ObservableValue<? extends Bounds> arg0, Bounds oldValue2, Bounds newValue2) -> {
if (circle.getBoundsInParent().intersects(plane.getBoundsInParent())) {
timeline.stop();
ap.getChildren().remove(circle);
}
});
}
}
Upvotes: 1
Views: 927
Reputation: 82461
The curve
method should map to the interval [0, 1]
. Your method however maps to much higher values. The value val
at time t
of a animation from t0
to t1
for a interpolator i
given start value val0
and end value val1
is calculated as follows:
val = val0 + (val1 - val0) * i.curve((t - t0) / (t1 - t0))
The parameter of the curve
method is the relative position in the time interval (0 = start of animation; 1 = end of animation). The result of the method is used to determine how close the value is to the end value (0 = still at the start value; 1 = at the end value).
Therefore you should probably calculate the top point hMax
in the cannonball's curve (as described e.g. here on Wikipedia) and use a different interpolator:
Interpolator interpolator = new Interpolator() {
@Override
protected double curve(double t) {
// parabola with zeros at t=0 and t=1 and a maximum of 1 at t=0.5
return 4 * t * (1 - t);
}
};
KeyValue yKV = new KeyValue(circle.centerYProperty(), hMax, interpolator);
Note that upward movement means decreasing the y
coordinate for the UI so in this case hMax
should be smaller than the y
value at the start.
Appart from that your shoot
method is never called and some fields are not initialized which would result in a NPE in case it was called. Furthermore if those 2 issues are fixed, a black circle on a black background will be hard to see...
Note that this is not using any physical fromulae and instead just uses some values chosen by me:
@Override
public void start(Stage primaryStage) {
Circle circle = new Circle(10);
circle.setManaged(false);
Pane pane = new Pane(circle);
circle.setCenterX(20);
circle.setCenterY(800);
Timeline timeline = new Timeline(new KeyFrame(Duration.ZERO,
new KeyValue(circle.centerXProperty(), 20),
new KeyValue(circle.centerYProperty(), 800)
), new KeyFrame(Duration.seconds(3),
new KeyValue(circle.centerXProperty(), 380),
new KeyValue(circle.centerYProperty(), 10, new Interpolator() {
@Override
protected double curve(double t) {
// parabola with zeros at t=0 and t=1 and a maximum of 1 at t=0.5
return 4 * t * (1 - t);
}
})
)
);
Scene scene = new Scene(pane, 400, 800);
scene.setOnMouseClicked(evt -> timeline.playFromStart());
primaryStage.setScene(scene);
primaryStage.show();
}
Note that Interpolator.curve
is supposed to return 0 for parameter 0 and 1 for parameter 1. Anything else will probably result in jumps, should the property be animated further. Maybe the y-movement in 2 parts would be more appropriate, in case you want to move the ball around after the animation is finished.
I.e.
Interpolator 1: t * (2 - t)
Interpolator 2: t * t
using half the time interval each with end values of the top and start y coordinate of the curve respectively.
Upvotes: 2