문제

I have a 3D game, and every time I move the cursor, I want it to reset to the middle. Problem is that robot.mouseMove() calls MouseEvent (It does make sense) and resets the position and so I can't rotate.

Thank you!

도움이 되었습니까?

해결책 2

Because Robot is generating a native event, the event will (eventually) make it's way to the Event Queue for processing by the EDT.

This means if you try and do something like...

removeMouseListener(...);
Robot.mouseMove(...);
addMouseListener(...);

It will, basically, have no effect, as the removal and additional of the mouse listener have occurred in the same cycle of the event processing, meaning the mouse event that the robot has raised will not have being processed (or will appear later in the queue)...

Instead, you need to raise some kind of flag that you can detect and then ignore the next incoming event...

if (!ignoreMouseMove) {
    ignoreMouseMove = true;
    // Do your normal processing...
    robot.mouseMove(...);
} else {
    ignoreMouseMove = false;
}

The basic example below detects the distance from the center a mouse has moved and updates a simple position variable (which basically acts as a compass point). This helps illustrate the movement, but more importantly, that we are breaking the event cycle...

enter image description here

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Robot;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestMouseMove {

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

    public TestMouseMove() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Robot bot;
        private int position = 0;

        public TestPane() {
            try {

                bot = new Robot();
                MouseAdapter ma = new MouseAdapter() {

                    boolean ignoreMouseMove = false;

                    @Override
                    public void mouseMoved(MouseEvent e) {
                        if (!ignoreMouseMove) {
                            ignoreMouseMove = true;
                            int x = getLocationOnScreen().x + (getWidth() / 2);
                            int y = getLocationOnScreen().y + (getHeight() / 2);

                            int distanceFromCenter = e.getPoint().x - (getWidth() / 2);
                            position += distanceFromCenter;
                            if (position < 0) {
                                position = 360 - position;
                            } else if (position > 360) {
                                position -= 360;
                            }
                            repaint();

                            bot.mouseMove(x, y);
                        } else {
                            ignoreMouseMove = false;
                        }
                    }

                    @Override
                    public void mouseClicked(MouseEvent e) {
                        System.exit(0);
                    }

                };
                addMouseListener(ma);
                addMouseMotionListener(ma);
            } catch (AWTException ex) {
                ex.printStackTrace();;
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            FontMetrics fm = g2d.getFontMetrics();

            int x = getWidth() / 2;
            int y = getHeight() / 2;

            int amount = position;

            while (x > 0) {
                if (amount == position) {
                    g2d.drawLine(x, y, x, y - 40);
                } else {
                    g2d.drawLine(x, y, x, y - 20);
                }
                String text = Integer.toString(amount);
                g2d.drawString(text, x - (fm.stringWidth(text) / 2), y + fm.getHeight());
                x -= 20;
                amount--;
                if (amount < 0) {
                    amount = 360 + amount;
                }
            }
            amount = position + 1;
            x = (getWidth() / 2) + 20;
            while (x < getWidth()) {
                g2d.drawLine(x, y, x, y - 20);
                if (position > 360) {
                    position = 360 - position;
                }
                String text = Integer.toString(amount);
                g2d.drawString(text, x - (fm.stringWidth(text) / 2), y + fm.getHeight());
                x += 20;
                amount++;
            }

            g2d.dispose();
        }
    }
}

다른 팁

I prefer code like the following:

component.removeMouseListener(...);
Robot.doSomething();
component.addMouseListener(...);

instead of setting a flag. Using this approach the code to manage the listener is in a single place in your code.

If you use a flag you need to

  1. define the flag variable,
  2. set/reset the variable
  3. test the variable

so you end up having code in multiple places in your class.

Edit:

Good point about the Robot being added to the end of the event queue. So, I would then wrap the code that adds the MouseListener back to the component in a SwingUtilities.invokeLater()

MadProgrammer's answer will, unfortunately, not quite work in all cases because:

  • It is possible to get an actual MouseEvent before the robot one. This will cause you to ignore the actual mouse event and process the robot one. You can't assume that the next event will be from the Robot and this definitely occurs (somewhat rarely) in practice.
  • It is also possible to get actual MouseEvent data added with the robot one. So this will cause you to lose part of your actual mouse movement that you would like to keep.

A more consistent answer is not to ignore the mouse event, but process it, and then subtract the exact amount you moved using the robot. Here's the jist:

public void mouseMoved(MouseEvent me) { 
    mouseDiffX += me.getX() - mouseX;
    mouseDiffY += me.getY() - mouseY;
    mouseX = me.getX();
    mouseY = me.getY();
    absoluteX = me.getLocationOnScreen().x;
    absoluteY = me.getLocationOnScreen().y;
} 

public void moveRobot() { 
    int newX = canvas.getWidth() / 2 + canvas.getLocationOnScreen().x;
    int newY = canvas.getHeight() / 2 + canvas.getLocationOnScreen().y;
    robotMoveX = newX - absoluteX;
    robotMoveY = newY - absoluteY;
    robotMoved = true;
}

public void update() { 
    if (robotMoved) { 
        mouseDiffX -= robotMoveX;
        mouseDiffY -= robotMoveY;
        robotMoved = false;
    }

    finalX += mouseDiffX;
    finalY += mouseDiffY;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top