Making games with JNativeHook

2019-01-29 00:01发布

问题:

I'm working on a simple overlay program for PC games. It's just a transparent rectangle positioned in the center of the screen but its size is controlled by the user's mouse wheel. So the concept is to simply match the size of the transparent rectangle to the size of the enemy player to calculate his distance.

Unfortunately I cannot make this happen with conventional mouse listeners because the mouse must be focused on the game and the overlay program at the same time. I'm trying JNativeHook, but I can't get my rectangle to update. Any advice?

public class Main extends Application implements NativeMouseWheelListener {

    Rectangle r = new Rectangle();
    int y = 540; 
    int width = 75;
    int height = 180;
    int velocity = 10;

    @Override
    public void start(Stage stage) throws Exception {
         AnchorPane root = new AnchorPane();
         r = rect(); 
         root.getChildren().add(r);
         root.setStyle("-fx-background-color: rgba(0, 0, 0, 0);");

         Scene scene = new Scene(root, 1920, 1080); 
         scene.setFill(null);

         stage.initStyle(StageStyle.TRANSPARENT);
         stage.setScene(scene);
         stage.setX(0);
         stage.setY(0);
         stage.show();
         stage.setAlwaysOnTop(true);
    }

    public void nativeMouseWheelMoved(NativeMouseWheelEvent e) {
        int direction = e.getWheelRotation();
        System.out.println("Mouse Wheel Moved: " + direction);
        r.setY(r.getY() + direction);
    }

    public Rectangle rect() {
        r.setWidth(width);
        r.setHeight(height);
        r.setX(960 - (width/2));
        r.setY(540);
        r.setFill(Color.TRANSPARENT);
        r.setStroke(Color.BLACK);
        return r;
    }

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

    public static void go() {
        try {
            GlobalScreen.registerNativeHook();
        }
        catch (NativeHookException ex) {
            System.err.println("There was a problem registering the native hook.");
            System.err.println(ex.getMessage());
            ex.printStackTrace();
            System.exit(1);
        }

        GlobalScreen.getInstance().addNativeMouseWheelListener(new Main());
    }
}

回答1:

I highly recommend taking @vince-emigh advice and running JNativeHook on the same thread as your ui environment by using a custom implementation of the AbstractExecutorService as follows.

public class JavaFxDispatchService extends AbstractExecutorService {
    private boolean running = false;

    public JavaFxDispatchService() {
        running = true;
    }

    public void shutdown() {
        running = false;
    }

    public List<Runnable> shutdownNow() {
        running = false;
        return new ArrayList<Runnable>(0);
    }

    public boolean isShutdown() {
        return !running;
    }

    public boolean isTerminated() {
        return !running;
    }

    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return true;
    }

    public void execute(Runnable r) {
        Platform.runLater(r);
    }
}

Then use the class as the event dispatcher:

// Set the event dispatcher to a swing safe executor service.
GlobalScreen.setEventDispatcher(new JavaFxDispatchService());

// Initialize native hook.
try {
    GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
    System.err.println("There was a problem registering the native hook.");
    System.err.println(ex.getMessage());
    ex.printStackTrace();
    System.exit(1);
}

GlobalScreen.addNativeKeyListener(this);

For more information, please see the Swing article in the wiki