1

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);
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 1
    Please post a [MCVE](http://stackoverflow.com/help/mcve) with focus on **complete**. – Roland Jul 01 '16 at 05:13
  • I've added the collision's method. Do you have other problems with my question? Tell me and I will update again that, but I think there is everything now – Bruno Battaglia Jul 01 '16 at 05:31
  • This is not **complete**. – Roland Jul 01 '16 at 05:32
  • Tell me what I should add. I described my problem and I posted the code with problems. Whit shoot() I should see a parabolic trajectory but it doesn't work and the other problem is that I can't have two circle in my scene at the same time – Bruno Battaglia Jul 01 '16 at 05:35
  • The problem is that you want help, but don't understand the concept of **complete**. From the little code you posted, one can see that you obviously work with a global variable and use it for scene modifications. That's totally wrong and that's why you can or should only have exactly 1 of these scene nodes. – Roland Jul 01 '16 at 05:39
  • 1
    Complete means somebody could copy and paste it, then compile and run it without changes to replicate your issue. It should also be minimal too, so that it only replicates the issue and nothing else. Also, best to ask a single question per question. – jewelsea Jul 01 '16 at 05:57
  • It should be good now – Bruno Battaglia Jul 01 '16 at 06:23
  • 1
    No it's not. shoot() is never invoked. – Roland Jul 01 '16 at 07:42
  • Someone else understood my problem and helped me. I think it wasn't so hard to get what my problem was. Thanks a lot, even if you did nothing – Bruno Battaglia Jul 01 '16 at 12:39

1 Answers1

2

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...

Example

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.

Community
  • 1
  • 1
fabian
  • 80,457
  • 12
  • 86
  • 114
  • @Bruno: Notice that the implementation of `curve()` above is algebraically equivalent to the one seen [here](http://stackoverflow.com/a/38031826/230513) : fabian's is simpler; mine makes the vertex explicit. Also consider a `PathTransition` along a suitable `Path`. – trashgod Jul 01 '16 at 09:50
  • I edited the post several times because @Roland didn't like it and I forgot to call my shoot method after the revision. Anyway, thanks a lot, I will work hard now to shot correctly my bullet. – Bruno Battaglia Jul 01 '16 at 12:52