Festbawi Festbawi - 23 days ago 8
Java Question

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.

Answer

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));
    }
}