views:

637

answers:

3

Hi all, I am looking for a way to capture or trap the mouse in a window after it has entered that window much like a mouse is trapped in a virtual machine window until a user presses CTRL+ALT+DEL or release the mouse in some other manner. How do I make this happen in Java? Going full screen is not an option.

EDIT:

Here is some SSCCE for ya. This code will trap your mouse in the window. To get out you just have to within the generated frame and move directly to the close button. If you will notice when your mouse tries to leave it automatically goes back to (0,0). What I need to know is how do I get it to go back to the coordinates where it exited from. I tried getX() and getY() in place of (0,0) but the robot does not return the mouse there (I think the response time is to slow). I also have had the robot move the mouse back to crosshair.x and crosshair.y but this (as well as the others) still allows the mouse to escape if the user clicks at the right moment. Any thoughts?

Main Class:

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferStrategy;
import java.awt.image.MemoryImageSource;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import javax.swing.JFrame;

public class Game extends JFrame implements MouseMotionListener, MouseListener{

    private int windowWidth = 640;
    private int windowHeight = 480;
        private Crosshair crosshair;

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

    public Game() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(windowWidth, windowHeight);
        this.setResizable(false);
        this.setLocation(0,0);
        this.setVisible(true);

        this.createBufferStrategy(2);
                addMouseMotionListener(this);
                addMouseListener(this);
        initGame();

        while(true) {
            long start = System.currentTimeMillis();
            gameLoop();
            while(System.currentTimeMillis()-start < 5) {
                            //empty while loop
            }
        }
    }

    private void initGame() {
            hideCursor();
            crosshair = new Crosshair (windowWidth/2, windowHeight/2);
    }

        private void gameLoop() {
            //game logic
            drawFrame();
        }

        private void drawFrame() {

            BufferStrategy bf = this.getBufferStrategy();
            Graphics g = (Graphics)bf.getDrawGraphics();
            try {
                g = bf.getDrawGraphics();
                Color darkBlue = new Color(0x010040);
                g.setColor(darkBlue);
                g.fillRect(0, 0, windowWidth, windowHeight);
                drawCrossHair(g);
            } finally {
                g.dispose();
            }
            bf.show();
            Toolkit.getDefaultToolkit().sync();
        }

        private void drawCrossHair(Graphics g){
            Color yellow = new Color (0xEDFF62);
            g.setColor(yellow);
            g.drawOval(crosshair.x, crosshair.y, 40, 40);

            g.fillArc(crosshair.x + 10, crosshair.y + 21 , 20, 20, -45, -90);
            g.fillArc(crosshair.x - 1, crosshair.y + 10, 20, 20, -135, -90);
            g.fillArc(crosshair.x + 10, crosshair.y - 1, 20, 20, -225, -90);
            g.fillArc(crosshair.x + 21, crosshair.y + 10, 20, 20, -315, -90);
        }

        @Override
        public void mouseDragged(MouseEvent e) {
        //empty method
        }

        @Override
        public void mouseMoved(MouseEvent e) {
        crosshair.x = e.getX();
        crosshair.y = e.getY();
        }

        private void hideCursor() {
            int[] pixels = new int[16 * 16];
            Image image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(16, 16, pixels, 0, 16));
            Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor(image, new Point(0, 0), "invisiblecursor");
            getContentPane().setCursor(transparentCursor);
    }

        public void mouseExited(MouseEvent e) {
            System.out.println("Event: " + e);
            try {
                Robot robot = new Robot();
                robot.mouseMove(0, 0);// When I use (getX(),getY()) instead of (0,0) the robot will not move the mouse at all even though getX() and getY() are the coordinates I want the mouse to be moved to.  Also the mouse can still escape, even when crosshair.x and crosshair.y are used as the coordinates.  It seems that robot is too slow.
            }
            catch (AWTException ex) {
                ex.printStackTrace();
            }
        }

        public void mouseEntered(MouseEvent e){
        }

        public void mousePressed(MouseEvent e) {
        }

        public void mouseReleased(MouseEvent e) {
        }

        public void mouseClicked(MouseEvent e) {
        }
}

Another Class:

public class Crosshair{
        public int x;
    public int y;
    public Crosshair(int x, int y) {
        this.x = x;
        this.y = y;
        }
}
+2  A: 

I guess you could use a Global Event Listener to listen for mouse entered events for the frame. Then on a mouse exited event I suppose you could use the Robot to reset the location of the mouse once it leaves the frame.

