Moffee
Moffee

Reputation: 411

JavaFX - Drawn shape that follows mouse stutters when moving small distances

I'm attempting to make a triangle shape drawn using GraphicsContext2D follow the mouse, while facing towards it.

It works all nice and nifty when you're moving over 10+ pixels of distance. However, the shape stutters when you're moving small precise distances and doesn't correctly face the mouse.

Game:

public class Game extends Application {

    final static private String GAME_NAME = "Trigon";
    final static private int WINDOW_WIDTH = 960;
    final static private int WINDOW_HEIGHT = 640;
    private PlayerShip ply;

    @Override
    public void start(Stage theStage) throws Exception {
        // Set title of window & make root, canvas, graphics context
        theStage.setTitle(GAME_NAME);
        Group root = new Group();
        Scene theScene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
        Canvas canvas = new Canvas(WINDOW_WIDTH, WINDOW_HEIGHT);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        theStage.setScene(theScene);
        root.getChildren().add(canvas);

        // Initialize game variables
        ply = new PlayerShip(WINDOW_WIDTH/2, WINDOW_HEIGHT/2);

        theScene.setOnMouseMoved(
            new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent e) {
                    ply.setPos(e.getX(), e.getY());
                }
            }
        );

        new AnimationTimer() {
            @Override
            public void handle(long currentNanoTime) {
                gc.setFill(Color.WHITE);
                gc.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
                ply.draw(gc);
            }
        }.start();

        theStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

PlayerShip:

public class PlayerShip {
    private double shipLength, shipWidth;
    private double posX, posY, rotAngle;

    public PlayerShip(double posX, double posY) {
        this(posX, posY);
    }

    public PlayerShip(double posX, double posY) {
        this.posX = posX;
        this.posY = posY;
        this.shipWidth = 30;
        this.shipLength = 30;
    }

    public void setPos(double posX, double posY) {
        double distX = posX - this.posX;
        double distY = posY - this.posY;
        rotAngle = Math.toDegrees(Math.atan2(distX, -distY));
        this.posX = posX;
        this.posY = posY;
    }

    public void draw(final GraphicsContext gc) {
        // Save
        gc.save();

        // Translate + rotate
        gc.translate(posX, posY);
        gc.rotate(rotAngle);

        // Draw ship
        gc.beginPath();
        gc.moveTo(0, -shipLength/2);
        gc.lineTo(shipWidth/2, shipLength/2);
        gc.lineTo(-shipWidth/2, shipLength/2);
        gc.lineTo(0, -shipLength/2);
        gc.stroke();
        gc.closePath();

        // Restore
        gc.restore();
    }
}

Sorry for the block of text or if I'm forgetting something important.

Upvotes: 1

Views: 277

Answers (1)

fabian
fabian

Reputation: 82461

This happens when the origin of the ship is too close to the position of the mouse. Small changes of the mouse position can lead to large changes of the angle, which results in the shuttering effect.

You could solve this issue by not moving the ship to the mouse location, but moving it to a point that is close to the mouse:

public void setPos(double posX, double posY) {
    double distX = posX - this.posX;
    double distY = posY - this.posY;

    // movement distance
    double magnitude = Math.sqrt(distX * distX + distY * distY);

    if (magnitude > 5) {
        // only move, if the distance is greater than 5

        // factor to move to distance 5
        double factor = (magnitude - 5) / magnitude;

        this.posX += distX * factor;
        this.posY += distY * factor;
        rotAngle = Math.toDegrees(Math.atan2(distX, -distY));
    }
}

Upvotes: 1

Related Questions