camickr
Ok camick, I read through your blog (very nice) and I set up a robot inside a mouseExited event. The robot moves the mouse back to a set of absolute coordinates, but I would like the robot to move the mouse back to the EXACT place where it exited. I used getX() and getY() but the robot will not move the mouse back there (I think the response time is too slow). I have also used the coordinates of my crosshair (crosshair.x and crosshair.y) but even then the mouse can escape if the user clicks at the right time. Any thoughts?
typoknig
You should be using event.getX() and event.getY(). Also these points are relative to the frame, whereas the Robot needs uses the location relative to the screen. You can use methods found in the SwingUtilities class to do the conversion. And you probably need to adjust these points since the mouseExited event will not necessarily generate points at the bounds of the frame, Depending on how fast the mouse is dragged.
camickr
Regarding the mouse escaping and clicking on another component I'm not sure how to solve the problem (although I couldn't duplicate it, I guess I'm too slow). Anyway when I run you demo, my CPU jumps up to 50% because of you infinite while loop. This is obviously not the way to structure a game. Maybe if the CPU wan't being hogged the Robot would respond faster. Game looping can generally be controlled by a Swing Timer so you don't hog the CPU. I'm also not sure where you got your code from. Swing is double buffered so there should be no need to use a BufferStrategy.
camickr
I modified the code from this tutorial:http://www.gamedeveloperskills.com/?p=52When I run my code on my computer my CPU usage only jumps to 13% (but I have a pretty powerful machine). I had already tried e.getX and e.getY but with less than satisfactor results (because if I move quick I can have the mouse into one of my other monitors before the event registers, so the coordinates of the event are actually in the other monitor I am referring to). Using the actual crosshair coordinates (crosshair.x and crosshair.y) seems to give me the best results, but mouse can still escape.
typoknig
It looks like controlling the crosshair with the mouse events driven by my joystick isn't going to work the way I want. I guess I will have to go back and try to use some sort of joystick input instead, unless you have any better ideas. Just a reminder, my joystick is the Hotas Cougar (I can generate almost any mouse or keyboard input from the joystick, right now I just have it acting like a mouse).
typoknig
Regarding the code for GameDeveloperSkill, see my answer below.
camickr
+1  A: 

As camickr mentions, you can do this using the Robot. It's generally something that people would recommend against, but here's a pretty good Robot primer to get you started:

http://www.developer.com/java/other/article.php/2212401/Introduction-to-the-Java-Robot-Class-in-Java.htm

Morinar
A: 

Well, I don't pretend to be a game developer but the code presented in the link is terrible (from what I can tell) for a simple "pong game". I copied the code and got it running so that the ball bounces around.

On my computer the CPU is 50% because of the infinite while loop that wastes CPU time to provide the animation for ball movement. The simple change to use a Thread that sleeps for 5ms rather than hog the CPU for 5ms, causes the CPU usage to drop down to about 18%.

I then changed the code to do the ball animation on a Swing panel. The code is easier to write and the CPU usage dropped to 1% and the ball animation was much faster.

Here is my final version. You should be able to swap back to using the buffered strategy by not adding the panel to the frame and then by invoking the drawFrame() method again.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;

import javax.swing.*;

public class Game3 extends JFrame {

    /**
     * @author Georgi Khomeriki
     */

    private Ball ball;

    // added this
    private JPanel gamePanel;

    private int windowWidth = 800;
    private int windowHeight = 600;


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

    public Game3() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(windowWidth, windowHeight);
        this.setResizable(false);
        this.setLocation(100, 100);
        this.setVisible(true);

        this.createBufferStrategy(2);

        initGame3();
/*
        while(true) {
            long start = System.currentTimeMillis();
            gameLoop();
            while(System.currentTimeMillis()-start < 5) {
                 //do nothing
            }
        }
*/

        Thread thread = new Thread(new Runnable()
        {
            public void run()
            {
                while(true)
                {
                    gameLoop();
                    try { Thread.sleep(5); }
                    catch(Exception e) {}
                }
            }
        });
        thread.start();

    }

    private void initGame3() {
        // all you're game variables should be initialized here
        ball = new Ball(windowWidth/2, windowHeight/2, 5, 5);

        // added these lines

        gamePanel = new GamePanel();
        gamePanel.setBackground(Color.BLACK);
        add(gamePanel);
    }

    private void gameLoop() {
        // your game logic goes here

// move the ball
    ball.x = ball.x + ball.dx;
    ball.y = ball.y + ball.dy;

    // change the direction of the ball if it hits a wall
    if(ball.x <= 0 || ball.x >= windowWidth-40)
        ball.dx = -ball.dx;
    if(ball.y <= 0 || ball.y >= windowHeight-40)
        ball.dy = -ball.dy;


        // changed to following to use Swing instead of buffer strategy
//      drawFrame();
        gamePanel.repaint();
    }

    private void drawFrame() {
        // code for the drawing goes here
        BufferStrategy bf = this.getBufferStrategy();
        Graphics g = null;

        try {
            g = bf.getDrawGraphics();

            // clear the back buffer (just draw a big black rectangle over it)
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, windowWidth, windowHeight);

drawBall(g);


        } finally {
            // It is best to dispose() a Graphics object when done with it.
            g.dispose();
        }

        // Shows the contents of the backbuffer on the screen.
        bf.show();

        //Tell the System to do the Drawing now, otherwise it can take a few extra ms until
        //Drawing is done which looks very jerky
        Toolkit.getDefaultToolkit().sync();
    }

    private void drawBall(Graphics g) {

        g.setColor(Color.GREEN);
        g.fillOval(ball.x, ball.y, 40, 40);
    }

    // added this

    class GamePanel extends JPanel
    {
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            drawBall(g);
        }
    }


    class Ball {

        public int x;
        public int y;
        public int dx;
        public int dy;

        public Ball(int x, int y, int dx, int dy) {
            this.x = x;
            this.y = y;
            this.dx = dx;
            this.dy = dy;
        }
    }



}
camickr
I have some other stuff to catch up on, but I will definitley look at this later this week. Thanks!
typoknig